From 05e8e8000b9b3029fb22ab97b9297a0e50679d5e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 2 Jun 2023 19:27:33 +0200 Subject: [PATCH 001/227] Multiple improvements - Make EEBUSService public in `CemImpl` - Update eebus-go to latest dev to support availability information --- cem/cem.go | 20 ++++++++++---------- cem/events.go | 4 ++-- emobility/scenario.go | 7 +++++++ go.mod | 2 +- go.sum | 4 ++-- grid/scenario.go | 1 + inverterbatteryvis/scenario.go | 1 + inverterpvvis/scenario.go | 1 + 8 files changed, 25 insertions(+), 15 deletions(-) diff --git a/cem/cem.go b/cem/cem.go index e7d70bc..812767e 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -14,7 +14,7 @@ import ( // Generic CEM implementation type CemImpl struct { - service *service.EEBUSService + Service *service.EEBUSService emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario scenarios.ScenariosI @@ -23,18 +23,18 @@ type CemImpl struct { func NewCEM(serviceDescription *service.Configuration, serviceHandler service.EEBUSServiceHandler, log logging.Logging) *CemImpl { cem := &CemImpl{ - service: service.NewEEBUSService(serviceDescription, serviceHandler), + Service: service.NewEEBUSService(serviceDescription, serviceHandler), Currency: model.CurrencyTypeEur, } - cem.service.SetLogging(log) + cem.Service.SetLogging(log) return cem } // Set up the supported usecases and features func (h *CemImpl) Setup() error { - if err := h.service.Setup(); err != nil { + if err := h.Service.Setup(); err != nil { return err } @@ -46,35 +46,35 @@ func (h *CemImpl) Setup() error { // Enable the supported usecases and features func (h *CemImpl) EnableEmobility(configuration emobility.EmobilityConfiguration) { - h.emobilityScenario = emobility.NewEMobilityScenario(h.service, h.Currency, configuration) + h.emobilityScenario = emobility.NewEMobilityScenario(h.Service, h.Currency, configuration) h.emobilityScenario.AddFeatures() h.emobilityScenario.AddUseCases() } func (h *CemImpl) EnableGrid() { - h.gridScenario = grid.NewGridScenario(h.service) + h.gridScenario = grid.NewGridScenario(h.Service) h.gridScenario.AddFeatures() h.gridScenario.AddUseCases() } func (h *CemImpl) EnableBatteryVisualization() { - h.inverterBatteryVisScenario = inverterbatteryvis.NewInverterVisScenario(h.service) + h.inverterBatteryVisScenario = inverterbatteryvis.NewInverterVisScenario(h.Service) h.inverterBatteryVisScenario.AddFeatures() h.inverterBatteryVisScenario.AddUseCases() } func (h *CemImpl) EnablePVVisualization() { - h.inverterPVVisScenario = inverterpvvis.NewInverterVisScenario(h.service) + h.inverterPVVisScenario = inverterpvvis.NewInverterVisScenario(h.Service) h.inverterPVVisScenario.AddFeatures() h.inverterPVVisScenario.AddUseCases() } func (h *CemImpl) Start() { - h.service.Start() + h.Service.Start() } func (h *CemImpl) Shutdown() { - h.service.Shutdown() + h.Service.Shutdown() } func (h *CemImpl) RegisterEmobilityRemoteDevice(details *service.ServiceDetails, dataProvider emobility.EmobilityDataProvider) *emobility.EMobilityImpl { diff --git a/cem/events.go b/cem/events.go index 3961f5d..05d0d0c 100644 --- a/cem/events.go +++ b/cem/events.go @@ -26,13 +26,13 @@ func (h *CemImpl) subscriptionRequestHandling(payload spine.EventPayload) { return } - remoteDevice := h.service.RemoteDeviceForSki(payload.Ski) + remoteDevice := h.Service.RemoteDeviceForSki(payload.Ski) if remoteDevice == nil { logging.Log.Info("No remote device found for SKI:", payload.Ski) return } - senderAddr := h.service.LocalDevice().FeatureByTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer).Address() + senderAddr := h.Service.LocalDevice().FeatureByTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer).Address() destinationAddr := payload.Feature.Address() if senderAddr == nil || destinationAddr == nil { logging.Log.Info("No sender or destination address found for SKI:", payload.Ski) diff --git a/emobility/scenario.go b/emobility/scenario.go index b3f1e85..b221864 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -80,24 +80,28 @@ func (e *EmobilityScenarioImpl) AddUseCases() { localEntity, model.UseCaseNameTypeEVSECommissioningAndConfiguration, model.SpecificationVersionType("1.0.1"), + true, []model.UseCaseScenarioSupportType{1, 2}) _ = spine.NewUseCase( localEntity, model.UseCaseNameTypeEVCommissioningAndConfiguration, model.SpecificationVersionType("1.0.1"), + true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) _ = spine.NewUseCase( localEntity, model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging, model.SpecificationVersionType("1.0.1"), + true, []model.UseCaseScenarioSupportType{1, 2, 3}) _ = spine.NewUseCase( localEntity, model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment, model.SpecificationVersionType("1.0.1b"), + true, []model.UseCaseScenarioSupportType{1, 2, 3}) _ = spine.NewUseCaseWithActor( @@ -105,12 +109,14 @@ func (e *EmobilityScenarioImpl) AddUseCases() { model.UseCaseActorTypeMonitoringAppliance, model.UseCaseNameTypeEVStateOfCharge, model.SpecificationVersionType("1.0.0"), + true, []model.UseCaseScenarioSupportType{1, 2, 3, 4}) _ = spine.NewUseCase( localEntity, model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging, model.SpecificationVersionType("1.0.1b"), + true, []model.UseCaseScenarioSupportType{1, 2, 3}) if e.configuration.CoordinatedChargingEnabled { @@ -118,6 +124,7 @@ func (e *EmobilityScenarioImpl) AddUseCases() { localEntity, model.UseCaseNameTypeCoordinatedEVCharging, model.SpecificationVersionType("1.0.1"), + true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) } } diff --git a/go.mod b/go.mod index 138a14a..984cf5d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.2.0 + github.com/enbility/eebus-go v0.0.0-20230602172504-93ff2e34717b github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.2 golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 diff --git a/go.sum b/go.sum index 8006c4c..2318dfa 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.2.0 h1:znQUfG1QYk0Q+vOacrsSNtXmitF1F2Rx9+ohwcRNlRw= -github.com/enbility/eebus-go v0.2.0/go.mod h1:Ozg1eDUfSbHfQ1dWfyAUa3h8dMtgM/01eO30kHca5zk= +github.com/enbility/eebus-go v0.0.0-20230602172504-93ff2e34717b h1:Jebo9Zzq5W+bRTOallaG8TwfKwTcxBK3vHWZ6OZb630= +github.com/enbility/eebus-go v0.0.0-20230602172504-93ff2e34717b/go.mod h1:eVM09KrxdiZXbV7d+EdYLi9TFEPZo+BwOAAzILXj0G0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/grid/scenario.go b/grid/scenario.go index 8e1700d..b535f6f 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -50,6 +50,7 @@ func (e *GridScenarioImpl) AddUseCases() { localEntity, model.UseCaseNameTypeMonitoringOfGridConnectionPoint, model.SpecificationVersionType("1.0.0 RC5"), + true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) } diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index f52c9fa..61e1b96 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -51,6 +51,7 @@ func (i *InverterBatteryVisScenarioImpl) AddUseCases() { model.UseCaseActorTypeVisualizationAppliance, model.UseCaseNameTypeVisualizationOfAggregatedBatteryData, model.SpecificationVersionType("1.0.0 RC1"), + true, []model.UseCaseScenarioSupportType{1, 2, 3, 4}) } diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index b6a3271..f85d695 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -51,6 +51,7 @@ func (i *InverterPVVisScenarioImpl) AddUseCases() { model.UseCaseActorTypeVisualizationAppliance, model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData, model.SpecificationVersionType("1.0.0 RC1"), + true, []model.UseCaseScenarioSupportType{1, 2, 3}) } From 14b26ff44cf06a046fa5e522862c329c96c497b1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 3 Jun 2023 08:42:03 +0200 Subject: [PATCH 002/227] Update to latest eebus-go dev --- cem/cem.go | 16 ++++++++-------- cmd/main.go | 9 ++++++++- emobility/emobility.go | 4 ++-- emobility/helper_test.go | 4 ++-- emobility/scenario.go | 8 ++++---- go.mod | 2 +- go.sum | 4 ++-- grid/grid.go | 4 ++-- grid/scenario.go | 8 ++++---- inverterbatteryvis/invertervis.go | 4 ++-- inverterbatteryvis/scenario.go | 8 ++++---- inverterpvvis/invertervis.go | 4 ++-- inverterpvvis/scenario.go | 8 ++++---- scenarios/types.go | 2 +- 14 files changed, 46 insertions(+), 39 deletions(-) diff --git a/cem/cem.go b/cem/cem.go index 812767e..e45af38 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -89,8 +89,8 @@ func (h *CemImpl) RegisterEmobilityRemoteDevice(details *service.ServiceDetails, return impl.(*emobility.EMobilityImpl) } -func (h *CemImpl) UnRegisterEmobilityRemoteDevice(remoteDeviceSki string) error { - return h.emobilityScenario.UnRegisterRemoteDevice(remoteDeviceSki) +func (h *CemImpl) UnRegisterEmobilityRemoteDevice(remoteDeviceSki string) { + h.emobilityScenario.UnRegisterRemoteDevice(remoteDeviceSki) } func (h *CemImpl) RegisterGridRemoteDevice(details *service.ServiceDetails) *grid.GridImpl { @@ -98,8 +98,8 @@ func (h *CemImpl) RegisterGridRemoteDevice(details *service.ServiceDetails) *gri return impl.(*grid.GridImpl) } -func (h *CemImpl) UnRegisterGridRemoteDevice(remoteDeviceSki string) error { - return h.gridScenario.UnRegisterRemoteDevice(remoteDeviceSki) +func (h *CemImpl) UnRegisterGridRemoteDevice(remoteDeviceSki string) { + h.gridScenario.UnRegisterRemoteDevice(remoteDeviceSki) } func (h *CemImpl) RegisterInverterBatteryVisRemoteDevice(details *service.ServiceDetails) *grid.GridImpl { @@ -107,8 +107,8 @@ func (h *CemImpl) RegisterInverterBatteryVisRemoteDevice(details *service.Servic return impl.(*grid.GridImpl) } -func (h *CemImpl) UnRegisterInverterBatteryVisRemoteDevice(remoteDeviceSki string) error { - return h.inverterBatteryVisScenario.UnRegisterRemoteDevice(remoteDeviceSki) +func (h *CemImpl) UnRegisterInverterBatteryVisRemoteDevice(remoteDeviceSki string) { + h.inverterBatteryVisScenario.UnRegisterRemoteDevice(remoteDeviceSki) } func (h *CemImpl) RegisterInverterPVVisRemoteDevice(details *service.ServiceDetails) *grid.GridImpl { @@ -116,6 +116,6 @@ func (h *CemImpl) RegisterInverterPVVisRemoteDevice(details *service.ServiceDeta return impl.(*grid.GridImpl) } -func (h *CemImpl) UnRegisterInverterPVVisRemoteDevice(remoteDeviceSki string) error { - return h.inverterPVVisScenario.UnRegisterRemoteDevice(remoteDeviceSki) +func (h *CemImpl) UnRegisterInverterPVVisRemoteDevice(remoteDeviceSki string) { + h.inverterPVVisScenario.UnRegisterRemoteDevice(remoteDeviceSki) } diff --git a/cmd/main.go b/cmd/main.go index 982a61e..e5691a6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -54,7 +54,14 @@ func (d *DemoCem) RemoteSKIConnected(service *service.EEBUSService, ski string) func (d *DemoCem) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} -func (h *DemoCem) ReportServiceShipID(ski string, shipdID string) {} +func (d *DemoCem) VisibleRemoteServicesUpdated(service *service.EEBUSService, entries []service.RemoteService) { +} + +func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} + +func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) {} + +func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } // Logging interface diff --git a/emobility/emobility.go b/emobility/emobility.go index 07484f2..7377d39 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -246,7 +246,7 @@ var _ EmobilityI = (*EMobilityImpl)(nil) // Add E-Mobility support func NewEMobility(service *service.EEBUSService, details *service.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { - ski := util.NormalizeSKI(details.SKI()) + ski := util.NormalizeSKI(details.SKI) emobility := &EMobilityImpl{ service: service, @@ -259,7 +259,7 @@ func NewEMobility(service *service.EEBUSService, details *service.ServiceDetails } spine.Events.Subscribe(emobility) - service.PairRemoteService(details) + service.RegisterRemoteSKI(ski, true) return emobility } diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 8e7c734..596f04b 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -94,7 +94,7 @@ const remoteSki string = "testremoteski" // we don't want to handle events in these tests for now, so we don't use NewEMobility(...) func NewTestEMobility(service *service.EEBUSService, details *service.ServiceDetails) *EMobilityImpl { - ski := util.NormalizeSKI(details.SKI()) + ski := util.NormalizeSKI(details.SKI) emobility := &EMobilityImpl{ service: service, @@ -102,7 +102,7 @@ func NewTestEMobility(service *service.EEBUSService, details *service.ServiceDet ski: ski, } - service.PairRemoteService(details) + service.RegisterRemoteSKI(ski, true) return emobility } diff --git a/emobility/scenario.go b/emobility/scenario.go index b221864..ed75659 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -135,7 +135,7 @@ func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *service.ServiceDet e.mux.Lock() defer e.mux.Unlock() - if em, ok := e.remoteDevices[details.SKI()]; ok { + if em, ok := e.remoteDevices[details.SKI]; ok { return em } @@ -144,17 +144,17 @@ func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *service.ServiceDet provider = dataProvider.(EmobilityDataProvider) } emobility := NewEMobility(e.Service, details, e.currency, e.configuration, provider) - e.remoteDevices[details.SKI()] = emobility + e.remoteDevices[details.SKI] = emobility return emobility } -func (e *EmobilityScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) error { +func (e *EmobilityScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { e.mux.Lock() defer e.mux.Unlock() delete(e.remoteDevices, remoteDeviceSki) - return e.Service.UnpairRemoteService(remoteDeviceSki) + e.Service.RegisterRemoteSKI(remoteDeviceSki, false) } func (e *EmobilityScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { diff --git a/go.mod b/go.mod index 984cf5d..54b2a55 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20230602172504-93ff2e34717b + github.com/enbility/eebus-go v0.0.0-20230603061049-09215c75c502 github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.2 golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 diff --git a/go.sum b/go.sum index 2318dfa..ead1b0e 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20230602172504-93ff2e34717b h1:Jebo9Zzq5W+bRTOallaG8TwfKwTcxBK3vHWZ6OZb630= -github.com/enbility/eebus-go v0.0.0-20230602172504-93ff2e34717b/go.mod h1:eVM09KrxdiZXbV7d+EdYLi9TFEPZo+BwOAAzILXj0G0= +github.com/enbility/eebus-go v0.0.0-20230603061049-09215c75c502 h1:DJz1aTpzzrGMrjaHi41k2wVIuAweX01mGGVwXLalRUU= +github.com/enbility/eebus-go v0.0.0-20230603061049-09215c75c502/go.mod h1:o+9MoYO/AdCOXvN/5JgQ5Uh7kIN5/9wFq2Fo30pLFN8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/grid/grid.go b/grid/grid.go index 4b6d985..5110813 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -35,7 +35,7 @@ var _ GridI = (*GridImpl)(nil) // Add Grid support func NewGrid(service *service.EEBUSService, details *service.ServiceDetails) *GridImpl { - ski := util.NormalizeSKI(details.SKI()) + ski := util.NormalizeSKI(details.SKI) grid := &GridImpl{ service: service, @@ -44,7 +44,7 @@ func NewGrid(service *service.EEBUSService, details *service.ServiceDetails) *Gr } spine.Events.Subscribe(grid) - service.PairRemoteService(details) + service.RegisterRemoteSKI(ski, true) return grid } diff --git a/grid/scenario.go b/grid/scenario.go index b535f6f..6006c28 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -60,22 +60,22 @@ func (e *GridScenarioImpl) RegisterRemoteDevice(details *service.ServiceDetails, e.mux.Lock() defer e.mux.Unlock() - if em, ok := e.remoteDevices[details.SKI()]; ok { + if em, ok := e.remoteDevices[details.SKI]; ok { return em } grid := NewGrid(e.Service, details) - e.remoteDevices[details.SKI()] = grid + e.remoteDevices[details.SKI] = grid return grid } -func (e *GridScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) error { +func (e *GridScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { e.mux.Lock() defer e.mux.Unlock() delete(e.remoteDevices, remoteDeviceSki) - return e.Service.UnpairRemoteService(remoteDeviceSki) + e.Service.RegisterRemoteSKI(remoteDeviceSki, false) } func (e *GridScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index d5aa5ae..e043127 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -30,7 +30,7 @@ var _ InverterBatteryVisI = (*InverterBatteryVisImpl)(nil) // Add InverterBatteryVis support func NewInverterBatteryVis(service *service.EEBUSService, details *service.ServiceDetails) *InverterBatteryVisImpl { - ski := util.NormalizeSKI(details.SKI()) + ski := util.NormalizeSKI(details.SKI) inverter := &InverterBatteryVisImpl{ service: service, @@ -39,7 +39,7 @@ func NewInverterBatteryVis(service *service.EEBUSService, details *service.Servi } spine.Events.Subscribe(inverter) - service.PairRemoteService(details) + service.RegisterRemoteSKI(ski, true) return inverter } diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index 61e1b96..c5a4fc0 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -61,22 +61,22 @@ func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *service.S i.mux.Lock() defer i.mux.Unlock() - if em, ok := i.remoteDevices[details.SKI()]; ok { + if em, ok := i.remoteDevices[details.SKI]; ok { return em } inverter := NewInverterBatteryVis(i.Service, details) - i.remoteDevices[details.SKI()] = inverter + i.remoteDevices[details.SKI] = inverter return inverter } -func (i *InverterBatteryVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) error { +func (i *InverterBatteryVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { i.mux.Lock() defer i.mux.Unlock() delete(i.remoteDevices, remoteDeviceSki) - return i.Service.UnpairRemoteService(remoteDeviceSki) + i.Service.RegisterRemoteSKI(remoteDeviceSki, false) } func (i *InverterBatteryVisScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 3706db0..f17e8d9 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -30,7 +30,7 @@ var _ InverterPVVisI = (*InverterPVVisImpl)(nil) // Add InverterPVVis support func NewInverterPVVis(service *service.EEBUSService, details *service.ServiceDetails) *InverterPVVisImpl { - ski := util.NormalizeSKI(details.SKI()) + ski := util.NormalizeSKI(details.SKI) inverter := &InverterPVVisImpl{ service: service, @@ -39,7 +39,7 @@ func NewInverterPVVis(service *service.EEBUSService, details *service.ServiceDet } spine.Events.Subscribe(inverter) - service.PairRemoteService(details) + service.RegisterRemoteSKI(ski, true) return inverter } diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index f85d695..022258b 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -61,22 +61,22 @@ func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *service.Servic i.mux.Lock() defer i.mux.Unlock() - if em, ok := i.remoteDevices[details.SKI()]; ok { + if em, ok := i.remoteDevices[details.SKI]; ok { return em } inverter := NewInverterPVVis(i.Service, details) - i.remoteDevices[details.SKI()] = inverter + i.remoteDevices[details.SKI] = inverter return inverter } -func (i *InverterPVVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) error { +func (i *InverterPVVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { i.mux.Lock() defer i.mux.Unlock() delete(i.remoteDevices, remoteDeviceSki) - return i.Service.UnpairRemoteService(remoteDeviceSki) + i.Service.RegisterRemoteSKI(remoteDeviceSki, false) } func (i *InverterPVVisScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { diff --git a/scenarios/types.go b/scenarios/types.go index ecc9a5f..03bb2b6 100644 --- a/scenarios/types.go +++ b/scenarios/types.go @@ -7,7 +7,7 @@ import ( // Implemented by *ScenarioImpl, used by CemImpl type ScenariosI interface { RegisterRemoteDevice(details *service.ServiceDetails, dataProvider any) any - UnRegisterRemoteDevice(remoteDeviceSki string) error + UnRegisterRemoteDevice(remoteDeviceSki string) AddFeatures() AddUseCases() } From 218e56a07c040ed59181f7af60f90d51c34507c7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 5 Jun 2023 22:12:35 +0200 Subject: [PATCH 003/227] Emobility chargingstrategy fix --- emobility/public.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/emobility/public.go b/emobility/public.go index bfded8f..c8369fc 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -656,10 +656,13 @@ func (e *EMobilityImpl) EVChargeStrategy() EVChargeStrategyType { switch { case firstSlot.Duration == nil: // if value is > 0 and duration does not exist, the EV is direct charging - if firstSlot.Value != nil { + if firstSlot.Value != nil && firstSlot.Value.GetValue() > 0 { return EVChargeStrategyTypeDirectCharging } + // maxValue will show the maximum amount the battery could take + return EVChargeStrategyTypeNoDemand + case firstSlot.Duration != nil: if _, err := firstSlot.Duration.GetTimeDuration(); err != nil { // we got an invalid duration From 7870ecbd7ff1f2a127cd227e3866b46cc15824aa Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 30 Jun 2023 11:29:23 +0200 Subject: [PATCH 004/227] Extend EVWriteControlLimits - generalize the parameter - add the possibility to set `IsActive` --- emobility/emobility.go | 2 +- emobility/public.go | 30 +++---- .../public_EVWriteLoadControlLimits_test.go | 88 ++++++++++++------- emobility/types.go | 13 +++ 4 files changed, 80 insertions(+), 53 deletions(-) diff --git a/emobility/emobility.go b/emobility/emobility.go index 7377d39..c215d26 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -110,7 +110,7 @@ type EmobilityI interface { // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific // and needs to have specific EVSE support for the specific EV brand. // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. - EVWriteLoadControlLimits(obligations, recommendations []float64) error + EVWriteLoadControlLimits(limits []EVLoadLimits) error // return the current communication standard type used to communicate between EVSE and EV // diff --git a/emobility/public.go b/emobility/public.go index c8369fc..c2b009c 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -338,17 +338,16 @@ func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { // send new LoadControlLimits to the remote EV // // parameters: -// - obligations: Overload Protection Limits per phase in A -// - recommendations: Self Consumption recommendations per phase in A +// - limits: a set of limits for a given limit category containing phase specific limit data // -// obligations: +// category obligations: // Sets a maximum A limit for each phase that the EV may not exceed. // Mainly used for implementing overload protection of the site or limiting the // maximum charge power of EVs when the EV and EVSE communicate via IEC61851 // and with ISO15118 if the EV does not support the Optimization of Self Consumption // usecase. // -// recommendations: +// category recommendations: // Sets a recommended charge power in A for each phase. This is mainly // used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. // The EV either needs to support the Optimization of Self Consumption usecase or @@ -361,7 +360,7 @@ func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { // - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. // - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. // - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. -func (e *EMobilityImpl) EVWriteLoadControlLimits(obligations, recommendations []float64) error { +func (e *EMobilityImpl) EVWriteLoadControlLimits(limits []EVLoadLimits) error { if e.evEntity == nil { return ErrEVDisconnected } @@ -372,17 +371,10 @@ func (e *EMobilityImpl) EVWriteLoadControlLimits(obligations, recommendations [] var limitData []model.LoadControlLimitDataType - for scopeTypes := 0; scopeTypes < 2; scopeTypes++ { - category := model.LoadControlCategoryTypeObligation - currentsPerPhase := obligations - if scopeTypes == 1 { - category = model.LoadControlCategoryTypeRecommendation - currentsPerPhase = recommendations - } - - for index, phaseLimit := range currentsPerPhase { - phaseName := util.PhaseNameMapping[index] + for _, scope := range limits { + category := scope.Category + for _, phaseLimit := range scope.PhaseData { // find out the appropriate limitId for each phase value // limitDescription contains the measurementId for each limitId limitDescriptions, err := e.evLoadControl.GetLimitDescriptionsForCategory(category) @@ -391,7 +383,7 @@ func (e *EMobilityImpl) EVWriteLoadControlLimits(obligations, recommendations [] } // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := e.evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) + elParamDesc, err := e.evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) if err != nil || elParamDesc.MeasurementId == nil { continue } @@ -420,12 +412,12 @@ func (e *EMobilityImpl) EVWriteLoadControlLimits(obligations, recommendations [] } // electricalPermittedValueSet contains the allowed min, max and the default values per phase - phaseLimit = e.evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit, *elParamDesc.ParameterId) + limit := e.evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) newLimit := model.LoadControlLimitDataType{ LimitId: limitDesc.LimitId, - IsLimitActive: eebusUtil.Ptr(true), - Value: model.NewScaledNumberType(phaseLimit), + IsLimitActive: eebusUtil.Ptr(phaseLimit.IsActive), + Value: model.NewScaledNumberType(limit), } limitData = append(limitData, newLimit) } diff --git a/emobility/public_EVWriteLoadControlLimits_test.go b/emobility/public_EVWriteLoadControlLimits_test.go index e7dcdc1..b240660 100644 --- a/emobility/public_EVWriteLoadControlLimits_test.go +++ b/emobility/public_EVWriteLoadControlLimits_test.go @@ -4,8 +4,9 @@ import ( "encoding/json" "testing" + "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/spine/model" - "github.com/enbility/eebus-go/util" + eebusUtil "github.com/enbility/eebus-go/util" "github.com/stretchr/testify/assert" "golang.org/x/exp/slices" ) @@ -13,23 +14,22 @@ import ( func Test_EVWriteLoadControlLimits(t *testing.T) { emobilty, eebusService := setupEmobility() - obligations := []float64{} - recommendations := []float64{} + loadLimits := []EVLoadLimits{} - err := emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err := emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) localDevice, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - err = emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) emobilty.evLoadControl = loadcontrol(localDevice, emobilty.evEntity) - err = emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) @@ -38,22 +38,22 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusUtil.Ptr(model.ElectricalConnectionPhaseNameTypeA), }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusUtil.Ptr(model.ElectricalConnectionPhaseNameTypeB), }, { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), - MeasurementId: util.Ptr(model.MeasurementIdType(2)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusUtil.Ptr(model.ElectricalConnectionPhaseNameTypeC), }, }, }}} @@ -62,7 +62,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) type dataStruct struct { @@ -146,8 +146,8 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { permittedData = append(permittedData, item) permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(phase)), + ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(phase)), PermittedValueSet: permittedData, } dataSet = append(dataSet, permittedItem) @@ -164,7 +164,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) @@ -174,9 +174,9 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { for index := range data.obligations { id := model.LoadControlLimitIdType(index) limitItem := model.LoadControlLimitDescriptionDataType{ - LimitId: util.Ptr(id), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeObligation), - MeasurementId: util.Ptr(model.MeasurementIdType(index)), + LimitId: eebusUtil.Ptr(id), + LimitCategory: eebusUtil.Ptr(model.LoadControlCategoryTypeObligation), + MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(index)), } limitDesc = append(limitDesc, limitItem) limitIdsObligation = append(limitIdsObligation, id) @@ -185,9 +185,9 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { for index := range data.recommendations { id := model.LoadControlLimitIdType(index + add) limitItem := model.LoadControlLimitDescriptionDataType{ - LimitId: util.Ptr(id), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeRecommendation), - MeasurementId: util.Ptr(model.MeasurementIdType(index + add)), + LimitId: eebusUtil.Ptr(id), + LimitCategory: eebusUtil.Ptr(model.LoadControlCategoryTypeRecommendation), + MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(index + add)), } limitDesc = append(limitDesc, limitItem) limitIdsRecommendation = append(limitIdsRecommendation, id) @@ -202,14 +202,14 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) limitData := []model.LoadControlLimitDataType{} for index := range limitDesc { limitItem := model.LoadControlLimitDataType{ - LimitId: util.Ptr(model.LoadControlLimitIdType(index)), - IsLimitChangeable: util.Ptr(true), + LimitId: eebusUtil.Ptr(model.LoadControlLimitIdType(index)), + IsLimitChangeable: eebusUtil.Ptr(true), } limitData = append(limitData, limitItem) } @@ -224,10 +224,32 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(obligations, recommendations) + err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) - err = emobilty.EVWriteLoadControlLimits(data.obligations, data.recommendations) + obligations := []EVLoadLimitsPhase{} + for index, obligation := range data.obligations { + phase := util.PhaseNameMapping[index] + obligations = append(obligations, EVLoadLimitsPhase{ + Phase: phase, + IsActive: true, + Value: obligation, + }) + } + recommendations := []EVLoadLimitsPhase{} + for index, obligation := range data.recommendations { + phase := util.PhaseNameMapping[index] + obligations = append(obligations, EVLoadLimitsPhase{ + Phase: phase, + IsActive: true, + Value: obligation, + }) + } + + err = emobilty.EVWriteLoadControlLimits([]EVLoadLimits{ + {Category: model.LoadControlCategoryTypeObligation, PhaseData: obligations}, + {Category: model.LoadControlCategoryTypeRecommendation, PhaseData: recommendations}, + }) assert.Nil(t, err) sentDatagram := model.Datagram{} diff --git a/emobility/types.go b/emobility/types.go index 40bd877..ca511a3 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -37,6 +37,19 @@ const ( EVChargeStrategyTypeTimedCharging EVChargeStrategyType = "timedcharging" ) +// Defines a phase specific limit +type EVLoadLimitsPhase struct { + Phase model.ElectricalConnectionPhaseNameType + IsActive bool + Value float64 +} + +// Contains limit data for a defined category +type EVLoadLimits struct { + Category model.LoadControlCategoryType + PhaseData []EVLoadLimitsPhase +} + // Contains details about the actual demands from the EV // // General: From 20651f260c714da6cbf384a204d854e555feada3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 28 Aug 2023 13:08:51 +0200 Subject: [PATCH 005/227] Fix EVLoadControlObligationLimits If the limit is inactive, report the maximum value --- emobility/public.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emobility/public.go b/emobility/public.go index c2b009c..3e902f0 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -317,8 +317,8 @@ func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { } var limitValue float64 - if limitIdData.Value == nil { - // assume maximum possible + if limitIdData.Value == nil || (limitIdData.IsLimitActive != nil && *limitIdData.IsLimitActive == false) { + // report maximum possible if no limit is available or the limit is not active _, dataMax, _, err := e.evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) if err != nil { return nil, features.ErrDataNotAvailable From 859cc5a2b57828f6d5ea84a5e60c1a77975bd53e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 28 Aug 2023 15:34:31 +0200 Subject: [PATCH 006/227] Fix linter --- emobility/public.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emobility/public.go b/emobility/public.go index 3e902f0..e5eb965 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -317,7 +317,7 @@ func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { } var limitValue float64 - if limitIdData.Value == nil || (limitIdData.IsLimitActive != nil && *limitIdData.IsLimitActive == false) { + if limitIdData.Value == nil || (limitIdData.IsLimitActive != nil && !*limitIdData.IsLimitActive) { // report maximum possible if no limit is available or the limit is not active _, dataMax, _, err := e.evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) if err != nil { From f5e03acd9179fdd2e40c479f80f6a8cdd23030dd Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:13:14 +0200 Subject: [PATCH 007/227] Allow currents < 2.2A --- emobility/public.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/emobility/public.go b/emobility/public.go index e5eb965..2598467 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -253,10 +253,6 @@ func (e *EMobilityImpl) EVCurrentLimits() ([]float64, []float64, []float64, erro // Min current data should be derived from min power data // but as this value is only properly provided via VAS the // currrent min values can not be trusted. - // Min current for 3-phase should be at least 2.2A (ISO) - if dataMin < 2.2 { - dataMin = 2.2 - } resultMin = append(resultMin, dataMin) resultMax = append(resultMax, dataMax) From 9b6a41b53f811145f8c3472f09e6c9428b0f6f62 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:14:09 +0200 Subject: [PATCH 008/227] Fxi test --- emobility/public_EVChargeStrategy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emobility/public_EVChargeStrategy_test.go b/emobility/public_EVChargeStrategy_test.go index 1602ef0..dc593f9 100644 --- a/emobility/public_EVChargeStrategy_test.go +++ b/emobility/public_EVChargeStrategy_test.go @@ -124,7 +124,7 @@ func Test_EVChargeStrategy(t *testing.T) { assert.Nil(t, err) data = emobilty.EVChargeStrategy() - assert.Equal(t, EVChargeStrategyTypeUnknown, data) + assert.Equal(t, EVChargeStrategyTypeNoDemand, data) cmd = []model.CmdType{{ TimeSeriesListData: &model.TimeSeriesListDataType{ From 35a70470c34d7e2de99d6df667923d0dc9274043 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:16:07 +0200 Subject: [PATCH 009/227] Automatically refetch current values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Measurements may not contain a timestamp or the values may be old, and maybe the remote service doesn’t notify about new measurements. This change will automatically trigger a request call for the latest measurements --- emobility/public.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/emobility/public.go b/emobility/public.go index 2598467..87e798c 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -206,6 +206,8 @@ func (e *EMobilityImpl) EVCurrentsPerPhase() ([]float64, error) { } var result []float64 + refetch := true + compare := time.Now().Add(-1 * time.Minute) for _, phase := range util.PhaseNameMapping { for _, item := range data { @@ -220,9 +222,21 @@ func (e *EMobilityImpl) EVCurrentsPerPhase() ([]float64, error) { phaseValue := item.Value.GetValue() result = append(result, phaseValue) + + if item.Timestamp != nil { + if timestamp, err := item.Timestamp.GetTime(); err == nil { + refetch = timestamp.Before(compare) + } + } } } + // if there was no timestamp provided or the time for the last value + // is older than 1 minute, send a read request + if refetch { + _, _ = e.evMeasurement.RequestValues() + } + return result, nil } From 6238418af0962c6860fb487a6013a81de053e957 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:53:29 +0200 Subject: [PATCH 010/227] Fix grid usecase setup --- grid/scenario.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grid/scenario.go b/grid/scenario.go index 6006c28..b7d5c5e 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -46,8 +46,9 @@ func (e *GridScenarioImpl) AddFeatures() { func (e *GridScenarioImpl) AddUseCases() { localEntity := e.Service.LocalEntity() - _ = spine.NewUseCase( + _ = spine.NewUseCaseWithActor( localEntity, + model.UseCaseActorTypeMonitoringAppliance, model.UseCaseNameTypeMonitoringOfGridConnectionPoint, model.SpecificationVersionType("1.0.0 RC5"), true, From 8f46c7958a46269b9b58fc1b21ac6715e819ce5f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:55:21 +0200 Subject: [PATCH 011/227] Update demo client --- README.md | 6 ++++-- cmd/main.go | 48 ++++++++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ea03fc3..1fa72e7 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,13 @@ More use cases and scenarios will hopefully follow in the future as well. ## Usage +Run the following command to see all the options: + ```sh -Usage: go run cmd/main.go +Usage: go run cmd/main.go ``` -Example certificate and key files are located in the keys folder +Example certificate and key files are located in the keys folder. If no certificate and key are provided in the options, new ones will be generated in the current folder. ### Explanation diff --git a/cmd/main.go b/cmd/main.go index e5691a6..cb2ca76 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,10 +2,11 @@ package main import ( "crypto/tls" + "flag" "fmt" + "log" "os" "os/signal" - "strconv" "syscall" "time" @@ -40,6 +41,8 @@ func (d *DemoCem) Setup() error { d.cem.EnableBatteryVisualization() d.cem.EnablePVVisualization() + d.cem.Service.Start() + return nil } @@ -108,44 +111,49 @@ func (d *DemoCem) Errorf(format string, args ...interface{}) { } // main app -func usage() { - fmt.Println("Usage: go run /cmd/main.go ") -} - func main() { - if len(os.Args) < 5 { - usage() - return - } + remoteSki := flag.String("remoteski", "", "The remote device SKI") + port := flag.Int("port", 4815, "Optional port for the EEBUS service") + crt := flag.String("crt", "cert.crt", "Optional filepath for the cert file") + key := flag.String("key", "cert.key", "Optional filepath for the key file") + iface := flag.String("iface", "", "Optional network interface the EEBUS connection should be limited to") - portValue, err := strconv.Atoi(os.Args[1]) - if err != nil { - fmt.Println("Port is invalid:", err) + flag.Parse() + + if len(os.Args) == 1 || remoteSki == nil || *remoteSki == "" { + flag.Usage() return } - certificate, err := tls.LoadX509KeyPair(os.Args[3], os.Args[4]) + certificate, err := tls.LoadX509KeyPair(*crt, *key) if err != nil { - fmt.Println("Certificate is invalid:", err) - return + certificate, err = service.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-10") + if err != nil { + log.Fatal(err) + } + } else { + fmt.Println("Using certificate file", *crt, "and key file", *key) } - ifaces := []string{os.Args[5]} - configuration, err := service.NewConfiguration( "Demo", "Demo", "HEMS", "123456789", model.DeviceTypeTypeEnergyManagementSystem, - portValue, + *port, certificate, 230) if err != nil { fmt.Println("Service data is invalid:", err) return } - configuration.SetInterfaces(ifaces) + + if iface != nil && *iface != "" { + ifaces := []string{*iface} + + configuration.SetInterfaces(ifaces) + } demo := NewDemoCem(configuration) if err := demo.Setup(); err != nil { @@ -153,7 +161,7 @@ func main() { return } - remoteService := service.NewServiceDetails(os.Args[2]) + remoteService := service.NewServiceDetails(*remoteSki) demo.cem.RegisterEmobilityRemoteDevice(remoteService, nil) // Clean exit to make sure mdns shutdown is invoked From 49ff2a93c6e918fe6576fa9832bf1c89b255fa16 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 14:55:42 +0200 Subject: [PATCH 012/227] Update dependencies --- go.mod | 20 ++++++++++---------- go.sum | 55 ++++++++++++++++++++++++------------------------------- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 54b2a55..ee1671d 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,27 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20230603061049-09215c75c502 + github.com/enbility/eebus-go v0.0.0-20230930121918-5134925e48e5 github.com/golang/mock v1.6.0 - github.com/stretchr/testify v1.8.2 - golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 + github.com/stretchr/testify v1.8.4 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 ) require ( + github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/holoplot/go-avahi v1.0.1 // indirect - github.com/libp2p/zeroconf/v2 v2.2.0 // indirect - github.com/miekg/dns v1.1.52 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rickb777/date v1.20.1 // indirect + github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/tools v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ead1b0e..b4f187f 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f h1:3+usd0uwuU/PnxDgKiQFc3FSR0C1PnV5Bk03NUKktaU= +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f/go.mod h1:Fhz5FR1v8Nv4Hj1v9oGXDz4C33+ZaAPqK+Y2wpfWvu0= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20230603061049-09215c75c502 h1:DJz1aTpzzrGMrjaHi41k2wVIuAweX01mGGVwXLalRUU= -github.com/enbility/eebus-go v0.0.0-20230603061049-09215c75c502/go.mod h1:o+9MoYO/AdCOXvN/5JgQ5Uh7kIN5/9wFq2Fo30pLFN8= +github.com/enbility/eebus-go v0.0.0-20230930121918-5134925e48e5 h1:lh17pW/rGdTmaKvUBYqQ6Rpj/uIHYWG0TN2r1qG0aJs= +github.com/enbility/eebus-go v0.0.0-20230930121918-5134925e48e5/go.mod h1:JyH+oCMEEyWlEip/zYDZViJVxQGpKW418TzrwVhSMbs= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -15,46 +16,39 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= -github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= -github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rickb777/date v1.20.1 h1:7MzSOc42Hbr5UXiQOihAAXoYDoeyzr0Hwvt+hCjBDV4= -github.com/rickb777/date v1.20.1/go.mod h1:9MqjVxT6a/AQTA4nxj9E6G3ksQiMESTn9/9kfE+CvwU= +github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ= +github.com/rickb777/date v1.20.5/go.mod h1:6BPrm3/aQI0I8jvlD1fAlm/86k5eSeTQ2mR5FEmTnSw= github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 h1:5oN1Pz/eDhCpbMbLstvIPa0b/BEQo6g6nwV3pLjfM6w= -golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -63,23 +57,22 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 83be69670198c5f720b5c71288cb8c8e6d8636fc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Sep 2023 18:44:17 +0200 Subject: [PATCH 013/227] Fix tests --- emobility/public_EVCurrentLimits_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/emobility/public_EVCurrentLimits_test.go b/emobility/public_EVCurrentLimits_test.go index 7dfc77b..7bd9c3d 100644 --- a/emobility/public_EVCurrentLimits_test.go +++ b/emobility/public_EVCurrentLimits_test.go @@ -86,7 +86,7 @@ func Test_EVCurrentLimits(t *testing.T) { { "1 Phase ISO15118", []permittedStruct{ - {true, 0.1, 0.1, 2, 2.2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, }, }, { @@ -104,9 +104,9 @@ func Test_EVCurrentLimits(t *testing.T) { { "3 Phase ISO15118", []permittedStruct{ - {true, 0.1, 0.1, 2, 2.2, 16, 16}, - {true, 0.1, 0.1, 2, 2.2, 16, 16}, - {true, 0.1, 0.1, 2, 2.2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, }, }, { From bc50955d0c32763181439e9e18c9c53763cab7e1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 21 Oct 2023 15:03:57 +0200 Subject: [PATCH 014/227] Minor refactoring --- emobility/emobility.go | 6 ++++-- emobility/events.go | 2 +- emobility/mock_emobility.go | 20 +++++++++---------- emobility/public.go | 6 +++--- ...> public_EVGetTimeSlotConstraints_test.go} | 10 +++++----- 5 files changed, 23 insertions(+), 21 deletions(-) rename emobility/{public_EVGetPowerConstraints_test.go => public_EVGetTimeSlotConstraints_test.go} (90%) diff --git a/emobility/emobility.go b/emobility/emobility.go index c215d26..357de35 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -8,6 +8,8 @@ import ( "github.com/enbility/eebus-go/util" ) +//go:generate mockgen -source emobility.go -destination mock_emobility.go -package emobility + // used by emobility and implemented by the CEM type EmobilityDataProvider interface { // The EV provided a charge strategy @@ -187,9 +189,9 @@ type EmobilityI interface { // if duration is 0, direct charging is active, otherwise timed charging is active EVEnergyDemand() (EVDemand, error) - // returns the constraints for the power slots + // returns the constraints for the time slots // - EVTimeSlotConstraints: details about the time slot constraints - EVGetPowerConstraints() EVTimeSlotConstraints + EVGetTimeSlotConstraints() EVTimeSlotConstraints // send power limits data to the EV // diff --git a/emobility/events.go b/emobility/events.go index bd7ba69..03dc1cb 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -123,7 +123,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { } // request CEM for power limits - constraints := e.EVGetPowerConstraints() + constraints := e.EVGetTimeSlotConstraints() if err != nil { logging.Log.Error("Error getting timeseries constraints:", err) } else { diff --git a/emobility/mock_emobility.go b/emobility/mock_emobility.go index 25a9b22..1e4f07a 100644 --- a/emobility/mock_emobility.go +++ b/emobility/mock_emobility.go @@ -268,18 +268,18 @@ func (mr *MockEmobilityIMockRecorder) EVGetIncentiveConstraints() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVGetIncentiveConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVGetIncentiveConstraints)) } -// EVGetPowerConstraints mocks base method. -func (m *MockEmobilityI) EVGetPowerConstraints() EVTimeSlotConstraints { +// EVGetTimeSlotConstraints mocks base method. +func (m *MockEmobilityI) EVGetTimeSlotConstraints() EVTimeSlotConstraints { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVGetPowerConstraints") + ret := m.ctrl.Call(m, "EVGetTimeSlotConstraints") ret0, _ := ret[0].(EVTimeSlotConstraints) return ret0 } -// EVGetPowerConstraints indicates an expected call of EVGetPowerConstraints. -func (mr *MockEmobilityIMockRecorder) EVGetPowerConstraints() *gomock.Call { +// EVGetTimeSlotConstraints indicates an expected call of EVGetTimeSlotConstraints. +func (mr *MockEmobilityIMockRecorder) EVGetTimeSlotConstraints() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVGetPowerConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVGetPowerConstraints)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVGetTimeSlotConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVGetTimeSlotConstraints)) } // EVIdentification mocks base method. @@ -387,17 +387,17 @@ func (mr *MockEmobilityIMockRecorder) EVWriteIncentives(data interface{}) *gomoc } // EVWriteLoadControlLimits mocks base method. -func (m *MockEmobilityI) EVWriteLoadControlLimits(obligations, recommendations []float64) error { +func (m *MockEmobilityI) EVWriteLoadControlLimits(limits []EVLoadLimits) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWriteLoadControlLimits", obligations, recommendations) + ret := m.ctrl.Call(m, "EVWriteLoadControlLimits", limits) ret0, _ := ret[0].(error) return ret0 } // EVWriteLoadControlLimits indicates an expected call of EVWriteLoadControlLimits. -func (mr *MockEmobilityIMockRecorder) EVWriteLoadControlLimits(obligations, recommendations interface{}) *gomock.Call { +func (mr *MockEmobilityIMockRecorder) EVWriteLoadControlLimits(limits interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteLoadControlLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteLoadControlLimits), obligations, recommendations) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteLoadControlLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteLoadControlLimits), limits) } // EVWritePowerLimits mocks base method. diff --git a/emobility/public.go b/emobility/public.go index 87e798c..803d70e 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -753,8 +753,8 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { return demand, nil } -// returns the constraints for the power slots -func (e *EMobilityImpl) EVGetPowerConstraints() EVTimeSlotConstraints { +// returns the constraints for the time slots +func (e *EMobilityImpl) EVGetTimeSlotConstraints() EVTimeSlotConstraints { result := EVTimeSlotConstraints{} if e.evEntity == nil || e.evTimeSeries == nil { @@ -804,7 +804,7 @@ func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { return errors.New("missing power limit data") } - constraints := e.EVGetPowerConstraints() + constraints := e.EVGetTimeSlotConstraints() if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { return errors.New("too few charge slots provided") diff --git a/emobility/public_EVGetPowerConstraints_test.go b/emobility/public_EVGetTimeSlotConstraints_test.go similarity index 90% rename from emobility/public_EVGetPowerConstraints_test.go rename to emobility/public_EVGetTimeSlotConstraints_test.go index 8fe7b26..dbd1321 100644 --- a/emobility/public_EVGetPowerConstraints_test.go +++ b/emobility/public_EVGetTimeSlotConstraints_test.go @@ -9,10 +9,10 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_EVGetPowerConstraints(t *testing.T) { +func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty, eebusService := setupEmobility() - constraints := emobilty.EVGetPowerConstraints() + constraints := emobilty.EVGetTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -23,7 +23,7 @@ func Test_EVGetPowerConstraints(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints = emobilty.EVGetPowerConstraints() + constraints = emobilty.EVGetTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -32,7 +32,7 @@ func Test_EVGetPowerConstraints(t *testing.T) { emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) - constraints = emobilty.EVGetPowerConstraints() + constraints = emobilty.EVGetTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -60,7 +60,7 @@ func Test_EVGetPowerConstraints(t *testing.T) { err := localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVGetPowerConstraints() + constraints = emobilty.EVGetTimeSlotConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) assert.Equal(t, time.Duration(1*time.Minute), constraints.MinSlotDuration) From ee11078ebf24a51425d6f74c4c8560b9d02eed0e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 29 Oct 2023 20:16:30 +0100 Subject: [PATCH 015/227] Update to latest eebus-go dev --- cem/cem.go | 2 +- emobility/emobility.go | 2 +- go.mod | 16 ++++---- go.sum | 62 ++++++++++++++++++++++--------- grid/grid.go | 2 +- inverterbatteryvis/invertervis.go | 2 +- inverterpvvis/invertervis.go | 2 +- 7 files changed, 58 insertions(+), 30 deletions(-) diff --git a/cem/cem.go b/cem/cem.go index e45af38..4a36021 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -38,7 +38,7 @@ func (h *CemImpl) Setup() error { return err } - spine.Events.Subscribe(h) + _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, h) return nil } diff --git a/emobility/emobility.go b/emobility/emobility.go index 357de35..13e36a4 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -259,7 +259,7 @@ func NewEMobility(service *service.EEBUSService, details *service.ServiceDetails evCurrentChargeStrategy: EVChargeStrategyTypeUnknown, configuration: configuration, } - spine.Events.Subscribe(emobility) + _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, emobility) service.RegisterRemoteSKI(ski, true) diff --git a/go.mod b/go.mod index ee1671d..0cc7188 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20230930121918-5134925e48e5 + github.com/enbility/eebus-go v0.0.0-20231029191406-3b80a7a18d41 github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.4 - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d ) require ( - github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f // indirect + github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -21,9 +21,11 @@ require ( github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.15.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// replace github.com/enbility/eebus-go => ../eebus-go diff --git a/go.sum b/go.sum index b4f187f..1b3bb61 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,21 @@ -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f h1:3+usd0uwuU/PnxDgKiQFc3FSR0C1PnV5Bk03NUKktaU= -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20230702124214-98a3b1bb316f/go.mod h1:Fhz5FR1v8Nv4Hj1v9oGXDz4C33+ZaAPqK+Y2wpfWvu0= +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df h1:qYVips8l0s1ldVoLSup0m+hYh5cMMA4ndcvocxhZuMc= +github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df/go.mod h1:OO5/UahoVBLyauLdDF4httPlSISqbrWDbHi9k99zUsc= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20230930121918-5134925e48e5 h1:lh17pW/rGdTmaKvUBYqQ6Rpj/uIHYWG0TN2r1qG0aJs= -github.com/enbility/eebus-go v0.0.0-20230930121918-5134925e48e5/go.mod h1:JyH+oCMEEyWlEip/zYDZViJVxQGpKW418TzrwVhSMbs= +github.com/enbility/eebus-go v0.0.0-20231029191406-3b80a7a18d41 h1:9iPIe2zs9vGOB8SGyNXnnZPAIXWIqdpV4KXx/bEXGHE= +github.com/enbility/eebus-go v0.0.0-20231029191406-3b80a7a18d41/go.mod h1:szcZ3J/iva5bwPEIA4B1d+ORKH+bF9ISwY8EaVciN6Y= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= @@ -30,45 +29,72 @@ github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/grid/grid.go b/grid/grid.go index 5110813..de0d5ab 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -42,7 +42,7 @@ func NewGrid(service *service.EEBUSService, details *service.ServiceDetails) *Gr entity: service.LocalEntity(), ski: ski, } - spine.Events.Subscribe(grid) + _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, grid) service.RegisterRemoteSKI(ski, true) diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index e043127..c887ec2 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -37,7 +37,7 @@ func NewInverterBatteryVis(service *service.EEBUSService, details *service.Servi entity: service.LocalEntity(), ski: ski, } - spine.Events.Subscribe(inverter) + _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, inverter) service.RegisterRemoteSKI(ski, true) diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index f17e8d9..18857f6 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -37,7 +37,7 @@ func NewInverterPVVis(service *service.EEBUSService, details *service.ServiceDet entity: service.LocalEntity(), ski: ski, } - spine.Events.Subscribe(inverter) + _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, inverter) service.RegisterRemoteSKI(ski, true) From 225126e3e680b2fc02c82be064ac8d192f467ba5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 29 Oct 2023 20:24:52 +0100 Subject: [PATCH 016/227] Remove direct linking of all scenarios into cem --- cem/cem.go | 76 ----------------------------------------------------- cmd/main.go | 27 +++++++++++++++---- 2 files changed, 22 insertions(+), 81 deletions(-) diff --git a/cem/cem.go b/cem/cem.go index 4a36021..78516d8 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -1,11 +1,6 @@ package cem import ( - "github.com/enbility/cemd/emobility" - "github.com/enbility/cemd/grid" - "github.com/enbility/cemd/inverterbatteryvis" - "github.com/enbility/cemd/inverterpvvis" - "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/spine" @@ -16,8 +11,6 @@ import ( type CemImpl struct { Service *service.EEBUSService - emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario scenarios.ScenariosI - Currency model.CurrencyType } @@ -43,32 +36,6 @@ func (h *CemImpl) Setup() error { return nil } -// Enable the supported usecases and features - -func (h *CemImpl) EnableEmobility(configuration emobility.EmobilityConfiguration) { - h.emobilityScenario = emobility.NewEMobilityScenario(h.Service, h.Currency, configuration) - h.emobilityScenario.AddFeatures() - h.emobilityScenario.AddUseCases() -} - -func (h *CemImpl) EnableGrid() { - h.gridScenario = grid.NewGridScenario(h.Service) - h.gridScenario.AddFeatures() - h.gridScenario.AddUseCases() -} - -func (h *CemImpl) EnableBatteryVisualization() { - h.inverterBatteryVisScenario = inverterbatteryvis.NewInverterVisScenario(h.Service) - h.inverterBatteryVisScenario.AddFeatures() - h.inverterBatteryVisScenario.AddUseCases() -} - -func (h *CemImpl) EnablePVVisualization() { - h.inverterPVVisScenario = inverterpvvis.NewInverterVisScenario(h.Service) - h.inverterPVVisScenario.AddFeatures() - h.inverterPVVisScenario.AddUseCases() -} - func (h *CemImpl) Start() { h.Service.Start() } @@ -76,46 +43,3 @@ func (h *CemImpl) Start() { func (h *CemImpl) Shutdown() { h.Service.Shutdown() } - -func (h *CemImpl) RegisterEmobilityRemoteDevice(details *service.ServiceDetails, dataProvider emobility.EmobilityDataProvider) *emobility.EMobilityImpl { - var impl any - - if dataProvider != nil { - impl = h.emobilityScenario.RegisterRemoteDevice(details, dataProvider) - } else { - impl = h.emobilityScenario.RegisterRemoteDevice(details, nil) - } - - return impl.(*emobility.EMobilityImpl) -} - -func (h *CemImpl) UnRegisterEmobilityRemoteDevice(remoteDeviceSki string) { - h.emobilityScenario.UnRegisterRemoteDevice(remoteDeviceSki) -} - -func (h *CemImpl) RegisterGridRemoteDevice(details *service.ServiceDetails) *grid.GridImpl { - impl := h.gridScenario.RegisterRemoteDevice(details, nil) - return impl.(*grid.GridImpl) -} - -func (h *CemImpl) UnRegisterGridRemoteDevice(remoteDeviceSki string) { - h.gridScenario.UnRegisterRemoteDevice(remoteDeviceSki) -} - -func (h *CemImpl) RegisterInverterBatteryVisRemoteDevice(details *service.ServiceDetails) *grid.GridImpl { - impl := h.inverterBatteryVisScenario.RegisterRemoteDevice(details, nil) - return impl.(*grid.GridImpl) -} - -func (h *CemImpl) UnRegisterInverterBatteryVisRemoteDevice(remoteDeviceSki string) { - h.inverterBatteryVisScenario.UnRegisterRemoteDevice(remoteDeviceSki) -} - -func (h *CemImpl) RegisterInverterPVVisRemoteDevice(details *service.ServiceDetails) *grid.GridImpl { - impl := h.inverterPVVisScenario.RegisterRemoteDevice(details, nil) - return impl.(*grid.GridImpl) -} - -func (h *CemImpl) UnRegisterInverterPVVisRemoteDevice(remoteDeviceSki string) { - h.inverterPVVisScenario.UnRegisterRemoteDevice(remoteDeviceSki) -} diff --git a/cmd/main.go b/cmd/main.go index cb2ca76..f48c4ec 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,6 +12,10 @@ import ( "github.com/enbility/cemd/cem" "github.com/enbility/cemd/emobility" + "github.com/enbility/cemd/grid" + "github.com/enbility/cemd/inverterbatteryvis" + "github.com/enbility/cemd/inverterpvvis" + "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/spine/model" @@ -19,6 +23,8 @@ import ( type DemoCem struct { cem *cem.CemImpl + + emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario scenarios.ScenariosI } func NewDemoCem(configuration *service.Configuration) *DemoCem { @@ -34,12 +40,23 @@ func (d *DemoCem) Setup() error { return err } - d.cem.EnableEmobility(emobility.EmobilityConfiguration{ + d.emobilityScenario = emobility.NewEMobilityScenario(d.cem.Service, d.cem.Currency, emobility.EmobilityConfiguration{ CoordinatedChargingEnabled: true, }) - d.cem.EnableGrid() - d.cem.EnableBatteryVisualization() - d.cem.EnablePVVisualization() + d.emobilityScenario.AddFeatures() + d.emobilityScenario.AddUseCases() + + d.gridScenario = grid.NewGridScenario(d.cem.Service) + d.gridScenario.AddFeatures() + d.gridScenario.AddUseCases() + + d.inverterBatteryVisScenario = inverterbatteryvis.NewInverterVisScenario(d.cem.Service) + d.inverterBatteryVisScenario.AddFeatures() + d.inverterBatteryVisScenario.AddUseCases() + + d.inverterPVVisScenario = inverterpvvis.NewInverterVisScenario(d.cem.Service) + d.inverterPVVisScenario.AddFeatures() + d.inverterPVVisScenario.AddUseCases() d.cem.Service.Start() @@ -162,7 +179,7 @@ func main() { } remoteService := service.NewServiceDetails(*remoteSki) - demo.cem.RegisterEmobilityRemoteDevice(remoteService, nil) + demo.emobilityScenario.RegisterRemoteDevice(remoteService, nil) // Clean exit to make sure mdns shutdown is invoked sig := make(chan os.Signal, 1) From fb61ccff5a8c417a16c3036ccac0676484bc5059 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 20 Nov 2023 16:17:06 +0100 Subject: [PATCH 017/227] Update EMobility - Renamed a few coordinated charging related methods - Added `EVChargePlan()` - Fixed IncentiveTable event handling --- emobility/emobility.go | 9 ++- emobility/events.go | 8 +-- emobility/mock_emobility.go | 71 +++++++++++-------- emobility/public.go | 64 +++++++++++++++-- ... => public_EVIncentiveConstraints_test.go} | 10 +-- ...o => public_EVTimeSlotConstraints_test.go} | 8 +-- emobility/types.go | 13 ++++ 7 files changed, 135 insertions(+), 48 deletions(-) rename emobility/{public_EVGetIncentiveConstraints_test.go => public_EVIncentiveConstraints_test.go} (88%) rename emobility/{public_EVGetTimeSlotConstraints_test.go => public_EVTimeSlotConstraints_test.go} (92%) diff --git a/emobility/emobility.go b/emobility/emobility.go index 13e36a4..3f86dc5 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -189,9 +189,14 @@ type EmobilityI interface { // if duration is 0, direct charging is active, otherwise timed charging is active EVEnergyDemand() (EVDemand, error) + // returns the current charge plan + // - EVChargePlan: details about the actual charge plan provided by the EV + // - error: if no data is available + EVChargePlan() (EVChargePlan, error) + // returns the constraints for the time slots // - EVTimeSlotConstraints: details about the time slot constraints - EVGetTimeSlotConstraints() EVTimeSlotConstraints + EVTimeSlotConstraints() EVTimeSlotConstraints // send power limits data to the EV // @@ -203,7 +208,7 @@ type EmobilityI interface { // returns the constraints for incentive slots // - EVIncentiveConstraints: details about the incentive slot constraints - EVGetIncentiveConstraints() EVIncentiveSlotConstraints + EVIncentiveConstraints() EVIncentiveSlotConstraints // send price slots data to the EV // diff --git a/emobility/events.go b/emobility/events.go index 03dc1cb..ffbf9f0 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -123,7 +123,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { } // request CEM for power limits - constraints := e.EVGetTimeSlotConstraints() + constraints := e.EVTimeSlotConstraints() if err != nil { logging.Log.Error("Error getting timeseries constraints:", err) } else { @@ -153,14 +153,14 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { // check if we received a plan e.evForwardChargePlanIfProvided() - case *model.IncentiveDescriptionDataType: + case *model.IncentiveTableDescriptionDataType: if e.evIncentiveTable == nil || payload.CmdClassifier == nil { break } switch *payload.CmdClassifier { case model.CmdClassifierTypeReply: - if err := e.evIncentiveTable.RequestConstraints(); err != nil { + if err := e.evIncentiveTable.RequestConstraints(); err == nil { break } @@ -179,7 +179,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { break } - constraints := e.EVGetIncentiveConstraints() + constraints := e.EVIncentiveConstraints() // request CEM for incentives e.dataProvider.EVRequestIncentives(demand, constraints) diff --git a/emobility/mock_emobility.go b/emobility/mock_emobility.go index 1e4f07a..f3cb36e 100644 --- a/emobility/mock_emobility.go +++ b/emobility/mock_emobility.go @@ -104,6 +104,21 @@ func (m *MockEmobilityI) EXPECT() *MockEmobilityIMockRecorder { return m.recorder } +// EVChargePlan mocks base method. +func (m *MockEmobilityI) EVChargePlan() (EVChargePlan, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVChargePlan") + ret0, _ := ret[0].(EVChargePlan) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVChargePlan indicates an expected call of EVChargePlan. +func (mr *MockEmobilityIMockRecorder) EVChargePlan() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargePlan", reflect.TypeOf((*MockEmobilityI)(nil).EVChargePlan)) +} + // EVChargeStrategy mocks base method. func (m *MockEmobilityI) EVChargeStrategy() EVChargeStrategyType { m.ctrl.T.Helper() @@ -254,34 +269,6 @@ func (mr *MockEmobilityIMockRecorder) EVEnergyDemand() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVEnergyDemand", reflect.TypeOf((*MockEmobilityI)(nil).EVEnergyDemand)) } -// EVGetIncentiveConstraints mocks base method. -func (m *MockEmobilityI) EVGetIncentiveConstraints() EVIncentiveSlotConstraints { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVGetIncentiveConstraints") - ret0, _ := ret[0].(EVIncentiveSlotConstraints) - return ret0 -} - -// EVGetIncentiveConstraints indicates an expected call of EVGetIncentiveConstraints. -func (mr *MockEmobilityIMockRecorder) EVGetIncentiveConstraints() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVGetIncentiveConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVGetIncentiveConstraints)) -} - -// EVGetTimeSlotConstraints mocks base method. -func (m *MockEmobilityI) EVGetTimeSlotConstraints() EVTimeSlotConstraints { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVGetTimeSlotConstraints") - ret0, _ := ret[0].(EVTimeSlotConstraints) - return ret0 -} - -// EVGetTimeSlotConstraints indicates an expected call of EVGetTimeSlotConstraints. -func (mr *MockEmobilityIMockRecorder) EVGetTimeSlotConstraints() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVGetTimeSlotConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVGetTimeSlotConstraints)) -} - // EVIdentification mocks base method. func (m *MockEmobilityI) EVIdentification() (string, error) { m.ctrl.T.Helper() @@ -297,6 +284,20 @@ func (mr *MockEmobilityIMockRecorder) EVIdentification() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIdentification", reflect.TypeOf((*MockEmobilityI)(nil).EVIdentification)) } +// EVIncentiveConstraints mocks base method. +func (m *MockEmobilityI) EVIncentiveConstraints() EVIncentiveSlotConstraints { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVIncentiveConstraints") + ret0, _ := ret[0].(EVIncentiveSlotConstraints) + return ret0 +} + +// EVIncentiveConstraints indicates an expected call of EVIncentiveConstraints. +func (mr *MockEmobilityIMockRecorder) EVIncentiveConstraints() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIncentiveConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVIncentiveConstraints)) +} + // EVLoadControlObligationLimits mocks base method. func (m *MockEmobilityI) EVLoadControlObligationLimits() ([]float64, error) { m.ctrl.T.Helper() @@ -372,6 +373,20 @@ func (mr *MockEmobilityIMockRecorder) EVSoCSupported() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoCSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVSoCSupported)) } +// EVTimeSlotConstraints mocks base method. +func (m *MockEmobilityI) EVTimeSlotConstraints() EVTimeSlotConstraints { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVTimeSlotConstraints") + ret0, _ := ret[0].(EVTimeSlotConstraints) + return ret0 +} + +// EVTimeSlotConstraints indicates an expected call of EVTimeSlotConstraints. +func (mr *MockEmobilityIMockRecorder) EVTimeSlotConstraints() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVTimeSlotConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVTimeSlotConstraints)) +} + // EVWriteIncentives mocks base method. func (m *MockEmobilityI) EVWriteIncentives(data []EVDurationSlotValue) error { m.ctrl.T.Helper() diff --git a/emobility/public.go b/emobility/public.go index 803d70e..5be6b3b 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -706,7 +706,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { return demand, features.ErrDataNotAvailable } - // we need at a time series slot + // we need at least a time series slot if data.TimeSeriesSlot == nil { return demand, features.ErrDataNotAvailable } @@ -753,8 +753,62 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { return demand, nil } +func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { + plan := EVChargePlan{} + + if e.evEntity == nil { + return plan, ErrEVDisconnected + } + + if e.evTimeSeries == nil { + return plan, features.ErrDataNotAvailable + } + + data, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) + if err != nil { + return plan, features.ErrDataNotAvailable + } + + // we need at least a time series slot + if data.TimeSeriesSlot == nil { + return plan, features.ErrDataNotAvailable + } + + // check the start time relative to now of the plan, default is now + if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { + plan.DurationUntilStart = 0.0 + + if start, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { + plan.DurationUntilStart = start + } + } + + // get the values for all slots + for _, slot := range data.TimeSeriesSlot { + newSlot := EVChargePlanSlotValue{} + + if duration, err := slot.Duration.GetTimeDuration(); err == nil { + newSlot.Duration = duration + } + + if slot.Value != nil { + newSlot.Value = slot.Value.GetValue() + } + if slot.MinValue != nil { + newSlot.MinValue = slot.MinValue.GetValue() + } + if slot.MaxValue != nil { + newSlot.MaxValue = slot.MaxValue.GetValue() + } + + plan.Slots = append(plan.Slots, newSlot) + } + + return plan, nil +} + // returns the constraints for the time slots -func (e *EMobilityImpl) EVGetTimeSlotConstraints() EVTimeSlotConstraints { +func (e *EMobilityImpl) EVTimeSlotConstraints() EVTimeSlotConstraints { result := EVTimeSlotConstraints{} if e.evEntity == nil || e.evTimeSeries == nil { @@ -804,7 +858,7 @@ func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { return errors.New("missing power limit data") } - constraints := e.EVGetTimeSlotConstraints() + constraints := e.EVTimeSlotConstraints() if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { return errors.New("too few charge slots provided") @@ -857,7 +911,7 @@ func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { } // returns the minimum and maximum number of incentive slots allowed -func (e *EMobilityImpl) EVGetIncentiveConstraints() EVIncentiveSlotConstraints { +func (e *EMobilityImpl) EVIncentiveConstraints() EVIncentiveSlotConstraints { result := EVIncentiveSlotConstraints{} if e.evEntity == nil || e.evIncentiveTable == nil { @@ -892,7 +946,7 @@ func (e *EMobilityImpl) EVWriteIncentives(data []EVDurationSlotValue) error { return errors.New("missing incentive data") } - constraints := e.EVGetIncentiveConstraints() + constraints := e.EVIncentiveConstraints() if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { return errors.New("too few charge slots provided") diff --git a/emobility/public_EVGetIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go similarity index 88% rename from emobility/public_EVGetIncentiveConstraints_test.go rename to emobility/public_EVIncentiveConstraints_test.go index 07f3f0b..4691367 100644 --- a/emobility/public_EVGetIncentiveConstraints_test.go +++ b/emobility/public_EVIncentiveConstraints_test.go @@ -11,7 +11,7 @@ import ( func Test_EVGetIncentiveConstraints(t *testing.T) { emobilty, eebusService := setupEmobility() - constraints := emobilty.EVGetIncentiveConstraints() + constraints := emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) @@ -19,13 +19,13 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints = emobilty.EVGetIncentiveConstraints() + constraints = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) emobilty.evIncentiveTable = incentiveTableConfiguration(localDevice, emobilty.evEntity) - constraints = emobilty.EVGetIncentiveConstraints() + constraints = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) @@ -48,7 +48,7 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { err := localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVGetIncentiveConstraints() + constraints = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) @@ -68,7 +68,7 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVGetIncentiveConstraints() + constraints = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) diff --git a/emobility/public_EVGetTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go similarity index 92% rename from emobility/public_EVGetTimeSlotConstraints_test.go rename to emobility/public_EVTimeSlotConstraints_test.go index dbd1321..7e3d0f8 100644 --- a/emobility/public_EVGetTimeSlotConstraints_test.go +++ b/emobility/public_EVTimeSlotConstraints_test.go @@ -12,7 +12,7 @@ import ( func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty, eebusService := setupEmobility() - constraints := emobilty.EVGetTimeSlotConstraints() + constraints := emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -23,7 +23,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints = emobilty.EVGetTimeSlotConstraints() + constraints = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -32,7 +32,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) - constraints = emobilty.EVGetTimeSlotConstraints() + constraints = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -60,7 +60,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { err := localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVGetTimeSlotConstraints() + constraints = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) assert.Equal(t, time.Duration(1*time.Minute), constraints.MinSlotDuration) diff --git a/emobility/types.go b/emobility/types.go index ca511a3..c8bc308 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -64,6 +64,19 @@ type EVDemand struct { DurationUntilEnd time.Duration // the duration from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active } +type EVChargePlan struct { + DurationUntilStart time.Duration // the duration from now until charging will start, this could be in the future but usualy is now + Slots []EVChargePlanSlotValue // Individual charging slot details +} + +// Contains details about a charging plan slot +type EVChargePlanSlotValue struct { + Duration time.Duration // Duration of the slot + Value float64 + MinValue float64 + MaxValue float64 +} + // Details about the time slot constraints type EVTimeSlotConstraints struct { MinSlots uint // the minimum number of slots, no minimum if 0 From 50a04fb15567e32f27cfc54722e7e55a889c1d5f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 20 Nov 2023 16:46:03 +0100 Subject: [PATCH 018/227] Use time instead of duration --- emobility/public.go | 31 +++++++++++++++++++++++++------ emobility/types.go | 22 +++++++++++----------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/emobility/public.go b/emobility/public.go index 5be6b3b..000da46 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -725,7 +725,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { } if firstSlot.Duration != nil { if tempDuration, err := firstSlot.Duration.GetTimeDuration(); err == nil { - demand.DurationUntilEnd = tempDuration + demand.End = time.Now().Add(tempDuration) } } @@ -748,7 +748,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { } } - demand.DurationUntilStart = relStartTime + demand.Start = time.Now().Add(relStartTime) return demand, nil } @@ -774,21 +774,40 @@ func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { return plan, features.ErrDataNotAvailable } + startAvailable := false // check the start time relative to now of the plan, default is now + currentStart := time.Now() + currentEnd := currentStart if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { - plan.DurationUntilStart = 0.0 if start, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { - plan.DurationUntilStart = start + currentStart = currentStart.Add(start) + startAvailable = true } } // get the values for all slots - for _, slot := range data.TimeSeriesSlot { + for index, slot := range data.TimeSeriesSlot { newSlot := EVChargePlanSlotValue{} + slotStartDefined := false + if index == 0 && startAvailable && (slot.TimePeriod == nil || slot.TimePeriod.StartTime == nil) { + newSlot.Start = currentStart + slotStartDefined = true + } + if slot.TimePeriod != nil && slot.TimePeriod.StartTime != nil { + if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { + newSlot.Start = time + slotStartDefined = true + } + } + if !slotStartDefined { + newSlot.Start = currentEnd + } + if duration, err := slot.Duration.GetTimeDuration(); err == nil { - newSlot.Duration = duration + newSlot.End = newSlot.Start.Add(duration) + currentEnd = newSlot.End } if slot.Value != nil { diff --git a/emobility/types.go b/emobility/types.go index c8bc308..b7ca2d2 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -57,24 +57,24 @@ type EVLoadLimits struct { // - If duration is 0, charge mode is EVChargeStrategyTypeDirectCharging and the slots should cover at least 48h // - If both are != 0, charge mode is EVChargeStrategyTypeTimedCharging and the slots should cover at least the duration, but at max 168h (7d) type EVDemand struct { - MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set - OptDemand float64 // demand in Wh to reach the timer SoC setting - MaxDemand float64 // the maximum possible demand until the battery is full - DurationUntilStart time.Duration // the duration from now until charging will start, this could be in the future but usualy is now - DurationUntilEnd time.Duration // the duration from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active + MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set + OptDemand float64 // demand in Wh to reach the timer SoC setting + MaxDemand float64 // the maximum possible demand until the battery is full + Start time.Time // the time when charging will start, this could be in the future but usualy is now + End time.Time // the time when minDemand or optDemand has to be reached, 0 if direct charge strategy is active } type EVChargePlan struct { - DurationUntilStart time.Duration // the duration from now until charging will start, this could be in the future but usualy is now - Slots []EVChargePlanSlotValue // Individual charging slot details + Slots []EVChargePlanSlotValue // Individual charging slot details } // Contains details about a charging plan slot type EVChargePlanSlotValue struct { - Duration time.Duration // Duration of the slot - Value float64 - MinValue float64 - MaxValue float64 + Start time.Time // The start time of the slot + End time.Time // The duration of the slot + Value float64 // planned power value + MinValue float64 // minimum power value + MaxValue float64 // maximum power value } // Details about the time slot constraints From 11d4713f062682d744085258e5490e376cf7b716 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 20 Nov 2023 16:46:50 +0100 Subject: [PATCH 019/227] Simplify source and destination for heartbeat --- cem/events.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cem/events.go b/cem/events.go index 05d0d0c..ee9c84a 100644 --- a/cem/events.go +++ b/cem/events.go @@ -32,17 +32,10 @@ func (h *CemImpl) subscriptionRequestHandling(payload spine.EventPayload) { return } - senderAddr := h.Service.LocalDevice().FeatureByTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer).Address() - destinationAddr := payload.Feature.Address() - if senderAddr == nil || destinationAddr == nil { - logging.Log.Info("No sender or destination address found for SKI:", payload.Ski) - return - } - switch payload.ChangeType { case spine.ElementChangeAdd: // start sending heartbeats - remoteDevice.StartHeartbeatSend(senderAddr, destinationAddr) + remoteDevice.StartHeartbeatSend(data.ServerAddress, data.ClientAddress) case spine.ElementChangeRemove: // stop sending heartbeats remoteDevice.Stopheartbeat() From 6cba02d1a4eef4dee121c0db6b47921f434209f6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 20 Nov 2023 17:17:07 +0100 Subject: [PATCH 020/227] Keep duration --- emobility/public.go | 4 ++-- emobility/types.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/emobility/public.go b/emobility/public.go index 000da46..e4d4c31 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -725,7 +725,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { } if firstSlot.Duration != nil { if tempDuration, err := firstSlot.Duration.GetTimeDuration(); err == nil { - demand.End = time.Now().Add(tempDuration) + demand.DurationUntilEnd = tempDuration } } @@ -748,7 +748,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { } } - demand.Start = time.Now().Add(relStartTime) + demand.DurationUntilStart = relStartTime return demand, nil } diff --git a/emobility/types.go b/emobility/types.go index b7ca2d2..6559084 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -57,11 +57,11 @@ type EVLoadLimits struct { // - If duration is 0, charge mode is EVChargeStrategyTypeDirectCharging and the slots should cover at least 48h // - If both are != 0, charge mode is EVChargeStrategyTypeTimedCharging and the slots should cover at least the duration, but at max 168h (7d) type EVDemand struct { - MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set - OptDemand float64 // demand in Wh to reach the timer SoC setting - MaxDemand float64 // the maximum possible demand until the battery is full - Start time.Time // the time when charging will start, this could be in the future but usualy is now - End time.Time // the time when minDemand or optDemand has to be reached, 0 if direct charge strategy is active + MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set + OptDemand float64 // demand in Wh to reach the timer SoC setting + MaxDemand float64 // the maximum possible demand until the battery is full + DurationUntilStart time.Duration // the duration from now until charging will start, this could be in the future but usualy is now + DurationUntilEnd time.Duration // the duration from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active } type EVChargePlan struct { From 987bf5bb77206f448e4d62d3df1a46b8624777ac Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 09:00:41 +0100 Subject: [PATCH 021/227] Update event subscription --- cem/cem.go | 2 +- emobility/emobility.go | 2 +- grid/grid.go | 2 +- inverterbatteryvis/invertervis.go | 2 +- inverterpvvis/invertervis.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cem/cem.go b/cem/cem.go index 78516d8..e1963a8 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -31,7 +31,7 @@ func (h *CemImpl) Setup() error { return err } - _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, h) + _ = spine.Events.Subscribe(h) return nil } diff --git a/emobility/emobility.go b/emobility/emobility.go index 3f86dc5..8e5e122 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -264,7 +264,7 @@ func NewEMobility(service *service.EEBUSService, details *service.ServiceDetails evCurrentChargeStrategy: EVChargeStrategyTypeUnknown, configuration: configuration, } - _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, emobility) + _ = spine.Events.Subscribe(emobility) service.RegisterRemoteSKI(ski, true) diff --git a/grid/grid.go b/grid/grid.go index de0d5ab..3297bbb 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -42,7 +42,7 @@ func NewGrid(service *service.EEBUSService, details *service.ServiceDetails) *Gr entity: service.LocalEntity(), ski: ski, } - _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, grid) + _ = spine.Events.Subscribe(grid) service.RegisterRemoteSKI(ski, true) diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index c887ec2..c5a83ae 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -37,7 +37,7 @@ func NewInverterBatteryVis(service *service.EEBUSService, details *service.Servi entity: service.LocalEntity(), ski: ski, } - _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, inverter) + _ = spine.Events.Subscribe(inverter) service.RegisterRemoteSKI(ski, true) diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 18857f6..8623721 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -37,7 +37,7 @@ func NewInverterPVVis(service *service.EEBUSService, details *service.ServiceDet entity: service.LocalEntity(), ski: ski, } - _ = spine.Events.Subscribe(spine.EventHandlerLevelMiddleware, inverter) + _ = spine.Events.Subscribe(inverter) service.RegisterRemoteSKI(ski, true) From b8fb2e73fb3c625138d76bd44854f8c5f7c64ae6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 13:03:13 +0100 Subject: [PATCH 022/227] Rework and fix coordinated charging --- emobility/emobility.go | 12 ++- emobility/events.go | 87 ++++++++++++++----- emobility/mock_emobility.go | 22 ++++- emobility/public.go | 28 +++--- .../public_EVIncentiveConstraints_test.go | 17 ++-- .../public_EVTimeSlotConstraints_test.go | 14 +-- emobility/types.go | 1 + 7 files changed, 133 insertions(+), 48 deletions(-) diff --git a/emobility/emobility.go b/emobility/emobility.go index 8e5e122..8fa6360 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -15,6 +15,12 @@ type EmobilityDataProvider interface { // The EV provided a charge strategy EVProvidedChargeStrategy(strategy EVChargeStrategyType) + // EV provided an energy demand + // + // Parameters: + // - demand: Contains details about the actual demands from the EV + EVProvidedEnergyDemand(demand EVDemand) + // Energy demand and duration is provided by the EV which requires the CEM // to respond with time slots containing power limits for each slot // @@ -196,7 +202,8 @@ type EmobilityI interface { // returns the constraints for the time slots // - EVTimeSlotConstraints: details about the time slot constraints - EVTimeSlotConstraints() EVTimeSlotConstraints + // - error: if no data is available + EVTimeSlotConstraints() (EVTimeSlotConstraints, error) // send power limits data to the EV // @@ -208,7 +215,8 @@ type EmobilityI interface { // returns the constraints for incentive slots // - EVIncentiveConstraints: details about the incentive slot constraints - EVIncentiveConstraints() EVIncentiveSlotConstraints + // - error: if no data is available + EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) // send price slots data to the EV // diff --git a/emobility/events.go b/emobility/events.go index ffbf9f0..4ade777 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -1,6 +1,8 @@ package emobility import ( + "time" + "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/spine" @@ -122,16 +124,30 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { break } - // request CEM for power limits - constraints := e.EVTimeSlotConstraints() + if e.dataProvider != nil { + e.dataProvider.EVProvidedEnergyDemand(demand) + } + + timeConstraints, err := e.EVTimeSlotConstraints() if err != nil { logging.Log.Error("Error getting timeseries constraints:", err) - } else { - if e.dataProvider == nil { - break - } - e.dataProvider.EVRequestPowerLimits(demand, constraints) + break + } + + incentiveConstraints, err := e.EVIncentiveConstraints() + if err != nil { + logging.Log.Error("Error getting incentive constraints:", err) + break + } + + if e.dataProvider != nil { + e.dataProvider.EVRequestPowerLimits(demand, timeConstraints) + e.dataProvider.EVRequestIncentives(demand, incentiveConstraints) + break } + + e.evWriteDefaultIncentives() + e.evWriteDefaultPowerLimits() } case *model.TimeSeriesConstraintsListDataType: @@ -169,20 +185,11 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { case model.CmdClassifierTypeNotify: // check if we are required to update the plan - if e.dataProvider == nil || !e.evCheckIncentiveTableDescriptionUpdateRequired() { - break - } - - demand, err := e.EVEnergyDemand() - if err != nil { - logging.Log.Error("Error getting energy demand:", err) + if !e.evCheckIncentiveTableDescriptionUpdateRequired() { break } - constraints := e.EVIncentiveConstraints() - - // request CEM for incentives - e.dataProvider.EVRequestIncentives(demand, constraints) + e.evWriteIncentiveTableDescriptions() } case *model.IncentiveTableConstraintsDataType: @@ -208,6 +215,44 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { e.dataProvider.EVProvidedChargeStrategy(chargeStrategy) } +func (e *EMobilityImpl) evWriteDefaultIncentives() { + // send default incentives for the maximum timeframe + // to fullfill spec, as there is no data provided + logging.Log.Info("Fallback sending default incentives") + data := []EVDurationSlotValue{ + {Duration: 7 * time.Hour * 24, Value: 0.30}, + } + _ = e.EVWriteIncentives(data) +} + +func (e *EMobilityImpl) evWriteDefaultPowerLimits() { + // send default power limits for the maximum timeframe + // to fullfill spec, as there is no data provided + logging.Log.Info("Fallback sending default power limits") + + paramDesc, err := e.evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) + if err != nil { + logging.Log.Error("Error getting parameter descriptions:", err) + return + } + + permitted, err := e.evElectricalConnection.GetPermittedValueSetForParameterId(*paramDesc.ParameterId) + if err != nil { + logging.Log.Error("Error getting permitted values:", err) + return + } + + if len(permitted.PermittedValueSet) < 1 || len(permitted.PermittedValueSet[0].Range) < 1 { + logging.Log.Error("No permitted value set available") + return + } + + data := []EVDurationSlotValue{ + {Duration: 7 * time.Hour * 24, Value: permitted.PermittedValueSet[0].Range[0].Max.GetValue()}, + } + _ = e.EVWritePowerLimits(data) +} + // request time series values func (e *EMobilityImpl) evRequestTimeSeriesValues() { if e.evTimeSeries == nil { @@ -221,6 +266,10 @@ func (e *EMobilityImpl) evRequestTimeSeriesValues() { // send the ev provided charge plan to the CEM func (e *EMobilityImpl) evForwardChargePlanIfProvided() { + if e.dataProvider == nil { + return + } + if data, err := e.evGetTimeSeriesPlanData(); err == nil { e.dataProvider.EVProvidedChargePlan(data) } @@ -277,8 +326,6 @@ func (e *EMobilityImpl) evRequestIncentiveValues() { if _, err := e.evIncentiveTable.RequestValues(); err != nil { logging.Log.Error("Error getting time series list values:", err) } - - e.evWriteIncentiveTableDescriptions() } // process required steps when an evse is connected diff --git a/emobility/mock_emobility.go b/emobility/mock_emobility.go index f3cb36e..29fcea3 100644 --- a/emobility/mock_emobility.go +++ b/emobility/mock_emobility.go @@ -57,6 +57,18 @@ func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargeStrategy(strate return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargeStrategy", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargeStrategy), strategy) } +// EVProvidedEnergyDemand mocks base method. +func (m *MockEmobilityDataProvider) EVProvidedEnergyDemand(demand EVDemand) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVProvidedEnergyDemand", demand) +} + +// EVProvidedEnergyDemand indicates an expected call of EVProvidedEnergyDemand. +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedEnergyDemand(demand interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedEnergyDemand", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedEnergyDemand), demand) +} + // EVRequestIncentives mocks base method. func (m *MockEmobilityDataProvider) EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) { m.ctrl.T.Helper() @@ -285,11 +297,12 @@ func (mr *MockEmobilityIMockRecorder) EVIdentification() *gomock.Call { } // EVIncentiveConstraints mocks base method. -func (m *MockEmobilityI) EVIncentiveConstraints() EVIncentiveSlotConstraints { +func (m *MockEmobilityI) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EVIncentiveConstraints") ret0, _ := ret[0].(EVIncentiveSlotConstraints) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // EVIncentiveConstraints indicates an expected call of EVIncentiveConstraints. @@ -374,11 +387,12 @@ func (mr *MockEmobilityIMockRecorder) EVSoCSupported() *gomock.Call { } // EVTimeSlotConstraints mocks base method. -func (m *MockEmobilityI) EVTimeSlotConstraints() EVTimeSlotConstraints { +func (m *MockEmobilityI) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EVTimeSlotConstraints") ret0, _ := ret[0].(EVTimeSlotConstraints) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // EVTimeSlotConstraints indicates an expected call of EVTimeSlotConstraints. diff --git a/emobility/public.go b/emobility/public.go index e4d4c31..d8c77b0 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -827,16 +827,16 @@ func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { } // returns the constraints for the time slots -func (e *EMobilityImpl) EVTimeSlotConstraints() EVTimeSlotConstraints { +func (e *EMobilityImpl) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { result := EVTimeSlotConstraints{} if e.evEntity == nil || e.evTimeSeries == nil { - return result + return result, features.ErrDataNotAvailable } constraints, err := e.evTimeSeries.GetConstraints() if err != nil { - return result + return result, err } // only use the first constraint @@ -864,7 +864,7 @@ func (e *EMobilityImpl) EVTimeSlotConstraints() EVTimeSlotConstraints { } } - return result + return result, nil } // send power limits to the EV @@ -877,7 +877,10 @@ func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { return errors.New("missing power limit data") } - constraints := e.EVTimeSlotConstraints() + constraints, err := e.EVTimeSlotConstraints() + if err != nil { + return err + } if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { return errors.New("too few charge slots provided") @@ -930,16 +933,16 @@ func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { } // returns the minimum and maximum number of incentive slots allowed -func (e *EMobilityImpl) EVIncentiveConstraints() EVIncentiveSlotConstraints { +func (e *EMobilityImpl) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { result := EVIncentiveSlotConstraints{} if e.evEntity == nil || e.evIncentiveTable == nil { - return result + return result, features.ErrDataNotAvailable } constraints, err := e.evIncentiveTable.GetConstraints() if err != nil { - return result + return result, err } // only use the first constraint @@ -952,7 +955,7 @@ func (e *EMobilityImpl) EVIncentiveConstraints() EVIncentiveSlotConstraints { result.MaxSlots = uint(*constraint.IncentiveSlotConstraints.SlotCountMax) } - return result + return result, nil } // send incentives to the EV @@ -965,7 +968,10 @@ func (e *EMobilityImpl) EVWriteIncentives(data []EVDurationSlotValue) error { return errors.New("missing incentive data") } - constraints := e.EVIncentiveConstraints() + constraints, err := e.EVIncentiveConstraints() + if err != nil { + return err + } if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { return errors.New("too few charge slots provided") @@ -1028,7 +1034,7 @@ func (e *EMobilityImpl) EVWriteIncentives(data []EVDurationSlotValue) error { IncentiveSlot: incentiveSlots, } - _, err := e.evIncentiveTable.WriteValues([]model.IncentiveTableType{incentiveData}) + _, err = e.evIncentiveTable.WriteValues([]model.IncentiveTableType{incentiveData}) return err } diff --git a/emobility/public_EVIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go index 4691367..b763af8 100644 --- a/emobility/public_EVIncentiveConstraints_test.go +++ b/emobility/public_EVIncentiveConstraints_test.go @@ -11,23 +11,26 @@ import ( func Test_EVGetIncentiveConstraints(t *testing.T) { emobilty, eebusService := setupEmobility() - constraints := emobilty.EVIncentiveConstraints() + constraints, err := emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) + assert.NotEqual(t, err, nil) localDevice, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) + assert.NotEqual(t, err, nil) emobilty.evIncentiveTable = incentiveTableConfiguration(localDevice, emobilty.evEntity) - constraints = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) + assert.NotEqual(t, err, nil) datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) @@ -45,12 +48,13 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { datagram.Payload.Cmd = cmd - err := localDevice.ProcessCmd(datagram, remoteDevice) + err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) + assert.Equal(t, err, nil) cmd = []model.CmdType{{ IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ @@ -68,8 +72,9 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) + assert.Equal(t, err, nil) } diff --git a/emobility/public_EVTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go index 7e3d0f8..97c5b77 100644 --- a/emobility/public_EVTimeSlotConstraints_test.go +++ b/emobility/public_EVTimeSlotConstraints_test.go @@ -12,32 +12,35 @@ import ( func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty, eebusService := setupEmobility() - constraints := emobilty.EVTimeSlotConstraints() + constraints, err := emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) assert.Equal(t, time.Duration(0), constraints.MaxSlotDuration) assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) + assert.NotEqual(t, err, nil) localDevice, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints = emobilty.EVTimeSlotConstraints() + constraints, err = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) assert.Equal(t, time.Duration(0), constraints.MaxSlotDuration) assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) + assert.NotEqual(t, err, nil) emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) - constraints = emobilty.EVTimeSlotConstraints() + constraints, err = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) assert.Equal(t, time.Duration(0), constraints.MaxSlotDuration) assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) + assert.NotEqual(t, err, nil) datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) @@ -57,13 +60,14 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { datagram.Payload.Cmd = cmd - err := localDevice.ProcessCmd(datagram, remoteDevice) + err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints = emobilty.EVTimeSlotConstraints() + constraints, err = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) assert.Equal(t, time.Duration(1*time.Minute), constraints.MinSlotDuration) assert.Equal(t, time.Duration(1*time.Hour), constraints.MaxSlotDuration) assert.Equal(t, time.Duration(1*time.Minute), constraints.SlotDurationStepSize) + assert.Equal(t, err, nil) } diff --git a/emobility/types.go b/emobility/types.go index 6559084..aff42f1 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -64,6 +64,7 @@ type EVDemand struct { DurationUntilEnd time.Duration // the duration from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active } +// Contains details about an EV generated charging plan type EVChargePlan struct { Slots []EVChargePlanSlotValue // Individual charging slot details } From 84dbed2472115b02b9b8610eebf9aa3ca550d7c3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 13:03:21 +0100 Subject: [PATCH 023/227] Fix a possible crash --- util/helper.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/helper.go b/util/helper.go index b2f7b04..3c48469 100644 --- a/util/helper.go +++ b/util/helper.go @@ -30,6 +30,10 @@ func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorT func EntityOfTypeForSki(service *service.EEBUSService, entityType model.EntityTypeType, ski string) (*spine.EntityRemoteImpl, error) { rDevice := service.RemoteDeviceForSki(ski) + if rDevice == nil { + return nil, features.ErrEntityNotFound + } + entities := rDevice.Entities() for _, entity := range entities { if entity.EntityType() == entityType { From dcc725f7fb6dff4050ea1e3f2d364794df9db053 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 18:51:41 +0100 Subject: [PATCH 024/227] Update charge plans und constraints support --- emobility/emobility.go | 5 ++- emobility/events.go | 8 +++-- emobility/mock_emobility.go | 20 ++++++++--- emobility/public.go | 71 ++++++++++++++++++++++++++++++++++--- emobility/types.go | 10 +++--- 5 files changed, 97 insertions(+), 17 deletions(-) diff --git a/emobility/emobility.go b/emobility/emobility.go index 8fa6360..e64b0df 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -42,7 +42,10 @@ type EmobilityDataProvider interface { EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) // The EV provided a charge plan - EVProvidedChargePlan(data []EVDurationSlotValue) + EVProvidedChargePlan(plan EVChargePlan) + + // The EV provided charge plan constraints + EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) } // used by the CEM and implemented by emobility diff --git a/emobility/events.go b/emobility/events.go index 4ade777..01b1bc8 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -270,8 +270,12 @@ func (e *EMobilityImpl) evForwardChargePlanIfProvided() { return } - if data, err := e.evGetTimeSeriesPlanData(); err == nil { - e.dataProvider.EVProvidedChargePlan(data) + if plan, err := e.EVChargePlan(); err == nil { + e.dataProvider.EVProvidedChargePlan(plan) + } + + if constraints, err := e.EVChargePlanConstraints(); err == nil { + e.dataProvider.EVProvidedChargePlanConstraints(constraints) } } diff --git a/emobility/mock_emobility.go b/emobility/mock_emobility.go index 29fcea3..99c18f7 100644 --- a/emobility/mock_emobility.go +++ b/emobility/mock_emobility.go @@ -34,15 +34,27 @@ func (m *MockEmobilityDataProvider) EXPECT() *MockEmobilityDataProviderMockRecor } // EVProvidedChargePlan mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargePlan(data []EVDurationSlotValue) { +func (m *MockEmobilityDataProvider) EVProvidedChargePlan(plan EVChargePlan) { m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargePlan", data) + m.ctrl.Call(m, "EVProvidedChargePlan", plan) } // EVProvidedChargePlan indicates an expected call of EVProvidedChargePlan. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlan(data interface{}) *gomock.Call { +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlan(plan interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlan", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlan), data) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlan", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlan), plan) +} + +// EVProvidedChargePlanConstraints mocks base method. +func (m *MockEmobilityDataProvider) EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVProvidedChargePlanConstraints", constraints) +} + +// EVProvidedChargePlanConstraints indicates an expected call of EVProvidedChargePlanConstraints. +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlanConstraints(constraints interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlanConstraints", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlanConstraints), constraints) } // EVProvidedChargeStrategy mocks base method. diff --git a/emobility/public.go b/emobility/public.go index d8c77b0..e60ab91 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -725,7 +725,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { } if firstSlot.Duration != nil { if tempDuration, err := firstSlot.Duration.GetTimeDuration(); err == nil { - demand.DurationUntilEnd = tempDuration + demand.DurationUntilEnd = tempDuration.Seconds() } } @@ -748,11 +748,65 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { } } - demand.DurationUntilStart = relStartTime + demand.DurationUntilStart = relStartTime.Seconds() return demand, nil } +func (e *EMobilityImpl) EVChargePlanConstraints() ([]EVDurationSlotValue, error) { + constraints := []EVDurationSlotValue{} + + if e.evEntity == nil { + return constraints, ErrEVDisconnected + } + + if e.evTimeSeries == nil { + return constraints, features.ErrDataNotAvailable + } + + data, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeConstraints) + if err != nil { + return constraints, features.ErrDataNotAvailable + } + + // we need at least a time series slot + if data.TimeSeriesSlot == nil { + return constraints, features.ErrDataNotAvailable + } + + // get the values for all slots + for _, slot := range data.TimeSeriesSlot { + newSlot := EVDurationSlotValue{} + + if slot.Duration != nil { + if duration, err := slot.Duration.GetTimeDuration(); err == nil { + newSlot.Duration = duration + } + } else if slot.TimePeriod != nil { + var slotStart, slotEnd time.Time + if slot.TimePeriod.StartTime != nil { + if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { + slotStart = time + } + } + if slot.TimePeriod.EndTime != nil { + if time, err := slot.TimePeriod.EndTime.GetTime(); err == nil { + slotEnd = time + } + } + newSlot.Duration = slotEnd.Sub(slotStart) + } + + if slot.MaxValue != nil { + newSlot.Value = slot.MaxValue.GetValue() + } + + constraints = append(constraints, newSlot) + } + + return constraints, nil +} + func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { plan := EVChargePlan{} @@ -805,9 +859,16 @@ func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { newSlot.Start = currentEnd } - if duration, err := slot.Duration.GetTimeDuration(); err == nil { - newSlot.End = newSlot.Start.Add(duration) - currentEnd = newSlot.End + if slot.Duration != nil { + if duration, err := slot.Duration.GetTimeDuration(); err == nil { + newSlot.End = newSlot.Start.Add(duration) + currentEnd = newSlot.End + } + } else if slot.TimePeriod != nil && slot.TimePeriod.EndTime != nil { + if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { + newSlot.End = time + currentEnd = newSlot.End + } } if slot.Value != nil { diff --git a/emobility/types.go b/emobility/types.go index aff42f1..ecad530 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -57,11 +57,11 @@ type EVLoadLimits struct { // - If duration is 0, charge mode is EVChargeStrategyTypeDirectCharging and the slots should cover at least 48h // - If both are != 0, charge mode is EVChargeStrategyTypeTimedCharging and the slots should cover at least the duration, but at max 168h (7d) type EVDemand struct { - MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set - OptDemand float64 // demand in Wh to reach the timer SoC setting - MaxDemand float64 // the maximum possible demand until the battery is full - DurationUntilStart time.Duration // the duration from now until charging will start, this could be in the future but usualy is now - DurationUntilEnd time.Duration // the duration from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active + MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set + OptDemand float64 // demand in Wh to reach the timer SoC setting + MaxDemand float64 // the maximum possible demand until the battery is full + DurationUntilStart float64 // the duration in s from now until charging will start, this could be in the future but usualy is now + DurationUntilEnd float64 // the duration in s from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active } // Contains details about an EV generated charging plan From b81b59fd4b58973b644d2a26c05f7cae4ea68e54 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 22 Nov 2023 19:31:36 +0100 Subject: [PATCH 025/227] Some cleanup --- emobility/events.go | 42 ------------------- ...ta_test.go => public_EVChargePlan_test.go} | 18 +++----- 2 files changed, 6 insertions(+), 54 deletions(-) rename emobility/{events_evGetTimeSeriesPlanData_test.go => public_EVChargePlan_test.go} (87%) diff --git a/emobility/events.go b/emobility/events.go index 01b1bc8..c29348e 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -279,48 +279,6 @@ func (e *EMobilityImpl) evForwardChargePlanIfProvided() { } } -func (e *EMobilityImpl) evGetTimeSeriesPlanData() ([]EVDurationSlotValue, error) { - if e.evTimeSeries == nil || e.dataProvider == nil { - return nil, ErrNotSupported - } - - timeSeries, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) - if err != nil { - return nil, err - } - - if len(timeSeries.TimeSeriesSlot) == 0 { - return nil, ErrNotSupported - } - - var data []EVDurationSlotValue - - for _, slot := range timeSeries.TimeSeriesSlot { - duration, err := slot.Duration.GetTimeDuration() - if err != nil { - logging.Log.Error("ev charge plan contains invalid duration:", err) - return nil, err - } - - if slot.MaxValue == nil { - continue - } - - item := EVDurationSlotValue{ - Duration: duration, - Value: slot.MaxValue.GetValue(), - } - - data = append(data, item) - } - - if len(data) == 0 { - return nil, ErrNotSupported - } - - return data, nil -} - // request incentive table values func (e *EMobilityImpl) evRequestIncentiveValues() { if e.evIncentiveTable == nil { diff --git a/emobility/events_evGetTimeSeriesPlanData_test.go b/emobility/public_EVChargePlan_test.go similarity index 87% rename from emobility/events_evGetTimeSeriesPlanData_test.go rename to emobility/public_EVChargePlan_test.go index 75ab384..96bafea 100644 --- a/emobility/events_evGetTimeSeriesPlanData_test.go +++ b/emobility/public_EVChargePlan_test.go @@ -9,12 +9,11 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_evGetTimeSeriesPlanData(t *testing.T) { +func Test_EVChargePlan(t *testing.T) { emobilty, eebusService := setupEmobility() - data, err := emobilty.evGetTimeSeriesPlanData() + _, err := emobilty.EVChargePlan() assert.NotNil(t, err) - assert.Nil(t, data) localDevice, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] @@ -25,15 +24,13 @@ func Test_evGetTimeSeriesPlanData(t *testing.T) { dataProviderMock := NewMockEmobilityDataProvider(ctrl) emobilty.dataProvider = dataProviderMock - data, err = emobilty.evGetTimeSeriesPlanData() + _, err = emobilty.EVChargePlan() assert.NotNil(t, err) - assert.Nil(t, data) emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) - data, err = emobilty.evGetTimeSeriesPlanData() + _, err = emobilty.EVChargePlan() assert.NotNil(t, err) - assert.Nil(t, data) datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) @@ -67,9 +64,8 @@ func Test_evGetTimeSeriesPlanData(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.evGetTimeSeriesPlanData() + _, err = emobilty.EVChargePlan() assert.NotNil(t, err) - assert.Nil(t, data) cmd = []model.CmdType{{ TimeSeriesListData: &model.TimeSeriesListDataType{ @@ -100,8 +96,6 @@ func Test_evGetTimeSeriesPlanData(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.evGetTimeSeriesPlanData() + _, err = emobilty.EVChargePlan() assert.Nil(t, err) - assert.NotNil(t, data) - } From b431ed175936cf074395333da2a4337493344529 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 29 Dec 2023 22:09:24 +0100 Subject: [PATCH 026/227] Major updates - Adhere to latest eebus-go API changes - Remove CEM event handling, as heartbeats are now handled by eebus-go --- cem/cem.go | 3 - cem/events.go | 43 --------------- cmd/main.go | 4 +- emobility/emobility.go | 4 +- emobility/evcoordinatedcharging_test.go | 22 ++++---- emobility/events.go | 24 ++++---- emobility/helper_test.go | 55 +++++++++++-------- emobility/public_EVChargePlan_test.go | 6 +- emobility/public_EVChargeStrategy_test.go | 10 ++-- emobility/public_EVChargedEnergy_test.go | 6 +- .../public_EVCommunicationStandard_test.go | 6 +- emobility/public_EVConnectedPhases_test.go | 6 +- ...lic_EVCoordinatedChargingSupported_test.go | 4 +- emobility/public_EVCurrentChargeState_test.go | 6 +- emobility/public_EVCurrentLimits_test.go | 6 +- emobility/public_EVCurrentsPerPhase_test.go | 10 ++-- emobility/public_EVEnergyDemand_test.go | 50 ++++++++--------- emobility/public_EVIdentification_test.go | 6 +- .../public_EVIncentiveConstraints_test.go | 6 +- ...mizationOfSelfConsumptionSupported_test.go | 8 +-- emobility/public_EVPowerPerPhase_test.go | 20 +++---- emobility/public_EVSoCSupported_test.go | 8 +-- emobility/public_EVSoC_test.go | 12 ++-- .../public_EVTimeSlotConstraints_test.go | 6 +- emobility/public_EVWriteIncentives_test.go | 8 +-- .../public_EVWriteLoadControlLimits_test.go | 12 ++-- emobility/public_EVWritePowerLimits_test.go | 8 +-- emobility/results.go | 21 +++---- emobility/scenario.go | 4 +- go.mod | 5 +- go.sum | 6 +- grid/events.go | 7 ++- grid/grid.go | 5 +- grid/scenario.go | 4 +- inverterbatteryvis/events.go | 5 +- inverterbatteryvis/invertervis.go | 5 +- inverterbatteryvis/scenario.go | 4 +- inverterpvvis/events.go | 7 ++- inverterpvvis/invertervis.go | 5 +- inverterpvvis/scenario.go | 4 +- util/helper.go | 2 +- 41 files changed, 212 insertions(+), 231 deletions(-) delete mode 100644 cem/events.go diff --git a/cem/cem.go b/cem/cem.go index e1963a8..062c23f 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -3,7 +3,6 @@ package cem import ( "github.com/enbility/eebus-go/logging" "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" ) @@ -31,8 +30,6 @@ func (h *CemImpl) Setup() error { return err } - _ = spine.Events.Subscribe(h) - return nil } diff --git a/cem/events.go b/cem/events.go deleted file mode 100644 index ee9c84a..0000000 --- a/cem/events.go +++ /dev/null @@ -1,43 +0,0 @@ -package cem - -import ( - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" -) - -// Handle events from eebus-go library -func (h *CemImpl) HandleEvent(payload spine.EventPayload) { - switch payload.EventType { - case spine.EventTypeSubscriptionChange: - switch payload.Data.(type) { - case model.SubscriptionManagementRequestCallType: - h.subscriptionRequestHandling(payload) - } - } -} - -// Handle subscription requests -func (h *CemImpl) subscriptionRequestHandling(payload spine.EventPayload) { - data := payload.Data.(model.SubscriptionManagementRequestCallType) - - // Heartbeat subscription requests? - if *data.ServerFeatureType != model.FeatureTypeTypeDeviceDiagnosis { - return - } - - remoteDevice := h.Service.RemoteDeviceForSki(payload.Ski) - if remoteDevice == nil { - logging.Log.Info("No remote device found for SKI:", payload.Ski) - return - } - - switch payload.ChangeType { - case spine.ElementChangeAdd: - // start sending heartbeats - remoteDevice.StartHeartbeatSend(data.ServerAddress, data.ClientAddress) - case spine.ElementChangeRemove: - // stop sending heartbeats - remoteDevice.Stopheartbeat() - } -} diff --git a/cmd/main.go b/cmd/main.go index f48c4ec..3a24a06 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -158,9 +158,11 @@ func main() { "HEMS", "123456789", model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, *port, certificate, - 230) + 230, + time.Second*4) if err != nil { fmt.Println("Service data is invalid:", err) return diff --git a/emobility/emobility.go b/emobility/emobility.go index e64b0df..fbd9c1b 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -266,9 +266,11 @@ var _ EmobilityI = (*EMobilityImpl)(nil) func NewEMobility(service *service.EEBUSService, details *service.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { ski := util.NormalizeSKI(details.SKI) + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + emobility := &EMobilityImpl{ service: service, - entity: service.LocalEntity(), + entity: localEntity, ski: ski, currency: currency, dataProvider: dataProvider, diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index a2f8c0b..f8f65e8 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -18,7 +18,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobility.evseEntity = entites[0] emobility.evEntity = entites[1] @@ -27,11 +27,11 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { dataProviderMock := NewMockEmobilityDataProvider(ctrl) emobility.dataProvider = dataProviderMock - emobility.evTimeSeries = timeSeriesConfiguration(localDevice, emobility.evEntity) - emobility.evIncentiveTable = incentiveTableConfiguration(localDevice, emobility.evEntity) + emobility.evTimeSeries = timeSeriesConfiguration(localEntity, emobility.evEntity) + emobility.evIncentiveTable = incentiveTableConfiguration(localEntity, emobility.evEntity) - datagramtt := datagramForEntityAndFeatures(false, localDevice, emobility.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - datagramit := datagramForEntityAndFeatures(false, localDevice, emobility.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) + datagramtt := datagramForEntityAndFeatures(false, localDevice, localEntity, emobility.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagramit := datagramForEntityAndFeatures(false, localDevice, localEntity, emobility.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) setupTimeSeries(t, datagramtt, localDevice, remoteDevice) setupIncentiveTable(t, datagramit, localDevice, remoteDevice) @@ -68,8 +68,8 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 74690.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) // the final plan @@ -146,8 +146,8 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 53400.0, demand.OptDemand) assert.Equal(t, 74690.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(time.Hour*52+time.Minute*40+time.Second*36), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, time.Duration(time.Hour*52+time.Minute*40+time.Second*36).Seconds(), demand.DurationUntilEnd) // the final plan @@ -240,8 +240,8 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.Equal(t, 600.0, demand.MinDemand) assert.Equal(t, 600.0, demand.OptDemand) assert.Equal(t, 75600.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) // the final plan diff --git a/emobility/events.go b/emobility/events.go index c29348e..b1744ed 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -294,14 +294,15 @@ func (e *EMobilityImpl) evRequestIncentiveValues() { func (e *EMobilityImpl) evseConnected(ski string, entity *spine.EntityRemoteImpl) { e.evseEntity = entity localDevice := e.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - f1, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f1, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } e.evseDeviceClassification = f1 - f2, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f2, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } @@ -348,20 +349,21 @@ func (e *EMobilityImpl) evDisconnected() { func (e *EMobilityImpl) evConnected(entity *spine.EntityRemoteImpl) { e.evEntity = entity localDevice := e.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) logging.Log.Debug("ev connected") // setup features - e.evDeviceClassification, _ = features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evDeviceDiagnosis, _ = features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evDeviceConfiguration, _ = features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evElectricalConnection, _ = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evMeasurement, _ = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evIdentification, _ = features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evLoadControl, _ = features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + e.evDeviceClassification, _ = features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evDeviceDiagnosis, _ = features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evDeviceConfiguration, _ = features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evElectricalConnection, _ = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evMeasurement, _ = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evIdentification, _ = features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evLoadControl, _ = features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if e.configuration.CoordinatedChargingEnabled { - e.evTimeSeries, _ = features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) - e.evIncentiveTable, _ = features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + e.evTimeSeries, _ = features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) + e.evIncentiveTable, _ = features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) } // optional requests are only logged as debug diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 596f04b..0624925 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "sync" + "time" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" @@ -96,9 +97,10 @@ const remoteSki string = "testremoteski" func NewTestEMobility(service *service.EEBUSService, details *service.ServiceDetails) *EMobilityImpl { ski := util.NormalizeSKI(details.SKI) + localEntity := service.LocalDevice().Entity([]model.AddressEntityType{1}) emobility := &EMobilityImpl{ service: service, - entity: service.LocalEntity(), + entity: localEntity, ski: ski, } @@ -109,7 +111,11 @@ func NewTestEMobility(service *service.EEBUSService, details *service.ServiceDet func setupEmobility() (*EMobilityImpl, *service.EEBUSService) { cert, _ := service.CreateCertificate("test", "test", "DE", "test") - configuration, _ := service.NewConfiguration("test", "test", "test", "test", model.DeviceTypeTypeEnergyManagementSystem, 9999, cert, 230.0) + configuration, _ := service.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) eebusService := service.NewEEBUSService(configuration, nil) _ = eebusService.Setup() details := service.NewServiceDetails(remoteSki) @@ -117,9 +123,9 @@ func setupEmobility() (*EMobilityImpl, *service.EEBUSService) { return emobility, eebusService } -func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, *spine.DeviceRemoteImpl, []*spine.EntityRemoteImpl, *WriteMessageHandler) { +func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, *spine.EntityLocalImpl, *spine.DeviceRemoteImpl, []*spine.EntityRemoteImpl, *WriteMessageHandler) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.Entities()[1] + localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) localDevice.AddEntity(localEntity) f := spine.NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) @@ -140,7 +146,8 @@ func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, * localEntity.AddFeature(f) writeHandler := &WriteMessageHandler{} - remoteDevice := spine.NewDeviceRemoteImpl(localDevice, remoteSki, writeHandler) + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemoteImpl(localDevice, remoteSki, sender) var clientRemoteFeatures = []struct { featureType model.FeatureTypeType @@ -265,10 +272,10 @@ func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, * fmt.Println(err) } - return localDevice, remoteDevice, entities, writeHandler + return localDevice, localEntity, remoteDevice, entities, writeHandler } -func datagramForEntityAndFeatures(notify bool, localDevice *spine.DeviceLocalImpl, remoteEntity *spine.EntityRemoteImpl, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { +func datagramForEntityAndFeatures(notify bool, localDevice *spine.DeviceLocalImpl, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { var addressSource, addressDestination *model.FeatureAddressType if remoteEntity == nil { // NodeManagement @@ -285,7 +292,7 @@ func datagramForEntityAndFeatures(notify bool, localDevice *spine.DeviceLocalImp rFeature := featureOfTypeAndRole(remoteEntity, featureType, remoteRole) addressSource = rFeature.Address() - lFeature := localDevice.FeatureByTypeAndRole(featureType, localRole) + lFeature := localEntity.FeatureOfTypeAndRole(featureType, localRole) addressDestination = lFeature.Address() } datagram := model.DatagramType{ @@ -316,64 +323,64 @@ func featureOfTypeAndRole(entity *spine.EntityRemoteImpl, featureType model.Feat return nil } -func deviceDiagnosis(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.DeviceDiagnosis { - feature, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func deviceDiagnosis(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.DeviceDiagnosis { + feature, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func electricalConnection(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.ElectricalConnection { - feature, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func electricalConnection(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.ElectricalConnection { + feature, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func measurement(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.Measurement { - feature, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func measurement(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.Measurement { + feature, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func deviceConfiguration(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.DeviceConfiguration { - feature, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func deviceConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.DeviceConfiguration { + feature, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func identificationConfiguration(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.Identification { - feature, err := features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func identificationConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.Identification { + feature, err := features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func loadcontrol(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.LoadControl { - feature, err := features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func loadcontrol(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.LoadControl { + feature, err := features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func timeSeriesConfiguration(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.TimeSeries { - feature, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func timeSeriesConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.TimeSeries { + feature, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } return feature } -func incentiveTableConfiguration(localDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) *features.IncentiveTable { - feature, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) +func incentiveTableConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.IncentiveTable { + feature, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) } diff --git a/emobility/public_EVChargePlan_test.go b/emobility/public_EVChargePlan_test.go index 96bafea..ae16b29 100644 --- a/emobility/public_EVChargePlan_test.go +++ b/emobility/public_EVChargePlan_test.go @@ -15,7 +15,7 @@ func Test_EVChargePlan(t *testing.T) { _, err := emobilty.EVChargePlan() assert.NotNil(t, err) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -27,12 +27,12 @@ func Test_EVChargePlan(t *testing.T) { _, err = emobilty.EVChargePlan() assert.NotNil(t, err) - emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) + emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) _, err = emobilty.EVChargePlan() assert.NotNil(t, err) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ diff --git a/emobility/public_EVChargeStrategy_test.go b/emobility/public_EVChargeStrategy_test.go index dc593f9..8e94c76 100644 --- a/emobility/public_EVChargeStrategy_test.go +++ b/emobility/public_EVChargeStrategy_test.go @@ -15,19 +15,19 @@ func Test_EVChargeStrategy(t *testing.T) { data := emobilty.EVChargeStrategy() assert.Equal(t, EVChargeStrategyTypeUnknown, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] data = emobilty.EVChargeStrategy() assert.Equal(t, EVChargeStrategyTypeUnknown, data) - emobilty.evDeviceConfiguration = deviceConfiguration(localDevice, emobilty.evEntity) + emobilty.evDeviceConfiguration = deviceConfiguration(localEntity, emobilty.evEntity) data = emobilty.EVChargeStrategy() assert.Equal(t, EVChargeStrategyTypeUnknown, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ @@ -65,12 +65,12 @@ func Test_EVChargeStrategy(t *testing.T) { data = emobilty.EVChargeStrategy() assert.Equal(t, EVChargeStrategyTypeUnknown, data) - emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) + emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) data = emobilty.EVChargeStrategy() assert.Equal(t, EVChargeStrategyTypeUnknown, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ diff --git a/emobility/public_EVChargedEnergy_test.go b/emobility/public_EVChargedEnergy_test.go index dfede96..91f8ff9 100644 --- a/emobility/public_EVChargedEnergy_test.go +++ b/emobility/public_EVChargedEnergy_test.go @@ -15,7 +15,7 @@ func Test_EVChargedEnergy(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVChargedEnergy(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - emobilty.evMeasurement = measurement(localDevice, emobilty.evEntity) + emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) data, err = emobilty.EVChargedEnergy() assert.NotNil(t, err) assert.Equal(t, 0.0, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ diff --git a/emobility/public_EVCommunicationStandard_test.go b/emobility/public_EVCommunicationStandard_test.go index 9bfdf1b..0bde89c 100644 --- a/emobility/public_EVCommunicationStandard_test.go +++ b/emobility/public_EVCommunicationStandard_test.go @@ -15,7 +15,7 @@ func Test_EVCommunicationStandard(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVCommunicationStandard(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - emobilty.evDeviceConfiguration = deviceConfiguration(localDevice, emobilty.evEntity) + emobilty.evDeviceConfiguration = deviceConfiguration(localEntity, emobilty.evEntity) data, err = emobilty.EVCommunicationStandard() assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ diff --git a/emobility/public_EVConnectedPhases_test.go b/emobility/public_EVConnectedPhases_test.go index 1f24f80..51d4ee9 100644 --- a/emobility/public_EVConnectedPhases_test.go +++ b/emobility/public_EVConnectedPhases_test.go @@ -15,7 +15,7 @@ func Test_EVConnectedPhases(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, uint(0), data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVConnectedPhases(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, uint(0), data) - emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) + emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) data, err = emobilty.EVConnectedPhases() assert.NotNil(t, err) assert.Equal(t, uint(0), data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ ElectricalConnectionDescriptionListData: &model.ElectricalConnectionDescriptionListDataType{ diff --git a/emobility/public_EVCoordinatedChargingSupported_test.go b/emobility/public_EVCoordinatedChargingSupported_test.go index cd8b1a4..b45b71a 100644 --- a/emobility/public_EVCoordinatedChargingSupported_test.go +++ b/emobility/public_EVCoordinatedChargingSupported_test.go @@ -15,7 +15,7 @@ func Test_EVCoordinatedChargingSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,7 +23,7 @@ func Test_EVCoordinatedChargingSupported(t *testing.T) { assert.Nil(t, err) assert.Equal(t, false, data) - datagram := datagramForEntityAndFeatures(true, localDevice, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) + datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) cmd := []model.CmdType{{ NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ diff --git a/emobility/public_EVCurrentChargeState_test.go b/emobility/public_EVCurrentChargeState_test.go index 7fa723c..3b99afb 100644 --- a/emobility/public_EVCurrentChargeState_test.go +++ b/emobility/public_EVCurrentChargeState_test.go @@ -15,7 +15,7 @@ func Test_EVCurrentChargeState(t *testing.T) { assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeUnplugged, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVCurrentChargeState(t *testing.T) { assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeUnplugged, data) - emobilty.evDeviceDiagnosis = deviceDiagnosis(localDevice, emobilty.evEntity) + emobilty.evDeviceDiagnosis = deviceDiagnosis(localEntity, emobilty.evEntity) data, err = emobilty.EVCurrentChargeState() assert.NotNil(t, err) assert.Equal(t, EVChargeStateTypeUnknown, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ DeviceDiagnosisStateData: &model.DeviceDiagnosisStateDataType{ diff --git a/emobility/public_EVCurrentLimits_test.go b/emobility/public_EVCurrentLimits_test.go index 7bd9c3d..548688e 100644 --- a/emobility/public_EVCurrentLimits_test.go +++ b/emobility/public_EVCurrentLimits_test.go @@ -17,7 +17,7 @@ func Test_EVCurrentLimits(t *testing.T) { assert.Nil(t, maxData) assert.Nil(t, defaultData) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -27,7 +27,7 @@ func Test_EVCurrentLimits(t *testing.T) { assert.Nil(t, maxData) assert.Nil(t, defaultData) - emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) + emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) minData, maxData, defaultData, err = emobilty.EVCurrentLimits() assert.NotNil(t, err) @@ -35,7 +35,7 @@ func Test_EVCurrentLimits(t *testing.T) { assert.Nil(t, maxData) assert.Nil(t, defaultData) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ diff --git a/emobility/public_EVCurrentsPerPhase_test.go b/emobility/public_EVCurrentsPerPhase_test.go index eeeb407..a56b429 100644 --- a/emobility/public_EVCurrentsPerPhase_test.go +++ b/emobility/public_EVCurrentsPerPhase_test.go @@ -15,7 +15,7 @@ func Test_EVCurrentsPerPhase(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,14 +23,14 @@ func Test_EVCurrentsPerPhase(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) - emobilty.evMeasurement = measurement(localDevice, emobilty.evEntity) + emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) + emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) data, err = emobilty.EVCurrentsPerPhase() assert.NotNil(t, err) assert.Nil(t, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ @@ -54,7 +54,7 @@ func Test_EVCurrentsPerPhase(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ diff --git a/emobility/public_EVEnergyDemand_test.go b/emobility/public_EVEnergyDemand_test.go index 82b6af8..b45f765 100644 --- a/emobility/public_EVEnergyDemand_test.go +++ b/emobility/public_EVEnergyDemand_test.go @@ -17,10 +17,10 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -29,20 +29,20 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) - emobilty.evDeviceConfiguration = deviceConfiguration(localDevice, emobilty.evEntity) + emobilty.evDeviceConfiguration = deviceConfiguration(localEntity, emobilty.evEntity) demand, err = emobilty.EVEnergyDemand() assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ @@ -63,8 +63,8 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) cmd = []model.CmdType{{ DeviceConfigurationKeyValueListData: &model.DeviceConfigurationKeyValueListDataType{ @@ -87,20 +87,20 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) - emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) + emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) demand, err = emobilty.EVEnergyDemand() assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ @@ -136,8 +136,8 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) cmd = []model.CmdType{{ TimeSeriesListData: &model.TimeSeriesListDataType{ @@ -166,8 +166,8 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) cmd = []model.CmdType{{ TimeSeriesListData: &model.TimeSeriesListDataType{ @@ -199,8 +199,8 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 1000.0, demand.MinDemand) assert.Equal(t, 10000.0, demand.OptDemand) assert.Equal(t, 100000.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(0), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, 0.0, demand.DurationUntilEnd) cmd = []model.CmdType{{ TimeSeriesListData: &model.TimeSeriesListDataType{ @@ -231,6 +231,6 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 10000.0, demand.OptDemand) assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, time.Duration(0), demand.DurationUntilStart) - assert.Equal(t, time.Duration(2*time.Hour), demand.DurationUntilEnd) + assert.Equal(t, 0.0, demand.DurationUntilStart) + assert.Equal(t, time.Duration(2*time.Hour).Seconds(), demand.DurationUntilEnd) } diff --git a/emobility/public_EVIdentification_test.go b/emobility/public_EVIdentification_test.go index a2ea394..75f17d9 100644 --- a/emobility/public_EVIdentification_test.go +++ b/emobility/public_EVIdentification_test.go @@ -15,7 +15,7 @@ func Test_EVIdentification(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, "", data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVIdentification(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, "", data) - emobilty.evIdentification = identificationConfiguration(localDevice, emobilty.evEntity) + emobilty.evIdentification = identificationConfiguration(localEntity, emobilty.evEntity) data, err = emobilty.EVIdentification() assert.NotNil(t, err) assert.Equal(t, "", data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeIdentification, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIdentification, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ IdentificationListData: &model.IdentificationListDataType{ diff --git a/emobility/public_EVIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go index b763af8..8662720 100644 --- a/emobility/public_EVIncentiveConstraints_test.go +++ b/emobility/public_EVIncentiveConstraints_test.go @@ -16,7 +16,7 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { assert.Equal(t, uint(0), constraints.MaxSlots) assert.NotEqual(t, err, nil) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -25,14 +25,14 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { assert.Equal(t, uint(0), constraints.MaxSlots) assert.NotEqual(t, err, nil) - emobilty.evIncentiveTable = incentiveTableConfiguration(localDevice, emobilty.evEntity) + emobilty.evIncentiveTable = incentiveTableConfiguration(localEntity, emobilty.evEntity) constraints, err = emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.NotEqual(t, err, nil) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ diff --git a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go index 3370d39..7a6128d 100644 --- a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go +++ b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go @@ -15,7 +15,7 @@ func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - emobilty.evLoadControl = loadcontrol(localDevice, emobilty.evEntity) + emobilty.evLoadControl = loadcontrol(localEntity, emobilty.evEntity) data, err = emobilty.EVOptimizationOfSelfConsumptionSupported() assert.Nil(t, err) assert.Equal(t, false, data) - datagram := datagramForEntityAndFeatures(true, localDevice, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) + datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) cmd := []model.CmdType{{ NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ @@ -55,7 +55,7 @@ func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ LoadControlLimitDescriptionListData: &model.LoadControlLimitDescriptionListDataType{ diff --git a/emobility/public_EVPowerPerPhase_test.go b/emobility/public_EVPowerPerPhase_test.go index ef1f7c8..faf6b24 100644 --- a/emobility/public_EVPowerPerPhase_test.go +++ b/emobility/public_EVPowerPerPhase_test.go @@ -15,7 +15,7 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,14 +23,14 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) - emobilty.evMeasurement = measurement(localDevice, emobilty.evEntity) + emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) + emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) data, err = emobilty.EVPowerPerPhase() assert.NotNil(t, err) assert.Nil(t, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ @@ -54,7 +54,7 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ @@ -103,7 +103,7 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -111,14 +111,14 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) - emobilty.evMeasurement = measurement(localDevice, emobilty.evEntity) + emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) + emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) data, err = emobilty.EVPowerPerPhase() assert.NotNil(t, err) assert.Nil(t, data) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ @@ -142,7 +142,7 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { assert.NotNil(t, err) assert.Nil(t, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ diff --git a/emobility/public_EVSoCSupported_test.go b/emobility/public_EVSoCSupported_test.go index d4a1ad8..39d1844 100644 --- a/emobility/public_EVSoCSupported_test.go +++ b/emobility/public_EVSoCSupported_test.go @@ -15,7 +15,7 @@ func Test_EVSoCSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVSoCSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - emobilty.evMeasurement = measurement(localDevice, emobilty.evEntity) + emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) data, err = emobilty.EVSoCSupported() assert.Nil(t, err) assert.Equal(t, false, data) - datagram := datagramForEntityAndFeatures(true, localDevice, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) + datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) cmd := []model.CmdType{{ NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ @@ -55,7 +55,7 @@ func Test_EVSoCSupported(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, false, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ diff --git a/emobility/public_EVSoC_test.go b/emobility/public_EVSoC_test.go index dee20db..7249de5 100644 --- a/emobility/public_EVSoC_test.go +++ b/emobility/public_EVSoC_test.go @@ -15,7 +15,7 @@ func Test_EVSoC(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -23,13 +23,13 @@ func Test_EVSoC(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - emobilty.evMeasurement = measurement(localDevice, emobilty.evEntity) + emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) data, err = emobilty.EVSoC() assert.NotNil(t, err) assert.Equal(t, 0.0, data) - datagram := datagramForEntityAndFeatures(true, localDevice, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) + datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) cmd := []model.CmdType{{ NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ @@ -55,7 +55,7 @@ func Test_EVSoC(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ @@ -77,7 +77,7 @@ func Test_EVSoC(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementListData: &model.MeasurementListDataType{ @@ -96,7 +96,7 @@ func Test_EVSoC(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, 0.0, data) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ MeasurementListData: &model.MeasurementListDataType{ diff --git a/emobility/public_EVTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go index 97c5b77..cf6faf4 100644 --- a/emobility/public_EVTimeSlotConstraints_test.go +++ b/emobility/public_EVTimeSlotConstraints_test.go @@ -20,7 +20,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) assert.NotEqual(t, err, nil) - localDevice, remoteDevice, entites, _ := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] @@ -32,7 +32,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) assert.NotEqual(t, err, nil) - emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) + emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) constraints, err = emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) @@ -42,7 +42,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) assert.NotEqual(t, err, nil) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ diff --git a/emobility/public_EVWriteIncentives_test.go b/emobility/public_EVWriteIncentives_test.go index 69d171e..ebab206 100644 --- a/emobility/public_EVWriteIncentives_test.go +++ b/emobility/public_EVWriteIncentives_test.go @@ -18,19 +18,19 @@ func Test_EVWriteIncentives(t *testing.T) { err := emobilty.EVWriteIncentives(data) assert.NotNil(t, err) - localDevice, remoteDevice, entites, writeHandler := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] err = emobilty.EVWriteIncentives(data) assert.NotNil(t, err) - emobilty.evIncentiveTable = incentiveTableConfiguration(localDevice, emobilty.evEntity) + emobilty.evIncentiveTable = incentiveTableConfiguration(localEntity, emobilty.evEntity) err = emobilty.EVWriteIncentives(data) assert.NotNil(t, err) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ @@ -111,7 +111,7 @@ func Test_EVWriteIncentives(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { for _, data := range tc.data { - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ diff --git a/emobility/public_EVWriteLoadControlLimits_test.go b/emobility/public_EVWriteLoadControlLimits_test.go index b240660..579adba 100644 --- a/emobility/public_EVWriteLoadControlLimits_test.go +++ b/emobility/public_EVWriteLoadControlLimits_test.go @@ -19,20 +19,20 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err := emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) - localDevice, remoteDevice, entites, writeHandler := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) - emobilty.evElectricalConnection = electricalConnection(localDevice, emobilty.evEntity) - emobilty.evLoadControl = loadcontrol(localDevice, emobilty.evEntity) + emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) + emobilty.evLoadControl = loadcontrol(localEntity, emobilty.evEntity) err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ @@ -153,7 +153,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { dataSet = append(dataSet, permittedItem) } - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ ElectricalConnectionPermittedValueSetListData: &model.ElectricalConnectionPermittedValueSetListDataType{ @@ -167,7 +167,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = emobilty.EVWriteLoadControlLimits(loadLimits) assert.NotNil(t, err) - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) limitDesc := []model.LoadControlLimitDescriptionDataType{} var limitIdsObligation, limitIdsRecommendation []model.LoadControlLimitIdType diff --git a/emobility/public_EVWritePowerLimits_test.go b/emobility/public_EVWritePowerLimits_test.go index 06a8c30..a3e236e 100644 --- a/emobility/public_EVWritePowerLimits_test.go +++ b/emobility/public_EVWritePowerLimits_test.go @@ -18,19 +18,19 @@ func Test_EVWritePowerLimits(t *testing.T) { err := emobilty.EVWritePowerLimits(data) assert.NotNil(t, err) - localDevice, remoteDevice, entites, writeHandler := setupDevices(eebusService) + localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] err = emobilty.EVWritePowerLimits(data) assert.NotNil(t, err) - emobilty.evTimeSeries = timeSeriesConfiguration(localDevice, emobilty.evEntity) + emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) err = emobilty.EVWritePowerLimits(data) assert.NotNil(t, err) - datagram := datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) cmd := []model.CmdType{{ TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ @@ -109,7 +109,7 @@ func Test_EVWritePowerLimits(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { for _, data := range tc.data { - datagram = datagramForEntityAndFeatures(false, localDevice, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) + datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) cmd = []model.CmdType{{ TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ diff --git a/emobility/results.go b/emobility/results.go index 3a7d6ca..b57f4b9 100644 --- a/emobility/results.go +++ b/emobility/results.go @@ -6,15 +6,11 @@ import ( ) func (e *EMobilityImpl) HandleResult(errorMsg spine.ResultMessage) { - if errorMsg.EntityRemote == e.evseEntity { - // handle errors coming from the remote EVSE entity - switch errorMsg.FeatureLocal.Type() { - case model.FeatureTypeTypeDeviceDiagnosis: - e.handleResultDeviceDiagnosis(errorMsg) - } + isEvse := errorMsg.EntityRemote == e.evseEntity + isEv := e.evEntity != nil && errorMsg.EntityRemote == e.evEntity - } else if e.evEntity != nil && errorMsg.EntityRemote == e.evEntity { - // handle errors coming from a remote EV entity + if isEvse || isEv { + // handle errors coming from the remote EVSE entity switch errorMsg.FeatureLocal.Type() { case model.FeatureTypeTypeDeviceDiagnosis: e.handleResultDeviceDiagnosis(errorMsg) @@ -30,9 +26,14 @@ func (e *EMobilityImpl) handleResultDeviceDiagnosis(resultMsg spine.ResultMessag return } - if resultMsg.DeviceRemote.IsHeartbeatMsgCounter(resultMsg.MsgCounterReference) { - resultMsg.DeviceRemote.Stopheartbeat() + // check if this is for a cached notify message + datagram, err := resultMsg.DeviceRemote.Sender().DatagramForMsgCounter(resultMsg.MsgCounterReference) + if err != nil { + return + } + if len(datagram.Payload.Cmd) > 0 && + datagram.Payload.Cmd[0].DeviceDiagnosisHeartbeatData != nil { // something is horribly wrong, disconnect and hope a new connection will fix it e.service.DisconnectSKI(resultMsg.DeviceRemote.Ski(), string(*resultMsg.Result.Description)) } diff --git a/emobility/scenario.go b/emobility/scenario.go index ed75659..b294b2b 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -34,7 +34,7 @@ func NewEMobilityScenario(service *service.EEBUSService, currency model.Currency // adds all the supported features to the local entity func (e *EmobilityScenarioImpl) AddFeatures() { - localEntity := e.Service.LocalEntity() + localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // server features { @@ -74,7 +74,7 @@ func (e *EmobilityScenarioImpl) AddFeatures() { // add supported e-mobility usecases func (e *EmobilityScenarioImpl) AddUseCases() { - localEntity := e.Service.LocalEntity() + localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) _ = spine.NewUseCase( localEntity, diff --git a/go.mod b/go.mod index 0cc7188..f1c5cb5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20231029191406-3b80a7a18d41 + github.com/enbility/eebus-go v0.0.0-20231229202420-a56f83821f31 github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20231006140011-7918f672742d @@ -15,6 +15,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/holoplot/go-avahi v1.0.1 // indirect github.com/miekg/dns v1.1.56 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -27,5 +28,3 @@ require ( golang.org/x/tools v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// replace github.com/enbility/eebus-go => ../eebus-go diff --git a/go.sum b/go.sum index 1b3bb61..7c2115c 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20231029191406-3b80a7a18d41 h1:9iPIe2zs9vGOB8SGyNXnnZPAIXWIqdpV4KXx/bEXGHE= -github.com/enbility/eebus-go v0.0.0-20231029191406-3b80a7a18d41/go.mod h1:szcZ3J/iva5bwPEIA4B1d+ORKH+bF9ISwY8EaVciN6Y= +github.com/enbility/eebus-go v0.0.0-20231229202420-a56f83821f31 h1:0UzjHC5XixsFO5bhVh1RngY9oaPdyfDv4uzcMoujj90= +github.com/enbility/eebus-go v0.0.0-20231229202420-a56f83821f31/go.mod h1:ZNtZZzOM8+BDpY434U7ZRwwjnPFfa5BlAmt6ds5OTN8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -14,6 +14,8 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= diff --git a/grid/events.go b/grid/events.go index e263de6..d7515b0 100644 --- a/grid/events.go +++ b/grid/events.go @@ -67,20 +67,21 @@ func (e *GridImpl) HandleEvent(payload spine.EventPayload) { func (e *GridImpl) gridConnected(ski string, entity *spine.EntityRemoteImpl) { e.gridEntity = entity localDevice := e.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - f1, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f1, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } e.gridDeviceConfiguration = f1 - f2, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f2, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } e.gridElectricalConnection = f2 - f3, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f3, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } diff --git a/grid/grid.go b/grid/grid.go index 3297bbb..3341cb9 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -4,6 +4,7 @@ import ( "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) @@ -37,9 +38,11 @@ var _ GridI = (*GridImpl)(nil) func NewGrid(service *service.EEBUSService, details *service.ServiceDetails) *GridImpl { ski := util.NormalizeSKI(details.SKI) + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + grid := &GridImpl{ service: service, - entity: service.LocalEntity(), + entity: localEntity, ski: ski, } _ = spine.Events.Subscribe(grid) diff --git a/grid/scenario.go b/grid/scenario.go index b7d5c5e..4e20410 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -28,7 +28,7 @@ func NewGridScenario(service *service.EEBUSService) *GridScenarioImpl { // adds all the supported features to the local entity func (e *GridScenarioImpl) AddFeatures() { - localEntity := e.Service.LocalEntity() + localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features var clientFeatures = []model.FeatureTypeType{ @@ -44,7 +44,7 @@ func (e *GridScenarioImpl) AddFeatures() { // add supported grid usecases func (e *GridScenarioImpl) AddUseCases() { - localEntity := e.Service.LocalEntity() + localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) _ = spine.NewUseCaseWithActor( localEntity, diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index e43a02e..6ac6fc4 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -82,14 +82,15 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity *spine.EntityRemoteImpl) { i.inverterEntity = entity localDevice := i.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - f1, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f1, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } i.inverterElectricalConnection = f1 - f2, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f2, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index c5a83ae..9831f18 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -4,6 +4,7 @@ import ( "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) @@ -32,9 +33,11 @@ var _ InverterBatteryVisI = (*InverterBatteryVisImpl)(nil) func NewInverterBatteryVis(service *service.EEBUSService, details *service.ServiceDetails) *InverterBatteryVisImpl { ski := util.NormalizeSKI(details.SKI) + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + inverter := &InverterBatteryVisImpl{ service: service, - entity: service.LocalEntity(), + entity: localEntity, ski: ski, } _ = spine.Events.Subscribe(inverter) diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index c5a4fc0..78bc3f1 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -28,7 +28,7 @@ func NewInverterVisScenario(service *service.EEBUSService) *InverterBatteryVisSc // adds all the supported features to the local entity func (i *InverterBatteryVisScenarioImpl) AddFeatures() { - localEntity := i.Service.LocalEntity() + localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features var clientFeatures = []model.FeatureTypeType{ @@ -44,7 +44,7 @@ func (i *InverterBatteryVisScenarioImpl) AddFeatures() { // add supported inverter usecases func (i *InverterBatteryVisScenarioImpl) AddUseCases() { - localEntity := i.Service.LocalEntity() + localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) _ = spine.NewUseCaseWithActor( localEntity, diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index a069efa..2687a18 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -92,20 +92,21 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { func (e *InverterPVVisImpl) inverterConnected(ski string, entity *spine.EntityRemoteImpl) { e.inverterEntity = entity localDevice := e.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - f1, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f1, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } e.inverterElectricalConnection = f1 - f2, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f2, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } e.inverterMeasurement = f2 - f3, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localDevice, entity) + f3, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { return } diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 8623721..2b07bab 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -4,6 +4,7 @@ import ( "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/spine" + "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" ) @@ -32,9 +33,11 @@ var _ InverterPVVisI = (*InverterPVVisImpl)(nil) func NewInverterPVVis(service *service.EEBUSService, details *service.ServiceDetails) *InverterPVVisImpl { ski := util.NormalizeSKI(details.SKI) + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + inverter := &InverterPVVisImpl{ service: service, - entity: service.LocalEntity(), + entity: localEntity, ski: ski, } _ = spine.Events.Subscribe(inverter) diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index 022258b..a882a35 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -28,7 +28,7 @@ func NewInverterVisScenario(service *service.EEBUSService) *InverterPVVisScenari // adds all the supported features to the local entity func (i *InverterPVVisScenarioImpl) AddFeatures() { - localEntity := i.Service.LocalEntity() + localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features var clientFeatures = []model.FeatureTypeType{ @@ -44,7 +44,7 @@ func (i *InverterPVVisScenarioImpl) AddFeatures() { // add supported inverter usecases func (i *InverterPVVisScenarioImpl) AddUseCases() { - localEntity := i.Service.LocalEntity() + localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) _ = spine.NewUseCaseWithActor( localEntity, diff --git a/util/helper.go b/util/helper.go index 3c48469..a7e8cf8 100644 --- a/util/helper.go +++ b/util/helper.go @@ -28,7 +28,7 @@ func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorT // return the remote entity of a given type and device ski func EntityOfTypeForSki(service *service.EEBUSService, entityType model.EntityTypeType, ski string) (*spine.EntityRemoteImpl, error) { - rDevice := service.RemoteDeviceForSki(ski) + rDevice := service.LocalDevice().RemoteDeviceForSki(ski) if rDevice == nil { return nil, features.ErrEntityNotFound From 85f1f131ec7aa7c4173d0629cdbf32e849b448b9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 29 Dec 2023 22:24:17 +0100 Subject: [PATCH 027/227] Fix tests --- emobility/helper_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 0624925..eca2b84 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -125,8 +125,7 @@ func setupEmobility() (*EMobilityImpl, *service.EEBUSService) { func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, *spine.EntityLocalImpl, *spine.DeviceRemoteImpl, []*spine.EntityRemoteImpl, *WriteMessageHandler) { localDevice := eebusService.LocalDevice() - localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) - localDevice.AddEntity(localEntity) + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) f := spine.NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) localEntity.AddFeature(f) From 4b756db1bfc3fbc278cd4834549bb66e77a14778 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 13:29:59 +0100 Subject: [PATCH 028/227] Update Github actions --- .github/workflows/default.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 86c198c..4290c93 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -15,10 +15,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ^1.18 From 9040ceb85e2c4fb27ffaf90ebceaa8071dfd3447 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 30 Dec 2023 13:30:16 +0100 Subject: [PATCH 029/227] Update eebus-go and dependencies --- go.mod | 16 ++++++++-------- go.sum | 31 ++++++++++++++++++------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index f1c5cb5..1dadd6b 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20231229202420-a56f83821f31 + github.com/enbility/eebus-go v0.0.0-20231230122742-1940014f21be github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.4 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/exp v0.0.0-20231226003508-02704c960a9b ) require ( @@ -14,17 +14,17 @@ require ( github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/holoplot/go-avahi v1.0.1 // indirect - github.com/miekg/dns v1.1.56 // indirect + github.com/miekg/dns v1.1.57 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/tools v0.16.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7c2115c..147f5bc 100644 --- a/go.sum +++ b/go.sum @@ -4,22 +4,23 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20231229202420-a56f83821f31 h1:0UzjHC5XixsFO5bhVh1RngY9oaPdyfDv4uzcMoujj90= -github.com/enbility/eebus-go v0.0.0-20231229202420-a56f83821f31/go.mod h1:ZNtZZzOM8+BDpY434U7ZRwwjnPFfa5BlAmt6ds5OTN8= +github.com/enbility/eebus-go v0.0.0-20231230122742-1940014f21be h1:cULyjS4D4Zxni0ljJaiL1kJZMeGBOT2bpBz6t3S5q5E= +github.com/enbility/eebus-go v0.0.0-20231230122742-1940014f21be/go.mod h1:S/J0ZJfM8xxALMYIfVrygV5cGK487px59A52ow6H+v8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= -github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -41,12 +42,14 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= +golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -55,14 +58,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -74,8 +78,9 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -87,16 +92,16 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 6a83ccaf2b86d3472623bcc5b1a04555f6513fdb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 31 Dec 2023 15:51:10 +0100 Subject: [PATCH 030/227] Update to latest eebus-go --- cmd/main.go | 2 +- go.mod | 2 +- go.sum | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 3a24a06..d9ecab1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -79,7 +79,7 @@ func (d *DemoCem) VisibleRemoteServicesUpdated(service *service.EEBUSService, en func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail service.ConnectionStateDetail) {} +func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *service.ConnectionStateDetail) {} func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } diff --git a/go.mod b/go.mod index 1dadd6b..1dfdeb3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20231230122742-1940014f21be + github.com/enbility/eebus-go v0.0.0-20231231144849-50a44231039b github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 147f5bc..39387b6 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20231230122742-1940014f21be h1:cULyjS4D4Zxni0ljJaiL1kJZMeGBOT2bpBz6t3S5q5E= -github.com/enbility/eebus-go v0.0.0-20231230122742-1940014f21be/go.mod h1:S/J0ZJfM8xxALMYIfVrygV5cGK487px59A52ow6H+v8= +github.com/enbility/eebus-go v0.0.0-20231231144849-50a44231039b h1:OyCDDM04bSH3vKTkO9S4KTpbSsCY2UtxFtrrYfniYqQ= +github.com/enbility/eebus-go v0.0.0-20231231144849-50a44231039b/go.mod h1:S/J0ZJfM8xxALMYIfVrygV5cGK487px59A52ow6H+v8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -40,8 +40,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= From b6b1d18c2d6d9b63706b6504f52369abd6de6edf Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 13:12:33 +0100 Subject: [PATCH 031/227] Update to latest eebus-go --- cmd/main.go | 2 +- emobility/emobility.go | 6 +- emobility/evcoordinatedcharging_test.go | 8 +-- emobility/events.go | 84 ++++++++++++------------- emobility/helper_test.go | 22 +++---- emobility/scenario.go | 34 +++++----- go.mod | 4 +- go.sum | 5 +- grid/events.go | 22 +++---- grid/grid.go | 4 +- grid/scenario.go | 6 +- inverterbatteryvis/events.go | 20 +++--- inverterbatteryvis/invertervis.go | 4 +- inverterbatteryvis/scenario.go | 6 +- inverterpvvis/events.go | 26 ++++---- inverterpvvis/invertervis.go | 4 +- inverterpvvis/scenario.go | 6 +- util/helper.go | 7 ++- 18 files changed, 140 insertions(+), 130 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index d9ecab1..eb5e41c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -67,7 +67,7 @@ func (d *DemoCem) Setup() error { func (d *DemoCem) RemoteServiceShipIDReported(service *service.EEBUSService, ski string, shipID string) { // we should associated the Ship ID with the SKI and store it // so the next connection can start trusted - logging.Log.Info("SKI", ski, "has Ship ID:", shipID) + logging.Log().Info("SKI", ski, "has Ship ID:", shipID) } func (d *DemoCem) RemoteSKIConnected(service *service.EEBUSService, ski string) {} diff --git a/emobility/emobility.go b/emobility/emobility.go index fbd9c1b..4db8d7d 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -231,12 +231,12 @@ type EmobilityI interface { } type EMobilityImpl struct { - entity *spine.EntityLocalImpl + entity spine.EntityLocal service *service.EEBUSService - evseEntity *spine.EntityRemoteImpl - evEntity *spine.EntityRemoteImpl + evseEntity spine.EntityRemote + evEntity spine.EntityRemote evseDeviceClassification *features.DeviceClassification evseDeviceDiagnosis *features.DeviceDiagnosis diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index f8f65e8..e371d6b 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -276,7 +276,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.Nil(t, err) } -func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice *spine.DeviceLocalImpl, remoteDevice *spine.DeviceRemoteImpl) { +func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { cmd := []model.CmdType{{ TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ @@ -325,7 +325,7 @@ func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice *spi assert.Nil(t, err) } -func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice *spine.DeviceLocalImpl, remoteDevice *spine.DeviceRemoteImpl) { +func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { cmd := []model.CmdType{{ IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ IncentiveTableDescription: []model.IncentiveTableDescriptionType{ @@ -348,7 +348,7 @@ func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice } /* -func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevice *spine.DeviceLocalImpl, remoteDevice *spine.DeviceRemoteImpl) { +func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { cmd := []model.CmdType{{ IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ IncentiveTableDescription: []model.IncentiveTableDescriptionType{ @@ -370,7 +370,7 @@ func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevi assert.Nil(t, err) } -func requestPowerTableUpdate(t *testing.T, datagram model.DatagramType, localDevice *spine.DeviceLocalImpl, remoteDevice *spine.DeviceRemoteImpl) { +func requestPowerTableUpdate(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { cmd := []model.CmdType{{ TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ diff --git a/emobility/events.go b/emobility/events.go index b1744ed..b3ababa 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -71,7 +71,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { // key value descriptions received, now get the data if _, err := e.evDeviceConfiguration.RequestKeyValues(); err != nil { - logging.Log.Error("Error getting configuration key values:", err) + logging.Log().Error("Error getting configuration key values:", err) } case *model.ElectricalConnectionParameterDescriptionListDataType: @@ -79,7 +79,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := e.evElectricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log.Error("Error getting electrical permitted values:", err) + logging.Log().Error("Error getting electrical permitted values:", err) } case *model.LoadControlLimitDescriptionListDataType: @@ -87,7 +87,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := e.evLoadControl.RequestLimitValues(); err != nil { - logging.Log.Error("Error getting loadcontrol limit values:", err) + logging.Log().Error("Error getting loadcontrol limit values:", err) } case *model.MeasurementDescriptionListDataType: @@ -95,7 +95,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := e.evMeasurement.RequestValues(); err != nil { - logging.Log.Error("Error getting measurement list values:", err) + logging.Log().Error("Error getting measurement list values:", err) } case *model.TimeSeriesDescriptionListDataType: @@ -120,7 +120,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { demand, err := e.EVEnergyDemand() if err != nil { - logging.Log.Error("Error getting energy demand:", err) + logging.Log().Error("Error getting energy demand:", err) break } @@ -130,13 +130,13 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { timeConstraints, err := e.EVTimeSlotConstraints() if err != nil { - logging.Log.Error("Error getting timeseries constraints:", err) + logging.Log().Error("Error getting timeseries constraints:", err) break } incentiveConstraints, err := e.EVIncentiveConstraints() if err != nil { - logging.Log.Error("Error getting incentive constraints:", err) + logging.Log().Error("Error getting incentive constraints:", err) break } @@ -218,7 +218,7 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { func (e *EMobilityImpl) evWriteDefaultIncentives() { // send default incentives for the maximum timeframe // to fullfill spec, as there is no data provided - logging.Log.Info("Fallback sending default incentives") + logging.Log().Info("Fallback sending default incentives") data := []EVDurationSlotValue{ {Duration: 7 * time.Hour * 24, Value: 0.30}, } @@ -228,22 +228,22 @@ func (e *EMobilityImpl) evWriteDefaultIncentives() { func (e *EMobilityImpl) evWriteDefaultPowerLimits() { // send default power limits for the maximum timeframe // to fullfill spec, as there is no data provided - logging.Log.Info("Fallback sending default power limits") + logging.Log().Info("Fallback sending default power limits") paramDesc, err := e.evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) if err != nil { - logging.Log.Error("Error getting parameter descriptions:", err) + logging.Log().Error("Error getting parameter descriptions:", err) return } permitted, err := e.evElectricalConnection.GetPermittedValueSetForParameterId(*paramDesc.ParameterId) if err != nil { - logging.Log.Error("Error getting permitted values:", err) + logging.Log().Error("Error getting permitted values:", err) return } if len(permitted.PermittedValueSet) < 1 || len(permitted.PermittedValueSet[0].Range) < 1 { - logging.Log.Error("No permitted value set available") + logging.Log().Error("No permitted value set available") return } @@ -260,7 +260,7 @@ func (e *EMobilityImpl) evRequestTimeSeriesValues() { } if _, err := e.evTimeSeries.RequestValues(); err != nil { - logging.Log.Error("Error getting time series list values:", err) + logging.Log().Error("Error getting time series list values:", err) } } @@ -286,12 +286,12 @@ func (e *EMobilityImpl) evRequestIncentiveValues() { } if _, err := e.evIncentiveTable.RequestValues(); err != nil { - logging.Log.Error("Error getting time series list values:", err) + logging.Log().Error("Error getting time series list values:", err) } } // process required steps when an evse is connected -func (e *EMobilityImpl) evseConnected(ski string, entity *spine.EntityRemoteImpl) { +func (e *EMobilityImpl) evseConnected(ski string, entity spine.EntityRemote) { e.evseEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -340,18 +340,18 @@ func (e *EMobilityImpl) evDisconnected() { e.evTimeSeries = nil e.evIncentiveTable = nil - logging.Log.Debug("ev disconnected") + logging.Log().Debug("ev disconnected") // TODO: add error handling } // an EV was connected, trigger required communication -func (e *EMobilityImpl) evConnected(entity *spine.EntityRemoteImpl) { +func (e *EMobilityImpl) evConnected(entity spine.EntityRemote) { e.evEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - logging.Log.Debug("ev connected") + logging.Log().Debug("ev connected") // setup features e.evDeviceClassification, _ = features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) @@ -370,102 +370,102 @@ func (e *EMobilityImpl) evConnected(entity *spine.EntityRemoteImpl) { // subscribe if err := e.evDeviceClassification.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evDeviceConfiguration.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evDeviceDiagnosis.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evElectricalConnection.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evMeasurement.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evLoadControl.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evIdentification.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if e.configuration.CoordinatedChargingEnabled { if err := e.evTimeSeries.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // this is optional if err := e.evIncentiveTable.SubscribeForEntity(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } } // bindings if err := e.evLoadControl.Bind(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if e.configuration.CoordinatedChargingEnabled { // this is optional if err := e.evTimeSeries.Bind(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // this is optional if err := e.evIncentiveTable.Bind(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } } // get ev configuration data if err := e.evDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get manufacturer details if _, err := e.evDeviceClassification.RequestManufacturerDetails(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get device diagnosis state if _, err := e.evDeviceDiagnosis.RequestState(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get electrical connection parameter if err := e.evElectricalConnection.RequestDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if err := e.evElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get measurement parameters if err := e.evMeasurement.RequestDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get loadlimit parameter if err := e.evLoadControl.RequestLimitDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get identification if _, err := e.evIdentification.RequestValues(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } if e.configuration.CoordinatedChargingEnabled { // get time series parameter if err := e.evTimeSeries.RequestDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } // get incentive table parameter if err := e.evIncentiveTable.RequestDescriptions(); err != nil { - logging.Log.Debug(err) + logging.Log().Debug(err) } } } @@ -480,7 +480,7 @@ func (e *EMobilityImpl) evWriteIncentiveTableDescriptions() { descriptions, err := e.evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) if err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } @@ -530,7 +530,7 @@ func (e *EMobilityImpl) evWriteIncentiveTableDescriptions() { _, err = e.evIncentiveTable.WriteDescriptions(data) if err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } } diff --git a/emobility/helper_test.go b/emobility/helper_test.go index eca2b84..2b78c58 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -123,7 +123,7 @@ func setupEmobility() (*EMobilityImpl, *service.EEBUSService) { return emobility, eebusService } -func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, *spine.EntityLocalImpl, *spine.DeviceRemoteImpl, []*spine.EntityRemoteImpl, *WriteMessageHandler) { +func setupDevices(eebusService *service.EEBUSService) (spine.DeviceLocal, spine.EntityLocal, spine.DeviceRemote, []spine.EntityRemote, *WriteMessageHandler) { localDevice := eebusService.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -274,7 +274,7 @@ func setupDevices(eebusService *service.EEBUSService) (*spine.DeviceLocalImpl, * return localDevice, localEntity, remoteDevice, entities, writeHandler } -func datagramForEntityAndFeatures(notify bool, localDevice *spine.DeviceLocalImpl, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { +func datagramForEntityAndFeatures(notify bool, localDevice spine.DeviceLocal, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { var addressSource, addressDestination *model.FeatureAddressType if remoteEntity == nil { // NodeManagement @@ -313,7 +313,7 @@ func datagramForEntityAndFeatures(notify bool, localDevice *spine.DeviceLocalImp return datagram } -func featureOfTypeAndRole(entity *spine.EntityRemoteImpl, featureType model.FeatureTypeType, role model.RoleType) *spine.FeatureRemoteImpl { +func featureOfTypeAndRole(entity spine.EntityRemote, featureType model.FeatureTypeType, role model.RoleType) spine.FeatureRemote { for _, f := range entity.Features() { if f.Type() == featureType && f.Role() == role { return f @@ -322,7 +322,7 @@ func featureOfTypeAndRole(entity *spine.EntityRemoteImpl, featureType model.Feat return nil } -func deviceDiagnosis(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.DeviceDiagnosis { +func deviceDiagnosis(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.DeviceDiagnosis { feature, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -330,7 +330,7 @@ func deviceDiagnosis(localEntity *spine.EntityLocalImpl, entity *spine.EntityRem return feature } -func electricalConnection(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.ElectricalConnection { +func electricalConnection(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.ElectricalConnection { feature, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -338,7 +338,7 @@ func electricalConnection(localEntity *spine.EntityLocalImpl, entity *spine.Enti return feature } -func measurement(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.Measurement { +func measurement(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.Measurement { feature, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -346,7 +346,7 @@ func measurement(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteI return feature } -func deviceConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.DeviceConfiguration { +func deviceConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.DeviceConfiguration { feature, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -354,7 +354,7 @@ func deviceConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.Entit return feature } -func identificationConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.Identification { +func identificationConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.Identification { feature, err := features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -362,7 +362,7 @@ func identificationConfiguration(localEntity *spine.EntityLocalImpl, entity *spi return feature } -func loadcontrol(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.LoadControl { +func loadcontrol(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.LoadControl { feature, err := features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -370,7 +370,7 @@ func loadcontrol(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteI return feature } -func timeSeriesConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.TimeSeries { +func timeSeriesConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.TimeSeries { feature, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -378,7 +378,7 @@ func timeSeriesConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.E return feature } -func incentiveTableConfiguration(localEntity *spine.EntityLocalImpl, entity *spine.EntityRemoteImpl) *features.IncentiveTable { +func incentiveTableConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.IncentiveTable { feature, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) diff --git a/emobility/scenario.go b/emobility/scenario.go index b294b2b..c8941e3 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -76,54 +76,60 @@ func (e *EmobilityScenarioImpl) AddFeatures() { func (e *EmobilityScenarioImpl) AddUseCases() { localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - _ = spine.NewUseCase( - localEntity, + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, model.UseCaseNameTypeEVSECommissioningAndConfiguration, model.SpecificationVersionType("1.0.1"), + "", true, []model.UseCaseScenarioSupportType{1, 2}) - _ = spine.NewUseCase( - localEntity, + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, model.UseCaseNameTypeEVCommissioningAndConfiguration, model.SpecificationVersionType("1.0.1"), + "", true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) - _ = spine.NewUseCase( - localEntity, + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging, model.SpecificationVersionType("1.0.1"), + "", true, []model.UseCaseScenarioSupportType{1, 2, 3}) - _ = spine.NewUseCase( - localEntity, + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment, model.SpecificationVersionType("1.0.1b"), + "", true, []model.UseCaseScenarioSupportType{1, 2, 3}) - _ = spine.NewUseCaseWithActor( - localEntity, + localEntity.AddUseCaseSupport( model.UseCaseActorTypeMonitoringAppliance, model.UseCaseNameTypeEVStateOfCharge, model.SpecificationVersionType("1.0.0"), + "", true, []model.UseCaseScenarioSupportType{1, 2, 3, 4}) - _ = spine.NewUseCase( - localEntity, + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging, model.SpecificationVersionType("1.0.1b"), + "", true, []model.UseCaseScenarioSupportType{1, 2, 3}) if e.configuration.CoordinatedChargingEnabled { - _ = spine.NewUseCase( - localEntity, + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, model.UseCaseNameTypeCoordinatedEVCharging, model.SpecificationVersionType("1.0.1"), + "", true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) } diff --git a/go.mod b/go.mod index 1dfdeb3..22ed251 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20231231144849-50a44231039b + github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40 github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b @@ -28,3 +28,5 @@ require ( golang.org/x/tools v0.16.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/enbility/eebus-go => ../eebus-go diff --git a/go.sum b/go.sum index 39387b6..b842a69 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20231231144849-50a44231039b h1:OyCDDM04bSH3vKTkO9S4KTpbSsCY2UtxFtrrYfniYqQ= -github.com/enbility/eebus-go v0.0.0-20231231144849-50a44231039b/go.mod h1:S/J0ZJfM8xxALMYIfVrygV5cGK487px59A52ow6H+v8= +github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40 h1:w1Zo2m4DpHK07g0ry71IWYsSbUQVc8llys8TneruclU= +github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40/go.mod h1:LQwgXW3RJm+j9x2Tn5C7G4hHxCMeZRDKup99eJkkFl4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -35,6 +35,7 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/grid/events.go b/grid/events.go index d7515b0..fb94ee1 100644 --- a/grid/events.go +++ b/grid/events.go @@ -49,12 +49,12 @@ func (e *GridImpl) HandleEvent(payload spine.EventPayload) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: // key value descriptions received, now get the data if _, err := e.gridDeviceConfiguration.RequestKeyValues(); err != nil { - logging.Log.Error("Error getting configuration key values:", err) + logging.Log().Error("Error getting configuration key values:", err) } case *model.MeasurementDescriptionListDataType: if _, err := e.gridMeasurement.RequestValues(); err != nil { - logging.Log.Error("Error getting measurement list values:", err) + logging.Log().Error("Error getting measurement list values:", err) } } @@ -64,7 +64,7 @@ func (e *GridImpl) HandleEvent(payload spine.EventPayload) { } // process required steps when a grid device is connected -func (e *GridImpl) gridConnected(ski string, entity *spine.EntityRemoteImpl) { +func (e *GridImpl) gridConnected(ski string, entity spine.EntityRemote) { e.gridEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -89,43 +89,43 @@ func (e *GridImpl) gridConnected(ski string, entity *spine.EntityRemoteImpl) { // subscribe if err := e.gridDeviceConfiguration.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } if err := e.gridElectricalConnection.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } if err := e.gridMeasurement.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } // get configuration data if err := e.gridDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } // get electrical connection parameter if err := e.gridElectricalConnection.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } if err := e.gridElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } // get measurement parameters if err := e.gridMeasurement.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } if err := e.gridMeasurement.RequestConstraints(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) return } } diff --git a/grid/grid.go b/grid/grid.go index 3341cb9..28d63ea 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -19,11 +19,11 @@ type GridI interface { } type GridImpl struct { - entity *spine.EntityLocalImpl + entity spine.EntityLocal service *service.EEBUSService - gridEntity *spine.EntityRemoteImpl + gridEntity spine.EntityRemote gridDeviceConfiguration *features.DeviceConfiguration gridElectricalConnection *features.ElectricalConnection diff --git a/grid/scenario.go b/grid/scenario.go index 4e20410..0cab638 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -46,11 +46,11 @@ func (e *GridScenarioImpl) AddFeatures() { func (e *GridScenarioImpl) AddUseCases() { localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - _ = spine.NewUseCaseWithActor( - localEntity, + localEntity.AddUseCaseSupport( model.UseCaseActorTypeMonitoringAppliance, model.UseCaseNameTypeMonitoringOfGridConnectionPoint, - model.SpecificationVersionType("1.0.0 RC5"), + model.SpecificationVersionType("1.0.0"), + "RC5", true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) } diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index 6ac6fc4..cd2f65e 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -56,7 +56,7 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := i.inverterElectricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log.Error("Error getting electrical permitted values:", err) + logging.Log().Error("Error getting electrical permitted values:", err) } case *model.ElectricalConnectionDescriptionListDataType: @@ -64,7 +64,7 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { break } if err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log.Error("Error getting electrical permitted values:", err) + logging.Log().Error("Error getting electrical permitted values:", err) } case *model.MeasurementDescriptionListDataType: @@ -72,14 +72,14 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := i.inverterMeasurement.RequestValues(); err != nil { - logging.Log.Error("Error getting measurement list values:", err) + logging.Log().Error("Error getting measurement list values:", err) } } } } // process required steps when a battery device entity is connected -func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity *spine.EntityRemoteImpl) { +func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity spine.EntityRemote) { i.inverterEntity = entity localDevice := i.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -98,28 +98,28 @@ func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity *spine.Ent // subscribe if err := i.inverterElectricalConnection.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := i.inverterMeasurement.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } // get electrical connection parameter if err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := i.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } // get measurement parameters if err := i.inverterMeasurement.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := i.inverterMeasurement.RequestConstraints(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } } diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index 9831f18..80bbdd3 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -16,11 +16,11 @@ type InverterBatteryVisI interface { } type InverterBatteryVisImpl struct { - entity *spine.EntityLocalImpl + entity spine.EntityLocal service *service.EEBUSService - inverterEntity *spine.EntityRemoteImpl + inverterEntity spine.EntityRemote inverterElectricalConnection *features.ElectricalConnection inverterMeasurement *features.Measurement diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index 78bc3f1..3729e97 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -46,11 +46,11 @@ func (i *InverterBatteryVisScenarioImpl) AddFeatures() { func (i *InverterBatteryVisScenarioImpl) AddUseCases() { localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - _ = spine.NewUseCaseWithActor( - localEntity, + localEntity.AddUseCaseSupport( model.UseCaseActorTypeVisualizationAppliance, model.UseCaseNameTypeVisualizationOfAggregatedBatteryData, - model.SpecificationVersionType("1.0.0 RC1"), + model.SpecificationVersionType("1.0.0"), + "RC1", true, []model.UseCaseScenarioSupportType{1, 2, 3, 4}) } diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index 2687a18..39c1c9a 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -58,7 +58,7 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { // key value descriptions received, now get the data if _, err := i.inverterDeviceConfiguration.RequestKeyValues(); err != nil { - logging.Log.Error("Error getting configuration key values:", err) + logging.Log().Error("Error getting configuration key values:", err) } case *model.ElectricalConnectionParameterDescriptionListDataType: @@ -66,7 +66,7 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := i.inverterElectricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log.Error("Error getting electrical permitted values:", err) + logging.Log().Error("Error getting electrical permitted values:", err) } case *model.ElectricalConnectionDescriptionListDataType: @@ -74,7 +74,7 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { break } if err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log.Error("Error getting electrical permitted values:", err) + logging.Log().Error("Error getting electrical permitted values:", err) } case *model.MeasurementDescriptionListDataType: @@ -82,14 +82,14 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { break } if _, err := i.inverterMeasurement.RequestValues(); err != nil { - logging.Log.Error("Error getting measurement list values:", err) + logging.Log().Error("Error getting measurement list values:", err) } } } } // process required steps when a pv device entity is connected -func (e *InverterPVVisImpl) inverterConnected(ski string, entity *spine.EntityRemoteImpl) { +func (e *InverterPVVisImpl) inverterConnected(ski string, entity spine.EntityRemote) { e.inverterEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -114,36 +114,36 @@ func (e *InverterPVVisImpl) inverterConnected(ski string, entity *spine.EntityRe // subscribe if err := e.inverterDeviceConfiguration.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := e.inverterElectricalConnection.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := e.inverterMeasurement.SubscribeForEntity(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } // get device configuration data if err := e.inverterDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } // get electrical connection parameter if err := e.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := e.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } // get measurement parameters if err := e.inverterMeasurement.RequestDescriptions(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } if err := e.inverterMeasurement.RequestConstraints(); err != nil { - logging.Log.Error(err) + logging.Log().Error(err) } } diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 2b07bab..04f9f81 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -15,11 +15,11 @@ type InverterPVVisI interface { } type InverterPVVisImpl struct { - entity *spine.EntityLocalImpl + entity spine.EntityLocal service *service.EEBUSService - inverterEntity *spine.EntityRemoteImpl + inverterEntity spine.EntityRemote inverterDeviceConfiguration *features.DeviceConfiguration inverterElectricalConnection *features.ElectricalConnection inverterMeasurement *features.Measurement diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index a882a35..6babec4 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -46,11 +46,11 @@ func (i *InverterPVVisScenarioImpl) AddFeatures() { func (i *InverterPVVisScenarioImpl) AddUseCases() { localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - _ = spine.NewUseCaseWithActor( - localEntity, + localEntity.AddUseCaseSupport( model.UseCaseActorTypeVisualizationAppliance, model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData, - model.SpecificationVersionType("1.0.0 RC1"), + model.SpecificationVersionType("1.0.0"), + "RC1", true, []model.UseCaseScenarioSupportType{1, 2, 3}) } diff --git a/util/helper.go b/util/helper.go index a7e8cf8..03fb4ab 100644 --- a/util/helper.go +++ b/util/helper.go @@ -10,8 +10,9 @@ import ( var PhaseNameMapping = []model.ElectricalConnectionPhaseNameType{model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC} // check if the given usecase, actor is supported by the remote device -func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorType, remoteDevice *spine.DeviceRemoteImpl) bool { - uci := remoteDevice.UseCaseManager().UseCaseInformation() +func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorType, remoteDevice spine.DeviceRemote) bool { + uci := remoteDevice.UseCases() + for _, element := range uci { if *element.Actor != actor { continue @@ -27,7 +28,7 @@ func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorT } // return the remote entity of a given type and device ski -func EntityOfTypeForSki(service *service.EEBUSService, entityType model.EntityTypeType, ski string) (*spine.EntityRemoteImpl, error) { +func EntityOfTypeForSki(service *service.EEBUSService, entityType model.EntityTypeType, ski string) (spine.EntityRemote, error) { rDevice := service.LocalDevice().RemoteDeviceForSki(ski) if rDevice == nil { From 8507881d19694f87c820cf7750dd828f448b1e34 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 13:22:57 +0100 Subject: [PATCH 032/227] Fix go.mod --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index 22ed251..1bf743d 100644 --- a/go.mod +++ b/go.mod @@ -28,5 +28,3 @@ require ( golang.org/x/tools v0.16.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace github.com/enbility/eebus-go => ../eebus-go From 7d11846b3c3e2fc4b2431c4be0f8d8505a8b4c45 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 23:06:08 +0100 Subject: [PATCH 033/227] Replace gomock module --- emobility/emobility.go | 2 +- emobility/evcoordinatedcharging_test.go | 2 +- emobility/mock_emobility.go | 456 ------------------------ emobility/public_EVChargePlan_test.go | 2 +- go.mod | 2 +- go.sum | 15 +- 6 files changed, 5 insertions(+), 474 deletions(-) delete mode 100644 emobility/mock_emobility.go diff --git a/emobility/emobility.go b/emobility/emobility.go index 4db8d7d..9376583 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -8,7 +8,7 @@ import ( "github.com/enbility/eebus-go/util" ) -//go:generate mockgen -source emobility.go -destination mock_emobility.go -package emobility +//go:generate mockgen -source emobility.go -destination mock_emobility_test.go -package emobility // used by emobility and implemented by the CEM type EmobilityDataProvider interface { diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index e371d6b..3187068 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -7,8 +7,8 @@ import ( "github.com/enbility/eebus-go/spine" "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + gomock "go.uber.org/mock/gomock" ) func Test_CoordinatedChargingScenarios(t *testing.T) { diff --git a/emobility/mock_emobility.go b/emobility/mock_emobility.go deleted file mode 100644 index 99c18f7..0000000 --- a/emobility/mock_emobility.go +++ /dev/null @@ -1,456 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: emobility.go - -// Package emobility is a generated GoMock package. -package emobility - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockEmobilityDataProvider is a mock of EmobilityDataProvider interface. -type MockEmobilityDataProvider struct { - ctrl *gomock.Controller - recorder *MockEmobilityDataProviderMockRecorder -} - -// MockEmobilityDataProviderMockRecorder is the mock recorder for MockEmobilityDataProvider. -type MockEmobilityDataProviderMockRecorder struct { - mock *MockEmobilityDataProvider -} - -// NewMockEmobilityDataProvider creates a new mock instance. -func NewMockEmobilityDataProvider(ctrl *gomock.Controller) *MockEmobilityDataProvider { - mock := &MockEmobilityDataProvider{ctrl: ctrl} - mock.recorder = &MockEmobilityDataProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEmobilityDataProvider) EXPECT() *MockEmobilityDataProviderMockRecorder { - return m.recorder -} - -// EVProvidedChargePlan mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargePlan(plan EVChargePlan) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargePlan", plan) -} - -// EVProvidedChargePlan indicates an expected call of EVProvidedChargePlan. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlan(plan interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlan", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlan), plan) -} - -// EVProvidedChargePlanConstraints mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargePlanConstraints", constraints) -} - -// EVProvidedChargePlanConstraints indicates an expected call of EVProvidedChargePlanConstraints. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlanConstraints(constraints interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlanConstraints", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlanConstraints), constraints) -} - -// EVProvidedChargeStrategy mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargeStrategy(strategy EVChargeStrategyType) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargeStrategy", strategy) -} - -// EVProvidedChargeStrategy indicates an expected call of EVProvidedChargeStrategy. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargeStrategy(strategy interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargeStrategy", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargeStrategy), strategy) -} - -// EVProvidedEnergyDemand mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedEnergyDemand(demand EVDemand) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedEnergyDemand", demand) -} - -// EVProvidedEnergyDemand indicates an expected call of EVProvidedEnergyDemand. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedEnergyDemand(demand interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedEnergyDemand", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedEnergyDemand), demand) -} - -// EVRequestIncentives mocks base method. -func (m *MockEmobilityDataProvider) EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVRequestIncentives", demand, constraints) -} - -// EVRequestIncentives indicates an expected call of EVRequestIncentives. -func (mr *MockEmobilityDataProviderMockRecorder) EVRequestIncentives(demand, constraints interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVRequestIncentives", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVRequestIncentives), demand, constraints) -} - -// EVRequestPowerLimits mocks base method. -func (m *MockEmobilityDataProvider) EVRequestPowerLimits(demand EVDemand, constraints EVTimeSlotConstraints) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVRequestPowerLimits", demand, constraints) -} - -// EVRequestPowerLimits indicates an expected call of EVRequestPowerLimits. -func (mr *MockEmobilityDataProviderMockRecorder) EVRequestPowerLimits(demand, constraints interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVRequestPowerLimits", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVRequestPowerLimits), demand, constraints) -} - -// MockEmobilityI is a mock of EmobilityI interface. -type MockEmobilityI struct { - ctrl *gomock.Controller - recorder *MockEmobilityIMockRecorder -} - -// MockEmobilityIMockRecorder is the mock recorder for MockEmobilityI. -type MockEmobilityIMockRecorder struct { - mock *MockEmobilityI -} - -// NewMockEmobilityI creates a new mock instance. -func NewMockEmobilityI(ctrl *gomock.Controller) *MockEmobilityI { - mock := &MockEmobilityI{ctrl: ctrl} - mock.recorder = &MockEmobilityIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEmobilityI) EXPECT() *MockEmobilityIMockRecorder { - return m.recorder -} - -// EVChargePlan mocks base method. -func (m *MockEmobilityI) EVChargePlan() (EVChargePlan, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVChargePlan") - ret0, _ := ret[0].(EVChargePlan) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVChargePlan indicates an expected call of EVChargePlan. -func (mr *MockEmobilityIMockRecorder) EVChargePlan() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargePlan", reflect.TypeOf((*MockEmobilityI)(nil).EVChargePlan)) -} - -// EVChargeStrategy mocks base method. -func (m *MockEmobilityI) EVChargeStrategy() EVChargeStrategyType { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVChargeStrategy") - ret0, _ := ret[0].(EVChargeStrategyType) - return ret0 -} - -// EVChargeStrategy indicates an expected call of EVChargeStrategy. -func (mr *MockEmobilityIMockRecorder) EVChargeStrategy() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargeStrategy", reflect.TypeOf((*MockEmobilityI)(nil).EVChargeStrategy)) -} - -// EVChargedEnergy mocks base method. -func (m *MockEmobilityI) EVChargedEnergy() (float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVChargedEnergy") - ret0, _ := ret[0].(float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVChargedEnergy indicates an expected call of EVChargedEnergy. -func (mr *MockEmobilityIMockRecorder) EVChargedEnergy() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargedEnergy", reflect.TypeOf((*MockEmobilityI)(nil).EVChargedEnergy)) -} - -// EVCommunicationStandard mocks base method. -func (m *MockEmobilityI) EVCommunicationStandard() (EVCommunicationStandardType, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCommunicationStandard") - ret0, _ := ret[0].(EVCommunicationStandardType) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCommunicationStandard indicates an expected call of EVCommunicationStandard. -func (mr *MockEmobilityIMockRecorder) EVCommunicationStandard() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCommunicationStandard", reflect.TypeOf((*MockEmobilityI)(nil).EVCommunicationStandard)) -} - -// EVConnected mocks base method. -func (m *MockEmobilityI) EVConnected() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVConnected") - ret0, _ := ret[0].(bool) - return ret0 -} - -// EVConnected indicates an expected call of EVConnected. -func (mr *MockEmobilityIMockRecorder) EVConnected() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVConnected", reflect.TypeOf((*MockEmobilityI)(nil).EVConnected)) -} - -// EVConnectedPhases mocks base method. -func (m *MockEmobilityI) EVConnectedPhases() (uint, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVConnectedPhases") - ret0, _ := ret[0].(uint) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVConnectedPhases indicates an expected call of EVConnectedPhases. -func (mr *MockEmobilityIMockRecorder) EVConnectedPhases() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVConnectedPhases", reflect.TypeOf((*MockEmobilityI)(nil).EVConnectedPhases)) -} - -// EVCoordinatedChargingSupported mocks base method. -func (m *MockEmobilityI) EVCoordinatedChargingSupported() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCoordinatedChargingSupported") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCoordinatedChargingSupported indicates an expected call of EVCoordinatedChargingSupported. -func (mr *MockEmobilityIMockRecorder) EVCoordinatedChargingSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCoordinatedChargingSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVCoordinatedChargingSupported)) -} - -// EVCurrentChargeState mocks base method. -func (m *MockEmobilityI) EVCurrentChargeState() (EVChargeStateType, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCurrentChargeState") - ret0, _ := ret[0].(EVChargeStateType) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCurrentChargeState indicates an expected call of EVCurrentChargeState. -func (mr *MockEmobilityIMockRecorder) EVCurrentChargeState() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentChargeState", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentChargeState)) -} - -// EVCurrentLimits mocks base method. -func (m *MockEmobilityI) EVCurrentLimits() ([]float64, []float64, []float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCurrentLimits") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].([]float64) - ret2, _ := ret[2].([]float64) - ret3, _ := ret[3].(error) - return ret0, ret1, ret2, ret3 -} - -// EVCurrentLimits indicates an expected call of EVCurrentLimits. -func (mr *MockEmobilityIMockRecorder) EVCurrentLimits() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentLimits)) -} - -// EVCurrentsPerPhase mocks base method. -func (m *MockEmobilityI) EVCurrentsPerPhase() ([]float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCurrentsPerPhase") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCurrentsPerPhase indicates an expected call of EVCurrentsPerPhase. -func (mr *MockEmobilityIMockRecorder) EVCurrentsPerPhase() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentsPerPhase", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentsPerPhase)) -} - -// EVEnergyDemand mocks base method. -func (m *MockEmobilityI) EVEnergyDemand() (EVDemand, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVEnergyDemand") - ret0, _ := ret[0].(EVDemand) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVEnergyDemand indicates an expected call of EVEnergyDemand. -func (mr *MockEmobilityIMockRecorder) EVEnergyDemand() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVEnergyDemand", reflect.TypeOf((*MockEmobilityI)(nil).EVEnergyDemand)) -} - -// EVIdentification mocks base method. -func (m *MockEmobilityI) EVIdentification() (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVIdentification") - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVIdentification indicates an expected call of EVIdentification. -func (mr *MockEmobilityIMockRecorder) EVIdentification() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIdentification", reflect.TypeOf((*MockEmobilityI)(nil).EVIdentification)) -} - -// EVIncentiveConstraints mocks base method. -func (m *MockEmobilityI) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVIncentiveConstraints") - ret0, _ := ret[0].(EVIncentiveSlotConstraints) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVIncentiveConstraints indicates an expected call of EVIncentiveConstraints. -func (mr *MockEmobilityIMockRecorder) EVIncentiveConstraints() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIncentiveConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVIncentiveConstraints)) -} - -// EVLoadControlObligationLimits mocks base method. -func (m *MockEmobilityI) EVLoadControlObligationLimits() ([]float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVLoadControlObligationLimits") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVLoadControlObligationLimits indicates an expected call of EVLoadControlObligationLimits. -func (mr *MockEmobilityIMockRecorder) EVLoadControlObligationLimits() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVLoadControlObligationLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVLoadControlObligationLimits)) -} - -// EVOptimizationOfSelfConsumptionSupported mocks base method. -func (m *MockEmobilityI) EVOptimizationOfSelfConsumptionSupported() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVOptimizationOfSelfConsumptionSupported") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVOptimizationOfSelfConsumptionSupported indicates an expected call of EVOptimizationOfSelfConsumptionSupported. -func (mr *MockEmobilityIMockRecorder) EVOptimizationOfSelfConsumptionSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVOptimizationOfSelfConsumptionSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVOptimizationOfSelfConsumptionSupported)) -} - -// EVPowerPerPhase mocks base method. -func (m *MockEmobilityI) EVPowerPerPhase() ([]float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVPowerPerPhase") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVPowerPerPhase indicates an expected call of EVPowerPerPhase. -func (mr *MockEmobilityIMockRecorder) EVPowerPerPhase() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVPowerPerPhase", reflect.TypeOf((*MockEmobilityI)(nil).EVPowerPerPhase)) -} - -// EVSoC mocks base method. -func (m *MockEmobilityI) EVSoC() (float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVSoC") - ret0, _ := ret[0].(float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVSoC indicates an expected call of EVSoC. -func (mr *MockEmobilityIMockRecorder) EVSoC() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoC", reflect.TypeOf((*MockEmobilityI)(nil).EVSoC)) -} - -// EVSoCSupported mocks base method. -func (m *MockEmobilityI) EVSoCSupported() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVSoCSupported") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVSoCSupported indicates an expected call of EVSoCSupported. -func (mr *MockEmobilityIMockRecorder) EVSoCSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoCSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVSoCSupported)) -} - -// EVTimeSlotConstraints mocks base method. -func (m *MockEmobilityI) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVTimeSlotConstraints") - ret0, _ := ret[0].(EVTimeSlotConstraints) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVTimeSlotConstraints indicates an expected call of EVTimeSlotConstraints. -func (mr *MockEmobilityIMockRecorder) EVTimeSlotConstraints() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVTimeSlotConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVTimeSlotConstraints)) -} - -// EVWriteIncentives mocks base method. -func (m *MockEmobilityI) EVWriteIncentives(data []EVDurationSlotValue) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWriteIncentives", data) - ret0, _ := ret[0].(error) - return ret0 -} - -// EVWriteIncentives indicates an expected call of EVWriteIncentives. -func (mr *MockEmobilityIMockRecorder) EVWriteIncentives(data interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteIncentives", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteIncentives), data) -} - -// EVWriteLoadControlLimits mocks base method. -func (m *MockEmobilityI) EVWriteLoadControlLimits(limits []EVLoadLimits) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWriteLoadControlLimits", limits) - ret0, _ := ret[0].(error) - return ret0 -} - -// EVWriteLoadControlLimits indicates an expected call of EVWriteLoadControlLimits. -func (mr *MockEmobilityIMockRecorder) EVWriteLoadControlLimits(limits interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteLoadControlLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteLoadControlLimits), limits) -} - -// EVWritePowerLimits mocks base method. -func (m *MockEmobilityI) EVWritePowerLimits(data []EVDurationSlotValue) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWritePowerLimits", data) - ret0, _ := ret[0].(error) - return ret0 -} - -// EVWritePowerLimits indicates an expected call of EVWritePowerLimits. -func (mr *MockEmobilityIMockRecorder) EVWritePowerLimits(data interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWritePowerLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWritePowerLimits), data) -} diff --git a/emobility/public_EVChargePlan_test.go b/emobility/public_EVChargePlan_test.go index ae16b29..cbdad83 100644 --- a/emobility/public_EVChargePlan_test.go +++ b/emobility/public_EVChargePlan_test.go @@ -5,8 +5,8 @@ import ( "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + gomock "go.uber.org/mock/gomock" ) func Test_EVChargePlan(t *testing.T) { diff --git a/go.mod b/go.mod index 1bf743d..fd33ce5 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40 - github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.8.4 + go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b ) diff --git a/go.sum b/go.sum index b842a69..78f4dac 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,6 @@ github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40/go.mod h1:LQwgXW github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= @@ -31,28 +29,24 @@ github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7 github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -61,16 +55,12 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -95,15 +85,12 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From c595ae375ea436aa1a387c87b4a16252222eaca5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 23:07:19 +0100 Subject: [PATCH 034/227] Update to latest eebus-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fd33ce5..1d1c528 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40 + github.com/enbility/eebus-go v0.0.0-20240109191414-52f14d2033f6 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 78f4dac..0534415 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40 h1:w1Zo2m4DpHK07g0ry71IWYsSbUQVc8llys8TneruclU= -github.com/enbility/eebus-go v0.0.0-20240109121001-eb9a1f604a40/go.mod h1:LQwgXW3RJm+j9x2Tn5C7G4hHxCMeZRDKup99eJkkFl4= +github.com/enbility/eebus-go v0.0.0-20240109191414-52f14d2033f6 h1:0gxTlkvgJHmG9lC5AvGxEQcbgXo0eh0foP8UOlpGLeE= +github.com/enbility/eebus-go v0.0.0-20240109191414-52f14d2033f6/go.mod h1:LQwgXW3RJm+j9x2Tn5C7G4hHxCMeZRDKup99eJkkFl4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From f251b2c88fcecbaae2c73342953110db7acb056f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Jan 2024 23:10:09 +0100 Subject: [PATCH 035/227] Add missing mock --- emobility/mock_emobility_test.go | 461 +++++++++++++++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 emobility/mock_emobility_test.go diff --git a/emobility/mock_emobility_test.go b/emobility/mock_emobility_test.go new file mode 100644 index 0000000..de5970c --- /dev/null +++ b/emobility/mock_emobility_test.go @@ -0,0 +1,461 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: emobility.go +// +// Generated by this command: +// +// mockgen -source emobility.go -destination mock_emobility_test.go -package emobility +// + +// Package emobility is a generated GoMock package. +package emobility + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockEmobilityDataProvider is a mock of EmobilityDataProvider interface. +type MockEmobilityDataProvider struct { + ctrl *gomock.Controller + recorder *MockEmobilityDataProviderMockRecorder +} + +// MockEmobilityDataProviderMockRecorder is the mock recorder for MockEmobilityDataProvider. +type MockEmobilityDataProviderMockRecorder struct { + mock *MockEmobilityDataProvider +} + +// NewMockEmobilityDataProvider creates a new mock instance. +func NewMockEmobilityDataProvider(ctrl *gomock.Controller) *MockEmobilityDataProvider { + mock := &MockEmobilityDataProvider{ctrl: ctrl} + mock.recorder = &MockEmobilityDataProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEmobilityDataProvider) EXPECT() *MockEmobilityDataProviderMockRecorder { + return m.recorder +} + +// EVProvidedChargePlan mocks base method. +func (m *MockEmobilityDataProvider) EVProvidedChargePlan(plan EVChargePlan) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVProvidedChargePlan", plan) +} + +// EVProvidedChargePlan indicates an expected call of EVProvidedChargePlan. +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlan(plan any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlan", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlan), plan) +} + +// EVProvidedChargePlanConstraints mocks base method. +func (m *MockEmobilityDataProvider) EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVProvidedChargePlanConstraints", constraints) +} + +// EVProvidedChargePlanConstraints indicates an expected call of EVProvidedChargePlanConstraints. +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlanConstraints(constraints any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlanConstraints", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlanConstraints), constraints) +} + +// EVProvidedChargeStrategy mocks base method. +func (m *MockEmobilityDataProvider) EVProvidedChargeStrategy(strategy EVChargeStrategyType) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVProvidedChargeStrategy", strategy) +} + +// EVProvidedChargeStrategy indicates an expected call of EVProvidedChargeStrategy. +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargeStrategy(strategy any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargeStrategy", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargeStrategy), strategy) +} + +// EVProvidedEnergyDemand mocks base method. +func (m *MockEmobilityDataProvider) EVProvidedEnergyDemand(demand EVDemand) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVProvidedEnergyDemand", demand) +} + +// EVProvidedEnergyDemand indicates an expected call of EVProvidedEnergyDemand. +func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedEnergyDemand(demand any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedEnergyDemand", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedEnergyDemand), demand) +} + +// EVRequestIncentives mocks base method. +func (m *MockEmobilityDataProvider) EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVRequestIncentives", demand, constraints) +} + +// EVRequestIncentives indicates an expected call of EVRequestIncentives. +func (mr *MockEmobilityDataProviderMockRecorder) EVRequestIncentives(demand, constraints any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVRequestIncentives", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVRequestIncentives), demand, constraints) +} + +// EVRequestPowerLimits mocks base method. +func (m *MockEmobilityDataProvider) EVRequestPowerLimits(demand EVDemand, constraints EVTimeSlotConstraints) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EVRequestPowerLimits", demand, constraints) +} + +// EVRequestPowerLimits indicates an expected call of EVRequestPowerLimits. +func (mr *MockEmobilityDataProviderMockRecorder) EVRequestPowerLimits(demand, constraints any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVRequestPowerLimits", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVRequestPowerLimits), demand, constraints) +} + +// MockEmobilityI is a mock of EmobilityI interface. +type MockEmobilityI struct { + ctrl *gomock.Controller + recorder *MockEmobilityIMockRecorder +} + +// MockEmobilityIMockRecorder is the mock recorder for MockEmobilityI. +type MockEmobilityIMockRecorder struct { + mock *MockEmobilityI +} + +// NewMockEmobilityI creates a new mock instance. +func NewMockEmobilityI(ctrl *gomock.Controller) *MockEmobilityI { + mock := &MockEmobilityI{ctrl: ctrl} + mock.recorder = &MockEmobilityIMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEmobilityI) EXPECT() *MockEmobilityIMockRecorder { + return m.recorder +} + +// EVChargePlan mocks base method. +func (m *MockEmobilityI) EVChargePlan() (EVChargePlan, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVChargePlan") + ret0, _ := ret[0].(EVChargePlan) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVChargePlan indicates an expected call of EVChargePlan. +func (mr *MockEmobilityIMockRecorder) EVChargePlan() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargePlan", reflect.TypeOf((*MockEmobilityI)(nil).EVChargePlan)) +} + +// EVChargeStrategy mocks base method. +func (m *MockEmobilityI) EVChargeStrategy() EVChargeStrategyType { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVChargeStrategy") + ret0, _ := ret[0].(EVChargeStrategyType) + return ret0 +} + +// EVChargeStrategy indicates an expected call of EVChargeStrategy. +func (mr *MockEmobilityIMockRecorder) EVChargeStrategy() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargeStrategy", reflect.TypeOf((*MockEmobilityI)(nil).EVChargeStrategy)) +} + +// EVChargedEnergy mocks base method. +func (m *MockEmobilityI) EVChargedEnergy() (float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVChargedEnergy") + ret0, _ := ret[0].(float64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVChargedEnergy indicates an expected call of EVChargedEnergy. +func (mr *MockEmobilityIMockRecorder) EVChargedEnergy() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargedEnergy", reflect.TypeOf((*MockEmobilityI)(nil).EVChargedEnergy)) +} + +// EVCommunicationStandard mocks base method. +func (m *MockEmobilityI) EVCommunicationStandard() (EVCommunicationStandardType, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVCommunicationStandard") + ret0, _ := ret[0].(EVCommunicationStandardType) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVCommunicationStandard indicates an expected call of EVCommunicationStandard. +func (mr *MockEmobilityIMockRecorder) EVCommunicationStandard() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCommunicationStandard", reflect.TypeOf((*MockEmobilityI)(nil).EVCommunicationStandard)) +} + +// EVConnected mocks base method. +func (m *MockEmobilityI) EVConnected() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVConnected") + ret0, _ := ret[0].(bool) + return ret0 +} + +// EVConnected indicates an expected call of EVConnected. +func (mr *MockEmobilityIMockRecorder) EVConnected() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVConnected", reflect.TypeOf((*MockEmobilityI)(nil).EVConnected)) +} + +// EVConnectedPhases mocks base method. +func (m *MockEmobilityI) EVConnectedPhases() (uint, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVConnectedPhases") + ret0, _ := ret[0].(uint) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVConnectedPhases indicates an expected call of EVConnectedPhases. +func (mr *MockEmobilityIMockRecorder) EVConnectedPhases() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVConnectedPhases", reflect.TypeOf((*MockEmobilityI)(nil).EVConnectedPhases)) +} + +// EVCoordinatedChargingSupported mocks base method. +func (m *MockEmobilityI) EVCoordinatedChargingSupported() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVCoordinatedChargingSupported") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVCoordinatedChargingSupported indicates an expected call of EVCoordinatedChargingSupported. +func (mr *MockEmobilityIMockRecorder) EVCoordinatedChargingSupported() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCoordinatedChargingSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVCoordinatedChargingSupported)) +} + +// EVCurrentChargeState mocks base method. +func (m *MockEmobilityI) EVCurrentChargeState() (EVChargeStateType, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVCurrentChargeState") + ret0, _ := ret[0].(EVChargeStateType) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVCurrentChargeState indicates an expected call of EVCurrentChargeState. +func (mr *MockEmobilityIMockRecorder) EVCurrentChargeState() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentChargeState", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentChargeState)) +} + +// EVCurrentLimits mocks base method. +func (m *MockEmobilityI) EVCurrentLimits() ([]float64, []float64, []float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVCurrentLimits") + ret0, _ := ret[0].([]float64) + ret1, _ := ret[1].([]float64) + ret2, _ := ret[2].([]float64) + ret3, _ := ret[3].(error) + return ret0, ret1, ret2, ret3 +} + +// EVCurrentLimits indicates an expected call of EVCurrentLimits. +func (mr *MockEmobilityIMockRecorder) EVCurrentLimits() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentLimits)) +} + +// EVCurrentsPerPhase mocks base method. +func (m *MockEmobilityI) EVCurrentsPerPhase() ([]float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVCurrentsPerPhase") + ret0, _ := ret[0].([]float64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVCurrentsPerPhase indicates an expected call of EVCurrentsPerPhase. +func (mr *MockEmobilityIMockRecorder) EVCurrentsPerPhase() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentsPerPhase", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentsPerPhase)) +} + +// EVEnergyDemand mocks base method. +func (m *MockEmobilityI) EVEnergyDemand() (EVDemand, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVEnergyDemand") + ret0, _ := ret[0].(EVDemand) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVEnergyDemand indicates an expected call of EVEnergyDemand. +func (mr *MockEmobilityIMockRecorder) EVEnergyDemand() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVEnergyDemand", reflect.TypeOf((*MockEmobilityI)(nil).EVEnergyDemand)) +} + +// EVIdentification mocks base method. +func (m *MockEmobilityI) EVIdentification() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVIdentification") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVIdentification indicates an expected call of EVIdentification. +func (mr *MockEmobilityIMockRecorder) EVIdentification() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIdentification", reflect.TypeOf((*MockEmobilityI)(nil).EVIdentification)) +} + +// EVIncentiveConstraints mocks base method. +func (m *MockEmobilityI) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVIncentiveConstraints") + ret0, _ := ret[0].(EVIncentiveSlotConstraints) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVIncentiveConstraints indicates an expected call of EVIncentiveConstraints. +func (mr *MockEmobilityIMockRecorder) EVIncentiveConstraints() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIncentiveConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVIncentiveConstraints)) +} + +// EVLoadControlObligationLimits mocks base method. +func (m *MockEmobilityI) EVLoadControlObligationLimits() ([]float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVLoadControlObligationLimits") + ret0, _ := ret[0].([]float64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVLoadControlObligationLimits indicates an expected call of EVLoadControlObligationLimits. +func (mr *MockEmobilityIMockRecorder) EVLoadControlObligationLimits() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVLoadControlObligationLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVLoadControlObligationLimits)) +} + +// EVOptimizationOfSelfConsumptionSupported mocks base method. +func (m *MockEmobilityI) EVOptimizationOfSelfConsumptionSupported() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVOptimizationOfSelfConsumptionSupported") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVOptimizationOfSelfConsumptionSupported indicates an expected call of EVOptimizationOfSelfConsumptionSupported. +func (mr *MockEmobilityIMockRecorder) EVOptimizationOfSelfConsumptionSupported() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVOptimizationOfSelfConsumptionSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVOptimizationOfSelfConsumptionSupported)) +} + +// EVPowerPerPhase mocks base method. +func (m *MockEmobilityI) EVPowerPerPhase() ([]float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVPowerPerPhase") + ret0, _ := ret[0].([]float64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVPowerPerPhase indicates an expected call of EVPowerPerPhase. +func (mr *MockEmobilityIMockRecorder) EVPowerPerPhase() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVPowerPerPhase", reflect.TypeOf((*MockEmobilityI)(nil).EVPowerPerPhase)) +} + +// EVSoC mocks base method. +func (m *MockEmobilityI) EVSoC() (float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVSoC") + ret0, _ := ret[0].(float64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVSoC indicates an expected call of EVSoC. +func (mr *MockEmobilityIMockRecorder) EVSoC() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoC", reflect.TypeOf((*MockEmobilityI)(nil).EVSoC)) +} + +// EVSoCSupported mocks base method. +func (m *MockEmobilityI) EVSoCSupported() (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVSoCSupported") + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVSoCSupported indicates an expected call of EVSoCSupported. +func (mr *MockEmobilityIMockRecorder) EVSoCSupported() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoCSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVSoCSupported)) +} + +// EVTimeSlotConstraints mocks base method. +func (m *MockEmobilityI) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVTimeSlotConstraints") + ret0, _ := ret[0].(EVTimeSlotConstraints) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EVTimeSlotConstraints indicates an expected call of EVTimeSlotConstraints. +func (mr *MockEmobilityIMockRecorder) EVTimeSlotConstraints() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVTimeSlotConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVTimeSlotConstraints)) +} + +// EVWriteIncentives mocks base method. +func (m *MockEmobilityI) EVWriteIncentives(data []EVDurationSlotValue) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVWriteIncentives", data) + ret0, _ := ret[0].(error) + return ret0 +} + +// EVWriteIncentives indicates an expected call of EVWriteIncentives. +func (mr *MockEmobilityIMockRecorder) EVWriteIncentives(data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteIncentives", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteIncentives), data) +} + +// EVWriteLoadControlLimits mocks base method. +func (m *MockEmobilityI) EVWriteLoadControlLimits(limits []EVLoadLimits) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVWriteLoadControlLimits", limits) + ret0, _ := ret[0].(error) + return ret0 +} + +// EVWriteLoadControlLimits indicates an expected call of EVWriteLoadControlLimits. +func (mr *MockEmobilityIMockRecorder) EVWriteLoadControlLimits(limits any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteLoadControlLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteLoadControlLimits), limits) +} + +// EVWritePowerLimits mocks base method. +func (m *MockEmobilityI) EVWritePowerLimits(data []EVDurationSlotValue) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EVWritePowerLimits", data) + ret0, _ := ret[0].(error) + return ret0 +} + +// EVWritePowerLimits indicates an expected call of EVWritePowerLimits. +func (mr *MockEmobilityIMockRecorder) EVWritePowerLimits(data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWritePowerLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWritePowerLimits), data) +} From ced9f2d9488b544ca9df7e8ceee371e14248b236 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 11:41:50 +0100 Subject: [PATCH 036/227] Update to latest eebus-go repo restructuring --- cem/cem.go | 9 ++-- cmd/main.go | 25 +++++------ emobility/emobility.go | 17 ++++---- emobility/evcoordinatedcharging_test.go | 12 +++--- emobility/events.go | 26 ++++++------ emobility/helper_test.go | 42 ++++++++++--------- emobility/public.go | 4 +- emobility/public_EVChargePlan_test.go | 2 +- emobility/public_EVChargeStrategy_test.go | 2 +- emobility/public_EVChargedEnergy_test.go | 2 +- .../public_EVCommunicationStandard_test.go | 2 +- emobility/public_EVConnectedPhases_test.go | 2 +- ...lic_EVCoordinatedChargingSupported_test.go | 2 +- emobility/public_EVCurrentChargeState_test.go | 2 +- emobility/public_EVCurrentLimits_test.go | 2 +- emobility/public_EVCurrentsPerPhase_test.go | 2 +- emobility/public_EVEnergyDemand_test.go | 2 +- emobility/public_EVIdentification_test.go | 2 +- .../public_EVIncentiveConstraints_test.go | 2 +- ...mizationOfSelfConsumptionSupported_test.go | 2 +- emobility/public_EVPowerPerPhase_test.go | 2 +- emobility/public_EVSoCSupported_test.go | 2 +- emobility/public_EVSoC_test.go | 2 +- .../public_EVTimeSlotConstraints_test.go | 2 +- emobility/public_EVWriteIncentives_test.go | 2 +- .../public_EVWriteLoadControlLimits_test.go | 2 +- emobility/public_EVWritePowerLimits_test.go | 2 +- emobility/results.go | 8 ++-- emobility/scenario.go | 12 +++--- emobility/types.go | 2 +- go.mod | 12 ++++-- go.sum | 22 ++++++---- grid/events.go | 24 +++++------ grid/grid.go | 15 +++---- grid/public.go | 2 +- grid/results.go | 4 +- grid/scenario.go | 12 +++--- inverterbatteryvis/events.go | 24 +++++------ inverterbatteryvis/invertervis.go | 15 +++---- inverterbatteryvis/public.go | 2 +- inverterbatteryvis/results.go | 4 +- inverterbatteryvis/scenario.go | 12 +++--- inverterpvvis/events.go | 24 +++++------ inverterpvvis/invertervis.go | 15 +++---- inverterpvvis/public.go | 2 +- inverterpvvis/results.go | 4 +- inverterpvvis/scenario.go | 12 +++--- scenarios/types.go | 8 ++-- util/helper.go | 10 ++--- 49 files changed, 219 insertions(+), 201 deletions(-) diff --git a/cem/cem.go b/cem/cem.go index 062c23f..13077b8 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -1,19 +1,20 @@ package cem import ( - "github.com/enbility/eebus-go/logging" + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/model" ) // Generic CEM implementation type CemImpl struct { - Service *service.EEBUSService + Service api.EEBUSService Currency model.CurrencyType } -func NewCEM(serviceDescription *service.Configuration, serviceHandler service.EEBUSServiceHandler, log logging.Logging) *CemImpl { +func NewCEM(serviceDescription *api.Configuration, serviceHandler api.EEBUSServiceHandler, log logging.Logging) *CemImpl { cem := &CemImpl{ Service: service.NewEEBUSService(serviceDescription, serviceHandler), Currency: model.CurrencyTypeEur, diff --git a/cmd/main.go b/cmd/main.go index eb5e41c..5eb8532 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,9 +16,10 @@ import ( "github.com/enbility/cemd/inverterbatteryvis" "github.com/enbility/cemd/inverterpvvis" "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/model" ) type DemoCem struct { @@ -27,7 +28,7 @@ type DemoCem struct { emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario scenarios.ScenariosI } -func NewDemoCem(configuration *service.Configuration) *DemoCem { +func NewDemoCem(configuration *api.Configuration) *DemoCem { demo := &DemoCem{} demo.cem = cem.NewCEM(configuration, demo, demo) @@ -64,22 +65,22 @@ func (d *DemoCem) Setup() error { } // report the Ship ID of a newly trusted connection -func (d *DemoCem) RemoteServiceShipIDReported(service *service.EEBUSService, ski string, shipID string) { +func (d *DemoCem) RemoteServiceShipIDReported(service api.EEBUSService, ski string, shipID string) { // we should associated the Ship ID with the SKI and store it // so the next connection can start trusted logging.Log().Info("SKI", ski, "has Ship ID:", shipID) } -func (d *DemoCem) RemoteSKIConnected(service *service.EEBUSService, ski string) {} +func (d *DemoCem) RemoteSKIConnected(service api.EEBUSService, ski string) {} -func (d *DemoCem) RemoteSKIDisconnected(service *service.EEBUSService, ski string) {} +func (d *DemoCem) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} -func (d *DemoCem) VisibleRemoteServicesUpdated(service *service.EEBUSService, entries []service.RemoteService) { +func (d *DemoCem) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { } func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *service.ConnectionStateDetail) {} +func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) {} func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } @@ -144,7 +145,7 @@ func main() { certificate, err := tls.LoadX509KeyPair(*crt, *key) if err != nil { - certificate, err = service.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-10") + certificate, err = cert.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-10") if err != nil { log.Fatal(err) } @@ -152,7 +153,7 @@ func main() { fmt.Println("Using certificate file", *crt, "and key file", *key) } - configuration, err := service.NewConfiguration( + configuration, err := api.NewConfiguration( "Demo", "Demo", "HEMS", @@ -180,7 +181,7 @@ func main() { return } - remoteService := service.NewServiceDetails(*remoteSki) + remoteService := api.NewServiceDetails(*remoteSki) demo.emobilityScenario.RegisterRemoteDevice(remoteService, nil) // Clean exit to make sure mdns shutdown is invoked diff --git a/emobility/emobility.go b/emobility/emobility.go index 9376583..be61ef7 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -1,11 +1,12 @@ package emobility import ( + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) //go:generate mockgen -source emobility.go -destination mock_emobility_test.go -package emobility @@ -231,12 +232,12 @@ type EmobilityI interface { } type EMobilityImpl struct { - entity spine.EntityLocal + entity spineapi.EntityLocal - service *service.EEBUSService + service api.EEBUSService - evseEntity spine.EntityRemote - evEntity spine.EntityRemote + evseEntity spineapi.EntityRemote + evEntity spineapi.EntityRemote evseDeviceClassification *features.DeviceClassification evseDeviceDiagnosis *features.DeviceDiagnosis @@ -263,7 +264,7 @@ type EMobilityImpl struct { var _ EmobilityI = (*EMobilityImpl)(nil) // Add E-Mobility support -func NewEMobility(service *service.EEBUSService, details *service.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { +func NewEMobility(service api.EEBUSService, details *api.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index 3187068..89c1b4f 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" gomock "go.uber.org/mock/gomock" ) @@ -276,7 +276,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.Nil(t, err) } -func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { +func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { cmd := []model.CmdType{{ TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ @@ -325,7 +325,7 @@ func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice spin assert.Nil(t, err) } -func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { +func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { cmd := []model.CmdType{{ IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ IncentiveTableDescription: []model.IncentiveTableDescriptionType{ @@ -348,7 +348,7 @@ func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice } /* -func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { +func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { cmd := []model.CmdType{{ IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ IncentiveTableDescription: []model.IncentiveTableDescriptionType{ @@ -370,7 +370,7 @@ func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevi assert.Nil(t, err) } -func requestPowerTableUpdate(t *testing.T, datagram model.DatagramType, localDevice spine.DeviceLocal, remoteDevice spine.DeviceRemote) { +func requestPowerTableUpdate(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { cmd := []model.CmdType{{ TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ diff --git a/emobility/events.go b/emobility/events.go index b3ababa..61332ad 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -4,14 +4,14 @@ import ( "time" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) // Internal EventHandler Interface for the CEM -func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { +func (e *EMobilityImpl) HandleEvent(payload api.EventPayload) { // only care about the registered SKI if payload.Ski != e.ski { return @@ -32,27 +32,27 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { } switch payload.EventType { - case spine.EventTypeDeviceChange: + case api.EventTypeDeviceChange: switch payload.ChangeType { - case spine.ElementChangeRemove: + case api.ElementChangeRemove: e.evseDisconnected() e.evDisconnected() } - case spine.EventTypeEntityChange: + case api.EventTypeEntityChange: if payload.Entity == nil { return } switch payload.ChangeType { - case spine.ElementChangeAdd: + case api.ElementChangeAdd: switch entityType { case model.EntityTypeTypeEVSE: e.evseConnected(payload.Ski, payload.Entity) case model.EntityTypeTypeEV: e.evConnected(payload.Entity) } - case spine.ElementChangeRemove: + case api.ElementChangeRemove: switch entityType { case model.EntityTypeTypeEVSE: e.evseDisconnected() @@ -61,8 +61,8 @@ func (e *EMobilityImpl) HandleEvent(payload spine.EventPayload) { } } - case spine.EventTypeDataChange: - if payload.ChangeType == spine.ElementChangeUpdate { + case api.EventTypeDataChange: + if payload.ChangeType == api.ElementChangeUpdate { switch payload.Data.(type) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: if e.evDeviceConfiguration == nil { @@ -291,7 +291,7 @@ func (e *EMobilityImpl) evRequestIncentiveValues() { } // process required steps when an evse is connected -func (e *EMobilityImpl) evseConnected(ski string, entity spine.EntityRemote) { +func (e *EMobilityImpl) evseConnected(ski string, entity api.EntityRemote) { e.evseEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -346,7 +346,7 @@ func (e *EMobilityImpl) evDisconnected() { } // an EV was connected, trigger required communication -func (e *EMobilityImpl) evConnected(entity spine.EntityRemote) { +func (e *EMobilityImpl) evConnected(entity api.EntityRemote) { e.evEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 2b78c58..c618687 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -6,11 +6,15 @@ import ( "sync" "time" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type WriteMessageHandler struct { @@ -19,7 +23,7 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ spine.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ shipapi.SpineDataConnection = (*WriteMessageHandler)(nil) func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { t.mux.Lock() @@ -94,7 +98,7 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg const remoteSki string = "testremoteski" // we don't want to handle events in these tests for now, so we don't use NewEMobility(...) -func NewTestEMobility(service *service.EEBUSService, details *service.ServiceDetails) *EMobilityImpl { +func NewTestEMobility(service api.EEBUSService, details *api.ServiceDetails) *EMobilityImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().Entity([]model.AddressEntityType{1}) @@ -109,21 +113,21 @@ func NewTestEMobility(service *service.EEBUSService, details *service.ServiceDet return emobility } -func setupEmobility() (*EMobilityImpl, *service.EEBUSService) { - cert, _ := service.CreateCertificate("test", "test", "DE", "test") - configuration, _ := service.NewConfiguration( +func setupEmobility() (*EMobilityImpl, api.EEBUSService) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := api.NewConfiguration( "test", "test", "test", "test", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, 9999, cert, 230.0, time.Second*4) eebusService := service.NewEEBUSService(configuration, nil) _ = eebusService.Setup() - details := service.NewServiceDetails(remoteSki) + details := api.NewServiceDetails(remoteSki) emobility := NewTestEMobility(eebusService, details) return emobility, eebusService } -func setupDevices(eebusService *service.EEBUSService) (spine.DeviceLocal, spine.EntityLocal, spine.DeviceRemote, []spine.EntityRemote, *WriteMessageHandler) { +func setupDevices(eebusService api.EEBUSService) (spineapi.DeviceLocal, spineapi.EntityLocal, spineapi.DeviceRemote, []spineapi.EntityRemote, *WriteMessageHandler) { localDevice := eebusService.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -274,7 +278,7 @@ func setupDevices(eebusService *service.EEBUSService) (spine.DeviceLocal, spine. return localDevice, localEntity, remoteDevice, entities, writeHandler } -func datagramForEntityAndFeatures(notify bool, localDevice spine.DeviceLocal, localEntity spine.EntityLocal, remoteEntity spine.EntityRemote, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { +func datagramForEntityAndFeatures(notify bool, localDevice spineapi.DeviceLocal, localEntity spineapi.EntityLocal, remoteEntity spineapi.EntityRemote, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { var addressSource, addressDestination *model.FeatureAddressType if remoteEntity == nil { // NodeManagement @@ -313,7 +317,7 @@ func datagramForEntityAndFeatures(notify bool, localDevice spine.DeviceLocal, lo return datagram } -func featureOfTypeAndRole(entity spine.EntityRemote, featureType model.FeatureTypeType, role model.RoleType) spine.FeatureRemote { +func featureOfTypeAndRole(entity spineapi.EntityRemote, featureType model.FeatureTypeType, role model.RoleType) spineapi.FeatureRemote { for _, f := range entity.Features() { if f.Type() == featureType && f.Role() == role { return f @@ -322,7 +326,7 @@ func featureOfTypeAndRole(entity spine.EntityRemote, featureType model.FeatureTy return nil } -func deviceDiagnosis(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.DeviceDiagnosis { +func deviceDiagnosis(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.DeviceDiagnosis { feature, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -330,7 +334,7 @@ func deviceDiagnosis(localEntity spine.EntityLocal, entity spine.EntityRemote) * return feature } -func electricalConnection(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.ElectricalConnection { +func electricalConnection(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.ElectricalConnection { feature, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -338,7 +342,7 @@ func electricalConnection(localEntity spine.EntityLocal, entity spine.EntityRemo return feature } -func measurement(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.Measurement { +func measurement(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.Measurement { feature, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -346,7 +350,7 @@ func measurement(localEntity spine.EntityLocal, entity spine.EntityRemote) *feat return feature } -func deviceConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.DeviceConfiguration { +func deviceConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.DeviceConfiguration { feature, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -354,7 +358,7 @@ func deviceConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemot return feature } -func identificationConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.Identification { +func identificationConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.Identification { feature, err := features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -362,7 +366,7 @@ func identificationConfiguration(localEntity spine.EntityLocal, entity spine.Ent return feature } -func loadcontrol(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.LoadControl { +func loadcontrol(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.LoadControl { feature, err := features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -370,7 +374,7 @@ func loadcontrol(localEntity spine.EntityLocal, entity spine.EntityRemote) *feat return feature } -func timeSeriesConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.TimeSeries { +func timeSeriesConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.TimeSeries { feature, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -378,7 +382,7 @@ func timeSeriesConfiguration(localEntity spine.EntityLocal, entity spine.EntityR return feature } -func incentiveTableConfiguration(localEntity spine.EntityLocal, entity spine.EntityRemote) *features.IncentiveTable { +func incentiveTableConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.IncentiveTable { feature, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) diff --git a/emobility/public.go b/emobility/public.go index e60ab91..b035753 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -6,8 +6,8 @@ import ( "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/spine/model" eebusUtil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" ) // return if an EV is connected @@ -177,7 +177,7 @@ func (e *EMobilityImpl) EVPowerPerPhase() ([]float64, error) { phaseValue := item.Value.GetValue() if !powerAvailable { - phaseValue *= e.service.Configuration.Voltage() + phaseValue *= e.service.Configuration().Voltage() } result = append(result, phaseValue) diff --git a/emobility/public_EVChargePlan_test.go b/emobility/public_EVChargePlan_test.go index cbdad83..0921bce 100644 --- a/emobility/public_EVChargePlan_test.go +++ b/emobility/public_EVChargePlan_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" gomock "go.uber.org/mock/gomock" ) diff --git a/emobility/public_EVChargeStrategy_test.go b/emobility/public_EVChargeStrategy_test.go index 8e94c76..11899c7 100644 --- a/emobility/public_EVChargeStrategy_test.go +++ b/emobility/public_EVChargeStrategy_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVChargedEnergy_test.go b/emobility/public_EVChargedEnergy_test.go index 91f8ff9..a52c199 100644 --- a/emobility/public_EVChargedEnergy_test.go +++ b/emobility/public_EVChargedEnergy_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVCommunicationStandard_test.go b/emobility/public_EVCommunicationStandard_test.go index 0bde89c..1e2f8c7 100644 --- a/emobility/public_EVCommunicationStandard_test.go +++ b/emobility/public_EVCommunicationStandard_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVConnectedPhases_test.go b/emobility/public_EVConnectedPhases_test.go index 51d4ee9..d023099 100644 --- a/emobility/public_EVConnectedPhases_test.go +++ b/emobility/public_EVConnectedPhases_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVCoordinatedChargingSupported_test.go b/emobility/public_EVCoordinatedChargingSupported_test.go index b45b71a..1c5c212 100644 --- a/emobility/public_EVCoordinatedChargingSupported_test.go +++ b/emobility/public_EVCoordinatedChargingSupported_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVCurrentChargeState_test.go b/emobility/public_EVCurrentChargeState_test.go index 3b99afb..05d3afe 100644 --- a/emobility/public_EVCurrentChargeState_test.go +++ b/emobility/public_EVCurrentChargeState_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVCurrentLimits_test.go b/emobility/public_EVCurrentLimits_test.go index 548688e..5f7a8d5 100644 --- a/emobility/public_EVCurrentLimits_test.go +++ b/emobility/public_EVCurrentLimits_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVCurrentsPerPhase_test.go b/emobility/public_EVCurrentsPerPhase_test.go index a56b429..1dcc34b 100644 --- a/emobility/public_EVCurrentsPerPhase_test.go +++ b/emobility/public_EVCurrentsPerPhase_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVEnergyDemand_test.go b/emobility/public_EVEnergyDemand_test.go index b45f765..c54dbd5 100644 --- a/emobility/public_EVEnergyDemand_test.go +++ b/emobility/public_EVEnergyDemand_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVIdentification_test.go b/emobility/public_EVIdentification_test.go index 75f17d9..614712d 100644 --- a/emobility/public_EVIdentification_test.go +++ b/emobility/public_EVIdentification_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go index 8662720..684565e 100644 --- a/emobility/public_EVIncentiveConstraints_test.go +++ b/emobility/public_EVIncentiveConstraints_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go index 7a6128d..4dc9115 100644 --- a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go +++ b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVPowerPerPhase_test.go b/emobility/public_EVPowerPerPhase_test.go index faf6b24..6ef6425 100644 --- a/emobility/public_EVPowerPerPhase_test.go +++ b/emobility/public_EVPowerPerPhase_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVSoCSupported_test.go b/emobility/public_EVSoCSupported_test.go index 39d1844..22e3cfe 100644 --- a/emobility/public_EVSoCSupported_test.go +++ b/emobility/public_EVSoCSupported_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVSoC_test.go b/emobility/public_EVSoC_test.go index 7249de5..498b855 100644 --- a/emobility/public_EVSoC_test.go +++ b/emobility/public_EVSoC_test.go @@ -3,8 +3,8 @@ package emobility import ( "testing" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go index cf6faf4..55b5197 100644 --- a/emobility/public_EVTimeSlotConstraints_test.go +++ b/emobility/public_EVTimeSlotConstraints_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVWriteIncentives_test.go b/emobility/public_EVWriteIncentives_test.go index ebab206..ffd3f63 100644 --- a/emobility/public_EVWriteIncentives_test.go +++ b/emobility/public_EVWriteIncentives_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/public_EVWriteLoadControlLimits_test.go b/emobility/public_EVWriteLoadControlLimits_test.go index 579adba..566203b 100644 --- a/emobility/public_EVWriteLoadControlLimits_test.go +++ b/emobility/public_EVWriteLoadControlLimits_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/spine/model" eebusUtil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "golang.org/x/exp/slices" ) diff --git a/emobility/public_EVWritePowerLimits_test.go b/emobility/public_EVWritePowerLimits_test.go index a3e236e..5b00b6c 100644 --- a/emobility/public_EVWritePowerLimits_test.go +++ b/emobility/public_EVWritePowerLimits_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) diff --git a/emobility/results.go b/emobility/results.go index b57f4b9..ec77c77 100644 --- a/emobility/results.go +++ b/emobility/results.go @@ -1,11 +1,11 @@ package emobility import ( - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) -func (e *EMobilityImpl) HandleResult(errorMsg spine.ResultMessage) { +func (e *EMobilityImpl) HandleResult(errorMsg api.ResultMessage) { isEvse := errorMsg.EntityRemote == e.evseEntity isEv := e.evEntity != nil && errorMsg.EntityRemote == e.evEntity @@ -20,7 +20,7 @@ func (e *EMobilityImpl) HandleResult(errorMsg spine.ResultMessage) { } // Handle DeviceDiagnosis Results -func (e *EMobilityImpl) handleResultDeviceDiagnosis(resultMsg spine.ResultMessage) { +func (e *EMobilityImpl) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { // is this an error for a heartbeat message? if *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { return diff --git a/emobility/scenario.go b/emobility/scenario.go index c8941e3..c90c428 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -4,10 +4,10 @@ import ( "sync" "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type EmobilityScenarioImpl struct { @@ -23,7 +23,7 @@ type EmobilityScenarioImpl struct { var _ scenarios.ScenariosI = (*EmobilityScenarioImpl)(nil) -func NewEMobilityScenario(service *service.EEBUSService, currency model.CurrencyType, configuration EmobilityConfiguration) *EmobilityScenarioImpl { +func NewEMobilityScenario(service api.EEBUSService, currency model.CurrencyType, configuration EmobilityConfiguration) *EmobilityScenarioImpl { return &EmobilityScenarioImpl{ ScenarioImpl: scenarios.NewScenarioImpl(service), remoteDevices: make(map[string]*EMobilityImpl), @@ -135,7 +135,7 @@ func (e *EmobilityScenarioImpl) AddUseCases() { } } -func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *service.ServiceDetails, dataProvider any) any { +func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { // TODO: emobility should be stored per remote SKI and // only be set for the SKI if the device supports it e.mux.Lock() @@ -163,7 +163,7 @@ func (e *EmobilityScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { e.Service.RegisterRemoteSKI(remoteDeviceSki, false) } -func (e *EmobilityScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { +func (e *EmobilityScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { e.mux.Lock() defer e.mux.Unlock() diff --git a/emobility/types.go b/emobility/types.go index ecad530..f3dd634 100644 --- a/emobility/types.go +++ b/emobility/types.go @@ -4,7 +4,7 @@ import ( "errors" "time" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/model" ) type EVCommunicationStandardType model.DeviceConfigurationKeyValueStringType diff --git a/go.mod b/go.mod index 1d1c528..ffc00fc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,9 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240109191414-52f14d2033f6 + github.com/enbility/eebus-go v0.0.0-20240115104025-e009d60ba532 + github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 + github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b @@ -23,8 +25,10 @@ require ( github.com/rickb777/plural v1.4.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// replace github.com/enbility/eebus-go => ../eebus-go diff --git a/go.sum b/go.sum index 0534415..872984f 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240109191414-52f14d2033f6 h1:0gxTlkvgJHmG9lC5AvGxEQcbgXo0eh0foP8UOlpGLeE= -github.com/enbility/eebus-go v0.0.0-20240109191414-52f14d2033f6/go.mod h1:LQwgXW3RJm+j9x2Tn5C7G4hHxCMeZRDKup99eJkkFl4= +github.com/enbility/eebus-go v0.0.0-20240115104025-e009d60ba532 h1:NI0sKmrlU0RFxDQNgL0Ys8SAiw2UxLYce6LNw1kLT+c= +github.com/enbility/eebus-go v0.0.0-20240115104025-e009d60ba532/go.mod h1:3YCuDrQ9mzndKI2s0fI7sFgluZH3aDIwIh6fxHQD7gc= +github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 h1:QyiMPmuUTL5TCe2Ax4anLtzEn7gvsP/hPjEwfpMqmWM= +github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6/go.mod h1:ZlzDQ8pDzhxT9r+6XMmU50fzgyuFnj9RyWWugJRB8s0= +github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= +github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -52,13 +56,13 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -68,8 +72,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -88,8 +92,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/grid/events.go b/grid/events.go index fb94ee1..5ffb532 100644 --- a/grid/events.go +++ b/grid/events.go @@ -2,13 +2,13 @@ package grid import ( "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) // Internal EventHandler Interface for the CEM -func (e *GridImpl) HandleEvent(payload spine.EventPayload) { +func (e *GridImpl) HandleEvent(payload api.EventPayload) { // we only care about the registered SKI if payload.Ski != e.ski { return @@ -20,30 +20,30 @@ func (e *GridImpl) HandleEvent(payload spine.EventPayload) { } switch payload.EventType { - case spine.EventTypeDeviceChange: + case api.EventTypeDeviceChange: switch payload.ChangeType { - case spine.ElementChangeRemove: + case api.ElementChangeRemove: e.gridDisconnected() } - case spine.EventTypeEntityChange: + case api.EventTypeEntityChange: entityType := payload.Entity.EntityType() switch payload.ChangeType { - case spine.ElementChangeAdd: + case api.ElementChangeAdd: switch entityType { case model.EntityTypeTypeGridConnectionPointOfPremises: e.gridConnected(payload.Ski, payload.Entity) } - case spine.ElementChangeRemove: + case api.ElementChangeRemove: switch entityType { case model.EntityTypeTypeGridConnectionPointOfPremises: e.gridDisconnected() } } - case spine.EventTypeDataChange: - if payload.ChangeType == spine.ElementChangeUpdate { + case api.EventTypeDataChange: + if payload.ChangeType == api.ElementChangeUpdate { switch payload.Data.(type) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: @@ -64,7 +64,7 @@ func (e *GridImpl) HandleEvent(payload spine.EventPayload) { } // process required steps when a grid device is connected -func (e *GridImpl) gridConnected(ski string, entity spine.EntityRemote) { +func (e *GridImpl) gridConnected(ski string, entity api.EntityRemote) { e.gridEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) diff --git a/grid/grid.go b/grid/grid.go index 28d63ea..70b7b3e 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -1,11 +1,12 @@ package grid import ( + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type GridI interface { @@ -19,11 +20,11 @@ type GridI interface { } type GridImpl struct { - entity spine.EntityLocal + entity spineapi.EntityLocal - service *service.EEBUSService + service api.EEBUSService - gridEntity spine.EntityRemote + gridEntity spineapi.EntityRemote gridDeviceConfiguration *features.DeviceConfiguration gridElectricalConnection *features.ElectricalConnection @@ -35,7 +36,7 @@ type GridImpl struct { var _ GridI = (*GridImpl)(nil) // Add Grid support -func NewGrid(service *service.EEBUSService, details *service.ServiceDetails) *GridImpl { +func NewGrid(service api.EEBUSService, details *api.ServiceDetails) *GridImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/grid/public.go b/grid/public.go index 31d9124..9f9fa07 100644 --- a/grid/public.go +++ b/grid/public.go @@ -3,7 +3,7 @@ package grid import ( "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/model" ) // return the power limitation factor diff --git a/grid/results.go b/grid/results.go index 0d6a6fa..62f8910 100644 --- a/grid/results.go +++ b/grid/results.go @@ -1,8 +1,8 @@ package grid import ( - "github.com/enbility/eebus-go/spine" + "github.com/enbility/spine-go/api" ) -func (e *GridImpl) HandleResult(errorMsg spine.ResultMessage) { +func (e *GridImpl) HandleResult(errorMsg api.ResultMessage) { } diff --git a/grid/scenario.go b/grid/scenario.go index 0cab638..71d0fcb 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -4,9 +4,9 @@ import ( "sync" "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type GridScenarioImpl struct { @@ -19,7 +19,7 @@ type GridScenarioImpl struct { var _ scenarios.ScenariosI = (*GridScenarioImpl)(nil) -func NewGridScenario(service *service.EEBUSService) *GridScenarioImpl { +func NewGridScenario(service api.EEBUSService) *GridScenarioImpl { return &GridScenarioImpl{ ScenarioImpl: scenarios.NewScenarioImpl(service), remoteDevices: make(map[string]*GridImpl), @@ -55,7 +55,7 @@ func (e *GridScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) } -func (e *GridScenarioImpl) RegisterRemoteDevice(details *service.ServiceDetails, dataProvider any) any { +func (e *GridScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { // TODO: grid should be stored per remote SKI and // only be set for the SKI if the device supports it e.mux.Lock() @@ -79,7 +79,7 @@ func (e *GridScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { e.Service.RegisterRemoteSKI(remoteDeviceSki, false) } -func (e *GridScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { +func (e *GridScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { e.mux.Lock() defer e.mux.Unlock() diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index cd2f65e..2c9d1a5 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -2,13 +2,13 @@ package inverterbatteryvis import ( "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) // Internal EventHandler Interface for the CEM -func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { +func (i *InverterBatteryVisImpl) HandleEvent(payload api.EventPayload) { // we only care about the registered SKI if payload.Ski != i.ski { return @@ -20,28 +20,28 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { } switch payload.EventType { - case spine.EventTypeDeviceChange: + case api.EventTypeDeviceChange: switch payload.ChangeType { - case spine.ElementChangeRemove: + case api.ElementChangeRemove: i.inverterDisconnected() } - case spine.EventTypeEntityChange: + case api.EventTypeEntityChange: entityType := payload.Entity.EntityType() if entityType != model.EntityTypeTypeBatterySystem { return } switch payload.ChangeType { - case spine.ElementChangeAdd: + case api.ElementChangeAdd: i.inverterConnected(payload.Ski, payload.Entity) - case spine.ElementChangeRemove: + case api.ElementChangeRemove: i.inverterDisconnected() } - case spine.EventTypeDataChange: - if payload.ChangeType != spine.ElementChangeUpdate { + case api.EventTypeDataChange: + if payload.ChangeType != api.ElementChangeUpdate { return } @@ -79,7 +79,7 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload spine.EventPayload) { } // process required steps when a battery device entity is connected -func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity spine.EntityRemote) { +func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity api.EntityRemote) { i.inverterEntity = entity localDevice := i.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index 80bbdd3..2134de2 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -1,11 +1,12 @@ package inverterbatteryvis import ( + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type InverterBatteryVisI interface { @@ -16,11 +17,11 @@ type InverterBatteryVisI interface { } type InverterBatteryVisImpl struct { - entity spine.EntityLocal + entity spineapi.EntityLocal - service *service.EEBUSService + service api.EEBUSService - inverterEntity spine.EntityRemote + inverterEntity spineapi.EntityRemote inverterElectricalConnection *features.ElectricalConnection inverterMeasurement *features.Measurement @@ -30,7 +31,7 @@ type InverterBatteryVisImpl struct { var _ InverterBatteryVisI = (*InverterBatteryVisImpl)(nil) // Add InverterBatteryVis support -func NewInverterBatteryVis(service *service.EEBUSService, details *service.ServiceDetails) *InverterBatteryVisImpl { +func NewInverterBatteryVis(service api.EEBUSService, details *api.ServiceDetails) *InverterBatteryVisImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterbatteryvis/public.go b/inverterbatteryvis/public.go index 339b099..9d818fb 100644 --- a/inverterbatteryvis/public.go +++ b/inverterbatteryvis/public.go @@ -3,7 +3,7 @@ package inverterbatteryvis import ( "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/model" ) // return the current battery (dis-)charge power (W) diff --git a/inverterbatteryvis/results.go b/inverterbatteryvis/results.go index 14eb3bb..e4bea8c 100644 --- a/inverterbatteryvis/results.go +++ b/inverterbatteryvis/results.go @@ -1,8 +1,8 @@ package inverterbatteryvis import ( - "github.com/enbility/eebus-go/spine" + "github.com/enbility/spine-go/api" ) -func (i *InverterBatteryVisImpl) HandleResult(errorMsg spine.ResultMessage) { +func (i *InverterBatteryVisImpl) HandleResult(errorMsg api.ResultMessage) { } diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index 3729e97..775c8e3 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -4,9 +4,9 @@ import ( "sync" "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type InverterBatteryVisScenarioImpl struct { @@ -19,7 +19,7 @@ type InverterBatteryVisScenarioImpl struct { var _ scenarios.ScenariosI = (*InverterBatteryVisScenarioImpl)(nil) -func NewInverterVisScenario(service *service.EEBUSService) *InverterBatteryVisScenarioImpl { +func NewInverterVisScenario(service api.EEBUSService) *InverterBatteryVisScenarioImpl { return &InverterBatteryVisScenarioImpl{ ScenarioImpl: scenarios.NewScenarioImpl(service), remoteDevices: make(map[string]*InverterBatteryVisImpl), @@ -55,7 +55,7 @@ func (i *InverterBatteryVisScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3, 4}) } -func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *service.ServiceDetails, dataProvider any) any { +func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { // TODO: invertervis should be stored per remote SKI and // only be set for the SKI if the device supports it i.mux.Lock() @@ -79,7 +79,7 @@ func (i *InverterBatteryVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki i.Service.RegisterRemoteSKI(remoteDeviceSki, false) } -func (i *InverterBatteryVisScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { +func (i *InverterBatteryVisScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { i.mux.Lock() defer i.mux.Unlock() diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index 39c1c9a..66385aa 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -2,13 +2,13 @@ package inverterpvvis import ( "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/logging" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) // Internal EventHandler Interface for the CEM -func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { +func (i *InverterPVVisImpl) HandleEvent(payload api.EventPayload) { // we only care about the registered SKI if payload.Ski != i.ski { return @@ -20,28 +20,28 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { } switch payload.EventType { - case spine.EventTypeDeviceChange: + case api.EventTypeDeviceChange: switch payload.ChangeType { - case spine.ElementChangeRemove: + case api.ElementChangeRemove: i.inverterDisconnected() } - case spine.EventTypeEntityChange: + case api.EventTypeEntityChange: entityType := payload.Entity.EntityType() if entityType != model.EntityTypeTypeBatterySystem { return } switch payload.ChangeType { - case spine.ElementChangeAdd: + case api.ElementChangeAdd: i.inverterConnected(payload.Ski, payload.Entity) - case spine.ElementChangeRemove: + case api.ElementChangeRemove: i.inverterDisconnected() } - case spine.EventTypeDataChange: - if payload.ChangeType != spine.ElementChangeUpdate { + case api.EventTypeDataChange: + if payload.ChangeType != api.ElementChangeUpdate { return } @@ -89,7 +89,7 @@ func (i *InverterPVVisImpl) HandleEvent(payload spine.EventPayload) { } // process required steps when a pv device entity is connected -func (e *InverterPVVisImpl) inverterConnected(ski string, entity spine.EntityRemote) { +func (e *InverterPVVisImpl) inverterConnected(ski string, entity api.EntityRemote) { e.inverterEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 04f9f81..453cbdc 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -1,11 +1,12 @@ package inverterpvvis import ( + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) type InverterPVVisI interface { @@ -15,11 +16,11 @@ type InverterPVVisI interface { } type InverterPVVisImpl struct { - entity spine.EntityLocal + entity spineapi.EntityLocal - service *service.EEBUSService + service api.EEBUSService - inverterEntity spine.EntityRemote + inverterEntity spineapi.EntityRemote inverterDeviceConfiguration *features.DeviceConfiguration inverterElectricalConnection *features.ElectricalConnection inverterMeasurement *features.Measurement @@ -30,7 +31,7 @@ type InverterPVVisImpl struct { var _ InverterPVVisI = (*InverterPVVisImpl)(nil) // Add InverterPVVis support -func NewInverterPVVis(service *service.EEBUSService, details *service.ServiceDetails) *InverterPVVisImpl { +func NewInverterPVVis(service api.EEBUSService, details *api.ServiceDetails) *InverterPVVisImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterpvvis/public.go b/inverterpvvis/public.go index 2db1d5c..a99714d 100644 --- a/inverterpvvis/public.go +++ b/inverterpvvis/public.go @@ -3,7 +3,7 @@ package inverterpvvis import ( "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/spine-go/model" ) // return the current photovoltaic production power (W) diff --git a/inverterpvvis/results.go b/inverterpvvis/results.go index 1d02bbd..0fea29f 100644 --- a/inverterpvvis/results.go +++ b/inverterpvvis/results.go @@ -1,8 +1,8 @@ package inverterpvvis import ( - "github.com/enbility/eebus-go/spine" + "github.com/enbility/spine-go/api" ) -func (i *InverterPVVisImpl) HandleResult(errorMsg spine.ResultMessage) { +func (i *InverterPVVisImpl) HandleResult(errorMsg api.ResultMessage) { } diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index 6babec4..86b3f72 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -4,9 +4,9 @@ import ( "sync" "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) type InverterPVVisScenarioImpl struct { @@ -19,7 +19,7 @@ type InverterPVVisScenarioImpl struct { var _ scenarios.ScenariosI = (*InverterPVVisScenarioImpl)(nil) -func NewInverterVisScenario(service *service.EEBUSService) *InverterPVVisScenarioImpl { +func NewInverterVisScenario(service api.EEBUSService) *InverterPVVisScenarioImpl { return &InverterPVVisScenarioImpl{ ScenarioImpl: scenarios.NewScenarioImpl(service), remoteDevices: make(map[string]*InverterPVVisImpl), @@ -55,7 +55,7 @@ func (i *InverterPVVisScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3}) } -func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *service.ServiceDetails, dataProvider any) any { +func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { // TODO: invertervis should be stored per remote SKI and // only be set for the SKI if the device supports it i.mux.Lock() @@ -79,7 +79,7 @@ func (i *InverterPVVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki strin i.Service.RegisterRemoteSKI(remoteDeviceSki, false) } -func (i *InverterPVVisScenarioImpl) HandleResult(errorMsg spine.ResultMessage) { +func (i *InverterPVVisScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { i.mux.Lock() defer i.mux.Unlock() diff --git a/scenarios/types.go b/scenarios/types.go index 03bb2b6..8ad3f3e 100644 --- a/scenarios/types.go +++ b/scenarios/types.go @@ -1,22 +1,22 @@ package scenarios import ( - "github.com/enbility/eebus-go/service" + "github.com/enbility/eebus-go/api" ) // Implemented by *ScenarioImpl, used by CemImpl type ScenariosI interface { - RegisterRemoteDevice(details *service.ServiceDetails, dataProvider any) any + RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any UnRegisterRemoteDevice(remoteDeviceSki string) AddFeatures() AddUseCases() } type ScenarioImpl struct { - Service *service.EEBUSService + Service api.EEBUSService } -func NewScenarioImpl(service *service.EEBUSService) *ScenarioImpl { +func NewScenarioImpl(service api.EEBUSService) *ScenarioImpl { return &ScenarioImpl{ Service: service, } diff --git a/util/helper.go b/util/helper.go index 03fb4ab..95c3051 100644 --- a/util/helper.go +++ b/util/helper.go @@ -1,16 +1,16 @@ package util import ( + "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/spine" - "github.com/enbility/eebus-go/spine/model" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) var PhaseNameMapping = []model.ElectricalConnectionPhaseNameType{model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC} // check if the given usecase, actor is supported by the remote device -func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorType, remoteDevice spine.DeviceRemote) bool { +func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorType, remoteDevice spineapi.DeviceRemote) bool { uci := remoteDevice.UseCases() for _, element := range uci { @@ -28,7 +28,7 @@ func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorT } // return the remote entity of a given type and device ski -func EntityOfTypeForSki(service *service.EEBUSService, entityType model.EntityTypeType, ski string) (spine.EntityRemote, error) { +func EntityOfTypeForSki(service api.EEBUSService, entityType model.EntityTypeType, ski string) (spineapi.EntityRemote, error) { rDevice := service.LocalDevice().RemoteDeviceForSki(ski) if rDevice == nil { From 9f752711208519503ee172b08643194e83859b5b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 11:46:00 +0100 Subject: [PATCH 037/227] Update go.mod --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index ffc00fc..29ffef3 100644 --- a/go.mod +++ b/go.mod @@ -30,5 +30,3 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// replace github.com/enbility/eebus-go => ../eebus-go From 6f71a9df97cac1188e0a250e7ceb21046d67ff1d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Jan 2024 13:53:54 +0100 Subject: [PATCH 038/227] Update to altest eebus-go and ship-go --- cmd/main.go | 2 +- emobility/helper_test.go | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 5eb8532..7da662f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,7 +17,7 @@ import ( "github.com/enbility/cemd/inverterpvvis" "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" + "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" "github.com/enbility/spine-go/model" ) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index c618687..60af5d2 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -7,11 +7,11 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/cert" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/util" shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" diff --git a/go.mod b/go.mod index 29ffef3..df200d3 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240115104025-e009d60ba532 - github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 + github.com/enbility/eebus-go v0.0.0-20240115124728-bbc9c5d26c85 + github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 diff --git a/go.sum b/go.sum index 872984f..db609b9 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240115104025-e009d60ba532 h1:NI0sKmrlU0RFxDQNgL0Ys8SAiw2UxLYce6LNw1kLT+c= -github.com/enbility/eebus-go v0.0.0-20240115104025-e009d60ba532/go.mod h1:3YCuDrQ9mzndKI2s0fI7sFgluZH3aDIwIh6fxHQD7gc= -github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6 h1:QyiMPmuUTL5TCe2Ax4anLtzEn7gvsP/hPjEwfpMqmWM= -github.com/enbility/ship-go v0.0.0-20240115080029-8cabdf4011c6/go.mod h1:ZlzDQ8pDzhxT9r+6XMmU50fzgyuFnj9RyWWugJRB8s0= +github.com/enbility/eebus-go v0.0.0-20240115124728-bbc9c5d26c85 h1:3YyIi+o2k+2jYNAYJUiyP70oEaxvwBqTQ62UwiBaosA= +github.com/enbility/eebus-go v0.0.0-20240115124728-bbc9c5d26c85/go.mod h1:tcw3xtMWA6Z/BcMdSiZCy+COjbt+306FD1ylccbajnc= +github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c h1:o9u0pYg0iyVW+iLtJZ5Xpico3bfv71zNowPARy3CZDY= +github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From f1764d80e96cd44297ae35f7b40393a24035726b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 16 Jan 2024 13:22:32 +0100 Subject: [PATCH 039/227] Update to latest eebus-go and ship-go --- cmd/main.go | 7 ++++--- emobility/emobility.go | 5 +++-- emobility/helper_test.go | 6 +++--- emobility/scenario.go | 3 ++- go.mod | 4 ++-- go.sum | 8 ++++---- grid/grid.go | 5 +++-- grid/scenario.go | 3 ++- inverterbatteryvis/invertervis.go | 5 +++-- inverterbatteryvis/scenario.go | 3 ++- inverterpvvis/invertervis.go | 5 +++-- inverterpvvis/scenario.go | 3 ++- scenarios/types.go | 3 ++- 13 files changed, 35 insertions(+), 25 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 7da662f..e4bed6f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,6 +17,7 @@ import ( "github.com/enbility/cemd/inverterpvvis" "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" "github.com/enbility/spine-go/model" @@ -75,12 +76,12 @@ func (d *DemoCem) RemoteSKIConnected(service api.EEBUSService, ski string) {} func (d *DemoCem) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} -func (d *DemoCem) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []api.RemoteService) { +func (d *DemoCem) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []shipapi.RemoteService) { } func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} -func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *api.ConnectionStateDetail) {} +func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } @@ -181,7 +182,7 @@ func main() { return } - remoteService := api.NewServiceDetails(*remoteSki) + remoteService := shipapi.NewServiceDetails(*remoteSki) demo.emobilityScenario.RegisterRemoteDevice(remoteService, nil) // Clean exit to make sure mdns shutdown is invoked diff --git a/emobility/emobility.go b/emobility/emobility.go index be61ef7..9c59412 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -3,7 +3,8 @@ package emobility import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -264,7 +265,7 @@ type EMobilityImpl struct { var _ EmobilityI = (*EMobilityImpl)(nil) // Add E-Mobility support -func NewEMobility(service api.EEBUSService, details *api.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { +func NewEMobility(service api.EEBUSService, details *shipapi.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 60af5d2..2af2277 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -9,9 +9,9 @@ import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/service" - "github.com/enbility/eebus-go/util" shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" + "github.com/enbility/ship-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -98,7 +98,7 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg const remoteSki string = "testremoteski" // we don't want to handle events in these tests for now, so we don't use NewEMobility(...) -func NewTestEMobility(service api.EEBUSService, details *api.ServiceDetails) *EMobilityImpl { +func NewTestEMobility(service api.EEBUSService, details *shipapi.ServiceDetails) *EMobilityImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().Entity([]model.AddressEntityType{1}) @@ -122,7 +122,7 @@ func setupEmobility() (*EMobilityImpl, api.EEBUSService) { 9999, cert, 230.0, time.Second*4) eebusService := service.NewEEBUSService(configuration, nil) _ = eebusService.Setup() - details := api.NewServiceDetails(remoteSki) + details := shipapi.NewServiceDetails(remoteSki) emobility := NewTestEMobility(eebusService, details) return emobility, eebusService } diff --git a/emobility/scenario.go b/emobility/scenario.go index c90c428..a65162c 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -6,6 +6,7 @@ import ( "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -135,7 +136,7 @@ func (e *EmobilityScenarioImpl) AddUseCases() { } } -func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { +func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { // TODO: emobility should be stored per remote SKI and // only be set for the SKI if the device supports it e.mux.Lock() diff --git a/go.mod b/go.mod index df200d3..8e8ce2d 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240115124728-bbc9c5d26c85 - github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c + github.com/enbility/eebus-go v0.0.0-20240116121635-bb2a283dd60b + github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 diff --git a/go.sum b/go.sum index db609b9..955991d 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240115124728-bbc9c5d26c85 h1:3YyIi+o2k+2jYNAYJUiyP70oEaxvwBqTQ62UwiBaosA= -github.com/enbility/eebus-go v0.0.0-20240115124728-bbc9c5d26c85/go.mod h1:tcw3xtMWA6Z/BcMdSiZCy+COjbt+306FD1ylccbajnc= -github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c h1:o9u0pYg0iyVW+iLtJZ5Xpico3bfv71zNowPARy3CZDY= -github.com/enbility/ship-go v0.0.0-20240115124432-379b22cd4d1c/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/eebus-go v0.0.0-20240116121635-bb2a283dd60b h1:QUJWhTLTQ9urz3nEGQ8sSkrNfxcZ0goTN0CKTsHIfU8= +github.com/enbility/eebus-go v0.0.0-20240116121635-bb2a283dd60b/go.mod h1:TDNE6540hO9/BZDo4v7gme+J/oEznCj1bSWWoz/CCnA= +github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 h1:udB9WMUnI/YvoW8armfvylz0iP4ISjWdWbgI7yRO+Jk= +github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/grid/grid.go b/grid/grid.go index 70b7b3e..ffe1063 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -3,7 +3,8 @@ package grid import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -36,7 +37,7 @@ type GridImpl struct { var _ GridI = (*GridImpl)(nil) // Add Grid support -func NewGrid(service api.EEBUSService, details *api.ServiceDetails) *GridImpl { +func NewGrid(service api.EEBUSService, details *shipapi.ServiceDetails) *GridImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/grid/scenario.go b/grid/scenario.go index 71d0fcb..86210b7 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -5,6 +5,7 @@ import ( "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -55,7 +56,7 @@ func (e *GridScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) } -func (e *GridScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { +func (e *GridScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { // TODO: grid should be stored per remote SKI and // only be set for the SKI if the device supports it e.mux.Lock() diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index 2134de2..35edf50 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -3,7 +3,8 @@ package inverterbatteryvis import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -31,7 +32,7 @@ type InverterBatteryVisImpl struct { var _ InverterBatteryVisI = (*InverterBatteryVisImpl)(nil) // Add InverterBatteryVis support -func NewInverterBatteryVis(service api.EEBUSService, details *api.ServiceDetails) *InverterBatteryVisImpl { +func NewInverterBatteryVis(service api.EEBUSService, details *shipapi.ServiceDetails) *InverterBatteryVisImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index 775c8e3..b8c5daf 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -5,6 +5,7 @@ import ( "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -55,7 +56,7 @@ func (i *InverterBatteryVisScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3, 4}) } -func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { +func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { // TODO: invertervis should be stored per remote SKI and // only be set for the SKI if the device supports it i.mux.Lock() diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 453cbdc..f190e49 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -3,7 +3,8 @@ package inverterpvvis import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/util" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -31,7 +32,7 @@ type InverterPVVisImpl struct { var _ InverterPVVisI = (*InverterPVVisImpl)(nil) // Add InverterPVVis support -func NewInverterPVVis(service api.EEBUSService, details *api.ServiceDetails) *InverterPVVisImpl { +func NewInverterPVVis(service api.EEBUSService, details *shipapi.ServiceDetails) *InverterPVVisImpl { ski := util.NormalizeSKI(details.SKI) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index 86b3f72..f96bcdb 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -5,6 +5,7 @@ import ( "github.com/enbility/cemd/scenarios" "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -55,7 +56,7 @@ func (i *InverterPVVisScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3}) } -func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any { +func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { // TODO: invertervis should be stored per remote SKI and // only be set for the SKI if the device supports it i.mux.Lock() diff --git a/scenarios/types.go b/scenarios/types.go index 8ad3f3e..772e41c 100644 --- a/scenarios/types.go +++ b/scenarios/types.go @@ -2,11 +2,12 @@ package scenarios import ( "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" ) // Implemented by *ScenarioImpl, used by CemImpl type ScenariosI interface { - RegisterRemoteDevice(details *api.ServiceDetails, dataProvider any) any + RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any UnRegisterRemoteDevice(remoteDeviceSki string) AddFeatures() AddUseCases() From 0153c67ebf8fad70d621e8145c904c83665f91a0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 19 Jan 2024 13:25:36 +0100 Subject: [PATCH 040/227] Check if feature are really available to avoid panics --- emobility/events.go | 134 +++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 46 deletions(-) diff --git a/emobility/events.go b/emobility/events.go index 61332ad..6d861df 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -369,35 +369,53 @@ func (e *EMobilityImpl) evConnected(entity api.EntityRemote) { // optional requests are only logged as debug // subscribe - if err := e.evDeviceClassification.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evDeviceClassification != nil { + if err := e.evDeviceClassification.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evDeviceConfiguration.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evDeviceConfiguration != nil { + if err := e.evDeviceConfiguration.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evDeviceDiagnosis.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evDeviceDiagnosis != nil { + if err := e.evDeviceDiagnosis.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evElectricalConnection.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evElectricalConnection != nil { + if err := e.evElectricalConnection.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evMeasurement.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evMeasurement != nil { + if err := e.evMeasurement.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evLoadControl.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evLoadControl != nil { + if err := e.evLoadControl.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evIdentification.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evIdentification != nil { + if err := e.evIdentification.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } if e.configuration.CoordinatedChargingEnabled { - if err := e.evTimeSeries.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evTimeSeries != nil { + if err := e.evTimeSeries.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } // this is optional - if err := e.evIncentiveTable.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) + if e.evIncentiveTable != nil { + if err := e.evIncentiveTable.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } } } @@ -407,65 +425,89 @@ func (e *EMobilityImpl) evConnected(entity api.EntityRemote) { } if e.configuration.CoordinatedChargingEnabled { - // this is optional - if err := e.evTimeSeries.Bind(); err != nil { - logging.Log().Debug(err) + if e.evTimeSeries != nil { + // this is optional + if err := e.evTimeSeries.Bind(); err != nil { + logging.Log().Debug(err) + } } - // this is optional - if err := e.evIncentiveTable.Bind(); err != nil { - logging.Log().Debug(err) + if e.evIncentiveTable != nil { + // this is optional + if err := e.evIncentiveTable.Bind(); err != nil { + logging.Log().Debug(err) + } } } // get ev configuration data - if err := e.evDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evDeviceConfiguration != nil { + if err := e.evDeviceConfiguration.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } } // get manufacturer details - if _, err := e.evDeviceClassification.RequestManufacturerDetails(); err != nil { - logging.Log().Debug(err) + if e.evDeviceClassification != nil { + if _, err := e.evDeviceClassification.RequestManufacturerDetails(); err != nil { + logging.Log().Debug(err) + } } // get device diagnosis state - if _, err := e.evDeviceDiagnosis.RequestState(); err != nil { - logging.Log().Debug(err) + if e.evDeviceDiagnosis != nil { + if _, err := e.evDeviceDiagnosis.RequestState(); err != nil { + logging.Log().Debug(err) + } } // get electrical connection parameter - if err := e.evElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evElectricalConnection != nil { + if err := e.evElectricalConnection.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } } - if err := e.evElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evElectricalConnection != nil { + if err := e.evElectricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Debug(err) + } } // get measurement parameters - if err := e.evMeasurement.RequestDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evMeasurement != nil { + if err := e.evMeasurement.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } } // get loadlimit parameter - if err := e.evLoadControl.RequestLimitDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evLoadControl != nil { + if err := e.evLoadControl.RequestLimitDescriptions(); err != nil { + logging.Log().Debug(err) + } } // get identification - if _, err := e.evIdentification.RequestValues(); err != nil { - logging.Log().Debug(err) + if e.evIdentification != nil { + if _, err := e.evIdentification.RequestValues(); err != nil { + logging.Log().Debug(err) + } } if e.configuration.CoordinatedChargingEnabled { - // get time series parameter - if err := e.evTimeSeries.RequestDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evTimeSeries != nil { + // get time series parameter + if err := e.evTimeSeries.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } } - // get incentive table parameter - if err := e.evIncentiveTable.RequestDescriptions(); err != nil { - logging.Log().Debug(err) + if e.evIncentiveTable != nil { + // get incentive table parameter + if err := e.evIncentiveTable.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } } } } From 427098b9eb37cbdf86b239b20314c16c67be96a7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 19 Jan 2024 18:28:58 +0100 Subject: [PATCH 041/227] Update eebus, ship and spine --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8e8ce2d..91b0298 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240116121635-bb2a283dd60b - github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 - github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c + github.com/enbility/eebus-go v0.0.0-20240119172724-1e1d0f8cd5ef + github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 + github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 955991d..9bd005c 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240116121635-bb2a283dd60b h1:QUJWhTLTQ9urz3nEGQ8sSkrNfxcZ0goTN0CKTsHIfU8= -github.com/enbility/eebus-go v0.0.0-20240116121635-bb2a283dd60b/go.mod h1:TDNE6540hO9/BZDo4v7gme+J/oEznCj1bSWWoz/CCnA= -github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4 h1:udB9WMUnI/YvoW8armfvylz0iP4ISjWdWbgI7yRO+Jk= -github.com/enbility/ship-go v0.0.0-20240116121420-cfcab81f59c4/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c h1:54J6I5Ln5ZVP5n6Dnm0mva/1cggZjfdmUp1SJaG/WTs= -github.com/enbility/spine-go v0.0.0-20240114194628-333ba9f8762c/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= +github.com/enbility/eebus-go v0.0.0-20240119172724-1e1d0f8cd5ef h1:rXYBC7qFBgHS8cnXuWwhfVOnt3UFC1q8Ts3gwk+pzyI= +github.com/enbility/eebus-go v0.0.0-20240119172724-1e1d0f8cd5ef/go.mod h1:obgAOuqW3j+ibJuIiUQ7+H7cRGS6W9canqZgxZlLWk0= +github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 h1:CphcdnbDvpzU+/uyy0k9v08Z4ngmYLVWCFbghOhircY= +github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 h1:WL550WMZIv3fUrFjJ5qSMD17On9Kb1n17JW/eBx4/Ak= +github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From c9f68430a8471a6bcb7051508be7000700d47ed2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 20 Jan 2024 15:22:25 +0100 Subject: [PATCH 042/227] Update to latest eebus and spine --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 91b0298..7b601bb 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240119172724-1e1d0f8cd5ef + github.com/enbility/eebus-go v0.0.0-20240120142050-b29ee60146a7 github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 - github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 + github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 9bd005c..da484ab 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240119172724-1e1d0f8cd5ef h1:rXYBC7qFBgHS8cnXuWwhfVOnt3UFC1q8Ts3gwk+pzyI= -github.com/enbility/eebus-go v0.0.0-20240119172724-1e1d0f8cd5ef/go.mod h1:obgAOuqW3j+ibJuIiUQ7+H7cRGS6W9canqZgxZlLWk0= +github.com/enbility/eebus-go v0.0.0-20240120142050-b29ee60146a7 h1:d3VhIepAOq/z7ckmG3+G5bSesaukfSi4gPse+53HwII= +github.com/enbility/eebus-go v0.0.0-20240120142050-b29ee60146a7/go.mod h1:HF2HpHVJqjORAY2jUaf6XzCb3c25sCPt6UFNghEFWvQ= github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 h1:CphcdnbDvpzU+/uyy0k9v08Z4ngmYLVWCFbghOhircY= github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710 h1:WL550WMZIv3fUrFjJ5qSMD17On9Kb1n17JW/eBx4/Ak= -github.com/enbility/spine-go v0.0.0-20240119171805-d94ea271d710/go.mod h1:qGA9ZP25YyRGssbK1h3TZ6IdRhaR1RHbTe2GVsTc7p0= +github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca h1:k2lWJM60QxOitR83mHL85n3fxL6Rvzfmp3BZQ55BULU= +github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca/go.mod h1:Ce7CrTfyW01BPudqELY99L9Fyo9d1mn1ogUqpB0vRX0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From 5bbb56a85c2b573ef60c8d7f3ef7eb7ac9a75a70 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 11:41:12 +0100 Subject: [PATCH 043/227] Update github action --- .github/workflows/default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 4290c93..ab95e46 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ^1.18 From f5af61c4dc8f78a0746524c8c075a03d391bbf25 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 11:41:24 +0100 Subject: [PATCH 044/227] Update eebus, ship and spine --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7b601bb..41edec5 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240120142050-b29ee60146a7 - github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 - github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca + github.com/enbility/eebus-go v0.0.0-20240122104002-521794de4bff + github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 + github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index da484ab..d8504d9 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240120142050-b29ee60146a7 h1:d3VhIepAOq/z7ckmG3+G5bSesaukfSi4gPse+53HwII= -github.com/enbility/eebus-go v0.0.0-20240120142050-b29ee60146a7/go.mod h1:HF2HpHVJqjORAY2jUaf6XzCb3c25sCPt6UFNghEFWvQ= -github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571 h1:CphcdnbDvpzU+/uyy0k9v08Z4ngmYLVWCFbghOhircY= -github.com/enbility/ship-go v0.0.0-20240119171219-2171f41f6571/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca h1:k2lWJM60QxOitR83mHL85n3fxL6Rvzfmp3BZQ55BULU= -github.com/enbility/spine-go v0.0.0-20240120141738-1c73b9c726ca/go.mod h1:Ce7CrTfyW01BPudqELY99L9Fyo9d1mn1ogUqpB0vRX0= +github.com/enbility/eebus-go v0.0.0-20240122104002-521794de4bff h1:zVQugaFBCPBavzFn7M22zNcoii0r6bGFTiJphAfZepw= +github.com/enbility/eebus-go v0.0.0-20240122104002-521794de4bff/go.mod h1:iqjnp8UOw0Qo6Of+bCXQdbOGQyuqB5b74FeS6QHz8cg= +github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 h1:zAyFAxT6sB8m/AoD95215v4Vr5Rp1vbrbYI7pdrAJnc= +github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f h1:6S5FahaQeRW/qBPAkVgKkvo1HWOeSLOPPUHqqpBMiEs= +github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f/go.mod h1:tOPz6dr3m+s+h1Ouu78EQS5kB9Buh3Pnu2jW6eQrwOM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From 6dec25e7429956755c33143bbe21eccd7ba6fb1a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 14:24:40 +0100 Subject: [PATCH 045/227] update eebus, spine and ship --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 41edec5..7d9e920 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.18 require ( - github.com/enbility/eebus-go v0.0.0-20240122104002-521794de4bff - github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 - github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f + github.com/enbility/eebus-go v0.0.0-20240122131904-27c0b876f02d + github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 + github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index d8504d9..702e7e3 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240122104002-521794de4bff h1:zVQugaFBCPBavzFn7M22zNcoii0r6bGFTiJphAfZepw= -github.com/enbility/eebus-go v0.0.0-20240122104002-521794de4bff/go.mod h1:iqjnp8UOw0Qo6Of+bCXQdbOGQyuqB5b74FeS6QHz8cg= -github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29 h1:zAyFAxT6sB8m/AoD95215v4Vr5Rp1vbrbYI7pdrAJnc= -github.com/enbility/ship-go v0.0.0-20240122103724-e4de8c1a1f29/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f h1:6S5FahaQeRW/qBPAkVgKkvo1HWOeSLOPPUHqqpBMiEs= -github.com/enbility/spine-go v0.0.0-20240122103838-f106aec9041f/go.mod h1:tOPz6dr3m+s+h1Ouu78EQS5kB9Buh3Pnu2jW6eQrwOM= +github.com/enbility/eebus-go v0.0.0-20240122131904-27c0b876f02d h1:U8umNzEhYxc55fGPnbeyWCZOxZvgDaT7hzKgN/d3eOw= +github.com/enbility/eebus-go v0.0.0-20240122131904-27c0b876f02d/go.mod h1:ek0c0V6VIP78E6rZhsyR7Tjp0hRexdY3HNLxG9mSmfM= +github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 h1:Xgzy+Jv5HxwJtnDRk1IVStxYvGd/hWFzKU8Z6qgH8FA= +github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= +github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd h1:DyHn0ql/JBu2olfUWiLmCGbXMpNwMMivrEtxxHrF+Sw= +github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd/go.mod h1:n6ITUaTl0XASZWRafB7hSdIVLKO7gxlZ+95CN+uHktQ= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From 11b2c5fbeaf389ba7fb7ebb520cf9d4e69bd29e9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 14:25:04 +0100 Subject: [PATCH 046/227] Integrade codefactor and fix a warning --- README.md | 1 + emobility/events.go | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1fa72e7..f3fa16c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4)](https://godoc.org/github.com/enbility/cemd) [![Coverage Status](https://coveralls.io/repos/github/enbility/cemd/badge.svg?branch=dev)](https://coveralls.io/github/enbility/cemd?branch=dev) [![Go report](https://goreportcard.com/badge/github.com/enbility/cemd)](https://goreportcard.com/report/github.com/enbility/cemd) +[![CodeFactor](https://www.codefactor.io/repository/github/enbility/cemd/badge)](https://www.codefactor.io/repository/github/enbility/cemd) The goal is to provide an EEBUS CEM implementation diff --git a/emobility/events.go b/emobility/events.go index 6d861df..813e65c 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -33,8 +33,7 @@ func (e *EMobilityImpl) HandleEvent(payload api.EventPayload) { switch payload.EventType { case api.EventTypeDeviceChange: - switch payload.ChangeType { - case api.ElementChangeRemove: + if payload.ChangeType == api.ElementChangeRemove { e.evseDisconnected() e.evDisconnected() } From 5a457dda8bf8ff3867f9e4ec1a985613ccc93c7c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 15:57:37 +0100 Subject: [PATCH 047/227] Update to go 1.21.1 and update eebus, spine and ship --- go.mod | 8 ++++---- go.sum | 17 +++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 7d9e920..8724f2a 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/enbility/cemd -go 1.18 +go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240122131904-27c0b876f02d - github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 - github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd + github.com/enbility/eebus-go v0.0.0-20240122145505-f617499b3ee9 + github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 + github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 702e7e3..62a4369 100644 --- a/go.sum +++ b/go.sum @@ -4,16 +4,17 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240122131904-27c0b876f02d h1:U8umNzEhYxc55fGPnbeyWCZOxZvgDaT7hzKgN/d3eOw= -github.com/enbility/eebus-go v0.0.0-20240122131904-27c0b876f02d/go.mod h1:ek0c0V6VIP78E6rZhsyR7Tjp0hRexdY3HNLxG9mSmfM= -github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6 h1:Xgzy+Jv5HxwJtnDRk1IVStxYvGd/hWFzKU8Z6qgH8FA= -github.com/enbility/ship-go v0.0.0-20240122130150-644592f032c6/go.mod h1:JdBm7s7ICvg+vTwqytgsJzYDckavZP8QKKxPOV4DElU= -github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd h1:DyHn0ql/JBu2olfUWiLmCGbXMpNwMMivrEtxxHrF+Sw= -github.com/enbility/spine-go v0.0.0-20240122131148-ab10d10d42fd/go.mod h1:n6ITUaTl0XASZWRafB7hSdIVLKO7gxlZ+95CN+uHktQ= +github.com/enbility/eebus-go v0.0.0-20240122145505-f617499b3ee9 h1:B8JE4pmOuYsM0yk7vqJDKo6VzqldNYVl+FL30zZByBo= +github.com/enbility/eebus-go v0.0.0-20240122145505-f617499b3ee9/go.mod h1:3EnfVQ4j6TNOGFXlGEjcE6JilswF4pMGQ6cH6ieGrmU= +github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 h1:dWZ5qjUPjZhtUviZmsTkuE+6xKSJrp34yx7Xb3iShQ0= +github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 h1:AEszB6babmfq9Bvx/iU7D0lolOTWERgs8hPyytV0IA0= +github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366/go.mod h1:VrxQBPf7SaaLRPNRe9OOY0e954oF/pFtO3glqSulpGg= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -24,6 +25,7 @@ github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SW github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ= @@ -31,6 +33,7 @@ github.com/rickb777/date v1.20.5/go.mod h1:6BPrm3/aQI0I8jvlD1fAlm/86k5eSeTQ2mR5F github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -63,6 +66,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -87,6 +91,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 2270cbc3443e378d3f282db70eb3bfa40e346c04 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 15:57:47 +0100 Subject: [PATCH 048/227] Add gosec to github action --- .github/workflows/default.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index ab95e46..1965e46 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -38,4 +38,15 @@ jobs: - name: Send coverage uses: shogo82148/actions-goveralls@v1 with: - path-to-profile: coverage.out \ No newline at end of file + path-to-profile: coverage.out + + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + # we let the report trigger content trigger a failure using the GitHub Security features. + args: '-no-fail -fmt sarif -out results.sarif ./...' + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v2 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: results.sarif \ No newline at end of file From 50eda67ee9b55c28830aaac7a968da7d0748c528 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 16:23:37 +0100 Subject: [PATCH 049/227] Update eebus, spine, ship --- go.mod | 6 +++--- go.sum | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8724f2a..791119a 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240122145505-f617499b3ee9 - github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 - github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 + github.com/enbility/eebus-go v0.0.0-20240122151925-b9cc1396dd65 + github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f + github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 62a4369..bea0ec2 100644 --- a/go.sum +++ b/go.sum @@ -2,14 +2,15 @@ github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df h1:qYVip github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df/go.mod h1:OO5/UahoVBLyauLdDF4httPlSISqbrWDbHi9k99zUsc= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240122145505-f617499b3ee9 h1:B8JE4pmOuYsM0yk7vqJDKo6VzqldNYVl+FL30zZByBo= -github.com/enbility/eebus-go v0.0.0-20240122145505-f617499b3ee9/go.mod h1:3EnfVQ4j6TNOGFXlGEjcE6JilswF4pMGQ6cH6ieGrmU= -github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6 h1:dWZ5qjUPjZhtUviZmsTkuE+6xKSJrp34yx7Xb3iShQ0= -github.com/enbility/ship-go v0.0.0-20240122143804-dfb477370ba6/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366 h1:AEszB6babmfq9Bvx/iU7D0lolOTWERgs8hPyytV0IA0= -github.com/enbility/spine-go v0.0.0-20240122144854-baad1d55e366/go.mod h1:VrxQBPf7SaaLRPNRe9OOY0e954oF/pFtO3glqSulpGg= +github.com/enbility/eebus-go v0.0.0-20240122151925-b9cc1396dd65 h1:Srk5XShQDsmQYB93E6OB8rL+qb1S9tG0aNVuXDW3lyg= +github.com/enbility/eebus-go v0.0.0-20240122151925-b9cc1396dd65/go.mod h1:tMs3wFz97KWsr7tQfqogRwcrxcdF+p0RfoHHJD2Bzvs= +github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f h1:6yWUydMGGS+PD2KiA+vNOVThpgK6MzGIU7bVjPQ646M= +github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 h1:mbuwnwfh9v3UeoSNkczDuBicwbZUAQvbqGI8YevlWww= +github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43/go.mod h1:BcNplM7cqwn27J7v06IJs8RZy5aSRRz0t2QTX4lcsdI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -32,8 +33,14 @@ github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ= github.com/rickb777/date v1.20.5/go.mod h1:6BPrm3/aQI0I8jvlD1fAlm/86k5eSeTQ2mR5FEmTnSw= github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -102,5 +109,6 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 61e1733339c1873951d4a35f2845822c65e61d53 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 16:24:08 +0100 Subject: [PATCH 050/227] =?UTF-8?q?Don=E2=80=99t=20search=20mDNS=20in=20te?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- emobility/helper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 2af2277..4f6eb55 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -108,7 +108,7 @@ func NewTestEMobility(service api.EEBUSService, details *shipapi.ServiceDetails) ski: ski, } - service.RegisterRemoteSKI(ski, true) + service.RegisterRemoteSKI(ski, false) return emobility } From 6125df2a516ad6b70553869048098216a13bfeef Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 16:24:53 +0100 Subject: [PATCH 051/227] Use serviceHandler mock in tests --- emobility/evcoordinatedcharging_test.go | 2 +- emobility/helper_test.go | 11 +++++++++-- emobility/public_EVChargePlan_test.go | 2 +- emobility/public_EVChargeStrategy_test.go | 2 +- emobility/public_EVChargedEnergy_test.go | 2 +- emobility/public_EVCommunicationStandard_test.go | 2 +- emobility/public_EVConnectedPhases_test.go | 2 +- .../public_EVCoordinatedChargingSupported_test.go | 2 +- emobility/public_EVCurrentChargeState_test.go | 2 +- emobility/public_EVCurrentLimits_test.go | 2 +- emobility/public_EVCurrentsPerPhase_test.go | 2 +- emobility/public_EVEnergyDemand_test.go | 2 +- emobility/public_EVIdentification_test.go | 2 +- emobility/public_EVIncentiveConstraints_test.go | 2 +- ...c_EVOptimizationOfSelfConsumptionSupported_test.go | 2 +- emobility/public_EVPowerPerPhase_test.go | 4 ++-- emobility/public_EVSoCSupported_test.go | 2 +- emobility/public_EVSoC_test.go | 2 +- emobility/public_EVTimeSlotConstraints_test.go | 2 +- emobility/public_EVWriteIncentives_test.go | 2 +- emobility/public_EVWriteLoadControlLimits_test.go | 2 +- emobility/public_EVWritePowerLimits_test.go | 2 +- 22 files changed, 31 insertions(+), 24 deletions(-) diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index 89c1b4f..7c2c787 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -12,7 +12,7 @@ import ( ) func Test_CoordinatedChargingScenarios(t *testing.T) { - emobility, eebusService := setupEmobility() + emobility, eebusService := setupEmobility(t) data, err := emobility.EVChargedEnergy() assert.NotNil(t, err) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index 4f6eb55..c9edb1e 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -4,10 +4,12 @@ import ( "encoding/json" "fmt" "sync" + "testing" "time" "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" + "github.com/enbility/eebus-go/mocks" "github.com/enbility/eebus-go/service" shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" @@ -15,6 +17,7 @@ import ( spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" ) type WriteMessageHandler struct { @@ -113,14 +116,18 @@ func NewTestEMobility(service api.EEBUSService, details *shipapi.ServiceDetails) return emobility } -func setupEmobility() (*EMobilityImpl, api.EEBUSService) { +func setupEmobility(t *testing.T) (*EMobilityImpl, api.EEBUSService) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := api.NewConfiguration( "test", "test", "test", "test", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, 9999, cert, 230.0, time.Second*4) - eebusService := service.NewEEBUSService(configuration, nil) + + serviceHandler := mocks.NewEEBUSServiceHandler(t) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + eebusService := service.NewEEBUSService(configuration, serviceHandler) _ = eebusService.Setup() details := shipapi.NewServiceDetails(remoteSki) emobility := NewTestEMobility(eebusService, details) diff --git a/emobility/public_EVChargePlan_test.go b/emobility/public_EVChargePlan_test.go index 0921bce..b0eff07 100644 --- a/emobility/public_EVChargePlan_test.go +++ b/emobility/public_EVChargePlan_test.go @@ -10,7 +10,7 @@ import ( ) func Test_EVChargePlan(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) _, err := emobilty.EVChargePlan() assert.NotNil(t, err) diff --git a/emobility/public_EVChargeStrategy_test.go b/emobility/public_EVChargeStrategy_test.go index 11899c7..a875a8c 100644 --- a/emobility/public_EVChargeStrategy_test.go +++ b/emobility/public_EVChargeStrategy_test.go @@ -10,7 +10,7 @@ import ( ) func Test_EVChargeStrategy(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data := emobilty.EVChargeStrategy() assert.Equal(t, EVChargeStrategyTypeUnknown, data) diff --git a/emobility/public_EVChargedEnergy_test.go b/emobility/public_EVChargedEnergy_test.go index a52c199..38f50aa 100644 --- a/emobility/public_EVChargedEnergy_test.go +++ b/emobility/public_EVChargedEnergy_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVChargedEnergy(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVChargedEnergy() assert.NotNil(t, err) diff --git a/emobility/public_EVCommunicationStandard_test.go b/emobility/public_EVCommunicationStandard_test.go index 1e2f8c7..244e3dd 100644 --- a/emobility/public_EVCommunicationStandard_test.go +++ b/emobility/public_EVCommunicationStandard_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVCommunicationStandard(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVCommunicationStandard() assert.NotNil(t, err) diff --git a/emobility/public_EVConnectedPhases_test.go b/emobility/public_EVConnectedPhases_test.go index d023099..a6c78de 100644 --- a/emobility/public_EVConnectedPhases_test.go +++ b/emobility/public_EVConnectedPhases_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVConnectedPhases(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVConnectedPhases() assert.NotNil(t, err) diff --git a/emobility/public_EVCoordinatedChargingSupported_test.go b/emobility/public_EVCoordinatedChargingSupported_test.go index 1c5c212..754d4e8 100644 --- a/emobility/public_EVCoordinatedChargingSupported_test.go +++ b/emobility/public_EVCoordinatedChargingSupported_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVCoordinatedChargingSupported(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVCoordinatedChargingSupported() assert.NotNil(t, err) diff --git a/emobility/public_EVCurrentChargeState_test.go b/emobility/public_EVCurrentChargeState_test.go index 05d3afe..30ecf1f 100644 --- a/emobility/public_EVCurrentChargeState_test.go +++ b/emobility/public_EVCurrentChargeState_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVCurrentChargeState(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVCurrentChargeState() assert.Nil(t, err) diff --git a/emobility/public_EVCurrentLimits_test.go b/emobility/public_EVCurrentLimits_test.go index 5f7a8d5..69d7c5c 100644 --- a/emobility/public_EVCurrentLimits_test.go +++ b/emobility/public_EVCurrentLimits_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVCurrentLimits(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) minData, maxData, defaultData, err := emobilty.EVCurrentLimits() assert.NotNil(t, err) diff --git a/emobility/public_EVCurrentsPerPhase_test.go b/emobility/public_EVCurrentsPerPhase_test.go index 1dcc34b..2063fb4 100644 --- a/emobility/public_EVCurrentsPerPhase_test.go +++ b/emobility/public_EVCurrentsPerPhase_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVCurrentsPerPhase(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVCurrentsPerPhase() assert.NotNil(t, err) diff --git a/emobility/public_EVEnergyDemand_test.go b/emobility/public_EVEnergyDemand_test.go index c54dbd5..0d943a6 100644 --- a/emobility/public_EVEnergyDemand_test.go +++ b/emobility/public_EVEnergyDemand_test.go @@ -10,7 +10,7 @@ import ( ) func Test_EVEnergySingleDemand(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) demand, err := emobilty.EVEnergyDemand() assert.NotNil(t, err) diff --git a/emobility/public_EVIdentification_test.go b/emobility/public_EVIdentification_test.go index 614712d..88f7e56 100644 --- a/emobility/public_EVIdentification_test.go +++ b/emobility/public_EVIdentification_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVIdentification(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVIdentification() assert.NotNil(t, err) diff --git a/emobility/public_EVIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go index 684565e..f6309ee 100644 --- a/emobility/public_EVIncentiveConstraints_test.go +++ b/emobility/public_EVIncentiveConstraints_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVGetIncentiveConstraints(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) constraints, err := emobilty.EVIncentiveConstraints() assert.Equal(t, uint(0), constraints.MinSlots) diff --git a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go index 4dc9115..24893da 100644 --- a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go +++ b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVOptimizationOfSelfConsumptionSupported() assert.NotNil(t, err) diff --git a/emobility/public_EVPowerPerPhase_test.go b/emobility/public_EVPowerPerPhase_test.go index 6ef6425..2b0238a 100644 --- a/emobility/public_EVPowerPerPhase_test.go +++ b/emobility/public_EVPowerPerPhase_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVPowerPerPhase_Power(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVPowerPerPhase() assert.NotNil(t, err) @@ -97,7 +97,7 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { } func Test_EVPowerPerPhase_Current(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVPowerPerPhase() assert.NotNil(t, err) diff --git a/emobility/public_EVSoCSupported_test.go b/emobility/public_EVSoCSupported_test.go index 22e3cfe..2546f22 100644 --- a/emobility/public_EVSoCSupported_test.go +++ b/emobility/public_EVSoCSupported_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVSoCSupported(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVSoCSupported() assert.NotNil(t, err) diff --git a/emobility/public_EVSoC_test.go b/emobility/public_EVSoC_test.go index 498b855..715d6db 100644 --- a/emobility/public_EVSoC_test.go +++ b/emobility/public_EVSoC_test.go @@ -9,7 +9,7 @@ import ( ) func Test_EVSoC(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data, err := emobilty.EVSoC() assert.NotNil(t, err) diff --git a/emobility/public_EVTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go index 55b5197..b4bc61f 100644 --- a/emobility/public_EVTimeSlotConstraints_test.go +++ b/emobility/public_EVTimeSlotConstraints_test.go @@ -10,7 +10,7 @@ import ( ) func Test_EVGetTimeSlotConstraints(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) constraints, err := emobilty.EVTimeSlotConstraints() assert.Equal(t, uint(0), constraints.MinSlots) diff --git a/emobility/public_EVWriteIncentives_test.go b/emobility/public_EVWriteIncentives_test.go index ffd3f63..b313daf 100644 --- a/emobility/public_EVWriteIncentives_test.go +++ b/emobility/public_EVWriteIncentives_test.go @@ -11,7 +11,7 @@ import ( ) func Test_EVWriteIncentives(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data := []EVDurationSlotValue{} diff --git a/emobility/public_EVWriteLoadControlLimits_test.go b/emobility/public_EVWriteLoadControlLimits_test.go index 566203b..96028ce 100644 --- a/emobility/public_EVWriteLoadControlLimits_test.go +++ b/emobility/public_EVWriteLoadControlLimits_test.go @@ -12,7 +12,7 @@ import ( ) func Test_EVWriteLoadControlLimits(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) loadLimits := []EVLoadLimits{} diff --git a/emobility/public_EVWritePowerLimits_test.go b/emobility/public_EVWritePowerLimits_test.go index 5b00b6c..1b81adc 100644 --- a/emobility/public_EVWritePowerLimits_test.go +++ b/emobility/public_EVWritePowerLimits_test.go @@ -11,7 +11,7 @@ import ( ) func Test_EVWritePowerLimits(t *testing.T) { - emobilty, eebusService := setupEmobility() + emobilty, eebusService := setupEmobility(t) data := []EVDurationSlotValue{} From 5dfc52d04bb923cec3b917bfcaf5d098233aff5d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 16:25:16 +0100 Subject: [PATCH 052/227] Update mod --- go.mod | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/go.mod b/go.mod index 791119a..fadd096 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect + github.com/stretchr/objx v0.5.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.20.0 // indirect @@ -30,3 +31,7 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// replace github.com/enbility/eebus-go => ../eebus-go +// replace github.com/enbility/ship-go => ../ship-go +// replace github.com/enbility/spine-go => ../spine-go From e1d03034c2bbdf70fe4f816221ffca68da0ab832 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 16:25:29 +0100 Subject: [PATCH 053/227] Fix pointer check and safety --- emobility/public.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/emobility/public.go b/emobility/public.go index b035753..4051988 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -311,8 +311,11 @@ func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { var limitDesc *model.LoadControlLimitDescriptionDataType for _, desc := range limitDescriptions { - if desc.MeasurementId != nil && *desc.MeasurementId == *elParamDesc.MeasurementId { - limitDesc = &desc + if desc.MeasurementId != nil && + elParamDesc.MeasurementId != nil && + *desc.MeasurementId == *elParamDesc.MeasurementId { + safeDesc := desc + limitDesc = &safeDesc break } } @@ -400,8 +403,11 @@ func (e *EMobilityImpl) EVWriteLoadControlLimits(limits []EVLoadLimits) error { var limitDesc *model.LoadControlLimitDescriptionDataType for _, desc := range limitDescriptions { - if desc.MeasurementId != nil && *desc.MeasurementId == *elParamDesc.MeasurementId { - limitDesc = &desc + if desc.MeasurementId != nil && + elParamDesc.MeasurementId != nil && + *desc.MeasurementId == *elParamDesc.MeasurementId { + safeDesc := desc + limitDesc = &safeDesc break } } From e9b973983ae4158e61457c6cd7e72cb1705a7e45 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 17:37:58 +0100 Subject: [PATCH 054/227] Update to latest ship, spine, eebus --- emobility/emobility.go | 2 +- emobility/helper_test.go | 2 +- emobility/scenario.go | 4 ++-- go.mod | 10 +++------- go.sum | 12 ++++++------ grid/grid.go | 2 +- grid/scenario.go | 4 ++-- inverterbatteryvis/invertervis.go | 2 +- inverterbatteryvis/scenario.go | 4 ++-- inverterpvvis/invertervis.go | 2 +- inverterpvvis/scenario.go | 4 ++-- 11 files changed, 22 insertions(+), 26 deletions(-) diff --git a/emobility/emobility.go b/emobility/emobility.go index 9c59412..d27a060 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -266,7 +266,7 @@ var _ EmobilityI = (*EMobilityImpl)(nil) // Add E-Mobility support func NewEMobility(service api.EEBUSService, details *shipapi.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { - ski := util.NormalizeSKI(details.SKI) + ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/emobility/helper_test.go b/emobility/helper_test.go index c9edb1e..fe93d66 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -102,7 +102,7 @@ const remoteSki string = "testremoteski" // we don't want to handle events in these tests for now, so we don't use NewEMobility(...) func NewTestEMobility(service api.EEBUSService, details *shipapi.ServiceDetails) *EMobilityImpl { - ski := util.NormalizeSKI(details.SKI) + ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().Entity([]model.AddressEntityType{1}) emobility := &EMobilityImpl{ diff --git a/emobility/scenario.go b/emobility/scenario.go index a65162c..9d2c409 100644 --- a/emobility/scenario.go +++ b/emobility/scenario.go @@ -142,7 +142,7 @@ func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDet e.mux.Lock() defer e.mux.Unlock() - if em, ok := e.remoteDevices[details.SKI]; ok { + if em, ok := e.remoteDevices[details.SKI()]; ok { return em } @@ -151,7 +151,7 @@ func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDet provider = dataProvider.(EmobilityDataProvider) } emobility := NewEMobility(e.Service, details, e.currency, e.configuration, provider) - e.remoteDevices[details.SKI] = emobility + e.remoteDevices[details.SKI()] = emobility return emobility } diff --git a/go.mod b/go.mod index fadd096..de4f45d 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240122151925-b9cc1396dd65 - github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f - github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 + github.com/enbility/eebus-go v0.0.0-20240122163606-e3405300a665 + github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a + github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b @@ -31,7 +31,3 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// replace github.com/enbility/eebus-go => ../eebus-go -// replace github.com/enbility/ship-go => ../ship-go -// replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index bea0ec2..d013022 100644 --- a/go.sum +++ b/go.sum @@ -5,12 +5,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240122151925-b9cc1396dd65 h1:Srk5XShQDsmQYB93E6OB8rL+qb1S9tG0aNVuXDW3lyg= -github.com/enbility/eebus-go v0.0.0-20240122151925-b9cc1396dd65/go.mod h1:tMs3wFz97KWsr7tQfqogRwcrxcdF+p0RfoHHJD2Bzvs= -github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f h1:6yWUydMGGS+PD2KiA+vNOVThpgK6MzGIU7bVjPQ646M= -github.com/enbility/ship-go v0.0.0-20240122150747-cde3451b810f/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43 h1:mbuwnwfh9v3UeoSNkczDuBicwbZUAQvbqGI8YevlWww= -github.com/enbility/spine-go v0.0.0-20240122151802-321e0fe43a43/go.mod h1:BcNplM7cqwn27J7v06IJs8RZy5aSRRz0t2QTX4lcsdI= +github.com/enbility/eebus-go v0.0.0-20240122163606-e3405300a665 h1:xxPxe8p9Rx7lsrQTAoiiFjsC8089A42VtL2qJtpCP2I= +github.com/enbility/eebus-go v0.0.0-20240122163606-e3405300a665/go.mod h1:zpx3PF/uOXoHQDd0ytzzcf669wBRCwOdGdr5k8SQBjw= +github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a h1:BWK59DNijiUQ4lQYxAKjSLd/7t4sSmt0ptfapwcDAUw= +github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd h1:N/ii735+ElnoHGqwUJ1BnDG4YZXIVxuHXw5vVsBArV8= +github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd/go.mod h1:7VQRShAU9yVnFhl9sUwFV0Yvy8gZsHMZzeLUs60AVVI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/grid/grid.go b/grid/grid.go index ffe1063..29699c6 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -38,7 +38,7 @@ var _ GridI = (*GridImpl)(nil) // Add Grid support func NewGrid(service api.EEBUSService, details *shipapi.ServiceDetails) *GridImpl { - ski := util.NormalizeSKI(details.SKI) + ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/grid/scenario.go b/grid/scenario.go index 86210b7..82bc800 100644 --- a/grid/scenario.go +++ b/grid/scenario.go @@ -62,12 +62,12 @@ func (e *GridScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, e.mux.Lock() defer e.mux.Unlock() - if em, ok := e.remoteDevices[details.SKI]; ok { + if em, ok := e.remoteDevices[details.SKI()]; ok { return em } grid := NewGrid(e.Service, details) - e.remoteDevices[details.SKI] = grid + e.remoteDevices[details.SKI()] = grid return grid } diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index 35edf50..2239ac2 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -33,7 +33,7 @@ var _ InverterBatteryVisI = (*InverterBatteryVisImpl)(nil) // Add InverterBatteryVis support func NewInverterBatteryVis(service api.EEBUSService, details *shipapi.ServiceDetails) *InverterBatteryVisImpl { - ski := util.NormalizeSKI(details.SKI) + ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/scenario.go index b8c5daf..f62bfd0 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/scenario.go @@ -62,12 +62,12 @@ func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *shipapi.S i.mux.Lock() defer i.mux.Unlock() - if em, ok := i.remoteDevices[details.SKI]; ok { + if em, ok := i.remoteDevices[details.SKI()]; ok { return em } inverter := NewInverterBatteryVis(i.Service, details) - i.remoteDevices[details.SKI] = inverter + i.remoteDevices[details.SKI()] = inverter return inverter } diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index f190e49..807ccaa 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -33,7 +33,7 @@ var _ InverterPVVisI = (*InverterPVVisImpl)(nil) // Add InverterPVVis support func NewInverterPVVis(service api.EEBUSService, details *shipapi.ServiceDetails) *InverterPVVisImpl { - ski := util.NormalizeSKI(details.SKI) + ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index f96bcdb..3f8fe72 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -62,12 +62,12 @@ func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *shipapi.Servic i.mux.Lock() defer i.mux.Unlock() - if em, ok := i.remoteDevices[details.SKI]; ok { + if em, ok := i.remoteDevices[details.SKI()]; ok { return em } inverter := NewInverterPVVis(i.Service, details) - i.remoteDevices[details.SKI] = inverter + i.remoteDevices[details.SKI()] = inverter return inverter } From 0ec5f5ec43c5bb7cdcb9f5b5b77ea6fa82e5dcb3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 22 Jan 2024 18:54:12 +0100 Subject: [PATCH 055/227] Update eebus, spine and ship --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index de4f45d..e4a522b 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240122163606-e3405300a665 - github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a - github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd + github.com/enbility/eebus-go v0.0.0-20240122174858-ae6f0b3fb3cf + github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec + github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index d013022..fd6f054 100644 --- a/go.sum +++ b/go.sum @@ -5,12 +5,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240122163606-e3405300a665 h1:xxPxe8p9Rx7lsrQTAoiiFjsC8089A42VtL2qJtpCP2I= -github.com/enbility/eebus-go v0.0.0-20240122163606-e3405300a665/go.mod h1:zpx3PF/uOXoHQDd0ytzzcf669wBRCwOdGdr5k8SQBjw= -github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a h1:BWK59DNijiUQ4lQYxAKjSLd/7t4sSmt0ptfapwcDAUw= -github.com/enbility/ship-go v0.0.0-20240122162711-a00c33a16e7a/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd h1:N/ii735+ElnoHGqwUJ1BnDG4YZXIVxuHXw5vVsBArV8= -github.com/enbility/spine-go v0.0.0-20240122163315-8326589e7fdd/go.mod h1:7VQRShAU9yVnFhl9sUwFV0Yvy8gZsHMZzeLUs60AVVI= +github.com/enbility/eebus-go v0.0.0-20240122174858-ae6f0b3fb3cf h1:+hy1FFJkXzyVLFjaLAOtdwTuRnIkl/XjAuSlGBTmPxM= +github.com/enbility/eebus-go v0.0.0-20240122174858-ae6f0b3fb3cf/go.mod h1:n+z0Hayk+RtBIJwQy10zcEK3n2conR3dAlZCtiXbB5Q= +github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec h1:Bu0hu9rsJIkUCtg64YJZte5OaGx0LStrmS10ufEma6o= +github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 h1:FoeVeEesGzKoFL1YRD+DS65Jm5O2vXqXl5/5k2WSbxg= +github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41/go.mod h1:v/MAO6HzXJ8fzvWkC2454W+bHmPO8Ry7lvRj/eWmREQ= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= From b49d80e3f95b707d3d1dcdceb5c9b39e9ab0333f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 23 Jan 2024 20:44:22 +0100 Subject: [PATCH 056/227] Refactor namings --- api/api.go | 13 + api/types.go | 15 ++ cem/cem.go | 6 +- cmd/main.go | 20 +- emobility/api.go | 223 ++++++++++++++++ emobility/emobility.go | 238 +----------------- emobility/evcoordinatedcharging_test.go | 12 +- emobility/events.go | 26 +- emobility/helper_test.go | 85 +++++-- emobility/public.go | 46 ++-- emobility/results.go | 4 +- emobility/{scenario.go => solution.go} | 33 +-- go.mod | 8 +- go.sum | 17 +- grid/api.go | 11 + grid/events.go | 6 +- grid/grid.go | 24 +- grid/public.go | 16 +- grid/results.go | 2 +- grid/{scenario.go => solution.go} | 30 +-- inverterbatteryvis/api.go | 8 + inverterbatteryvis/events.go | 6 +- inverterbatteryvis/invertervis.go | 21 +- inverterbatteryvis/public.go | 10 +- inverterbatteryvis/results.go | 2 +- .../{scenario.go => solution.go} | 16 +- inverterpvvis/api.go | 7 + inverterpvvis/events.go | 6 +- inverterpvvis/invertervis.go | 22 +- inverterpvvis/public.go | 8 +- inverterpvvis/results.go | 2 +- inverterpvvis/scenario.go | 16 +- scenarios/types.go | 24 -- util/helper.go | 10 +- 34 files changed, 524 insertions(+), 469 deletions(-) create mode 100644 api/api.go create mode 100644 api/types.go create mode 100644 emobility/api.go rename emobility/{scenario.go => solution.go} (83%) create mode 100644 grid/api.go rename grid/{scenario.go => solution.go} (68%) create mode 100644 inverterbatteryvis/api.go rename inverterbatteryvis/{scenario.go => solution.go} (83%) create mode 100644 inverterpvvis/api.go delete mode 100644 scenarios/types.go diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..8993425 --- /dev/null +++ b/api/api.go @@ -0,0 +1,13 @@ +package api + +import ( + shipapi "github.com/enbility/ship-go/api" +) + +// Implemented by *Solutions, used by Cem +type SolutionInterface interface { + RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any + UnRegisterRemoteDevice(remoteDeviceSki string) + AddFeatures() + AddUseCases() +} diff --git a/api/types.go b/api/types.go new file mode 100644 index 0000000..7a66f1e --- /dev/null +++ b/api/types.go @@ -0,0 +1,15 @@ +package api + +import ( + "github.com/enbility/eebus-go/api" +) + +type Solution struct { + Service api.ServiceInterface +} + +func NewSolution(service api.ServiceInterface) *Solution { + return &Solution{ + Service: service, + } +} diff --git a/cem/cem.go b/cem/cem.go index 13077b8..f7902f2 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -9,14 +9,14 @@ import ( // Generic CEM implementation type CemImpl struct { - Service api.EEBUSService + Service api.ServiceInterface Currency model.CurrencyType } -func NewCEM(serviceDescription *api.Configuration, serviceHandler api.EEBUSServiceHandler, log logging.Logging) *CemImpl { +func NewCEM(serviceDescription *api.Configuration, serviceHandler api.ServiceReaderInterface, log logging.LoggingInterface) *CemImpl { cem := &CemImpl{ - Service: service.NewEEBUSService(serviceDescription, serviceHandler), + Service: service.NewService(serviceDescription, serviceHandler), Currency: model.CurrencyTypeEur, } diff --git a/cmd/main.go b/cmd/main.go index e4bed6f..fb32718 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,13 +10,13 @@ import ( "syscall" "time" + "github.com/enbility/cemd/api" "github.com/enbility/cemd/cem" "github.com/enbility/cemd/emobility" "github.com/enbility/cemd/grid" "github.com/enbility/cemd/inverterbatteryvis" "github.com/enbility/cemd/inverterpvvis" - "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/api" + eebusapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" @@ -26,10 +26,10 @@ import ( type DemoCem struct { cem *cem.CemImpl - emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario scenarios.ScenariosI + emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario api.SolutionInterface } -func NewDemoCem(configuration *api.Configuration) *DemoCem { +func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { demo := &DemoCem{} demo.cem = cem.NewCEM(configuration, demo, demo) @@ -42,7 +42,7 @@ func (d *DemoCem) Setup() error { return err } - d.emobilityScenario = emobility.NewEMobilityScenario(d.cem.Service, d.cem.Currency, emobility.EmobilityConfiguration{ + d.emobilityScenario = emobility.NewEMobilitySolution(d.cem.Service, d.cem.Currency, emobility.EmobilityConfiguration{ CoordinatedChargingEnabled: true, }) d.emobilityScenario.AddFeatures() @@ -66,17 +66,17 @@ func (d *DemoCem) Setup() error { } // report the Ship ID of a newly trusted connection -func (d *DemoCem) RemoteServiceShipIDReported(service api.EEBUSService, ski string, shipID string) { +func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { // we should associated the Ship ID with the SKI and store it // so the next connection can start trusted logging.Log().Info("SKI", ski, "has Ship ID:", shipID) } -func (d *DemoCem) RemoteSKIConnected(service api.EEBUSService, ski string) {} +func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} -func (d *DemoCem) RemoteSKIDisconnected(service api.EEBUSService, ski string) {} +func (d *DemoCem) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} -func (d *DemoCem) VisibleRemoteServicesUpdated(service api.EEBUSService, entries []shipapi.RemoteService) { +func (d *DemoCem) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface, entries []shipapi.RemoteService) { } func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} @@ -154,7 +154,7 @@ func main() { fmt.Println("Using certificate file", *crt, "and key file", *key) } - configuration, err := api.NewConfiguration( + configuration, err := eebusapi.NewConfiguration( "Demo", "Demo", "HEMS", diff --git a/emobility/api.go b/emobility/api.go new file mode 100644 index 0000000..7278a67 --- /dev/null +++ b/emobility/api.go @@ -0,0 +1,223 @@ +package emobility + +//go:generate mockgen -source emobility.go -destination mock_emobility_test.go -package emobility + +// used by emobility and implemented by the CEM +type EmobilityDataProvider interface { + // The EV provided a charge strategy + EVProvidedChargeStrategy(strategy EVChargeStrategyType) + + // EV provided an energy demand + // + // Parameters: + // - demand: Contains details about the actual demands from the EV + EVProvidedEnergyDemand(demand EVDemand) + + // Energy demand and duration is provided by the EV which requires the CEM + // to respond with time slots containing power limits for each slot + // + // `EVWritePowerLimits` must be invoked within <55s, idealy <15s, after receiving this call + // + // Parameters: + // - demand: Contains details about the actual demands from the EV + // - constraints: Contains details about the time slot constraints + EVRequestPowerLimits(demand EVDemand, constraints EVTimeSlotConstraints) + + // Energy demand and duration is provided by the EV which requires the CEM + // to respond with time slots containing incentives for each slot + // + // `EVWriteIncentives` must be invoked within <20s after receiving this call + // + // Parameters: + // - demand: Contains details about the actual demands from the EV + // - constraints: Contains details about the incentive slot constraints + EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) + + // The EV provided a charge plan + EVProvidedChargePlan(plan EVChargePlan) + + // The EV provided charge plan constraints + EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) +} + +// used by the CEM and implemented by emobility +type EMobilityInterface interface { + // return if an EV is connected + EVConnected() bool + + // return the current charge state of the EV + EVCurrentChargeState() (EVChargeStateType, error) + + // return the number of ac connected phases of the EV or 0 if it is unknown + EVConnectedPhases() (uint, error) + + // return the charged energy measurement in Wh of the connected EV + // + // possible errors: + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVChargedEnergy() (float64, error) + + // return the last power measurement for each phase of the connected EV + // + // possible errors: + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVPowerPerPhase() ([]float64, error) + + // return the last current measurement for each phase of the connected EV + // + // possible errors: + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVCurrentsPerPhase() ([]float64, error) + + // return the min, max, default limits for each phase of the connected EV + // + // possible errors: + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVCurrentLimits() ([]float64, []float64, []float64, error) + + // return the current loadcontrol obligation limits + // + // possible errors: + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVLoadControlObligationLimits() ([]float64, error) + + // send new LoadControlLimits to the remote EV + // + // parameters: + // - obligations: Overload Protection Limits per phase in A + // - recommendations: Self Consumption recommendations per phase in A + // + // obligations: + // Sets a maximum A limit for each phase that the EV may not exceed. + // Mainly used for implementing overload protection of the site or limiting the + // maximum charge power of EVs when the EV and EVSE communicate via IEC61851 + // and with ISO15118 if the EV does not support the Optimization of Self Consumption + // usecase. + // + // recommendations: + // Sets a recommended charge power in A for each phase. This is mainly + // used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. + // The EV either needs to support the Optimization of Self Consumption usecase or + // the EVSE needs to be able map the recommendations into oligation limits which then + // works for all EVs communication either via IEC61851 or ISO15118. + // + // note: + // For obligations to work for optimizing solar excess power, the EV needs to + // have an energy demand. Recommendations work even if the EV does not have an active + // energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. + // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific + // and needs to have specific EVSE support for the specific EV brand. + // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. + EVWriteLoadControlLimits(limits []EVLoadLimits) error + + // return the current communication standard type used to communicate between EVSE and EV + // + // if an EV is connected via IEC61851, no ISO15118 specific data can be provided! + // sometimes the connection starts with IEC61851 before it switches + // to ISO15118, and sometimes it falls back again. so the error return is + // never absolut for the whole connection time, except if the use case + // is not supported + // + // the values are not constant and can change due to communication problems, bugs, and + // sometimes communication starts with IEC61851 before it switches to ISO + // + // possible errors: + // - ErrDataNotAvailable if that information is not (yet) available + // - ErrNotSupported if getting the communication standard is not supported + // - and others + EVCommunicationStandard() (EVCommunicationStandardType, error) + + // returns the identification of the currently connected EV or nil if not available + // + // possible errors: + // - ErrDataNotAvailable if that information is not (yet) available + // - and others + EVIdentification() (string, error) + + // returns if the EVSE and EV combination support optimzation of self consumption + // + // possible errors: + // - ErrDataNotAvailable if that information is not (yet) available + // - and others + EVOptimizationOfSelfConsumptionSupported() (bool, error) + + // return if the EVSE and EV combination support providing an SoC + // + // requires EVSoCSupported to return true + // only works with a current ISO15118-2 with VAS or ISO15118-20 + // communication between EVSE and EV + // + // possible errors: + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVSoCSupported() (bool, error) + + // return the last known SoC of the connected EV + // + // requires EVSoCSupported to return true + // only works with a current ISO15118-2 with VAS or ISO15118-20 + // communication between EVSE and EV + // + // possible errors: + // - ErrNotSupported if support for SoC is not possible + // - ErrDataNotAvailable if no such measurement is (yet) available + // - and others + EVSoC() (float64, error) + + // returns if the EVSE and EV combination support coordinated charging + // + // possible errors: + // - ErrDataNotAvailable if that information is not (yet) available + // - and others + EVCoordinatedChargingSupported() (bool, error) + + // returns the current charging stratey + // + // returns EVChargeStrategyTypeUnknown if it could not be determined, e.g. + // if the vehicle communication is via IEC61851 or the EV doesn't provide + // any information about its charging mode or plan + EVChargeStrategy() EVChargeStrategyType + + // returns the current energy demand + // - EVDemand: details about the actual demands from the EV + // - error: if no data is available + // + // if duration is 0, direct charging is active, otherwise timed charging is active + EVEnergyDemand() (EVDemand, error) + + // returns the current charge plan + // - EVChargePlan: details about the actual charge plan provided by the EV + // - error: if no data is available + EVChargePlan() (EVChargePlan, error) + + // returns the constraints for the time slots + // - EVTimeSlotConstraints: details about the time slot constraints + // - error: if no data is available + EVTimeSlotConstraints() (EVTimeSlotConstraints, error) + + // send power limits data to the EV + // + // returns an error if sending failed or charge slot count do not meet requirements + // + // this needs to be invoked either <55s, idealy <15s, of receiving a call to EVRequestPowerLimits + // or if the CEM requires the EV to change its charge plan + EVWritePowerLimits(data []EVDurationSlotValue) error + + // returns the constraints for incentive slots + // - EVIncentiveConstraints: details about the incentive slot constraints + // - error: if no data is available + EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) + + // send price slots data to the EV + // + // returns an error if sending failed or charge slot count do not meet requirements + // + // this needs to be invoked either within 20s of receiving a call to EVRequestIncentives + // or if the CEM requires the EV to change its charge plan + EVWriteIncentives(data []EVDurationSlotValue) error +} diff --git a/emobility/emobility.go b/emobility/emobility.go index d27a060..a280074 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -10,235 +10,13 @@ import ( "github.com/enbility/spine-go/spine" ) -//go:generate mockgen -source emobility.go -destination mock_emobility_test.go -package emobility +type EMobility struct { + entity spineapi.EntityLocalInterface -// used by emobility and implemented by the CEM -type EmobilityDataProvider interface { - // The EV provided a charge strategy - EVProvidedChargeStrategy(strategy EVChargeStrategyType) + service api.ServiceInterface - // EV provided an energy demand - // - // Parameters: - // - demand: Contains details about the actual demands from the EV - EVProvidedEnergyDemand(demand EVDemand) - - // Energy demand and duration is provided by the EV which requires the CEM - // to respond with time slots containing power limits for each slot - // - // `EVWritePowerLimits` must be invoked within <55s, idealy <15s, after receiving this call - // - // Parameters: - // - demand: Contains details about the actual demands from the EV - // - constraints: Contains details about the time slot constraints - EVRequestPowerLimits(demand EVDemand, constraints EVTimeSlotConstraints) - - // Energy demand and duration is provided by the EV which requires the CEM - // to respond with time slots containing incentives for each slot - // - // `EVWriteIncentives` must be invoked within <20s after receiving this call - // - // Parameters: - // - demand: Contains details about the actual demands from the EV - // - constraints: Contains details about the incentive slot constraints - EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) - - // The EV provided a charge plan - EVProvidedChargePlan(plan EVChargePlan) - - // The EV provided charge plan constraints - EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) -} - -// used by the CEM and implemented by emobility -type EmobilityI interface { - // return if an EV is connected - EVConnected() bool - - // return the current charge state of the EV - EVCurrentChargeState() (EVChargeStateType, error) - - // return the number of ac connected phases of the EV or 0 if it is unknown - EVConnectedPhases() (uint, error) - - // return the charged energy measurement in Wh of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVChargedEnergy() (float64, error) - - // return the last power measurement for each phase of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVPowerPerPhase() ([]float64, error) - - // return the last current measurement for each phase of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVCurrentsPerPhase() ([]float64, error) - - // return the min, max, default limits for each phase of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVCurrentLimits() ([]float64, []float64, []float64, error) - - // return the current loadcontrol obligation limits - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVLoadControlObligationLimits() ([]float64, error) - - // send new LoadControlLimits to the remote EV - // - // parameters: - // - obligations: Overload Protection Limits per phase in A - // - recommendations: Self Consumption recommendations per phase in A - // - // obligations: - // Sets a maximum A limit for each phase that the EV may not exceed. - // Mainly used for implementing overload protection of the site or limiting the - // maximum charge power of EVs when the EV and EVSE communicate via IEC61851 - // and with ISO15118 if the EV does not support the Optimization of Self Consumption - // usecase. - // - // recommendations: - // Sets a recommended charge power in A for each phase. This is mainly - // used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. - // The EV either needs to support the Optimization of Self Consumption usecase or - // the EVSE needs to be able map the recommendations into oligation limits which then - // works for all EVs communication either via IEC61851 or ISO15118. - // - // note: - // For obligations to work for optimizing solar excess power, the EV needs to - // have an energy demand. Recommendations work even if the EV does not have an active - // energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. - // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific - // and needs to have specific EVSE support for the specific EV brand. - // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. - EVWriteLoadControlLimits(limits []EVLoadLimits) error - - // return the current communication standard type used to communicate between EVSE and EV - // - // if an EV is connected via IEC61851, no ISO15118 specific data can be provided! - // sometimes the connection starts with IEC61851 before it switches - // to ISO15118, and sometimes it falls back again. so the error return is - // never absolut for the whole connection time, except if the use case - // is not supported - // - // the values are not constant and can change due to communication problems, bugs, and - // sometimes communication starts with IEC61851 before it switches to ISO - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - ErrNotSupported if getting the communication standard is not supported - // - and others - EVCommunicationStandard() (EVCommunicationStandardType, error) - - // returns the identification of the currently connected EV or nil if not available - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - and others - EVIdentification() (string, error) - - // returns if the EVSE and EV combination support optimzation of self consumption - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - and others - EVOptimizationOfSelfConsumptionSupported() (bool, error) - - // return if the EVSE and EV combination support providing an SoC - // - // requires EVSoCSupported to return true - // only works with a current ISO15118-2 with VAS or ISO15118-20 - // communication between EVSE and EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVSoCSupported() (bool, error) - - // return the last known SoC of the connected EV - // - // requires EVSoCSupported to return true - // only works with a current ISO15118-2 with VAS or ISO15118-20 - // communication between EVSE and EV - // - // possible errors: - // - ErrNotSupported if support for SoC is not possible - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVSoC() (float64, error) - - // returns if the EVSE and EV combination support coordinated charging - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - and others - EVCoordinatedChargingSupported() (bool, error) - - // returns the current charging stratey - // - // returns EVChargeStrategyTypeUnknown if it could not be determined, e.g. - // if the vehicle communication is via IEC61851 or the EV doesn't provide - // any information about its charging mode or plan - EVChargeStrategy() EVChargeStrategyType - - // returns the current energy demand - // - EVDemand: details about the actual demands from the EV - // - error: if no data is available - // - // if duration is 0, direct charging is active, otherwise timed charging is active - EVEnergyDemand() (EVDemand, error) - - // returns the current charge plan - // - EVChargePlan: details about the actual charge plan provided by the EV - // - error: if no data is available - EVChargePlan() (EVChargePlan, error) - - // returns the constraints for the time slots - // - EVTimeSlotConstraints: details about the time slot constraints - // - error: if no data is available - EVTimeSlotConstraints() (EVTimeSlotConstraints, error) - - // send power limits data to the EV - // - // returns an error if sending failed or charge slot count do not meet requirements - // - // this needs to be invoked either <55s, idealy <15s, of receiving a call to EVRequestPowerLimits - // or if the CEM requires the EV to change its charge plan - EVWritePowerLimits(data []EVDurationSlotValue) error - - // returns the constraints for incentive slots - // - EVIncentiveConstraints: details about the incentive slot constraints - // - error: if no data is available - EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) - - // send price slots data to the EV - // - // returns an error if sending failed or charge slot count do not meet requirements - // - // this needs to be invoked either within 20s of receiving a call to EVRequestIncentives - // or if the CEM requires the EV to change its charge plan - EVWriteIncentives(data []EVDurationSlotValue) error -} - -type EMobilityImpl struct { - entity spineapi.EntityLocal - - service api.EEBUSService - - evseEntity spineapi.EntityRemote - evEntity spineapi.EntityRemote + evseEntity spineapi.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface evseDeviceClassification *features.DeviceClassification evseDeviceDiagnosis *features.DeviceDiagnosis @@ -262,15 +40,15 @@ type EMobilityImpl struct { dataProvider EmobilityDataProvider } -var _ EmobilityI = (*EMobilityImpl)(nil) +var _ EMobilityInterface = (*EMobility)(nil) // Add E-Mobility support -func NewEMobility(service api.EEBUSService, details *shipapi.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobilityImpl { +func NewEMobility(service api.ServiceInterface, details *shipapi.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobility { ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - emobility := &EMobilityImpl{ + emobility := &EMobility{ service: service, entity: localEntity, ski: ski, diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index 7c2c787..030bcb5 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -276,7 +276,11 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { assert.Nil(t, err) } -func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { +func setupTimeSeries( + t *testing.T, + datagram model.DatagramType, + localDevice api.DeviceLocalInterface, + remoteDevice api.DeviceRemoteInterface) { cmd := []model.CmdType{{ TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ @@ -325,7 +329,11 @@ func setupTimeSeries(t *testing.T, datagram model.DatagramType, localDevice api. assert.Nil(t, err) } -func setupIncentiveTable(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { +func setupIncentiveTable( + t *testing.T, + datagram model.DatagramType, + localDevice api.DeviceLocalInterface, + remoteDevice api.DeviceRemoteInterface) { cmd := []model.CmdType{{ IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ IncentiveTableDescription: []model.IncentiveTableDescriptionType{ diff --git a/emobility/events.go b/emobility/events.go index 813e65c..eaeab73 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -11,7 +11,7 @@ import ( ) // Internal EventHandler Interface for the CEM -func (e *EMobilityImpl) HandleEvent(payload api.EventPayload) { +func (e *EMobility) HandleEvent(payload api.EventPayload) { // only care about the registered SKI if payload.Ski != e.ski { return @@ -214,7 +214,7 @@ func (e *EMobilityImpl) HandleEvent(payload api.EventPayload) { e.dataProvider.EVProvidedChargeStrategy(chargeStrategy) } -func (e *EMobilityImpl) evWriteDefaultIncentives() { +func (e *EMobility) evWriteDefaultIncentives() { // send default incentives for the maximum timeframe // to fullfill spec, as there is no data provided logging.Log().Info("Fallback sending default incentives") @@ -224,7 +224,7 @@ func (e *EMobilityImpl) evWriteDefaultIncentives() { _ = e.EVWriteIncentives(data) } -func (e *EMobilityImpl) evWriteDefaultPowerLimits() { +func (e *EMobility) evWriteDefaultPowerLimits() { // send default power limits for the maximum timeframe // to fullfill spec, as there is no data provided logging.Log().Info("Fallback sending default power limits") @@ -253,7 +253,7 @@ func (e *EMobilityImpl) evWriteDefaultPowerLimits() { } // request time series values -func (e *EMobilityImpl) evRequestTimeSeriesValues() { +func (e *EMobility) evRequestTimeSeriesValues() { if e.evTimeSeries == nil { return } @@ -264,7 +264,7 @@ func (e *EMobilityImpl) evRequestTimeSeriesValues() { } // send the ev provided charge plan to the CEM -func (e *EMobilityImpl) evForwardChargePlanIfProvided() { +func (e *EMobility) evForwardChargePlanIfProvided() { if e.dataProvider == nil { return } @@ -279,7 +279,7 @@ func (e *EMobilityImpl) evForwardChargePlanIfProvided() { } // request incentive table values -func (e *EMobilityImpl) evRequestIncentiveValues() { +func (e *EMobility) evRequestIncentiveValues() { if e.evIncentiveTable == nil { return } @@ -290,7 +290,7 @@ func (e *EMobilityImpl) evRequestIncentiveValues() { } // process required steps when an evse is connected -func (e *EMobilityImpl) evseConnected(ski string, entity api.EntityRemote) { +func (e *EMobility) evseConnected(ski string, entity api.EntityRemoteInterface) { e.evseEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -312,7 +312,7 @@ func (e *EMobilityImpl) evseConnected(ski string, entity api.EntityRemote) { } // an EV was disconnected -func (e *EMobilityImpl) evseDisconnected() { +func (e *EMobility) evseDisconnected() { e.evseEntity = nil e.evseDeviceClassification = nil @@ -322,7 +322,7 @@ func (e *EMobilityImpl) evseDisconnected() { } // an EV was disconnected, trigger required cleanup -func (e *EMobilityImpl) evDisconnected() { +func (e *EMobility) evDisconnected() { if e.evEntity == nil { return } @@ -345,7 +345,7 @@ func (e *EMobilityImpl) evDisconnected() { } // an EV was connected, trigger required communication -func (e *EMobilityImpl) evConnected(entity api.EntityRemote) { +func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { e.evEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -514,7 +514,7 @@ func (e *EMobilityImpl) evConnected(entity api.EntityRemote) { // inform the EVSE about used currency and boundary units // // # SPINE UC CoordinatedEVCharging 2.4.3 -func (e *EMobilityImpl) evWriteIncentiveTableDescriptions() { +func (e *EMobility) evWriteIncentiveTableDescriptions() { if e.evIncentiveTable == nil { return } @@ -577,7 +577,7 @@ func (e *EMobilityImpl) evWriteIncentiveTableDescriptions() { // check timeSeries descriptions if constraints element has updateRequired set to true // as this triggers the CEM to send power tables within 20s -func (e *EMobilityImpl) evCheckTimeSeriesDescriptionConstraintsUpdateRequired() bool { +func (e *EMobility) evCheckTimeSeriesDescriptionConstraintsUpdateRequired() bool { if e.evTimeSeries == nil { return false } @@ -596,7 +596,7 @@ func (e *EMobilityImpl) evCheckTimeSeriesDescriptionConstraintsUpdateRequired() // check incentibeTable descriptions if the tariff description has updateRequired set to true // as this triggers the CEM to send incentive tables within 20s -func (e *EMobilityImpl) evCheckIncentiveTableDescriptionUpdateRequired() bool { +func (e *EMobility) evCheckIncentiveTableDescriptionUpdateRequired() bool { if e.evIncentiveTable == nil { return false } diff --git a/emobility/helper_test.go b/emobility/helper_test.go index fe93d66..e87407b 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -26,9 +26,9 @@ type WriteMessageHandler struct { mux sync.Mutex } -var _ shipapi.SpineDataConnection = (*WriteMessageHandler)(nil) +var _ shipapi.ShipConnectionDataWriterInterface = (*WriteMessageHandler)(nil) -func (t *WriteMessageHandler) WriteSpineMessage(message []byte) { +func (t *WriteMessageHandler) WriteShipMessageWithPayload(message []byte) { t.mux.Lock() defer t.mux.Unlock() @@ -101,11 +101,11 @@ func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.Msg const remoteSki string = "testremoteski" // we don't want to handle events in these tests for now, so we don't use NewEMobility(...) -func NewTestEMobility(service api.EEBUSService, details *shipapi.ServiceDetails) *EMobilityImpl { +func NewTestEMobility(service api.ServiceInterface, details *shipapi.ServiceDetails) *EMobility { ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().Entity([]model.AddressEntityType{1}) - emobility := &EMobilityImpl{ + emobility := &EMobility{ service: service, entity: localEntity, ski: ski, @@ -116,7 +116,7 @@ func NewTestEMobility(service api.EEBUSService, details *shipapi.ServiceDetails) return emobility } -func setupEmobility(t *testing.T) (*EMobilityImpl, api.EEBUSService) { +func setupEmobility(t *testing.T) (*EMobility, api.ServiceInterface) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := api.NewConfiguration( "test", "test", "test", "test", @@ -124,40 +124,46 @@ func setupEmobility(t *testing.T) (*EMobilityImpl, api.EEBUSService) { []model.EntityTypeType{model.EntityTypeTypeCEM}, 9999, cert, 230.0, time.Second*4) - serviceHandler := mocks.NewEEBUSServiceHandler(t) + serviceHandler := mocks.NewServiceReaderInterface(t) serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() - eebusService := service.NewEEBUSService(configuration, serviceHandler) + eebusService := service.NewService(configuration, serviceHandler) _ = eebusService.Setup() details := shipapi.NewServiceDetails(remoteSki) emobility := NewTestEMobility(eebusService, details) return emobility, eebusService } -func setupDevices(eebusService api.EEBUSService) (spineapi.DeviceLocal, spineapi.EntityLocal, spineapi.DeviceRemote, []spineapi.EntityRemote, *WriteMessageHandler) { +func setupDevices( + eebusService api.ServiceInterface) ( + spineapi.DeviceLocalInterface, + spineapi.EntityLocalInterface, + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface, + *WriteMessageHandler) { localDevice := eebusService.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - f := spine.NewFeatureLocalImpl(1, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(3, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(4, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(5, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) + f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(6, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + f = spine.NewFeatureLocal(6, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(6, localEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeClient) + f = spine.NewFeatureLocal(6, localEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocalImpl(6, localEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeClient) + f = spine.NewFeatureLocal(6, localEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeClient) localEntity.AddFeature(f) writeHandler := &WriteMessageHandler{} sender := spine.NewSender(writeHandler) - remoteDevice := spine.NewDeviceRemoteImpl(localDevice, remoteSki, sender) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) var clientRemoteFeatures = []struct { featureType model.FeatureTypeType @@ -285,7 +291,13 @@ func setupDevices(eebusService api.EEBUSService) (spineapi.DeviceLocal, spineapi return localDevice, localEntity, remoteDevice, entities, writeHandler } -func datagramForEntityAndFeatures(notify bool, localDevice spineapi.DeviceLocal, localEntity spineapi.EntityLocal, remoteEntity spineapi.EntityRemote, featureType model.FeatureTypeType, remoteRole, localRole model.RoleType) model.DatagramType { +func datagramForEntityAndFeatures( + notify bool, + localDevice spineapi.DeviceLocalInterface, + localEntity spineapi.EntityLocalInterface, + remoteEntity spineapi.EntityRemoteInterface, + featureType model.FeatureTypeType, + remoteRole, localRole model.RoleType) model.DatagramType { var addressSource, addressDestination *model.FeatureAddressType if remoteEntity == nil { // NodeManagement @@ -324,7 +336,10 @@ func datagramForEntityAndFeatures(notify bool, localDevice spineapi.DeviceLocal, return datagram } -func featureOfTypeAndRole(entity spineapi.EntityRemote, featureType model.FeatureTypeType, role model.RoleType) spineapi.FeatureRemote { +func featureOfTypeAndRole( + entity spineapi.EntityRemoteInterface, + featureType model.FeatureTypeType, + role model.RoleType) spineapi.FeatureRemoteInterface { for _, f := range entity.Features() { if f.Type() == featureType && f.Role() == role { return f @@ -333,7 +348,9 @@ func featureOfTypeAndRole(entity spineapi.EntityRemote, featureType model.Featur return nil } -func deviceDiagnosis(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.DeviceDiagnosis { +func deviceDiagnosis( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.DeviceDiagnosis { feature, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -341,7 +358,9 @@ func deviceDiagnosis(localEntity spineapi.EntityLocal, entity spineapi.EntityRem return feature } -func electricalConnection(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.ElectricalConnection { +func electricalConnection( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.ElectricalConnection { feature, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -349,7 +368,9 @@ func electricalConnection(localEntity spineapi.EntityLocal, entity spineapi.Enti return feature } -func measurement(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.Measurement { +func measurement( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.Measurement { feature, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -357,7 +378,9 @@ func measurement(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) return feature } -func deviceConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.DeviceConfiguration { +func deviceConfiguration( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.DeviceConfiguration { feature, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -365,7 +388,9 @@ func deviceConfiguration(localEntity spineapi.EntityLocal, entity spineapi.Entit return feature } -func identificationConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.Identification { +func identificationConfiguration( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.Identification { feature, err := features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -373,7 +398,9 @@ func identificationConfiguration(localEntity spineapi.EntityLocal, entity spinea return feature } -func loadcontrol(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.LoadControl { +func loadcontrol( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.LoadControl { feature, err := features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -381,7 +408,9 @@ func loadcontrol(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) return feature } -func timeSeriesConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.TimeSeries { +func timeSeriesConfiguration( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.TimeSeries { feature, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) @@ -389,7 +418,9 @@ func timeSeriesConfiguration(localEntity spineapi.EntityLocal, entity spineapi.E return feature } -func incentiveTableConfiguration(localEntity spineapi.EntityLocal, entity spineapi.EntityRemote) *features.IncentiveTable { +func incentiveTableConfiguration( + localEntity spineapi.EntityLocalInterface, + entity spineapi.EntityRemoteInterface) *features.IncentiveTable { feature, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) if err != nil { fmt.Println(err) diff --git a/emobility/public.go b/emobility/public.go index 4051988..6300116 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -14,7 +14,7 @@ import ( // // this includes all required features and // minimal data being available -func (e *EMobilityImpl) EVConnected() bool { +func (e *EMobility) EVConnected() bool { // To report an EV as being connected, also consider all required // features to be available and assigned if e.evEntity == nil || @@ -52,7 +52,7 @@ func (e *EMobilityImpl) EVConnected() bool { } // return the current charge state of the EV -func (e *EMobilityImpl) EVCurrentChargeState() (EVChargeStateType, error) { +func (e *EMobility) EVCurrentChargeState() (EVChargeStateType, error) { if e.evEntity == nil || e.evDeviceDiagnosis == nil { return EVChargeStateTypeUnplugged, nil } @@ -82,7 +82,7 @@ func (e *EMobilityImpl) EVCurrentChargeState() (EVChargeStateType, error) { } // return the number of ac connected phases of the EV or 0 if it is unknown -func (e *EMobilityImpl) EVConnectedPhases() (uint, error) { +func (e *EMobility) EVConnectedPhases() (uint, error) { if e.evEntity == nil || e.evElectricalConnection == nil { return 0, ErrEVDisconnected } @@ -111,7 +111,7 @@ func (e *EMobilityImpl) EVConnectedPhases() (uint, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVChargedEnergy() (float64, error) { +func (e *EMobility) EVChargedEnergy() (float64, error) { if e.evEntity == nil || e.evMeasurement == nil { return 0, ErrEVDisconnected } @@ -138,7 +138,7 @@ func (e *EMobilityImpl) EVChargedEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVPowerPerPhase() ([]float64, error) { +func (e *EMobility) EVPowerPerPhase() ([]float64, error) { if e.evEntity == nil || e.evMeasurement == nil { return nil, ErrEVDisconnected } @@ -192,7 +192,7 @@ func (e *EMobilityImpl) EVPowerPerPhase() ([]float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVCurrentsPerPhase() ([]float64, error) { +func (e *EMobility) EVCurrentsPerPhase() ([]float64, error) { if e.evEntity == nil || e.evElectricalConnection == nil { return nil, ErrEVDisconnected } @@ -245,7 +245,7 @@ func (e *EMobilityImpl) EVCurrentsPerPhase() ([]float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVCurrentLimits() ([]float64, []float64, []float64, error) { +func (e *EMobility) EVCurrentLimits() ([]float64, []float64, []float64, error) { if e.evEntity == nil || e.evElectricalConnection == nil { return nil, nil, nil, ErrEVDisconnected } @@ -284,7 +284,7 @@ func (e *EMobilityImpl) EVCurrentLimits() ([]float64, []float64, []float64, erro // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { +func (e *EMobility) EVLoadControlObligationLimits() ([]float64, error) { if e.evEntity == nil || e.evElectricalConnection == nil || e.evLoadControl == nil { return nil, ErrEVDisconnected } @@ -373,7 +373,7 @@ func (e *EMobilityImpl) EVLoadControlObligationLimits() ([]float64, error) { // - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. // - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. // - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. -func (e *EMobilityImpl) EVWriteLoadControlLimits(limits []EVLoadLimits) error { +func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { if e.evEntity == nil { return ErrEVDisconnected } @@ -459,7 +459,7 @@ func (e *EMobilityImpl) EVWriteLoadControlLimits(limits []EVLoadLimits) error { // - ErrDataNotAvailable if that information is not (yet) available // - ErrNotSupported if getting the communication standard is not supported // - and others -func (e *EMobilityImpl) EVCommunicationStandard() (EVCommunicationStandardType, error) { +func (e *EMobility) EVCommunicationStandard() (EVCommunicationStandardType, error) { if e.evEntity == nil || e.evDeviceConfiguration == nil { return EVCommunicationStandardTypeUnknown, ErrEVDisconnected } @@ -488,7 +488,7 @@ func (e *EMobilityImpl) EVCommunicationStandard() (EVCommunicationStandardType, // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *EMobilityImpl) EVIdentification() (string, error) { +func (e *EMobility) EVIdentification() (string, error) { if e.evEntity == nil { return "", ErrEVDisconnected } @@ -518,7 +518,7 @@ func (e *EMobilityImpl) EVIdentification() (string, error) { // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *EMobilityImpl) EVOptimizationOfSelfConsumptionSupported() (bool, error) { +func (e *EMobility) EVOptimizationOfSelfConsumptionSupported() (bool, error) { if e.evEntity == nil || e.evLoadControl == nil { return false, ErrEVDisconnected } @@ -550,7 +550,7 @@ func (e *EMobilityImpl) EVOptimizationOfSelfConsumptionSupported() (bool, error) // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVSoCSupported() (bool, error) { +func (e *EMobility) EVSoCSupported() (bool, error) { if e.evEntity == nil || e.evMeasurement == nil { return false, ErrEVDisconnected } @@ -587,7 +587,7 @@ func (e *EMobilityImpl) EVSoCSupported() (bool, error) { // - ErrNotSupported if support for SoC is not possible // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobilityImpl) EVSoC() (float64, error) { +func (e *EMobility) EVSoC() (float64, error) { if e.evEntity == nil || e.evMeasurement == nil { return 0, ErrEVDisconnected } @@ -617,7 +617,7 @@ func (e *EMobilityImpl) EVSoC() (float64, error) { // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *EMobilityImpl) EVCoordinatedChargingSupported() (bool, error) { +func (e *EMobility) EVCoordinatedChargingSupported() (bool, error) { if e.evEntity == nil { return false, ErrEVDisconnected } @@ -636,7 +636,7 @@ func (e *EMobilityImpl) EVCoordinatedChargingSupported() (bool, error) { } // returns the current charging strategy -func (e *EMobilityImpl) EVChargeStrategy() EVChargeStrategyType { +func (e *EMobility) EVChargeStrategy() EVChargeStrategyType { if e.evEntity == nil || e.evTimeSeries == nil { return EVChargeStrategyTypeUnknown } @@ -696,7 +696,7 @@ func (e *EMobilityImpl) EVChargeStrategy() EVChargeStrategyType { } // returns the current energy demand in Wh and the duration -func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { +func (e *EMobility) EVEnergyDemand() (EVDemand, error) { demand := EVDemand{} if e.evEntity == nil { @@ -759,7 +759,7 @@ func (e *EMobilityImpl) EVEnergyDemand() (EVDemand, error) { return demand, nil } -func (e *EMobilityImpl) EVChargePlanConstraints() ([]EVDurationSlotValue, error) { +func (e *EMobility) EVChargePlanConstraints() ([]EVDurationSlotValue, error) { constraints := []EVDurationSlotValue{} if e.evEntity == nil { @@ -813,7 +813,7 @@ func (e *EMobilityImpl) EVChargePlanConstraints() ([]EVDurationSlotValue, error) return constraints, nil } -func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { +func (e *EMobility) EVChargePlan() (EVChargePlan, error) { plan := EVChargePlan{} if e.evEntity == nil { @@ -894,7 +894,7 @@ func (e *EMobilityImpl) EVChargePlan() (EVChargePlan, error) { } // returns the constraints for the time slots -func (e *EMobilityImpl) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { +func (e *EMobility) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { result := EVTimeSlotConstraints{} if e.evEntity == nil || e.evTimeSeries == nil { @@ -935,7 +935,7 @@ func (e *EMobilityImpl) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { } // send power limits to the EV -func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { +func (e *EMobility) EVWritePowerLimits(data []EVDurationSlotValue) error { if e.evEntity == nil || e.evTimeSeries == nil { return ErrNotSupported } @@ -1000,7 +1000,7 @@ func (e *EMobilityImpl) EVWritePowerLimits(data []EVDurationSlotValue) error { } // returns the minimum and maximum number of incentive slots allowed -func (e *EMobilityImpl) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { +func (e *EMobility) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { result := EVIncentiveSlotConstraints{} if e.evEntity == nil || e.evIncentiveTable == nil { @@ -1026,7 +1026,7 @@ func (e *EMobilityImpl) EVIncentiveConstraints() (EVIncentiveSlotConstraints, er } // send incentives to the EV -func (e *EMobilityImpl) EVWriteIncentives(data []EVDurationSlotValue) error { +func (e *EMobility) EVWriteIncentives(data []EVDurationSlotValue) error { if e.evEntity == nil || e.evIncentiveTable == nil { return features.ErrDataNotAvailable } diff --git a/emobility/results.go b/emobility/results.go index ec77c77..ce8e3ca 100644 --- a/emobility/results.go +++ b/emobility/results.go @@ -5,7 +5,7 @@ import ( "github.com/enbility/spine-go/model" ) -func (e *EMobilityImpl) HandleResult(errorMsg api.ResultMessage) { +func (e *EMobility) HandleResult(errorMsg api.ResultMessage) { isEvse := errorMsg.EntityRemote == e.evseEntity isEv := e.evEntity != nil && errorMsg.EntityRemote == e.evEntity @@ -20,7 +20,7 @@ func (e *EMobilityImpl) HandleResult(errorMsg api.ResultMessage) { } // Handle DeviceDiagnosis Results -func (e *EMobilityImpl) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { +func (e *EMobility) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { // is this an error for a heartbeat message? if *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { return diff --git a/emobility/scenario.go b/emobility/solution.go similarity index 83% rename from emobility/scenario.go rename to emobility/solution.go index 9d2c409..4096cc1 100644 --- a/emobility/scenario.go +++ b/emobility/solution.go @@ -3,18 +3,18 @@ package emobility import ( "sync" - "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/api" + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/util" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) -type EmobilityScenarioImpl struct { - *scenarios.ScenarioImpl +type EmobilitySolution struct { + *api.Solution - remoteDevices map[string]*EMobilityImpl + remoteDevices map[string]*EMobility mux sync.Mutex @@ -22,19 +22,22 @@ type EmobilityScenarioImpl struct { configuration EmobilityConfiguration } -var _ scenarios.ScenariosI = (*EmobilityScenarioImpl)(nil) +var _ api.SolutionInterface = (*EmobilitySolution)(nil) -func NewEMobilityScenario(service api.EEBUSService, currency model.CurrencyType, configuration EmobilityConfiguration) *EmobilityScenarioImpl { - return &EmobilityScenarioImpl{ - ScenarioImpl: scenarios.NewScenarioImpl(service), - remoteDevices: make(map[string]*EMobilityImpl), +func NewEMobilitySolution( + service eebusapi.ServiceInterface, + currency model.CurrencyType, + configuration EmobilityConfiguration) *EmobilitySolution { + return &EmobilitySolution{ + Solution: api.NewSolution(service), + remoteDevices: make(map[string]*EMobility), currency: currency, configuration: configuration, } } // adds all the supported features to the local entity -func (e *EmobilityScenarioImpl) AddFeatures() { +func (e *EmobilitySolution) AddFeatures() { localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // server features @@ -74,7 +77,7 @@ func (e *EmobilityScenarioImpl) AddFeatures() { } // add supported e-mobility usecases -func (e *EmobilityScenarioImpl) AddUseCases() { +func (e *EmobilitySolution) AddUseCases() { localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( @@ -136,7 +139,7 @@ func (e *EmobilityScenarioImpl) AddUseCases() { } } -func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { +func (e *EmobilitySolution) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { // TODO: emobility should be stored per remote SKI and // only be set for the SKI if the device supports it e.mux.Lock() @@ -155,7 +158,7 @@ func (e *EmobilityScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDet return emobility } -func (e *EmobilityScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { +func (e *EmobilitySolution) UnRegisterRemoteDevice(remoteDeviceSki string) { e.mux.Lock() defer e.mux.Unlock() @@ -164,7 +167,7 @@ func (e *EmobilityScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { e.Service.RegisterRemoteSKI(remoteDeviceSki, false) } -func (e *EmobilityScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { +func (e *EmobilitySolution) HandleResult(errorMsg spineapi.ResultMessage) { e.mux.Lock() defer e.mux.Unlock() diff --git a/go.mod b/go.mod index e4a522b..e9ee8c3 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240122174858-ae6f0b3fb3cf - github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec - github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 + github.com/enbility/eebus-go v0.0.0-20240123191239-38836994d74c + github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 + github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b @@ -18,7 +18,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/holoplot/go-avahi v1.0.1 // indirect + github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed // indirect github.com/miekg/dns v1.1.57 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/date v1.20.5 // indirect diff --git a/go.sum b/go.sum index fd6f054..61ca1e6 100644 --- a/go.sum +++ b/go.sum @@ -5,13 +5,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240122174858-ae6f0b3fb3cf h1:+hy1FFJkXzyVLFjaLAOtdwTuRnIkl/XjAuSlGBTmPxM= -github.com/enbility/eebus-go v0.0.0-20240122174858-ae6f0b3fb3cf/go.mod h1:n+z0Hayk+RtBIJwQy10zcEK3n2conR3dAlZCtiXbB5Q= -github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec h1:Bu0hu9rsJIkUCtg64YJZte5OaGx0LStrmS10ufEma6o= -github.com/enbility/ship-go v0.0.0-20240122172808-3c326eca22ec/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41 h1:FoeVeEesGzKoFL1YRD+DS65Jm5O2vXqXl5/5k2WSbxg= -github.com/enbility/spine-go v0.0.0-20240122174730-b2b9c6f10f41/go.mod h1:v/MAO6HzXJ8fzvWkC2454W+bHmPO8Ry7lvRj/eWmREQ= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/enbility/eebus-go v0.0.0-20240123191239-38836994d74c h1:nw7IcECZKHBBCt/ev1dOgHhfVSJ5UbL61U2yXdbUKls= +github.com/enbility/eebus-go v0.0.0-20240123191239-38836994d74c/go.mod h1:hPGvK0YSiY2P47teGVvDgYkRD2GcFGZxA1NqVF8dXAk= +github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 h1:I54U0+MHwtsHUS7OLEt0z5fGNZOpV7G3EgQxRvFXeKI= +github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f h1:BmUN9NM/mWblJIvvctkPgHN65/+ZmwyN1IF3a6Jzdy0= +github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -20,8 +19,8 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfvvk= -github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= +github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= +github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= diff --git a/grid/api.go b/grid/api.go new file mode 100644 index 0000000..209ab16 --- /dev/null +++ b/grid/api.go @@ -0,0 +1,11 @@ +package grid + +type GridInterface interface { + PowerLimitationFactor() (float64, error) + MomentaryPowerConsumptionOrProduction() (float64, error) + TotalFeedInEnergy() (float64, error) + TotalConsumedEnergy() (float64, error) + MomentaryCurrentConsumptionOrProduction() ([]float64, error) + Voltage() ([]float64, error) + Frequency() (float64, error) +} diff --git a/grid/events.go b/grid/events.go index 5ffb532..6baaa21 100644 --- a/grid/events.go +++ b/grid/events.go @@ -8,7 +8,7 @@ import ( ) // Internal EventHandler Interface for the CEM -func (e *GridImpl) HandleEvent(payload api.EventPayload) { +func (e *Grid) HandleEvent(payload api.EventPayload) { // we only care about the registered SKI if payload.Ski != e.ski { return @@ -64,7 +64,7 @@ func (e *GridImpl) HandleEvent(payload api.EventPayload) { } // process required steps when a grid device is connected -func (e *GridImpl) gridConnected(ski string, entity api.EntityRemote) { +func (e *Grid) gridConnected(ski string, entity api.EntityRemoteInterface) { e.gridEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -131,7 +131,7 @@ func (e *GridImpl) gridConnected(ski string, entity api.EntityRemote) { } // a grid device was disconnected -func (e *GridImpl) gridDisconnected() { +func (e *Grid) gridDisconnected() { e.gridEntity = nil e.gridDeviceConfiguration = nil diff --git a/grid/grid.go b/grid/grid.go index 29699c6..49d917a 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -10,22 +10,12 @@ import ( "github.com/enbility/spine-go/spine" ) -type GridI interface { - PowerLimitationFactor() (float64, error) - MomentaryPowerConsumptionOrProduction() (float64, error) - TotalFeedInEnergy() (float64, error) - TotalConsumedEnergy() (float64, error) - MomentaryCurrentConsumptionOrProduction() ([]float64, error) - Voltage() ([]float64, error) - Frequency() (float64, error) -} - -type GridImpl struct { - entity spineapi.EntityLocal +type Grid struct { + entity spineapi.EntityLocalInterface - service api.EEBUSService + service api.ServiceInterface - gridEntity spineapi.EntityRemote + gridEntity spineapi.EntityRemoteInterface gridDeviceConfiguration *features.DeviceConfiguration gridElectricalConnection *features.ElectricalConnection @@ -34,15 +24,15 @@ type GridImpl struct { ski string } -var _ GridI = (*GridImpl)(nil) +var _ GridInterface = (*Grid)(nil) // Add Grid support -func NewGrid(service api.EEBUSService, details *shipapi.ServiceDetails) *GridImpl { +func NewGrid(service api.ServiceInterface, details *shipapi.ServiceDetails) *Grid { ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - grid := &GridImpl{ + grid := &Grid{ service: service, entity: localEntity, ski: ski, diff --git a/grid/public.go b/grid/public.go index 9f9fa07..62294d9 100644 --- a/grid/public.go +++ b/grid/public.go @@ -12,7 +12,7 @@ import ( // - ErrDataNotAvailable if that information is not (yet) available // - ErrNotSupported if getting the communication standard is not supported // - and others -func (g *GridImpl) PowerLimitationFactor() (float64, error) { +func (g *Grid) PowerLimitationFactor() (float64, error) { if g.gridEntity == nil { return 0, util.ErrDeviceDisconnected } @@ -47,7 +47,7 @@ func (g *GridImpl) PowerLimitationFactor() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (g *GridImpl) MomentaryPowerConsumptionOrProduction() (float64, error) { +func (g *Grid) MomentaryPowerConsumptionOrProduction() (float64, error) { measurement := model.MeasurementTypeTypePower commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACPowerTotal @@ -84,7 +84,7 @@ func (g *GridImpl) MomentaryPowerConsumptionOrProduction() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (g *GridImpl) TotalFeedInEnergy() (float64, error) { +func (g *Grid) TotalFeedInEnergy() (float64, error) { measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeGridFeedIn @@ -107,7 +107,7 @@ func (g *GridImpl) TotalFeedInEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (g *GridImpl) TotalConsumedEnergy() (float64, error) { +func (g *Grid) TotalConsumedEnergy() (float64, error) { measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeGridConsumption @@ -130,7 +130,7 @@ func (g *GridImpl) TotalConsumedEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (g *GridImpl) MomentaryCurrentConsumptionOrProduction() ([]float64, error) { +func (g *Grid) MomentaryCurrentConsumptionOrProduction() ([]float64, error) { measurement := model.MeasurementTypeTypeCurrent commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACCurrent @@ -181,7 +181,7 @@ func (g *GridImpl) MomentaryCurrentConsumptionOrProduction() ([]float64, error) // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (g *GridImpl) Voltage() ([]float64, error) { +func (g *Grid) Voltage() ([]float64, error) { measurement := model.MeasurementTypeTypeVoltage commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACVoltage @@ -222,7 +222,7 @@ func (g *GridImpl) Voltage() ([]float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (g *GridImpl) Frequency() (float64, error) { +func (g *Grid) Frequency() (float64, error) { measurement := model.MeasurementTypeTypeFrequency commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACFrequency @@ -242,7 +242,7 @@ func (g *GridImpl) Frequency() (float64, error) { // helper -func (g *GridImpl) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { +func (g *Grid) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { if g.gridEntity == nil { return nil, util.ErrDeviceDisconnected } diff --git a/grid/results.go b/grid/results.go index 62f8910..756c16f 100644 --- a/grid/results.go +++ b/grid/results.go @@ -4,5 +4,5 @@ import ( "github.com/enbility/spine-go/api" ) -func (e *GridImpl) HandleResult(errorMsg api.ResultMessage) { +func (e *Grid) HandleResult(errorMsg api.ResultMessage) { } diff --git a/grid/scenario.go b/grid/solution.go similarity index 68% rename from grid/scenario.go rename to grid/solution.go index 82bc800..bb68e8a 100644 --- a/grid/scenario.go +++ b/grid/solution.go @@ -3,32 +3,32 @@ package grid import ( "sync" - "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/api" + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) -type GridScenarioImpl struct { - *scenarios.ScenarioImpl +type GridSolution struct { + *api.Solution - remoteDevices map[string]*GridImpl + remoteDevices map[string]*Grid mux sync.Mutex } -var _ scenarios.ScenariosI = (*GridScenarioImpl)(nil) +var _ api.SolutionInterface = (*GridSolution)(nil) -func NewGridScenario(service api.EEBUSService) *GridScenarioImpl { - return &GridScenarioImpl{ - ScenarioImpl: scenarios.NewScenarioImpl(service), - remoteDevices: make(map[string]*GridImpl), +func NewGridScenario(service eebusapi.ServiceInterface) *GridSolution { + return &GridSolution{ + Solution: api.NewSolution(service), + remoteDevices: make(map[string]*Grid), } } // adds all the supported features to the local entity -func (e *GridScenarioImpl) AddFeatures() { +func (e *GridSolution) AddFeatures() { localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features @@ -44,7 +44,7 @@ func (e *GridScenarioImpl) AddFeatures() { } // add supported grid usecases -func (e *GridScenarioImpl) AddUseCases() { +func (e *GridSolution) AddUseCases() { localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( @@ -56,7 +56,7 @@ func (e *GridScenarioImpl) AddUseCases() { []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) } -func (e *GridScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { +func (e *GridSolution) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { // TODO: grid should be stored per remote SKI and // only be set for the SKI if the device supports it e.mux.Lock() @@ -71,7 +71,7 @@ func (e *GridScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, return grid } -func (e *GridScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { +func (e *GridSolution) UnRegisterRemoteDevice(remoteDeviceSki string) { e.mux.Lock() defer e.mux.Unlock() @@ -80,7 +80,7 @@ func (e *GridScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { e.Service.RegisterRemoteSKI(remoteDeviceSki, false) } -func (e *GridScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { +func (e *GridSolution) HandleResult(errorMsg spineapi.ResultMessage) { e.mux.Lock() defer e.mux.Unlock() diff --git a/inverterbatteryvis/api.go b/inverterbatteryvis/api.go new file mode 100644 index 0000000..f38ebdf --- /dev/null +++ b/inverterbatteryvis/api.go @@ -0,0 +1,8 @@ +package inverterbatteryvis + +type InverterBatteryVisInterface interface { + CurrentDisChargePower() (float64, error) + TotalChargeEnergy() (float64, error) + TotalDischargeEnergy() (float64, error) + CurrentStateOfCharge() (float64, error) +} diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index 2c9d1a5..0359597 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -8,7 +8,7 @@ import ( ) // Internal EventHandler Interface for the CEM -func (i *InverterBatteryVisImpl) HandleEvent(payload api.EventPayload) { +func (i *InverterBatteryVis) HandleEvent(payload api.EventPayload) { // we only care about the registered SKI if payload.Ski != i.ski { return @@ -79,7 +79,7 @@ func (i *InverterBatteryVisImpl) HandleEvent(payload api.EventPayload) { } // process required steps when a battery device entity is connected -func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity api.EntityRemote) { +func (i *InverterBatteryVis) inverterConnected(ski string, entity api.EntityRemoteInterface) { i.inverterEntity = entity localDevice := i.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -124,7 +124,7 @@ func (i *InverterBatteryVisImpl) inverterConnected(ski string, entity api.Entity } // a battery device entity was disconnected -func (i *InverterBatteryVisImpl) inverterDisconnected() { +func (i *InverterBatteryVis) inverterDisconnected() { i.inverterEntity = nil i.inverterElectricalConnection = nil diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go index 2239ac2..7ee8e16 100644 --- a/inverterbatteryvis/invertervis.go +++ b/inverterbatteryvis/invertervis.go @@ -10,34 +10,27 @@ import ( "github.com/enbility/spine-go/spine" ) -type InverterBatteryVisI interface { - CurrentDisChargePower() (float64, error) - TotalChargeEnergy() (float64, error) - TotalDischargeEnergy() (float64, error) - CurrentStateOfCharge() (float64, error) -} - -type InverterBatteryVisImpl struct { - entity spineapi.EntityLocal +type InverterBatteryVis struct { + entity spineapi.EntityLocalInterface - service api.EEBUSService + service api.ServiceInterface - inverterEntity spineapi.EntityRemote + inverterEntity spineapi.EntityRemoteInterface inverterElectricalConnection *features.ElectricalConnection inverterMeasurement *features.Measurement ski string } -var _ InverterBatteryVisI = (*InverterBatteryVisImpl)(nil) +var _ InverterBatteryVisInterface = (*InverterBatteryVis)(nil) // Add InverterBatteryVis support -func NewInverterBatteryVis(service api.EEBUSService, details *shipapi.ServiceDetails) *InverterBatteryVisImpl { +func NewInverterBatteryVis(service api.ServiceInterface, details *shipapi.ServiceDetails) *InverterBatteryVis { ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - inverter := &InverterBatteryVisImpl{ + inverter := &InverterBatteryVis{ service: service, entity: localEntity, ski: ski, diff --git a/inverterbatteryvis/public.go b/inverterbatteryvis/public.go index 9d818fb..6e9ab51 100644 --- a/inverterbatteryvis/public.go +++ b/inverterbatteryvis/public.go @@ -14,7 +14,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVisImpl) CurrentDisChargePower() (float64, error) { +func (i *InverterBatteryVis) CurrentDisChargePower() (float64, error) { measurement := model.MeasurementTypeTypePower commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACPowerTotal @@ -39,7 +39,7 @@ func (i *InverterBatteryVisImpl) CurrentDisChargePower() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVisImpl) TotalChargeEnergy() (float64, error) { +func (i *InverterBatteryVis) TotalChargeEnergy() (float64, error) { measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeCharge @@ -62,7 +62,7 @@ func (i *InverterBatteryVisImpl) TotalChargeEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVisImpl) TotalDischargeEnergy() (float64, error) { +func (i *InverterBatteryVis) TotalDischargeEnergy() (float64, error) { measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeDischarge @@ -85,7 +85,7 @@ func (i *InverterBatteryVisImpl) TotalDischargeEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVisImpl) CurrentStateOfCharge() (float64, error) { +func (i *InverterBatteryVis) CurrentStateOfCharge() (float64, error) { measurement := model.MeasurementTypeTypePercentage commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeStateOfCharge @@ -105,7 +105,7 @@ func (i *InverterBatteryVisImpl) CurrentStateOfCharge() (float64, error) { // helper -func (i *InverterBatteryVisImpl) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { +func (i *InverterBatteryVis) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { if i.inverterEntity == nil { return nil, util.ErrDeviceDisconnected } diff --git a/inverterbatteryvis/results.go b/inverterbatteryvis/results.go index e4bea8c..7a679ed 100644 --- a/inverterbatteryvis/results.go +++ b/inverterbatteryvis/results.go @@ -4,5 +4,5 @@ import ( "github.com/enbility/spine-go/api" ) -func (i *InverterBatteryVisImpl) HandleResult(errorMsg api.ResultMessage) { +func (i *InverterBatteryVis) HandleResult(errorMsg api.ResultMessage) { } diff --git a/inverterbatteryvis/scenario.go b/inverterbatteryvis/solution.go similarity index 83% rename from inverterbatteryvis/scenario.go rename to inverterbatteryvis/solution.go index f62bfd0..5bad74d 100644 --- a/inverterbatteryvis/scenario.go +++ b/inverterbatteryvis/solution.go @@ -3,27 +3,27 @@ package inverterbatteryvis import ( "sync" - "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/api" + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) type InverterBatteryVisScenarioImpl struct { - *scenarios.ScenarioImpl + *api.Solution - remoteDevices map[string]*InverterBatteryVisImpl + remoteDevices map[string]*InverterBatteryVis mux sync.Mutex } -var _ scenarios.ScenariosI = (*InverterBatteryVisScenarioImpl)(nil) +var _ api.SolutionInterface = (*InverterBatteryVisScenarioImpl)(nil) -func NewInverterVisScenario(service api.EEBUSService) *InverterBatteryVisScenarioImpl { +func NewInverterVisScenario(service eebusapi.ServiceInterface) *InverterBatteryVisScenarioImpl { return &InverterBatteryVisScenarioImpl{ - ScenarioImpl: scenarios.NewScenarioImpl(service), - remoteDevices: make(map[string]*InverterBatteryVisImpl), + Solution: api.NewSolution(service), + remoteDevices: make(map[string]*InverterBatteryVis), } } diff --git a/inverterpvvis/api.go b/inverterpvvis/api.go new file mode 100644 index 0000000..2ded92a --- /dev/null +++ b/inverterpvvis/api.go @@ -0,0 +1,7 @@ +package inverterpvvis + +type InverterPVVisInterface interface { + CurrentProductionPower() (float64, error) + NominalPeakPower() (float64, error) + TotalPVYield() (float64, error) +} diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index 66385aa..3bd4dca 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -8,7 +8,7 @@ import ( ) // Internal EventHandler Interface for the CEM -func (i *InverterPVVisImpl) HandleEvent(payload api.EventPayload) { +func (i *InverterPVVis) HandleEvent(payload api.EventPayload) { // we only care about the registered SKI if payload.Ski != i.ski { return @@ -89,7 +89,7 @@ func (i *InverterPVVisImpl) HandleEvent(payload api.EventPayload) { } // process required steps when a pv device entity is connected -func (e *InverterPVVisImpl) inverterConnected(ski string, entity api.EntityRemote) { +func (e *InverterPVVis) inverterConnected(ski string, entity api.EntityRemoteInterface) { e.inverterEntity = entity localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -148,7 +148,7 @@ func (e *InverterPVVisImpl) inverterConnected(ski string, entity api.EntityRemot } // a pv device entity was disconnected -func (e *InverterPVVisImpl) inverterDisconnected() { +func (e *InverterPVVis) inverterDisconnected() { e.inverterMeasurement = nil e.inverterElectricalConnection = nil diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go index 807ccaa..ac89ce5 100644 --- a/inverterpvvis/invertervis.go +++ b/inverterpvvis/invertervis.go @@ -1,7 +1,7 @@ package inverterpvvis import ( - "github.com/enbility/eebus-go/api" + eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/util" @@ -10,18 +10,12 @@ import ( "github.com/enbility/spine-go/spine" ) -type InverterPVVisI interface { - CurrentProductionPower() (float64, error) - NominalPeakPower() (float64, error) - TotalPVYield() (float64, error) -} - -type InverterPVVisImpl struct { - entity spineapi.EntityLocal +type InverterPVVis struct { + entity spineapi.EntityLocalInterface - service api.EEBUSService + service eebusapi.ServiceInterface - inverterEntity spineapi.EntityRemote + inverterEntity spineapi.EntityRemoteInterface inverterDeviceConfiguration *features.DeviceConfiguration inverterElectricalConnection *features.ElectricalConnection inverterMeasurement *features.Measurement @@ -29,15 +23,15 @@ type InverterPVVisImpl struct { ski string } -var _ InverterPVVisI = (*InverterPVVisImpl)(nil) +var _ InverterPVVisInterface = (*InverterPVVis)(nil) // Add InverterPVVis support -func NewInverterPVVis(service api.EEBUSService, details *shipapi.ServiceDetails) *InverterPVVisImpl { +func NewInverterPVVis(service eebusapi.ServiceInterface, details *shipapi.ServiceDetails) *InverterPVVis { ski := util.NormalizeSKI(details.SKI()) localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - inverter := &InverterPVVisImpl{ + inverter := &InverterPVVis{ service: service, entity: localEntity, ski: ski, diff --git a/inverterpvvis/public.go b/inverterpvvis/public.go index a99714d..8ebd01c 100644 --- a/inverterpvvis/public.go +++ b/inverterpvvis/public.go @@ -11,7 +11,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterPVVisImpl) CurrentProductionPower() (float64, error) { +func (i *InverterPVVis) CurrentProductionPower() (float64, error) { measurement := model.MeasurementTypeTypePower commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACPowerTotal @@ -36,7 +36,7 @@ func (i *InverterPVVisImpl) CurrentProductionPower() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterPVVisImpl) NominalPeakPower() (float64, error) { +func (i *InverterPVVis) NominalPeakPower() (float64, error) { if i.inverterEntity == nil { return 0, util.ErrDeviceDisconnected } @@ -73,7 +73,7 @@ func (i *InverterPVVisImpl) NominalPeakPower() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterPVVisImpl) TotalPVYield() (float64, error) { +func (i *InverterPVVis) TotalPVYield() (float64, error) { measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACYieldTotal @@ -93,7 +93,7 @@ func (i *InverterPVVisImpl) TotalPVYield() (float64, error) { // helper -func (i *InverterPVVisImpl) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { +func (i *InverterPVVis) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { if i.inverterEntity == nil { return nil, util.ErrDeviceDisconnected } diff --git a/inverterpvvis/results.go b/inverterpvvis/results.go index 0fea29f..48657e6 100644 --- a/inverterpvvis/results.go +++ b/inverterpvvis/results.go @@ -4,5 +4,5 @@ import ( "github.com/enbility/spine-go/api" ) -func (i *InverterPVVisImpl) HandleResult(errorMsg api.ResultMessage) { +func (i *InverterPVVis) HandleResult(errorMsg api.ResultMessage) { } diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go index 3f8fe72..edabf11 100644 --- a/inverterpvvis/scenario.go +++ b/inverterpvvis/scenario.go @@ -3,27 +3,27 @@ package inverterpvvis import ( "sync" - "github.com/enbility/cemd/scenarios" - "github.com/enbility/eebus-go/api" + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) type InverterPVVisScenarioImpl struct { - *scenarios.ScenarioImpl + *api.Solution - remoteDevices map[string]*InverterPVVisImpl + remoteDevices map[string]*InverterPVVis mux sync.Mutex } -var _ scenarios.ScenariosI = (*InverterPVVisScenarioImpl)(nil) +var _ api.SolutionInterface = (*InverterPVVisScenarioImpl)(nil) -func NewInverterVisScenario(service api.EEBUSService) *InverterPVVisScenarioImpl { +func NewInverterVisScenario(service eebusapi.ServiceInterface) *InverterPVVisScenarioImpl { return &InverterPVVisScenarioImpl{ - ScenarioImpl: scenarios.NewScenarioImpl(service), - remoteDevices: make(map[string]*InverterPVVisImpl), + Solution: api.NewSolution(service), + remoteDevices: make(map[string]*InverterPVVis), } } diff --git a/scenarios/types.go b/scenarios/types.go deleted file mode 100644 index 772e41c..0000000 --- a/scenarios/types.go +++ /dev/null @@ -1,24 +0,0 @@ -package scenarios - -import ( - "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" -) - -// Implemented by *ScenarioImpl, used by CemImpl -type ScenariosI interface { - RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any - UnRegisterRemoteDevice(remoteDeviceSki string) - AddFeatures() - AddUseCases() -} - -type ScenarioImpl struct { - Service api.EEBUSService -} - -func NewScenarioImpl(service api.EEBUSService) *ScenarioImpl { - return &ScenarioImpl{ - Service: service, - } -} diff --git a/util/helper.go b/util/helper.go index 95c3051..7989862 100644 --- a/util/helper.go +++ b/util/helper.go @@ -10,7 +10,10 @@ import ( var PhaseNameMapping = []model.ElectricalConnectionPhaseNameType{model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC} // check if the given usecase, actor is supported by the remote device -func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorType, remoteDevice spineapi.DeviceRemote) bool { +func IsUsecaseSupported( + usecase model.UseCaseNameType, + actor model.UseCaseActorType, + remoteDevice spineapi.DeviceRemoteInterface) bool { uci := remoteDevice.UseCases() for _, element := range uci { @@ -28,7 +31,10 @@ func IsUsecaseSupported(usecase model.UseCaseNameType, actor model.UseCaseActorT } // return the remote entity of a given type and device ski -func EntityOfTypeForSki(service api.EEBUSService, entityType model.EntityTypeType, ski string) (spineapi.EntityRemote, error) { +func EntityOfTypeForSki( + service api.ServiceInterface, + entityType model.EntityTypeType, + ski string) (spineapi.EntityRemoteInterface, error) { rDevice := service.LocalDevice().RemoteDeviceForSki(ski) if rDevice == nil { From 5faa7c78cf811b653c5616ac37bb760c0d1ab976 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 25 Jan 2024 19:02:37 +0100 Subject: [PATCH 057/227] Various updates - Update to latest spine and eebus libs - Do not save features but check for them every time needed --- emobility/api.go | 46 +-- emobility/emobility.go | 14 - emobility/evcoordinatedcharging_test.go | 18 +- emobility/events.go | 330 ++++++++---------- emobility/features.go | 61 ++++ emobility/helper_test.go | 81 ----- emobility/public.go | 227 +++++++----- emobility/public_EVChargePlan_test.go | 14 +- emobility/public_EVChargeStrategy_test.go | 33 +- emobility/public_EVChargedEnergy_test.go | 19 +- .../public_EVCommunicationStandard_test.go | 21 +- emobility/public_EVConnectedPhases_test.go | 19 +- ...lic_EVCoordinatedChargingSupported_test.go | 8 +- emobility/public_EVCurrentChargeState_test.go | 27 +- emobility/public_EVCurrentLimits_test.go | 19 +- emobility/public_EVCurrentsPerPhase_test.go | 22 +- emobility/public_EVEnergyDemand_test.go | 26 +- emobility/public_EVIdentification_test.go | 12 +- .../public_EVIncentiveConstraints_test.go | 19 +- ...mizationOfSelfConsumptionSupported_test.go | 21 +- emobility/public_EVPowerPerPhase_test.go | 44 +-- emobility/public_EVSoCSupported_test.go | 21 +- emobility/public_EVSoC_test.go | 23 +- .../public_EVTimeSlotConstraints_test.go | 17 +- emobility/public_EVWriteIncentives_test.go | 19 +- .../public_EVWriteLoadControlLimits_test.go | 21 +- emobility/public_EVWritePowerLimits_test.go | 19 +- go.mod | 4 +- go.sum | 8 +- 29 files changed, 630 insertions(+), 583 deletions(-) create mode 100644 emobility/features.go diff --git a/emobility/api.go b/emobility/api.go index 7278a67..cfe097f 100644 --- a/emobility/api.go +++ b/emobility/api.go @@ -1,5 +1,7 @@ package emobility +import "github.com/enbility/spine-go/api" + //go:generate mockgen -source emobility.go -destination mock_emobility_test.go -package emobility // used by emobility and implemented by the CEM @@ -43,48 +45,48 @@ type EmobilityDataProvider interface { // used by the CEM and implemented by emobility type EMobilityInterface interface { // return if an EV is connected - EVConnected() bool + EVConnected(remoteEntity api.EntityRemoteInterface) bool // return the current charge state of the EV - EVCurrentChargeState() (EVChargeStateType, error) + EVCurrentChargeState(remoteEntity api.EntityRemoteInterface) (EVChargeStateType, error) // return the number of ac connected phases of the EV or 0 if it is unknown - EVConnectedPhases() (uint, error) + EVConnectedPhases(remoteEntity api.EntityRemoteInterface) (uint, error) // return the charged energy measurement in Wh of the connected EV // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVChargedEnergy() (float64, error) + EVChargedEnergy(remoteEntity api.EntityRemoteInterface) (float64, error) // return the last power measurement for each phase of the connected EV // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVPowerPerPhase() ([]float64, error) + EVPowerPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) // return the last current measurement for each phase of the connected EV // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVCurrentsPerPhase() ([]float64, error) + EVCurrentsPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) // return the min, max, default limits for each phase of the connected EV // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVCurrentLimits() ([]float64, []float64, []float64, error) + EVCurrentLimits(remoteEntity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) // return the current loadcontrol obligation limits // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVLoadControlObligationLimits() ([]float64, error) + EVLoadControlObligationLimits(remoteEntity api.EntityRemoteInterface) ([]float64, error) // send new LoadControlLimits to the remote EV // @@ -113,7 +115,7 @@ type EMobilityInterface interface { // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific // and needs to have specific EVSE support for the specific EV brand. // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. - EVWriteLoadControlLimits(limits []EVLoadLimits) error + EVWriteLoadControlLimits(remoteEntity api.EntityRemoteInterface, limits []EVLoadLimits) error // return the current communication standard type used to communicate between EVSE and EV // @@ -130,21 +132,21 @@ type EMobilityInterface interface { // - ErrDataNotAvailable if that information is not (yet) available // - ErrNotSupported if getting the communication standard is not supported // - and others - EVCommunicationStandard() (EVCommunicationStandardType, error) + EVCommunicationStandard(remoteEntity api.EntityRemoteInterface) (EVCommunicationStandardType, error) // returns the identification of the currently connected EV or nil if not available // // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others - EVIdentification() (string, error) + EVIdentification(remoteEntity api.EntityRemoteInterface) (string, error) // returns if the EVSE and EV combination support optimzation of self consumption // // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others - EVOptimizationOfSelfConsumptionSupported() (bool, error) + EVOptimizationOfSelfConsumptionSupported(remoteEntity api.EntityRemoteInterface) (bool, error) // return if the EVSE and EV combination support providing an SoC // @@ -155,7 +157,7 @@ type EMobilityInterface interface { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVSoCSupported() (bool, error) + EVSoCSupported(remoteEntity api.EntityRemoteInterface) (bool, error) // return the last known SoC of the connected EV // @@ -167,38 +169,38 @@ type EMobilityInterface interface { // - ErrNotSupported if support for SoC is not possible // - ErrDataNotAvailable if no such measurement is (yet) available // - and others - EVSoC() (float64, error) + EVSoC(remoteEntity api.EntityRemoteInterface) (float64, error) // returns if the EVSE and EV combination support coordinated charging // // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others - EVCoordinatedChargingSupported() (bool, error) + EVCoordinatedChargingSupported(remoteEntity api.EntityRemoteInterface) (bool, error) // returns the current charging stratey // // returns EVChargeStrategyTypeUnknown if it could not be determined, e.g. // if the vehicle communication is via IEC61851 or the EV doesn't provide // any information about its charging mode or plan - EVChargeStrategy() EVChargeStrategyType + EVChargeStrategy(remoteEntity api.EntityRemoteInterface) EVChargeStrategyType // returns the current energy demand // - EVDemand: details about the actual demands from the EV // - error: if no data is available // // if duration is 0, direct charging is active, otherwise timed charging is active - EVEnergyDemand() (EVDemand, error) + EVEnergyDemand(remoteEntity api.EntityRemoteInterface) (EVDemand, error) // returns the current charge plan // - EVChargePlan: details about the actual charge plan provided by the EV // - error: if no data is available - EVChargePlan() (EVChargePlan, error) + EVChargePlan(remoteEntity api.EntityRemoteInterface) (EVChargePlan, error) // returns the constraints for the time slots // - EVTimeSlotConstraints: details about the time slot constraints // - error: if no data is available - EVTimeSlotConstraints() (EVTimeSlotConstraints, error) + EVTimeSlotConstraints(remoteEntity api.EntityRemoteInterface) (EVTimeSlotConstraints, error) // send power limits data to the EV // @@ -206,12 +208,12 @@ type EMobilityInterface interface { // // this needs to be invoked either <55s, idealy <15s, of receiving a call to EVRequestPowerLimits // or if the CEM requires the EV to change its charge plan - EVWritePowerLimits(data []EVDurationSlotValue) error + EVWritePowerLimits(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error // returns the constraints for incentive slots // - EVIncentiveConstraints: details about the incentive slot constraints // - error: if no data is available - EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) + EVIncentiveConstraints(remoteEntity api.EntityRemoteInterface) (EVIncentiveSlotConstraints, error) // send price slots data to the EV // @@ -219,5 +221,5 @@ type EMobilityInterface interface { // // this needs to be invoked either within 20s of receiving a call to EVRequestIncentives // or if the CEM requires the EV to change its charge plan - EVWriteIncentives(data []EVDurationSlotValue) error + EVWriteIncentives(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error } diff --git a/emobility/emobility.go b/emobility/emobility.go index a280074..81070be 100644 --- a/emobility/emobility.go +++ b/emobility/emobility.go @@ -2,7 +2,6 @@ package emobility import ( "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/util" spineapi "github.com/enbility/spine-go/api" @@ -18,19 +17,6 @@ type EMobility struct { evseEntity spineapi.EntityRemoteInterface evEntity spineapi.EntityRemoteInterface - evseDeviceClassification *features.DeviceClassification - evseDeviceDiagnosis *features.DeviceDiagnosis - - evDeviceClassification *features.DeviceClassification - evDeviceDiagnosis *features.DeviceDiagnosis - evDeviceConfiguration *features.DeviceConfiguration - evElectricalConnection *features.ElectricalConnection - evMeasurement *features.Measurement - evIdentification *features.Identification - evLoadControl *features.LoadControl - evTimeSeries *features.TimeSeries - evIncentiveTable *features.IncentiveTable - evCurrentChargeStrategy EVChargeStrategyType ski string diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index 030bcb5..94e1748 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -6,15 +6,22 @@ import ( "github.com/enbility/eebus-go/util" "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" gomock "go.uber.org/mock/gomock" ) func Test_CoordinatedChargingScenarios(t *testing.T) { emobility, eebusService := setupEmobility(t) - data, err := emobility.EVChargedEnergy() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobility.EVChargedEnergy(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -27,9 +34,6 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { dataProviderMock := NewMockEmobilityDataProvider(ctrl) emobility.dataProvider = dataProviderMock - emobility.evTimeSeries = timeSeriesConfiguration(localEntity, emobility.evEntity) - emobility.evIncentiveTable = incentiveTableConfiguration(localEntity, emobility.evEntity) - datagramtt := datagramForEntityAndFeatures(false, localDevice, localEntity, emobility.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) datagramit := datagramForEntityAndFeatures(false, localDevice, localEntity, emobility.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) @@ -63,7 +67,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { err = localDevice.ProcessCmd(datagramtt, remoteDevice) assert.Nil(t, err) - demand, err := emobility.EVEnergyDemand() + demand, err := emobility.EVEnergyDemand(emobility.evEntity) assert.Nil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -141,7 +145,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { err = localDevice.ProcessCmd(datagramtt, remoteDevice) assert.Nil(t, err) - demand, err = emobility.EVEnergyDemand() + demand, err = emobility.EVEnergyDemand(emobility.evEntity) assert.Nil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 53400.0, demand.OptDemand) @@ -235,7 +239,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { err = localDevice.ProcessCmd(datagramtt, remoteDevice) assert.Nil(t, err) - demand, err = emobility.EVEnergyDemand() + demand, err = emobility.EVEnergyDemand(emobility.evEntity) assert.Nil(t, err) assert.Equal(t, 600.0, demand.MinDemand) assert.Equal(t, 600.0, demand.OptDemand) diff --git a/emobility/events.go b/emobility/events.go index eaeab73..0417d1b 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -64,60 +64,68 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { if payload.ChangeType == api.ElementChangeUpdate { switch payload.Data.(type) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: - if e.evDeviceConfiguration == nil { + evDeviceConfiguration, err := e.deviceConfiguration(payload.Entity) + if err != nil { break } // key value descriptions received, now get the data - if _, err := e.evDeviceConfiguration.RequestKeyValues(); err != nil { + if _, err := evDeviceConfiguration.RequestKeyValues(); err != nil { logging.Log().Error("Error getting configuration key values:", err) } case *model.ElectricalConnectionParameterDescriptionListDataType: - if e.evElectricalConnection == nil { + evElectricalConnection, err := e.electricalConnection(payload.Entity) + if err != nil { break } - if _, err := e.evElectricalConnection.RequestPermittedValueSets(); err != nil { + + if _, err := evElectricalConnection.RequestPermittedValueSets(); err != nil { logging.Log().Error("Error getting electrical permitted values:", err) } case *model.LoadControlLimitDescriptionListDataType: - if e.evLoadControl == nil { + evLoadControl, err := e.loadControl(payload.Entity) + if err != nil { break } - if _, err := e.evLoadControl.RequestLimitValues(); err != nil { + + if _, err := evLoadControl.RequestLimitValues(); err != nil { logging.Log().Error("Error getting loadcontrol limit values:", err) } case *model.MeasurementDescriptionListDataType: - if e.evMeasurement == nil { + evMeasurement, err := e.measurement(payload.Entity) + if err != nil { break } - if _, err := e.evMeasurement.RequestValues(); err != nil { + + if _, err := evMeasurement.RequestValues(); err != nil { logging.Log().Error("Error getting measurement list values:", err) } case *model.TimeSeriesDescriptionListDataType: - if e.evTimeSeries == nil || payload.CmdClassifier == nil { + evTimeSeries, err := e.timeSeries(payload.Entity) + if err != nil || payload.CmdClassifier == nil { break } switch *payload.CmdClassifier { case model.CmdClassifierTypeReply: - if err := e.evTimeSeries.RequestConstraints(); err == nil { + if err := evTimeSeries.RequestConstraints(); err == nil { break } // if constraints do not exist, directly request values - e.evRequestTimeSeriesValues() + e.evRequestTimeSeriesValues(payload.Entity) case model.CmdClassifierTypeNotify: // check if we are required to update the plan - if !e.evCheckTimeSeriesDescriptionConstraintsUpdateRequired() { + if !e.evCheckTimeSeriesDescriptionConstraintsUpdateRequired(payload.Entity) { break } - demand, err := e.EVEnergyDemand() + demand, err := e.EVEnergyDemand(payload.Entity) if err != nil { logging.Log().Error("Error getting energy demand:", err) break @@ -127,13 +135,13 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { e.dataProvider.EVProvidedEnergyDemand(demand) } - timeConstraints, err := e.EVTimeSlotConstraints() + timeConstraints, err := e.EVTimeSlotConstraints(payload.Entity) if err != nil { logging.Log().Error("Error getting timeseries constraints:", err) break } - incentiveConstraints, err := e.EVIncentiveConstraints() + incentiveConstraints, err := e.EVIncentiveConstraints(payload.Entity) if err != nil { logging.Log().Error("Error getting incentive constraints:", err) break @@ -145,12 +153,12 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { break } - e.evWriteDefaultIncentives() - e.evWriteDefaultPowerLimits() + e.evWriteDefaultIncentives(payload.Entity) + e.evWriteDefaultPowerLimits(payload.Entity) } case *model.TimeSeriesConstraintsListDataType: - if e.evTimeSeries == nil || payload.CmdClassifier == nil { + if _, err := e.timeSeries(payload.Entity); err != nil || payload.CmdClassifier == nil { break } @@ -158,42 +166,43 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { break } - e.evRequestTimeSeriesValues() + e.evRequestTimeSeriesValues(payload.Entity) case *model.TimeSeriesListDataType: - if e.evTimeSeries == nil || payload.CmdClassifier == nil { + if _, err := e.timeSeries(payload.Entity); err != nil || payload.CmdClassifier == nil { break } // check if we received a plan - e.evForwardChargePlanIfProvided() + e.evForwardChargePlanIfProvided(payload.Entity) case *model.IncentiveTableDescriptionDataType: - if e.evIncentiveTable == nil || payload.CmdClassifier == nil { + evIncentiveTable, err := e.incentiveTable(payload.Entity) + if err != nil || payload.CmdClassifier == nil { break } switch *payload.CmdClassifier { case model.CmdClassifierTypeReply: - if err := e.evIncentiveTable.RequestConstraints(); err == nil { + if err := evIncentiveTable.RequestConstraints(); err == nil { break } // if constraints do not exist, directly request values - e.evRequestIncentiveValues() + e.evRequestIncentiveValues(payload.Entity) case model.CmdClassifierTypeNotify: // check if we are required to update the plan - if !e.evCheckIncentiveTableDescriptionUpdateRequired() { + if !e.evCheckIncentiveTableDescriptionUpdateRequired(payload.Entity) { break } - e.evWriteIncentiveTableDescriptions() + e.evWriteIncentiveTableDescriptions(payload.Entity) } case *model.IncentiveTableConstraintsDataType: if *payload.CmdClassifier == model.CmdClassifierTypeReply { - e.evRequestIncentiveValues() + e.evRequestIncentiveValues(payload.Entity) } } } @@ -204,7 +213,7 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { } // check if the charge strategy changed - chargeStrategy := e.EVChargeStrategy() + chargeStrategy := e.EVChargeStrategy(payload.Entity) if chargeStrategy == e.evCurrentChargeStrategy { return } @@ -214,28 +223,42 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { e.dataProvider.EVProvidedChargeStrategy(chargeStrategy) } -func (e *EMobility) evWriteDefaultIncentives() { +func (e *EMobility) localCemEntity() api.EntityLocalInterface { + localDevice := e.service.LocalDevice() + + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + return localEntity +} + +func (e *EMobility) evWriteDefaultIncentives(remoteEntity api.EntityRemoteInterface) { // send default incentives for the maximum timeframe // to fullfill spec, as there is no data provided logging.Log().Info("Fallback sending default incentives") data := []EVDurationSlotValue{ {Duration: 7 * time.Hour * 24, Value: 0.30}, } - _ = e.EVWriteIncentives(data) + _ = e.EVWriteIncentives(remoteEntity, data) } -func (e *EMobility) evWriteDefaultPowerLimits() { +func (e *EMobility) evWriteDefaultPowerLimits(remoteEntity api.EntityRemoteInterface) { // send default power limits for the maximum timeframe // to fullfill spec, as there is no data provided logging.Log().Info("Fallback sending default power limits") - paramDesc, err := e.evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) + evElectricalConnection, err := e.electricalConnection(remoteEntity) + if err != nil { + logging.Log().Error("electrical connection feature not found") + return + } + + paramDesc, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) if err != nil { logging.Log().Error("Error getting parameter descriptions:", err) return } - permitted, err := e.evElectricalConnection.GetPermittedValueSetForParameterId(*paramDesc.ParameterId) + permitted, err := evElectricalConnection.GetPermittedValueSetForParameterId(*paramDesc.ParameterId) if err != nil { logging.Log().Error("Error getting permitted values:", err) return @@ -249,42 +272,48 @@ func (e *EMobility) evWriteDefaultPowerLimits() { data := []EVDurationSlotValue{ {Duration: 7 * time.Hour * 24, Value: permitted.PermittedValueSet[0].Range[0].Max.GetValue()}, } - _ = e.EVWritePowerLimits(data) + _ = e.EVWritePowerLimits(remoteEntity, data) } // request time series values -func (e *EMobility) evRequestTimeSeriesValues() { - if e.evTimeSeries == nil { +func (e *EMobility) evRequestTimeSeriesValues(remoteEntity api.EntityRemoteInterface) { + localEntity := e.localCemEntity() + + evTimeSeries, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + if err != nil { return } - if _, err := e.evTimeSeries.RequestValues(); err != nil { + if _, err := evTimeSeries.RequestValues(); err != nil { logging.Log().Error("Error getting time series list values:", err) } } // send the ev provided charge plan to the CEM -func (e *EMobility) evForwardChargePlanIfProvided() { +func (e *EMobility) evForwardChargePlanIfProvided(remoteEntity api.EntityRemoteInterface) { if e.dataProvider == nil { return } - if plan, err := e.EVChargePlan(); err == nil { + if plan, err := e.EVChargePlan(remoteEntity); err == nil { e.dataProvider.EVProvidedChargePlan(plan) } - if constraints, err := e.EVChargePlanConstraints(); err == nil { + if constraints, err := e.EVChargePlanConstraints(remoteEntity); err == nil { e.dataProvider.EVProvidedChargePlanConstraints(constraints) } } // request incentive table values -func (e *EMobility) evRequestIncentiveValues() { - if e.evIncentiveTable == nil { +func (e *EMobility) evRequestIncentiveValues(remoteEntity api.EntityRemoteInterface) { + localEntity := e.localCemEntity() + + evIncentiveTable, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + if err != nil { return } - if _, err := e.evIncentiveTable.RequestValues(); err != nil { + if _, err := evIncentiveTable.RequestValues(); err != nil { logging.Log().Error("Error getting time series list values:", err) } } @@ -295,29 +324,19 @@ func (e *EMobility) evseConnected(ski string, entity api.EntityRemoteInterface) localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - f1, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return + if evseDeviceClassification, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { + _, _ = evseDeviceClassification.RequestManufacturerDetails() } - e.evseDeviceClassification = f1 - f2, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return + if evseDeviceDiagnosis, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { + _, _ = evseDeviceDiagnosis.RequestState() } - e.evseDeviceDiagnosis = f2 - - _, _ = e.evseDeviceClassification.RequestManufacturerDetails() - _, _ = e.evseDeviceDiagnosis.RequestState() } // an EV was disconnected func (e *EMobility) evseDisconnected() { e.evseEntity = nil - e.evseDeviceClassification = nil - e.evseDeviceDiagnosis = nil - e.evDisconnected() } @@ -329,16 +348,6 @@ func (e *EMobility) evDisconnected() { e.evEntity = nil - e.evDeviceClassification = nil - e.evDeviceDiagnosis = nil - e.evDeviceConfiguration = nil - e.evElectricalConnection = nil - e.evMeasurement = nil - e.evIdentification = nil - e.evLoadControl = nil - e.evTimeSeries = nil - e.evIncentiveTable = nil - logging.Log().Debug("ev disconnected") // TODO: add error handling @@ -347,179 +356,144 @@ func (e *EMobility) evDisconnected() { // an EV was connected, trigger required communication func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { e.evEntity = entity - localDevice := e.service.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) logging.Log().Debug("ev connected") - // setup features - e.evDeviceClassification, _ = features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evDeviceDiagnosis, _ = features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evDeviceConfiguration, _ = features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evElectricalConnection, _ = features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evMeasurement, _ = features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evIdentification, _ = features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evLoadControl, _ = features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if e.configuration.CoordinatedChargingEnabled { - e.evTimeSeries, _ = features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - e.evIncentiveTable, _ = features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - } - - // optional requests are only logged as debug - - // subscribe - if e.evDeviceClassification != nil { - if err := e.evDeviceClassification.SubscribeForEntity(); err != nil { + // initialise features, e.g. subscriptions, bindings + if evDeviceClassification, err := e.deviceClassification(entity); err == nil { + if err := evDeviceClassification.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - if e.evDeviceConfiguration != nil { - if err := e.evDeviceConfiguration.SubscribeForEntity(); err != nil { + + // get manufacturer details + if _, err := evDeviceClassification.RequestManufacturerDetails(); err != nil { logging.Log().Debug(err) } } - if e.evDeviceDiagnosis != nil { - if err := e.evDeviceDiagnosis.SubscribeForEntity(); err != nil { + + if evDeviceConfiguration, err := e.deviceConfiguration(entity); err == nil { + if err := evDeviceConfiguration.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - if e.evElectricalConnection != nil { - if err := e.evElectricalConnection.SubscribeForEntity(); err != nil { + // get ev configuration data + if err := evDeviceConfiguration.RequestDescriptions(); err != nil { logging.Log().Debug(err) } } - if e.evMeasurement != nil { - if err := e.evMeasurement.SubscribeForEntity(); err != nil { + + if evDeviceDiagnosis, err := e.deviceDiagnosis(entity); err == nil { + if err := evDeviceDiagnosis.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - if e.evLoadControl != nil { - if err := e.evLoadControl.SubscribeForEntity(); err != nil { + + // get device diagnosis state + if _, err := evDeviceDiagnosis.RequestState(); err != nil { logging.Log().Debug(err) } } - if e.evIdentification != nil { - if err := e.evIdentification.SubscribeForEntity(); err != nil { + + if evElectricalConnection, err := e.electricalConnection(entity); err == nil { + if err := evElectricalConnection.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - if e.configuration.CoordinatedChargingEnabled { - if e.evTimeSeries != nil { - if err := e.evTimeSeries.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) - } - } - // this is optional - if e.evIncentiveTable != nil { - if err := e.evIncentiveTable.SubscribeForEntity(); err != nil { - logging.Log().Debug(err) - } + // get electrical connection parameter + if err := evElectricalConnection.RequestDescriptions(); err != nil { + logging.Log().Debug(err) } - } - // bindings - if err := e.evLoadControl.Bind(); err != nil { - logging.Log().Debug(err) - } - - if e.configuration.CoordinatedChargingEnabled { - if e.evTimeSeries != nil { - // this is optional - if err := e.evTimeSeries.Bind(); err != nil { - logging.Log().Debug(err) - } + if err := evElectricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Debug(err) } - if e.evIncentiveTable != nil { - // this is optional - if err := e.evIncentiveTable.Bind(); err != nil { - logging.Log().Debug(err) - } - } } - // get ev configuration data - if e.evDeviceConfiguration != nil { - if err := e.evDeviceConfiguration.RequestDescriptions(); err != nil { + if evMeasurement, err := e.measurement(entity); err == nil { + if err := evMeasurement.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - // get manufacturer details - if e.evDeviceClassification != nil { - if _, err := e.evDeviceClassification.RequestManufacturerDetails(); err != nil { + // get measurement parameters + if err := evMeasurement.RequestDescriptions(); err != nil { logging.Log().Debug(err) } - } - // get device diagnosis state - if e.evDeviceDiagnosis != nil { - if _, err := e.evDeviceDiagnosis.RequestState(); err != nil { - logging.Log().Debug(err) - } } - // get electrical connection parameter - if e.evElectricalConnection != nil { - if err := e.evElectricalConnection.RequestDescriptions(); err != nil { + if evLoadControl, err := e.loadControl(entity); err == nil { + if err := evLoadControl.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - if e.evElectricalConnection != nil { - if err := e.evElectricalConnection.RequestParameterDescriptions(); err != nil { + if err := evLoadControl.Bind(); err != nil { logging.Log().Debug(err) } - } - // get measurement parameters - if e.evMeasurement != nil { - if err := e.evMeasurement.RequestDescriptions(); err != nil { + // get loadlimit parameter + if err := evLoadControl.RequestLimitDescriptions(); err != nil { logging.Log().Debug(err) } + } - // get loadlimit parameter - if e.evLoadControl != nil { - if err := e.evLoadControl.RequestLimitDescriptions(); err != nil { + if evIdentification, err := e.identification(entity); err == nil { + if err := evIdentification.SubscribeForEntity(); err != nil { logging.Log().Debug(err) } - } - // get identification - if e.evIdentification != nil { - if _, err := e.evIdentification.RequestValues(); err != nil { + // get identification + if _, err := evIdentification.RequestValues(); err != nil { logging.Log().Debug(err) } } if e.configuration.CoordinatedChargingEnabled { - if e.evTimeSeries != nil { + if evTimeSeries, err := e.timeSeries(entity); err == nil { + if err := evTimeSeries.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } + + if err := evTimeSeries.Bind(); err != nil { + logging.Log().Debug(err) + } + // get time series parameter - if err := e.evTimeSeries.RequestDescriptions(); err != nil { + if err := evTimeSeries.RequestDescriptions(); err != nil { logging.Log().Debug(err) } + } - if e.evIncentiveTable != nil { + if evIncentiveTable, err := e.incentiveTable(entity); err == nil { + if err := evIncentiveTable.SubscribeForEntity(); err != nil { + logging.Log().Debug(err) + } + + if err := evIncentiveTable.Bind(); err != nil { + logging.Log().Debug(err) + } + // get incentive table parameter - if err := e.evIncentiveTable.RequestDescriptions(); err != nil { + if err := evIncentiveTable.RequestDescriptions(); err != nil { logging.Log().Debug(err) } + } + } } // inform the EVSE about used currency and boundary units // // # SPINE UC CoordinatedEVCharging 2.4.3 -func (e *EMobility) evWriteIncentiveTableDescriptions() { - if e.evIncentiveTable == nil { +func (e *EMobility) evWriteIncentiveTableDescriptions(remoteEntity api.EntityRemoteInterface) { + evIncentiveTable, err := e.incentiveTable(remoteEntity) + if err != nil { + logging.Log().Error("incentivetable feature not found") return } - descriptions, err := e.evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) + descriptions, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) if err != nil { logging.Log().Error(err) return @@ -569,7 +543,7 @@ func (e *EMobility) evWriteIncentiveTableDescriptions() { }, } - _, err = e.evIncentiveTable.WriteDescriptions(data) + _, err = evIncentiveTable.WriteDescriptions(data) if err != nil { logging.Log().Error(err) } @@ -577,12 +551,14 @@ func (e *EMobility) evWriteIncentiveTableDescriptions() { // check timeSeries descriptions if constraints element has updateRequired set to true // as this triggers the CEM to send power tables within 20s -func (e *EMobility) evCheckTimeSeriesDescriptionConstraintsUpdateRequired() bool { - if e.evTimeSeries == nil { +func (e *EMobility) evCheckTimeSeriesDescriptionConstraintsUpdateRequired(remoteEntity api.EntityRemoteInterface) bool { + evTimeSeries, err := e.timeSeries(remoteEntity) + if err != nil { + logging.Log().Error("timeseries feature not found") return false } - data, err := e.evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) + data, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) if err != nil { return false } @@ -596,12 +572,14 @@ func (e *EMobility) evCheckTimeSeriesDescriptionConstraintsUpdateRequired() bool // check incentibeTable descriptions if the tariff description has updateRequired set to true // as this triggers the CEM to send incentive tables within 20s -func (e *EMobility) evCheckIncentiveTableDescriptionUpdateRequired() bool { - if e.evIncentiveTable == nil { +func (e *EMobility) evCheckIncentiveTableDescriptionUpdateRequired(remoteEntity api.EntityRemoteInterface) bool { + evIncentiveTable, err := e.incentiveTable(remoteEntity) + if err != nil { + logging.Log().Error("incentivetable feature not found") return false } - data, err := e.evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) + data, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) if err != nil { return false } diff --git a/emobility/features.go b/emobility/features.go new file mode 100644 index 0000000..3ee4535 --- /dev/null +++ b/emobility/features.go @@ -0,0 +1,61 @@ +package emobility + +import ( + "github.com/enbility/eebus-go/features" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +func (e *EMobility) deviceClassification(remoteEntity api.EntityRemoteInterface) (*features.DeviceClassification, error) { + localEntity := e.localCemEntity() + + return features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) deviceConfiguration(remoteEntity api.EntityRemoteInterface) (*features.DeviceConfiguration, error) { + localEntity := e.localCemEntity() + + return features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) deviceDiagnosis(remoteEntity api.EntityRemoteInterface) (*features.DeviceDiagnosis, error) { + localEntity := e.localCemEntity() + + return features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) electricalConnection(remoteEntity api.EntityRemoteInterface) (*features.ElectricalConnection, error) { + localEntity := e.localCemEntity() + + return features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) measurement(remoteEntity api.EntityRemoteInterface) (*features.Measurement, error) { + localEntity := e.localCemEntity() + + return features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) loadControl(remoteEntity api.EntityRemoteInterface) (*features.LoadControl, error) { + localEntity := e.localCemEntity() + + return features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) identification(remoteEntity api.EntityRemoteInterface) (*features.Identification, error) { + localEntity := e.localCemEntity() + + return features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) timeSeries(remoteEntity api.EntityRemoteInterface) (*features.TimeSeries, error) { + localEntity := e.localCemEntity() + + return features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func (e *EMobility) incentiveTable(remoteEntity api.EntityRemoteInterface) (*features.IncentiveTable, error) { + localEntity := e.localCemEntity() + + return features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} diff --git a/emobility/helper_test.go b/emobility/helper_test.go index e87407b..58e3cba 100644 --- a/emobility/helper_test.go +++ b/emobility/helper_test.go @@ -8,7 +8,6 @@ import ( "time" "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" "github.com/enbility/eebus-go/mocks" "github.com/enbility/eebus-go/service" shipapi "github.com/enbility/ship-go/api" @@ -347,83 +346,3 @@ func featureOfTypeAndRole( } return nil } - -func deviceDiagnosis( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.DeviceDiagnosis { - feature, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func electricalConnection( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.ElectricalConnection { - feature, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func measurement( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.Measurement { - feature, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func deviceConfiguration( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.DeviceConfiguration { - feature, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func identificationConfiguration( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.Identification { - feature, err := features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func loadcontrol( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.LoadControl { - feature, err := features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func timeSeriesConfiguration( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.TimeSeries { - feature, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} - -func incentiveTableConfiguration( - localEntity spineapi.EntityLocalInterface, - entity spineapi.EntityRemoteInterface) *features.IncentiveTable { - feature, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - fmt.Println(err) - } - return feature -} diff --git a/emobility/public.go b/emobility/public.go index 6300116..3f55b5d 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -7,6 +7,7 @@ import ( "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" eebusUtil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -14,36 +15,31 @@ import ( // // this includes all required features and // minimal data being available -func (e *EMobility) EVConnected() bool { +func (e *EMobility) EVConnected(remoteEntity api.EntityRemoteInterface) bool { // To report an EV as being connected, also consider all required // features to be available and assigned - if e.evEntity == nil || - e.evDeviceDiagnosis == nil || - e.evElectricalConnection == nil || - e.evMeasurement == nil || - e.evLoadControl == nil || - e.evDeviceConfiguration == nil { + if e.evEntity == nil { return false } // getting current charge state should work - if _, err := e.EVCurrentChargeState(); err != nil { + if _, err := e.EVCurrentChargeState(remoteEntity); err != nil { return false } // the communication standard needs to be available - if _, err := e.EVCommunicationStandard(); err != nil { + if _, err := e.EVCommunicationStandard(remoteEntity); err != nil { return false } // getting currents measurements should work - if _, err := e.EVCurrentsPerPhase(); err != nil && err != features.ErrDataNotAvailable { + if _, err := e.EVCurrentsPerPhase(remoteEntity); err != nil && err != features.ErrDataNotAvailable { // features.ErrDataNotAvailable check in case of measurements not being provided but the feature works return false } // getting limits should work - if _, err := e.EVLoadControlObligationLimits(); err != nil && err != features.ErrDataNotAvailable { + if _, err := e.EVLoadControlObligationLimits(remoteEntity); err != nil && err != features.ErrDataNotAvailable { // features.ErrDataNotAvailable check in case of load control limits not being provided but the feature works return false } @@ -52,12 +48,14 @@ func (e *EMobility) EVConnected() bool { } // return the current charge state of the EV -func (e *EMobility) EVCurrentChargeState() (EVChargeStateType, error) { - if e.evEntity == nil || e.evDeviceDiagnosis == nil { +func (e *EMobility) EVCurrentChargeState(remoteEntity api.EntityRemoteInterface) (EVChargeStateType, error) { + evDeviceDiagnosis, err := e.deviceDiagnosis(remoteEntity) + + if e.evEntity == nil || err != nil { return EVChargeStateTypeUnplugged, nil } - diagnosisState, err := e.evDeviceDiagnosis.GetState() + diagnosisState, err := evDeviceDiagnosis.GetState() if err != nil { return EVChargeStateTypeUnknown, err } @@ -82,12 +80,14 @@ func (e *EMobility) EVCurrentChargeState() (EVChargeStateType, error) { } // return the number of ac connected phases of the EV or 0 if it is unknown -func (e *EMobility) EVConnectedPhases() (uint, error) { - if e.evEntity == nil || e.evElectricalConnection == nil { +func (e *EMobility) EVConnectedPhases(remoteEntity api.EntityRemoteInterface) (uint, error) { + evElectricalConnection, err := e.electricalConnection(remoteEntity) + + if e.evEntity == nil || err != nil { return 0, ErrEVDisconnected } - data, err := e.evElectricalConnection.GetDescriptions() + data, err := evElectricalConnection.GetDescriptions() if err != nil { return 0, features.ErrDataNotAvailable } @@ -111,15 +111,17 @@ func (e *EMobility) EVConnectedPhases() (uint, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVChargedEnergy() (float64, error) { - if e.evEntity == nil || e.evMeasurement == nil { +func (e *EMobility) EVChargedEnergy(remoteEntity api.EntityRemoteInterface) (float64, error) { + evMeasurement, err := e.measurement(remoteEntity) + + if e.evEntity == nil || err != nil { return 0, ErrEVDisconnected } measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeCharge - data, err := e.evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + data, err := evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) if err != nil { return 0, err } @@ -138,8 +140,11 @@ func (e *EMobility) EVChargedEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVPowerPerPhase() ([]float64, error) { - if e.evEntity == nil || e.evMeasurement == nil { +func (e *EMobility) EVPowerPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) { + evMeasurement, err := e.measurement(remoteEntity) + evElectricalConnection, err2 := e.electricalConnection(remoteEntity) + + if e.evEntity == nil || err != nil || err2 != nil { return nil, ErrEVDisconnected } @@ -149,14 +154,14 @@ func (e *EMobility) EVPowerPerPhase() ([]float64, error) { measurement := model.MeasurementTypeTypePower commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACPower - data, err := e.evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + data, err = evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) if err != nil || len(data) == 0 { powerAvailable = false // If power is not provided, fall back to power calculations via currents measurement = model.MeasurementTypeTypeCurrent scope = model.ScopeTypeTypeACCurrent - data, err = e.evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + data, err = evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) if err != nil { return nil, err } @@ -170,7 +175,7 @@ func (e *EMobility) EVPowerPerPhase() ([]float64, error) { continue } - elParam, err := e.evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + elParam, err := evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) if err != nil || elParam.AcMeasuredPhases == nil || *elParam.AcMeasuredPhases != phase { continue } @@ -192,15 +197,18 @@ func (e *EMobility) EVPowerPerPhase() ([]float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVCurrentsPerPhase() ([]float64, error) { - if e.evEntity == nil || e.evElectricalConnection == nil { +func (e *EMobility) EVCurrentsPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) { + evMeasurement, err := e.measurement(remoteEntity) + evElectricalConnection, err2 := e.electricalConnection(remoteEntity) + + if e.evEntity == nil || err != nil || err2 != nil { return nil, ErrEVDisconnected } measurement := model.MeasurementTypeTypeCurrent commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACCurrent - data, err := e.evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + data, err := evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) if err != nil { return nil, err } @@ -215,7 +223,7 @@ func (e *EMobility) EVCurrentsPerPhase() ([]float64, error) { continue } - elParam, err := e.evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + elParam, err := evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) if err != nil || elParam.AcMeasuredPhases == nil || *elParam.AcMeasuredPhases != phase { continue } @@ -234,7 +242,7 @@ func (e *EMobility) EVCurrentsPerPhase() ([]float64, error) { // if there was no timestamp provided or the time for the last value // is older than 1 minute, send a read request if refetch { - _, _ = e.evMeasurement.RequestValues() + _, _ = evMeasurement.RequestValues() } return result, nil @@ -245,8 +253,10 @@ func (e *EMobility) EVCurrentsPerPhase() ([]float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVCurrentLimits() ([]float64, []float64, []float64, error) { - if e.evEntity == nil || e.evElectricalConnection == nil { +func (e *EMobility) EVCurrentLimits(remoteEntity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + evElectricalConnection, err := e.electricalConnection(remoteEntity) + + if e.evEntity == nil || err != nil { return nil, nil, nil, ErrEVDisconnected } @@ -254,12 +264,12 @@ func (e *EMobility) EVCurrentLimits() ([]float64, []float64, []float64, error) { for _, phaseName := range util.PhaseNameMapping { // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := e.evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) if err != nil || elParamDesc.ParameterId == nil { continue } - dataMin, dataMax, dataDefault, err := e.evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) + dataMin, dataMax, dataDefault, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) if err != nil { continue } @@ -284,14 +294,17 @@ func (e *EMobility) EVCurrentLimits() ([]float64, []float64, []float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVLoadControlObligationLimits() ([]float64, error) { - if e.evEntity == nil || e.evElectricalConnection == nil || e.evLoadControl == nil { +func (e *EMobility) EVLoadControlObligationLimits(remoteEntity api.EntityRemoteInterface) ([]float64, error) { + evLoadControl, err := e.loadControl(remoteEntity) + evElectricalConnection, err2 := e.electricalConnection(remoteEntity) + + if e.evEntity == nil || err != nil || err2 != nil { return nil, ErrEVDisconnected } // find out the appropriate limitId for each phase value // limitDescription contains the measurementId for each limitId - limitDescriptions, err := e.evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) + limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) if err != nil { return nil, features.ErrDataNotAvailable } @@ -302,7 +315,7 @@ func (e *EMobility) EVLoadControlObligationLimits() ([]float64, error) { phaseName := util.PhaseNameMapping[i] // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := e.evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) if err != nil || elParamDesc.MeasurementId == nil { // there is no data for this phase, the phase may not exit result = append(result, 0) @@ -324,7 +337,7 @@ func (e *EMobility) EVLoadControlObligationLimits() ([]float64, error) { return nil, features.ErrDataNotAvailable } - limitIdData, err := e.evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) + limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) if err != nil { return nil, features.ErrDataNotAvailable } @@ -332,7 +345,7 @@ func (e *EMobility) EVLoadControlObligationLimits() ([]float64, error) { var limitValue float64 if limitIdData.Value == nil || (limitIdData.IsLimitActive != nil && !*limitIdData.IsLimitActive) { // report maximum possible if no limit is available or the limit is not active - _, dataMax, _, err := e.evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) + _, dataMax, _, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) if err != nil { return nil, features.ErrDataNotAvailable } @@ -373,12 +386,15 @@ func (e *EMobility) EVLoadControlObligationLimits() ([]float64, error) { // - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. // - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. // - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. -func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { +func (e *EMobility) EVWriteLoadControlLimits(remoteEntity api.EntityRemoteInterface, limits []EVLoadLimits) error { if e.evEntity == nil { return ErrEVDisconnected } - if e.evElectricalConnection == nil || e.evLoadControl == nil { + evLoadControl, err := e.loadControl(remoteEntity) + evElectricalConnection, err2 := e.electricalConnection(remoteEntity) + + if err != nil || err2 != nil { return features.ErrDataNotAvailable } @@ -390,13 +406,13 @@ func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { for _, phaseLimit := range scope.PhaseData { // find out the appropriate limitId for each phase value // limitDescription contains the measurementId for each limitId - limitDescriptions, err := e.evLoadControl.GetLimitDescriptionsForCategory(category) + limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) if err != nil { continue } // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := e.evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) if err != nil || elParamDesc.MeasurementId == nil { continue } @@ -416,7 +432,7 @@ func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { continue } - limitIdData, err := e.evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) + limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) if err != nil { continue } @@ -428,7 +444,7 @@ func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { } // electricalPermittedValueSet contains the allowed min, max and the default values per phase - limit := e.evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) + limit := evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) newLimit := model.LoadControlLimitDataType{ LimitId: limitDesc.LimitId, @@ -439,7 +455,7 @@ func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { } } - _, err := e.evLoadControl.WriteLimitValues(limitData) + _, err = evLoadControl.WriteLimitValues(limitData) return err } @@ -459,18 +475,20 @@ func (e *EMobility) EVWriteLoadControlLimits(limits []EVLoadLimits) error { // - ErrDataNotAvailable if that information is not (yet) available // - ErrNotSupported if getting the communication standard is not supported // - and others -func (e *EMobility) EVCommunicationStandard() (EVCommunicationStandardType, error) { - if e.evEntity == nil || e.evDeviceConfiguration == nil { +func (e *EMobility) EVCommunicationStandard(remoteEntity api.EntityRemoteInterface) (EVCommunicationStandardType, error) { + evDeviceConfiguration, err := e.deviceConfiguration(remoteEntity) + + if e.evEntity == nil || err != nil { return EVCommunicationStandardTypeUnknown, ErrEVDisconnected } // check if device configuration descriptions has an communication standard key name - _, err := e.evDeviceConfiguration.GetDescriptionForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard) + _, err = evDeviceConfiguration.GetDescriptionForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard) if err != nil { return EVCommunicationStandardTypeUnknown, err } - data, err := e.evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) + data, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) if err != nil { return EVCommunicationStandardTypeUnknown, err } @@ -488,16 +506,18 @@ func (e *EMobility) EVCommunicationStandard() (EVCommunicationStandardType, erro // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *EMobility) EVIdentification() (string, error) { +func (e *EMobility) EVIdentification(remoteEntity api.EntityRemoteInterface) (string, error) { if e.evEntity == nil { return "", ErrEVDisconnected } - if e.evIdentification == nil { + evIdentification, err := e.identification(remoteEntity) + + if err != nil { return "", features.ErrDataNotAvailable } - identifications, err := e.evIdentification.GetValues() + identifications, err := evIdentification.GetValues() if err != nil { return "", err } @@ -518,8 +538,10 @@ func (e *EMobility) EVIdentification() (string, error) { // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *EMobility) EVOptimizationOfSelfConsumptionSupported() (bool, error) { - if e.evEntity == nil || e.evLoadControl == nil { +func (e *EMobility) EVOptimizationOfSelfConsumptionSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + evLoadControl, err := e.loadControl(remoteEntity) + + if e.evEntity == nil || err != nil { return false, ErrEVDisconnected } @@ -534,7 +556,7 @@ func (e *EMobility) EVOptimizationOfSelfConsumptionSupported() (bool, error) { } // check if loadcontrol limit descriptions contains a recommendation category - if _, err = e.evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation); err != nil { + if _, err = evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation); err != nil { return false, err } @@ -550,8 +572,10 @@ func (e *EMobility) EVOptimizationOfSelfConsumptionSupported() (bool, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVSoCSupported() (bool, error) { - if e.evEntity == nil || e.evMeasurement == nil { +func (e *EMobility) EVSoCSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + evMeasurement, err := e.measurement(remoteEntity) + + if e.evEntity == nil || err != nil { return false, ErrEVDisconnected } @@ -566,7 +590,7 @@ func (e *EMobility) EVSoCSupported() (bool, error) { } // check if measurement descriptions has an SoC scope type - desc, err := e.evMeasurement.GetDescriptionsForScope(model.ScopeTypeTypeStateOfCharge) + desc, err := evMeasurement.GetDescriptionsForScope(model.ScopeTypeTypeStateOfCharge) if err != nil { return false, err } @@ -587,13 +611,15 @@ func (e *EMobility) EVSoCSupported() (bool, error) { // - ErrNotSupported if support for SoC is not possible // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *EMobility) EVSoC() (float64, error) { - if e.evEntity == nil || e.evMeasurement == nil { +func (e *EMobility) EVSoC(remoteEntity api.EntityRemoteInterface) (float64, error) { + evMeasurement, err := e.measurement(remoteEntity) + + if e.evEntity == nil || err != nil { return 0, ErrEVDisconnected } // check if the SoC is supported - support, err := e.EVSoCSupported() + support, err := e.EVSoCSupported(remoteEntity) if err != nil { return 0, err } @@ -601,7 +627,7 @@ func (e *EMobility) EVSoC() (float64, error) { return 0, features.ErrNotSupported } - data, err := e.evMeasurement.GetValuesForTypeCommodityScope(model.MeasurementTypeTypePercentage, model.CommodityTypeTypeElectricity, model.ScopeTypeTypeStateOfCharge) + data, err := evMeasurement.GetValuesForTypeCommodityScope(model.MeasurementTypeTypePercentage, model.CommodityTypeTypeElectricity, model.ScopeTypeTypeStateOfCharge) if err != nil { return 0, err } @@ -617,7 +643,7 @@ func (e *EMobility) EVSoC() (float64, error) { // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *EMobility) EVCoordinatedChargingSupported() (bool, error) { +func (e *EMobility) EVCoordinatedChargingSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { if e.evEntity == nil { return false, ErrEVDisconnected } @@ -636,19 +662,21 @@ func (e *EMobility) EVCoordinatedChargingSupported() (bool, error) { } // returns the current charging strategy -func (e *EMobility) EVChargeStrategy() EVChargeStrategyType { - if e.evEntity == nil || e.evTimeSeries == nil { +func (e *EMobility) EVChargeStrategy(remoteEntity api.EntityRemoteInterface) EVChargeStrategyType { + evTimeSeries, err := e.timeSeries(remoteEntity) + + if e.evEntity == nil || err != nil { return EVChargeStrategyTypeUnknown } // only ISO communication can provide a charging strategy information - com, err := e.EVCommunicationStandard() + com, err := e.EVCommunicationStandard(remoteEntity) if err != nil || com == EVCommunicationStandardTypeUnknown || com == EVCommunicationStandardTypeIEC61851 { return EVChargeStrategyTypeUnknown } // only the time series data for singledemand is relevant for detecting the charging strategy - data, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) if err != nil { return EVChargeStrategyTypeUnknown } @@ -696,18 +724,20 @@ func (e *EMobility) EVChargeStrategy() EVChargeStrategyType { } // returns the current energy demand in Wh and the duration -func (e *EMobility) EVEnergyDemand() (EVDemand, error) { +func (e *EMobility) EVEnergyDemand(remoteEntity api.EntityRemoteInterface) (EVDemand, error) { demand := EVDemand{} if e.evEntity == nil { return demand, ErrEVDisconnected } - if e.evTimeSeries == nil { + evTimeSeries, err := e.timeSeries(remoteEntity) + + if err != nil { return demand, features.ErrDataNotAvailable } - data, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) if err != nil { return demand, features.ErrDataNotAvailable } @@ -759,18 +789,20 @@ func (e *EMobility) EVEnergyDemand() (EVDemand, error) { return demand, nil } -func (e *EMobility) EVChargePlanConstraints() ([]EVDurationSlotValue, error) { +func (e *EMobility) EVChargePlanConstraints(remoteEntity api.EntityRemoteInterface) ([]EVDurationSlotValue, error) { constraints := []EVDurationSlotValue{} if e.evEntity == nil { return constraints, ErrEVDisconnected } - if e.evTimeSeries == nil { + evTimeSeries, err := e.timeSeries(remoteEntity) + + if err != nil { return constraints, features.ErrDataNotAvailable } - data, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeConstraints) + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeConstraints) if err != nil { return constraints, features.ErrDataNotAvailable } @@ -813,18 +845,20 @@ func (e *EMobility) EVChargePlanConstraints() ([]EVDurationSlotValue, error) { return constraints, nil } -func (e *EMobility) EVChargePlan() (EVChargePlan, error) { +func (e *EMobility) EVChargePlan(remoteEntity api.EntityRemoteInterface) (EVChargePlan, error) { plan := EVChargePlan{} if e.evEntity == nil { return plan, ErrEVDisconnected } - if e.evTimeSeries == nil { + evTimeSeries, err := e.timeSeries(remoteEntity) + + if err != nil { return plan, features.ErrDataNotAvailable } - data, err := e.evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) if err != nil { return plan, features.ErrDataNotAvailable } @@ -894,14 +928,16 @@ func (e *EMobility) EVChargePlan() (EVChargePlan, error) { } // returns the constraints for the time slots -func (e *EMobility) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { +func (e *EMobility) EVTimeSlotConstraints(remoteEntity api.EntityRemoteInterface) (EVTimeSlotConstraints, error) { result := EVTimeSlotConstraints{} - if e.evEntity == nil || e.evTimeSeries == nil { + evTimeSeries, err := e.timeSeries(remoteEntity) + + if e.evEntity == nil || err != nil { return result, features.ErrDataNotAvailable } - constraints, err := e.evTimeSeries.GetConstraints() + constraints, err := evTimeSeries.GetConstraints() if err != nil { return result, err } @@ -935,8 +971,10 @@ func (e *EMobility) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { } // send power limits to the EV -func (e *EMobility) EVWritePowerLimits(data []EVDurationSlotValue) error { - if e.evEntity == nil || e.evTimeSeries == nil { +func (e *EMobility) EVWritePowerLimits(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error { + evTimeSeries, err := e.timeSeries(remoteEntity) + + if e.evEntity == nil || err != nil { return ErrNotSupported } @@ -944,7 +982,7 @@ func (e *EMobility) EVWritePowerLimits(data []EVDurationSlotValue) error { return errors.New("missing power limit data") } - constraints, err := e.EVTimeSlotConstraints() + constraints, err := e.EVTimeSlotConstraints(remoteEntity) if err != nil { return err } @@ -957,7 +995,7 @@ func (e *EMobility) EVWritePowerLimits(data []EVDurationSlotValue) error { return errors.New("too many charge slots provided") } - desc, err := e.evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) + desc, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) if err != nil { return ErrNotSupported } @@ -994,20 +1032,21 @@ func (e *EMobility) EVWritePowerLimits(data []EVDurationSlotValue) error { TimeSeriesSlot: timeSeriesSlots, } - _, err = e.evTimeSeries.WriteValues([]model.TimeSeriesDataType{timeSeriesData}) + _, err = evTimeSeries.WriteValues([]model.TimeSeriesDataType{timeSeriesData}) return err } // returns the minimum and maximum number of incentive slots allowed -func (e *EMobility) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { +func (e *EMobility) EVIncentiveConstraints(remoteEntity api.EntityRemoteInterface) (EVIncentiveSlotConstraints, error) { result := EVIncentiveSlotConstraints{} - if e.evEntity == nil || e.evIncentiveTable == nil { + evIncentiveTable, err := e.incentiveTable(remoteEntity) + if e.evEntity == nil || err != nil { return result, features.ErrDataNotAvailable } - constraints, err := e.evIncentiveTable.GetConstraints() + constraints, err := evIncentiveTable.GetConstraints() if err != nil { return result, err } @@ -1026,8 +1065,10 @@ func (e *EMobility) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) } // send incentives to the EV -func (e *EMobility) EVWriteIncentives(data []EVDurationSlotValue) error { - if e.evEntity == nil || e.evIncentiveTable == nil { +func (e *EMobility) EVWriteIncentives(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error { + evIncentiveTable, err := e.incentiveTable(remoteEntity) + + if e.evEntity == nil || err != nil { return features.ErrDataNotAvailable } @@ -1035,7 +1076,7 @@ func (e *EMobility) EVWriteIncentives(data []EVDurationSlotValue) error { return errors.New("missing incentive data") } - constraints, err := e.EVIncentiveConstraints() + constraints, err := e.EVIncentiveConstraints(remoteEntity) if err != nil { return err } @@ -1101,7 +1142,7 @@ func (e *EMobility) EVWriteIncentives(data []EVDurationSlotValue) error { IncentiveSlot: incentiveSlots, } - _, err = e.evIncentiveTable.WriteValues([]model.IncentiveTableType{incentiveData}) + _, err = evIncentiveTable.WriteValues([]model.IncentiveTableType{incentiveData}) return err } diff --git a/emobility/public_EVChargePlan_test.go b/emobility/public_EVChargePlan_test.go index b0eff07..c6345cf 100644 --- a/emobility/public_EVChargePlan_test.go +++ b/emobility/public_EVChargePlan_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" gomock "go.uber.org/mock/gomock" @@ -12,7 +13,8 @@ import ( func Test_EVChargePlan(t *testing.T) { emobilty, eebusService := setupEmobility(t) - _, err := emobilty.EVChargePlan() + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + _, err := emobilty.EVChargePlan(mockRemoteEntity) assert.NotNil(t, err) localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) @@ -24,12 +26,10 @@ func Test_EVChargePlan(t *testing.T) { dataProviderMock := NewMockEmobilityDataProvider(ctrl) emobilty.dataProvider = dataProviderMock - _, err = emobilty.EVChargePlan() + _, err = emobilty.EVChargePlan(emobilty.evEntity) assert.NotNil(t, err) - emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) - - _, err = emobilty.EVChargePlan() + _, err = emobilty.EVChargePlan(emobilty.evEntity) assert.NotNil(t, err) datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) @@ -64,7 +64,7 @@ func Test_EVChargePlan(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - _, err = emobilty.EVChargePlan() + _, err = emobilty.EVChargePlan(emobilty.evEntity) assert.NotNil(t, err) cmd = []model.CmdType{{ @@ -96,6 +96,6 @@ func Test_EVChargePlan(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - _, err = emobilty.EVChargePlan() + _, err = emobilty.EVChargePlan(emobilty.evEntity) assert.Nil(t, err) } diff --git a/emobility/public_EVChargeStrategy_test.go b/emobility/public_EVChargeStrategy_test.go index a875a8c..4f95e6d 100644 --- a/emobility/public_EVChargeStrategy_test.go +++ b/emobility/public_EVChargeStrategy_test.go @@ -5,26 +5,31 @@ import ( "time" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVChargeStrategy(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data := emobilty.EVChargeStrategy() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data := emobilty.EVChargeStrategy(mockRemoteEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) - emobilty.evDeviceConfiguration = deviceConfiguration(localEntity, emobilty.evEntity) - - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) @@ -43,7 +48,7 @@ func Test_EVChargeStrategy(t *testing.T) { err := localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) cmd = []model.CmdType{{ @@ -62,12 +67,10 @@ func Test_EVChargeStrategy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) - emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) - - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) @@ -101,7 +104,7 @@ func Test_EVChargeStrategy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeUnknown, data) cmd = []model.CmdType{{ @@ -123,7 +126,7 @@ func Test_EVChargeStrategy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeNoDemand, data) cmd = []model.CmdType{{ @@ -147,7 +150,7 @@ func Test_EVChargeStrategy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeNoDemand, data) cmd = []model.CmdType{{ @@ -170,7 +173,7 @@ func Test_EVChargeStrategy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeDirectCharging, data) cmd = []model.CmdType{{ @@ -194,6 +197,6 @@ func Test_EVChargeStrategy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data = emobilty.EVChargeStrategy() + data = emobilty.EVChargeStrategy(emobilty.evEntity) assert.Equal(t, EVChargeStrategyTypeTimedCharging, data) } diff --git a/emobility/public_EVChargedEnergy_test.go b/emobility/public_EVChargedEnergy_test.go index 38f50aa..0c8964d 100644 --- a/emobility/public_EVChargedEnergy_test.go +++ b/emobility/public_EVChargedEnergy_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVChargedEnergy(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVChargedEnergy() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVChargedEnergy(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -19,13 +26,11 @@ func Test_EVChargedEnergy(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVChargedEnergy() + data, err = emobilty.EVChargedEnergy(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) - emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) - - data, err = emobilty.EVChargedEnergy() + data, err = emobilty.EVChargedEnergy(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -48,7 +53,7 @@ func Test_EVChargedEnergy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVChargedEnergy() + data, err = emobilty.EVChargedEnergy(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -66,7 +71,7 @@ func Test_EVChargedEnergy(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVChargedEnergy() + data, err = emobilty.EVChargedEnergy(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 80.0, data) } diff --git a/emobility/public_EVCommunicationStandard_test.go b/emobility/public_EVCommunicationStandard_test.go index 244e3dd..5acc398 100644 --- a/emobility/public_EVCommunicationStandard_test.go +++ b/emobility/public_EVCommunicationStandard_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVCommunicationStandard(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVCommunicationStandard() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVCommunicationStandard(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) @@ -19,13 +26,11 @@ func Test_EVCommunicationStandard(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVCommunicationStandard() + data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - emobilty.evDeviceConfiguration = deviceConfiguration(localEntity, emobilty.evEntity) - - data, err = emobilty.EVCommunicationStandard() + data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) @@ -45,7 +50,7 @@ func Test_EVCommunicationStandard(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCommunicationStandard() + data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) @@ -63,7 +68,7 @@ func Test_EVCommunicationStandard(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCommunicationStandard() + data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, EVCommunicationStandardTypeUnknown, data) @@ -83,7 +88,7 @@ func Test_EVCommunicationStandard(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCommunicationStandard() + data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, EVCommunicationStandardTypeISO151182ED1, data) } diff --git a/emobility/public_EVConnectedPhases_test.go b/emobility/public_EVConnectedPhases_test.go index a6c78de..8fe69fa 100644 --- a/emobility/public_EVConnectedPhases_test.go +++ b/emobility/public_EVConnectedPhases_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVConnectedPhases(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVConnectedPhases() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVConnectedPhases(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, uint(0), data) @@ -19,13 +26,11 @@ func Test_EVConnectedPhases(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVConnectedPhases() + data, err = emobilty.EVConnectedPhases(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, uint(0), data) - emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) - - data, err = emobilty.EVConnectedPhases() + data, err = emobilty.EVConnectedPhases(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, uint(0), data) @@ -44,7 +49,7 @@ func Test_EVConnectedPhases(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVConnectedPhases() + data, err = emobilty.EVConnectedPhases(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, uint(3), data) @@ -62,7 +67,7 @@ func Test_EVConnectedPhases(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVConnectedPhases() + data, err = emobilty.EVConnectedPhases(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, uint(1), data) } diff --git a/emobility/public_EVCoordinatedChargingSupported_test.go b/emobility/public_EVCoordinatedChargingSupported_test.go index 754d4e8..6df2eff 100644 --- a/emobility/public_EVCoordinatedChargingSupported_test.go +++ b/emobility/public_EVCoordinatedChargingSupported_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) @@ -11,7 +12,8 @@ import ( func Test_EVCoordinatedChargingSupported(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVCoordinatedChargingSupported() + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + data, err := emobilty.EVCoordinatedChargingSupported(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, false, data) @@ -19,7 +21,7 @@ func Test_EVCoordinatedChargingSupported(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVCoordinatedChargingSupported() + data, err = emobilty.EVCoordinatedChargingSupported(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, false, data) @@ -45,7 +47,7 @@ func Test_EVCoordinatedChargingSupported(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCoordinatedChargingSupported() + data, err = emobilty.EVCoordinatedChargingSupported(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, true, data) } diff --git a/emobility/public_EVCurrentChargeState_test.go b/emobility/public_EVCurrentChargeState_test.go index 30ecf1f..050c789 100644 --- a/emobility/public_EVCurrentChargeState_test.go +++ b/emobility/public_EVCurrentChargeState_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVCurrentChargeState(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVCurrentChargeState() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVCurrentChargeState(mockRemoteEntity) assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeUnplugged, data) @@ -19,13 +26,7 @@ func Test_EVCurrentChargeState(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVCurrentChargeState() - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypeUnplugged, data) - - emobilty.evDeviceDiagnosis = deviceDiagnosis(localEntity, emobilty.evEntity) - - data, err = emobilty.EVCurrentChargeState() + data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, EVChargeStateTypeUnknown, data) @@ -40,7 +41,7 @@ func Test_EVCurrentChargeState(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentChargeState() + data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeActive, data) @@ -53,7 +54,7 @@ func Test_EVCurrentChargeState(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentChargeState() + data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, EVChargeStateTypePaused, data) @@ -66,7 +67,7 @@ func Test_EVCurrentChargeState(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentChargeState() + data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeError, data) @@ -79,7 +80,7 @@ func Test_EVCurrentChargeState(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentChargeState() + data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeFinished, data) @@ -92,7 +93,7 @@ func Test_EVCurrentChargeState(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentChargeState() + data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, EVChargeStateTypeUnknown, data) } diff --git a/emobility/public_EVCurrentLimits_test.go b/emobility/public_EVCurrentLimits_test.go index 69d7c5c..996d9ce 100644 --- a/emobility/public_EVCurrentLimits_test.go +++ b/emobility/public_EVCurrentLimits_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVCurrentLimits(t *testing.T) { emobilty, eebusService := setupEmobility(t) - minData, maxData, defaultData, err := emobilty.EVCurrentLimits() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + minData, maxData, defaultData, err := emobilty.EVCurrentLimits(mockRemoteEntity) assert.NotNil(t, err) assert.Nil(t, minData) assert.Nil(t, maxData) @@ -21,15 +28,13 @@ func Test_EVCurrentLimits(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - minData, maxData, defaultData, err = emobilty.EVCurrentLimits() + minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, minData) assert.Nil(t, maxData) assert.Nil(t, defaultData) - emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) - - minData, maxData, defaultData, err = emobilty.EVCurrentLimits() + minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, minData) assert.Nil(t, maxData) @@ -66,7 +71,7 @@ func Test_EVCurrentLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - minData, maxData, defaultData, err = emobilty.EVCurrentLimits() + minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, minData) assert.Nil(t, maxData) @@ -162,7 +167,7 @@ func Test_EVCurrentLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - minData, maxData, defaultData, err = emobilty.EVCurrentLimits() + minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) assert.Nil(t, err) assert.Nil(t, err) diff --git a/emobility/public_EVCurrentsPerPhase_test.go b/emobility/public_EVCurrentsPerPhase_test.go index 2063fb4..bc35230 100644 --- a/emobility/public_EVCurrentsPerPhase_test.go +++ b/emobility/public_EVCurrentsPerPhase_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVCurrentsPerPhase(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVCurrentsPerPhase() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVCurrentsPerPhase(mockRemoteEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -19,14 +26,11 @@ func Test_EVCurrentsPerPhase(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVCurrentsPerPhase() + data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) - emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) - emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) - - data, err = emobilty.EVCurrentsPerPhase() + data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -50,7 +54,7 @@ func Test_EVCurrentsPerPhase(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -73,7 +77,7 @@ func Test_EVCurrentsPerPhase(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentsPerPhase() + data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -91,7 +95,7 @@ func Test_EVCurrentsPerPhase(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVCurrentsPerPhase() + data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 10.0, data[0]) } diff --git a/emobility/public_EVEnergyDemand_test.go b/emobility/public_EVEnergyDemand_test.go index 0d943a6..9d5c2d4 100644 --- a/emobility/public_EVEnergyDemand_test.go +++ b/emobility/public_EVEnergyDemand_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) @@ -12,7 +13,8 @@ import ( func Test_EVEnergySingleDemand(t *testing.T) { emobilty, eebusService := setupEmobility(t) - demand, err := emobilty.EVEnergyDemand() + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + demand, err := emobilty.EVEnergyDemand(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -24,7 +26,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -32,9 +34,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.DurationUntilStart) assert.Equal(t, 0.0, demand.DurationUntilEnd) - emobilty.evDeviceConfiguration = deviceConfiguration(localEntity, emobilty.evEntity) - - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -58,7 +58,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -82,7 +82,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -90,9 +90,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { assert.Equal(t, 0.0, demand.DurationUntilStart) assert.Equal(t, 0.0, demand.DurationUntilEnd) - emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) - - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -131,7 +129,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -161,7 +159,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 0.0, demand.OptDemand) @@ -194,7 +192,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 1000.0, demand.MinDemand) assert.Equal(t, 10000.0, demand.OptDemand) @@ -226,7 +224,7 @@ func Test_EVEnergySingleDemand(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - demand, err = emobilty.EVEnergyDemand() + demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 0.0, demand.MinDemand) assert.Equal(t, 10000.0, demand.OptDemand) diff --git a/emobility/public_EVIdentification_test.go b/emobility/public_EVIdentification_test.go index 88f7e56..ef7d872 100644 --- a/emobility/public_EVIdentification_test.go +++ b/emobility/public_EVIdentification_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) @@ -11,7 +12,8 @@ import ( func Test_EVIdentification(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVIdentification() + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + data, err := emobilty.EVIdentification(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, "", data) @@ -19,13 +21,11 @@ func Test_EVIdentification(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVIdentification() + data, err = emobilty.EVIdentification(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, "", data) - emobilty.evIdentification = identificationConfiguration(localEntity, emobilty.evEntity) - - data, err = emobilty.EVIdentification() + data, err = emobilty.EVIdentification(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, "", data) @@ -46,7 +46,7 @@ func Test_EVIdentification(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVIdentification() + data, err = emobilty.EVIdentification(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, "test", data) } diff --git a/emobility/public_EVIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go index f6309ee..1609722 100644 --- a/emobility/public_EVIncentiveConstraints_test.go +++ b/emobility/public_EVIncentiveConstraints_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVGetIncentiveConstraints(t *testing.T) { emobilty, eebusService := setupEmobility(t) - constraints, err := emobilty.EVIncentiveConstraints() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + constraints, err := emobilty.EVIncentiveConstraints(mockRemoteEntity) assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.NotEqual(t, err, nil) @@ -20,14 +27,12 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints, err = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.NotEqual(t, err, nil) - emobilty.evIncentiveTable = incentiveTableConfiguration(localEntity, emobilty.evEntity) - - constraints, err = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.NotEqual(t, err, nil) @@ -51,7 +56,7 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints, err = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) assert.Equal(t, err, nil) @@ -72,7 +77,7 @@ func Test_EVGetIncentiveConstraints(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints, err = emobilty.EVIncentiveConstraints() + constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, err, nil) diff --git a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go index 24893da..496a1a7 100644 --- a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go +++ b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVOptimizationOfSelfConsumptionSupported() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVOptimizationOfSelfConsumptionSupported(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, false, data) @@ -19,13 +26,7 @@ func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported() - assert.NotNil(t, err) - assert.Equal(t, false, data) - - emobilty.evLoadControl = loadcontrol(localEntity, emobilty.evEntity) - - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported() + data, err = emobilty.EVOptimizationOfSelfConsumptionSupported(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, false, data) @@ -51,7 +52,7 @@ func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported() + data, err = emobilty.EVOptimizationOfSelfConsumptionSupported(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, false, data) @@ -71,7 +72,7 @@ func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported() + data, err = emobilty.EVOptimizationOfSelfConsumptionSupported(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, true, data) } diff --git a/emobility/public_EVPowerPerPhase_test.go b/emobility/public_EVPowerPerPhase_test.go index 2b0238a..2a2e24f 100644 --- a/emobility/public_EVPowerPerPhase_test.go +++ b/emobility/public_EVPowerPerPhase_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVPowerPerPhase_Power(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVPowerPerPhase() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVPowerPerPhase(mockRemoteEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -19,14 +26,11 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) - emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) - emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) - - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -50,7 +54,7 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -73,7 +77,7 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -91,7 +95,7 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 80.0, data[0]) } @@ -99,7 +103,12 @@ func Test_EVPowerPerPhase_Power(t *testing.T) { func Test_EVPowerPerPhase_Current(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVPowerPerPhase() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVPowerPerPhase(mockRemoteEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -107,14 +116,7 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVPowerPerPhase() - assert.NotNil(t, err) - assert.Nil(t, data) - - emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) - emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) - - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -138,7 +140,7 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -161,7 +163,7 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.NotNil(t, err) assert.Nil(t, data) @@ -179,7 +181,7 @@ func Test_EVPowerPerPhase_Current(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVPowerPerPhase() + data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 2300.0, data[0]) } diff --git a/emobility/public_EVSoCSupported_test.go b/emobility/public_EVSoCSupported_test.go index 2546f22..12d5f6d 100644 --- a/emobility/public_EVSoCSupported_test.go +++ b/emobility/public_EVSoCSupported_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVSoCSupported(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVSoCSupported() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVSoCSupported(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, false, data) @@ -19,13 +26,7 @@ func Test_EVSoCSupported(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVSoCSupported() - assert.NotNil(t, err) - assert.Equal(t, false, data) - - emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) - - data, err = emobilty.EVSoCSupported() + data, err = emobilty.EVSoCSupported(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, false, data) @@ -51,7 +52,7 @@ func Test_EVSoCSupported(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVSoCSupported() + data, err = emobilty.EVSoCSupported(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, false, data) @@ -71,7 +72,7 @@ func Test_EVSoCSupported(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVSoCSupported() + data, err = emobilty.EVSoCSupported(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, true, data) } diff --git a/emobility/public_EVSoC_test.go b/emobility/public_EVSoC_test.go index 715d6db..7d6dc25 100644 --- a/emobility/public_EVSoC_test.go +++ b/emobility/public_EVSoC_test.go @@ -4,14 +4,21 @@ import ( "testing" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVSoC(t *testing.T) { emobilty, eebusService := setupEmobility(t) - data, err := emobilty.EVSoC() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + data, err := emobilty.EVSoC(mockRemoteEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -19,13 +26,11 @@ func Test_EVSoC(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - data, err = emobilty.EVSoC() + data, err = emobilty.EVSoC(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) - emobilty.evMeasurement = measurement(localEntity, emobilty.evEntity) - - data, err = emobilty.EVSoC() + data, err = emobilty.EVSoC(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -51,7 +56,7 @@ func Test_EVSoC(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVSoC() + data, err = emobilty.EVSoC(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -73,7 +78,7 @@ func Test_EVSoC(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVSoC() + data, err = emobilty.EVSoC(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -92,7 +97,7 @@ func Test_EVSoC(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVSoC() + data, err = emobilty.EVSoC(emobilty.evEntity) assert.NotNil(t, err) assert.Equal(t, 0.0, data) @@ -112,7 +117,7 @@ func Test_EVSoC(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - data, err = emobilty.EVSoC() + data, err = emobilty.EVSoC(emobilty.evEntity) assert.Nil(t, err) assert.Equal(t, 80.0, data) } diff --git a/emobility/public_EVTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go index b4bc61f..f763a21 100644 --- a/emobility/public_EVTimeSlotConstraints_test.go +++ b/emobility/public_EVTimeSlotConstraints_test.go @@ -5,14 +5,21 @@ import ( "time" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty, eebusService := setupEmobility(t) - constraints, err := emobilty.EVTimeSlotConstraints() + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + constraints, err := emobilty.EVTimeSlotConstraints(mockRemoteEntity) assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -24,7 +31,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - constraints, err = emobilty.EVTimeSlotConstraints() + constraints, err = emobilty.EVTimeSlotConstraints(emobilty.evEntity) assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -32,9 +39,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) assert.NotEqual(t, err, nil) - emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) - - constraints, err = emobilty.EVTimeSlotConstraints() + constraints, err = emobilty.EVTimeSlotConstraints(emobilty.evEntity) assert.Equal(t, uint(0), constraints.MinSlots) assert.Equal(t, uint(0), constraints.MaxSlots) assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) @@ -63,7 +68,7 @@ func Test_EVGetTimeSlotConstraints(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - constraints, err = emobilty.EVTimeSlotConstraints() + constraints, err = emobilty.EVTimeSlotConstraints(emobilty.evEntity) assert.Equal(t, uint(1), constraints.MinSlots) assert.Equal(t, uint(10), constraints.MaxSlots) assert.Equal(t, time.Duration(1*time.Minute), constraints.MinSlotDuration) diff --git a/emobility/public_EVWriteIncentives_test.go b/emobility/public_EVWriteIncentives_test.go index b313daf..0ddb08a 100644 --- a/emobility/public_EVWriteIncentives_test.go +++ b/emobility/public_EVWriteIncentives_test.go @@ -6,8 +6,10 @@ import ( "time" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVWriteIncentives(t *testing.T) { @@ -15,19 +17,22 @@ func Test_EVWriteIncentives(t *testing.T) { data := []EVDurationSlotValue{} - err := emobilty.EVWriteIncentives(data) + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + err := emobilty.EVWriteIncentives(mockRemoteEntity, data) assert.NotNil(t, err) localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - err = emobilty.EVWriteIncentives(data) + err = emobilty.EVWriteIncentives(emobilty.evEntity, data) assert.NotNil(t, err) - emobilty.evIncentiveTable = incentiveTableConfiguration(localEntity, emobilty.evEntity) - - err = emobilty.EVWriteIncentives(data) + err = emobilty.EVWriteIncentives(emobilty.evEntity, data) assert.NotNil(t, err) datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) @@ -48,7 +53,7 @@ func Test_EVWriteIncentives(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteIncentives(data) + err = emobilty.EVWriteIncentives(emobilty.evEntity, data) assert.NotNil(t, err) type dataStruct struct { @@ -129,7 +134,7 @@ func Test_EVWriteIncentives(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteIncentives(data.slots) + err = emobilty.EVWriteIncentives(emobilty.evEntity, data.slots) if data.error { assert.NotNil(t, err) continue diff --git a/emobility/public_EVWriteLoadControlLimits_test.go b/emobility/public_EVWriteLoadControlLimits_test.go index 96028ce..64b1c65 100644 --- a/emobility/public_EVWriteLoadControlLimits_test.go +++ b/emobility/public_EVWriteLoadControlLimits_test.go @@ -6,6 +6,7 @@ import ( "github.com/enbility/cemd/util" eebusUtil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" "golang.org/x/exp/slices" @@ -16,20 +17,18 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { loadLimits := []EVLoadLimits{} - err := emobilty.EVWriteLoadControlLimits(loadLimits) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + err := emobilty.EVWriteLoadControlLimits(mockRemoteEntity, loadLimits) assert.NotNil(t, err) localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - err = emobilty.EVWriteLoadControlLimits(loadLimits) + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) assert.NotNil(t, err) - emobilty.evElectricalConnection = electricalConnection(localEntity, emobilty.evEntity) - emobilty.evLoadControl = loadcontrol(localEntity, emobilty.evEntity) - - err = emobilty.EVWriteLoadControlLimits(loadLimits) + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) assert.NotNil(t, err) datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) @@ -62,7 +61,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(loadLimits) + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) assert.NotNil(t, err) type dataStruct struct { @@ -164,7 +163,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(loadLimits) + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) assert.NotNil(t, err) datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) @@ -202,7 +201,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(loadLimits) + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) assert.NotNil(t, err) limitData := []model.LoadControlLimitDataType{} @@ -224,7 +223,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWriteLoadControlLimits(loadLimits) + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) assert.NotNil(t, err) obligations := []EVLoadLimitsPhase{} @@ -246,7 +245,7 @@ func Test_EVWriteLoadControlLimits(t *testing.T) { }) } - err = emobilty.EVWriteLoadControlLimits([]EVLoadLimits{ + err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, []EVLoadLimits{ {Category: model.LoadControlCategoryTypeObligation, PhaseData: obligations}, {Category: model.LoadControlCategoryTypeRecommendation, PhaseData: recommendations}, }) diff --git a/emobility/public_EVWritePowerLimits_test.go b/emobility/public_EVWritePowerLimits_test.go index 1b81adc..079cb6d 100644 --- a/emobility/public_EVWritePowerLimits_test.go +++ b/emobility/public_EVWritePowerLimits_test.go @@ -6,8 +6,10 @@ import ( "time" "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func Test_EVWritePowerLimits(t *testing.T) { @@ -15,19 +17,22 @@ func Test_EVWritePowerLimits(t *testing.T) { data := []EVDurationSlotValue{} - err := emobilty.EVWritePowerLimits(data) + mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) + mockRemoteEntity := mocks.NewEntityRemoteInterface(t) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) + mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) + err := emobilty.EVWritePowerLimits(mockRemoteEntity, data) assert.NotNil(t, err) localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) emobilty.evseEntity = entites[0] emobilty.evEntity = entites[1] - err = emobilty.EVWritePowerLimits(data) + err = emobilty.EVWritePowerLimits(emobilty.evEntity, data) assert.NotNil(t, err) - emobilty.evTimeSeries = timeSeriesConfiguration(localEntity, emobilty.evEntity) - - err = emobilty.EVWritePowerLimits(data) + err = emobilty.EVWritePowerLimits(emobilty.evEntity, data) assert.NotNil(t, err) datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) @@ -46,7 +51,7 @@ func Test_EVWritePowerLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWritePowerLimits(data) + err = emobilty.EVWritePowerLimits(emobilty.evEntity, data) assert.NotNil(t, err) type dataStruct struct { @@ -126,7 +131,7 @@ func Test_EVWritePowerLimits(t *testing.T) { err = localDevice.ProcessCmd(datagram, remoteDevice) assert.Nil(t, err) - err = emobilty.EVWritePowerLimits(data.slots) + err = emobilty.EVWritePowerLimits(emobilty.evEntity, data.slots) if data.error { assert.NotNil(t, err) continue diff --git a/go.mod b/go.mod index e9ee8c3..ef72c08 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240123191239-38836994d74c + github.com/enbility/eebus-go v0.0.0-20240125160546-db5d41b14006 github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 - github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f + github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index 61ca1e6..a464d44 100644 --- a/go.sum +++ b/go.sum @@ -5,12 +5,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240123191239-38836994d74c h1:nw7IcECZKHBBCt/ev1dOgHhfVSJ5UbL61U2yXdbUKls= -github.com/enbility/eebus-go v0.0.0-20240123191239-38836994d74c/go.mod h1:hPGvK0YSiY2P47teGVvDgYkRD2GcFGZxA1NqVF8dXAk= +github.com/enbility/eebus-go v0.0.0-20240125160546-db5d41b14006 h1:AYTsL/EUWKt7xItmn3jPzzdGN5KTCeq72c0NFs8bwWE= +github.com/enbility/eebus-go v0.0.0-20240125160546-db5d41b14006/go.mod h1:2YKlVriF55lBo/AaiK0XPIeRImpWelKOHCHXW3NtOU0= github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 h1:I54U0+MHwtsHUS7OLEt0z5fGNZOpV7G3EgQxRvFXeKI= github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f h1:BmUN9NM/mWblJIvvctkPgHN65/+ZmwyN1IF3a6Jzdy0= -github.com/enbility/spine-go v0.0.0-20240123190024-7065975ef07f/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= +github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb h1:H9mjA/l2lxZChvrruZLSew2Dafs6Yp6GAhHq/3lYfPc= +github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From 9228821543c75092bf4d726b0d967ec7ce2b321c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 17:36:31 +0100 Subject: [PATCH 058/227] Update eebus, spine and ship --- emobility/events.go | 18 +++++++++--------- go.mod | 6 +++--- go.sum | 12 ++++++------ grid/events.go | 6 +++--- inverterbatteryvis/events.go | 4 ++-- inverterpvvis/events.go | 6 +++--- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/emobility/events.go b/emobility/events.go index 0417d1b..34a0e0e 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -361,7 +361,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { // initialise features, e.g. subscriptions, bindings if evDeviceClassification, err := e.deviceClassification(entity); err == nil { - if err := evDeviceClassification.SubscribeForEntity(); err != nil { + if err := evDeviceClassification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -372,7 +372,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evDeviceConfiguration, err := e.deviceConfiguration(entity); err == nil { - if err := evDeviceConfiguration.SubscribeForEntity(); err != nil { + if err := evDeviceConfiguration.Subscribe(); err != nil { logging.Log().Debug(err) } // get ev configuration data @@ -382,7 +382,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evDeviceDiagnosis, err := e.deviceDiagnosis(entity); err == nil { - if err := evDeviceDiagnosis.SubscribeForEntity(); err != nil { + if err := evDeviceDiagnosis.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -393,7 +393,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evElectricalConnection, err := e.electricalConnection(entity); err == nil { - if err := evElectricalConnection.SubscribeForEntity(); err != nil { + if err := evElectricalConnection.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -409,7 +409,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evMeasurement, err := e.measurement(entity); err == nil { - if err := evMeasurement.SubscribeForEntity(); err != nil { + if err := evMeasurement.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -421,7 +421,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evLoadControl, err := e.loadControl(entity); err == nil { - if err := evLoadControl.SubscribeForEntity(); err != nil { + if err := evLoadControl.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -437,7 +437,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evIdentification, err := e.identification(entity); err == nil { - if err := evIdentification.SubscribeForEntity(); err != nil { + if err := evIdentification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -449,7 +449,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { if e.configuration.CoordinatedChargingEnabled { if evTimeSeries, err := e.timeSeries(entity); err == nil { - if err := evTimeSeries.SubscribeForEntity(); err != nil { + if err := evTimeSeries.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -465,7 +465,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evIncentiveTable, err := e.incentiveTable(entity); err == nil { - if err := evIncentiveTable.SubscribeForEntity(); err != nil { + if err := evIncentiveTable.Subscribe(); err != nil { logging.Log().Debug(err) } diff --git a/go.mod b/go.mod index ef72c08..8e19d8b 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240125160546-db5d41b14006 - github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 - github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb + github.com/enbility/eebus-go v0.0.0-20240128162859-e78cbfd10ba1 + github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e + github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index a464d44..d979300 100644 --- a/go.sum +++ b/go.sum @@ -5,12 +5,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240125160546-db5d41b14006 h1:AYTsL/EUWKt7xItmn3jPzzdGN5KTCeq72c0NFs8bwWE= -github.com/enbility/eebus-go v0.0.0-20240125160546-db5d41b14006/go.mod h1:2YKlVriF55lBo/AaiK0XPIeRImpWelKOHCHXW3NtOU0= -github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4 h1:I54U0+MHwtsHUS7OLEt0z5fGNZOpV7G3EgQxRvFXeKI= -github.com/enbility/ship-go v0.0.0-20240123184928-6739ac1a68c4/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb h1:H9mjA/l2lxZChvrruZLSew2Dafs6Yp6GAhHq/3lYfPc= -github.com/enbility/spine-go v0.0.0-20240125155659-6851be30eecb/go.mod h1:VkPWyFUVlf385o7NeesvAiYoDDDnYyLuIYYdhzfJ0pY= +github.com/enbility/eebus-go v0.0.0-20240128162859-e78cbfd10ba1 h1:qwEArNwPfb3dA58yVoYOTzNBCPII/NTG9el4iXxe+/4= +github.com/enbility/eebus-go v0.0.0-20240128162859-e78cbfd10ba1/go.mod h1:HfsHi904LIoOZYNLAnUJX4XDpIz5CmnRXewd+gkRqfo= +github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e h1:SEwRSLtyI33v4CjyqhfGTV2U+4EI9s8Z5ap5PJBGhbc= +github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 h1:0S8EZeDfCzN0VZUyiiHOTgkTcnheN7Vuzfy5fG4ZDLU= +github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1/go.mod h1:ETVDSeiTPNJZ8WBAuYBIP+8+9qbDl8lpbKbiAisYMTg= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= diff --git a/grid/events.go b/grid/events.go index 6baaa21..ca3072d 100644 --- a/grid/events.go +++ b/grid/events.go @@ -88,15 +88,15 @@ func (e *Grid) gridConnected(ski string, entity api.EntityRemoteInterface) { e.gridMeasurement = f3 // subscribe - if err := e.gridDeviceConfiguration.SubscribeForEntity(); err != nil { + if err := e.gridDeviceConfiguration.Subscribe(); err != nil { logging.Log().Error(err) return } - if err := e.gridElectricalConnection.SubscribeForEntity(); err != nil { + if err := e.gridElectricalConnection.Subscribe(); err != nil { logging.Log().Error(err) return } - if err := e.gridMeasurement.SubscribeForEntity(); err != nil { + if err := e.gridMeasurement.Subscribe(); err != nil { logging.Log().Error(err) return } diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index 0359597..f672bf0 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -97,10 +97,10 @@ func (i *InverterBatteryVis) inverterConnected(ski string, entity api.EntityRemo i.inverterMeasurement = f2 // subscribe - if err := i.inverterElectricalConnection.SubscribeForEntity(); err != nil { + if err := i.inverterElectricalConnection.Subscribe(); err != nil { logging.Log().Error(err) } - if err := i.inverterMeasurement.SubscribeForEntity(); err != nil { + if err := i.inverterMeasurement.Subscribe(); err != nil { logging.Log().Error(err) } diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index 3bd4dca..d29bbcd 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -113,13 +113,13 @@ func (e *InverterPVVis) inverterConnected(ski string, entity api.EntityRemoteInt e.inverterDeviceConfiguration = f3 // subscribe - if err := e.inverterDeviceConfiguration.SubscribeForEntity(); err != nil { + if err := e.inverterDeviceConfiguration.Subscribe(); err != nil { logging.Log().Error(err) } - if err := e.inverterElectricalConnection.SubscribeForEntity(); err != nil { + if err := e.inverterElectricalConnection.Subscribe(); err != nil { logging.Log().Error(err) } - if err := e.inverterMeasurement.SubscribeForEntity(); err != nil { + if err := e.inverterMeasurement.Subscribe(); err != nil { logging.Log().Error(err) } From 6de5ae3b2ee52757dedd804d1853a9d05beebfa7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 17:36:40 +0100 Subject: [PATCH 059/227] Update github action --- .github/workflows/default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 1965e46..ee8d23a 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -46,7 +46,7 @@ jobs: # we let the report trigger content trigger a failure using the GitHub Security features. args: '-no-fail -fmt sarif -out results.sarif ./...' - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: # Path to SARIF file relative to the root of the repository sarif_file: results.sarif \ No newline at end of file From 8b990068cc897b5c84f90c86ec75df83c662ca2b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 18:32:21 +0100 Subject: [PATCH 060/227] Fix panic --- emobility/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emobility/events.go b/emobility/events.go index 34a0e0e..e90398e 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -208,7 +208,7 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { } } - if e.dataProvider == nil { + if e.dataProvider == nil || payload.Entity == nil { return } From 7d9dff3d977d646de83fd99dc954642564c8f8e2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 21:17:25 +0100 Subject: [PATCH 061/227] Update github workflow --- .github/workflows/default.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index ee8d23a..39ee541 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -26,7 +26,7 @@ jobs: run: go build -v ./... - name: Lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@master with: version: latest skip-pkg-cache: true @@ -36,9 +36,9 @@ jobs: run: go test -race -v -coverprofile=coverage.out -covermode=atomic ./... - name: Send coverage - uses: shogo82148/actions-goveralls@v1 + uses: coverallsapp/github-action@v2 with: - path-to-profile: coverage.out + file: coverage.out - name: Run Gosec Security Scanner uses: securego/gosec@master From 45bbc67d2880947dd675ec82db92791ab7b8000f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 28 Jan 2024 21:17:34 +0100 Subject: [PATCH 062/227] Update eebus, spine and ship --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8e19d8b..797faa7 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240128162859-e78cbfd10ba1 - github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e - github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 + github.com/enbility/eebus-go v0.0.0-20240128201236-02552d237ad9 + github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d + github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b diff --git a/go.sum b/go.sum index d979300..ba386d0 100644 --- a/go.sum +++ b/go.sum @@ -5,12 +5,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240128162859-e78cbfd10ba1 h1:qwEArNwPfb3dA58yVoYOTzNBCPII/NTG9el4iXxe+/4= -github.com/enbility/eebus-go v0.0.0-20240128162859-e78cbfd10ba1/go.mod h1:HfsHi904LIoOZYNLAnUJX4XDpIz5CmnRXewd+gkRqfo= -github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e h1:SEwRSLtyI33v4CjyqhfGTV2U+4EI9s8Z5ap5PJBGhbc= -github.com/enbility/ship-go v0.0.0-20240128143740-0e207dd3699e/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1 h1:0S8EZeDfCzN0VZUyiiHOTgkTcnheN7Vuzfy5fG4ZDLU= -github.com/enbility/spine-go v0.0.0-20240128162444-92f4e9dcd5f1/go.mod h1:ETVDSeiTPNJZ8WBAuYBIP+8+9qbDl8lpbKbiAisYMTg= +github.com/enbility/eebus-go v0.0.0-20240128201236-02552d237ad9 h1:CynxlvZTwqEWosWBhYOo5DQAqqytJO+QGlcQmMb+5QY= +github.com/enbility/eebus-go v0.0.0-20240128201236-02552d237ad9/go.mod h1:PNYOYQmb98K1R9RtIADuQZOgfvFyn872CNEBRcpa7eA= +github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d h1:zxstkLtwd0K1+ayEj1jeXciMyU6vOkE3hIZiSfVRWfA= +github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= +github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c h1:bpJPdXlRB0NzfwcIHq/N64YG48r7syEnsYTAF8kEogw= +github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= From bdc592f303a9aa28e8fac89bed17b0a6ffded591 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 8 Feb 2024 17:18:57 +0100 Subject: [PATCH 063/227] Fix possible panic --- emobility/results.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/emobility/results.go b/emobility/results.go index ce8e3ca..920f5b5 100644 --- a/emobility/results.go +++ b/emobility/results.go @@ -1,6 +1,8 @@ package emobility import ( + "fmt" + "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -35,6 +37,10 @@ func (e *EMobility) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { if len(datagram.Payload.Cmd) > 0 && datagram.Payload.Cmd[0].DeviceDiagnosisHeartbeatData != nil { // something is horribly wrong, disconnect and hope a new connection will fix it - e.service.DisconnectSKI(resultMsg.DeviceRemote.Ski(), string(*resultMsg.Result.Description)) + errorText := fmt.Sprintf("Error Code: %d", resultMsg.Result.ErrorNumber) + if resultMsg.Result.Description != nil { + errorText = fmt.Sprintf("%s - %s", errorText, string(*resultMsg.Result.Description)) + } + e.service.DisconnectSKI(resultMsg.DeviceRemote.Ski(), errorText) } } From 78ea63e43553a1c71a4042fb7b086558bd6d4559 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 10 Feb 2024 11:37:42 +0100 Subject: [PATCH 064/227] Update ship, spine, eebus & other dependencies --- go.mod | 20 +++++++------- go.sum | 85 ++++++++++++++-------------------------------------------- 2 files changed, 30 insertions(+), 75 deletions(-) diff --git a/go.mod b/go.mod index 797faa7..df9b829 100644 --- a/go.mod +++ b/go.mod @@ -3,31 +3,31 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240128201236-02552d237ad9 - github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d - github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c + github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c + github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 + github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 - golang.org/x/exp v0.0.0-20231226003508-02704c960a9b + golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 ) require ( - github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed // indirect - github.com/miekg/dns v1.1.57 // indirect + github.com/holoplot/go-avahi v0.0.0-20240210093433-b8dc0fc11e7e // indirect + github.com/miekg/dns v1.1.58 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rickb777/date v1.20.5 // indirect github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ba386d0..955e389 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,16 @@ -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df h1:qYVips8l0s1ldVoLSup0m+hYh5cMMA4ndcvocxhZuMc= -github.com/DerAndereAndi/zeroconf/v2 v2.0.0-20231028092313-1ae0ab54a2df/go.mod h1:OO5/UahoVBLyauLdDF4httPlSISqbrWDbHi9k99zUsc= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240128201236-02552d237ad9 h1:CynxlvZTwqEWosWBhYOo5DQAqqytJO+QGlcQmMb+5QY= -github.com/enbility/eebus-go v0.0.0-20240128201236-02552d237ad9/go.mod h1:PNYOYQmb98K1R9RtIADuQZOgfvFyn872CNEBRcpa7eA= -github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d h1:zxstkLtwd0K1+ayEj1jeXciMyU6vOkE3hIZiSfVRWfA= -github.com/enbility/ship-go v0.0.0-20240128200240-010ebb9a9e2d/go.mod h1:6xPvus7uzBqq3p2mQRWmbjP8n1/gpMil3jikRNBDUCg= -github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c h1:bpJPdXlRB0NzfwcIHq/N64YG48r7syEnsYTAF8kEogw= -github.com/enbility/spine-go v0.0.0-20240128200921-f69ec326eb2c/go.mod h1:QkF2SPaU8djEJ8qd5FeraOUI+x2gwONnS4QrUxl8I3s= +github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c h1:iGmlyQiRkEch2RwO5wl7mCSAlaI7nOn+Ed1OKx6r/uk= +github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c/go.mod h1:7nekYlHCIb4nG1rsW0S12/iz/b/8IK5glvloplqTgaY= +github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 h1:Ufq7ObSy69KSBv5hBouVcuk6h2IuLM6WDm3AgM9cN9I= +github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a h1:2pQeQmBfxA7AgmBGtEkPMWsdH5+E74VTvRpuFgc5cu8= +github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= +github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -19,11 +19,10 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed h1:AMm8KKtfeEhUlj45DYJBSMW2VcLO1Tss3jaMUqb+VvE= -github.com/holoplot/go-avahi v0.0.0-20231130181535-fd8f1d0307ed/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= -github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/holoplot/go-avahi v0.0.0-20240210093433-b8dc0fc11e7e h1:XOKmPp6CgtFByseoBaL5Ew9b6NWSie+nr6pMFeO0Tvc= +github.com/holoplot/go-avahi v0.0.0-20240210093433-b8dc0fc11e7e/go.mod h1:WRfsMEGa+MvsfqqKmS7Ye1jrnfRW6kfF/CTP9UMZj0Q= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -42,70 +41,26 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k= gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= +golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From d54e409f010fa65f7c25f90fafb6a37c6105a633 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 18 Feb 2024 21:43:52 +0100 Subject: [PATCH 065/227] Refactor - Introduce use case specific interface - Introduce generic event mechanism - Start implementation of EVSECC and EVCC Use Cases --- .github/workflows/default.yml | 5 +- .mockery.yaml | 10 + api/api.go | 51 ++++ cem/cem.go | 37 ++- cmd/democem/democem.go | 32 ++ cmd/democem/evsecc.go | 12 + cmd/democem/logging.go | 50 +++ cmd/democem/service.go | 27 ++ cmd/main.go | 121 +------- go.mod | 4 +- go.sum | 10 +- ucevcc/api.go | 80 +++++ ucevcc/events.go | 138 +++++++++ ucevcc/public.go | 286 ++++++++++++++++++ ucevcc/results.go | 8 + ucevcc/ucevcc.go | 63 ++++ ucevsecc/api.go | 36 +++ ucevsecc/events.go | 79 +++++ ucevsecc/mocks/EvseCCUseCaseInterface.go | 96 ++++++ .../mocks/EvseCCUseCaseReaderInterface.go | 136 +++++++++ ucevsecc/public.go | 81 +++++ ucevsecc/results.go | 8 + ucevsecc/ucevsecc.go | 59 ++++ util/features.go | 46 +++ 24 files changed, 1336 insertions(+), 139 deletions(-) create mode 100644 .mockery.yaml create mode 100644 cmd/democem/democem.go create mode 100644 cmd/democem/evsecc.go create mode 100644 cmd/democem/logging.go create mode 100644 cmd/democem/service.go create mode 100644 ucevcc/api.go create mode 100644 ucevcc/events.go create mode 100644 ucevcc/public.go create mode 100644 ucevcc/results.go create mode 100644 ucevcc/ucevcc.go create mode 100644 ucevsecc/api.go create mode 100644 ucevsecc/events.go create mode 100644 ucevsecc/mocks/EvseCCUseCaseInterface.go create mode 100644 ucevsecc/mocks/EvseCCUseCaseReaderInterface.go create mode 100644 ucevsecc/public.go create mode 100644 ucevsecc/results.go create mode 100644 ucevsecc/ucevsecc.go create mode 100644 util/features.go diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 39ee541..e0c42ae 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -33,7 +33,10 @@ jobs: skip-build-cache: true - name: Test - run: go test -race -v -coverprofile=coverage.out -covermode=atomic ./... + run: go test -race -v -coverprofile=coverage_temp.out -covermode=atomic ./... + + - name: Remove mocks and cmd from coverage + run: grep -v -e "/cemd/mocks/" -e "/cemd/cmd/" coverage_temp.out > coverage.out - name: Send coverage uses: coverallsapp/github-action@v2 diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 0000000..732f4e6 --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,10 @@ +with-expecter: true +inpackage: false +dir: "{{ .InterfaceDir }}/mocks/" +mockname: "{{.InterfaceName}}" +outpkg: "mocks" +filename: "{{.InterfaceName}}.go" +all: true +packages: + github.com/enbility/cemd/api: + github.com/enbility/cemd/evsecc: diff --git a/api/api.go b/api/api.go index 8993425..5f5244b 100644 --- a/api/api.go +++ b/api/api.go @@ -1,9 +1,60 @@ package api import ( + "errors" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) +//go:generate mockery + +// Implemented by CEM +type CemInterface interface { + // Setup the EEBUS service + Setup() error + + // Start the EEBUS service + Start() + + // Shutdown the EEBUS service + Shutdown() + + // Add a use case implementation + AddUseCase(usecase UseCaseInterface) +} + +// Implemented by each UseCase +type UseCaseInterface interface { + // provide the usecase name + UseCaseName() model.UseCaseNameType + + // add the features + AddFeatures() + + // add the usecase + AddUseCase() +} + +// interface for informing the cem about specific events +// for each supported usecase +// +// UseCaseEventType values can be found in the api definition of each +// supported usecase +// +// implemented by the actual CEM, used by UCEvseCCInterface implementation +type UseCaseEventReaderInterface interface { + // Inform about a new usecase specific event + SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event UseCaseEventType) +} + +// type for usecase specfic event names +type UseCaseEventType string + +var ErrNoEvseEntity = errors.New("entity is not an EVSE") +var ErrNoEvEntity = errors.New("entity is not an EV") + // Implemented by *Solutions, used by Cem type SolutionInterface interface { RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any diff --git a/cem/cem.go b/cem/cem.go index f7902f2..4d0d636 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -1,21 +1,24 @@ package cem import ( - "github.com/enbility/eebus-go/api" + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/service" "github.com/enbility/ship-go/logging" "github.com/enbility/spine-go/model" ) // Generic CEM implementation -type CemImpl struct { - Service api.ServiceInterface +type Cem struct { + Service eebusapi.ServiceInterface Currency model.CurrencyType + + usecases []api.UseCaseInterface } -func NewCEM(serviceDescription *api.Configuration, serviceHandler api.ServiceReaderInterface, log logging.LoggingInterface) *CemImpl { - cem := &CemImpl{ +func NewCEM(serviceDescription *eebusapi.Configuration, serviceHandler eebusapi.ServiceReaderInterface, log logging.LoggingInterface) *Cem { + cem := &Cem{ Service: service.NewService(serviceDescription, serviceHandler), Currency: model.CurrencyTypeEur, } @@ -25,19 +28,27 @@ func NewCEM(serviceDescription *api.Configuration, serviceHandler api.ServiceRea return cem } -// Set up the supported usecases and features -func (h *CemImpl) Setup() error { - if err := h.Service.Setup(); err != nil { - return err - } +var _ api.CemInterface = (*Cem)(nil) - return nil +// Set up the eebus service +func (h *Cem) Setup() error { + return h.Service.Setup() } -func (h *CemImpl) Start() { +// Start the EEBUS service +func (h *Cem) Start() { h.Service.Start() } -func (h *CemImpl) Shutdown() { +// Shutdown the EEBUS servic +func (h *Cem) Shutdown() { h.Service.Shutdown() } + +// Add a use case implementation +func (h *Cem) AddUseCase(usecase api.UseCaseInterface) { + h.usecases = append(h.usecases, usecase) + + usecase.AddFeatures() + usecase.AddUseCase() +} diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go new file mode 100644 index 0000000..7d6dc47 --- /dev/null +++ b/cmd/democem/democem.go @@ -0,0 +1,32 @@ +package democem + +import ( + "github.com/enbility/cemd/cem" + "github.com/enbility/cemd/ucevsecc" + eebusapi "github.com/enbility/eebus-go/api" +) + +type DemoCem struct { + cem *cem.Cem +} + +func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { + demo := &DemoCem{} + + demo.cem = cem.NewCEM(configuration, demo, demo) + + return demo +} + +func (d *DemoCem) Setup() error { + if err := d.cem.Setup(); err != nil { + return err + } + + evsecc := ucevsecc.NewUCEvseCC(d.cem.Service, d.cem.Service.LocalService(), d) + d.cem.AddUseCase(evsecc) + + d.cem.Start() + + return nil +} diff --git a/cmd/democem/evsecc.go b/cmd/democem/evsecc.go new file mode 100644 index 0000000..124ee51 --- /dev/null +++ b/cmd/democem/evsecc.go @@ -0,0 +1,12 @@ +package democem + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +var _ api.UseCaseEventReaderInterface = (*DemoCem)(nil) + +// Handle incomfing usecase specific event +func (h *DemoCem) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} diff --git a/cmd/democem/logging.go b/cmd/democem/logging.go new file mode 100644 index 0000000..957219f --- /dev/null +++ b/cmd/democem/logging.go @@ -0,0 +1,50 @@ +package democem + +import ( + "fmt" + "time" +) + +// Logging interface + +func (d *DemoCem) log(level string, args ...interface{}) { + t := time.Now() + fmt.Printf("%s: %s %s", t.Format(time.RFC3339), level, fmt.Sprintln(args...)) +} + +func (d *DemoCem) logf(level, format string, args ...interface{}) { + t := time.Now() + fmt.Printf("%s: %s %s\n", t.Format(time.RFC3339), level, fmt.Sprintf(format, args...)) +} + +func (d *DemoCem) Trace(args ...interface{}) { + d.log("TRACE", args...) +} + +func (d *DemoCem) Tracef(format string, args ...interface{}) { + d.logf("TRACE", format, args...) +} + +func (d *DemoCem) Debug(args ...interface{}) { + d.log("DEBUG", args...) +} + +func (d *DemoCem) Debugf(format string, args ...interface{}) { + d.logf("DEBUG", format, args...) +} + +func (d *DemoCem) Info(args ...interface{}) { + d.log("INFO", args...) +} + +func (d *DemoCem) Infof(format string, args ...interface{}) { + d.logf("INFO", format, args...) +} + +func (d *DemoCem) Error(args ...interface{}) { + d.log("ERROR", args...) +} + +func (d *DemoCem) Errorf(format string, args ...interface{}) { + d.logf("ERROR", format, args...) +} diff --git a/cmd/democem/service.go b/cmd/democem/service.go new file mode 100644 index 0000000..b2ce40b --- /dev/null +++ b/cmd/democem/service.go @@ -0,0 +1,27 @@ +package democem + +import ( + eebusapi "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/logging" +) + +// report the Ship ID of a newly trusted connection +func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { + // we should associated the Ship ID with the SKI and store it + // so the next connection can start trusted + logging.Log().Info("SKI", ski, "has Ship ID:", shipID) +} + +func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} + +func (d *DemoCem) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} + +func (d *DemoCem) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface, entries []shipapi.RemoteService) { +} + +func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} + +func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} + +func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } diff --git a/cmd/main.go b/cmd/main.go index fb32718..332fb96 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,125 +10,12 @@ import ( "syscall" "time" - "github.com/enbility/cemd/api" - "github.com/enbility/cemd/cem" - "github.com/enbility/cemd/emobility" - "github.com/enbility/cemd/grid" - "github.com/enbility/cemd/inverterbatteryvis" - "github.com/enbility/cemd/inverterpvvis" + "github.com/enbility/cemd/cmd/democem" eebusapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" "github.com/enbility/ship-go/cert" - "github.com/enbility/ship-go/logging" "github.com/enbility/spine-go/model" ) -type DemoCem struct { - cem *cem.CemImpl - - emobilityScenario, gridScenario, inverterBatteryVisScenario, inverterPVVisScenario api.SolutionInterface -} - -func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { - demo := &DemoCem{} - - demo.cem = cem.NewCEM(configuration, demo, demo) - - return demo -} - -func (d *DemoCem) Setup() error { - if err := d.cem.Setup(); err != nil { - return err - } - - d.emobilityScenario = emobility.NewEMobilitySolution(d.cem.Service, d.cem.Currency, emobility.EmobilityConfiguration{ - CoordinatedChargingEnabled: true, - }) - d.emobilityScenario.AddFeatures() - d.emobilityScenario.AddUseCases() - - d.gridScenario = grid.NewGridScenario(d.cem.Service) - d.gridScenario.AddFeatures() - d.gridScenario.AddUseCases() - - d.inverterBatteryVisScenario = inverterbatteryvis.NewInverterVisScenario(d.cem.Service) - d.inverterBatteryVisScenario.AddFeatures() - d.inverterBatteryVisScenario.AddUseCases() - - d.inverterPVVisScenario = inverterpvvis.NewInverterVisScenario(d.cem.Service) - d.inverterPVVisScenario.AddFeatures() - d.inverterPVVisScenario.AddUseCases() - - d.cem.Service.Start() - - return nil -} - -// report the Ship ID of a newly trusted connection -func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { - // we should associated the Ship ID with the SKI and store it - // so the next connection can start trusted - logging.Log().Info("SKI", ski, "has Ship ID:", shipID) -} - -func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} - -func (d *DemoCem) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} - -func (d *DemoCem) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface, entries []shipapi.RemoteService) { -} - -func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} - -func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} - -func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } - -// Logging interface - -func (d *DemoCem) log(level string, args ...interface{}) { - t := time.Now() - fmt.Printf("%s: %s %s", t.Format(time.RFC3339), level, fmt.Sprintln(args...)) -} - -func (d *DemoCem) logf(level, format string, args ...interface{}) { - t := time.Now() - fmt.Printf("%s: %s %s\n", t.Format(time.RFC3339), level, fmt.Sprintf(format, args...)) -} - -func (d *DemoCem) Trace(args ...interface{}) { - d.log("TRACE", args...) -} - -func (d *DemoCem) Tracef(format string, args ...interface{}) { - d.logf("TRACE", format, args...) -} - -func (d *DemoCem) Debug(args ...interface{}) { - d.log("DEBUG", args...) -} - -func (d *DemoCem) Debugf(format string, args ...interface{}) { - d.logf("DEBUG", format, args...) -} - -func (d *DemoCem) Info(args ...interface{}) { - d.log("INFO", args...) -} - -func (d *DemoCem) Infof(format string, args ...interface{}) { - d.logf("INFO", format, args...) -} - -func (d *DemoCem) Error(args ...interface{}) { - d.log("ERROR", args...) -} - -func (d *DemoCem) Errorf(format string, args ...interface{}) { - d.logf("ERROR", format, args...) -} - // main app func main() { remoteSki := flag.String("remoteski", "", "The remote device SKI") @@ -176,14 +63,14 @@ func main() { configuration.SetInterfaces(ifaces) } - demo := NewDemoCem(configuration) + demo := democem.NewDemoCem(configuration) if err := demo.Setup(); err != nil { fmt.Println("Error setting up cem: ", err) return } - remoteService := shipapi.NewServiceDetails(*remoteSki) - demo.emobilityScenario.RegisterRemoteDevice(remoteService, nil) + // remoteService := shipapi.NewServiceDetails(*remoteSki) + // demo.emobilityScenario.RegisterRemoteDevice(remoteService, nil) // Clean exit to make sure mdns shutdown is invoked sig := make(chan os.Signal, 1) diff --git a/go.mod b/go.mod index df9b829..8380fe0 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c - github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 - github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a + github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 + github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 diff --git a/go.sum b/go.sum index 955e389..c02fb0b 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c h1:iGmlyQiRkEch2RwO5wl7mCSAlaI7nOn+Ed1OKx6r/uk= github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c/go.mod h1:7nekYlHCIb4nG1rsW0S12/iz/b/8IK5glvloplqTgaY= -github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3 h1:Ufq7ObSy69KSBv5hBouVcuk6h2IuLM6WDm3AgM9cN9I= -github.com/enbility/ship-go v0.0.0-20240210102308-d36d889b00d3/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a h1:2pQeQmBfxA7AgmBGtEkPMWsdH5+E74VTvRpuFgc5cu8= -github.com/enbility/spine-go v0.0.0-20240210102900-9676534a3b0a/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= +github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f h1:VRQSmiN661D+p433V3ufgpg2vI58SJcCnXUbyA1vwn8= +github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -45,8 +45,6 @@ gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8 gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= -golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= diff --git a/ucevcc/api.go b/ucevcc/api.go new file mode 100644 index 0000000..669de1c --- /dev/null +++ b/ucevcc/api.go @@ -0,0 +1,80 @@ +package ucevcc + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +//go:generate mockery + +// interface for the EV Commissioning and Configuration UseCase +type UCEvCCInterface interface { + api.UseCaseInterface + + // Scenario 1 & 8 + + // return if the EV is connected + EVConnected(entity spineapi.EntityRemoteInterface) bool + + // Scenario 2 + + // return the current communication standard type used to communicate between EVSE and EV + EVCommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) + + // Scenario 3 + + // return if the EV supports asymmetric charging + EVAsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) + + // Scenario 4 + + // return the identifications of the currently connected EV or nil if not available + // these can be multiple, e.g. PCID, Mac Address, RFID + EVIdentifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) + + // Scenario 5 + + // the manufacturer data of an EVSE + // returns deviceName, serialNumber, error + EVManufacturerData(ski string, entity spineapi.EntityRemoteInterface) (string, string, error) + + // Scenario 6 + + // return the number of ac connected phases of the EV or 0 if it is unknown + EVConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) + + // return the min, max, default limits for each phase of the connected EV + EVCurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) + + // Scenario 7 + + // is the EV in sleep mode + EVInSleepMode(ski string, entity spineapi.EntityRemoteInterface) (bool, error) +} + +// EV identification +type IdentificationItem struct { + // the identification value + Value string + + // the type of the identification value, e.g. + ValueType model.IdentificationTypeType +} + +const ( + // An EV was connected + UCEvCCEventConnected api.UseCaseEventType = "ucEvConnected" + + // An EV was disconnected + UCEvCCEventDisconnected api.UseCaseEventType = "ucEvDisonnected" + + // EV device configuration data was updated (CommunicationStandard, Asymmetric charging) + UCEvCCEventConfigurationUdpate api.UseCaseEventType = "ucEvConfigurationUpdate" + + // EV manufacturer data was updated + UCEvCCEventManufacturerUpdate api.UseCaseEventType = "ucEvManufacturerUpdate" + + // EV charging power limits updated + UCEvCCEventChargingPowerLimitsUpdate api.UseCaseEventType = "ucEvPowerLimitsUpdate" +) diff --git a/ucevcc/events.go b/ucevcc/events.go new file mode 100644 index 0000000..8c2554a --- /dev/null +++ b/ucevcc/events.go @@ -0,0 +1,138 @@ +package ucevcc + +import ( + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCEvCC) HandleEvent(payload api.EventPayload) { + // only about events from an EVSE entity or device changes for this remote device + + if payload.Entity == nil { + return + } + + entityType := payload.Entity.EntityType() + if entityType != model.EntityTypeTypeEV { + return + } + + switch payload.EventType { + case api.EventTypeDeviceChange: + if payload.ChangeType == api.ElementChangeRemove { + e.evDisconnected(payload.Ski, payload.Entity) + } + + case api.EventTypeEntityChange: + switch payload.ChangeType { + case api.ElementChangeAdd: + e.evConnected(payload.Ski, payload.Entity) + case api.ElementChangeRemove: + e.evDisconnected(payload.Ski, payload.Entity) + } + + case api.EventTypeDataChange: + if payload.ChangeType != api.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.evConfigurationDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceClassificationManufacturerDataType: + e.evManufacturerDataUpdate(payload.Ski, payload.Entity) + case *model.ElectricalConnectionParameterDescriptionListDataType: + e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) + case *model.ElectricalConnectionPermittedValueSetListDataType: + e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) + } + } +} + +// an EVSE was connected +func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, bindings + if evDeviceClassification, err := util.DeviceClassification(e.service, entity); err == nil { + if err := evDeviceClassification.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get manufacturer details + if _, err := evDeviceClassification.RequestManufacturerDetails(); err != nil { + logging.Log().Debug(err) + } + } + + if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if err := evDeviceConfiguration.Subscribe(); err != nil { + logging.Log().Debug(err) + } + // get ev configuration data + if err := evDeviceConfiguration.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + } + + if evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity); err == nil { + if err := evDeviceDiagnosis.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get device diagnosis state + if _, err := evDeviceDiagnosis.RequestState(); err != nil { + logging.Log().Debug(err) + } + } + + if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if err := evElectricalConnection.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get electrical connection parameter + if err := evElectricalConnection.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + + if err := evElectricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Debug(err) + } + + } + + if evIdentification, err := util.Identification(e.service, entity); err == nil { + if err := evIdentification.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get identification + if _, err := evIdentification.RequestValues(); err != nil { + logging.Log().Debug(err) + } + } + + e.reader.SpineEvent(ski, entity, UCEvCCEventConnected) +} + +// an EV was disconnected +func (e *UCEvCC) evDisconnected(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCCEventDisconnected) +} + +// the configuration key Data of an EV was updated +func (e *UCEvCC) evConfigurationDataUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCCEventConfigurationUdpate) +} + +// the manufacturer Data of an EV was updated +func (e *UCEvCC) evManufacturerDataUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCCEventManufacturerUpdate) +} + +// the manufacturer Data of an EV was updated +func (e *UCEvCC) evElectricalParamerDescriptionUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCCEventChargingPowerLimitsUpdate) +} diff --git a/ucevcc/public.go b/ucevcc/public.go new file mode 100644 index 0000000..a2adfbf --- /dev/null +++ b/ucevcc/public.go @@ -0,0 +1,286 @@ +package ucevcc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return if an EV is connected +// +// this includes all required features and +// minimal data being available +func (e *UCEvCC) EVConnected(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || entity.Device() == nil { + return false + } + + remoteDevice := e.service.LocalDevice().RemoteDeviceForSki(entity.Device().Ski()) + if remoteDevice == nil { + return false + } + + // check if the device still has an entity assigned with the provided entities address + return remoteDevice.Entity(entity.Address().Entity) == entity +} + +func (e *UCEvCC) deviceConfigurationValueForKeyName( + entity spineapi.EntityRemoteInterface, + keyname model.DeviceConfigurationKeyNameType, + valueType model.DeviceConfigurationKeyValueTypeType) (any, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return nil, api.ErrNoEvEntity + } + + evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil { + return nil, features.ErrDataNotAvailable + } + + // check if device configuration descriptions has an communication standard key name + _, err = evDeviceConfiguration.GetDescriptionForKeyName(keyname) + if err != nil { + return nil, err + } + + data, err := evDeviceConfiguration.GetKeyValueForKeyName(keyname, valueType) + if err != nil { + return nil, err + } + + if data == nil { + return nil, features.ErrDataNotAvailable + } + + return data, nil +} + +// return the current communication standard type used to communicate between EVSE and EV +// +// if an EV is connected via IEC61851, no ISO15118 specific data can be provided! +// sometimes the connection starts with IEC61851 before it switches +// to ISO15118, and sometimes it falls back again. so the error return is +// never absolut for the whole connection time, except if the use case +// is not supported +// +// the values are not constant and can change due to communication problems, bugs, and +// sometimes communication starts with IEC61851 before it switches to ISO +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - ErrNotSupported if getting the communication standard is not supported +// - and others +func (e *UCEvCC) EVCommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { + unknown := "unknown" + + data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) + if err != nil { + return unknown, err + } + + if data == nil { + return unknown, features.ErrDataNotAvailable + } + + value := data.(*model.DeviceConfigurationKeyValueStringType) + + return string(*value), nil +} + +// return if the EV supports asymmetric charging +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +func (e *UCEvCC) EVAsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeBoolean) + if err != nil { + return false, err + } + + if data == nil { + return false, features.ErrDataNotAvailable + } + + value := data.(*bool) + + return bool(*value), nil +} + +// return the identifications of the currently connected EV or nil if not available +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCEvCC) EVIdentifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return nil, api.ErrNoEvEntity + } + + evIdentification, err := util.Identification(e.service, entity) + if err != nil { + return nil, features.ErrDataNotAvailable + } + + identifications, err := evIdentification.GetValues() + if err != nil { + return nil, err + } + + var ids []IdentificationItem + for _, identification := range identifications { + item := IdentificationItem{} + + typ := identification.IdentificationType + if typ != nil { + item.ValueType = *typ + } + + value := identification.IdentificationValue + if value != nil { + item.Value = string(*value) + } + + ids = append(ids, item) + } + + return ids, nil +} + +// the manufacturer data of an EVSE +// returns deviceName, serialNumber, error +func (e *UCEvCC) EVManufacturerData( + ski string, + entity spineapi.EntityRemoteInterface, +) ( + string, + string, + error, +) { + deviceName := "" + serialNumber := "" + + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return deviceName, serialNumber, api.ErrNoEvEntity + } + + evDeviceClassification, err := util.DeviceClassification(e.service, entity) + if err != nil { + return deviceName, serialNumber, features.ErrDataNotAvailable + } + + data, err := evDeviceClassification.GetManufacturerDetails() + if err != nil { + return deviceName, serialNumber, err + } + + if data.DeviceName != nil { + deviceName = string(*data.DeviceName) + } + + if data.SerialNumber != nil { + serialNumber = string(*data.SerialNumber) + } + + return deviceName, serialNumber, nil +} + +// return the number of ac connected phases of the EV or 0 if it is unknown +func (e *UCEvCC) EVConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return 0, api.ErrNoEvEntity + } + + evElectricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return 0, features.ErrDataNotAvailable + } + + data, err := evElectricalConnection.GetDescriptions() + if err != nil { + return 0, features.ErrDataNotAvailable + } + + for _, item := range data { + if item.ElectricalConnectionId != nil && item.AcConnectedPhases != nil { + return *item.AcConnectedPhases, nil + } + } + + // default to 0 if the value is not available + return 0, nil +} + +// return the min, max, default limits for each phase of the connected EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCEvCC) EVCurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return nil, nil, nil, api.ErrNoEvEntity + } + + evElectricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return nil, nil, nil, features.ErrDataNotAvailable + } + + var resultMin, resultMax, resultDefault []float64 + + for _, phaseName := range util.PhaseNameMapping { + // electricalParameterDescription contains the measured phase for each measurementId + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) + if err != nil || elParamDesc.ParameterId == nil { + continue + } + + dataMin, dataMax, dataDefault, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) + if err != nil { + continue + } + + // Min current data should be derived from min power data + // but as this value is only properly provided via VAS the + // currrent min values can not be trusted. + + resultMin = append(resultMin, dataMin) + resultMax = append(resultMax, dataMax) + resultDefault = append(resultDefault, dataDefault) + } + + if len(resultMin) == 0 { + return nil, nil, nil, features.ErrDataNotAvailable + } + + return resultMin, resultMax, resultDefault, nil +} + +// is the EV in sleep mode +// returns operatingState, lastErrorCode, error +func (e *UCEvCC) EVInSleepMode( + ski string, + entity spineapi.EntityRemoteInterface, +) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { + return false, api.ErrNoEvseEntity + } + + evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) + if err != nil { + return false, err + } + + data, err := evseDeviceDiagnosis.GetState() + if err != nil { + return false, err + } + + if data.OperatingState != nil && + *data.OperatingState == model.DeviceDiagnosisOperatingStateTypeStandby { + return true, nil + } + + return false, nil +} diff --git a/ucevcc/results.go b/ucevcc/results.go new file mode 100644 index 0000000..b6d9599 --- /dev/null +++ b/ucevcc/results.go @@ -0,0 +1,8 @@ +package ucevcc + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCEvCC) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go new file mode 100644 index 0000000..fff1b6d --- /dev/null +++ b/ucevcc/ucevcc.go @@ -0,0 +1,63 @@ +package ucevcc + +import ( + "github.com/enbility/cemd/api" + serviceapi "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCEvCC struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCEvCCInterface = (*UCEvCC)(nil) + +func NewUCEvseCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvCC { + uc := &UCEvCC{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCEvCC) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeEVCommissioningAndConfiguration +} + +func (e *UCEvCC) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeIdentification, + model.FeatureTypeTypeDeviceClassification, + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeLoadControl, + } + + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } +} + +func (e *UCEvCC) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) +} diff --git a/ucevsecc/api.go b/ucevsecc/api.go new file mode 100644 index 0000000..de6bff6 --- /dev/null +++ b/ucevsecc/api.go @@ -0,0 +1,36 @@ +package ucevsecc + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCEvseCCInterface interface { + api.UseCaseInterface + + // the manufacturer data of an EVSE + // returns deviceName, serialNumber, error + EVSEManufacturerData(ski string, entity spineapi.EntityRemoteInterface) (string, string, error) + + // the operating state data of an EVSE + // returns operatingState, lastErrorCode, error + EVSEOperatingState(ski string, entity spineapi.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) +} + +const ( + // An EVSE was connected + UCEvseCCEventConnected api.UseCaseEventType = "ucEvseConnected" + + // An EVSE was disconnected + UCEvseCCEventDisconnected api.UseCaseEventType = "ucEvseDisonnected" + + // EVSE manufacturer data was updated + UCEvseCCEventManufacturerUpdate api.UseCaseEventType = "ucEvseManufacturerUpdate" + + // EVSE operation state was updated + UCEvseCCEventOperationStateUpdate api.UseCaseEventType = "ucEvseOperationStateUpdate" +) diff --git a/ucevsecc/events.go b/ucevsecc/events.go new file mode 100644 index 0000000..f91391b --- /dev/null +++ b/ucevsecc/events.go @@ -0,0 +1,79 @@ +package ucevsecc + +import ( + "github.com/enbility/eebus-go/features" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCEvseCC) HandleEvent(payload api.EventPayload) { + // only about events from an EVSE entity or device changes for this remote device + + if payload.Entity == nil { + return + } + + entityType := payload.Entity.EntityType() + if entityType != model.EntityTypeTypeEVSE { + return + } + + switch payload.EventType { + case api.EventTypeDeviceChange: + if payload.ChangeType == api.ElementChangeRemove { + e.evseDisconnected(payload.Ski, payload.Entity) + } + + case api.EventTypeEntityChange: + switch payload.ChangeType { + case api.ElementChangeAdd: + e.evseConnected(payload.Ski, payload.Entity) + case api.ElementChangeRemove: + e.evseDisconnected(payload.Ski, payload.Entity) + } + + case api.EventTypeDataChange: + if payload.ChangeType != api.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.DeviceClassificationManufacturerDataType: + e.evseManufacturerDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceDiagnosisStateDataType: + e.evseStateUpdate(payload.Ski, payload.Entity) + } + } +} + +// an EVSE was connected +func (e *UCEvseCC) evseConnected(ski string, entity api.EntityRemoteInterface) { + localDevice := e.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + if evseDeviceClassification, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { + _, _ = evseDeviceClassification.RequestManufacturerDetails() + } + + if evseDeviceDiagnosis, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { + _, _ = evseDeviceDiagnosis.RequestState() + } + + e.reader.SpineEvent(ski, entity, UCEvseCCEventConnected) +} + +// an EVSE was disconnected +func (e *UCEvseCC) evseDisconnected(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvseCCEventDisconnected) +} + +// the manufacturer Data of an EVSE was updated +func (e *UCEvseCC) evseManufacturerDataUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvseCCEventManufacturerUpdate) +} + +// the operating State of an EVSE was updated +func (e *UCEvseCC) evseStateUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvseCCEventOperationStateUpdate) +} diff --git a/ucevsecc/mocks/EvseCCUseCaseInterface.go b/ucevsecc/mocks/EvseCCUseCaseInterface.go new file mode 100644 index 0000000..f187b60 --- /dev/null +++ b/ucevsecc/mocks/EvseCCUseCaseInterface.go @@ -0,0 +1,96 @@ +// Code generated by mockery v2.40.3. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// EvseCCUseCaseInterface is an autogenerated mock type for the EvseCCUseCaseInterface type +type EvseCCUseCaseInterface struct { + mock.Mock +} + +type EvseCCUseCaseInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *EvseCCUseCaseInterface) EXPECT() *EvseCCUseCaseInterface_Expecter { + return &EvseCCUseCaseInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *EvseCCUseCaseInterface) AddFeatures() { + _m.Called() +} + +// EvseCCUseCaseInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type EvseCCUseCaseInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *EvseCCUseCaseInterface_Expecter) AddFeatures() *EvseCCUseCaseInterface_AddFeatures_Call { + return &EvseCCUseCaseInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *EvseCCUseCaseInterface_AddFeatures_Call) Run(run func()) *EvseCCUseCaseInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EvseCCUseCaseInterface_AddFeatures_Call) Return() *EvseCCUseCaseInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *EvseCCUseCaseInterface_AddFeatures_Call) RunAndReturn(run func()) *EvseCCUseCaseInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *EvseCCUseCaseInterface) AddUseCase() { + _m.Called() +} + +// EvseCCUseCaseInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type EvseCCUseCaseInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *EvseCCUseCaseInterface_Expecter) AddUseCase() *EvseCCUseCaseInterface_AddUseCase_Call { + return &EvseCCUseCaseInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *EvseCCUseCaseInterface_AddUseCase_Call) Run(run func()) *EvseCCUseCaseInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EvseCCUseCaseInterface_AddUseCase_Call) Return() *EvseCCUseCaseInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *EvseCCUseCaseInterface_AddUseCase_Call) RunAndReturn(run func()) *EvseCCUseCaseInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// NewEvseCCUseCaseInterface creates a new instance of EvseCCUseCaseInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEvseCCUseCaseInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *EvseCCUseCaseInterface { + mock := &EvseCCUseCaseInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/ucevsecc/mocks/EvseCCUseCaseReaderInterface.go b/ucevsecc/mocks/EvseCCUseCaseReaderInterface.go new file mode 100644 index 0000000..b32aab5 --- /dev/null +++ b/ucevsecc/mocks/EvseCCUseCaseReaderInterface.go @@ -0,0 +1,136 @@ +// Code generated by mockery v2.40.3. DO NOT EDIT. + +package mocks + +import ( + model "github.com/enbility/spine-go/model" + mock "github.com/stretchr/testify/mock" +) + +// EvseCCUseCaseReaderInterface is an autogenerated mock type for the EvseCCUseCaseReaderInterface type +type EvseCCUseCaseReaderInterface struct { + mock.Mock +} + +type EvseCCUseCaseReaderInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *EvseCCUseCaseReaderInterface) EXPECT() *EvseCCUseCaseReaderInterface_Expecter { + return &EvseCCUseCaseReaderInterface_Expecter{mock: &_m.Mock} +} + +// EVSEConnected provides a mock function with given fields: ski +func (_m *EvseCCUseCaseReaderInterface) EVSEConnected(ski string) { + _m.Called(ski) +} + +// EvseCCUseCaseReaderInterface_EVSEConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVSEConnected' +type EvseCCUseCaseReaderInterface_EVSEConnected_Call struct { + *mock.Call +} + +// EVSEConnected is a helper method to define mock.On call +// - ski string +func (_e *EvseCCUseCaseReaderInterface_Expecter) EVSEConnected(ski interface{}) *EvseCCUseCaseReaderInterface_EVSEConnected_Call { + return &EvseCCUseCaseReaderInterface_EVSEConnected_Call{Call: _e.mock.On("EVSEConnected", ski)} +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEConnected_Call) Run(run func(ski string)) *EvseCCUseCaseReaderInterface_EVSEConnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEConnected_Call) Return() *EvseCCUseCaseReaderInterface_EVSEConnected_Call { + _c.Call.Return() + return _c +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEConnected_Call) RunAndReturn(run func(string)) *EvseCCUseCaseReaderInterface_EVSEConnected_Call { + _c.Call.Return(run) + return _c +} + +// EVSEDisconnected provides a mock function with given fields: ski +func (_m *EvseCCUseCaseReaderInterface) EVSEDisconnected(ski string) { + _m.Called(ski) +} + +// EvseCCUseCaseReaderInterface_EVSEDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVSEDisconnected' +type EvseCCUseCaseReaderInterface_EVSEDisconnected_Call struct { + *mock.Call +} + +// EVSEDisconnected is a helper method to define mock.On call +// - ski string +func (_e *EvseCCUseCaseReaderInterface_Expecter) EVSEDisconnected(ski interface{}) *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { + return &EvseCCUseCaseReaderInterface_EVSEDisconnected_Call{Call: _e.mock.On("EVSEDisconnected", ski)} +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call) Run(run func(ski string)) *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call) Return() *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { + _c.Call.Return() + return _c +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call) RunAndReturn(run func(string)) *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { + _c.Call.Return(run) + return _c +} + +// EVSEOperatingStateChanged provides a mock function with given fields: ski, operatingState, lastErrorCode +func (_m *EvseCCUseCaseReaderInterface) EVSEOperatingStateChanged(ski string, operatingState *model.DeviceDiagnosisOperatingStateType, lastErrorCode *model.LastErrorCodeType) { + _m.Called(ski, operatingState, lastErrorCode) +} + +// EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVSEOperatingStateChanged' +type EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call struct { + *mock.Call +} + +// EVSEOperatingStateChanged is a helper method to define mock.On call +// - ski string +// - operatingState *model.DeviceDiagnosisOperatingStateType +// - lastErrorCode *model.LastErrorCodeType +func (_e *EvseCCUseCaseReaderInterface_Expecter) EVSEOperatingStateChanged(ski interface{}, operatingState interface{}, lastErrorCode interface{}) *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { + return &EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call{Call: _e.mock.On("EVSEOperatingStateChanged", ski, operatingState, lastErrorCode)} +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call) Run(run func(ski string, operatingState *model.DeviceDiagnosisOperatingStateType, lastErrorCode *model.LastErrorCodeType)) *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(*model.DeviceDiagnosisOperatingStateType), args[2].(*model.LastErrorCodeType)) + }) + return _c +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call) Return() *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { + _c.Call.Return() + return _c +} + +func (_c *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call) RunAndReturn(run func(string, *model.DeviceDiagnosisOperatingStateType, *model.LastErrorCodeType)) *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { + _c.Call.Return(run) + return _c +} + +// NewEvseCCUseCaseReaderInterface creates a new instance of EvseCCUseCaseReaderInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEvseCCUseCaseReaderInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *EvseCCUseCaseReaderInterface { + mock := &EvseCCUseCaseReaderInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/ucevsecc/public.go b/ucevsecc/public.go new file mode 100644 index 0000000..8793fab --- /dev/null +++ b/ucevsecc/public.go @@ -0,0 +1,81 @@ +package ucevsecc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// the manufacturer data of an EVSE +// returns deviceName, serialNumber, error +func (e *UCEvseCC) EVSEManufacturerData( + ski string, + entity spineapi.EntityRemoteInterface, +) ( + string, + string, + error, +) { + deviceName := "" + serialNumber := "" + + if entity.EntityType() != model.EntityTypeTypeEVSE { + return deviceName, serialNumber, api.ErrNoEvseEntity + } + + evseDeviceClassification, err := util.DeviceClassification(e.service, entity) + if err != nil { + return deviceName, serialNumber, err + } + + data, err := evseDeviceClassification.GetManufacturerDetails() + if err != nil { + return deviceName, serialNumber, err + } + + if data.DeviceName != nil { + deviceName = string(*data.DeviceName) + } + + if data.SerialNumber != nil { + serialNumber = string(*data.SerialNumber) + } + + return deviceName, serialNumber, nil +} + +// the operating state data of an EVSE +// returns operatingState, lastErrorCode, error +func (e *UCEvseCC) EVSEOperatingState( + ski string, + entity spineapi.EntityRemoteInterface, +) ( + model.DeviceDiagnosisOperatingStateType, string, error, +) { + operatingState := model.DeviceDiagnosisOperatingStateTypeNormalOperation + lastErrorCode := "" + + if entity.EntityType() != model.EntityTypeTypeEVSE { + return operatingState, lastErrorCode, api.ErrNoEvseEntity + } + + evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) + if err != nil { + return operatingState, lastErrorCode, err + } + + data, err := evseDeviceDiagnosis.GetState() + if err != nil { + return operatingState, lastErrorCode, err + } + + if data.OperatingState != nil { + operatingState = *data.OperatingState + } + if data.LastErrorCode != nil { + lastErrorCode = string(*data.LastErrorCode) + } + + return operatingState, lastErrorCode, nil +} diff --git a/ucevsecc/results.go b/ucevsecc/results.go new file mode 100644 index 0000000..4df4bd3 --- /dev/null +++ b/ucevsecc/results.go @@ -0,0 +1,8 @@ +package ucevsecc + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCEvseCC) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go new file mode 100644 index 0000000..05d9e15 --- /dev/null +++ b/ucevsecc/ucevsecc.go @@ -0,0 +1,59 @@ +package ucevsecc + +import ( + "github.com/enbility/cemd/api" + serviceapi "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCEvseCC struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCEvseCCInterface = (*UCEvseCC)(nil) + +func NewUCEvseCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvseCC { + uc := &UCEvseCC{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCEvseCC) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeEVSECommissioningAndConfiguration +} + +func (e *UCEvseCC) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceClassification, + model.FeatureTypeTypeDeviceDiagnosis, + } + + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } +} + +func (e *UCEvseCC) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2}) +} diff --git a/util/features.go b/util/features.go new file mode 100644 index 0000000..5a0d6b0 --- /dev/null +++ b/util/features.go @@ -0,0 +1,46 @@ +package util + +import ( + eebusapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +func localCemEntity(service eebusapi.ServiceInterface) api.EntityLocalInterface { + localDevice := service.LocalDevice() + + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + return localEntity +} + +func DeviceClassification(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceClassification, error) { + localEntity := localCemEntity(service) + + return features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func DeviceConfiguration(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceConfiguration, error) { + localEntity := localCemEntity(service) + + return features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func DeviceDiagnosis(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceDiagnosis, error) { + localEntity := localCemEntity(service) + + return features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func ElectricalConnection(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.ElectricalConnection, error) { + localEntity := localCemEntity(service) + + return features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func Identification(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.Identification, error) { + localEntity := localCemEntity(service) + + return features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} From 9d1759b676ec3b882a8b866ab87e384311575226 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 18 Feb 2024 21:51:45 +0100 Subject: [PATCH 066/227] Update eebus --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8380fe0..da7673a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c + github.com/enbility/eebus-go v0.0.0-20240218151147-ff26e3c2d343 github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index c02fb0b..71fa9e2 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c h1:iGmlyQiRkEch2RwO5wl7mCSAlaI7nOn+Ed1OKx6r/uk= -github.com/enbility/eebus-go v0.0.0-20240210103202-53e41447262c/go.mod h1:7nekYlHCIb4nG1rsW0S12/iz/b/8IK5glvloplqTgaY= +github.com/enbility/eebus-go v0.0.0-20240218151147-ff26e3c2d343 h1:cSGEBuoF7YRReesFyeuk4+vnUEsfT7A7+iF6AlKLPX8= +github.com/enbility/eebus-go v0.0.0-20240218151147-ff26e3c2d343/go.mod h1:4SLI8oQxVOXPhAVh0Nbd74zNkgoydd8WwkffqTUjI1Y= github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f h1:VRQSmiN661D+p433V3ufgpg2vI58SJcCnXUbyA1vwn8= From f9e63669c8108b4c196fed764ef091784a777c96 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 19 Feb 2024 20:23:46 +0100 Subject: [PATCH 067/227] Add another usecase, move tests, some cleanup --- emobility/api.go | 55 -- emobility/evcoordinatedcharging_test.go | 5 +- emobility/events.go | 36 +- emobility/public.go | 216 ------ emobility/public_EVChargedEnergy_test.go | 77 --- .../public_EVCommunicationStandard_test.go | 94 --- emobility/public_EVConnectedPhases_test.go | 73 -- emobility/public_EVCurrentLimits_test.go | 184 ----- emobility/public_EVCurrentsPerPhase_test.go | 101 --- emobility/public_EVIdentification_test.go | 52 -- emobility/public_EVPowerPerPhase_test.go | 187 ----- go.mod | 6 + go.sum | 6 - grid/events.go | 12 +- inverterbatteryvis/events.go | 10 +- inverterpvvis/events.go | 14 +- ucevcc/api.go | 15 +- ucevcc/events.go | 53 +- ucevcc/public.go | 6 +- ucevcc/public_test.go | 643 ++++++++++++++++++ ucevcc/ucevcc.go | 2 +- ucevcem/api.go | 33 + ucevcem/events.go | 77 +++ ucevcem/public.go | 162 +++++ ucevcem/public_test.go | 433 ++++++++++++ ucevcem/results.go | 8 + ucevcem/ucevcem.go | 52 ++ ucevsecc/api.go | 4 +- ucevsecc/mocks/EvseCCUseCaseInterface.go | 96 --- .../mocks/EvseCCUseCaseReaderInterface.go | 136 ---- ucevsecc/public.go | 6 +- ucevsecc/public_test.go | 262 +++++++ util/features.go | 6 + 33 files changed, 1775 insertions(+), 1347 deletions(-) delete mode 100644 emobility/public_EVChargedEnergy_test.go delete mode 100644 emobility/public_EVCommunicationStandard_test.go delete mode 100644 emobility/public_EVConnectedPhases_test.go delete mode 100644 emobility/public_EVCurrentLimits_test.go delete mode 100644 emobility/public_EVCurrentsPerPhase_test.go delete mode 100644 emobility/public_EVIdentification_test.go delete mode 100644 emobility/public_EVPowerPerPhase_test.go create mode 100644 ucevcc/public_test.go create mode 100644 ucevcem/api.go create mode 100644 ucevcem/events.go create mode 100644 ucevcem/public.go create mode 100644 ucevcem/public_test.go create mode 100644 ucevcem/results.go create mode 100644 ucevcem/ucevcem.go delete mode 100644 ucevsecc/mocks/EvseCCUseCaseInterface.go delete mode 100644 ucevsecc/mocks/EvseCCUseCaseReaderInterface.go create mode 100644 ucevsecc/public_test.go diff --git a/emobility/api.go b/emobility/api.go index cfe097f..28879a8 100644 --- a/emobility/api.go +++ b/emobility/api.go @@ -50,37 +50,6 @@ type EMobilityInterface interface { // return the current charge state of the EV EVCurrentChargeState(remoteEntity api.EntityRemoteInterface) (EVChargeStateType, error) - // return the number of ac connected phases of the EV or 0 if it is unknown - EVConnectedPhases(remoteEntity api.EntityRemoteInterface) (uint, error) - - // return the charged energy measurement in Wh of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVChargedEnergy(remoteEntity api.EntityRemoteInterface) (float64, error) - - // return the last power measurement for each phase of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVPowerPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) - - // return the last current measurement for each phase of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVCurrentsPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) - - // return the min, max, default limits for each phase of the connected EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVCurrentLimits(remoteEntity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) - // return the current loadcontrol obligation limits // // possible errors: @@ -117,30 +86,6 @@ type EMobilityInterface interface { // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. EVWriteLoadControlLimits(remoteEntity api.EntityRemoteInterface, limits []EVLoadLimits) error - // return the current communication standard type used to communicate between EVSE and EV - // - // if an EV is connected via IEC61851, no ISO15118 specific data can be provided! - // sometimes the connection starts with IEC61851 before it switches - // to ISO15118, and sometimes it falls back again. so the error return is - // never absolut for the whole connection time, except if the use case - // is not supported - // - // the values are not constant and can change due to communication problems, bugs, and - // sometimes communication starts with IEC61851 before it switches to ISO - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - ErrNotSupported if getting the communication standard is not supported - // - and others - EVCommunicationStandard(remoteEntity api.EntityRemoteInterface) (EVCommunicationStandardType, error) - - // returns the identification of the currently connected EV or nil if not available - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - and others - EVIdentification(remoteEntity api.EntityRemoteInterface) (string, error) - // returns if the EVSE and EV combination support optimzation of self consumption // // possible errors: diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go index 94e1748..f9b8fd3 100644 --- a/emobility/evcoordinatedcharging_test.go +++ b/emobility/evcoordinatedcharging_test.go @@ -21,9 +21,6 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobility.EVChargedEnergy(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) emobility.evseEntity = entites[0] @@ -64,7 +61,7 @@ func Test_CoordinatedChargingScenarios(t *testing.T) { datagramtt.Payload.Cmd = cmd - err = localDevice.ProcessCmd(datagramtt, remoteDevice) + err := localDevice.ProcessCmd(datagramtt, remoteDevice) assert.Nil(t, err) demand, err := emobility.EVEnergyDemand(emobility.evEntity) diff --git a/emobility/events.go b/emobility/events.go index e90398e..187bce3 100644 --- a/emobility/events.go +++ b/emobility/events.go @@ -184,7 +184,7 @@ func (e *EMobility) HandleEvent(payload api.EventPayload) { switch *payload.CmdClassifier { case model.CmdClassifierTypeReply: - if err := evIncentiveTable.RequestConstraints(); err == nil { + if _, err := evIncentiveTable.RequestConstraints(); err == nil { break } @@ -361,7 +361,7 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { // initialise features, e.g. subscriptions, bindings if evDeviceClassification, err := e.deviceClassification(entity); err == nil { - if err := evDeviceClassification.Subscribe(); err != nil { + if _, err := evDeviceClassification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -372,17 +372,17 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evDeviceConfiguration, err := e.deviceConfiguration(entity); err == nil { - if err := evDeviceConfiguration.Subscribe(); err != nil { + if _, err := evDeviceConfiguration.Subscribe(); err != nil { logging.Log().Debug(err) } // get ev configuration data - if err := evDeviceConfiguration.RequestDescriptions(); err != nil { + if _, err := evDeviceConfiguration.RequestDescriptions(); err != nil { logging.Log().Debug(err) } } if evDeviceDiagnosis, err := e.deviceDiagnosis(entity); err == nil { - if err := evDeviceDiagnosis.Subscribe(); err != nil { + if _, err := evDeviceDiagnosis.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -393,23 +393,23 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evElectricalConnection, err := e.electricalConnection(entity); err == nil { - if err := evElectricalConnection.Subscribe(); err != nil { + if _, err := evElectricalConnection.Subscribe(); err != nil { logging.Log().Debug(err) } // get electrical connection parameter - if err := evElectricalConnection.RequestDescriptions(); err != nil { + if _, err := evElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Debug(err) } - if err := evElectricalConnection.RequestParameterDescriptions(); err != nil { + if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Debug(err) } } if evMeasurement, err := e.measurement(entity); err == nil { - if err := evMeasurement.Subscribe(); err != nil { + if _, err := evMeasurement.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -421,23 +421,23 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evLoadControl, err := e.loadControl(entity); err == nil { - if err := evLoadControl.Subscribe(); err != nil { + if _, err := evLoadControl.Subscribe(); err != nil { logging.Log().Debug(err) } - if err := evLoadControl.Bind(); err != nil { + if _, err := evLoadControl.Bind(); err != nil { logging.Log().Debug(err) } // get loadlimit parameter - if err := evLoadControl.RequestLimitDescriptions(); err != nil { + if _, err := evLoadControl.RequestLimitDescriptions(); err != nil { logging.Log().Debug(err) } } if evIdentification, err := e.identification(entity); err == nil { - if err := evIdentification.Subscribe(); err != nil { + if _, err := evIdentification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -449,11 +449,11 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { if e.configuration.CoordinatedChargingEnabled { if evTimeSeries, err := e.timeSeries(entity); err == nil { - if err := evTimeSeries.Subscribe(); err != nil { + if _, err := evTimeSeries.Subscribe(); err != nil { logging.Log().Debug(err) } - if err := evTimeSeries.Bind(); err != nil { + if _, err := evTimeSeries.Bind(); err != nil { logging.Log().Debug(err) } @@ -465,16 +465,16 @@ func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { } if evIncentiveTable, err := e.incentiveTable(entity); err == nil { - if err := evIncentiveTable.Subscribe(); err != nil { + if _, err := evIncentiveTable.Subscribe(); err != nil { logging.Log().Debug(err) } - if err := evIncentiveTable.Bind(); err != nil { + if _, err := evIncentiveTable.Bind(); err != nil { logging.Log().Debug(err) } // get incentive table parameter - if err := evIncentiveTable.RequestDescriptions(); err != nil { + if _, err := evIncentiveTable.RequestDescriptions(); err != nil { logging.Log().Debug(err) } diff --git a/emobility/public.go b/emobility/public.go index 3f55b5d..e645285 100644 --- a/emobility/public.go +++ b/emobility/public.go @@ -32,12 +32,6 @@ func (e *EMobility) EVConnected(remoteEntity api.EntityRemoteInterface) bool { return false } - // getting currents measurements should work - if _, err := e.EVCurrentsPerPhase(remoteEntity); err != nil && err != features.ErrDataNotAvailable { - // features.ErrDataNotAvailable check in case of measurements not being provided but the feature works - return false - } - // getting limits should work if _, err := e.EVLoadControlObligationLimits(remoteEntity); err != nil && err != features.ErrDataNotAvailable { // features.ErrDataNotAvailable check in case of load control limits not being provided but the feature works @@ -79,216 +73,6 @@ func (e *EMobility) EVCurrentChargeState(remoteEntity api.EntityRemoteInterface) return EVChargeStateTypeUnknown, nil } -// return the number of ac connected phases of the EV or 0 if it is unknown -func (e *EMobility) EVConnectedPhases(remoteEntity api.EntityRemoteInterface) (uint, error) { - evElectricalConnection, err := e.electricalConnection(remoteEntity) - - if e.evEntity == nil || err != nil { - return 0, ErrEVDisconnected - } - - data, err := evElectricalConnection.GetDescriptions() - if err != nil { - return 0, features.ErrDataNotAvailable - } - - for _, item := range data { - if item.ElectricalConnectionId == nil { - continue - } - - if item.AcConnectedPhases != nil { - return *item.AcConnectedPhases, nil - } - } - - // default to 3 if the value is not available - return 3, nil -} - -// return the charged energy measurement in Wh of the connected EV -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVChargedEnergy(remoteEntity api.EntityRemoteInterface) (float64, error) { - evMeasurement, err := e.measurement(remoteEntity) - - if e.evEntity == nil || err != nil { - return 0, ErrEVDisconnected - } - - measurement := model.MeasurementTypeTypeEnergy - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeCharge - data, err := evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // we assume there is only one result - value := data[0].Value - if value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), err -} - -// return the last power measurement for each phase of the connected EV -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVPowerPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) { - evMeasurement, err := e.measurement(remoteEntity) - evElectricalConnection, err2 := e.electricalConnection(remoteEntity) - - if e.evEntity == nil || err != nil || err2 != nil { - return nil, ErrEVDisconnected - } - - var data []model.MeasurementDataType - - powerAvailable := true - measurement := model.MeasurementTypeTypePower - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACPower - data, err = evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil || len(data) == 0 { - powerAvailable = false - - // If power is not provided, fall back to power calculations via currents - measurement = model.MeasurementTypeTypeCurrent - scope = model.ScopeTypeTypeACCurrent - data, err = evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return nil, err - } - } - - var result []float64 - - for _, phase := range util.PhaseNameMapping { - for _, item := range data { - if item.Value == nil { - continue - } - - elParam, err := evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || elParam.AcMeasuredPhases == nil || *elParam.AcMeasuredPhases != phase { - continue - } - - phaseValue := item.Value.GetValue() - if !powerAvailable { - phaseValue *= e.service.Configuration().Voltage() - } - - result = append(result, phaseValue) - } - } - - return result, nil -} - -// return the last current measurement for each phase of the connected EV -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVCurrentsPerPhase(remoteEntity api.EntityRemoteInterface) ([]float64, error) { - evMeasurement, err := e.measurement(remoteEntity) - evElectricalConnection, err2 := e.electricalConnection(remoteEntity) - - if e.evEntity == nil || err != nil || err2 != nil { - return nil, ErrEVDisconnected - } - - measurement := model.MeasurementTypeTypeCurrent - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACCurrent - data, err := evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return nil, err - } - - var result []float64 - refetch := true - compare := time.Now().Add(-1 * time.Minute) - - for _, phase := range util.PhaseNameMapping { - for _, item := range data { - if item.Value == nil { - continue - } - - elParam, err := evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || elParam.AcMeasuredPhases == nil || *elParam.AcMeasuredPhases != phase { - continue - } - - phaseValue := item.Value.GetValue() - result = append(result, phaseValue) - - if item.Timestamp != nil { - if timestamp, err := item.Timestamp.GetTime(); err == nil { - refetch = timestamp.Before(compare) - } - } - } - } - - // if there was no timestamp provided or the time for the last value - // is older than 1 minute, send a read request - if refetch { - _, _ = evMeasurement.RequestValues() - } - - return result, nil -} - -// return the min, max, default limits for each phase of the connected EV -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVCurrentLimits(remoteEntity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) { - evElectricalConnection, err := e.electricalConnection(remoteEntity) - - if e.evEntity == nil || err != nil { - return nil, nil, nil, ErrEVDisconnected - } - - var resultMin, resultMax, resultDefault []float64 - - for _, phaseName := range util.PhaseNameMapping { - // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) - if err != nil || elParamDesc.ParameterId == nil { - continue - } - - dataMin, dataMax, dataDefault, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) - if err != nil { - continue - } - - // Min current data should be derived from min power data - // but as this value is only properly provided via VAS the - // currrent min values can not be trusted. - - resultMin = append(resultMin, dataMin) - resultMax = append(resultMax, dataMax) - resultDefault = append(resultDefault, dataDefault) - } - - if len(resultMin) == 0 { - return nil, nil, nil, features.ErrDataNotAvailable - } - return resultMin, resultMax, resultDefault, nil -} - // return the current loadcontrol obligation limits // // possible errors: diff --git a/emobility/public_EVChargedEnergy_test.go b/emobility/public_EVChargedEnergy_test.go deleted file mode 100644 index 0c8964d..0000000 --- a/emobility/public_EVChargedEnergy_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVChargedEnergy(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVChargedEnergy(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVChargedEnergy(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - data, err = emobilty.EVChargedEnergy(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), - CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), - ScopeType: util.Ptr(model.ScopeTypeTypeCharge), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVChargedEnergy(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - cmd = []model.CmdType{{ - MeasurementListData: &model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - Value: model.NewScaledNumberType(80), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVChargedEnergy(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 80.0, data) -} diff --git a/emobility/public_EVCommunicationStandard_test.go b/emobility/public_EVCommunicationStandard_test.go deleted file mode 100644 index 5acc398..0000000 --- a/emobility/public_EVCommunicationStandard_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVCommunicationStandard(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVCommunicationStandard(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - - data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - - cmd = []model.CmdType{{ - DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, EVCommunicationStandardTypeUnknown, data) - - cmd = []model.CmdType{{ - DeviceConfigurationKeyValueListData: &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - Value: &model.DeviceConfigurationKeyValueValueType{ - String: util.Ptr(model.DeviceConfigurationKeyValueStringType(EVCommunicationStandardTypeISO151182ED1)), - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCommunicationStandard(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, EVCommunicationStandardTypeISO151182ED1, data) -} diff --git a/emobility/public_EVConnectedPhases_test.go b/emobility/public_EVConnectedPhases_test.go deleted file mode 100644 index 8fe69fa..0000000 --- a/emobility/public_EVConnectedPhases_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVConnectedPhases(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVConnectedPhases(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, uint(0), data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVConnectedPhases(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, uint(0), data) - - data, err = emobilty.EVConnectedPhases(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, uint(0), data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - ElectricalConnectionDescriptionListData: &model.ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVConnectedPhases(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, uint(3), data) - - cmd = []model.CmdType{{ - ElectricalConnectionDescriptionListData: &model.ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - AcConnectedPhases: util.Ptr(uint(1)), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVConnectedPhases(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, uint(1), data) -} diff --git a/emobility/public_EVCurrentLimits_test.go b/emobility/public_EVCurrentLimits_test.go deleted file mode 100644 index 996d9ce..0000000 --- a/emobility/public_EVCurrentLimits_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVCurrentLimits(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - minData, maxData, defaultData, err := emobilty.EVCurrentLimits(mockRemoteEntity) - assert.NotNil(t, err) - assert.Nil(t, minData) - assert.Nil(t, maxData) - assert.Nil(t, defaultData) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, minData) - assert.Nil(t, maxData) - assert.Nil(t, defaultData) - - minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, minData) - assert.Nil(t, maxData) - assert.Nil(t, defaultData) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - }, - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), - }, - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), - MeasurementId: util.Ptr(model.MeasurementIdType(2)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, minData) - assert.Nil(t, maxData) - assert.Nil(t, defaultData) - - type permittedStruct struct { - defaultExists bool - defaultValue, expectedDefaultValue float64 - minValue, expectedMinValue float64 - maxValue, expectedMaxValue float64 - } - - tests := []struct { - name string - permitted []permittedStruct - }{ - { - "1 Phase ISO15118", - []permittedStruct{ - {true, 0.1, 0.1, 2, 2, 16, 16}, - }, - }, - { - "1 Phase IEC61851", - []permittedStruct{ - {true, 0.0, 0.0, 6, 6, 16, 16}, - }, - }, - { - "1 Phase IEC61851 Elli", - []permittedStruct{ - {false, 0.0, 0.0, 6, 6, 16, 16}, - }, - }, - { - "3 Phase ISO15118", - []permittedStruct{ - {true, 0.1, 0.1, 2, 2, 16, 16}, - {true, 0.1, 0.1, 2, 2, 16, 16}, - {true, 0.1, 0.1, 2, 2, 16, 16}, - }, - }, - { - "3 Phase IEC61851", - []permittedStruct{ - {true, 0.0, 0.0, 6, 6, 16, 16}, - {true, 0.0, 0.0, 6, 6, 16, 16}, - {true, 0.0, 0.0, 6, 6, 16, 16}, - }, - }, - { - "3 Phase IEC61851 Elli", - []permittedStruct{ - {false, 0.0, 0.0, 6, 6, 16, 16}, - {false, 0.0, 0.0, 6, 6, 16, 16}, - {false, 0.0, 0.0, 6, 6, 16, 16}, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} - permittedData := []model.ScaledNumberSetType{} - for index, data := range tc.permitted { - item := model.ScaledNumberSetType{ - Range: []model.ScaledNumberRangeType{ - { - Min: model.NewScaledNumberType(data.minValue), - Max: model.NewScaledNumberType(data.maxValue), - }, - }, - } - if data.defaultExists { - item.Value = []model.ScaledNumberType{*model.NewScaledNumberType(data.defaultValue)} - } - permittedData = append(permittedData, item) - - permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(index)), - PermittedValueSet: permittedData, - } - dataSet = append(dataSet, permittedItem) - } - - cmd = []model.CmdType{{ - ElectricalConnectionPermittedValueSetListData: &model.ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: dataSet, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - minData, maxData, defaultData, err = emobilty.EVCurrentLimits(emobilty.evEntity) - assert.Nil(t, err) - - assert.Nil(t, err) - assert.Equal(t, len(tc.permitted), len(minData)) - assert.Equal(t, len(tc.permitted), len(maxData)) - assert.Equal(t, len(tc.permitted), len(defaultData)) - for index, item := range tc.permitted { - assert.Equal(t, item.expectedMinValue, minData[index]) - assert.Equal(t, item.expectedMaxValue, maxData[index]) - assert.Equal(t, item.expectedDefaultValue, defaultData[index]) - } - }) - } -} diff --git a/emobility/public_EVCurrentsPerPhase_test.go b/emobility/public_EVCurrentsPerPhase_test.go deleted file mode 100644 index bc35230..0000000 --- a/emobility/public_EVCurrentsPerPhase_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVCurrentsPerPhase(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVCurrentsPerPhase(mockRemoteEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), - CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - cmd = []model.CmdType{{ - MeasurementListData: &model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - Value: model.NewScaledNumberType(10), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentsPerPhase(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 10.0, data[0]) -} diff --git a/emobility/public_EVIdentification_test.go b/emobility/public_EVIdentification_test.go deleted file mode 100644 index ef7d872..0000000 --- a/emobility/public_EVIdentification_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" -) - -func Test_EVIdentification(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - data, err := emobilty.EVIdentification(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, "", data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVIdentification(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, "", data) - - data, err = emobilty.EVIdentification(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, "", data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIdentification, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - IdentificationListData: &model.IdentificationListDataType{ - IdentificationData: []model.IdentificationDataType{ - { - IdentificationId: util.Ptr(model.IdentificationIdType(0)), - IdentificationType: util.Ptr(model.IdentificationTypeTypeEui64), - IdentificationValue: util.Ptr(model.IdentificationValueType("test")), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVIdentification(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, "test", data) -} diff --git a/emobility/public_EVPowerPerPhase_test.go b/emobility/public_EVPowerPerPhase_test.go deleted file mode 100644 index 2a2e24f..0000000 --- a/emobility/public_EVPowerPerPhase_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVPowerPerPhase_Power(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVPowerPerPhase(mockRemoteEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ScopeType: util.Ptr(model.ScopeTypeTypeACPower), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - MeasurementType: util.Ptr(model.MeasurementTypeTypePower), - CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), - ScopeType: util.Ptr(model.ScopeTypeTypeACPower), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - cmd = []model.CmdType{{ - MeasurementListData: &model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - Value: model.NewScaledNumberType(80), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 80.0, data[0]) -} - -func Test_EVPowerPerPhase_Current(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVPowerPerPhase(mockRemoteEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), - CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.NotNil(t, err) - assert.Nil(t, data) - - cmd = []model.CmdType{{ - MeasurementListData: &model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - Value: model.NewScaledNumberType(10), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVPowerPerPhase(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 2300.0, data[0]) -} diff --git a/go.mod b/go.mod index da7673a..bc67c58 100644 --- a/go.mod +++ b/go.mod @@ -31,3 +31,9 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/enbility/eebus-go => ../eebus-go + +replace github.com/enbility/ship-go => ../ship-go + +replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index 71fa9e2..525b3f3 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,6 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240218151147-ff26e3c2d343 h1:cSGEBuoF7YRReesFyeuk4+vnUEsfT7A7+iF6AlKLPX8= -github.com/enbility/eebus-go v0.0.0-20240218151147-ff26e3c2d343/go.mod h1:4SLI8oQxVOXPhAVh0Nbd74zNkgoydd8WwkffqTUjI1Y= -github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= -github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f h1:VRQSmiN661D+p433V3ufgpg2vI58SJcCnXUbyA1vwn8= -github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/grid/events.go b/grid/events.go index ca3072d..31a4c3d 100644 --- a/grid/events.go +++ b/grid/events.go @@ -88,32 +88,32 @@ func (e *Grid) gridConnected(ski string, entity api.EntityRemoteInterface) { e.gridMeasurement = f3 // subscribe - if err := e.gridDeviceConfiguration.Subscribe(); err != nil { + if _, err := e.gridDeviceConfiguration.Subscribe(); err != nil { logging.Log().Error(err) return } - if err := e.gridElectricalConnection.Subscribe(); err != nil { + if _, err := e.gridElectricalConnection.Subscribe(); err != nil { logging.Log().Error(err) return } - if err := e.gridMeasurement.Subscribe(); err != nil { + if _, err := e.gridMeasurement.Subscribe(); err != nil { logging.Log().Error(err) return } // get configuration data - if err := e.gridDeviceConfiguration.RequestDescriptions(); err != nil { + if _, err := e.gridDeviceConfiguration.RequestDescriptions(); err != nil { logging.Log().Error(err) return } // get electrical connection parameter - if err := e.gridElectricalConnection.RequestDescriptions(); err != nil { + if _, err := e.gridElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Error(err) return } - if err := e.gridElectricalConnection.RequestParameterDescriptions(); err != nil { + if _, err := e.gridElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Error(err) return } diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index f672bf0..eed1850 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -63,7 +63,7 @@ func (i *InverterBatteryVis) HandleEvent(payload api.EventPayload) { if i.inverterElectricalConnection == nil { break } - if err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { + if _, err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Error("Error getting electrical permitted values:", err) } @@ -97,19 +97,19 @@ func (i *InverterBatteryVis) inverterConnected(ski string, entity api.EntityRemo i.inverterMeasurement = f2 // subscribe - if err := i.inverterElectricalConnection.Subscribe(); err != nil { + if _, err := i.inverterElectricalConnection.Subscribe(); err != nil { logging.Log().Error(err) } - if err := i.inverterMeasurement.Subscribe(); err != nil { + if _, err := i.inverterMeasurement.Subscribe(); err != nil { logging.Log().Error(err) } // get electrical connection parameter - if err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { + if _, err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Error(err) } - if err := i.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { + if _, err := i.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Error(err) } diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index d29bbcd..c8f80e7 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -73,7 +73,7 @@ func (i *InverterPVVis) HandleEvent(payload api.EventPayload) { if i.inverterElectricalConnection == nil { break } - if err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { + if _, err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Error("Error getting electrical permitted values:", err) } @@ -113,27 +113,27 @@ func (e *InverterPVVis) inverterConnected(ski string, entity api.EntityRemoteInt e.inverterDeviceConfiguration = f3 // subscribe - if err := e.inverterDeviceConfiguration.Subscribe(); err != nil { + if _, err := e.inverterDeviceConfiguration.Subscribe(); err != nil { logging.Log().Error(err) } - if err := e.inverterElectricalConnection.Subscribe(); err != nil { + if _, err := e.inverterElectricalConnection.Subscribe(); err != nil { logging.Log().Error(err) } - if err := e.inverterMeasurement.Subscribe(); err != nil { + if _, err := e.inverterMeasurement.Subscribe(); err != nil { logging.Log().Error(err) } // get device configuration data - if err := e.inverterDeviceConfiguration.RequestDescriptions(); err != nil { + if _, err := e.inverterDeviceConfiguration.RequestDescriptions(); err != nil { logging.Log().Error(err) } // get electrical connection parameter - if err := e.inverterElectricalConnection.RequestDescriptions(); err != nil { + if _, err := e.inverterElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Error(err) } - if err := e.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { + if _, err := e.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Error(err) } diff --git a/ucevcc/api.go b/ucevcc/api.go index 669de1c..5d8a1af 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -37,7 +37,7 @@ type UCEvCCInterface interface { // the manufacturer data of an EVSE // returns deviceName, serialNumber, error - EVManufacturerData(ski string, entity spineapi.EntityRemoteInterface) (string, string, error) + EVManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) // Scenario 6 @@ -50,7 +50,7 @@ type UCEvCCInterface interface { // Scenario 7 // is the EV in sleep mode - EVInSleepMode(ski string, entity spineapi.EntityRemoteInterface) (bool, error) + EVInSleepMode(entity spineapi.EntityRemoteInterface) (bool, error) } // EV identification @@ -62,6 +62,10 @@ type IdentificationItem struct { ValueType model.IdentificationTypeType } +const ( + UcEVCCUnknownCommunicationStandard string = "unknown" +) + const ( // An EV was connected UCEvCCEventConnected api.UseCaseEventType = "ucEvConnected" @@ -75,6 +79,9 @@ const ( // EV manufacturer data was updated UCEvCCEventManufacturerUpdate api.UseCaseEventType = "ucEvManufacturerUpdate" - // EV charging power limits updated - UCEvCCEventChargingPowerLimitsUpdate api.UseCaseEventType = "ucEvPowerLimitsUpdate" + // EV electrical connection data was updated (number of phases of the EVSE) + UCEvCCEventElectricalConnectionUpdate api.UseCaseEventType = "ucEvElectricalConnectionUpdate" + + // EV permitted power limits updated + UCEvCCEventPermittedLimitsUpdate api.UseCaseEventType = "ucEvPermittedLimitsUpdate" ) diff --git a/ucevcc/events.go b/ucevcc/events.go index 8c2554a..4af3209 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -44,19 +44,21 @@ func (e *UCEvCC) HandleEvent(payload api.EventPayload) { e.evConfigurationDataUpdate(payload.Ski, payload.Entity) case *model.DeviceClassificationManufacturerDataType: e.evManufacturerDataUpdate(payload.Ski, payload.Entity) + case *model.ElectricalConnectionDescriptionListDataType: + e.evElectricalDescriptionUpdate(payload.Ski, payload.Entity) case *model.ElectricalConnectionParameterDescriptionListDataType: e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) case *model.ElectricalConnectionPermittedValueSetListDataType: - e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) + e.evElectricalPermittedValuesUpdate(payload.Ski, payload.Entity) } } } -// an EVSE was connected +// an EV was connected func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { - // initialise features, e.g. subscriptions, bindings + // initialise features, e.g. subscriptions, descriptions if evDeviceClassification, err := util.DeviceClassification(e.service, entity); err == nil { - if err := evDeviceClassification.Subscribe(); err != nil { + if _, err := evDeviceClassification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -67,17 +69,17 @@ func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { } if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { - if err := evDeviceConfiguration.Subscribe(); err != nil { + if _, err := evDeviceConfiguration.Subscribe(); err != nil { logging.Log().Debug(err) } // get ev configuration data - if err := evDeviceConfiguration.RequestDescriptions(); err != nil { + if _, err := evDeviceConfiguration.RequestDescriptions(); err != nil { logging.Log().Debug(err) } } if evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity); err == nil { - if err := evDeviceDiagnosis.Subscribe(); err != nil { + if _, err := evDeviceDiagnosis.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -88,23 +90,23 @@ func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { } if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { - if err := evElectricalConnection.Subscribe(); err != nil { + if _, err := evElectricalConnection.Subscribe(); err != nil { logging.Log().Debug(err) } - // get electrical connection parameter - if err := evElectricalConnection.RequestDescriptions(); err != nil { + // get electrical connection descriptions + if _, err := evElectricalConnection.RequestDescriptions(); err != nil { logging.Log().Debug(err) } - if err := evElectricalConnection.RequestParameterDescriptions(); err != nil { + // get electrical connection parameter descriptions + if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Debug(err) } - } if evIdentification, err := util.Identification(e.service, entity); err == nil { - if err := evIdentification.Subscribe(); err != nil { + if _, err := evIdentification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -125,6 +127,13 @@ func (e *UCEvCC) evDisconnected(ski string, entity api.EntityRemoteInterface) { // the configuration key Data of an EV was updated func (e *UCEvCC) evConfigurationDataUpdate(ski string, entity api.EntityRemoteInterface) { e.reader.SpineEvent(ski, entity, UCEvCCEventConfigurationUdpate) + + if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + // key value descriptions received, now get the data + if _, err := evDeviceConfiguration.RequestKeyValues(); err != nil { + logging.Log().Error("Error getting configuration key values:", err) + } + } } // the manufacturer Data of an EV was updated @@ -132,7 +141,21 @@ func (e *UCEvCC) evManufacturerDataUpdate(ski string, entity api.EntityRemoteInt e.reader.SpineEvent(ski, entity, UCEvCCEventManufacturerUpdate) } -// the manufacturer Data of an EV was updated +// the electrical connection description data of an EV was updated +func (e *UCEvCC) evElectricalDescriptionUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCCEventElectricalConnectionUpdate) +} + +// the electrical connection parameter description data of an EV was updated func (e *UCEvCC) evElectricalParamerDescriptionUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCCEventChargingPowerLimitsUpdate) + if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := evElectricalConnection.RequestPermittedValueSets(); err != nil { + logging.Log().Error("Error getting electrical permitted values:", err) + } + } +} + +// the electrical connection permitted value sets data of an EV was updated +func (e *UCEvCC) evElectricalPermittedValuesUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCCEventPermittedLimitsUpdate) } diff --git a/ucevcc/public.go b/ucevcc/public.go index a2adfbf..75d1737 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -73,7 +73,7 @@ func (e *UCEvCC) deviceConfigurationValueForKeyName( // - ErrNotSupported if getting the communication standard is not supported // - and others func (e *UCEvCC) EVCommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { - unknown := "unknown" + unknown := UcEVCCUnknownCommunicationStandard data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) if err != nil { @@ -151,7 +151,6 @@ func (e *UCEvCC) EVIdentifications(entity spineapi.EntityRemoteInterface) ([]Ide // the manufacturer data of an EVSE // returns deviceName, serialNumber, error func (e *UCEvCC) EVManufacturerData( - ski string, entity spineapi.EntityRemoteInterface, ) ( string, @@ -260,10 +259,9 @@ func (e *UCEvCC) EVCurrentLimits(entity spineapi.EntityRemoteInterface) ([]float // is the EV in sleep mode // returns operatingState, lastErrorCode, error func (e *UCEvCC) EVInSleepMode( - ski string, entity spineapi.EntityRemoteInterface, ) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return false, api.ErrNoEvseEntity } diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go new file mode 100644 index 0000000..1e62a0e --- /dev/null +++ b/ucevcc/public_test.go @@ -0,0 +1,643 @@ +package ucevcc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + "github.com/enbility/eebus-go/util" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEvCCSuite(t *testing.T) { + suite.Run(t, new(EvCCSuite)) +} + +type EvCCSuite struct { + suite.Suite + + sut *UCEvCC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *EvCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EvCCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEvCC(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +func (s *EvCCSuite) Test_EVConnected() { + data := s.sut.EVConnected(nil) + assert.Equal(s.T(), false, data) + + data = s.sut.EVConnected(s.mockRemoteEntity) + assert.Equal(s.T(), false, data) + + data = s.sut.EVConnected(s.evEntity) + assert.Equal(s.T(), true, data) +} + +func (s *EvCCSuite) Test_EVCommunicationStandard() { + data, err := s.sut.EVCommunicationStandard(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + + data, err = s.sut.EVCommunicationStandard(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVCommunicationStandard(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + + descData = &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVCommunicationStandard(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + + devData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, devData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVCommunicationStandard(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), string(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), data) +} + +func (s *EvCCSuite) Test_EVAsymmetricChargingSupported() { + data, err := s.sut.EVAsymmetricChargingSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData = &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + devData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + Boolean: eebusutil.Ptr(true), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, devData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} + +func (s *EvCCSuite) Test_EVIdentification() { + data, err := s.sut.EVIdentifications(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), []IdentificationItem(nil), data) + + data, err = s.sut.EVIdentifications(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), []IdentificationItem(nil), data) + + data, err = s.sut.EVIdentifications(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), []IdentificationItem(nil), data) + + idData := &model.IdentificationListDataType{ + IdentificationData: []model.IdentificationDataType{ + { + IdentificationId: util.Ptr(model.IdentificationIdType(0)), + IdentificationType: util.Ptr(model.IdentificationTypeTypeEui64), + IdentificationValue: util.Ptr(model.IdentificationValueType("test")), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIdentification, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeIdentificationListData, idData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVIdentifications(s.evEntity) + assert.Nil(s.T(), err) + resultData := []IdentificationItem{{Value: "test", ValueType: model.IdentificationTypeTypeEui64}} + assert.Equal(s.T(), resultData, data) +} + +func (s *EvCCSuite) Test_EVManufacturerData() { + device, serial, err := s.sut.EVManufacturerData(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + device, serial, err = s.sut.EVManufacturerData(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + device, serial, err = s.sut.EVManufacturerData(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + descData := &model.DeviceClassificationManufacturerDataType{} + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + device, serial, err = s.sut.EVManufacturerData(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + descData = &model.DeviceClassificationManufacturerDataType{ + DeviceName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), + SerialNumber: eebusutil.Ptr(model.DeviceClassificationStringType("12345")), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + device, serial, err = s.sut.EVManufacturerData(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), "test", device) + assert.Equal(s.T(), "12345", serial) +} + +func (s *EvCCSuite) Test_EVConnectedPhases() { + data, err := s.sut.EVConnectedPhases(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), uint(0), data) + + data, err = s.sut.EVConnectedPhases(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), uint(0), data) + + data, err = s.sut.EVConnectedPhases(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), uint(0), data) + + descData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVConnectedPhases(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), uint(0), data) + + descData = &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + AcConnectedPhases: util.Ptr(uint(1)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVConnectedPhases(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), uint(1), data) +} + +func (s *EvCCSuite) Test_EVCurrentLimits() { + minData, maxData, defaultData, err := s.sut.EVCurrentLimits(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: util.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: util.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + type permittedStruct struct { + defaultExists bool + defaultValue, expectedDefaultValue float64 + minValue, expectedMinValue float64 + maxValue, expectedMaxValue float64 + } + + tests := []struct { + name string + permitted []permittedStruct + }{ + { + "1 Phase ISO15118", + []permittedStruct{ + {true, 0.1, 0.1, 2, 2, 16, 16}, + }, + }, + { + "1 Phase IEC61851", + []permittedStruct{ + {true, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + { + "1 Phase IEC61851 Elli", + []permittedStruct{ + {false, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + { + "3 Phase ISO15118", + []permittedStruct{ + {true, 0.1, 0.1, 2, 2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, + }, + }, + { + "3 Phase IEC61851", + []permittedStruct{ + {true, 0.0, 0.0, 6, 6, 16, 16}, + {true, 0.0, 0.0, 6, 6, 16, 16}, + {true, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + { + "3 Phase IEC61851 Elli", + []permittedStruct{ + {false, 0.0, 0.0, 6, 6, 16, 16}, + {false, 0.0, 0.0, 6, 6, 16, 16}, + {false, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + } + + for _, tc := range tests { + s.T().Run(tc.name, func(t *testing.T) { + dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} + permittedData := []model.ScaledNumberSetType{} + for index, data := range tc.permitted { + item := model.ScaledNumberSetType{ + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(data.minValue), + Max: model.NewScaledNumberType(data.maxValue), + }, + }, + } + if data.defaultExists { + item.Value = []model.ScaledNumberType{*model.NewScaledNumberType(data.defaultValue)} + } + permittedData = append(permittedData, item) + + permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(index)), + PermittedValueSet: permittedData, + } + dataSet = append(dataSet, permittedItem) + } + + permData := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: dataSet, + } + + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) + assert.Nil(s.T(), fErr) + + minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + assert.Nil(s.T(), err) + + assert.Nil(s.T(), err) + assert.Equal(s.T(), len(tc.permitted), len(minData)) + assert.Equal(s.T(), len(tc.permitted), len(maxData)) + assert.Equal(s.T(), len(tc.permitted), len(defaultData)) + for index, item := range tc.permitted { + assert.Equal(s.T(), item.expectedMinValue, minData[index]) + assert.Equal(s.T(), item.expectedMaxValue, maxData[index]) + assert.Equal(s.T(), item.expectedDefaultValue, defaultData[index]) + } + }) + } +} + +func (s *EvCCSuite) Test_EVInSleepMode() { + data, err := s.sut.EVInSleepMode(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.EVInSleepMode(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.DeviceDiagnosisStateDataType{} + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVInSleepMode(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData = &model.DeviceDiagnosisStateDataType{ + OperatingState: eebusutil.Ptr(model.DeviceDiagnosisOperatingStateTypeStandby), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVInSleepMode(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeDeviceConfiguration, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + {model.FeatureTypeTypeIdentification, + []model.FunctionType{ + model.FunctionTypeIdentificationListData, + }, + }, + {model.FeatureTypeTypeDeviceClassification, + []model.FunctionType{ + model.FunctionTypeDeviceClassificationManufacturerData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionDescriptionListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index fff1b6d..0805109 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -16,7 +16,7 @@ type UCEvCC struct { var _ UCEvCCInterface = (*UCEvCC)(nil) -func NewUCEvseCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvCC { +func NewUCEvCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvCC { uc := &UCEvCC{ service: service, reader: reader, diff --git a/ucevcem/api.go b/ucevcem/api.go new file mode 100644 index 0000000..bee13b6 --- /dev/null +++ b/ucevcem/api.go @@ -0,0 +1,33 @@ +package ucevcem + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCEvCEMInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the last current measurement for each phase of the connected EV + EVCurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 2 + + // return the last power measurement for each phase of the connected EV + EVPowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 3 + + // return the charged energy measurement in Wh of the connected EV + EVChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) +} + +const ( + // EV measurement data updated + UCEvCEMMeasurementDataUpdate api.UseCaseEventType = "ucEvCEMMeasurementDataUpdate" +) diff --git a/ucevcem/events.go b/ucevcem/events.go new file mode 100644 index 0000000..54615e9 --- /dev/null +++ b/ucevcem/events.go @@ -0,0 +1,77 @@ +package ucevcem + +import ( + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCEvCEM) HandleEvent(payload api.EventPayload) { + // only about events from an EVSE entity or device changes for this remote device + + if payload.Entity == nil { + return + } + + entityType := payload.Entity.EntityType() + if entityType != model.EntityTypeTypeEV { + return + } + + switch payload.EventType { + case api.EventTypeEntityChange: + switch payload.ChangeType { + case api.ElementChangeAdd: + e.evConnected(payload.Ski, payload.Entity) + } + + case api.EventTypeDataChange: + if payload.ChangeType != api.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.MeasurementDescriptionListDataType: + e.evMeasurementDescriptionDataUpdate(payload.Ski, payload.Entity) + case *model.MeasurementListDataType: + e.evMeasurementDataUpdate(payload.Ski, payload.Entity) + } + } +} + +// an EV was connected +func (e *UCEvCEM) evConnected(ski string, entity api.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, descriptions + if evMeasurement, err := util.Measurement(e.service, entity); err == nil { + if _, err := evMeasurement.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get measurement descriptions + if err := evMeasurement.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + + // get measurement constraints + if err := evMeasurement.RequestConstraints(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the measurement description data of an EV was updated +func (e *UCEvCEM) evMeasurementDescriptionDataUpdate(ski string, entity api.EntityRemoteInterface) { + if evMeasurement, err := util.Measurement(e.service, entity); err == nil { + // get measurement values + if _, err := evMeasurement.RequestValues(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the measurement data of an EV was updated +func (e *UCEvCEM) evMeasurementDataUpdate(ski string, entity api.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, UCEvCEMMeasurementDataUpdate) +} diff --git a/ucevcem/public.go b/ucevcem/public.go new file mode 100644 index 0000000..9420ff8 --- /dev/null +++ b/ucevcem/public.go @@ -0,0 +1,162 @@ +package ucevcem + +import ( + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the last current measurement for each phase of the connected EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCEvCEM) EVCurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity.EntityType() != model.EntityTypeTypeEV { + return nil, api.ErrNoEvseEntity + } + + evMeasurement, err := util.Measurement(e.service, entity) + evElectricalConnection, err2 := util.ElectricalConnection(e.service, entity) + if err != nil || err2 != nil { + return nil, err + } + + measurement := model.MeasurementTypeTypeCurrent + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACCurrent + data, err := evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + if err != nil { + return nil, err + } + + var result []float64 + refetch := true + compare := time.Now().Add(-1 * time.Minute) + + for _, phase := range util.PhaseNameMapping { + for _, item := range data { + if item.Value == nil { + continue + } + + elParam, err := evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || elParam.AcMeasuredPhases == nil || *elParam.AcMeasuredPhases != phase { + continue + } + + phaseValue := item.Value.GetValue() + result = append(result, phaseValue) + + if item.Timestamp != nil { + if timestamp, err := item.Timestamp.GetTime(); err == nil { + refetch = timestamp.Before(compare) + } + } + } + } + + // if there was no timestamp provided or the time for the last value + // is older than 1 minute, send a read request + if refetch { + _, _ = evMeasurement.RequestValues() + } + + return result, nil +} + +// return the last power measurement for each phase of the connected EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCEvCEM) EVPowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity.EntityType() != model.EntityTypeTypeEV { + return nil, api.ErrNoEvseEntity + } + + evMeasurement, err := util.Measurement(e.service, entity) + evElectricalConnection, err2 := util.ElectricalConnection(e.service, entity) + if err != nil || err2 != nil { + return nil, err + } + + var data []model.MeasurementDataType + + powerAvailable := true + measurement := model.MeasurementTypeTypePower + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACPower + data, err = evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + if err != nil || len(data) == 0 { + powerAvailable = false + + // If power is not provided, fall back to power calculations via currents + measurement = model.MeasurementTypeTypeCurrent + scope = model.ScopeTypeTypeACCurrent + data, err = evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + if err != nil { + return nil, err + } + } + + var result []float64 + + for _, phase := range util.PhaseNameMapping { + for _, item := range data { + if item.Value == nil { + continue + } + + elParam, err := evElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || elParam.AcMeasuredPhases == nil || *elParam.AcMeasuredPhases != phase { + continue + } + + phaseValue := item.Value.GetValue() + if !powerAvailable { + phaseValue *= e.service.Configuration().Voltage() + } + + result = append(result, phaseValue) + } + } + + return result, nil +} + +// return the charged energy measurement in Wh of the connected EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCEvCEM) EVChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity.EntityType() != model.EntityTypeTypeEV { + return 0, api.ErrNoEvseEntity + } + + evMeasurement, err := util.Measurement(e.service, entity) + if err != nil { + return 0, err + } + + measurement := model.MeasurementTypeTypeEnergy + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeCharge + data, err := evMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume there is only one result + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), err +} diff --git a/ucevcem/public_test.go b/ucevcem/public_test.go new file mode 100644 index 0000000..5ad4dc8 --- /dev/null +++ b/ucevcem/public_test.go @@ -0,0 +1,433 @@ +package ucevcem + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEvCEMSuite(t *testing.T) { + suite.Run(t, new(EvCEMSuite)) +} + +type EvCEMSuite struct { + suite.Suite + + sut *UCEvCEM + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *EvCEMSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EvCEMSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEvCEM(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +func (s *EvCEMSuite) Test_EVCurrentsPerPhase() { + data, err := s.sut.EVCurrentsPerPhase(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + paramDesc := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measDesc := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + }, + } + + rFeature = s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data[0]) +} + +func (s *EvCEMSuite) Test_EVPowerPerPhase_Power() { + data, err := s.sut.EVPowerPerPhase(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + paramDesc := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measDesc := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + }, + } + + rFeature = s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(80), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 80.0, data[0]) +} + +func (s *EvCEMSuite) Test_EVPowerPerPhase_Current() { + data, err := s.sut.EVPowerPerPhase(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + paramDesc := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measDesc := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + }, + } + + rFeature = s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVPowerPerPhase(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 2300.0, data[0]) +} + +func (s *EvCEMSuite) Test_EVChargedEnergy() { + data, err := s.sut.EVChargedEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.EVChargedEnergy(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.EVChargedEnergy(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measDesc := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeCharge), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVChargedEnergy(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(80), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVChargedEnergy(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 80.0, data) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionDescriptionListData, + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, + { + model.FeatureTypeTypeMeasurement, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementListData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucevcem/results.go b/ucevcem/results.go new file mode 100644 index 0000000..b8e77f6 --- /dev/null +++ b/ucevcem/results.go @@ -0,0 +1,8 @@ +package ucevcem + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCEvCEM) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go new file mode 100644 index 0000000..d6a6695 --- /dev/null +++ b/ucevcem/ucevcem.go @@ -0,0 +1,52 @@ +package ucevcem + +import ( + "github.com/enbility/cemd/api" + serviceapi "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCEvCEM struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCEvCEMInterface = (*UCEvCEM)(nil) + +func NewUCEvCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvCEM { + uc := &UCEvCEM{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCEvCEM) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging +} + +func (e *UCEvCEM) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + f.AddResultHandler(e) +} + +func (e *UCEvCEM) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2, 3}) +} diff --git a/ucevsecc/api.go b/ucevsecc/api.go index de6bff6..3c36ef0 100644 --- a/ucevsecc/api.go +++ b/ucevsecc/api.go @@ -14,11 +14,11 @@ type UCEvseCCInterface interface { // the manufacturer data of an EVSE // returns deviceName, serialNumber, error - EVSEManufacturerData(ski string, entity spineapi.EntityRemoteInterface) (string, string, error) + EVSEManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) // the operating state data of an EVSE // returns operatingState, lastErrorCode, error - EVSEOperatingState(ski string, entity spineapi.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) + EVSEOperatingState(entity spineapi.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) } const ( diff --git a/ucevsecc/mocks/EvseCCUseCaseInterface.go b/ucevsecc/mocks/EvseCCUseCaseInterface.go deleted file mode 100644 index f187b60..0000000 --- a/ucevsecc/mocks/EvseCCUseCaseInterface.go +++ /dev/null @@ -1,96 +0,0 @@ -// Code generated by mockery v2.40.3. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// EvseCCUseCaseInterface is an autogenerated mock type for the EvseCCUseCaseInterface type -type EvseCCUseCaseInterface struct { - mock.Mock -} - -type EvseCCUseCaseInterface_Expecter struct { - mock *mock.Mock -} - -func (_m *EvseCCUseCaseInterface) EXPECT() *EvseCCUseCaseInterface_Expecter { - return &EvseCCUseCaseInterface_Expecter{mock: &_m.Mock} -} - -// AddFeatures provides a mock function with given fields: -func (_m *EvseCCUseCaseInterface) AddFeatures() { - _m.Called() -} - -// EvseCCUseCaseInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' -type EvseCCUseCaseInterface_AddFeatures_Call struct { - *mock.Call -} - -// AddFeatures is a helper method to define mock.On call -func (_e *EvseCCUseCaseInterface_Expecter) AddFeatures() *EvseCCUseCaseInterface_AddFeatures_Call { - return &EvseCCUseCaseInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} -} - -func (_c *EvseCCUseCaseInterface_AddFeatures_Call) Run(run func()) *EvseCCUseCaseInterface_AddFeatures_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EvseCCUseCaseInterface_AddFeatures_Call) Return() *EvseCCUseCaseInterface_AddFeatures_Call { - _c.Call.Return() - return _c -} - -func (_c *EvseCCUseCaseInterface_AddFeatures_Call) RunAndReturn(run func()) *EvseCCUseCaseInterface_AddFeatures_Call { - _c.Call.Return(run) - return _c -} - -// AddUseCase provides a mock function with given fields: -func (_m *EvseCCUseCaseInterface) AddUseCase() { - _m.Called() -} - -// EvseCCUseCaseInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' -type EvseCCUseCaseInterface_AddUseCase_Call struct { - *mock.Call -} - -// AddUseCase is a helper method to define mock.On call -func (_e *EvseCCUseCaseInterface_Expecter) AddUseCase() *EvseCCUseCaseInterface_AddUseCase_Call { - return &EvseCCUseCaseInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} -} - -func (_c *EvseCCUseCaseInterface_AddUseCase_Call) Run(run func()) *EvseCCUseCaseInterface_AddUseCase_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *EvseCCUseCaseInterface_AddUseCase_Call) Return() *EvseCCUseCaseInterface_AddUseCase_Call { - _c.Call.Return() - return _c -} - -func (_c *EvseCCUseCaseInterface_AddUseCase_Call) RunAndReturn(run func()) *EvseCCUseCaseInterface_AddUseCase_Call { - _c.Call.Return(run) - return _c -} - -// NewEvseCCUseCaseInterface creates a new instance of EvseCCUseCaseInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewEvseCCUseCaseInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *EvseCCUseCaseInterface { - mock := &EvseCCUseCaseInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/ucevsecc/mocks/EvseCCUseCaseReaderInterface.go b/ucevsecc/mocks/EvseCCUseCaseReaderInterface.go deleted file mode 100644 index b32aab5..0000000 --- a/ucevsecc/mocks/EvseCCUseCaseReaderInterface.go +++ /dev/null @@ -1,136 +0,0 @@ -// Code generated by mockery v2.40.3. DO NOT EDIT. - -package mocks - -import ( - model "github.com/enbility/spine-go/model" - mock "github.com/stretchr/testify/mock" -) - -// EvseCCUseCaseReaderInterface is an autogenerated mock type for the EvseCCUseCaseReaderInterface type -type EvseCCUseCaseReaderInterface struct { - mock.Mock -} - -type EvseCCUseCaseReaderInterface_Expecter struct { - mock *mock.Mock -} - -func (_m *EvseCCUseCaseReaderInterface) EXPECT() *EvseCCUseCaseReaderInterface_Expecter { - return &EvseCCUseCaseReaderInterface_Expecter{mock: &_m.Mock} -} - -// EVSEConnected provides a mock function with given fields: ski -func (_m *EvseCCUseCaseReaderInterface) EVSEConnected(ski string) { - _m.Called(ski) -} - -// EvseCCUseCaseReaderInterface_EVSEConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVSEConnected' -type EvseCCUseCaseReaderInterface_EVSEConnected_Call struct { - *mock.Call -} - -// EVSEConnected is a helper method to define mock.On call -// - ski string -func (_e *EvseCCUseCaseReaderInterface_Expecter) EVSEConnected(ski interface{}) *EvseCCUseCaseReaderInterface_EVSEConnected_Call { - return &EvseCCUseCaseReaderInterface_EVSEConnected_Call{Call: _e.mock.On("EVSEConnected", ski)} -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEConnected_Call) Run(run func(ski string)) *EvseCCUseCaseReaderInterface_EVSEConnected_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEConnected_Call) Return() *EvseCCUseCaseReaderInterface_EVSEConnected_Call { - _c.Call.Return() - return _c -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEConnected_Call) RunAndReturn(run func(string)) *EvseCCUseCaseReaderInterface_EVSEConnected_Call { - _c.Call.Return(run) - return _c -} - -// EVSEDisconnected provides a mock function with given fields: ski -func (_m *EvseCCUseCaseReaderInterface) EVSEDisconnected(ski string) { - _m.Called(ski) -} - -// EvseCCUseCaseReaderInterface_EVSEDisconnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVSEDisconnected' -type EvseCCUseCaseReaderInterface_EVSEDisconnected_Call struct { - *mock.Call -} - -// EVSEDisconnected is a helper method to define mock.On call -// - ski string -func (_e *EvseCCUseCaseReaderInterface_Expecter) EVSEDisconnected(ski interface{}) *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { - return &EvseCCUseCaseReaderInterface_EVSEDisconnected_Call{Call: _e.mock.On("EVSEDisconnected", ski)} -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call) Run(run func(ski string)) *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call) Return() *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { - _c.Call.Return() - return _c -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call) RunAndReturn(run func(string)) *EvseCCUseCaseReaderInterface_EVSEDisconnected_Call { - _c.Call.Return(run) - return _c -} - -// EVSEOperatingStateChanged provides a mock function with given fields: ski, operatingState, lastErrorCode -func (_m *EvseCCUseCaseReaderInterface) EVSEOperatingStateChanged(ski string, operatingState *model.DeviceDiagnosisOperatingStateType, lastErrorCode *model.LastErrorCodeType) { - _m.Called(ski, operatingState, lastErrorCode) -} - -// EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVSEOperatingStateChanged' -type EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call struct { - *mock.Call -} - -// EVSEOperatingStateChanged is a helper method to define mock.On call -// - ski string -// - operatingState *model.DeviceDiagnosisOperatingStateType -// - lastErrorCode *model.LastErrorCodeType -func (_e *EvseCCUseCaseReaderInterface_Expecter) EVSEOperatingStateChanged(ski interface{}, operatingState interface{}, lastErrorCode interface{}) *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { - return &EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call{Call: _e.mock.On("EVSEOperatingStateChanged", ski, operatingState, lastErrorCode)} -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call) Run(run func(ski string, operatingState *model.DeviceDiagnosisOperatingStateType, lastErrorCode *model.LastErrorCodeType)) *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*model.DeviceDiagnosisOperatingStateType), args[2].(*model.LastErrorCodeType)) - }) - return _c -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call) Return() *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { - _c.Call.Return() - return _c -} - -func (_c *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call) RunAndReturn(run func(string, *model.DeviceDiagnosisOperatingStateType, *model.LastErrorCodeType)) *EvseCCUseCaseReaderInterface_EVSEOperatingStateChanged_Call { - _c.Call.Return(run) - return _c -} - -// NewEvseCCUseCaseReaderInterface creates a new instance of EvseCCUseCaseReaderInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewEvseCCUseCaseReaderInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *EvseCCUseCaseReaderInterface { - mock := &EvseCCUseCaseReaderInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/ucevsecc/public.go b/ucevsecc/public.go index 8793fab..e2deb5c 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -10,7 +10,6 @@ import ( // the manufacturer data of an EVSE // returns deviceName, serialNumber, error func (e *UCEvseCC) EVSEManufacturerData( - ski string, entity spineapi.EntityRemoteInterface, ) ( string, @@ -20,7 +19,7 @@ func (e *UCEvseCC) EVSEManufacturerData( deviceName := "" serialNumber := "" - if entity.EntityType() != model.EntityTypeTypeEVSE { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { return deviceName, serialNumber, api.ErrNoEvseEntity } @@ -48,7 +47,6 @@ func (e *UCEvseCC) EVSEManufacturerData( // the operating state data of an EVSE // returns operatingState, lastErrorCode, error func (e *UCEvseCC) EVSEOperatingState( - ski string, entity spineapi.EntityRemoteInterface, ) ( model.DeviceDiagnosisOperatingStateType, string, error, @@ -56,7 +54,7 @@ func (e *UCEvseCC) EVSEOperatingState( operatingState := model.DeviceDiagnosisOperatingStateTypeNormalOperation lastErrorCode := "" - if entity.EntityType() != model.EntityTypeTypeEVSE { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { return operatingState, lastErrorCode, api.ErrNoEvseEntity } diff --git a/ucevsecc/public_test.go b/ucevsecc/public_test.go new file mode 100644 index 0000000..7458008 --- /dev/null +++ b/ucevsecc/public_test.go @@ -0,0 +1,262 @@ +package ucevsecc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEvCCSuite(t *testing.T) { + suite.Run(t, new(EvseCCSuite)) +} + +type EvseCCSuite struct { + suite.Suite + + sut *UCEvseCC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evseEntity spineapi.EntityRemoteInterface +} + +func (s *EvseCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EvseCCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEvseCC(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evseEntity = entities[0] +} + +func (s *EvseCCSuite) Test_EVSEManufacturerData() { + device, serial, err := s.sut.EVSEManufacturerData(nil) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + device, serial, err = s.sut.EVSEManufacturerData(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + device, serial, err = s.sut.EVSEManufacturerData(s.evseEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + descData := &model.DeviceClassificationManufacturerDataType{} + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evseEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + device, serial, err = s.sut.EVSEManufacturerData(s.evseEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), "", device) + assert.Equal(s.T(), "", serial) + + descData = &model.DeviceClassificationManufacturerDataType{ + DeviceName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), + SerialNumber: eebusutil.Ptr(model.DeviceClassificationStringType("12345")), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + device, serial, err = s.sut.EVSEManufacturerData(s.evseEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), "test", device) + assert.Equal(s.T(), "12345", serial) +} + +func (s *EvseCCSuite) Test_EVSEOperatingState() { + data, errCode, err := s.sut.EVSEOperatingState(nil) + assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) + assert.Equal(s.T(), "", errCode) + assert.Nil(s.T(), nil, err) + + data, errCode, err = s.sut.EVSEOperatingState(s.mockRemoteEntity) + assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) + assert.Equal(s.T(), "", errCode) + assert.NotNil(s.T(), err) + + data, errCode, err = s.sut.EVSEOperatingState(s.evseEntity) + assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) + assert.Equal(s.T(), "", errCode) + assert.NotNil(s.T(), err) + + descData := &model.DeviceDiagnosisStateDataType{} + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evseEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, errCode, err = s.sut.EVSEOperatingState(s.evseEntity) + assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) + assert.Equal(s.T(), "", errCode) + assert.Nil(s.T(), err) + + descData = &model.DeviceDiagnosisStateDataType{ + OperatingState: eebusutil.Ptr(model.DeviceDiagnosisOperatingStateTypeStandby), + LastErrorCode: eebusutil.Ptr(model.LastErrorCodeType("error")), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, errCode, err = s.sut.EVSEOperatingState(s.evseEntity) + assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeStandby, data) + assert.Equal(s.T(), "error", errCode) + assert.Nil(s.T(), err) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeDeviceClassification, + []model.FunctionType{ + model.FunctionTypeDeviceClassificationManufacturerData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/util/features.go b/util/features.go index 5a0d6b0..3612185 100644 --- a/util/features.go +++ b/util/features.go @@ -44,3 +44,9 @@ func Identification(service eebusapi.ServiceInterface, remoteEntity api.EntityRe return features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) } + +func Measurement(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.Measurement, error) { + localEntity := localCemEntity(service) + + return features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} From 9bb7c63ca5a41a9e5c4cfccd0742d9a8269b5434 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 19 Feb 2024 20:34:37 +0100 Subject: [PATCH 068/227] Update spine, eebus and fix build --- go.mod | 10 ++-------- go.sum | 6 ++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index bc67c58..6722eeb 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240218151147-ff26e3c2d343 + github.com/enbility/eebus-go v0.0.0-20240219192901-0457306c3e3a github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 - github.com/enbility/spine-go v0.0.0-20240215131224-a7c6b999150f + github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 @@ -31,9 +31,3 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace github.com/enbility/eebus-go => ../eebus-go - -replace github.com/enbility/ship-go => ../ship-go - -replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index 525b3f3..b1191e8 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/enbility/eebus-go v0.0.0-20240219192901-0457306c3e3a h1:9ZFkh6dWmRNbK9KUL7ume3BbBFJc4BXIhbf6ce2eloI= +github.com/enbility/eebus-go v0.0.0-20240219192901-0457306c3e3a/go.mod h1:CIXa8fLvwpeDQOZVe20mxmX+vQHvVutCrnmc/nr3KqM= +github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= +github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 h1:D9nrLCu8CHVDlchyjkWRAsUPFbA+ELpDSh4sp/acACE= +github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 12e7760ddfdbb7d4592dddfc50ffa357ab1c848e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 18:35:00 +0100 Subject: [PATCH 069/227] Move all emobility usecases into new structure --- api/api.go | 15 +- api/types.go | 291 ++++++ cmd/democem/democem.go | 2 +- emobility/api.go | 170 ---- emobility/emobility.go | 51 - emobility/evcoordinatedcharging_test.go | 413 -------- emobility/events.go | 594 ----------- emobility/features.go | 61 -- emobility/helper_test.go | 348 ------- emobility/mock_emobility_test.go | 461 --------- emobility/public.go | 932 ------------------ emobility/public_EVChargePlan_test.go | 101 -- emobility/public_EVChargeStrategy_test.go | 202 ---- ...lic_EVCoordinatedChargingSupported_test.go | 53 - emobility/public_EVCurrentChargeState_test.go | 99 -- emobility/public_EVEnergyDemand_test.go | 234 ----- .../public_EVIncentiveConstraints_test.go | 85 -- ...mizationOfSelfConsumptionSupported_test.go | 78 -- emobility/public_EVSoCSupported_test.go | 78 -- emobility/public_EVSoC_test.go | 123 --- .../public_EVTimeSlotConstraints_test.go | 78 -- emobility/public_EVWriteIncentives_test.go | 162 --- .../public_EVWriteLoadControlLimits_test.go | 276 ------ emobility/public_EVWritePowerLimits_test.go | 159 --- emobility/results.go | 46 - emobility/solution.go | 184 ---- emobility/types.go | 108 -- go.mod | 9 +- go.sum | 14 +- grid/events.go | 4 +- inverterbatteryvis/events.go | 4 +- inverterpvvis/events.go | 4 +- uccevc/api.go | 57 ++ uccevc/events.go | 208 ++++ uccevc/public_scen1.go | 135 +++ uccevc/public_scen1_test.go | 338 +++++++ uccevc/public_scen2.go | 172 ++++ uccevc/public_scen2_test.go | 165 ++++ uccevc/public_scen3.go | 263 +++++ uccevc/public_scen3_test.go | 176 ++++ uccevc/public_scen4.go | 147 +++ uccevc/public_scen4_test.go | 76 ++ uccevc/public_test.go | 339 +++++++ uccevc/results.go | 8 + uccevc/testhelper_test.go | 195 ++++ uccevc/uccevc.go | 105 ++ uccevc/uccevc_test.go | 58 ++ ucevcc/api.go | 42 +- ucevcc/events.go | 134 ++- ucevcc/public.go | 60 +- ucevcc/public_test.go | 369 ++----- ucevcc/results.go | 41 +- ucevcc/testhelper_test.go | 201 ++++ ucevcc/ucevcc.go | 42 +- ucevcem/api.go | 16 +- ucevcem/events.go | 61 +- ucevcem/public.go | 34 +- ucevcem/public_test.go | 256 ++--- ucevcem/results.go | 2 +- ucevcem/testhelper_test.go | 180 ++++ ucevcem/ucevcem.go | 39 +- ucevsecc/api.go | 20 +- ucevsecc/events.go | 53 +- ucevsecc/public.go | 4 +- ucevsecc/public_test.go | 197 +--- ucevsecc/results.go | 2 +- ucevsecc/testhelper_test.go | 179 ++++ ucevsecc/ucevsecc.go | 49 +- ucevsoc/api.go | 25 + ucevsoc/events.go | 110 +++ ucevsoc/public.go | 41 + ucevsoc/public_test.go | 91 ++ ucevsoc/results.go | 8 + ucevsoc/testhelper_test.go | 183 ++++ ucevsoc/ucevsoc.go | 98 ++ ucevsoc/ucevsoc_test.go | 58 ++ ucopev/api.go | 52 + ucopev/events.go | 95 ++ ucopev/public.go | 39 + ucopev/results.go | 8 + ucopev/ucopev.go | 93 ++ ucoscev/api.go | 45 + ucoscev/events.go | 58 ++ ucoscev/public.go | 32 + ucoscev/results.go | 8 + ucoscev/testhelper_test.go | 193 ++++ ucoscev/ucoscev.go | 92 ++ ucoscev/ucoscev_test.go | 58 ++ util/features.go | 24 + util/helper.go | 50 + util/loadcontrol.go | 180 ++++ util/loadcontrol_test.go | 227 +++++ util/measurement.go | 31 + util/testhelper_test.go | 188 ++++ 94 files changed, 5989 insertions(+), 5960 deletions(-) delete mode 100644 emobility/api.go delete mode 100644 emobility/emobility.go delete mode 100644 emobility/evcoordinatedcharging_test.go delete mode 100644 emobility/events.go delete mode 100644 emobility/features.go delete mode 100644 emobility/helper_test.go delete mode 100644 emobility/mock_emobility_test.go delete mode 100644 emobility/public.go delete mode 100644 emobility/public_EVChargePlan_test.go delete mode 100644 emobility/public_EVChargeStrategy_test.go delete mode 100644 emobility/public_EVCoordinatedChargingSupported_test.go delete mode 100644 emobility/public_EVCurrentChargeState_test.go delete mode 100644 emobility/public_EVEnergyDemand_test.go delete mode 100644 emobility/public_EVIncentiveConstraints_test.go delete mode 100644 emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go delete mode 100644 emobility/public_EVSoCSupported_test.go delete mode 100644 emobility/public_EVSoC_test.go delete mode 100644 emobility/public_EVTimeSlotConstraints_test.go delete mode 100644 emobility/public_EVWriteIncentives_test.go delete mode 100644 emobility/public_EVWriteLoadControlLimits_test.go delete mode 100644 emobility/public_EVWritePowerLimits_test.go delete mode 100644 emobility/results.go delete mode 100644 emobility/solution.go delete mode 100644 emobility/types.go create mode 100644 uccevc/api.go create mode 100644 uccevc/events.go create mode 100644 uccevc/public_scen1.go create mode 100644 uccevc/public_scen1_test.go create mode 100644 uccevc/public_scen2.go create mode 100644 uccevc/public_scen2_test.go create mode 100644 uccevc/public_scen3.go create mode 100644 uccevc/public_scen3_test.go create mode 100644 uccevc/public_scen4.go create mode 100644 uccevc/public_scen4_test.go create mode 100644 uccevc/public_test.go create mode 100644 uccevc/results.go create mode 100644 uccevc/testhelper_test.go create mode 100644 uccevc/uccevc.go create mode 100644 uccevc/uccevc_test.go create mode 100644 ucevcc/testhelper_test.go create mode 100644 ucevcem/testhelper_test.go create mode 100644 ucevsecc/testhelper_test.go create mode 100644 ucevsoc/api.go create mode 100644 ucevsoc/events.go create mode 100644 ucevsoc/public.go create mode 100644 ucevsoc/public_test.go create mode 100644 ucevsoc/results.go create mode 100644 ucevsoc/testhelper_test.go create mode 100644 ucevsoc/ucevsoc.go create mode 100644 ucevsoc/ucevsoc_test.go create mode 100644 ucopev/api.go create mode 100644 ucopev/events.go create mode 100644 ucopev/public.go create mode 100644 ucopev/results.go create mode 100644 ucopev/ucopev.go create mode 100644 ucoscev/api.go create mode 100644 ucoscev/events.go create mode 100644 ucoscev/public.go create mode 100644 ucoscev/results.go create mode 100644 ucoscev/testhelper_test.go create mode 100644 ucoscev/ucoscev.go create mode 100644 ucoscev/ucoscev_test.go create mode 100644 util/loadcontrol.go create mode 100644 util/loadcontrol_test.go create mode 100644 util/measurement.go create mode 100644 util/testhelper_test.go diff --git a/api/api.go b/api/api.go index 5f5244b..1f315c8 100644 --- a/api/api.go +++ b/api/api.go @@ -1,8 +1,6 @@ package api import ( - "errors" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" @@ -35,6 +33,13 @@ type UseCaseInterface interface { // add the usecase AddUseCase() + + // returns if the entity supports the usecase + // + // possible errors: + // - ErrDataNotAvailable if that information is not (yet) available + // - and others + IsUseCaseSupported(remoteEntity spineapi.EntityRemoteInterface) (bool, error) } // interface for informing the cem about specific events @@ -49,12 +54,6 @@ type UseCaseEventReaderInterface interface { SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event UseCaseEventType) } -// type for usecase specfic event names -type UseCaseEventType string - -var ErrNoEvseEntity = errors.New("entity is not an EVSE") -var ErrNoEvEntity = errors.New("entity is not an EV") - // Implemented by *Solutions, used by Cem type SolutionInterface interface { RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any diff --git a/api/types.go b/api/types.go index 7a66f1e..2da8378 100644 --- a/api/types.go +++ b/api/types.go @@ -1,9 +1,300 @@ package api import ( + "errors" + "time" + "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" +) + +type EVChargeStateType string + +const ( + EVChargeStateTypeUnknown EVChargeStateType = "Unknown" + EVChargeStateTypeUnplugged EVChargeStateType = "unplugged" + EVChargeStateTypeError EVChargeStateType = "error" + EVChargeStateTypePaused EVChargeStateType = "paused" + EVChargeStateTypeActive EVChargeStateType = "active" + EVChargeStateTypeFinished EVChargeStateType = "finished" +) + +// Defines a phase specific limit +type LoadLimitsPhase struct { + Phase model.ElectricalConnectionPhaseNameType + IsActive bool + Value float64 +} + +type EVChargeStrategyType string + +const ( + EVChargeStrategyTypeUnknown EVChargeStrategyType = "unknown" + EVChargeStrategyTypeNoDemand EVChargeStrategyType = "nodemand" + EVChargeStrategyTypeDirectCharging EVChargeStrategyType = "directcharging" + EVChargeStrategyTypeMinSoC EVChargeStrategyType = "minsoc" + EVChargeStrategyTypeTimedCharging EVChargeStrategyType = "timedcharging" ) +// Contains details about the actual demands from the EV +// +// General: +// - If duration and energy is 0, charge mode is EVChargeStrategyTypeNoDemand +// - If duration is 0, charge mode is EVChargeStrategyTypeDirectCharging and the slots should cover at least 48h +// - If both are != 0, charge mode is EVChargeStrategyTypeTimedCharging and the slots should cover at least the duration, but at max 168h (7d) +type Demand struct { + MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set + OptDemand float64 // demand in Wh to reach the timer SoC setting + MaxDemand float64 // the maximum possible demand until the battery is full + DurationUntilStart float64 // the duration in s from now until charging will start, this could be in the future but usualy is now + DurationUntilEnd float64 // the duration in s from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active +} + +// Contains details about an EV generated charging plan +type ChargePlan struct { + Slots []ChargePlanSlotValue // Individual charging slot details +} + +// Contains details about a charging plan slot +type ChargePlanSlotValue struct { + Start time.Time // The start time of the slot + End time.Time // The duration of the slot + Value float64 // planned power value + MinValue float64 // minimum power value + MaxValue float64 // maximum power value +} + +// Details about the time slot constraints +type TimeSlotConstraints struct { + MinSlots uint // the minimum number of slots, no minimum if 0 + MaxSlots uint // the maximum number of slots, unlimited if 0 + MinSlotDuration time.Duration // the minimum duration of a slot, no minimum if 0 + MaxSlotDuration time.Duration // the maximum duration of a slot, unlimited if 0 + SlotDurationStepSize time.Duration // the duration has to be a multiple of this value if != 0 +} + +// Details about the incentive slot constraints +type IncentiveSlotConstraints struct { + MinSlots uint // the minimum number of slots, no minimum if 0 + MaxSlots uint // the maximum number of slots, unlimited if 0 +} + +// details about the boundary +type TierBoundaryDescription struct { + // the id of the boundary + Id uint + + // the type of the boundary + Type model.TierBoundaryTypeType + + // the unit of the boundary + Unit model.UnitOfMeasurementType +} + +// details about incentive +type IncentiveDescription struct { + // the id of the incentive + Id uint + + // the type of the incentive + Type model.IncentiveTypeType + + // the currency of the incentive, if it is price based + Currency model.CurrencyType +} + +// Contains about one tier in a tariff +type IncentiveTableDescriptionTier struct { + // the id of the tier + Id uint + + // the tiers type + Type model.TierTypeType + + // each tear has 1 to 3 boundaries + // used for different power limits, e.g. 0-1kW x€, 1-3kW y€, ... + Boundaries []TierBoundaryDescription + + // each tier has 1 to 3 incentives + // - price/costs (absolute or relative) + // - renewable energy percentage + // - CO2 emissions + Incentives []IncentiveDescription +} + +// Contains details about a tariff +type IncentiveTariffDescription struct { + // each tariff can have 1 to 3 tiers + Tiers []IncentiveTableDescriptionTier +} + +// Contains details about power limits or incentives for a defined timeframe +type DurationSlotValue struct { + Duration time.Duration // Duration of this slot + Value float64 // Energy Cost or Power Limit +} + +// value if the UCEVCC communication standard is unknown +const ( + UCEVCCCommunicationStandardUnknown string = "unknown" +) + +// type for usecase specfic event names +type UseCaseEventType string + +const ( + // UCCEVC + + // EV provided an energy demand + UCCEVCEnergyDemandProvided UseCaseEventType = "ucCEVCEnergyDemandProvided" + + // EV provided a charge plan + UCCEVCChargePlanProvided UseCaseEventType = "ucCEVCChargePlanProvided" + + // EV provided a charge plan constraints + UCCEVCChargePlanConstraintsProvided UseCaseEventType = "ucCEVCChargePlanConstraintsProvided" + + UCCEVCIncentiveDescriptionsRequired UseCaseEventType = "ucCEVCIncentiveDescriptionsRequired" + + // EV incentive table data updated + UCCEVCIncentiveTableDataUpdate UseCaseEventType = "ucCEVCIncentiveTableDataUpdate" + + // EV requested power limits + UCCEVPowerLimitsRequested UseCaseEventType = "ucCEVPowerLimitsRequested" + + // EV requested incentives + UCCEVCIncentivesRequested UseCaseEventType = "ucCEVCIncentivesRequested" + + // UCEVCC + + // An EV was connected + // + // Use Case EVCC, Scenario 1 + UCEVCCEventConnected UseCaseEventType = "ucEVCCEventConnected" + + // An EV was disconnected + // + // Use Case EVCC, Scenario 8 + UCEVCCEventDisconnected UseCaseEventType = "ucEVCCEventDisconnected" + + // EV communication standard data was updated + // + // Use Case EVCC, Scenario 2 + UCEVCCCommunicationStandardDataUpdate UseCaseEventType = "ucEVCCCommunicationStandardDataUpdate" + + // EV asymmetric charging data was updated + // + // Use Case EVCC, Scenario 3 + UCEVCCAsymmetricChargingDataUpdate UseCaseEventType = "ucEVCCAsymmetricChargingDataUpdate" + + // EV identificationdata was updated + // + // Use Case EVCC, Scenario 4 + UCEVCCIdentificationDataUpdate UseCaseEventType = "ucEVCCIdentificationDataUpdate" + + // EV manufacturer data was updated + // + // Use Case EVCC, Scenario 5 + UCEVCCManufacturerDataUpdate UseCaseEventType = "ucEVCCManufacturerDataUpdate" + + // EV charging power limits + // + // Use Case EVCC, Scenario 6 + UCEVCCChargingPowerLimitsDataUpdate UseCaseEventType = "ucEVCCChargingPowerLimitsDataUpdate" + + // EV permitted power limits updated + // + // Use Case EVCC, Scenario 7 + UCEVCCSleepModeDataUpdate UseCaseEventType = "ucEVCCSleepModeDataUpdate" + + // UCEVCEM + + // EV number of connected phases data updated + // + // Use Case EVCEM, Scenario 1 + UCEVCEMNumberOfConnectedPhasesDataUpdate UseCaseEventType = "ucEVCEMNumberOfConnectedPhasesDataUpdate" + + // EV current measurement data updated + // + // Use Case EVCEM, Scenario 1 + UCEVCEMCurrentMeasurementDataUpdate UseCaseEventType = "ucEVCEMCurrentMeasurementDataUpdate" + + // EV power measurement data updated + // + // Use Case EVCEM, Scenario 2 + // + // Note: the referred data may be updated together with UCEVCEMCurrentMeasurementDataUpdate + UCEVCEMPowerMeasurementDataUpdate UseCaseEventType = "ucEVCEMCurrentMeasurementDataUpdate" + + // EV charging energy measurement data updated + // + // Use Case EVCEM, Scenario 3 + // + // Note: the referred data may be updated together with UCEVCEMCurrentMeasurementDataUpdate + UCEVCEMChargingEnergyMeasurementDataUpdate UseCaseEventType = "UCEVCEMChargingEnergyMeasurementDataUpdate" + + // UCEVSECC + + // An EVSE was connected + UCEVSECCEventConnected UseCaseEventType = "ucEVSEConnected" + + // An EVSE was disconnected + UCEVSECCEventDisconnected UseCaseEventType = "ucEVSEDisonnected" + + // EVSE manufacturer data was updated + // + // Use Case EVSECC, Scenario 1 + UCEVSECCEventManufacturerUpdate UseCaseEventType = "ucEVSEManufacturerUpdate" + + // EVSE operation state was updated + // + // Use Case EVSECC, Scenario 2 + UCEVSECCEventOperationStateUpdate UseCaseEventType = "ucEVSEOperationStateUpdate" + + // UCEVSOC + + // EV state of charge data was updated + // + // Use Case EVSOC, Scenario 1 + UCEVSOCStateOfChargeDataUpdate UseCaseEventType = "ucEVSOCStateOfChargeDataUpdate" + + // EV nominal capacity data was updated + // + // Use Case EVSOC, Scenario 2 + UCEVSOCNominalCapacityDataUpdate UseCaseEventType = "ucEVSOCNominalCapacityDataUpdate" + + // EV state of health data was updated + // + // Use Case EVSOC, Scenario 3 + UCEVSOCStateOfHealthDataUpdate UseCaseEventType = "ucEVSOCStateOfHealthDataUpdate" + + // EV actual range data was updated + // + // Use Case EVSOC, Scenario 4 + UCEVSOCActualRangeDataUpdate UseCaseEventType = "ucEVSOCActualRangeDataUpdate" + + // UCOPEV + + // EV load control obligation limit data updated + // + // Use Case OPEV, Scenario 1 + UCOPEVLoadControlLimitDataUpdate UseCaseEventType = "ucOPEVLoadControlLimitDataUpdate" + + // UCOSCEV + + // EV load control recommendation limit data updated + // + // Use Case OSCEV, Scenario 1 + // + // Note: the referred data may be updated together with UCOPEVLoadControlLimitDataUpdate + UCOSCEVLoadControlLimitDataUpdate UseCaseEventType = "ucOSCEVLoadControlLimitDataUpdate" +) + +var ErrNoEvseEntity = errors.New("entity is not an EVSE") +var ErrNoEvEntity = errors.New("entity is not an EV") +var ErrEVDisconnected = errors.New("ev is disconnected") +var ErrNotSupported = errors.New("function is not supported") + type Solution struct { Service api.ServiceInterface } diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index 7d6dc47..1d1d08c 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -23,7 +23,7 @@ func (d *DemoCem) Setup() error { return err } - evsecc := ucevsecc.NewUCEvseCC(d.cem.Service, d.cem.Service.LocalService(), d) + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.cem.Service.LocalService(), d) d.cem.AddUseCase(evsecc) d.cem.Start() diff --git a/emobility/api.go b/emobility/api.go deleted file mode 100644 index 28879a8..0000000 --- a/emobility/api.go +++ /dev/null @@ -1,170 +0,0 @@ -package emobility - -import "github.com/enbility/spine-go/api" - -//go:generate mockgen -source emobility.go -destination mock_emobility_test.go -package emobility - -// used by emobility and implemented by the CEM -type EmobilityDataProvider interface { - // The EV provided a charge strategy - EVProvidedChargeStrategy(strategy EVChargeStrategyType) - - // EV provided an energy demand - // - // Parameters: - // - demand: Contains details about the actual demands from the EV - EVProvidedEnergyDemand(demand EVDemand) - - // Energy demand and duration is provided by the EV which requires the CEM - // to respond with time slots containing power limits for each slot - // - // `EVWritePowerLimits` must be invoked within <55s, idealy <15s, after receiving this call - // - // Parameters: - // - demand: Contains details about the actual demands from the EV - // - constraints: Contains details about the time slot constraints - EVRequestPowerLimits(demand EVDemand, constraints EVTimeSlotConstraints) - - // Energy demand and duration is provided by the EV which requires the CEM - // to respond with time slots containing incentives for each slot - // - // `EVWriteIncentives` must be invoked within <20s after receiving this call - // - // Parameters: - // - demand: Contains details about the actual demands from the EV - // - constraints: Contains details about the incentive slot constraints - EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) - - // The EV provided a charge plan - EVProvidedChargePlan(plan EVChargePlan) - - // The EV provided charge plan constraints - EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) -} - -// used by the CEM and implemented by emobility -type EMobilityInterface interface { - // return if an EV is connected - EVConnected(remoteEntity api.EntityRemoteInterface) bool - - // return the current charge state of the EV - EVCurrentChargeState(remoteEntity api.EntityRemoteInterface) (EVChargeStateType, error) - - // return the current loadcontrol obligation limits - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVLoadControlObligationLimits(remoteEntity api.EntityRemoteInterface) ([]float64, error) - - // send new LoadControlLimits to the remote EV - // - // parameters: - // - obligations: Overload Protection Limits per phase in A - // - recommendations: Self Consumption recommendations per phase in A - // - // obligations: - // Sets a maximum A limit for each phase that the EV may not exceed. - // Mainly used for implementing overload protection of the site or limiting the - // maximum charge power of EVs when the EV and EVSE communicate via IEC61851 - // and with ISO15118 if the EV does not support the Optimization of Self Consumption - // usecase. - // - // recommendations: - // Sets a recommended charge power in A for each phase. This is mainly - // used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. - // The EV either needs to support the Optimization of Self Consumption usecase or - // the EVSE needs to be able map the recommendations into oligation limits which then - // works for all EVs communication either via IEC61851 or ISO15118. - // - // note: - // For obligations to work for optimizing solar excess power, the EV needs to - // have an energy demand. Recommendations work even if the EV does not have an active - // energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. - // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific - // and needs to have specific EVSE support for the specific EV brand. - // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. - EVWriteLoadControlLimits(remoteEntity api.EntityRemoteInterface, limits []EVLoadLimits) error - - // returns if the EVSE and EV combination support optimzation of self consumption - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - and others - EVOptimizationOfSelfConsumptionSupported(remoteEntity api.EntityRemoteInterface) (bool, error) - - // return if the EVSE and EV combination support providing an SoC - // - // requires EVSoCSupported to return true - // only works with a current ISO15118-2 with VAS or ISO15118-20 - // communication between EVSE and EV - // - // possible errors: - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVSoCSupported(remoteEntity api.EntityRemoteInterface) (bool, error) - - // return the last known SoC of the connected EV - // - // requires EVSoCSupported to return true - // only works with a current ISO15118-2 with VAS or ISO15118-20 - // communication between EVSE and EV - // - // possible errors: - // - ErrNotSupported if support for SoC is not possible - // - ErrDataNotAvailable if no such measurement is (yet) available - // - and others - EVSoC(remoteEntity api.EntityRemoteInterface) (float64, error) - - // returns if the EVSE and EV combination support coordinated charging - // - // possible errors: - // - ErrDataNotAvailable if that information is not (yet) available - // - and others - EVCoordinatedChargingSupported(remoteEntity api.EntityRemoteInterface) (bool, error) - - // returns the current charging stratey - // - // returns EVChargeStrategyTypeUnknown if it could not be determined, e.g. - // if the vehicle communication is via IEC61851 or the EV doesn't provide - // any information about its charging mode or plan - EVChargeStrategy(remoteEntity api.EntityRemoteInterface) EVChargeStrategyType - - // returns the current energy demand - // - EVDemand: details about the actual demands from the EV - // - error: if no data is available - // - // if duration is 0, direct charging is active, otherwise timed charging is active - EVEnergyDemand(remoteEntity api.EntityRemoteInterface) (EVDemand, error) - - // returns the current charge plan - // - EVChargePlan: details about the actual charge plan provided by the EV - // - error: if no data is available - EVChargePlan(remoteEntity api.EntityRemoteInterface) (EVChargePlan, error) - - // returns the constraints for the time slots - // - EVTimeSlotConstraints: details about the time slot constraints - // - error: if no data is available - EVTimeSlotConstraints(remoteEntity api.EntityRemoteInterface) (EVTimeSlotConstraints, error) - - // send power limits data to the EV - // - // returns an error if sending failed or charge slot count do not meet requirements - // - // this needs to be invoked either <55s, idealy <15s, of receiving a call to EVRequestPowerLimits - // or if the CEM requires the EV to change its charge plan - EVWritePowerLimits(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error - - // returns the constraints for incentive slots - // - EVIncentiveConstraints: details about the incentive slot constraints - // - error: if no data is available - EVIncentiveConstraints(remoteEntity api.EntityRemoteInterface) (EVIncentiveSlotConstraints, error) - - // send price slots data to the EV - // - // returns an error if sending failed or charge slot count do not meet requirements - // - // this needs to be invoked either within 20s of receiving a call to EVRequestIncentives - // or if the CEM requires the EV to change its charge plan - EVWriteIncentives(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error -} diff --git a/emobility/emobility.go b/emobility/emobility.go deleted file mode 100644 index 81070be..0000000 --- a/emobility/emobility.go +++ /dev/null @@ -1,51 +0,0 @@ -package emobility - -import ( - "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/util" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" -) - -type EMobility struct { - entity spineapi.EntityLocalInterface - - service api.ServiceInterface - - evseEntity spineapi.EntityRemoteInterface - evEntity spineapi.EntityRemoteInterface - - evCurrentChargeStrategy EVChargeStrategyType - - ski string - currency model.CurrencyType - - configuration EmobilityConfiguration - dataProvider EmobilityDataProvider -} - -var _ EMobilityInterface = (*EMobility)(nil) - -// Add E-Mobility support -func NewEMobility(service api.ServiceInterface, details *shipapi.ServiceDetails, currency model.CurrencyType, configuration EmobilityConfiguration, dataProvider EmobilityDataProvider) *EMobility { - ski := util.NormalizeSKI(details.SKI()) - - localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - emobility := &EMobility{ - service: service, - entity: localEntity, - ski: ski, - currency: currency, - dataProvider: dataProvider, - evCurrentChargeStrategy: EVChargeStrategyTypeUnknown, - configuration: configuration, - } - _ = spine.Events.Subscribe(emobility) - - service.RegisterRemoteSKI(ski, true) - - return emobility -} diff --git a/emobility/evcoordinatedcharging_test.go b/emobility/evcoordinatedcharging_test.go deleted file mode 100644 index f9b8fd3..0000000 --- a/emobility/evcoordinatedcharging_test.go +++ /dev/null @@ -1,413 +0,0 @@ -package emobility - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - gomock "go.uber.org/mock/gomock" -) - -func Test_CoordinatedChargingScenarios(t *testing.T) { - emobility, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobility.evseEntity = entites[0] - emobility.evEntity = entites[1] - - ctrl := gomock.NewController(t) - - dataProviderMock := NewMockEmobilityDataProvider(ctrl) - emobility.dataProvider = dataProviderMock - - datagramtt := datagramForEntityAndFeatures(false, localDevice, localEntity, emobility.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - datagramit := datagramForEntityAndFeatures(false, localDevice, localEntity, emobility.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) - - setupTimeSeries(t, datagramtt, localDevice, remoteDevice) - setupIncentiveTable(t, datagramit, localDevice, remoteDevice) - - // demand, No Profile No Timer demand - - cmd := []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Value: model.NewScaledNumberType(0), - MaxValue: model.NewScaledNumberType(74690), - }, - }, - }, - }, - }, - }} - - datagramtt.Payload.Cmd = cmd - - err := localDevice.ProcessCmd(datagramtt, remoteDevice) - assert.Nil(t, err) - - demand, err := emobility.EVEnergyDemand(emobility.evEntity) - assert.Nil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 74690.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - // the final plan - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("PT18H3M7S")), - MaxValue: model.NewScaledNumberType(4163), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("PT42M")), - MaxValue: model.NewScaledNumberType(2736), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), - }, - }, - }, - }, - }, - }} - - datagramtt.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagramtt, remoteDevice) - assert.Nil(t, err) - - // demand, profile + timer with 80% target and no climate, minSoC reached - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P2DT4H40M36S")), - Value: model.NewScaledNumberType(53400), - MaxValue: model.NewScaledNumberType(74690), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), - }, - }, - }, - }, - }, - }} - - datagramtt.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagramtt, remoteDevice) - assert.Nil(t, err) - - demand, err = emobility.EVEnergyDemand(emobility.evEntity) - assert.Nil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 53400.0, demand.OptDemand) - assert.Equal(t, 74690.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, time.Duration(time.Hour*52+time.Minute*40+time.Second*36).Seconds(), demand.DurationUntilEnd) - - // the final plan - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("P1DT15H24M24S")), - MaxValue: model.NewScaledNumberType(0), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("PT12H35M50S")), - MaxValue: model.NewScaledNumberType(4163), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(2)), - Duration: util.Ptr(model.DurationType("PT40M22S")), - MaxValue: model.NewScaledNumberType(0), - }, - }, - }, - }, - }, - }} - - datagramtt.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagramtt, remoteDevice) - assert.Nil(t, err) - - // demand, profile with 25% min SoC, minSoC not reached, no timer - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("PT8M42S")), - MaxValue: model.NewScaledNumberType(4212), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), - }, - }, - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Value: model.NewScaledNumberType(600), - MinValue: model.NewScaledNumberType(600), - MaxValue: model.NewScaledNumberType(75600), - }, - }, - }, - }, - }, - }} - - datagramtt.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagramtt, remoteDevice) - assert.Nil(t, err) - - demand, err = emobility.EVEnergyDemand(emobility.evEntity) - assert.Nil(t, err) - assert.Equal(t, 600.0, demand.MinDemand) - assert.Equal(t, 600.0, demand.OptDemand) - assert.Equal(t, 75600.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - // the final plan - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("PT8M42S")), - MaxValue: model.NewScaledNumberType(4212), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), - }, - }, - }, - }, - }, - }} - - datagramtt.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagramtt, remoteDevice) - assert.Nil(t, err) -} - -func setupTimeSeries( - t *testing.T, - datagram model.DatagramType, - localDevice api.DeviceLocalInterface, - remoteDevice api.DeviceRemoteInterface) { - cmd := []model.CmdType{{ - TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - SlotCountMax: util.Ptr(model.TimeSeriesSlotCountType(30)), - }, - }, - }, - }} - - datagram.Payload.Cmd = cmd - - err := localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - cmd = []model.CmdType{{ - TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), - TimeSeriesWriteable: util.Ptr(true), - UpdateRequired: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypePlan), - TimeSeriesWriteable: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeSingleDemand), - TimeSeriesWriteable: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeWh), - }, - }, - }, - }} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) -} - -func setupIncentiveTable( - t *testing.T, - datagram model.DatagramType, - localDevice api.DeviceLocalInterface, - remoteDevice api.DeviceRemoteInterface) { - cmd := []model.CmdType{{ - IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ - IncentiveTableDescription: []model.IncentiveTableDescriptionType{ - { - TariffDescription: &model.TariffDescriptionDataType{ - TariffId: util.Ptr(model.TariffIdType(1)), - TariffWriteable: util.Ptr(true), - UpdateRequired: util.Ptr(false), - ScopeType: util.Ptr(model.ScopeTypeTypeSimpleIncentiveTable), - }, - }, - }, - }, - }} - - datagram.Payload.Cmd = cmd - - err := localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) -} - -/* -func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { - cmd := []model.CmdType{{ - IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ - IncentiveTableDescription: []model.IncentiveTableDescriptionType{ - { - TariffDescription: &model.TariffDescriptionDataType{ - TariffId: util.Ptr(model.TariffIdType(1)), - TariffWriteable: util.Ptr(true), - UpdateRequired: util.Ptr(true), - ScopeType: util.Ptr(model.ScopeTypeTypeSimpleIncentiveTable), - }, - }, - }, - }, - }} - - datagram.Payload.Cmd = cmd - - err := localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) -} - -func requestPowerTableUpdate(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { - cmd := []model.CmdType{{ - TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), - TimeSeriesWriteable: util.Ptr(true), - UpdateRequired: util.Ptr(true), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypePlan), - TimeSeriesWriteable: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), - TimeSeriesWriteable: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeWh), - }, - }, - }, - }} - - datagram.Payload.Cmd = cmd - - err := localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) -} -*/ diff --git a/emobility/events.go b/emobility/events.go deleted file mode 100644 index 187bce3..0000000 --- a/emobility/events.go +++ /dev/null @@ -1,594 +0,0 @@ -package emobility - -import ( - "time" - - "github.com/enbility/eebus-go/features" - "github.com/enbility/eebus-go/util" - "github.com/enbility/ship-go/logging" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -// Internal EventHandler Interface for the CEM -func (e *EMobility) HandleEvent(payload api.EventPayload) { - // only care about the registered SKI - if payload.Ski != e.ski { - return - } - - // only care about events for this remote device - if payload.Device != nil && payload.Device.Ski() != e.ski { - return - } - - // we care only about events from an EVSE or EV entity or device changes for this remote device - var entityType model.EntityTypeType - if payload.Entity != nil { - entityType = payload.Entity.EntityType() - if entityType != model.EntityTypeTypeEVSE && entityType != model.EntityTypeTypeEV { - return - } - } - - switch payload.EventType { - case api.EventTypeDeviceChange: - if payload.ChangeType == api.ElementChangeRemove { - e.evseDisconnected() - e.evDisconnected() - } - - case api.EventTypeEntityChange: - if payload.Entity == nil { - return - } - - switch payload.ChangeType { - case api.ElementChangeAdd: - switch entityType { - case model.EntityTypeTypeEVSE: - e.evseConnected(payload.Ski, payload.Entity) - case model.EntityTypeTypeEV: - e.evConnected(payload.Entity) - } - case api.ElementChangeRemove: - switch entityType { - case model.EntityTypeTypeEVSE: - e.evseDisconnected() - case model.EntityTypeTypeEV: - e.evDisconnected() - } - } - - case api.EventTypeDataChange: - if payload.ChangeType == api.ElementChangeUpdate { - switch payload.Data.(type) { - case *model.DeviceConfigurationKeyValueDescriptionListDataType: - evDeviceConfiguration, err := e.deviceConfiguration(payload.Entity) - if err != nil { - break - } - - // key value descriptions received, now get the data - if _, err := evDeviceConfiguration.RequestKeyValues(); err != nil { - logging.Log().Error("Error getting configuration key values:", err) - } - - case *model.ElectricalConnectionParameterDescriptionListDataType: - evElectricalConnection, err := e.electricalConnection(payload.Entity) - if err != nil { - break - } - - if _, err := evElectricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log().Error("Error getting electrical permitted values:", err) - } - - case *model.LoadControlLimitDescriptionListDataType: - evLoadControl, err := e.loadControl(payload.Entity) - if err != nil { - break - } - - if _, err := evLoadControl.RequestLimitValues(); err != nil { - logging.Log().Error("Error getting loadcontrol limit values:", err) - } - - case *model.MeasurementDescriptionListDataType: - evMeasurement, err := e.measurement(payload.Entity) - if err != nil { - break - } - - if _, err := evMeasurement.RequestValues(); err != nil { - logging.Log().Error("Error getting measurement list values:", err) - } - - case *model.TimeSeriesDescriptionListDataType: - evTimeSeries, err := e.timeSeries(payload.Entity) - if err != nil || payload.CmdClassifier == nil { - break - } - - switch *payload.CmdClassifier { - case model.CmdClassifierTypeReply: - if err := evTimeSeries.RequestConstraints(); err == nil { - break - } - - // if constraints do not exist, directly request values - e.evRequestTimeSeriesValues(payload.Entity) - - case model.CmdClassifierTypeNotify: - // check if we are required to update the plan - if !e.evCheckTimeSeriesDescriptionConstraintsUpdateRequired(payload.Entity) { - break - } - - demand, err := e.EVEnergyDemand(payload.Entity) - if err != nil { - logging.Log().Error("Error getting energy demand:", err) - break - } - - if e.dataProvider != nil { - e.dataProvider.EVProvidedEnergyDemand(demand) - } - - timeConstraints, err := e.EVTimeSlotConstraints(payload.Entity) - if err != nil { - logging.Log().Error("Error getting timeseries constraints:", err) - break - } - - incentiveConstraints, err := e.EVIncentiveConstraints(payload.Entity) - if err != nil { - logging.Log().Error("Error getting incentive constraints:", err) - break - } - - if e.dataProvider != nil { - e.dataProvider.EVRequestPowerLimits(demand, timeConstraints) - e.dataProvider.EVRequestIncentives(demand, incentiveConstraints) - break - } - - e.evWriteDefaultIncentives(payload.Entity) - e.evWriteDefaultPowerLimits(payload.Entity) - } - - case *model.TimeSeriesConstraintsListDataType: - if _, err := e.timeSeries(payload.Entity); err != nil || payload.CmdClassifier == nil { - break - } - - if *payload.CmdClassifier != model.CmdClassifierTypeReply { - break - } - - e.evRequestTimeSeriesValues(payload.Entity) - - case *model.TimeSeriesListDataType: - if _, err := e.timeSeries(payload.Entity); err != nil || payload.CmdClassifier == nil { - break - } - - // check if we received a plan - e.evForwardChargePlanIfProvided(payload.Entity) - - case *model.IncentiveTableDescriptionDataType: - evIncentiveTable, err := e.incentiveTable(payload.Entity) - if err != nil || payload.CmdClassifier == nil { - break - } - - switch *payload.CmdClassifier { - case model.CmdClassifierTypeReply: - if _, err := evIncentiveTable.RequestConstraints(); err == nil { - break - } - - // if constraints do not exist, directly request values - e.evRequestIncentiveValues(payload.Entity) - - case model.CmdClassifierTypeNotify: - // check if we are required to update the plan - if !e.evCheckIncentiveTableDescriptionUpdateRequired(payload.Entity) { - break - } - - e.evWriteIncentiveTableDescriptions(payload.Entity) - } - - case *model.IncentiveTableConstraintsDataType: - if *payload.CmdClassifier == model.CmdClassifierTypeReply { - e.evRequestIncentiveValues(payload.Entity) - } - } - } - } - - if e.dataProvider == nil || payload.Entity == nil { - return - } - - // check if the charge strategy changed - chargeStrategy := e.EVChargeStrategy(payload.Entity) - if chargeStrategy == e.evCurrentChargeStrategy { - return - } - - // update the current value and inform the dataProvider - e.evCurrentChargeStrategy = chargeStrategy - e.dataProvider.EVProvidedChargeStrategy(chargeStrategy) -} - -func (e *EMobility) localCemEntity() api.EntityLocalInterface { - localDevice := e.service.LocalDevice() - - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - return localEntity -} - -func (e *EMobility) evWriteDefaultIncentives(remoteEntity api.EntityRemoteInterface) { - // send default incentives for the maximum timeframe - // to fullfill spec, as there is no data provided - logging.Log().Info("Fallback sending default incentives") - data := []EVDurationSlotValue{ - {Duration: 7 * time.Hour * 24, Value: 0.30}, - } - _ = e.EVWriteIncentives(remoteEntity, data) -} - -func (e *EMobility) evWriteDefaultPowerLimits(remoteEntity api.EntityRemoteInterface) { - // send default power limits for the maximum timeframe - // to fullfill spec, as there is no data provided - logging.Log().Info("Fallback sending default power limits") - - evElectricalConnection, err := e.electricalConnection(remoteEntity) - if err != nil { - logging.Log().Error("electrical connection feature not found") - return - } - - paramDesc, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) - if err != nil { - logging.Log().Error("Error getting parameter descriptions:", err) - return - } - - permitted, err := evElectricalConnection.GetPermittedValueSetForParameterId(*paramDesc.ParameterId) - if err != nil { - logging.Log().Error("Error getting permitted values:", err) - return - } - - if len(permitted.PermittedValueSet) < 1 || len(permitted.PermittedValueSet[0].Range) < 1 { - logging.Log().Error("No permitted value set available") - return - } - - data := []EVDurationSlotValue{ - {Duration: 7 * time.Hour * 24, Value: permitted.PermittedValueSet[0].Range[0].Max.GetValue()}, - } - _ = e.EVWritePowerLimits(remoteEntity, data) -} - -// request time series values -func (e *EMobility) evRequestTimeSeriesValues(remoteEntity api.EntityRemoteInterface) { - localEntity := e.localCemEntity() - - evTimeSeries, err := features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) - if err != nil { - return - } - - if _, err := evTimeSeries.RequestValues(); err != nil { - logging.Log().Error("Error getting time series list values:", err) - } -} - -// send the ev provided charge plan to the CEM -func (e *EMobility) evForwardChargePlanIfProvided(remoteEntity api.EntityRemoteInterface) { - if e.dataProvider == nil { - return - } - - if plan, err := e.EVChargePlan(remoteEntity); err == nil { - e.dataProvider.EVProvidedChargePlan(plan) - } - - if constraints, err := e.EVChargePlanConstraints(remoteEntity); err == nil { - e.dataProvider.EVProvidedChargePlanConstraints(constraints) - } -} - -// request incentive table values -func (e *EMobility) evRequestIncentiveValues(remoteEntity api.EntityRemoteInterface) { - localEntity := e.localCemEntity() - - evIncentiveTable, err := features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) - if err != nil { - return - } - - if _, err := evIncentiveTable.RequestValues(); err != nil { - logging.Log().Error("Error getting time series list values:", err) - } -} - -// process required steps when an evse is connected -func (e *EMobility) evseConnected(ski string, entity api.EntityRemoteInterface) { - e.evseEntity = entity - localDevice := e.service.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - if evseDeviceClassification, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { - _, _ = evseDeviceClassification.RequestManufacturerDetails() - } - - if evseDeviceDiagnosis, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { - _, _ = evseDeviceDiagnosis.RequestState() - } -} - -// an EV was disconnected -func (e *EMobility) evseDisconnected() { - e.evseEntity = nil - - e.evDisconnected() -} - -// an EV was disconnected, trigger required cleanup -func (e *EMobility) evDisconnected() { - if e.evEntity == nil { - return - } - - e.evEntity = nil - - logging.Log().Debug("ev disconnected") - - // TODO: add error handling -} - -// an EV was connected, trigger required communication -func (e *EMobility) evConnected(entity api.EntityRemoteInterface) { - e.evEntity = entity - - logging.Log().Debug("ev connected") - - // initialise features, e.g. subscriptions, bindings - if evDeviceClassification, err := e.deviceClassification(entity); err == nil { - if _, err := evDeviceClassification.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - // get manufacturer details - if _, err := evDeviceClassification.RequestManufacturerDetails(); err != nil { - logging.Log().Debug(err) - } - } - - if evDeviceConfiguration, err := e.deviceConfiguration(entity); err == nil { - if _, err := evDeviceConfiguration.Subscribe(); err != nil { - logging.Log().Debug(err) - } - // get ev configuration data - if _, err := evDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log().Debug(err) - } - } - - if evDeviceDiagnosis, err := e.deviceDiagnosis(entity); err == nil { - if _, err := evDeviceDiagnosis.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - // get device diagnosis state - if _, err := evDeviceDiagnosis.RequestState(); err != nil { - logging.Log().Debug(err) - } - } - - if evElectricalConnection, err := e.electricalConnection(entity); err == nil { - if _, err := evElectricalConnection.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - // get electrical connection parameter - if _, err := evElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Debug(err) - } - - if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log().Debug(err) - } - - } - - if evMeasurement, err := e.measurement(entity); err == nil { - if _, err := evMeasurement.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - // get measurement parameters - if err := evMeasurement.RequestDescriptions(); err != nil { - logging.Log().Debug(err) - } - - } - - if evLoadControl, err := e.loadControl(entity); err == nil { - if _, err := evLoadControl.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - if _, err := evLoadControl.Bind(); err != nil { - logging.Log().Debug(err) - } - - // get loadlimit parameter - if _, err := evLoadControl.RequestLimitDescriptions(); err != nil { - logging.Log().Debug(err) - } - - } - - if evIdentification, err := e.identification(entity); err == nil { - if _, err := evIdentification.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - // get identification - if _, err := evIdentification.RequestValues(); err != nil { - logging.Log().Debug(err) - } - } - - if e.configuration.CoordinatedChargingEnabled { - if evTimeSeries, err := e.timeSeries(entity); err == nil { - if _, err := evTimeSeries.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - if _, err := evTimeSeries.Bind(); err != nil { - logging.Log().Debug(err) - } - - // get time series parameter - if err := evTimeSeries.RequestDescriptions(); err != nil { - logging.Log().Debug(err) - } - - } - - if evIncentiveTable, err := e.incentiveTable(entity); err == nil { - if _, err := evIncentiveTable.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - if _, err := evIncentiveTable.Bind(); err != nil { - logging.Log().Debug(err) - } - - // get incentive table parameter - if _, err := evIncentiveTable.RequestDescriptions(); err != nil { - logging.Log().Debug(err) - } - - } - - } -} - -// inform the EVSE about used currency and boundary units -// -// # SPINE UC CoordinatedEVCharging 2.4.3 -func (e *EMobility) evWriteIncentiveTableDescriptions(remoteEntity api.EntityRemoteInterface) { - evIncentiveTable, err := e.incentiveTable(remoteEntity) - if err != nil { - logging.Log().Error("incentivetable feature not found") - return - } - - descriptions, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) - if err != nil { - logging.Log().Error(err) - return - } - - // - tariff, min 1 - // each tariff has - // - tiers: min 1, max 3 - // each tier has: - // - boundaries: min 1, used for different power limits, e.g. 0-1kW x€, 1-3kW y€, ... - // - incentives: min 1, max 3 - // - price/costs (absolute or relative) - // - renewable energy percentage - // - CO2 emissions - // - // limit this to - // - 1 tariff - // - 1 tier - // - 1 boundary - // - 1 incentive (price) - // incentive type has to be the same for all sent power limits! - data := []model.IncentiveTableDescriptionType{ - { - TariffDescription: descriptions[0].TariffDescription, - Tier: []model.IncentiveTableDescriptionTierType{ - { - TierDescription: &model.TierDescriptionDataType{ - TierId: util.Ptr(model.TierIdType(1)), - TierType: util.Ptr(model.TierTypeTypeDynamicCost), - }, - BoundaryDescription: []model.TierBoundaryDescriptionDataType{ - { - BoundaryId: util.Ptr(model.TierBoundaryIdType(1)), - BoundaryType: util.Ptr(model.TierBoundaryTypeTypePowerBoundary), - BoundaryUnit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - }, - IncentiveDescription: []model.IncentiveDescriptionDataType{ - { - IncentiveId: util.Ptr(model.IncentiveIdType(1)), - IncentiveType: util.Ptr(model.IncentiveTypeTypeAbsoluteCost), - Currency: util.Ptr(e.currency), - }, - }, - }, - }, - }, - } - - _, err = evIncentiveTable.WriteDescriptions(data) - if err != nil { - logging.Log().Error(err) - } -} - -// check timeSeries descriptions if constraints element has updateRequired set to true -// as this triggers the CEM to send power tables within 20s -func (e *EMobility) evCheckTimeSeriesDescriptionConstraintsUpdateRequired(remoteEntity api.EntityRemoteInterface) bool { - evTimeSeries, err := e.timeSeries(remoteEntity) - if err != nil { - logging.Log().Error("timeseries feature not found") - return false - } - - data, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) - if err != nil { - return false - } - - if data.UpdateRequired != nil { - return *data.UpdateRequired - } - - return false -} - -// check incentibeTable descriptions if the tariff description has updateRequired set to true -// as this triggers the CEM to send incentive tables within 20s -func (e *EMobility) evCheckIncentiveTableDescriptionUpdateRequired(remoteEntity api.EntityRemoteInterface) bool { - evIncentiveTable, err := e.incentiveTable(remoteEntity) - if err != nil { - logging.Log().Error("incentivetable feature not found") - return false - } - - data, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) - if err != nil { - return false - } - - // only use the first description and therein the first tariff - item := data[0].TariffDescription - if item.UpdateRequired != nil { - return *item.UpdateRequired - } - - return false -} diff --git a/emobility/features.go b/emobility/features.go deleted file mode 100644 index 3ee4535..0000000 --- a/emobility/features.go +++ /dev/null @@ -1,61 +0,0 @@ -package emobility - -import ( - "github.com/enbility/eebus-go/features" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -func (e *EMobility) deviceClassification(remoteEntity api.EntityRemoteInterface) (*features.DeviceClassification, error) { - localEntity := e.localCemEntity() - - return features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) deviceConfiguration(remoteEntity api.EntityRemoteInterface) (*features.DeviceConfiguration, error) { - localEntity := e.localCemEntity() - - return features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) deviceDiagnosis(remoteEntity api.EntityRemoteInterface) (*features.DeviceDiagnosis, error) { - localEntity := e.localCemEntity() - - return features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) electricalConnection(remoteEntity api.EntityRemoteInterface) (*features.ElectricalConnection, error) { - localEntity := e.localCemEntity() - - return features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) measurement(remoteEntity api.EntityRemoteInterface) (*features.Measurement, error) { - localEntity := e.localCemEntity() - - return features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) loadControl(remoteEntity api.EntityRemoteInterface) (*features.LoadControl, error) { - localEntity := e.localCemEntity() - - return features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) identification(remoteEntity api.EntityRemoteInterface) (*features.Identification, error) { - localEntity := e.localCemEntity() - - return features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) timeSeries(remoteEntity api.EntityRemoteInterface) (*features.TimeSeries, error) { - localEntity := e.localCemEntity() - - return features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} - -func (e *EMobility) incentiveTable(remoteEntity api.EntityRemoteInterface) (*features.IncentiveTable, error) { - localEntity := e.localCemEntity() - - return features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) -} diff --git a/emobility/helper_test.go b/emobility/helper_test.go deleted file mode 100644 index 58e3cba..0000000 --- a/emobility/helper_test.go +++ /dev/null @@ -1,348 +0,0 @@ -package emobility - -import ( - "encoding/json" - "fmt" - "sync" - "testing" - "time" - - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/mocks" - "github.com/enbility/eebus-go/service" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/cert" - "github.com/enbility/ship-go/util" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" - "github.com/stretchr/testify/mock" -) - -type WriteMessageHandler struct { - sentMessages [][]byte - - mux sync.Mutex -} - -var _ shipapi.ShipConnectionDataWriterInterface = (*WriteMessageHandler)(nil) - -func (t *WriteMessageHandler) WriteShipMessageWithPayload(message []byte) { - t.mux.Lock() - defer t.mux.Unlock() - - t.sentMessages = append(t.sentMessages, message) -} - -func (t *WriteMessageHandler) LastMessage() []byte { - t.mux.Lock() - defer t.mux.Unlock() - - if len(t.sentMessages) == 0 { - return nil - } - - return t.sentMessages[len(t.sentMessages)-1] -} - -func (t *WriteMessageHandler) MessageWithReference(msgCounterReference *model.MsgCounterType) []byte { - t.mux.Lock() - defer t.mux.Unlock() - - var datagram model.Datagram - - for _, msg := range t.sentMessages { - if err := json.Unmarshal(msg, &datagram); err != nil { - return nil - } - if datagram.Datagram.Header.MsgCounterReference == nil { - continue - } - if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { - continue - } - if datagram.Datagram.Payload.Cmd[0].ResultData != nil { - continue - } - - return msg - } - - return nil -} - -func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.MsgCounterType) []byte { - t.mux.Lock() - defer t.mux.Unlock() - - var datagram model.Datagram - - for _, msg := range t.sentMessages { - if err := json.Unmarshal(msg, &datagram); err != nil { - return nil - } - if datagram.Datagram.Header.MsgCounterReference == nil { - continue - } - if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { - continue - } - if datagram.Datagram.Payload.Cmd[0].ResultData == nil { - continue - } - - return msg - } - - return nil -} - -const remoteSki string = "testremoteski" - -// we don't want to handle events in these tests for now, so we don't use NewEMobility(...) -func NewTestEMobility(service api.ServiceInterface, details *shipapi.ServiceDetails) *EMobility { - ski := util.NormalizeSKI(details.SKI()) - - localEntity := service.LocalDevice().Entity([]model.AddressEntityType{1}) - emobility := &EMobility{ - service: service, - entity: localEntity, - ski: ski, - } - - service.RegisterRemoteSKI(ski, false) - - return emobility -} - -func setupEmobility(t *testing.T) (*EMobility, api.ServiceInterface) { - cert, _ := cert.CreateCertificate("test", "test", "DE", "test") - configuration, _ := api.NewConfiguration( - "test", "test", "test", "test", - model.DeviceTypeTypeEnergyManagementSystem, - []model.EntityTypeType{model.EntityTypeTypeCEM}, - 9999, cert, 230.0, time.Second*4) - - serviceHandler := mocks.NewServiceReaderInterface(t) - serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() - - eebusService := service.NewService(configuration, serviceHandler) - _ = eebusService.Setup() - details := shipapi.NewServiceDetails(remoteSki) - emobility := NewTestEMobility(eebusService, details) - return emobility, eebusService -} - -func setupDevices( - eebusService api.ServiceInterface) ( - spineapi.DeviceLocalInterface, - spineapi.EntityLocalInterface, - spineapi.DeviceRemoteInterface, - []spineapi.EntityRemoteInterface, - *WriteMessageHandler) { - localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(6, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(6, localEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(6, localEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeClient) - localEntity.AddFeature(f) - - writeHandler := &WriteMessageHandler{} - sender := spine.NewSender(writeHandler) - remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - - var clientRemoteFeatures = []struct { - featureType model.FeatureTypeType - supportedFcts []model.FunctionType - }{ - { - model.FeatureTypeTypeDeviceDiagnosis, - []model.FunctionType{}, - }, - { - model.FeatureTypeTypeDeviceConfiguration, - []model.FunctionType{ - model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, - model.FunctionTypeDeviceConfigurationKeyValueListData, - }, - }, - {model.FeatureTypeTypeElectricalConnection, - []model.FunctionType{ - model.FunctionTypeElectricalConnectionDescriptionListData, - model.FunctionTypeElectricalConnectionParameterDescriptionListData, - model.FunctionTypeElectricalConnectionPermittedValueSetListData, - }, - }, - { - model.FeatureTypeTypeMeasurement, - []model.FunctionType{ - model.FunctionTypeMeasurementDescriptionListData, - model.FunctionTypeMeasurementListData, - }, - }, - { - model.FeatureTypeTypeLoadControl, - []model.FunctionType{ - model.FunctionTypeLoadControlLimitDescriptionListData, - model.FunctionTypeLoadControlLimitListData, - }, - }, - { - model.FeatureTypeTypeIdentification, - []model.FunctionType{ - model.FunctionTypeIdentificationListData, - }, - }, - {model.FeatureTypeTypeTimeSeries, - []model.FunctionType{ - model.FunctionTypeTimeSeriesDescriptionListData, - model.FunctionTypeTimeSeriesListData, - model.FunctionTypeTimeSeriesConstraintsListData, - }, - }, - {model.FeatureTypeTypeIncentiveTable, - []model.FunctionType{ - model.FunctionTypeIncentiveTableConstraintsData, - }, - }, - } - - remoteDeviceName := "remote" - - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { - supportedFcts := []model.FunctionPropertyType{} - for _, fct := range feature.supportedFcts { - supportedFct := model.FunctionPropertyType{ - Function: util.Ptr(fct), - PossibleOperations: &model.PossibleOperationsType{ - Read: &model.PossibleOperationsReadType{}, - }, - } - supportedFcts = append(supportedFcts, supportedFct) - } - - featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ - Description: &model.NetworkManagementFeatureDescriptionDataType{ - FeatureAddress: &model.FeatureAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - Feature: util.Ptr(model.AddressFeatureType(index)), - }, - FeatureType: util.Ptr(feature.featureType), - Role: util.Ptr(model.RoleTypeServer), - SupportedFunction: supportedFcts, - }, - } - featureInformations = append(featureInformations, featureInformation) - } - - detailedData := &model.NodeManagementDetailedDiscoveryDataType{ - DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ - Description: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - }, - }, - }, - EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - }, - EntityType: util.Ptr(model.EntityTypeTypeEVSE), - }, - }, - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - }, - EntityType: util.Ptr(model.EntityTypeTypeEV), - }, - }, - }, - FeatureInformation: featureInformations, - } - localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) - - entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) - if err != nil { - fmt.Println(err) - } - - return localDevice, localEntity, remoteDevice, entities, writeHandler -} - -func datagramForEntityAndFeatures( - notify bool, - localDevice spineapi.DeviceLocalInterface, - localEntity spineapi.EntityLocalInterface, - remoteEntity spineapi.EntityRemoteInterface, - featureType model.FeatureTypeType, - remoteRole, localRole model.RoleType) model.DatagramType { - var addressSource, addressDestination *model.FeatureAddressType - if remoteEntity == nil { - // NodeManagement - addressSource = &model.FeatureAddressType{ - Entity: []model.AddressEntityType{0}, - Feature: util.Ptr(model.AddressFeatureType(0)), - } - addressDestination = &model.FeatureAddressType{ - Device: localDevice.Address(), - Entity: []model.AddressEntityType{0}, - Feature: util.Ptr(model.AddressFeatureType(0)), - } - } else { - rFeature := featureOfTypeAndRole(remoteEntity, featureType, remoteRole) - addressSource = rFeature.Address() - - lFeature := localEntity.FeatureOfTypeAndRole(featureType, localRole) - addressDestination = lFeature.Address() - } - datagram := model.DatagramType{ - Header: model.HeaderType{ - AddressSource: addressSource, - AddressDestination: addressDestination, - MsgCounter: util.Ptr(model.MsgCounterType(1)), - MsgCounterReference: util.Ptr(model.MsgCounterType(1)), - CmdClassifier: util.Ptr(model.CmdClassifierTypeReply), - }, - Payload: model.PayloadType{ - Cmd: []model.CmdType{}, - }, - } - if notify { - datagram.Header.CmdClassifier = util.Ptr(model.CmdClassifierTypeNotify) - } - - return datagram -} - -func featureOfTypeAndRole( - entity spineapi.EntityRemoteInterface, - featureType model.FeatureTypeType, - role model.RoleType) spineapi.FeatureRemoteInterface { - for _, f := range entity.Features() { - if f.Type() == featureType && f.Role() == role { - return f - } - } - return nil -} diff --git a/emobility/mock_emobility_test.go b/emobility/mock_emobility_test.go deleted file mode 100644 index de5970c..0000000 --- a/emobility/mock_emobility_test.go +++ /dev/null @@ -1,461 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: emobility.go -// -// Generated by this command: -// -// mockgen -source emobility.go -destination mock_emobility_test.go -package emobility -// - -// Package emobility is a generated GoMock package. -package emobility - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockEmobilityDataProvider is a mock of EmobilityDataProvider interface. -type MockEmobilityDataProvider struct { - ctrl *gomock.Controller - recorder *MockEmobilityDataProviderMockRecorder -} - -// MockEmobilityDataProviderMockRecorder is the mock recorder for MockEmobilityDataProvider. -type MockEmobilityDataProviderMockRecorder struct { - mock *MockEmobilityDataProvider -} - -// NewMockEmobilityDataProvider creates a new mock instance. -func NewMockEmobilityDataProvider(ctrl *gomock.Controller) *MockEmobilityDataProvider { - mock := &MockEmobilityDataProvider{ctrl: ctrl} - mock.recorder = &MockEmobilityDataProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEmobilityDataProvider) EXPECT() *MockEmobilityDataProviderMockRecorder { - return m.recorder -} - -// EVProvidedChargePlan mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargePlan(plan EVChargePlan) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargePlan", plan) -} - -// EVProvidedChargePlan indicates an expected call of EVProvidedChargePlan. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlan(plan any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlan", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlan), plan) -} - -// EVProvidedChargePlanConstraints mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargePlanConstraints(constraints []EVDurationSlotValue) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargePlanConstraints", constraints) -} - -// EVProvidedChargePlanConstraints indicates an expected call of EVProvidedChargePlanConstraints. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargePlanConstraints(constraints any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargePlanConstraints", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargePlanConstraints), constraints) -} - -// EVProvidedChargeStrategy mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedChargeStrategy(strategy EVChargeStrategyType) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedChargeStrategy", strategy) -} - -// EVProvidedChargeStrategy indicates an expected call of EVProvidedChargeStrategy. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedChargeStrategy(strategy any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedChargeStrategy", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedChargeStrategy), strategy) -} - -// EVProvidedEnergyDemand mocks base method. -func (m *MockEmobilityDataProvider) EVProvidedEnergyDemand(demand EVDemand) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVProvidedEnergyDemand", demand) -} - -// EVProvidedEnergyDemand indicates an expected call of EVProvidedEnergyDemand. -func (mr *MockEmobilityDataProviderMockRecorder) EVProvidedEnergyDemand(demand any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVProvidedEnergyDemand", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVProvidedEnergyDemand), demand) -} - -// EVRequestIncentives mocks base method. -func (m *MockEmobilityDataProvider) EVRequestIncentives(demand EVDemand, constraints EVIncentiveSlotConstraints) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVRequestIncentives", demand, constraints) -} - -// EVRequestIncentives indicates an expected call of EVRequestIncentives. -func (mr *MockEmobilityDataProviderMockRecorder) EVRequestIncentives(demand, constraints any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVRequestIncentives", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVRequestIncentives), demand, constraints) -} - -// EVRequestPowerLimits mocks base method. -func (m *MockEmobilityDataProvider) EVRequestPowerLimits(demand EVDemand, constraints EVTimeSlotConstraints) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "EVRequestPowerLimits", demand, constraints) -} - -// EVRequestPowerLimits indicates an expected call of EVRequestPowerLimits. -func (mr *MockEmobilityDataProviderMockRecorder) EVRequestPowerLimits(demand, constraints any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVRequestPowerLimits", reflect.TypeOf((*MockEmobilityDataProvider)(nil).EVRequestPowerLimits), demand, constraints) -} - -// MockEmobilityI is a mock of EmobilityI interface. -type MockEmobilityI struct { - ctrl *gomock.Controller - recorder *MockEmobilityIMockRecorder -} - -// MockEmobilityIMockRecorder is the mock recorder for MockEmobilityI. -type MockEmobilityIMockRecorder struct { - mock *MockEmobilityI -} - -// NewMockEmobilityI creates a new mock instance. -func NewMockEmobilityI(ctrl *gomock.Controller) *MockEmobilityI { - mock := &MockEmobilityI{ctrl: ctrl} - mock.recorder = &MockEmobilityIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEmobilityI) EXPECT() *MockEmobilityIMockRecorder { - return m.recorder -} - -// EVChargePlan mocks base method. -func (m *MockEmobilityI) EVChargePlan() (EVChargePlan, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVChargePlan") - ret0, _ := ret[0].(EVChargePlan) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVChargePlan indicates an expected call of EVChargePlan. -func (mr *MockEmobilityIMockRecorder) EVChargePlan() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargePlan", reflect.TypeOf((*MockEmobilityI)(nil).EVChargePlan)) -} - -// EVChargeStrategy mocks base method. -func (m *MockEmobilityI) EVChargeStrategy() EVChargeStrategyType { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVChargeStrategy") - ret0, _ := ret[0].(EVChargeStrategyType) - return ret0 -} - -// EVChargeStrategy indicates an expected call of EVChargeStrategy. -func (mr *MockEmobilityIMockRecorder) EVChargeStrategy() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargeStrategy", reflect.TypeOf((*MockEmobilityI)(nil).EVChargeStrategy)) -} - -// EVChargedEnergy mocks base method. -func (m *MockEmobilityI) EVChargedEnergy() (float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVChargedEnergy") - ret0, _ := ret[0].(float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVChargedEnergy indicates an expected call of EVChargedEnergy. -func (mr *MockEmobilityIMockRecorder) EVChargedEnergy() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVChargedEnergy", reflect.TypeOf((*MockEmobilityI)(nil).EVChargedEnergy)) -} - -// EVCommunicationStandard mocks base method. -func (m *MockEmobilityI) EVCommunicationStandard() (EVCommunicationStandardType, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCommunicationStandard") - ret0, _ := ret[0].(EVCommunicationStandardType) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCommunicationStandard indicates an expected call of EVCommunicationStandard. -func (mr *MockEmobilityIMockRecorder) EVCommunicationStandard() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCommunicationStandard", reflect.TypeOf((*MockEmobilityI)(nil).EVCommunicationStandard)) -} - -// EVConnected mocks base method. -func (m *MockEmobilityI) EVConnected() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVConnected") - ret0, _ := ret[0].(bool) - return ret0 -} - -// EVConnected indicates an expected call of EVConnected. -func (mr *MockEmobilityIMockRecorder) EVConnected() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVConnected", reflect.TypeOf((*MockEmobilityI)(nil).EVConnected)) -} - -// EVConnectedPhases mocks base method. -func (m *MockEmobilityI) EVConnectedPhases() (uint, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVConnectedPhases") - ret0, _ := ret[0].(uint) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVConnectedPhases indicates an expected call of EVConnectedPhases. -func (mr *MockEmobilityIMockRecorder) EVConnectedPhases() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVConnectedPhases", reflect.TypeOf((*MockEmobilityI)(nil).EVConnectedPhases)) -} - -// EVCoordinatedChargingSupported mocks base method. -func (m *MockEmobilityI) EVCoordinatedChargingSupported() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCoordinatedChargingSupported") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCoordinatedChargingSupported indicates an expected call of EVCoordinatedChargingSupported. -func (mr *MockEmobilityIMockRecorder) EVCoordinatedChargingSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCoordinatedChargingSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVCoordinatedChargingSupported)) -} - -// EVCurrentChargeState mocks base method. -func (m *MockEmobilityI) EVCurrentChargeState() (EVChargeStateType, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCurrentChargeState") - ret0, _ := ret[0].(EVChargeStateType) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCurrentChargeState indicates an expected call of EVCurrentChargeState. -func (mr *MockEmobilityIMockRecorder) EVCurrentChargeState() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentChargeState", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentChargeState)) -} - -// EVCurrentLimits mocks base method. -func (m *MockEmobilityI) EVCurrentLimits() ([]float64, []float64, []float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCurrentLimits") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].([]float64) - ret2, _ := ret[2].([]float64) - ret3, _ := ret[3].(error) - return ret0, ret1, ret2, ret3 -} - -// EVCurrentLimits indicates an expected call of EVCurrentLimits. -func (mr *MockEmobilityIMockRecorder) EVCurrentLimits() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentLimits)) -} - -// EVCurrentsPerPhase mocks base method. -func (m *MockEmobilityI) EVCurrentsPerPhase() ([]float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVCurrentsPerPhase") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVCurrentsPerPhase indicates an expected call of EVCurrentsPerPhase. -func (mr *MockEmobilityIMockRecorder) EVCurrentsPerPhase() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVCurrentsPerPhase", reflect.TypeOf((*MockEmobilityI)(nil).EVCurrentsPerPhase)) -} - -// EVEnergyDemand mocks base method. -func (m *MockEmobilityI) EVEnergyDemand() (EVDemand, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVEnergyDemand") - ret0, _ := ret[0].(EVDemand) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVEnergyDemand indicates an expected call of EVEnergyDemand. -func (mr *MockEmobilityIMockRecorder) EVEnergyDemand() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVEnergyDemand", reflect.TypeOf((*MockEmobilityI)(nil).EVEnergyDemand)) -} - -// EVIdentification mocks base method. -func (m *MockEmobilityI) EVIdentification() (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVIdentification") - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVIdentification indicates an expected call of EVIdentification. -func (mr *MockEmobilityIMockRecorder) EVIdentification() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIdentification", reflect.TypeOf((*MockEmobilityI)(nil).EVIdentification)) -} - -// EVIncentiveConstraints mocks base method. -func (m *MockEmobilityI) EVIncentiveConstraints() (EVIncentiveSlotConstraints, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVIncentiveConstraints") - ret0, _ := ret[0].(EVIncentiveSlotConstraints) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVIncentiveConstraints indicates an expected call of EVIncentiveConstraints. -func (mr *MockEmobilityIMockRecorder) EVIncentiveConstraints() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVIncentiveConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVIncentiveConstraints)) -} - -// EVLoadControlObligationLimits mocks base method. -func (m *MockEmobilityI) EVLoadControlObligationLimits() ([]float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVLoadControlObligationLimits") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVLoadControlObligationLimits indicates an expected call of EVLoadControlObligationLimits. -func (mr *MockEmobilityIMockRecorder) EVLoadControlObligationLimits() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVLoadControlObligationLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVLoadControlObligationLimits)) -} - -// EVOptimizationOfSelfConsumptionSupported mocks base method. -func (m *MockEmobilityI) EVOptimizationOfSelfConsumptionSupported() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVOptimizationOfSelfConsumptionSupported") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVOptimizationOfSelfConsumptionSupported indicates an expected call of EVOptimizationOfSelfConsumptionSupported. -func (mr *MockEmobilityIMockRecorder) EVOptimizationOfSelfConsumptionSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVOptimizationOfSelfConsumptionSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVOptimizationOfSelfConsumptionSupported)) -} - -// EVPowerPerPhase mocks base method. -func (m *MockEmobilityI) EVPowerPerPhase() ([]float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVPowerPerPhase") - ret0, _ := ret[0].([]float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVPowerPerPhase indicates an expected call of EVPowerPerPhase. -func (mr *MockEmobilityIMockRecorder) EVPowerPerPhase() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVPowerPerPhase", reflect.TypeOf((*MockEmobilityI)(nil).EVPowerPerPhase)) -} - -// EVSoC mocks base method. -func (m *MockEmobilityI) EVSoC() (float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVSoC") - ret0, _ := ret[0].(float64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVSoC indicates an expected call of EVSoC. -func (mr *MockEmobilityIMockRecorder) EVSoC() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoC", reflect.TypeOf((*MockEmobilityI)(nil).EVSoC)) -} - -// EVSoCSupported mocks base method. -func (m *MockEmobilityI) EVSoCSupported() (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVSoCSupported") - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVSoCSupported indicates an expected call of EVSoCSupported. -func (mr *MockEmobilityIMockRecorder) EVSoCSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVSoCSupported", reflect.TypeOf((*MockEmobilityI)(nil).EVSoCSupported)) -} - -// EVTimeSlotConstraints mocks base method. -func (m *MockEmobilityI) EVTimeSlotConstraints() (EVTimeSlotConstraints, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVTimeSlotConstraints") - ret0, _ := ret[0].(EVTimeSlotConstraints) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EVTimeSlotConstraints indicates an expected call of EVTimeSlotConstraints. -func (mr *MockEmobilityIMockRecorder) EVTimeSlotConstraints() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVTimeSlotConstraints", reflect.TypeOf((*MockEmobilityI)(nil).EVTimeSlotConstraints)) -} - -// EVWriteIncentives mocks base method. -func (m *MockEmobilityI) EVWriteIncentives(data []EVDurationSlotValue) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWriteIncentives", data) - ret0, _ := ret[0].(error) - return ret0 -} - -// EVWriteIncentives indicates an expected call of EVWriteIncentives. -func (mr *MockEmobilityIMockRecorder) EVWriteIncentives(data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteIncentives", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteIncentives), data) -} - -// EVWriteLoadControlLimits mocks base method. -func (m *MockEmobilityI) EVWriteLoadControlLimits(limits []EVLoadLimits) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWriteLoadControlLimits", limits) - ret0, _ := ret[0].(error) - return ret0 -} - -// EVWriteLoadControlLimits indicates an expected call of EVWriteLoadControlLimits. -func (mr *MockEmobilityIMockRecorder) EVWriteLoadControlLimits(limits any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWriteLoadControlLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWriteLoadControlLimits), limits) -} - -// EVWritePowerLimits mocks base method. -func (m *MockEmobilityI) EVWritePowerLimits(data []EVDurationSlotValue) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EVWritePowerLimits", data) - ret0, _ := ret[0].(error) - return ret0 -} - -// EVWritePowerLimits indicates an expected call of EVWritePowerLimits. -func (mr *MockEmobilityIMockRecorder) EVWritePowerLimits(data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EVWritePowerLimits", reflect.TypeOf((*MockEmobilityI)(nil).EVWritePowerLimits), data) -} diff --git a/emobility/public.go b/emobility/public.go deleted file mode 100644 index e645285..0000000 --- a/emobility/public.go +++ /dev/null @@ -1,932 +0,0 @@ -package emobility - -import ( - "errors" - "time" - - "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" - eebusUtil "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -// return if an EV is connected -// -// this includes all required features and -// minimal data being available -func (e *EMobility) EVConnected(remoteEntity api.EntityRemoteInterface) bool { - // To report an EV as being connected, also consider all required - // features to be available and assigned - if e.evEntity == nil { - return false - } - - // getting current charge state should work - if _, err := e.EVCurrentChargeState(remoteEntity); err != nil { - return false - } - - // the communication standard needs to be available - if _, err := e.EVCommunicationStandard(remoteEntity); err != nil { - return false - } - - // getting limits should work - if _, err := e.EVLoadControlObligationLimits(remoteEntity); err != nil && err != features.ErrDataNotAvailable { - // features.ErrDataNotAvailable check in case of load control limits not being provided but the feature works - return false - } - - return true -} - -// return the current charge state of the EV -func (e *EMobility) EVCurrentChargeState(remoteEntity api.EntityRemoteInterface) (EVChargeStateType, error) { - evDeviceDiagnosis, err := e.deviceDiagnosis(remoteEntity) - - if e.evEntity == nil || err != nil { - return EVChargeStateTypeUnplugged, nil - } - - diagnosisState, err := evDeviceDiagnosis.GetState() - if err != nil { - return EVChargeStateTypeUnknown, err - } - - operatingState := diagnosisState.OperatingState - if operatingState == nil { - return EVChargeStateTypeUnknown, features.ErrDataNotAvailable - } - - switch *operatingState { - case model.DeviceDiagnosisOperatingStateTypeNormalOperation: - return EVChargeStateTypeActive, nil - case model.DeviceDiagnosisOperatingStateTypeStandby: - return EVChargeStateTypePaused, nil - case model.DeviceDiagnosisOperatingStateTypeFailure: - return EVChargeStateTypeError, nil - case model.DeviceDiagnosisOperatingStateTypeFinished: - return EVChargeStateTypeFinished, nil - } - - return EVChargeStateTypeUnknown, nil -} - -// return the current loadcontrol obligation limits -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVLoadControlObligationLimits(remoteEntity api.EntityRemoteInterface) ([]float64, error) { - evLoadControl, err := e.loadControl(remoteEntity) - evElectricalConnection, err2 := e.electricalConnection(remoteEntity) - - if e.evEntity == nil || err != nil || err2 != nil { - return nil, ErrEVDisconnected - } - - // find out the appropriate limitId for each phase value - // limitDescription contains the measurementId for each limitId - limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) - if err != nil { - return nil, features.ErrDataNotAvailable - } - - var result []float64 - - for i := 0; i < 3; i++ { - phaseName := util.PhaseNameMapping[i] - - // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) - if err != nil || elParamDesc.MeasurementId == nil { - // there is no data for this phase, the phase may not exit - result = append(result, 0) - continue - } - - var limitDesc *model.LoadControlLimitDescriptionDataType - for _, desc := range limitDescriptions { - if desc.MeasurementId != nil && - elParamDesc.MeasurementId != nil && - *desc.MeasurementId == *elParamDesc.MeasurementId { - safeDesc := desc - limitDesc = &safeDesc - break - } - } - - if limitDesc == nil || limitDesc.LimitId == nil { - return nil, features.ErrDataNotAvailable - } - - limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) - if err != nil { - return nil, features.ErrDataNotAvailable - } - - var limitValue float64 - if limitIdData.Value == nil || (limitIdData.IsLimitActive != nil && !*limitIdData.IsLimitActive) { - // report maximum possible if no limit is available or the limit is not active - _, dataMax, _, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) - if err != nil { - return nil, features.ErrDataNotAvailable - } - - limitValue = dataMax - } else { - limitValue = limitIdData.Value.GetValue() - } - - result = append(result, limitValue) - } - - return result, nil -} - -// send new LoadControlLimits to the remote EV -// -// parameters: -// - limits: a set of limits for a given limit category containing phase specific limit data -// -// category obligations: -// Sets a maximum A limit for each phase that the EV may not exceed. -// Mainly used for implementing overload protection of the site or limiting the -// maximum charge power of EVs when the EV and EVSE communicate via IEC61851 -// and with ISO15118 if the EV does not support the Optimization of Self Consumption -// usecase. -// -// category recommendations: -// Sets a recommended charge power in A for each phase. This is mainly -// used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. -// The EV either needs to support the Optimization of Self Consumption usecase or -// the EVSE needs to be able map the recommendations into oligation limits which then -// works for all EVs communication either via IEC61851 or ISO15118. -// -// notes: -// - For obligations to work for optimizing solar excess power, the EV needs to have an energy demand. -// - Recommendations work even if the EV does not have an active energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. -// - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. -// - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. -// - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. -func (e *EMobility) EVWriteLoadControlLimits(remoteEntity api.EntityRemoteInterface, limits []EVLoadLimits) error { - if e.evEntity == nil { - return ErrEVDisconnected - } - - evLoadControl, err := e.loadControl(remoteEntity) - evElectricalConnection, err2 := e.electricalConnection(remoteEntity) - - if err != nil || err2 != nil { - return features.ErrDataNotAvailable - } - - var limitData []model.LoadControlLimitDataType - - for _, scope := range limits { - category := scope.Category - - for _, phaseLimit := range scope.PhaseData { - // find out the appropriate limitId for each phase value - // limitDescription contains the measurementId for each limitId - limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) - if err != nil { - continue - } - - // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) - if err != nil || elParamDesc.MeasurementId == nil { - continue - } - - var limitDesc *model.LoadControlLimitDescriptionDataType - for _, desc := range limitDescriptions { - if desc.MeasurementId != nil && - elParamDesc.MeasurementId != nil && - *desc.MeasurementId == *elParamDesc.MeasurementId { - safeDesc := desc - limitDesc = &safeDesc - break - } - } - - if limitDesc == nil || limitDesc.LimitId == nil { - continue - } - - limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) - if err != nil { - continue - } - - // EEBus_UC_TS_OverloadProtectionByEvChargingCurrentCurtailment V1.01b 3.2.1.2.2.2 - // If omitted or set to "true", the timePeriod, value and isLimitActive element SHALL be writeable by a client. - if limitIdData.IsLimitChangeable != nil && !*limitIdData.IsLimitChangeable { - continue - } - - // electricalPermittedValueSet contains the allowed min, max and the default values per phase - limit := evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) - - newLimit := model.LoadControlLimitDataType{ - LimitId: limitDesc.LimitId, - IsLimitActive: eebusUtil.Ptr(phaseLimit.IsActive), - Value: model.NewScaledNumberType(limit), - } - limitData = append(limitData, newLimit) - } - } - - _, err = evLoadControl.WriteLimitValues(limitData) - - return err -} - -// return the current communication standard type used to communicate between EVSE and EV -// -// if an EV is connected via IEC61851, no ISO15118 specific data can be provided! -// sometimes the connection starts with IEC61851 before it switches -// to ISO15118, and sometimes it falls back again. so the error return is -// never absolut for the whole connection time, except if the use case -// is not supported -// -// the values are not constant and can change due to communication problems, bugs, and -// sometimes communication starts with IEC61851 before it switches to ISO -// -// possible errors: -// - ErrDataNotAvailable if that information is not (yet) available -// - ErrNotSupported if getting the communication standard is not supported -// - and others -func (e *EMobility) EVCommunicationStandard(remoteEntity api.EntityRemoteInterface) (EVCommunicationStandardType, error) { - evDeviceConfiguration, err := e.deviceConfiguration(remoteEntity) - - if e.evEntity == nil || err != nil { - return EVCommunicationStandardTypeUnknown, ErrEVDisconnected - } - - // check if device configuration descriptions has an communication standard key name - _, err = evDeviceConfiguration.GetDescriptionForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard) - if err != nil { - return EVCommunicationStandardTypeUnknown, err - } - - data, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) - if err != nil { - return EVCommunicationStandardTypeUnknown, err - } - - if data == nil { - return EVCommunicationStandardTypeUnknown, features.ErrDataNotAvailable - } - - value := data.(*model.DeviceConfigurationKeyValueStringType) - return EVCommunicationStandardType(*value), nil -} - -// returns the identification of the currently connected EV or nil if not available -// -// possible errors: -// - ErrDataNotAvailable if that information is not (yet) available -// - and others -func (e *EMobility) EVIdentification(remoteEntity api.EntityRemoteInterface) (string, error) { - if e.evEntity == nil { - return "", ErrEVDisconnected - } - - evIdentification, err := e.identification(remoteEntity) - - if err != nil { - return "", features.ErrDataNotAvailable - } - - identifications, err := evIdentification.GetValues() - if err != nil { - return "", err - } - - for _, identification := range identifications { - value := identification.IdentificationValue - if value == nil { - continue - } - - return string(*value), nil - } - return "", nil -} - -// returns if the EVSE and EV combination support optimzation of self consumption -// -// possible errors: -// - ErrDataNotAvailable if that information is not (yet) available -// - and others -func (e *EMobility) EVOptimizationOfSelfConsumptionSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { - evLoadControl, err := e.loadControl(remoteEntity) - - if e.evEntity == nil || err != nil { - return false, ErrEVDisconnected - } - - evEntity, err := util.EntityOfTypeForSki(e.service, model.EntityTypeTypeEV, e.ski) - if err != nil { - return false, err - } - - // check if the Optimization of self consumption usecase is supported - if !util.IsUsecaseSupported(model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging, model.UseCaseActorTypeEV, evEntity.Device()) { - return false, nil - } - - // check if loadcontrol limit descriptions contains a recommendation category - if _, err = evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation); err != nil { - return false, err - } - - return true, nil -} - -// return if the EVSE and EV combination support providing an SoC -// -// requires EVSoCSupported to return true -// only works with a current ISO15118-2 with VAS or ISO15118-20 -// communication between EVSE and EV -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVSoCSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { - evMeasurement, err := e.measurement(remoteEntity) - - if e.evEntity == nil || err != nil { - return false, ErrEVDisconnected - } - - evEntity, err := util.EntityOfTypeForSki(e.service, model.EntityTypeTypeEV, e.ski) - if err != nil { - return false, err - } - - // check if the SoC usecase is supported - if !util.IsUsecaseSupported(model.UseCaseNameTypeEVStateOfCharge, model.UseCaseActorTypeEV, evEntity.Device()) { - return false, nil - } - - // check if measurement descriptions has an SoC scope type - desc, err := evMeasurement.GetDescriptionsForScope(model.ScopeTypeTypeStateOfCharge) - if err != nil { - return false, err - } - if len(desc) == 0 { - return false, features.ErrDataNotAvailable - } - - return true, nil -} - -// return the last known SoC of the connected EV -// -// requires EVSoCSupported to return true -// only works with a current ISO15118-2 with VAS or ISO15118-20 -// communication between EVSE and EV -// -// possible errors: -// - ErrNotSupported if support for SoC is not possible -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (e *EMobility) EVSoC(remoteEntity api.EntityRemoteInterface) (float64, error) { - evMeasurement, err := e.measurement(remoteEntity) - - if e.evEntity == nil || err != nil { - return 0, ErrEVDisconnected - } - - // check if the SoC is supported - support, err := e.EVSoCSupported(remoteEntity) - if err != nil { - return 0, err - } - if !support { - return 0, features.ErrNotSupported - } - - data, err := evMeasurement.GetValuesForTypeCommodityScope(model.MeasurementTypeTypePercentage, model.CommodityTypeTypeElectricity, model.ScopeTypeTypeStateOfCharge) - if err != nil { - return 0, err - } - - // we assume there is only one value, nil is already checked - value := data[0].Value - - return value.GetValue(), nil -} - -// returns if the EVSE and EV combination support coordinated charging -// -// possible errors: -// - ErrDataNotAvailable if that information is not (yet) available -// - and others -func (e *EMobility) EVCoordinatedChargingSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { - if e.evEntity == nil { - return false, ErrEVDisconnected - } - - evEntity, err := util.EntityOfTypeForSki(e.service, model.EntityTypeTypeEV, e.ski) - if err != nil { - return false, err - } - - // check if the Coordinated charging usecase is supported - if !util.IsUsecaseSupported(model.UseCaseNameTypeCoordinatedEVCharging, model.UseCaseActorTypeEV, evEntity.Device()) { - return false, nil - } - - return true, nil -} - -// returns the current charging strategy -func (e *EMobility) EVChargeStrategy(remoteEntity api.EntityRemoteInterface) EVChargeStrategyType { - evTimeSeries, err := e.timeSeries(remoteEntity) - - if e.evEntity == nil || err != nil { - return EVChargeStrategyTypeUnknown - } - - // only ISO communication can provide a charging strategy information - com, err := e.EVCommunicationStandard(remoteEntity) - if err != nil || com == EVCommunicationStandardTypeUnknown || com == EVCommunicationStandardTypeIEC61851 { - return EVChargeStrategyTypeUnknown - } - - // only the time series data for singledemand is relevant for detecting the charging strategy - data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) - if err != nil { - return EVChargeStrategyTypeUnknown - } - - // without time series slots, there is no known strategy - if data.TimeSeriesSlot == nil || len(data.TimeSeriesSlot) == 0 { - return EVChargeStrategyTypeUnknown - } - - // get the value for the first slot - firstSlot := data.TimeSeriesSlot[0] - - switch { - case firstSlot.Duration == nil: - // if value is > 0 and duration does not exist, the EV is direct charging - if firstSlot.Value != nil && firstSlot.Value.GetValue() > 0 { - return EVChargeStrategyTypeDirectCharging - } - - // maxValue will show the maximum amount the battery could take - return EVChargeStrategyTypeNoDemand - - case firstSlot.Duration != nil: - if _, err := firstSlot.Duration.GetTimeDuration(); err != nil { - // we got an invalid duration - return EVChargeStrategyTypeUnknown - } - - if firstSlot.MinValue != nil && firstSlot.MinValue.GetValue() > 0 { - return EVChargeStrategyTypeMinSoC - } - - if firstSlot.Value != nil { - if firstSlot.Value.GetValue() > 0 { - // there is demand and a duration - return EVChargeStrategyTypeTimedCharging - } - - return EVChargeStrategyTypeNoDemand - } - - } - - return EVChargeStrategyTypeUnknown -} - -// returns the current energy demand in Wh and the duration -func (e *EMobility) EVEnergyDemand(remoteEntity api.EntityRemoteInterface) (EVDemand, error) { - demand := EVDemand{} - - if e.evEntity == nil { - return demand, ErrEVDisconnected - } - - evTimeSeries, err := e.timeSeries(remoteEntity) - - if err != nil { - return demand, features.ErrDataNotAvailable - } - - data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) - if err != nil { - return demand, features.ErrDataNotAvailable - } - - // we need at least a time series slot - if data.TimeSeriesSlot == nil { - return demand, features.ErrDataNotAvailable - } - - // get the value for the first slot, ignore all others, which - // in the tests so far always have min/max/value 0 - firstSlot := data.TimeSeriesSlot[0] - if firstSlot.MinValue != nil { - demand.MinDemand = firstSlot.MinValue.GetValue() - } - if firstSlot.Value != nil { - demand.OptDemand = firstSlot.Value.GetValue() - } - if firstSlot.MaxValue != nil { - demand.MaxDemand = firstSlot.MaxValue.GetValue() - } - if firstSlot.Duration != nil { - if tempDuration, err := firstSlot.Duration.GetTimeDuration(); err == nil { - demand.DurationUntilEnd = tempDuration.Seconds() - } - } - - // start time has to be defined either in TimePeriod or the first slot - relStartTime := time.Duration(0) - - startTimeSet := false - if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { - if temp, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { - relStartTime = temp - startTimeSet = true - } - } - - if !startTimeSet { - if firstSlot.TimePeriod != nil && firstSlot.TimePeriod.StartTime != nil { - if temp, err := firstSlot.TimePeriod.StartTime.GetTimeDuration(); err == nil { - relStartTime = temp - } - } - } - - demand.DurationUntilStart = relStartTime.Seconds() - - return demand, nil -} - -func (e *EMobility) EVChargePlanConstraints(remoteEntity api.EntityRemoteInterface) ([]EVDurationSlotValue, error) { - constraints := []EVDurationSlotValue{} - - if e.evEntity == nil { - return constraints, ErrEVDisconnected - } - - evTimeSeries, err := e.timeSeries(remoteEntity) - - if err != nil { - return constraints, features.ErrDataNotAvailable - } - - data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeConstraints) - if err != nil { - return constraints, features.ErrDataNotAvailable - } - - // we need at least a time series slot - if data.TimeSeriesSlot == nil { - return constraints, features.ErrDataNotAvailable - } - - // get the values for all slots - for _, slot := range data.TimeSeriesSlot { - newSlot := EVDurationSlotValue{} - - if slot.Duration != nil { - if duration, err := slot.Duration.GetTimeDuration(); err == nil { - newSlot.Duration = duration - } - } else if slot.TimePeriod != nil { - var slotStart, slotEnd time.Time - if slot.TimePeriod.StartTime != nil { - if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { - slotStart = time - } - } - if slot.TimePeriod.EndTime != nil { - if time, err := slot.TimePeriod.EndTime.GetTime(); err == nil { - slotEnd = time - } - } - newSlot.Duration = slotEnd.Sub(slotStart) - } - - if slot.MaxValue != nil { - newSlot.Value = slot.MaxValue.GetValue() - } - - constraints = append(constraints, newSlot) - } - - return constraints, nil -} - -func (e *EMobility) EVChargePlan(remoteEntity api.EntityRemoteInterface) (EVChargePlan, error) { - plan := EVChargePlan{} - - if e.evEntity == nil { - return plan, ErrEVDisconnected - } - - evTimeSeries, err := e.timeSeries(remoteEntity) - - if err != nil { - return plan, features.ErrDataNotAvailable - } - - data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) - if err != nil { - return plan, features.ErrDataNotAvailable - } - - // we need at least a time series slot - if data.TimeSeriesSlot == nil { - return plan, features.ErrDataNotAvailable - } - - startAvailable := false - // check the start time relative to now of the plan, default is now - currentStart := time.Now() - currentEnd := currentStart - if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { - - if start, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { - currentStart = currentStart.Add(start) - startAvailable = true - } - } - - // get the values for all slots - for index, slot := range data.TimeSeriesSlot { - newSlot := EVChargePlanSlotValue{} - - slotStartDefined := false - if index == 0 && startAvailable && (slot.TimePeriod == nil || slot.TimePeriod.StartTime == nil) { - newSlot.Start = currentStart - slotStartDefined = true - } - if slot.TimePeriod != nil && slot.TimePeriod.StartTime != nil { - if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { - newSlot.Start = time - slotStartDefined = true - } - } - if !slotStartDefined { - newSlot.Start = currentEnd - } - - if slot.Duration != nil { - if duration, err := slot.Duration.GetTimeDuration(); err == nil { - newSlot.End = newSlot.Start.Add(duration) - currentEnd = newSlot.End - } - } else if slot.TimePeriod != nil && slot.TimePeriod.EndTime != nil { - if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { - newSlot.End = time - currentEnd = newSlot.End - } - } - - if slot.Value != nil { - newSlot.Value = slot.Value.GetValue() - } - if slot.MinValue != nil { - newSlot.MinValue = slot.MinValue.GetValue() - } - if slot.MaxValue != nil { - newSlot.MaxValue = slot.MaxValue.GetValue() - } - - plan.Slots = append(plan.Slots, newSlot) - } - - return plan, nil -} - -// returns the constraints for the time slots -func (e *EMobility) EVTimeSlotConstraints(remoteEntity api.EntityRemoteInterface) (EVTimeSlotConstraints, error) { - result := EVTimeSlotConstraints{} - - evTimeSeries, err := e.timeSeries(remoteEntity) - - if e.evEntity == nil || err != nil { - return result, features.ErrDataNotAvailable - } - - constraints, err := evTimeSeries.GetConstraints() - if err != nil { - return result, err - } - - // only use the first constraint - constraint := constraints[0] - - if constraint.SlotCountMin != nil { - result.MinSlots = uint(*constraint.SlotCountMin) - } - if constraint.SlotCountMax != nil { - result.MaxSlots = uint(*constraint.SlotCountMax) - } - if constraint.SlotDurationMin != nil { - if duration, err := constraint.SlotDurationMin.GetTimeDuration(); err == nil { - result.MinSlotDuration = duration - } - } - if constraint.SlotDurationMax != nil { - if duration, err := constraint.SlotDurationMax.GetTimeDuration(); err == nil { - result.MaxSlotDuration = duration - } - } - if constraint.SlotDurationStepSize != nil { - if duration, err := constraint.SlotDurationStepSize.GetTimeDuration(); err == nil { - result.SlotDurationStepSize = duration - } - } - - return result, nil -} - -// send power limits to the EV -func (e *EMobility) EVWritePowerLimits(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error { - evTimeSeries, err := e.timeSeries(remoteEntity) - - if e.evEntity == nil || err != nil { - return ErrNotSupported - } - - if len(data) == 0 { - return errors.New("missing power limit data") - } - - constraints, err := e.EVTimeSlotConstraints(remoteEntity) - if err != nil { - return err - } - - if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { - return errors.New("too few charge slots provided") - } - - if constraints.MaxSlots != 0 && constraints.MaxSlots < uint(len(data)) { - return errors.New("too many charge slots provided") - } - - desc, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) - if err != nil { - return ErrNotSupported - } - - timeSeriesSlots := []model.TimeSeriesSlotType{} - var totalDuration time.Duration - for index, slot := range data { - relativeStart := totalDuration - - timeSeriesSlot := model.TimeSeriesSlotType{ - TimeSeriesSlotId: eebusUtil.Ptr(model.TimeSeriesSlotIdType(index)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(relativeStart), - }, - MaxValue: model.NewScaledNumberType(slot.Value), - } - - // the last slot also needs an End Time - if index == len(data)-1 { - relativeEndTime := relativeStart + slot.Duration - timeSeriesSlot.TimePeriod.EndTime = model.NewAbsoluteOrRelativeTimeTypeFromDuration(relativeEndTime) - } - timeSeriesSlots = append(timeSeriesSlots, timeSeriesSlot) - - totalDuration += slot.Duration - } - - timeSeriesData := model.TimeSeriesDataType{ - TimeSeriesId: desc.TimeSeriesId, - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(totalDuration), - }, - TimeSeriesSlot: timeSeriesSlots, - } - - _, err = evTimeSeries.WriteValues([]model.TimeSeriesDataType{timeSeriesData}) - - return err -} - -// returns the minimum and maximum number of incentive slots allowed -func (e *EMobility) EVIncentiveConstraints(remoteEntity api.EntityRemoteInterface) (EVIncentiveSlotConstraints, error) { - result := EVIncentiveSlotConstraints{} - - evIncentiveTable, err := e.incentiveTable(remoteEntity) - if e.evEntity == nil || err != nil { - return result, features.ErrDataNotAvailable - } - - constraints, err := evIncentiveTable.GetConstraints() - if err != nil { - return result, err - } - - // only use the first constraint - constraint := constraints[0] - - if constraint.IncentiveSlotConstraints.SlotCountMin != nil { - result.MinSlots = uint(*constraint.IncentiveSlotConstraints.SlotCountMin) - } - if constraint.IncentiveSlotConstraints.SlotCountMax != nil { - result.MaxSlots = uint(*constraint.IncentiveSlotConstraints.SlotCountMax) - } - - return result, nil -} - -// send incentives to the EV -func (e *EMobility) EVWriteIncentives(remoteEntity api.EntityRemoteInterface, data []EVDurationSlotValue) error { - evIncentiveTable, err := e.incentiveTable(remoteEntity) - - if e.evEntity == nil || err != nil { - return features.ErrDataNotAvailable - } - - if len(data) == 0 { - return errors.New("missing incentive data") - } - - constraints, err := e.EVIncentiveConstraints(remoteEntity) - if err != nil { - return err - } - - if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { - return errors.New("too few charge slots provided") - } - - if constraints.MaxSlots != 0 && constraints.MaxSlots < uint(len(data)) { - return errors.New("too many charge slots provided") - } - - incentiveSlots := []model.IncentiveTableIncentiveSlotType{} - var totalDuration time.Duration - for index, slot := range data { - relativeStart := totalDuration - - timeInterval := &model.TimeTableDataType{ - StartTime: &model.AbsoluteOrRecurringTimeType{ - Relative: model.NewDurationType(relativeStart), - }, - } - - // the last slot also needs an End Time - if index == len(data)-1 { - relativeEndTime := relativeStart + slot.Duration - timeInterval.EndTime = &model.AbsoluteOrRecurringTimeType{ - Relative: model.NewDurationType(relativeEndTime), - } - } - - incentiveSlot := model.IncentiveTableIncentiveSlotType{ - TimeInterval: timeInterval, - Tier: []model.IncentiveTableTierType{ - { - Tier: &model.TierDataType{ - TierId: eebusUtil.Ptr(model.TierIdType(1)), - }, - Boundary: []model.TierBoundaryDataType{ - { - BoundaryId: eebusUtil.Ptr(model.TierBoundaryIdType(1)), // only 1 boundary exists - LowerBoundaryValue: model.NewScaledNumberType(0), - }, - }, - Incentive: []model.IncentiveDataType{ - { - IncentiveId: eebusUtil.Ptr(model.IncentiveIdType(1)), // always use price - Value: model.NewScaledNumberType(slot.Value), - }, - }, - }, - }, - } - incentiveSlots = append(incentiveSlots, incentiveSlot) - - totalDuration += slot.Duration - } - - incentiveData := model.IncentiveTableType{ - Tariff: &model.TariffDataType{ - TariffId: eebusUtil.Ptr(model.TariffIdType(0)), - }, - IncentiveSlot: incentiveSlots, - } - - _, err = evIncentiveTable.WriteValues([]model.IncentiveTableType{incentiveData}) - - return err -} diff --git a/emobility/public_EVChargePlan_test.go b/emobility/public_EVChargePlan_test.go deleted file mode 100644 index c6345cf..0000000 --- a/emobility/public_EVChargePlan_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - gomock "go.uber.org/mock/gomock" -) - -func Test_EVChargePlan(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - _, err := emobilty.EVChargePlan(mockRemoteEntity) - assert.NotNil(t, err) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - ctrl := gomock.NewController(t) - - dataProviderMock := NewMockEmobilityDataProvider(ctrl) - emobilty.dataProvider = dataProviderMock - - _, err = emobilty.EVChargePlan(emobilty.evEntity) - assert.NotNil(t, err) - - _, err = emobilty.EVChargePlan(emobilty.evEntity) - assert.NotNil(t, err) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), - TimeSeriesWriteable: util.Ptr(true), - UpdateRequired: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypePlan), - TimeSeriesWriteable: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeSingleDemand), - TimeSeriesWriteable: util.Ptr(false), - Unit: util.Ptr(model.UnitOfMeasurementTypeWh), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - _, err = emobilty.EVChargePlan(emobilty.evEntity) - assert.NotNil(t, err) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("PT5M36S")), - MaxValue: model.NewScaledNumberType(4201), - }, - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: util.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - _, err = emobilty.EVChargePlan(emobilty.evEntity) - assert.Nil(t, err) -} diff --git a/emobility/public_EVChargeStrategy_test.go b/emobility/public_EVChargeStrategy_test.go deleted file mode 100644 index 4f95e6d..0000000 --- a/emobility/public_EVChargeStrategy_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package emobility - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVChargeStrategy(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data := emobilty.EVChargeStrategy(mockRemoteEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err := localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - cmd = []model.CmdType{{ - DeviceConfigurationKeyValueListData: &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - Value: &model.DeviceConfigurationKeyValueValueType{ - String: util.Ptr(model.DeviceConfigurationKeyValueStringType(EVCommunicationStandardTypeISO151182ED1)), - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeSingleDemand), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeUnknown, data) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeNoDemand, data) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Duration: util.Ptr(model.DurationType("PT0S")), - Value: model.NewScaledNumberType(0), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeNoDemand, data) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Value: model.NewScaledNumberType(10000), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeDirectCharging, data) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Value: model.NewScaledNumberType(10000), - Duration: model.NewDurationType(2 * time.Hour), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data = emobilty.EVChargeStrategy(emobilty.evEntity) - assert.Equal(t, EVChargeStrategyTypeTimedCharging, data) -} diff --git a/emobility/public_EVCoordinatedChargingSupported_test.go b/emobility/public_EVCoordinatedChargingSupported_test.go deleted file mode 100644 index 6df2eff..0000000 --- a/emobility/public_EVCoordinatedChargingSupported_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" -) - -func Test_EVCoordinatedChargingSupported(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - data, err := emobilty.EVCoordinatedChargingSupported(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, false, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVCoordinatedChargingSupported(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, false, data) - - datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) - - cmd := []model.CmdType{{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ - UseCaseInformation: []model.UseCaseInformationDataType{ - { - Actor: util.Ptr(model.UseCaseActorTypeEV), - UseCaseSupport: []model.UseCaseSupportType{ - { - UseCaseName: util.Ptr(model.UseCaseNameTypeCoordinatedEVCharging), - UseCaseAvailable: util.Ptr(true), - ScenarioSupport: []model.UseCaseScenarioSupportType{1}, - }, - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCoordinatedChargingSupported(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, true, data) -} diff --git a/emobility/public_EVCurrentChargeState_test.go b/emobility/public_EVCurrentChargeState_test.go deleted file mode 100644 index 050c789..0000000 --- a/emobility/public_EVCurrentChargeState_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVCurrentChargeState(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVCurrentChargeState(mockRemoteEntity) - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypeUnplugged, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, EVChargeStateTypeUnknown, data) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - DeviceDiagnosisStateData: &model.DeviceDiagnosisStateDataType{ - OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypeActive, data) - - cmd = []model.CmdType{{ - DeviceDiagnosisStateData: &model.DeviceDiagnosisStateDataType{ - OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeStandby), - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypePaused, data) - - cmd = []model.CmdType{{ - DeviceDiagnosisStateData: &model.DeviceDiagnosisStateDataType{ - OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeFailure), - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypeError, data) - - cmd = []model.CmdType{{ - DeviceDiagnosisStateData: &model.DeviceDiagnosisStateDataType{ - OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeFinished), - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypeFinished, data) - - cmd = []model.CmdType{{ - DeviceDiagnosisStateData: &model.DeviceDiagnosisStateDataType{ - OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeInAlarm), - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVCurrentChargeState(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, EVChargeStateTypeUnknown, data) -} diff --git a/emobility/public_EVEnergyDemand_test.go b/emobility/public_EVEnergyDemand_test.go deleted file mode 100644 index 9d5c2d4..0000000 --- a/emobility/public_EVEnergyDemand_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package emobility - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" -) - -func Test_EVEnergySingleDemand(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - demand, err := emobilty.EVEnergyDemand(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - DeviceConfigurationKeyValueDescriptionListData: &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - cmd = []model.CmdType{{ - DeviceConfigurationKeyValueListData: &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ - { - KeyId: util.Ptr(model.DeviceConfigurationKeyIdType(0)), - Value: &model.DeviceConfigurationKeyValueValueType{ - String: util.Ptr(model.DeviceConfigurationKeyValueStringType(EVCommunicationStandardTypeISO151182ED1)), - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeSingleDemand), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 0.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - MinValue: model.NewScaledNumberType(1000), - Value: model.NewScaledNumberType(10000), - MaxValue: model.NewScaledNumberType(100000), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 1000.0, demand.MinDemand) - assert.Equal(t, 10000.0, demand.OptDemand) - assert.Equal(t, 100000.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, 0.0, demand.DurationUntilEnd) - - cmd = []model.CmdType{{ - TimeSeriesListData: &model.TimeSeriesListDataType{ - TimeSeriesData: []model.TimeSeriesDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimePeriod: &model.TimePeriodType{ - StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), - }, - TimeSeriesSlot: []model.TimeSeriesSlotType{ - { - TimeSeriesSlotId: util.Ptr(model.TimeSeriesSlotIdType(0)), - Value: model.NewScaledNumberType(10000), - Duration: model.NewDurationType(2 * time.Hour), - }, - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - demand, err = emobilty.EVEnergyDemand(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 0.0, demand.MinDemand) - assert.Equal(t, 10000.0, demand.OptDemand) - assert.Equal(t, 0.0, demand.MaxDemand) - assert.Equal(t, 0.0, demand.DurationUntilStart) - assert.Equal(t, time.Duration(2*time.Hour).Seconds(), demand.DurationUntilEnd) -} diff --git a/emobility/public_EVIncentiveConstraints_test.go b/emobility/public_EVIncentiveConstraints_test.go deleted file mode 100644 index 1609722..0000000 --- a/emobility/public_EVIncentiveConstraints_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVGetIncentiveConstraints(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - constraints, err := emobilty.EVIncentiveConstraints(mockRemoteEntity) - assert.Equal(t, uint(0), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.NotEqual(t, err, nil) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) - assert.Equal(t, uint(0), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.NotEqual(t, err, nil) - - constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) - assert.Equal(t, uint(0), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.NotEqual(t, err, nil) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ - IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ - { - IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ - SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), - SlotCountMax: util.Ptr(model.TimeSlotCountType(10)), - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) - assert.Equal(t, uint(1), constraints.MinSlots) - assert.Equal(t, uint(10), constraints.MaxSlots) - assert.Equal(t, err, nil) - - cmd = []model.CmdType{{ - IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ - IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ - { - IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ - SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), - }, - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - constraints, err = emobilty.EVIncentiveConstraints(emobilty.evEntity) - assert.Equal(t, uint(1), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.Equal(t, err, nil) - -} diff --git a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go b/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go deleted file mode 100644 index 496a1a7..0000000 --- a/emobility/public_EVOptimizationOfSelfConsumptionSupported_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVOptimizationOfSelfConsumptionSupported(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVOptimizationOfSelfConsumptionSupported(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, false, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, false, data) - - datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) - - cmd := []model.CmdType{{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ - UseCaseInformation: []model.UseCaseInformationDataType{ - { - Actor: util.Ptr(model.UseCaseActorTypeEV), - UseCaseSupport: []model.UseCaseSupportType{ - { - UseCaseName: util.Ptr(model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging), - UseCaseAvailable: util.Ptr(true), - ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3}, - }, - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, false, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - LoadControlLimitDescriptionListData: &model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ - { - LimitId: util.Ptr(model.LoadControlLimitIdType(0)), - LimitCategory: util.Ptr(model.LoadControlCategoryTypeRecommendation), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVOptimizationOfSelfConsumptionSupported(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, true, data) -} diff --git a/emobility/public_EVSoCSupported_test.go b/emobility/public_EVSoCSupported_test.go deleted file mode 100644 index 12d5f6d..0000000 --- a/emobility/public_EVSoCSupported_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVSoCSupported(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVSoCSupported(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, false, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVSoCSupported(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, false, data) - - datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) - - cmd := []model.CmdType{{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ - UseCaseInformation: []model.UseCaseInformationDataType{ - { - Actor: util.Ptr(model.UseCaseActorTypeEV), - UseCaseSupport: []model.UseCaseSupportType{ - { - UseCaseName: util.Ptr(model.UseCaseNameTypeEVStateOfCharge), - UseCaseAvailable: util.Ptr(true), - ScenarioSupport: []model.UseCaseScenarioSupportType{1}, - }, - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVSoCSupported(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, false, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - ScopeType: util.Ptr(model.ScopeTypeTypeStateOfCharge), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVSoCSupported(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, true, data) -} diff --git a/emobility/public_EVSoC_test.go b/emobility/public_EVSoC_test.go deleted file mode 100644 index 7d6dc25..0000000 --- a/emobility/public_EVSoC_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package emobility - -import ( - "testing" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVSoC(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - data, err := emobilty.EVSoC(mockRemoteEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - data, err = emobilty.EVSoC(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - data, err = emobilty.EVSoC(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - datagram := datagramForEntityAndFeatures(true, localDevice, localEntity, nil, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial, model.RoleTypeSpecial) - - cmd := []model.CmdType{{ - NodeManagementUseCaseData: &model.NodeManagementUseCaseDataType{ - UseCaseInformation: []model.UseCaseInformationDataType{ - { - Actor: util.Ptr(model.UseCaseActorTypeEV), - UseCaseSupport: []model.UseCaseSupportType{ - { - UseCaseName: util.Ptr(model.UseCaseNameTypeEVStateOfCharge), - UseCaseAvailable: util.Ptr(true), - ScenarioSupport: []model.UseCaseScenarioSupportType{1}, - }, - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVSoC(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementDescriptionListData: &model.MeasurementDescriptionListDataType{ - MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - MeasurementType: util.Ptr(model.MeasurementTypeTypePercentage), - CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), - ScopeType: util.Ptr(model.ScopeTypeTypeStateOfCharge), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVSoC(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementListData: &model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVSoC(emobilty.evEntity) - assert.NotNil(t, err) - assert.Equal(t, 0.0, data) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - MeasurementListData: &model.MeasurementListDataType{ - MeasurementData: []model.MeasurementDataType{ - { - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - Value: model.NewScaledNumberType(80), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - data, err = emobilty.EVSoC(emobilty.evEntity) - assert.Nil(t, err) - assert.Equal(t, 80.0, data) -} diff --git a/emobility/public_EVTimeSlotConstraints_test.go b/emobility/public_EVTimeSlotConstraints_test.go deleted file mode 100644 index f763a21..0000000 --- a/emobility/public_EVTimeSlotConstraints_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package emobility - -import ( - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVGetTimeSlotConstraints(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - constraints, err := emobilty.EVTimeSlotConstraints(mockRemoteEntity) - assert.Equal(t, uint(0), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) - assert.Equal(t, time.Duration(0), constraints.MaxSlotDuration) - assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) - assert.NotEqual(t, err, nil) - - localDevice, localEntity, remoteDevice, entites, _ := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - constraints, err = emobilty.EVTimeSlotConstraints(emobilty.evEntity) - assert.Equal(t, uint(0), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) - assert.Equal(t, time.Duration(0), constraints.MaxSlotDuration) - assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) - assert.NotEqual(t, err, nil) - - constraints, err = emobilty.EVTimeSlotConstraints(emobilty.evEntity) - assert.Equal(t, uint(0), constraints.MinSlots) - assert.Equal(t, uint(0), constraints.MaxSlots) - assert.Equal(t, time.Duration(0), constraints.MinSlotDuration) - assert.Equal(t, time.Duration(0), constraints.MaxSlotDuration) - assert.Equal(t, time.Duration(0), constraints.SlotDurationStepSize) - assert.NotEqual(t, err, nil) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - SlotCountMin: util.Ptr(model.TimeSeriesSlotCountType(1)), - SlotCountMax: util.Ptr(model.TimeSeriesSlotCountType(10)), - SlotDurationMin: model.NewDurationType(1 * time.Minute), - SlotDurationMax: model.NewDurationType(60 * time.Minute), - SlotDurationStepSize: model.NewDurationType(1 * time.Minute), - }, - }, - }}} - - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - constraints, err = emobilty.EVTimeSlotConstraints(emobilty.evEntity) - assert.Equal(t, uint(1), constraints.MinSlots) - assert.Equal(t, uint(10), constraints.MaxSlots) - assert.Equal(t, time.Duration(1*time.Minute), constraints.MinSlotDuration) - assert.Equal(t, time.Duration(1*time.Hour), constraints.MaxSlotDuration) - assert.Equal(t, time.Duration(1*time.Minute), constraints.SlotDurationStepSize) - assert.Equal(t, err, nil) -} diff --git a/emobility/public_EVWriteIncentives_test.go b/emobility/public_EVWriteIncentives_test.go deleted file mode 100644 index 0ddb08a..0000000 --- a/emobility/public_EVWriteIncentives_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package emobility - -import ( - "encoding/json" - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVWriteIncentives(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - data := []EVDurationSlotValue{} - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - err := emobilty.EVWriteIncentives(mockRemoteEntity, data) - assert.NotNil(t, err) - - localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - err = emobilty.EVWriteIncentives(emobilty.evEntity, data) - assert.NotNil(t, err) - - err = emobilty.EVWriteIncentives(emobilty.evEntity, data) - assert.NotNil(t, err) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ - IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ - { - IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ - SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), - SlotCountMax: util.Ptr(model.TimeSlotCountType(10)), - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWriteIncentives(emobilty.evEntity, data) - assert.NotNil(t, err) - - type dataStruct struct { - error bool - minSlots, maxSlots uint - slots []EVDurationSlotValue - } - - tests := []struct { - name string - data []dataStruct - }{ - { - "too few slots", - []dataStruct{ - { - true, 2, 2, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 0.1}, - }, - }, - }, - }, { - "too many slots", - []dataStruct{ - { - true, 1, 1, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 0.1}, - {Duration: time.Hour, Value: 0.1}, - }, - }, - }, - }, - { - "1 slot", - []dataStruct{ - { - false, 1, 1, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 0.1}, - }, - }, - }, - }, - { - "2 slots", - []dataStruct{ - { - false, 1, 2, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 0.1}, - {Duration: 30 * time.Minute, Value: 0.2}, - }, - }, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - for _, data := range tc.data { - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - IncentiveTableConstraintsData: &model.IncentiveTableConstraintsDataType{ - IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ - { - IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ - SlotCountMin: util.Ptr(model.TimeSlotCountType(data.minSlots)), - SlotCountMax: util.Ptr(model.TimeSlotCountType(data.maxSlots)), - }, - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWriteIncentives(emobilty.evEntity, data.slots) - if data.error { - assert.NotNil(t, err) - continue - } else { - assert.Nil(t, err) - } - - sentDatagram := model.Datagram{} - sentBytes := writeHandler.LastMessage() - err := json.Unmarshal(sentBytes, &sentDatagram) - assert.Nil(t, err) - - sentCmd := sentDatagram.Datagram.Payload.Cmd - assert.Equal(t, 1, len(sentCmd)) - - sentIncentiveData := sentCmd[0].IncentiveTableData.IncentiveTable[0].IncentiveSlot - assert.Equal(t, len(data.slots), len(sentIncentiveData)) - - for index, item := range sentIncentiveData { - assert.Equal(t, data.slots[index].Value, item.Tier[0].Incentive[0].Value.GetValue()) - } - } - }) - } -} diff --git a/emobility/public_EVWriteLoadControlLimits_test.go b/emobility/public_EVWriteLoadControlLimits_test.go deleted file mode 100644 index 64b1c65..0000000 --- a/emobility/public_EVWriteLoadControlLimits_test.go +++ /dev/null @@ -1,276 +0,0 @@ -package emobility - -import ( - "encoding/json" - "testing" - - "github.com/enbility/cemd/util" - eebusUtil "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "golang.org/x/exp/slices" -) - -func Test_EVWriteLoadControlLimits(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - loadLimits := []EVLoadLimits{} - - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - err := emobilty.EVWriteLoadControlLimits(mockRemoteEntity, loadLimits) - assert.NotNil(t, err) - - localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) - assert.NotNil(t, err) - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) - assert.NotNil(t, err) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{ - ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ - { - ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(0)), - AcMeasuredPhases: eebusUtil.Ptr(model.ElectricalConnectionPhaseNameTypeA), - }, - { - ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(1)), - MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(1)), - AcMeasuredPhases: eebusUtil.Ptr(model.ElectricalConnectionPhaseNameTypeB), - }, - { - ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(2)), - MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(2)), - AcMeasuredPhases: eebusUtil.Ptr(model.ElectricalConnectionPhaseNameTypeC), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) - assert.NotNil(t, err) - - type dataStruct struct { - phases int - permittedDefaultExists bool - permittedDefaultValue float64 - permittedMinValue float64 - permittedMaxValue float64 - obligations, obligationsExpected []float64 - recommendations, recommendationsExpected []float64 - } - - tests := []struct { - name string - data []dataStruct - }{ - { - "1 Phase ISO15118", - []dataStruct{ - {1, true, 0.1, 2, 16, []float64{0}, []float64{0.1}, []float64{}, []float64{}}, - {1, true, 0.1, 2, 16, []float64{2.2}, []float64{2.2}, []float64{}, []float64{}}, - {1, true, 0.1, 2, 16, []float64{10}, []float64{10}, []float64{}, []float64{}}, - {1, true, 0.1, 2, 16, []float64{16}, []float64{16}, []float64{}, []float64{}}, - }, - }, - { - "3 Phase ISO15118", - []dataStruct{ - {3, true, 0.1, 2, 16, []float64{0, 0, 0}, []float64{0.1, 0.1, 0.1}, []float64{}, []float64{}}, - {3, true, 0.1, 2, 16, []float64{2.2, 2.2, 2.2}, []float64{2.2, 2.2, 2.2}, []float64{}, []float64{}}, - {3, true, 0.1, 2, 16, []float64{10, 10, 10}, []float64{10, 10, 10}, []float64{}, []float64{}}, - {3, true, 0.1, 2, 16, []float64{16, 16, 16}, []float64{16, 16, 16}, []float64{}, []float64{}}, - }, - }, - { - "1 Phase IEC61851", - []dataStruct{ - {1, true, 0, 6, 16, []float64{0}, []float64{0}, []float64{}, []float64{}}, - {1, true, 0, 6, 16, []float64{6}, []float64{6}, []float64{}, []float64{}}, - {1, true, 0, 6, 16, []float64{10}, []float64{10}, []float64{}, []float64{}}, - {1, true, 0, 6, 16, []float64{16}, []float64{16}, []float64{}, []float64{}}, - }, - }, - { - "3 Phase IEC61851", - []dataStruct{ - {3, true, 0, 6, 16, []float64{0, 0, 0}, []float64{0, 0, 0}, []float64{}, []float64{}}, - {3, true, 0, 6, 16, []float64{6, 6, 6}, []float64{6, 6, 6}, []float64{}, []float64{}}, - {3, true, 0, 6, 16, []float64{10, 10, 10}, []float64{10, 10, 10}, []float64{}, []float64{}}, - {3, true, 0, 6, 16, []float64{16, 16, 16}, []float64{16, 16, 16}, []float64{}, []float64{}}, - }, - }, - { - "3 Phase IEC61851 Elli", - []dataStruct{ - {3, false, 0, 6, 16, []float64{0, 0, 0}, []float64{0, 0, 0}, []float64{}, []float64{}}, - {3, false, 0, 6, 16, []float64{6, 6, 6}, []float64{6, 6, 6}, []float64{}, []float64{}}, - {3, false, 0, 6, 16, []float64{10, 10, 10}, []float64{10, 10, 10}, []float64{}, []float64{}}, - {3, false, 0, 6, 16, []float64{16, 16, 16}, []float64{16, 16, 16}, []float64{}, []float64{}}, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} - permittedData := []model.ScaledNumberSetType{} - for _, data := range tc.data { - for phase := 0; phase < data.phases; phase++ { - item := model.ScaledNumberSetType{ - Range: []model.ScaledNumberRangeType{ - { - Min: model.NewScaledNumberType(data.permittedMinValue), - Max: model.NewScaledNumberType(data.permittedMaxValue), - }, - }, - } - if data.permittedDefaultExists { - item.Value = []model.ScaledNumberType{*model.NewScaledNumberType(data.permittedDefaultValue)} - } - permittedData = append(permittedData, item) - - permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ - ElectricalConnectionId: eebusUtil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusUtil.Ptr(model.ElectricalConnectionParameterIdType(phase)), - PermittedValueSet: permittedData, - } - dataSet = append(dataSet, permittedItem) - } - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - ElectricalConnectionPermittedValueSetListData: &model.ElectricalConnectionPermittedValueSetListDataType{ - ElectricalConnectionPermittedValueSetData: dataSet, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) - assert.NotNil(t, err) - - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer, model.RoleTypeClient) - - limitDesc := []model.LoadControlLimitDescriptionDataType{} - var limitIdsObligation, limitIdsRecommendation []model.LoadControlLimitIdType - for index := range data.obligations { - id := model.LoadControlLimitIdType(index) - limitItem := model.LoadControlLimitDescriptionDataType{ - LimitId: eebusUtil.Ptr(id), - LimitCategory: eebusUtil.Ptr(model.LoadControlCategoryTypeObligation), - MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(index)), - } - limitDesc = append(limitDesc, limitItem) - limitIdsObligation = append(limitIdsObligation, id) - } - add := len(limitDesc) - for index := range data.recommendations { - id := model.LoadControlLimitIdType(index + add) - limitItem := model.LoadControlLimitDescriptionDataType{ - LimitId: eebusUtil.Ptr(id), - LimitCategory: eebusUtil.Ptr(model.LoadControlCategoryTypeRecommendation), - MeasurementId: eebusUtil.Ptr(model.MeasurementIdType(index + add)), - } - limitDesc = append(limitDesc, limitItem) - limitIdsRecommendation = append(limitIdsRecommendation, id) - } - - cmd = []model.CmdType{{ - LoadControlLimitDescriptionListData: &model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: limitDesc, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) - assert.NotNil(t, err) - - limitData := []model.LoadControlLimitDataType{} - for index := range limitDesc { - limitItem := model.LoadControlLimitDataType{ - LimitId: eebusUtil.Ptr(model.LoadControlLimitIdType(index)), - IsLimitChangeable: eebusUtil.Ptr(true), - } - limitData = append(limitData, limitItem) - } - sentLimits := len(limitData) - - cmd = []model.CmdType{{ - LoadControlLimitListData: &model.LoadControlLimitListDataType{ - LoadControlLimitData: limitData, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, loadLimits) - assert.NotNil(t, err) - - obligations := []EVLoadLimitsPhase{} - for index, obligation := range data.obligations { - phase := util.PhaseNameMapping[index] - obligations = append(obligations, EVLoadLimitsPhase{ - Phase: phase, - IsActive: true, - Value: obligation, - }) - } - recommendations := []EVLoadLimitsPhase{} - for index, obligation := range data.recommendations { - phase := util.PhaseNameMapping[index] - obligations = append(obligations, EVLoadLimitsPhase{ - Phase: phase, - IsActive: true, - Value: obligation, - }) - } - - err = emobilty.EVWriteLoadControlLimits(emobilty.evEntity, []EVLoadLimits{ - {Category: model.LoadControlCategoryTypeObligation, PhaseData: obligations}, - {Category: model.LoadControlCategoryTypeRecommendation, PhaseData: recommendations}, - }) - assert.Nil(t, err) - - sentDatagram := model.Datagram{} - sentBytes := writeHandler.LastMessage() - err := json.Unmarshal(sentBytes, &sentDatagram) - assert.Nil(t, err) - - sentCmd := sentDatagram.Datagram.Payload.Cmd - assert.Equal(t, 1, len(sentCmd)) - - sentLimitData := sentCmd[0].LoadControlLimitListData.LoadControlLimitData - assert.Equal(t, sentLimits, len(sentLimitData)) - - for _, item := range sentLimitData { - if index := slices.Index(limitIdsObligation, *item.LimitId); index >= 0 { - assert.Equal(t, data.obligationsExpected[index], item.Value.GetValue()) - } - if index := slices.Index(limitIdsRecommendation, *item.LimitId); index >= 0 { - assert.Equal(t, data.recommendationsExpected[index], item.Value.GetValue()) - } - } - } - }) - } -} diff --git a/emobility/public_EVWritePowerLimits_test.go b/emobility/public_EVWritePowerLimits_test.go deleted file mode 100644 index 079cb6d..0000000 --- a/emobility/public_EVWritePowerLimits_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package emobility - -import ( - "encoding/json" - "testing" - "time" - - "github.com/enbility/eebus-go/util" - "github.com/enbility/spine-go/mocks" - "github.com/enbility/spine-go/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func Test_EVWritePowerLimits(t *testing.T) { - emobilty, eebusService := setupEmobility(t) - - data := []EVDurationSlotValue{} - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(t) - mockRemoteEntity := mocks.NewEntityRemoteInterface(t) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(t) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature) - mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice) - err := emobilty.EVWritePowerLimits(mockRemoteEntity, data) - assert.NotNil(t, err) - - localDevice, localEntity, remoteDevice, entites, writeHandler := setupDevices(eebusService) - emobilty.evseEntity = entites[0] - emobilty.evEntity = entites[1] - - err = emobilty.EVWritePowerLimits(emobilty.evEntity, data) - assert.NotNil(t, err) - - err = emobilty.EVWritePowerLimits(emobilty.evEntity, data) - assert.NotNil(t, err) - - datagram := datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - - cmd := []model.CmdType{{ - TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ - TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWritePowerLimits(emobilty.evEntity, data) - assert.NotNil(t, err) - - type dataStruct struct { - error bool - minSlots, maxSlots uint - slots []EVDurationSlotValue - } - - tests := []struct { - name string - data []dataStruct - }{ - { - "too few slots", - []dataStruct{ - { - true, 2, 2, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 11000}, - }, - }, - }, - }, { - "too many slots", - []dataStruct{ - { - true, 1, 1, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 11000}, - {Duration: time.Hour, Value: 11000}, - }, - }, - }, - }, - { - "1 slot", - []dataStruct{ - { - false, 1, 1, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 11000}, - }, - }, - }, - }, - { - "2 slots", - []dataStruct{ - { - false, 1, 2, - []EVDurationSlotValue{ - {Duration: time.Hour, Value: 11000}, - {Duration: 30 * time.Minute, Value: 5000}, - }, - }, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - for _, data := range tc.data { - datagram = datagramForEntityAndFeatures(false, localDevice, localEntity, emobilty.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer, model.RoleTypeClient) - - cmd = []model.CmdType{{ - TimeSeriesConstraintsListData: &model.TimeSeriesConstraintsListDataType{ - TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ - { - TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), - SlotCountMin: util.Ptr(model.TimeSeriesSlotCountType(data.minSlots)), - SlotCountMax: util.Ptr(model.TimeSeriesSlotCountType(data.maxSlots)), - }, - }, - }}} - datagram.Payload.Cmd = cmd - - err = localDevice.ProcessCmd(datagram, remoteDevice) - assert.Nil(t, err) - - err = emobilty.EVWritePowerLimits(emobilty.evEntity, data.slots) - if data.error { - assert.NotNil(t, err) - continue - } else { - assert.Nil(t, err) - } - - sentDatagram := model.Datagram{} - sentBytes := writeHandler.LastMessage() - err := json.Unmarshal(sentBytes, &sentDatagram) - assert.Nil(t, err) - - sentCmd := sentDatagram.Datagram.Payload.Cmd - assert.Equal(t, 1, len(sentCmd)) - - sentPowerLimitsData := sentCmd[0].TimeSeriesListData.TimeSeriesData[0].TimeSeriesSlot - assert.Equal(t, len(data.slots), len(sentPowerLimitsData)) - - for index, item := range sentPowerLimitsData { - assert.Equal(t, data.slots[index].Value, item.MaxValue.GetValue()) - } - } - }) - } -} diff --git a/emobility/results.go b/emobility/results.go deleted file mode 100644 index 920f5b5..0000000 --- a/emobility/results.go +++ /dev/null @@ -1,46 +0,0 @@ -package emobility - -import ( - "fmt" - - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -func (e *EMobility) HandleResult(errorMsg api.ResultMessage) { - isEvse := errorMsg.EntityRemote == e.evseEntity - isEv := e.evEntity != nil && errorMsg.EntityRemote == e.evEntity - - if isEvse || isEv { - // handle errors coming from the remote EVSE entity - switch errorMsg.FeatureLocal.Type() { - case model.FeatureTypeTypeDeviceDiagnosis: - e.handleResultDeviceDiagnosis(errorMsg) - } - - } -} - -// Handle DeviceDiagnosis Results -func (e *EMobility) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { - // is this an error for a heartbeat message? - if *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { - return - } - - // check if this is for a cached notify message - datagram, err := resultMsg.DeviceRemote.Sender().DatagramForMsgCounter(resultMsg.MsgCounterReference) - if err != nil { - return - } - - if len(datagram.Payload.Cmd) > 0 && - datagram.Payload.Cmd[0].DeviceDiagnosisHeartbeatData != nil { - // something is horribly wrong, disconnect and hope a new connection will fix it - errorText := fmt.Sprintf("Error Code: %d", resultMsg.Result.ErrorNumber) - if resultMsg.Result.Description != nil { - errorText = fmt.Sprintf("%s - %s", errorText, string(*resultMsg.Result.Description)) - } - e.service.DisconnectSKI(resultMsg.DeviceRemote.Ski(), errorText) - } -} diff --git a/emobility/solution.go b/emobility/solution.go deleted file mode 100644 index 4096cc1..0000000 --- a/emobility/solution.go +++ /dev/null @@ -1,184 +0,0 @@ -package emobility - -import ( - "sync" - - "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/util" - shipapi "github.com/enbility/ship-go/api" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -type EmobilitySolution struct { - *api.Solution - - remoteDevices map[string]*EMobility - - mux sync.Mutex - - currency model.CurrencyType - configuration EmobilityConfiguration -} - -var _ api.SolutionInterface = (*EmobilitySolution)(nil) - -func NewEMobilitySolution( - service eebusapi.ServiceInterface, - currency model.CurrencyType, - configuration EmobilityConfiguration) *EmobilitySolution { - return &EmobilitySolution{ - Solution: api.NewSolution(service), - remoteDevices: make(map[string]*EMobility), - currency: currency, - configuration: configuration, - } -} - -// adds all the supported features to the local entity -func (e *EmobilitySolution) AddFeatures() { - localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - // server features - { - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - f.AddResultHandler(e) - f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, true, false) - - // Set the initial state - deviceDiagnosisStateDate := &model.DeviceDiagnosisStateDataType{ - OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), - } - f.SetData(model.FunctionTypeDeviceDiagnosisStateData, deviceDiagnosisStateDate) - - f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) - } - - // client features - var clientFeatures = []model.FeatureTypeType{ - model.FeatureTypeTypeDeviceDiagnosis, - model.FeatureTypeTypeDeviceClassification, - model.FeatureTypeTypeDeviceConfiguration, - model.FeatureTypeTypeElectricalConnection, - model.FeatureTypeTypeMeasurement, - model.FeatureTypeTypeLoadControl, - model.FeatureTypeTypeIdentification, - } - - if e.configuration.CoordinatedChargingEnabled { - clientFeatures = append(clientFeatures, model.FeatureTypeTypeTimeSeries) - clientFeatures = append(clientFeatures, model.FeatureTypeTypeIncentiveTable) - } - for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) - } -} - -// add supported e-mobility usecases -func (e *EmobilitySolution) AddUseCases() { - localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeEVSECommissioningAndConfiguration, - model.SpecificationVersionType("1.0.1"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2}) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeEVCommissioningAndConfiguration, - model.SpecificationVersionType("1.0.1"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging, - model.SpecificationVersionType("1.0.1"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2, 3}) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment, - model.SpecificationVersionType("1.0.1b"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2, 3}) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeMonitoringAppliance, - model.UseCaseNameTypeEVStateOfCharge, - model.SpecificationVersionType("1.0.0"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2, 3, 4}) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging, - model.SpecificationVersionType("1.0.1b"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2, 3}) - - if e.configuration.CoordinatedChargingEnabled { - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, - model.UseCaseNameTypeCoordinatedEVCharging, - model.SpecificationVersionType("1.0.1"), - "", - true, - []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) - } -} - -func (e *EmobilitySolution) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { - // TODO: emobility should be stored per remote SKI and - // only be set for the SKI if the device supports it - e.mux.Lock() - defer e.mux.Unlock() - - if em, ok := e.remoteDevices[details.SKI()]; ok { - return em - } - - var provider EmobilityDataProvider - if dataProvider != nil { - provider = dataProvider.(EmobilityDataProvider) - } - emobility := NewEMobility(e.Service, details, e.currency, e.configuration, provider) - e.remoteDevices[details.SKI()] = emobility - return emobility -} - -func (e *EmobilitySolution) UnRegisterRemoteDevice(remoteDeviceSki string) { - e.mux.Lock() - defer e.mux.Unlock() - - delete(e.remoteDevices, remoteDeviceSki) - - e.Service.RegisterRemoteSKI(remoteDeviceSki, false) -} - -func (e *EmobilitySolution) HandleResult(errorMsg spineapi.ResultMessage) { - e.mux.Lock() - defer e.mux.Unlock() - - if errorMsg.DeviceRemote == nil { - return - } - - em, ok := e.remoteDevices[errorMsg.DeviceRemote.Ski()] - if !ok { - return - } - - em.HandleResult(errorMsg) -} diff --git a/emobility/types.go b/emobility/types.go deleted file mode 100644 index f3dd634..0000000 --- a/emobility/types.go +++ /dev/null @@ -1,108 +0,0 @@ -package emobility - -import ( - "errors" - "time" - - "github.com/enbility/spine-go/model" -) - -type EVCommunicationStandardType model.DeviceConfigurationKeyValueStringType - -const ( - EVCommunicationStandardTypeUnknown EVCommunicationStandardType = "unknown" - EVCommunicationStandardTypeISO151182ED1 EVCommunicationStandardType = "iso15118-2ed1" - EVCommunicationStandardTypeISO151182ED2 EVCommunicationStandardType = "iso15118-2ed2" - EVCommunicationStandardTypeIEC61851 EVCommunicationStandardType = "iec61851" -) - -type EVChargeStateType string - -const ( - EVChargeStateTypeUnknown EVChargeStateType = "Unknown" - EVChargeStateTypeUnplugged EVChargeStateType = "unplugged" - EVChargeStateTypeError EVChargeStateType = "error" - EVChargeStateTypePaused EVChargeStateType = "paused" - EVChargeStateTypeActive EVChargeStateType = "active" - EVChargeStateTypeFinished EVChargeStateType = "finished" -) - -type EVChargeStrategyType string - -const ( - EVChargeStrategyTypeUnknown EVChargeStrategyType = "unknown" - EVChargeStrategyTypeNoDemand EVChargeStrategyType = "nodemand" - EVChargeStrategyTypeDirectCharging EVChargeStrategyType = "directcharging" - EVChargeStrategyTypeMinSoC EVChargeStrategyType = "minsoc" - EVChargeStrategyTypeTimedCharging EVChargeStrategyType = "timedcharging" -) - -// Defines a phase specific limit -type EVLoadLimitsPhase struct { - Phase model.ElectricalConnectionPhaseNameType - IsActive bool - Value float64 -} - -// Contains limit data for a defined category -type EVLoadLimits struct { - Category model.LoadControlCategoryType - PhaseData []EVLoadLimitsPhase -} - -// Contains details about the actual demands from the EV -// -// General: -// - If duration and energy is 0, charge mode is EVChargeStrategyTypeNoDemand -// - If duration is 0, charge mode is EVChargeStrategyTypeDirectCharging and the slots should cover at least 48h -// - If both are != 0, charge mode is EVChargeStrategyTypeTimedCharging and the slots should cover at least the duration, but at max 168h (7d) -type EVDemand struct { - MinDemand float64 // minimum demand in Wh to reach the minSoC setting, 0 if not set - OptDemand float64 // demand in Wh to reach the timer SoC setting - MaxDemand float64 // the maximum possible demand until the battery is full - DurationUntilStart float64 // the duration in s from now until charging will start, this could be in the future but usualy is now - DurationUntilEnd float64 // the duration in s from now until minDemand or optDemand has to be reached, 0 if direct charge strategy is active -} - -// Contains details about an EV generated charging plan -type EVChargePlan struct { - Slots []EVChargePlanSlotValue // Individual charging slot details -} - -// Contains details about a charging plan slot -type EVChargePlanSlotValue struct { - Start time.Time // The start time of the slot - End time.Time // The duration of the slot - Value float64 // planned power value - MinValue float64 // minimum power value - MaxValue float64 // maximum power value -} - -// Details about the time slot constraints -type EVTimeSlotConstraints struct { - MinSlots uint // the minimum number of slots, no minimum if 0 - MaxSlots uint // the maximum number of slots, unlimited if 0 - MinSlotDuration time.Duration // the minimum duration of a slot, no minimum if 0 - MaxSlotDuration time.Duration // the maximum duration of a slot, unlimited if 0 - SlotDurationStepSize time.Duration // the duration has to be a multiple of this value if != 0 -} - -// Details about the incentive slot constraints -type EVIncentiveSlotConstraints struct { - MinSlots uint // the minimum number of slots, no minimum if 0 - MaxSlots uint // the maximum number of slots, unlimited if 0 -} - -// Contains details about power limits or incentives for a defined timeframe -type EVDurationSlotValue struct { - Duration time.Duration // Duration of this slot - Value float64 // Energy Cost or Power Limit -} - -var ErrEVDisconnected = errors.New("ev is disconnected") -var ErrNotSupported = errors.New("function is not supported") - -// Allows to exclude some features -type EmobilityConfiguration struct { - CoordinatedChargingEnabled bool -} diff --git a/go.mod b/go.mod index 6722eeb..1e40345 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,10 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240219192901-0457306c3e3a - github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 - github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 + github.com/enbility/eebus-go v0.0.0-20240222173124-444cee6d0e50 + github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 + github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 github.com/stretchr/testify v1.8.4 - go.uber.org/mock v0.4.0 - golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 ) require ( @@ -25,6 +23,7 @@ require ( github.com/rickb777/plural v1.4.1 // indirect github.com/stretchr/objx v0.5.1 // indirect gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect + go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect diff --git a/go.sum b/go.sum index b1191e8..1afc580 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240219192901-0457306c3e3a h1:9ZFkh6dWmRNbK9KUL7ume3BbBFJc4BXIhbf6ce2eloI= -github.com/enbility/eebus-go v0.0.0-20240219192901-0457306c3e3a/go.mod h1:CIXa8fLvwpeDQOZVe20mxmX+vQHvVutCrnmc/nr3KqM= -github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082 h1:BmevZOzjfBjGFB4U8iYPgnY8zDhDJbAODksJ5tzLRfg= -github.com/enbility/ship-go v0.0.0-20240218150505-eb6afd77b082/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444 h1:D9nrLCu8CHVDlchyjkWRAsUPFbA+ELpDSh4sp/acACE= -github.com/enbility/spine-go v0.0.0-20240219192650-aadb6d2b4444/go.mod h1:soGjheTI7Fpwva7cUqcpITUWkx38cfbrbYOK1mrhqYQ= +github.com/enbility/eebus-go v0.0.0-20240222173124-444cee6d0e50 h1:yD7LEdrQpiny+h0FsCnjJ5+CO0bMvDLw6vtu6mnXaB8= +github.com/enbility/eebus-go v0.0.0-20240222173124-444cee6d0e50/go.mod h1:w0fBLO6XWA5/5+VD0fs/RHFDH+izTyRYPVLDeq5sc8o= +github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= +github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 h1:qX9JNqBrxH8COazT+jnLpr7vEZt/8CVshy0r+k9uQcM= +github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -45,8 +45,6 @@ gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8 gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= -golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= diff --git a/grid/events.go b/grid/events.go index 31a4c3d..6952947 100644 --- a/grid/events.go +++ b/grid/events.go @@ -119,12 +119,12 @@ func (e *Grid) gridConnected(ski string, entity api.EntityRemoteInterface) { } // get measurement parameters - if err := e.gridMeasurement.RequestDescriptions(); err != nil { + if _, err := e.gridMeasurement.RequestDescriptions(); err != nil { logging.Log().Error(err) return } - if err := e.gridMeasurement.RequestConstraints(); err != nil { + if _, err := e.gridMeasurement.RequestConstraints(); err != nil { logging.Log().Error(err) return } diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go index eed1850..cd4b52b 100644 --- a/inverterbatteryvis/events.go +++ b/inverterbatteryvis/events.go @@ -114,11 +114,11 @@ func (i *InverterBatteryVis) inverterConnected(ski string, entity api.EntityRemo } // get measurement parameters - if err := i.inverterMeasurement.RequestDescriptions(); err != nil { + if _, err := i.inverterMeasurement.RequestDescriptions(); err != nil { logging.Log().Error(err) } - if err := i.inverterMeasurement.RequestConstraints(); err != nil { + if _, err := i.inverterMeasurement.RequestConstraints(); err != nil { logging.Log().Error(err) } } diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go index c8f80e7..1bdf0ca 100644 --- a/inverterpvvis/events.go +++ b/inverterpvvis/events.go @@ -138,11 +138,11 @@ func (e *InverterPVVis) inverterConnected(ski string, entity api.EntityRemoteInt } // get measurement parameters - if err := e.inverterMeasurement.RequestDescriptions(); err != nil { + if _, err := e.inverterMeasurement.RequestDescriptions(); err != nil { logging.Log().Error(err) } - if err := e.inverterMeasurement.RequestConstraints(); err != nil { + if _, err := e.inverterMeasurement.RequestConstraints(); err != nil { logging.Log().Error(err) } } diff --git a/uccevc/api.go b/uccevc/api.go new file mode 100644 index 0000000..7a99922 --- /dev/null +++ b/uccevc/api.go @@ -0,0 +1,57 @@ +package uccevc + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCCEVCInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // returns the current charging stratey + // + // returns EVChargeStrategyTypeUnknown if it could not be determined, e.g. + // if the vehicle communication is via IEC61851 or the EV doesn't provide + // any information about its charging mode or plan + ChargeStrategy(remoteEntity spineapi.EntityRemoteInterface) api.EVChargeStrategyType + + // returns the current energy demand + // - EVDemand: details about the actual demands from the EV + // - error: if no data is available + // + // if duration is 0, direct charging is active, otherwise timed charging is active + EnergyDemand(remoteEntity spineapi.EntityRemoteInterface) (api.Demand, error) + + // Scenario 2 + + TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api.TimeSlotConstraints, error) + + // send power limits to the EV + // if no data is provided, default power limits with the max possible value for 7 days will be sent + WritePowerLimits(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error + + // Scenario 3 + + IncentiveConstraints(entity spineapi.EntityRemoteInterface) (api.IncentiveSlotConstraints, error) + + WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInterface, data []api.IncentiveTariffDescription) error + + // send incentives to the EV + // if no data is provided, default incentives with the same price for 7 days will be sent + WriteIncentives(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error + + // Scenario 4 + + ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([]api.DurationSlotValue, error) + + ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePlan, error) + + // Scenario 5 & 6 + + // this is automatically covered by the SPINE implementation +} diff --git a/uccevc/events.go b/uccevc/events.go new file mode 100644 index 0000000..2f272f5 --- /dev/null +++ b/uccevc/events.go @@ -0,0 +1,208 @@ +package uccevc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCCEVC) HandleEvent(payload spineapi.EventPayload) { + // only about events from an EV entity or device changes for this remote device + + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + return + } + + if util.IsEvConnected(payload) { + e.evConnected(payload.Entity) + return + } + + switch payload.EventType { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.TimeSeriesDescriptionListDataType: + e.evTimeSeriesDescriptionDataUpdate(payload.Ski, payload.Entity) + + case *model.TimeSeriesListDataType: + e.evTimeSeriesDataUpdate(payload.Ski, payload.Entity) + + case *model.IncentiveTableDescriptionDataType: + e.evIncentiveTableDescriptionDataUpdate(payload.Ski, payload.Entity) + + case *model.IncentiveDataType: + e.evIncentiveTableDataUpdate(payload.Ski, payload.Entity) + } + } +} + +// an EV was connected +func (e *UCCEVC) evConnected(entity spineapi.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, descriptions + if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if _, err := evDeviceConfiguration.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get device configuration descriptions + if _, err := evDeviceConfiguration.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + } + + if evTimeSeries, err := util.TimeSeries(e.service, entity); err == nil { + if _, err := evTimeSeries.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + if _, err := evTimeSeries.Bind(); err != nil { + logging.Log().Debug(err) + } + + // get time series descriptions + if _, err := evTimeSeries.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + + // get time series constraints + if _, err := evTimeSeries.RequestConstraints(); err != nil { + logging.Log().Debug(err) + } + } + + if evIncentiveTable, err := util.IncentiveTable(e.service, entity); err == nil { + if _, err := evIncentiveTable.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + if _, err := evIncentiveTable.Bind(); err != nil { + logging.Log().Debug(err) + } + + // get incentivetable descriptions + if _, err := evIncentiveTable.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the time series description data of an EV was updated +func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + if evTimeSeries, err := util.TimeSeries(e.service, entity); err == nil { + // get time series values + if _, err := evTimeSeries.RequestValues(); err != nil { + logging.Log().Debug(err) + } + } + + // check if we are required to update the plan + if !e.evCheckTimeSeriesDescriptionConstraintsUpdateRequired(entity) { + return + } + + _, err := e.EnergyDemand(entity) + if err != nil { + return + } + + e.reader.SpineEvent(ski, entity, api.UCCEVCEnergyDemandProvided) + + _, err = e.TimeSlotConstraints(entity) + if err != nil { + logging.Log().Error("Error getting timeseries constraints:", err) + return + } + + _, err = e.IncentiveConstraints(entity) + if err != nil { + logging.Log().Error("Error getting incentive constraints:", err) + return + } + + e.reader.SpineEvent(ski, entity, api.UCCEVPowerLimitsRequested) + e.reader.SpineEvent(ski, entity, api.UCCEVCIncentivesRequested) +} + +// the load control limit data of an EV was updated +func (e *UCCEVC) evTimeSeriesDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + if _, err := e.ChargePlan(entity); err == nil { + e.reader.SpineEvent(ski, entity, api.UCCEVCChargePlanProvided) + } + + if _, err := e.ChargePlanConstraints(entity); err == nil { + e.reader.SpineEvent(ski, entity, api.UCCEVCChargePlanConstraintsProvided) + } +} + +// the incentive table description data of an EV was updated +func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + if evIncentiveTable, err := util.IncentiveTable(e.service, entity); err == nil { + // get time series values + if _, err := evIncentiveTable.RequestValues(); err != nil { + logging.Log().Debug(err) + } + } + + // check if we are required to update the plan + if !e.evCheckIncentiveTableDescriptionUpdateRequired(entity) { + return + } + + e.reader.SpineEvent(ski, entity, api.UCCEVCIncentiveDescriptionsRequired) +} + +// the load control limit data of an EV was updated +func (e *UCCEVC) evIncentiveTableDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, api.UCCEVCIncentiveTableDataUpdate) +} + +// check timeSeries descriptions if constraints element has updateRequired set to true +// as this triggers the CEM to send power tables within 20s +func (e *UCCEVC) evCheckTimeSeriesDescriptionConstraintsUpdateRequired(entity spineapi.EntityRemoteInterface) bool { + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + logging.Log().Error("timeseries feature not found") + return false + } + + data, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) + if err != nil { + return false + } + + if data.UpdateRequired != nil { + return *data.UpdateRequired + } + + return false +} + +// check incentibeTable descriptions if the tariff description has updateRequired set to true +// as this triggers the CEM to send incentive tables within 20s +func (e *UCCEVC) evCheckIncentiveTableDescriptionUpdateRequired(entity spineapi.EntityRemoteInterface) bool { + evIncentiveTable, err := util.IncentiveTable(e.service, entity) + if err != nil { + logging.Log().Error("incentivetable feature not found") + return false + } + + data, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) + if err != nil { + return false + } + + // only use the first description and therein the first tariff + item := data[0].TariffDescription + if item.UpdateRequired != nil { + return *item.UpdateRequired + } + + return false +} diff --git a/uccevc/public_scen1.go b/uccevc/public_scen1.go new file mode 100644 index 0000000..f881899 --- /dev/null +++ b/uccevc/public_scen1.go @@ -0,0 +1,135 @@ +package uccevc + +import ( + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// returns the current charging strategy +func (e *UCCEVC) ChargeStrategy(entity spineapi.EntityRemoteInterface) api.EVChargeStrategyType { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return api.EVChargeStrategyTypeUnknown + } + + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return api.EVChargeStrategyTypeUnknown + } + + // only the time series data for singledemand is relevant for detecting the charging strategy + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) + if err != nil { + return api.EVChargeStrategyTypeUnknown + } + + // without time series slots, there is no known strategy + if data.TimeSeriesSlot == nil || len(data.TimeSeriesSlot) == 0 { + return api.EVChargeStrategyTypeUnknown + } + + // get the value for the first slot + firstSlot := data.TimeSeriesSlot[0] + + switch { + case firstSlot.Duration == nil: + // if value is > 0 and duration does not exist, the EV is direct charging + if firstSlot.Value != nil && firstSlot.Value.GetValue() > 0 { + return api.EVChargeStrategyTypeDirectCharging + } + + // maxValue will show the maximum amount the battery could take + return api.EVChargeStrategyTypeNoDemand + + case firstSlot.Duration != nil: + if _, err := firstSlot.Duration.GetTimeDuration(); err != nil { + // we got an invalid duration + return api.EVChargeStrategyTypeUnknown + } + + if firstSlot.MinValue != nil && firstSlot.MinValue.GetValue() > 0 { + return api.EVChargeStrategyTypeMinSoC + } + + if firstSlot.Value != nil { + if firstSlot.Value.GetValue() > 0 { + // there is demand and a duration + return api.EVChargeStrategyTypeTimedCharging + } + + return api.EVChargeStrategyTypeNoDemand + } + + } + + return api.EVChargeStrategyTypeUnknown +} + +// returns the current energy demand in Wh and the duration +func (e *UCCEVC) EnergyDemand(entity spineapi.EntityRemoteInterface) (api.Demand, error) { + demand := api.Demand{} + + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return demand, api.ErrEVDisconnected + } + + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return demand, features.ErrDataNotAvailable + } + + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) + if err != nil { + return demand, features.ErrDataNotAvailable + } + + // we need at least a time series slot + if data.TimeSeriesSlot == nil { + return demand, features.ErrDataNotAvailable + } + + // get the value for the first slot, ignore all others, which + // in the tests so far always have min/max/value 0 + firstSlot := data.TimeSeriesSlot[0] + if firstSlot.MinValue != nil { + demand.MinDemand = firstSlot.MinValue.GetValue() + } + if firstSlot.Value != nil { + demand.OptDemand = firstSlot.Value.GetValue() + } + if firstSlot.MaxValue != nil { + demand.MaxDemand = firstSlot.MaxValue.GetValue() + } + if firstSlot.Duration != nil { + if tempDuration, err := firstSlot.Duration.GetTimeDuration(); err == nil { + demand.DurationUntilEnd = tempDuration.Seconds() + } + } + + // start time has to be defined either in TimePeriod or the first slot + relStartTime := time.Duration(0) + + startTimeSet := false + if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { + if temp, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { + relStartTime = temp + startTimeSet = true + } + } + + if !startTimeSet { + if firstSlot.TimePeriod != nil && firstSlot.TimePeriod.StartTime != nil { + if temp, err := firstSlot.TimePeriod.StartTime.GetTimeDuration(); err == nil { + relStartTime = temp + } + } + } + + demand.DurationUntilStart = relStartTime.Seconds() + + return demand, nil +} diff --git a/uccevc/public_scen1_test.go b/uccevc/public_scen1_test.go new file mode 100644 index 0000000..c606299 --- /dev/null +++ b/uccevc/public_scen1_test.go @@ -0,0 +1,338 @@ +package uccevc + +import ( + "time" + + "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_EVChargeStrategy() { + data := s.sut.ChargeStrategy(s.mockRemoteEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeUnknown, data) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeUnknown, data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeUnknown, data) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringType(model.DeviceConfigurationKeyValueStringTypeISO151182ED2)), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeUnknown, data) + + timeDescData := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeSingleDemand), + }, + }, + } + + rTimeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDescData, nil, nil) + assert.Nil(s.T(), fErr) + + timeData := &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeUnknown, data) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeNoDemand, data) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT0S")), + Value: model.NewScaledNumberType(0), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeNoDemand, data) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Value: model.NewScaledNumberType(10000), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeDirectCharging, data) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Value: model.NewScaledNumberType(10000), + Duration: model.NewDurationType(2 * time.Hour), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + data = s.sut.ChargeStrategy(s.evEntity) + assert.Equal(s.T(), api.EVChargeStrategyTypeTimedCharging, data) +} + +func (s *UCCEVCSuite) Test_EVEnergySingleDemand() { + demand, err := s.sut.EnergyDemand(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + timeDescData := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeSingleDemand), + }, + }, + } + + rTimeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDescData, nil, nil) + assert.Nil(s.T(), fErr) + + timeData := &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + MinValue: model.NewScaledNumberType(1000), + Value: model.NewScaledNumberType(10000), + MaxValue: model.NewScaledNumberType(100000), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1000.0, demand.MinDemand) + assert.Equal(s.T(), 10000.0, demand.OptDemand) + assert.Equal(s.T(), 100000.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Value: model.NewScaledNumberType(10000), + Duration: model.NewDurationType(2 * time.Hour), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 10000.0, demand.OptDemand) + assert.Equal(s.T(), 0.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), time.Duration(2*time.Hour).Seconds(), demand.DurationUntilEnd) +} diff --git a/uccevc/public_scen2.go b/uccevc/public_scen2.go new file mode 100644 index 0000000..0a78c5c --- /dev/null +++ b/uccevc/public_scen2.go @@ -0,0 +1,172 @@ +package uccevc + +import ( + "errors" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// returns the constraints for the time slots +func (e *UCCEVC) TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api.TimeSlotConstraints, error) { + result := api.TimeSlotConstraints{} + + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return result, api.ErrEVDisconnected + } + + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return result, features.ErrDataNotAvailable + } + + constraints, err := evTimeSeries.GetConstraints() + if err != nil { + return result, err + } + + // only use the first constraint + constraint := constraints[0] + + if constraint.SlotCountMin != nil { + result.MinSlots = uint(*constraint.SlotCountMin) + } + if constraint.SlotCountMax != nil { + result.MaxSlots = uint(*constraint.SlotCountMax) + } + if constraint.SlotDurationMin != nil { + if duration, err := constraint.SlotDurationMin.GetTimeDuration(); err == nil { + result.MinSlotDuration = duration + } + } + if constraint.SlotDurationMax != nil { + if duration, err := constraint.SlotDurationMax.GetTimeDuration(); err == nil { + result.MaxSlotDuration = duration + } + } + if constraint.SlotDurationStepSize != nil { + if duration, err := constraint.SlotDurationStepSize.GetTimeDuration(); err == nil { + result.SlotDurationStepSize = duration + } + } + + return result, nil +} + +// send power limits to the EV +// if no data is provided, default power limits with the max possible value for 7 days will be sent +func (e *UCCEVC) WritePowerLimits(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return api.ErrEVDisconnected + } + + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return features.ErrDataNotAvailable + } + + if len(data) == 0 { + data, err = e.defaultPowerLimits(entity) + if err != nil { + return err + } + } + + constraints, err := e.TimeSlotConstraints(entity) + if err != nil { + return err + } + + if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { + return errors.New("too few charge slots provided") + } + + if constraints.MaxSlots != 0 && constraints.MaxSlots < uint(len(data)) { + return errors.New("too many charge slots provided") + } + + desc, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) + if err != nil { + return api.ErrNotSupported + } + + timeSeriesSlots := []model.TimeSeriesSlotType{} + var totalDuration time.Duration + for index, slot := range data { + relativeStart := totalDuration + + timeSeriesSlot := model.TimeSeriesSlotType{ + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(index)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(relativeStart), + }, + MaxValue: model.NewScaledNumberType(slot.Value), + } + + // the last slot also needs an End Time + if index == len(data)-1 { + relativeEndTime := relativeStart + slot.Duration + timeSeriesSlot.TimePeriod.EndTime = model.NewAbsoluteOrRelativeTimeTypeFromDuration(relativeEndTime) + } + timeSeriesSlots = append(timeSeriesSlots, timeSeriesSlot) + + totalDuration += slot.Duration + } + + timeSeriesData := model.TimeSeriesDataType{ + TimeSeriesId: desc.TimeSeriesId, + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(totalDuration), + }, + TimeSeriesSlot: timeSeriesSlots, + } + + _, err = evTimeSeries.WriteValues([]model.TimeSeriesDataType{timeSeriesData}) + + return err +} + +func (e *UCCEVC) defaultPowerLimits(entity spineapi.EntityRemoteInterface) ([]api.DurationSlotValue, error) { + // send default power limits for the maximum timeframe + // to fullfill spec, as there is no data provided + logging.Log().Info("Fallback sending default power limits") + + evElectricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + logging.Log().Error("electrical connection feature not found") + return nil, err + } + + paramDesc, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) + if err != nil { + logging.Log().Error("Error getting parameter descriptions:", err) + return nil, err + } + + permitted, err := evElectricalConnection.GetPermittedValueSetForParameterId(*paramDesc.ParameterId) + if err != nil { + logging.Log().Error("Error getting permitted values:", err) + return nil, err + } + + if len(permitted.PermittedValueSet) < 1 || len(permitted.PermittedValueSet[0].Range) < 1 { + text := "No permitted value set available" + logging.Log().Error(text) + return nil, errors.New(text) + } + + data := []api.DurationSlotValue{ + { + Duration: 7 * time.Hour * 24, + Value: permitted.PermittedValueSet[0].Range[0].Max.GetValue(), + }, + } + return data, nil +} diff --git a/uccevc/public_scen2_test.go b/uccevc/public_scen2_test.go new file mode 100644 index 0000000..3df03d3 --- /dev/null +++ b/uccevc/public_scen2_test.go @@ -0,0 +1,165 @@ +package uccevc + +import ( + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_EVGetTimeSlotConstraints() { + constraints, err := s.sut.TimeSlotConstraints(s.mockRemoteEntity) + assert.Equal(s.T(), uint(0), constraints.MinSlots) + assert.Equal(s.T(), uint(0), constraints.MaxSlots) + assert.Equal(s.T(), time.Duration(0), constraints.MinSlotDuration) + assert.Equal(s.T(), time.Duration(0), constraints.MaxSlotDuration) + assert.Equal(s.T(), time.Duration(0), constraints.SlotDurationStepSize) + assert.NotEqual(s.T(), err, nil) + + constraints, err = s.sut.TimeSlotConstraints(s.evEntity) + assert.Equal(s.T(), uint(0), constraints.MinSlots) + assert.Equal(s.T(), uint(0), constraints.MaxSlots) + assert.Equal(s.T(), time.Duration(0), constraints.MinSlotDuration) + assert.Equal(s.T(), time.Duration(0), constraints.MaxSlotDuration) + assert.Equal(s.T(), time.Duration(0), constraints.SlotDurationStepSize) + assert.NotEqual(s.T(), err, nil) + + constData := &model.TimeSeriesConstraintsListDataType{ + TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + SlotCountMin: eebusutil.Ptr(model.TimeSeriesSlotCountType(1)), + SlotCountMax: eebusutil.Ptr(model.TimeSeriesSlotCountType(10)), + SlotDurationMin: model.NewDurationType(1 * time.Minute), + SlotDurationMax: model.NewDurationType(60 * time.Minute), + SlotDurationStepSize: model.NewDurationType(1 * time.Minute), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeTimeSeriesConstraintsListData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + constraints, err = s.sut.TimeSlotConstraints(s.evEntity) + assert.Equal(s.T(), uint(1), constraints.MinSlots) + assert.Equal(s.T(), uint(10), constraints.MaxSlots) + assert.Equal(s.T(), time.Duration(1*time.Minute), constraints.MinSlotDuration) + assert.Equal(s.T(), time.Duration(1*time.Hour), constraints.MaxSlotDuration) + assert.Equal(s.T(), time.Duration(1*time.Minute), constraints.SlotDurationStepSize) + assert.Equal(s.T(), err, nil) +} + +func (s *UCCEVCSuite) Test_EVWritePowerLimits() { + data := []api.DurationSlotValue{} + + err := s.sut.WritePowerLimits(s.mockRemoteEntity, data) + assert.NotNil(s.T(), err) + + err = s.sut.WritePowerLimits(s.evEntity, data) + assert.NotNil(s.T(), err) + + descData := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WritePowerLimits(s.evEntity, data) + assert.NotNil(s.T(), err) + + type dataStruct struct { + error bool + minSlots, maxSlots uint + slots []api.DurationSlotValue + } + + tests := []struct { + name string + data []dataStruct + }{ + { + "too few slots", + []dataStruct{ + { + true, 2, 2, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 11000}, + }, + }, + }, + }, { + "too many slots", + []dataStruct{ + { + true, 1, 1, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 11000}, + {Duration: time.Hour, Value: 11000}, + }, + }, + }, + }, + { + "1 slot", + []dataStruct{ + { + false, 1, 1, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 11000}, + }, + }, + }, + }, + { + "2 slots", + []dataStruct{ + { + false, 1, 2, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 11000}, + {Duration: 30 * time.Minute, Value: 5000}, + }, + }, + }, + }, + } + + for _, tc := range tests { + s.T().Run(tc.name, func(t *testing.T) { + for _, data := range tc.data { + constData := &model.TimeSeriesConstraintsListDataType{ + TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(0)), + SlotCountMin: util.Ptr(model.TimeSeriesSlotCountType(data.minSlots)), + SlotCountMax: util.Ptr(model.TimeSeriesSlotCountType(data.maxSlots)), + }, + }, + } + + fErr := rFeature.UpdateData(model.FunctionTypeTimeSeriesConstraintsListData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WritePowerLimits(s.evEntity, data.slots) + if data.error { + assert.NotNil(t, err) + continue + } else { + assert.Nil(t, err) + } + } + }) + } +} diff --git a/uccevc/public_scen3.go b/uccevc/public_scen3.go new file mode 100644 index 0000000..2428da4 --- /dev/null +++ b/uccevc/public_scen3.go @@ -0,0 +1,263 @@ +package uccevc + +import ( + "errors" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// returns the minimum and maximum number of incentive slots allowed +func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (api.IncentiveSlotConstraints, error) { + result := api.IncentiveSlotConstraints{} + + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return result, api.ErrEVDisconnected + } + + evIncentiveTable, err := util.IncentiveTable(e.service, entity) + if err != nil { + return result, features.ErrDataNotAvailable + } + + constraints, err := evIncentiveTable.GetConstraints() + if err != nil { + return result, err + } + + // only use the first constraint + constraint := constraints[0] + + if constraint.IncentiveSlotConstraints.SlotCountMin != nil { + result.MinSlots = uint(*constraint.IncentiveSlotConstraints.SlotCountMin) + } + if constraint.IncentiveSlotConstraints.SlotCountMax != nil { + result.MaxSlots = uint(*constraint.IncentiveSlotConstraints.SlotCountMax) + } + + return result, nil +} + +// inform the EVSE about used currency and boundary units +// +// SPINE UC CoordinatedEVCharging 2.4.3 +func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInterface, data []api.IncentiveTariffDescription) error { + evIncentiveTable, err := util.IncentiveTable(e.service, entity) + if err != nil { + logging.Log().Error("incentivetable feature not found") + return err + } + + descriptions, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) + if err != nil { + logging.Log().Error(err) + return err + } + + // default tariff + // + // - tariff, min 1 + // each tariff has + // - tiers: min 1, max 3 + // each tier has: + // - boundaries: min 1, used for different power limits, e.g. 0-1kW x€, 1-3kW y€, ... + // - incentives: min 1, max 3 + // - price/costs (absolute or relative) + // - renewable energy percentage + // - CO2 emissions + // + // limit this to + // - 1 tariff + // - 1 tier + // - 1 boundary + // - 1 incentive (price) + // incentive type has to be the same for all sent power limits! + descData := []model.IncentiveTableDescriptionType{ + { + TariffDescription: descriptions[0].TariffDescription, + Tier: []model.IncentiveTableDescriptionTierType{ + { + TierDescription: &model.TierDescriptionDataType{ + TierId: eebusutil.Ptr(model.TierIdType(0)), + TierType: eebusutil.Ptr(model.TierTypeTypeDynamicCost), + }, + BoundaryDescription: []model.TierBoundaryDescriptionDataType{ + { + BoundaryId: eebusutil.Ptr(model.TierBoundaryIdType(0)), + BoundaryType: eebusutil.Ptr(model.TierBoundaryTypeTypePowerBoundary), + BoundaryUnit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + }, + IncentiveDescription: []model.IncentiveDescriptionDataType{ + { + IncentiveId: eebusutil.Ptr(model.IncentiveIdType(0)), + IncentiveType: eebusutil.Ptr(model.IncentiveTypeTypeAbsoluteCost), + Currency: eebusutil.Ptr(model.CurrencyTypeEur), + }, + }, + }, + }, + }, + } + + if data != nil { + descData = []model.IncentiveTableDescriptionType{} + + for index, tariff := range data { + tariffDesc := descriptions[0].TariffDescription + if len(descriptions) > index { + tariffDesc = descriptions[index].TariffDescription + } + + newTariff := model.IncentiveTableDescriptionType{ + TariffDescription: tariffDesc, + } + + tierData := []model.IncentiveTableDescriptionTierType{} + for _, tier := range tariff.Tiers { + newTier := model.IncentiveTableDescriptionTierType{} + + newTier.TierDescription = &model.TierDescriptionDataType{ + TierId: eebusutil.Ptr(model.TierIdType(tier.Id)), + TierType: eebusutil.Ptr(tier.Type), + } + + boundaryDescription := []model.TierBoundaryDescriptionDataType{} + for _, boundary := range tier.Boundaries { + newBoundary := model.TierBoundaryDescriptionDataType{ + BoundaryId: eebusutil.Ptr(model.TierBoundaryIdType(boundary.Id)), + BoundaryType: eebusutil.Ptr(boundary.Type), + BoundaryUnit: eebusutil.Ptr(boundary.Unit), + } + boundaryDescription = append(boundaryDescription, newBoundary) + } + newTier.BoundaryDescription = boundaryDescription + + incentiveDescription := []model.IncentiveDescriptionDataType{} + for _, incentive := range tier.Incentives { + newIncentive := model.IncentiveDescriptionDataType{ + IncentiveId: eebusutil.Ptr(model.IncentiveIdType(incentive.Id)), + IncentiveType: eebusutil.Ptr(incentive.Type), + } + if incentive.Currency != "" { + newIncentive.Currency = eebusutil.Ptr(incentive.Currency) + } + incentiveDescription = append(incentiveDescription, newIncentive) + } + newTier.IncentiveDescription = incentiveDescription + + tierData = append(tierData, newTier) + } + + newTariff.Tier = tierData + + descData = append(descData, newTariff) + } + } + + _, err = evIncentiveTable.WriteDescriptions(descData) + if err != nil { + logging.Log().Error(err) + return err + } + + return nil +} + +// send incentives to the EV +// if no data is provided, default incentives with the same price for 7 days will be sent +func (e *UCCEVC) WriteIncentives(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return api.ErrEVDisconnected + } + + evIncentiveTable, err := util.IncentiveTable(e.service, entity) + if err != nil { + return features.ErrDataNotAvailable + } + + if len(data) == 0 { + // send default incentives for the maximum timeframe + // to fullfill spec, as there is no data provided + logging.Log().Info("Fallback sending default incentives") + data = []api.DurationSlotValue{ + {Duration: 7 * time.Hour * 24, Value: 0.30}, + } + } + + constraints, err := e.IncentiveConstraints(entity) + if err != nil { + return err + } + + if constraints.MinSlots != 0 && constraints.MinSlots > uint(len(data)) { + return errors.New("too few charge slots provided") + } + + if constraints.MaxSlots != 0 && constraints.MaxSlots < uint(len(data)) { + return errors.New("too many charge slots provided") + } + + incentiveSlots := []model.IncentiveTableIncentiveSlotType{} + var totalDuration time.Duration + for index, slot := range data { + relativeStart := totalDuration + + timeInterval := &model.TimeTableDataType{ + StartTime: &model.AbsoluteOrRecurringTimeType{ + Relative: model.NewDurationType(relativeStart), + }, + } + + // the last slot also needs an End Time + if index == len(data)-1 { + relativeEndTime := relativeStart + slot.Duration + timeInterval.EndTime = &model.AbsoluteOrRecurringTimeType{ + Relative: model.NewDurationType(relativeEndTime), + } + } + + incentiveSlot := model.IncentiveTableIncentiveSlotType{ + TimeInterval: timeInterval, + Tier: []model.IncentiveTableTierType{ + { + Tier: &model.TierDataType{ + TierId: eebusutil.Ptr(model.TierIdType(0)), + }, + Boundary: []model.TierBoundaryDataType{ + { + BoundaryId: eebusutil.Ptr(model.TierBoundaryIdType(0)), // only 1 boundary exists + LowerBoundaryValue: model.NewScaledNumberType(0), + }, + }, + Incentive: []model.IncentiveDataType{ + { + IncentiveId: eebusutil.Ptr(model.IncentiveIdType(0)), // always use price + Value: model.NewScaledNumberType(slot.Value), + }, + }, + }, + }, + } + incentiveSlots = append(incentiveSlots, incentiveSlot) + + totalDuration += slot.Duration + } + + incentiveData := model.IncentiveTableType{ + Tariff: &model.TariffDataType{ + TariffId: eebusutil.Ptr(model.TariffIdType(0)), + }, + IncentiveSlot: incentiveSlots, + } + + _, err = evIncentiveTable.WriteValues([]model.IncentiveTableType{incentiveData}) + + return err +} diff --git a/uccevc/public_scen3_test.go b/uccevc/public_scen3_test.go new file mode 100644 index 0000000..7884f3d --- /dev/null +++ b/uccevc/public_scen3_test.go @@ -0,0 +1,176 @@ +package uccevc + +import ( + "testing" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/ship-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_EVGetIncentiveConstraints() { + constraints, err := s.sut.IncentiveConstraints(s.mockRemoteEntity) + assert.Equal(s.T(), uint(0), constraints.MinSlots) + assert.Equal(s.T(), uint(0), constraints.MaxSlots) + assert.NotEqual(s.T(), err, nil) + + constraints, err = s.sut.IncentiveConstraints(s.evEntity) + assert.Equal(s.T(), uint(0), constraints.MinSlots) + assert.Equal(s.T(), uint(0), constraints.MaxSlots) + assert.NotEqual(s.T(), err, nil) + + constData := &model.IncentiveTableConstraintsDataType{ + IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ + { + IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ + SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), + SlotCountMax: util.Ptr(model.TimeSlotCountType(10)), + }, + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeIncentiveTableConstraintsData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + constraints, err = s.sut.IncentiveConstraints(s.evEntity) + assert.Equal(s.T(), uint(1), constraints.MinSlots) + assert.Equal(s.T(), uint(10), constraints.MaxSlots) + assert.Equal(s.T(), err, nil) + + constData = &model.IncentiveTableConstraintsDataType{ + IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ + { + IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ + SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeIncentiveTableConstraintsData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + constraints, err = s.sut.IncentiveConstraints(s.evEntity) + assert.Equal(s.T(), uint(1), constraints.MinSlots) + assert.Equal(s.T(), uint(0), constraints.MaxSlots) + assert.Equal(s.T(), err, nil) +} + +func (s *UCCEVCSuite) Test_EVWriteIncentives() { + data := []api.DurationSlotValue{} + + err := s.sut.WriteIncentives(s.mockRemoteEntity, data) + assert.NotNil(s.T(), err) + + err = s.sut.WriteIncentives(s.evEntity, data) + assert.NotNil(s.T(), err) + + constData := &model.IncentiveTableConstraintsDataType{ + IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ + { + IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ + SlotCountMin: util.Ptr(model.TimeSlotCountType(1)), + SlotCountMax: util.Ptr(model.TimeSlotCountType(10)), + }, + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeIncentiveTableConstraintsData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WriteIncentives(s.evEntity, data) + assert.NotNil(s.T(), err) + + type dataStruct struct { + error bool + minSlots, maxSlots uint + slots []api.DurationSlotValue + } + + tests := []struct { + name string + data []dataStruct + }{ + { + "too few slots", + []dataStruct{ + { + true, 2, 2, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 0.1}, + }, + }, + }, + }, { + "too many slots", + []dataStruct{ + { + true, 1, 1, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 0.1}, + {Duration: time.Hour, Value: 0.1}, + }, + }, + }, + }, + { + "1 slot", + []dataStruct{ + { + false, 1, 1, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 0.1}, + }, + }, + }, + }, + { + "2 slots", + []dataStruct{ + { + false, 1, 2, + []api.DurationSlotValue{ + {Duration: time.Hour, Value: 0.1}, + {Duration: 30 * time.Minute, Value: 0.2}, + }, + }, + }, + }, + } + + for _, tc := range tests { + s.T().Run(tc.name, func(t *testing.T) { + for _, data := range tc.data { + + constData = &model.IncentiveTableConstraintsDataType{ + IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ + { + IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ + SlotCountMin: util.Ptr(model.TimeSlotCountType(data.minSlots)), + SlotCountMax: util.Ptr(model.TimeSlotCountType(data.maxSlots)), + }, + }, + }, + } + + fErr := rFeature.UpdateData(model.FunctionTypeIncentiveTableConstraintsData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WriteIncentives(s.evEntity, data.slots) + if data.error { + assert.NotNil(t, err) + continue + } else { + assert.Nil(t, err) + } + + } + }) + } +} diff --git a/uccevc/public_scen4.go b/uccevc/public_scen4.go new file mode 100644 index 0000000..7487967 --- /dev/null +++ b/uccevc/public_scen4.go @@ -0,0 +1,147 @@ +package uccevc + +import ( + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +func (e *UCCEVC) ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([]api.DurationSlotValue, error) { + constraints := []api.DurationSlotValue{} + + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return constraints, api.ErrEVDisconnected + } + + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return constraints, features.ErrDataNotAvailable + } + + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeConstraints) + if err != nil { + return constraints, features.ErrDataNotAvailable + } + + // we need at least a time series slot + if data.TimeSeriesSlot == nil { + return constraints, features.ErrDataNotAvailable + } + + // get the values for all slots + for _, slot := range data.TimeSeriesSlot { + newSlot := api.DurationSlotValue{} + + if slot.Duration != nil { + if duration, err := slot.Duration.GetTimeDuration(); err == nil { + newSlot.Duration = duration + } + } else if slot.TimePeriod != nil { + var slotStart, slotEnd time.Time + if slot.TimePeriod.StartTime != nil { + if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { + slotStart = time + } + } + if slot.TimePeriod.EndTime != nil { + if time, err := slot.TimePeriod.EndTime.GetTime(); err == nil { + slotEnd = time + } + } + newSlot.Duration = slotEnd.Sub(slotStart) + } + + if slot.MaxValue != nil { + newSlot.Value = slot.MaxValue.GetValue() + } + + constraints = append(constraints, newSlot) + } + + return constraints, nil +} + +func (e *UCCEVC) ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePlan, error) { + plan := api.ChargePlan{} + + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return plan, api.ErrEVDisconnected + } + + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return plan, features.ErrDataNotAvailable + } + + data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) + if err != nil { + return plan, features.ErrDataNotAvailable + } + + // we need at least a time series slot + if data.TimeSeriesSlot == nil { + return plan, features.ErrDataNotAvailable + } + + startAvailable := false + // check the start time relative to now of the plan, default is now + currentStart := time.Now() + currentEnd := currentStart + if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { + + if start, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { + currentStart = currentStart.Add(start) + startAvailable = true + } + } + + // get the values for all slots + for index, slot := range data.TimeSeriesSlot { + newSlot := api.ChargePlanSlotValue{} + + slotStartDefined := false + if index == 0 && startAvailable && (slot.TimePeriod == nil || slot.TimePeriod.StartTime == nil) { + newSlot.Start = currentStart + slotStartDefined = true + } + if slot.TimePeriod != nil && slot.TimePeriod.StartTime != nil { + if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { + newSlot.Start = time + slotStartDefined = true + } + } + if !slotStartDefined { + newSlot.Start = currentEnd + } + + if slot.Duration != nil { + if duration, err := slot.Duration.GetTimeDuration(); err == nil { + newSlot.End = newSlot.Start.Add(duration) + currentEnd = newSlot.End + } + } else if slot.TimePeriod != nil && slot.TimePeriod.EndTime != nil { + if time, err := slot.TimePeriod.StartTime.GetTime(); err == nil { + newSlot.End = time + currentEnd = newSlot.End + } + } + + if slot.Value != nil { + newSlot.Value = slot.Value.GetValue() + } + if slot.MinValue != nil { + newSlot.MinValue = slot.MinValue.GetValue() + } + if slot.MaxValue != nil { + newSlot.MaxValue = slot.MaxValue.GetValue() + } + + plan.Slots = append(plan.Slots, newSlot) + } + + return plan, nil +} diff --git a/uccevc/public_scen4_test.go b/uccevc/public_scen4_test.go new file mode 100644 index 0000000..489c712 --- /dev/null +++ b/uccevc/public_scen4_test.go @@ -0,0 +1,76 @@ +package uccevc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_EVChargePlan() { + _, err := s.sut.ChargePlan(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.ChargePlan(s.evEntity) + assert.NotNil(s.T(), err) + + descData := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), + TimeSeriesWriteable: util.Ptr(true), + UpdateRequired: util.Ptr(false), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + }, + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypePlan), + TimeSeriesWriteable: util.Ptr(false), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + }, + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeSingleDemand), + TimeSeriesWriteable: util.Ptr(false), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.ChargePlan(s.evEntity) + assert.NotNil(s.T(), err) + + timeData := &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT5M36S")), + MaxValue: model.NewScaledNumberType(4201), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("P1D")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.ChargePlan(s.evEntity) + assert.Nil(s.T(), err) +} diff --git a/uccevc/public_test.go b/uccevc/public_test.go new file mode 100644 index 0000000..83ab4f3 --- /dev/null +++ b/uccevc/public_test.go @@ -0,0 +1,339 @@ +package uccevc + +import ( + "time" + + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_CoordinatedChargingScenarios() { + timeConst := &model.TimeSeriesConstraintsListDataType{ + TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(1)), + SlotCountMax: eebusutil.Ptr(model.TimeSeriesSlotCountType(30)), + }, + }, + } + + rTimeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr := rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesConstraintsListData, timeConst, nil, nil) + assert.Nil(s.T(), fErr) + + timeDesc := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(1)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeConstraints), + TimeSeriesWriteable: eebusutil.Ptr(true), + UpdateRequired: eebusutil.Ptr(false), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypePlan), + TimeSeriesWriteable: eebusutil.Ptr(false), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(3)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeSingleDemand), + TimeSeriesWriteable: eebusutil.Ptr(false), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeWh), + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDesc, nil, nil) + assert.Nil(s.T(), fErr) + + incDesc := &model.IncentiveTableDescriptionDataType{ + IncentiveTableDescription: []model.IncentiveTableDescriptionType{ + { + TariffDescription: &model.TariffDescriptionDataType{ + TariffId: eebusutil.Ptr(model.TariffIdType(1)), + TariffWriteable: eebusutil.Ptr(true), + UpdateRequired: eebusutil.Ptr(false), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeSimpleIncentiveTable), + }, + }, + }, + } + + rIncentiveFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer) + fErr = rIncentiveFeature.UpdateData(model.FunctionTypeIncentiveTableDescriptionData, incDesc, nil, nil) + assert.Nil(s.T(), fErr) + + // demand, No Profile No Timer demand + + timeData := &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(3)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Value: model.NewScaledNumberType(0), + MaxValue: model.NewScaledNumberType(74690), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err := s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 0.0, demand.OptDemand) + assert.Equal(s.T(), 74690.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + // the final plan + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT18H3M7S")), + MaxValue: model.NewScaledNumberType(4163), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("PT42M")), + MaxValue: model.NewScaledNumberType(2736), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("P1D")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + // demand, profile + timer with 80% target and no climate, minSoC reached + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(3)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("P2DT4H40M36S")), + Value: model.NewScaledNumberType(53400), + MaxValue: model.NewScaledNumberType(74690), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("P1D")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 0.0, demand.MinDemand) + assert.Equal(s.T(), 53400.0, demand.OptDemand) + assert.Equal(s.T(), 74690.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), time.Duration(time.Hour*52+time.Minute*40+time.Second*36).Seconds(), demand.DurationUntilEnd) + + // the final plan + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("P1DT15H24M24S")), + MaxValue: model.NewScaledNumberType(0), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("PT12H35M50S")), + MaxValue: model.NewScaledNumberType(4163), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(2)), + Duration: eebusutil.Ptr(model.DurationType("PT40M22S")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + // demand, profile with 25% min SoC, minSoC not reached, no timer + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(1)), + }, + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT8M42S")), + MaxValue: model.NewScaledNumberType(4212), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("P1D")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(3)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Value: model.NewScaledNumberType(600), + MinValue: model.NewScaledNumberType(600), + MaxValue: model.NewScaledNumberType(75600), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err = s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 600.0, demand.MinDemand) + assert.Equal(s.T(), 600.0, demand.OptDemand) + assert.Equal(s.T(), 75600.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + // the final plan + + timeData = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT8M42S")), + MaxValue: model.NewScaledNumberType(4212), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + Duration: eebusutil.Ptr(model.DurationType("P1D")), + MaxValue: model.NewScaledNumberType(0), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) +} + +/* +func requestIncentiveUpdate(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { + cmd := []model.CmdType{{ + IncentiveTableDescriptionData: &model.IncentiveTableDescriptionDataType{ + IncentiveTableDescription: []model.IncentiveTableDescriptionType{ + { + TariffDescription: &model.TariffDescriptionDataType{ + TariffId: util.Ptr(model.TariffIdType(1)), + TariffWriteable: util.Ptr(true), + UpdateRequired: util.Ptr(true), + ScopeType: util.Ptr(model.ScopeTypeTypeSimpleIncentiveTable), + }, + }, + }, + }, + }} + + datagram.Payload.Cmd = cmd + + err := localDevice.ProcessCmd(datagram, remoteDevice) + assert.Nil(t, err) +} + +func requestPowerTableUpdate(t *testing.T, datagram model.DatagramType, localDevice api.DeviceLocal, remoteDevice api.DeviceRemote) { + cmd := []model.CmdType{{ + TimeSeriesDescriptionListData: &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), + TimeSeriesWriteable: util.Ptr(true), + UpdateRequired: util.Ptr(true), + }, + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(2)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypePlan), + TimeSeriesWriteable: util.Ptr(false), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + }, + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(3)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), + TimeSeriesWriteable: util.Ptr(false), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + }, + }, + }, + }} + + datagram.Payload.Cmd = cmd + + err := localDevice.ProcessCmd(datagram, remoteDevice) + assert.Nil(t, err) +} +*/ diff --git a/uccevc/results.go b/uccevc/results.go new file mode 100644 index 0000000..946ad45 --- /dev/null +++ b/uccevc/results.go @@ -0,0 +1,8 @@ +package uccevc + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCCEVC) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go new file mode 100644 index 0000000..11d76b8 --- /dev/null +++ b/uccevc/testhelper_test.go @@ -0,0 +1,195 @@ +package uccevc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestUCCEVCSuite(t *testing.T) { + suite.Run(t, new(UCCEVCSuite)) +} + +type UCCEVCSuite struct { + suite.Suite + + sut *UCCEVC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *UCCEVCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCCEVC(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + {model.FeatureTypeTypeTimeSeries, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeTimeSeriesConstraintsListData, + model.FunctionTypeTimeSeriesDescriptionListData, + model.FunctionTypeTimeSeriesListData, + }, + }, + {model.FeatureTypeTypeIncentiveTable, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeIncentiveTableConstraintsData, + model.FunctionTypeIncentiveTableDescriptionData, + model.FunctionTypeIncentiveTableData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go new file mode 100644 index 0000000..40f99e9 --- /dev/null +++ b/uccevc/uccevc.go @@ -0,0 +1,105 @@ +package uccevc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCCEVC struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCCEVCInterface = (*UCCEVC)(nil) + +func NewUCCEVC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCCEVC { + uc := &UCCEVC{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCCEVC) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeCoordinatedEVCharging +} + +func (e *UCCEVC) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddResultHandler(e) +} + +func (e *UCCEVC) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2, 3}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCCEVC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{2, 3, 4, 5, 6, 7, 8}, + []model.FeatureTypeType{ + model.FeatureTypeTypeTimeSeries, + model.FeatureTypeTypeIncentiveTable, + }, + ) { + return false, nil + } + + // check for required features + evTimeSeries, err := util.TimeSeries(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + evIncentiveTable, err := util.IncentiveTable(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if timeseries descriptions contains constraints data + if _, err = evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints); err != nil { + return false, err + } + + // check if incentive table descriptions contains data for the required scope + if _, err = evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable); err != nil { + return false, err + } + + return true, nil +} diff --git a/uccevc/uccevc_test.go b/uccevc/uccevc_test.go new file mode 100644 index 0000000..4b4a2e7 --- /dev/null +++ b/uccevc/uccevc_test.go @@ -0,0 +1,58 @@ +package uccevc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeRecommendation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/ucevcc/api.go b/ucevcc/api.go index 5d8a1af..59b77bc 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -9,9 +9,12 @@ import ( //go:generate mockery // interface for the EV Commissioning and Configuration UseCase -type UCEvCCInterface interface { +type UCEVCCInterface interface { api.UseCaseInterface + // return the current charge state of the EV + CurrentChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) + // Scenario 1 & 8 // return if the EV is connected @@ -20,32 +23,29 @@ type UCEvCCInterface interface { // Scenario 2 // return the current communication standard type used to communicate between EVSE and EV - EVCommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) + CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) // Scenario 3 // return if the EV supports asymmetric charging - EVAsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) + AsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) // Scenario 4 // return the identifications of the currently connected EV or nil if not available // these can be multiple, e.g. PCID, Mac Address, RFID - EVIdentifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) + Identifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) // Scenario 5 // the manufacturer data of an EVSE // returns deviceName, serialNumber, error - EVManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) // Scenario 6 - // return the number of ac connected phases of the EV or 0 if it is unknown - EVConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) - // return the min, max, default limits for each phase of the connected EV - EVCurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) + CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) // Scenario 7 @@ -61,27 +61,3 @@ type IdentificationItem struct { // the type of the identification value, e.g. ValueType model.IdentificationTypeType } - -const ( - UcEVCCUnknownCommunicationStandard string = "unknown" -) - -const ( - // An EV was connected - UCEvCCEventConnected api.UseCaseEventType = "ucEvConnected" - - // An EV was disconnected - UCEvCCEventDisconnected api.UseCaseEventType = "ucEvDisonnected" - - // EV device configuration data was updated (CommunicationStandard, Asymmetric charging) - UCEvCCEventConfigurationUdpate api.UseCaseEventType = "ucEvConfigurationUpdate" - - // EV manufacturer data was updated - UCEvCCEventManufacturerUpdate api.UseCaseEventType = "ucEvManufacturerUpdate" - - // EV electrical connection data was updated (number of phases of the EVSE) - UCEvCCEventElectricalConnectionUpdate api.UseCaseEventType = "ucEvElectricalConnectionUpdate" - - // EV permitted power limits updated - UCEvCCEventPermittedLimitsUpdate api.UseCaseEventType = "ucEvPermittedLimitsUpdate" -) diff --git a/ucevcc/events.go b/ucevcc/events.go index 4af3209..ccf0112 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -1,61 +1,59 @@ package ucevcc import ( + "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" - "github.com/enbility/spine-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) // handle SPINE events -func (e *UCEvCC) HandleEvent(payload api.EventPayload) { - // only about events from an EVSE entity or device changes for this remote device +func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { + // only about events from an EV entity or device changes for this remote device - if payload.Entity == nil { + if util.IsDeviceDisconnected(payload) { + e.evDisconnected(payload.Ski, payload.Entity) return } - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeEV { + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { return } - switch payload.EventType { - case api.EventTypeDeviceChange: - if payload.ChangeType == api.ElementChangeRemove { - e.evDisconnected(payload.Ski, payload.Entity) - } - - case api.EventTypeEntityChange: - switch payload.ChangeType { - case api.ElementChangeAdd: - e.evConnected(payload.Ski, payload.Entity) - case api.ElementChangeRemove: - e.evDisconnected(payload.Ski, payload.Entity) - } + if util.IsEvConnected(payload) { + e.evConnected(payload.Ski, payload.Entity) + return + } else if util.IsEvDisconnected(payload) { + e.evDisconnected(payload.Ski, payload.Entity) + return + } - case api.EventTypeDataChange: - if payload.ChangeType != api.ElementChangeUpdate { + switch payload.EventType { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { return } switch payload.Data.(type) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.evConfigurationDescriptionDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceConfigurationKeyValueListDataType: e.evConfigurationDataUpdate(payload.Ski, payload.Entity) case *model.DeviceClassificationManufacturerDataType: e.evManufacturerDataUpdate(payload.Ski, payload.Entity) - case *model.ElectricalConnectionDescriptionListDataType: - e.evElectricalDescriptionUpdate(payload.Ski, payload.Entity) case *model.ElectricalConnectionParameterDescriptionListDataType: e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) case *model.ElectricalConnectionPermittedValueSetListDataType: e.evElectricalPermittedValuesUpdate(payload.Ski, payload.Entity) + case *model.IdentificationListDataType: + e.evIdentificationDataUpdate(payload.Ski, payload.Entity) } } } // an EV was connected -func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { +func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) { // initialise features, e.g. subscriptions, descriptions if evDeviceClassification, err := util.DeviceClassification(e.service, entity); err == nil { if _, err := evDeviceClassification.Subscribe(); err != nil { @@ -94,13 +92,13 @@ func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { logging.Log().Debug(err) } - // get electrical connection descriptions - if _, err := evElectricalConnection.RequestDescriptions(); err != nil { + // get electrical connection parameter descriptions + if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Debug(err) } - // get electrical connection parameter descriptions - if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { + // get electrical permitted values descriptions + if _, err := evElectricalConnection.RequestPermittedValueSets(); err != nil { logging.Log().Debug(err) } } @@ -116,18 +114,16 @@ func (e *UCEvCC) evConnected(ski string, entity api.EntityRemoteInterface) { } } - e.reader.SpineEvent(ski, entity, UCEvCCEventConnected) + e.reader.SpineEvent(ski, entity, api.UCEVCCEventConnected) } // an EV was disconnected -func (e *UCEvCC) evDisconnected(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCCEventDisconnected) +func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, api.UCEVCCEventDisconnected) } -// the configuration key Data of an EV was updated -func (e *UCEvCC) evConfigurationDataUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCCEventConfigurationUdpate) - +// the configuration key description data of an EV was updated +func (e *UCEVCC) evConfigurationDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { // key value descriptions received, now get the data if _, err := evDeviceConfiguration.RequestKeyValues(); err != nil { @@ -136,18 +132,52 @@ func (e *UCEvCC) evConfigurationDataUpdate(ski string, entity api.EntityRemoteIn } } -// the manufacturer Data of an EV was updated -func (e *UCEvCC) evManufacturerDataUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCCEventManufacturerUpdate) +// the configuration key data of an EV was updated +func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil { + return + } + + // Scenario 2 + if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVCCCommunicationStandardDataUpdate) + } + + // Scenario 3 + if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVCCAsymmetricChargingDataUpdate) + } +} + +// the identification data of an EV was updated +func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evIdentification, err := util.Identification(e.service, entity) + if err != nil { + return + } + + // Scenario 4 + if values, err := evIdentification.GetValues(); err == nil { + for _, item := range values { + if item.IdentificationId == nil || item.IdentificationValue == nil { + continue + } + + e.reader.SpineEvent(ski, entity, api.UCEVCCIdentificationDataUpdate) + return + } + } } -// the electrical connection description data of an EV was updated -func (e *UCEvCC) evElectricalDescriptionUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCCEventElectricalConnectionUpdate) +// the manufacturer data of an EV was updated +func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 5 + e.reader.SpineEvent(ski, entity, api.UCEVCCManufacturerDataUpdate) } // the electrical connection parameter description data of an EV was updated -func (e *UCEvCC) evElectricalParamerDescriptionUpdate(ski string, entity api.EntityRemoteInterface) { +func (e *UCEVCC) evElectricalParamerDescriptionUpdate(ski string, entity spineapi.EntityRemoteInterface) { if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { if _, err := evElectricalConnection.RequestPermittedValueSets(); err != nil { logging.Log().Error("Error getting electrical permitted values:", err) @@ -156,6 +186,22 @@ func (e *UCEvCC) evElectricalParamerDescriptionUpdate(ski string, entity api.Ent } // the electrical connection permitted value sets data of an EV was updated -func (e *UCEvCC) evElectricalPermittedValuesUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCCEventPermittedLimitsUpdate) +func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evElectricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return + } + + data, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) + if err != nil || data.ParameterId == nil { + return + } + + values, err := evElectricalConnection.GetPermittedValueSetForParameterId(*data.ParameterId) + if err != nil || values == nil { + return + } + + // Scenario 6 + e.reader.SpineEvent(ski, entity, api.UCEVCCChargingPowerLimitsDataUpdate) } diff --git a/ucevcc/public.go b/ucevcc/public.go index 75d1737..8b6b926 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -8,15 +8,55 @@ import ( "github.com/enbility/spine-go/model" ) +// return the current charge state of the EV +func (e *UCEVCC) CurrentChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return api.EVChargeStateTypeUnplugged, nil + } + + evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) + if err != nil { + return api.EVChargeStateTypeUnplugged, nil + } + + diagnosisState, err := evDeviceDiagnosis.GetState() + if err != nil { + return api.EVChargeStateTypeUnknown, err + } + + operatingState := diagnosisState.OperatingState + if operatingState == nil { + return api.EVChargeStateTypeUnknown, features.ErrDataNotAvailable + } + + switch *operatingState { + case model.DeviceDiagnosisOperatingStateTypeNormalOperation: + return api.EVChargeStateTypeActive, nil + case model.DeviceDiagnosisOperatingStateTypeStandby: + return api.EVChargeStateTypePaused, nil + case model.DeviceDiagnosisOperatingStateTypeFailure: + return api.EVChargeStateTypeError, nil + case model.DeviceDiagnosisOperatingStateTypeFinished: + return api.EVChargeStateTypeFinished, nil + } + + return api.EVChargeStateTypeUnknown, nil +} + // return if an EV is connected // // this includes all required features and // minimal data being available -func (e *UCEvCC) EVConnected(entity spineapi.EntityRemoteInterface) bool { +func (e *UCEVCC) EVConnected(entity spineapi.EntityRemoteInterface) bool { if entity == nil || entity.Device() == nil { return false } + // getting current charge state should work + if _, err := e.CurrentChargeState(entity); err != nil { + return false + } + remoteDevice := e.service.LocalDevice().RemoteDeviceForSki(entity.Device().Ski()) if remoteDevice == nil { return false @@ -26,7 +66,7 @@ func (e *UCEvCC) EVConnected(entity spineapi.EntityRemoteInterface) bool { return remoteDevice.Entity(entity.Address().Entity) == entity } -func (e *UCEvCC) deviceConfigurationValueForKeyName( +func (e *UCEVCC) deviceConfigurationValueForKeyName( entity spineapi.EntityRemoteInterface, keyname model.DeviceConfigurationKeyNameType, valueType model.DeviceConfigurationKeyValueTypeType) (any, error) { @@ -72,8 +112,8 @@ func (e *UCEvCC) deviceConfigurationValueForKeyName( // - ErrDataNotAvailable if that information is not (yet) available // - ErrNotSupported if getting the communication standard is not supported // - and others -func (e *UCEvCC) EVCommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { - unknown := UcEVCCUnknownCommunicationStandard +func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { + unknown := api.UCEVCCCommunicationStandardUnknown data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) if err != nil { @@ -93,7 +133,7 @@ func (e *UCEvCC) EVCommunicationStandard(entity spineapi.EntityRemoteInterface) // // possible errors: // - ErrDataNotAvailable if that information is not (yet) available -func (e *UCEvCC) EVAsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) { +func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) { data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeBoolean) if err != nil { return false, err @@ -113,7 +153,7 @@ func (e *UCEvCC) EVAsymmetricChargingSupported(entity spineapi.EntityRemoteInter // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *UCEvCC) EVIdentifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) { +func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return nil, api.ErrNoEvEntity } @@ -150,7 +190,7 @@ func (e *UCEvCC) EVIdentifications(entity spineapi.EntityRemoteInterface) ([]Ide // the manufacturer data of an EVSE // returns deviceName, serialNumber, error -func (e *UCEvCC) EVManufacturerData( +func (e *UCEVCC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( string, @@ -186,7 +226,7 @@ func (e *UCEvCC) EVManufacturerData( } // return the number of ac connected phases of the EV or 0 if it is unknown -func (e *UCEvCC) EVConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { +func (e *UCEVCC) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return 0, api.ErrNoEvEntity } @@ -216,7 +256,7 @@ func (e *UCEvCC) EVConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEvCC) EVCurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { +func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return nil, nil, nil, api.ErrNoEvEntity } @@ -258,7 +298,7 @@ func (e *UCEvCC) EVCurrentLimits(entity spineapi.EntityRemoteInterface) ([]float // is the EV in sleep mode // returns operatingState, lastErrorCode, error -func (e *UCEvCC) EVInSleepMode( +func (e *UCEVCC) EVInSleepMode( entity spineapi.EntityRemoteInterface, ) (bool, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index 1e62a0e..6ca99f3 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -1,98 +1,111 @@ package ucevcc import ( - "fmt" "testing" - "time" "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - eebusmocks "github.com/enbility/eebus-go/mocks" - "github.com/enbility/eebus-go/service" "github.com/enbility/eebus-go/util" eebusutil "github.com/enbility/eebus-go/util" - "github.com/enbility/ship-go/cert" - shipmocks "github.com/enbility/ship-go/mocks" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" ) -func TestEvCCSuite(t *testing.T) { - suite.Run(t, new(EvCCSuite)) -} +func (s *EVCCSuite) Test_EVCurrentChargeState() { + data, err := s.sut.CurrentChargeState(s.mockRemoteEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypeUnplugged, data) -type EvCCSuite struct { - suite.Suite + data, err = s.sut.CurrentChargeState(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypeUnknown, data) - sut *UCEvCC + stateData := &model.DeviceDiagnosisStateDataType{ + OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), + } - service eebusapi.ServiceInterface + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) + assert.Nil(s.T(), fErr) - remoteDevice spineapi.DeviceRemoteInterface - mockRemoteEntity *mocks.EntityRemoteInterface - evEntity spineapi.EntityRemoteInterface -} + data, err = s.sut.CurrentChargeState(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypeActive, data) -func (s *EvCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { -} + stateData = &model.DeviceDiagnosisStateDataType{ + OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeStandby), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentChargeState(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypePaused, data) -func (s *EvCCSuite) BeforeTest(suiteName, testName string) { - cert, _ := cert.CreateCertificate("test", "test", "DE", "test") - configuration, _ := eebusapi.NewConfiguration( - "test", "test", "test", "test", - model.DeviceTypeTypeEnergyManagementSystem, - []model.EntityTypeType{model.EntityTypeTypeCEM}, - 9999, cert, 230.0, time.Second*4) - - serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) - serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() - - s.service = service.NewService(configuration, serviceHandler) - _ = s.service.Setup() - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) - s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() - mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() - s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() - s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() - entityAddress := &model.EntityAddressType{} - s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() - - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) - s.sut = NewUCEvCC(s.service, s.service.LocalService(), s) - s.sut.AddFeatures() - s.sut.AddUseCase() - s.evEntity = entities[1] + stateData = &model.DeviceDiagnosisStateDataType{ + OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeFailure), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentChargeState(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypeError, data) + + stateData = &model.DeviceDiagnosisStateDataType{ + OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeFinished), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentChargeState(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypeFinished, data) + + stateData = &model.DeviceDiagnosisStateDataType{ + OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeInAlarm), + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentChargeState(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), api.EVChargeStateTypeUnknown, data) } -func (s *EvCCSuite) Test_EVConnected() { +func (s *EVCCSuite) Test_EVConnected() { data := s.sut.EVConnected(nil) assert.Equal(s.T(), false, data) data = s.sut.EVConnected(s.mockRemoteEntity) assert.Equal(s.T(), false, data) + data = s.sut.EVConnected(s.evEntity) + assert.Equal(s.T(), false, data) + + stateData := &model.DeviceDiagnosisStateDataType{ + OperatingState: util.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) + assert.Nil(s.T(), fErr) + data = s.sut.EVConnected(s.evEntity) assert.Equal(s.T(), true, data) } -func (s *EvCCSuite) Test_EVCommunicationStandard() { - data, err := s.sut.EVCommunicationStandard(s.mockRemoteEntity) +func (s *EVCCSuite) Test_EVCommunicationStandard() { + data, err := s.sut.CommunicationStandard(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) - data, err = s.sut.EVCommunicationStandard(s.evEntity) + data, err = s.sut.CommunicationStandard(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -107,9 +120,9 @@ func (s *EvCCSuite) Test_EVCommunicationStandard() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVCommunicationStandard(s.evEntity) + data, err = s.sut.CommunicationStandard(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) descData = &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -123,9 +136,9 @@ func (s *EvCCSuite) Test_EVCommunicationStandard() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVCommunicationStandard(s.evEntity) + data, err = s.sut.CommunicationStandard(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), UcEVCCUnknownCommunicationStandard, data) + assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) devData := &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ @@ -141,17 +154,17 @@ func (s *EvCCSuite) Test_EVCommunicationStandard() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, devData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVCommunicationStandard(s.evEntity) + data, err = s.sut.CommunicationStandard(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), string(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), data) } -func (s *EvCCSuite) Test_EVAsymmetricChargingSupported() { - data, err := s.sut.EVAsymmetricChargingSupported(s.mockRemoteEntity) +func (s *EVCCSuite) Test_EVAsymmetricChargingSupported() { + data, err := s.sut.AsymmetricChargingSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) - data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupported(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -168,7 +181,7 @@ func (s *EvCCSuite) Test_EVAsymmetricChargingSupported() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupported(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -184,7 +197,7 @@ func (s *EvCCSuite) Test_EVAsymmetricChargingSupported() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupported(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -202,21 +215,21 @@ func (s *EvCCSuite) Test_EVAsymmetricChargingSupported() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, devData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVAsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupported(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), true, data) } -func (s *EvCCSuite) Test_EVIdentification() { - data, err := s.sut.EVIdentifications(s.mockRemoteEntity) +func (s *EVCCSuite) Test_EVIdentification() { + data, err := s.sut.Identifications(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), []IdentificationItem(nil), data) - data, err = s.sut.EVIdentifications(s.evEntity) + data, err = s.sut.Identifications(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), []IdentificationItem(nil), data) - data, err = s.sut.EVIdentifications(s.evEntity) + data, err = s.sut.Identifications(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), []IdentificationItem(nil), data) @@ -234,24 +247,24 @@ func (s *EvCCSuite) Test_EVIdentification() { fErr := rFeature.UpdateData(model.FunctionTypeIdentificationListData, idData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVIdentifications(s.evEntity) + data, err = s.sut.Identifications(s.evEntity) assert.Nil(s.T(), err) resultData := []IdentificationItem{{Value: "test", ValueType: model.IdentificationTypeTypeEui64}} assert.Equal(s.T(), resultData, data) } -func (s *EvCCSuite) Test_EVManufacturerData() { - device, serial, err := s.sut.EVManufacturerData(s.mockRemoteEntity) +func (s *EVCCSuite) Test_EVManufacturerData() { + device, serial, err := s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) - device, serial, err = s.sut.EVManufacturerData(s.evEntity) + device, serial, err = s.sut.ManufacturerData(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) - device, serial, err = s.sut.EVManufacturerData(s.evEntity) + device, serial, err = s.sut.ManufacturerData(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) @@ -262,7 +275,7 @@ func (s *EvCCSuite) Test_EVManufacturerData() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.EVManufacturerData(s.evEntity) + device, serial, err = s.sut.ManufacturerData(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) @@ -275,72 +288,26 @@ func (s *EvCCSuite) Test_EVManufacturerData() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.EVManufacturerData(s.evEntity) + device, serial, err = s.sut.ManufacturerData(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), "test", device) assert.Equal(s.T(), "12345", serial) } -func (s *EvCCSuite) Test_EVConnectedPhases() { - data, err := s.sut.EVConnectedPhases(s.mockRemoteEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), uint(0), data) - - data, err = s.sut.EVConnectedPhases(s.evEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), uint(0), data) - - data, err = s.sut.EVConnectedPhases(s.evEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), uint(0), data) - - descData := &model.ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - }, - }, - } - - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) - fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) - assert.Nil(s.T(), fErr) - - data, err = s.sut.EVConnectedPhases(s.evEntity) - assert.Nil(s.T(), err) - assert.Equal(s.T(), uint(0), data) - - descData = &model.ElectricalConnectionDescriptionListDataType{ - ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - AcConnectedPhases: util.Ptr(uint(1)), - }, - }, - } - - fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) - assert.Nil(s.T(), fErr) - - data, err = s.sut.EVConnectedPhases(s.evEntity) - assert.Nil(s.T(), err) - assert.Equal(s.T(), uint(1), data) -} - -func (s *EvCCSuite) Test_EVCurrentLimits() { - minData, maxData, defaultData, err := s.sut.EVCurrentLimits(s.mockRemoteEntity) +func (s *EVCCSuite) Test_EVCurrentLimits() { + minData, maxData, defaultData, err := s.sut.CurrentLimits(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), minData) assert.Nil(s.T(), maxData) assert.Nil(s.T(), defaultData) - minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), minData) assert.Nil(s.T(), maxData) assert.Nil(s.T(), defaultData) - minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), minData) assert.Nil(s.T(), maxData) @@ -373,7 +340,7 @@ func (s *EvCCSuite) Test_EVCurrentLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), minData) assert.Nil(s.T(), maxData) @@ -467,7 +434,7 @@ func (s *EvCCSuite) Test_EVCurrentLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - minData, maxData, defaultData, err = s.sut.EVCurrentLimits(s.evEntity) + minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) assert.Nil(s.T(), err) assert.Nil(s.T(), err) @@ -483,7 +450,7 @@ func (s *EvCCSuite) Test_EVCurrentLimits() { } } -func (s *EvCCSuite) Test_EVInSleepMode() { +func (s *EVCCSuite) Test_EVInSleepMode() { data, err := s.sut.EVInSleepMode(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -513,131 +480,3 @@ func (s *EvCCSuite) Test_EVInSleepMode() { assert.Nil(s.T(), err) assert.Equal(s.T(), true, data) } - -const remoteSki string = "testremoteski" - -func setupDevices( - eebusService eebusapi.ServiceInterface, t *testing.T) ( - spineapi.DeviceRemoteInterface, - []spineapi.EntityRemoteInterface) { - localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - localEntity.AddFeature(f) - - writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) - writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() - sender := spine.NewSender(writeHandler) - remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - - var clientRemoteFeatures = []struct { - featureType model.FeatureTypeType - supportedFcts []model.FunctionType - }{ - {model.FeatureTypeTypeDeviceConfiguration, - []model.FunctionType{ - model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, - model.FunctionTypeDeviceConfigurationKeyValueListData, - }, - }, - {model.FeatureTypeTypeIdentification, - []model.FunctionType{ - model.FunctionTypeIdentificationListData, - }, - }, - {model.FeatureTypeTypeDeviceClassification, - []model.FunctionType{ - model.FunctionTypeDeviceClassificationManufacturerData, - }, - }, - {model.FeatureTypeTypeElectricalConnection, - []model.FunctionType{ - model.FunctionTypeElectricalConnectionDescriptionListData, - }, - }, - {model.FeatureTypeTypeDeviceDiagnosis, - []model.FunctionType{ - model.FunctionTypeDeviceDiagnosisStateData, - }, - }, - } - - remoteDeviceName := "remote" - - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { - supportedFcts := []model.FunctionPropertyType{} - for _, fct := range feature.supportedFcts { - supportedFct := model.FunctionPropertyType{ - Function: eebusutil.Ptr(fct), - PossibleOperations: &model.PossibleOperationsType{ - Read: &model.PossibleOperationsReadType{}, - }, - } - supportedFcts = append(supportedFcts, supportedFct) - } - - featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ - Description: &model.NetworkManagementFeatureDescriptionDataType{ - FeatureAddress: &model.FeatureAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - Feature: eebusutil.Ptr(model.AddressFeatureType(index)), - }, - FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(model.RoleTypeServer), - SupportedFunction: supportedFcts, - }, - } - featureInformations = append(featureInformations, featureInformation) - } - - detailedData := &model.NodeManagementDetailedDiscoveryDataType{ - DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ - Description: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - }, - }, - }, - EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - }, - EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), - }, - }, - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - }, - EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), - }, - }, - }, - FeatureInformation: featureInformations, - } - - entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) - if err != nil { - fmt.Println(err) - } - - localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) - - return remoteDevice, entities -} diff --git a/ucevcc/results.go b/ucevcc/results.go index b6d9599..1619e4c 100644 --- a/ucevcc/results.go +++ b/ucevcc/results.go @@ -1,8 +1,47 @@ package ucevcc import ( + "fmt" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) -func (e *UCEvCC) HandleResult(errorMsg api.ResultMessage) { +func (e *UCEVCC) HandleResult(errorMsg api.ResultMessage) { + // before SPINE 1.3 the heartbeats are on the EVSE entity + if errorMsg.EntityRemote == nil || + (errorMsg.EntityRemote.EntityType() != model.EntityTypeTypeEV && + errorMsg.EntityRemote.EntityType() != model.EntityTypeTypeEVSE) { + return + } + + // handle errors coming from the remote EVSE entity + switch errorMsg.FeatureLocal.Type() { + case model.FeatureTypeTypeDeviceDiagnosis: + e.handleResultDeviceDiagnosis(errorMsg) + } +} + +// Handle DeviceDiagnosis Results +func (e *UCEVCC) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { + // is this an error for a heartbeat message? + if *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { + return + } + + // check if this is for a cached notify message + datagram, err := resultMsg.DeviceRemote.Sender().DatagramForMsgCounter(resultMsg.MsgCounterReference) + if err != nil { + return + } + + if len(datagram.Payload.Cmd) > 0 && + datagram.Payload.Cmd[0].DeviceDiagnosisHeartbeatData != nil { + // something is horribly wrong, disconnect and hope a new connection will fix it + errorText := fmt.Sprintf("Error Code: %d", resultMsg.Result.ErrorNumber) + if resultMsg.Result.Description != nil { + errorText = fmt.Sprintf("%s - %s", errorText, string(*resultMsg.Result.Description)) + } + e.service.DisconnectSKI(resultMsg.DeviceRemote.Ski(), errorText) + } } diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go new file mode 100644 index 0000000..67083b2 --- /dev/null +++ b/ucevcc/testhelper_test.go @@ -0,0 +1,201 @@ +package ucevcc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEVCCSuite(t *testing.T) { + suite.Run(t, new(EVCCSuite)) +} + +type EVCCSuite struct { + suite.Suite + + sut *UCEVCC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *EVCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EVCCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEVCC(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeDeviceConfiguration, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + {model.FeatureTypeTypeIdentification, + []model.FunctionType{ + model.FunctionTypeIdentificationListData, + }, + }, + {model.FeatureTypeTypeDeviceClassification, + []model.FunctionType{ + model.FunctionTypeDeviceClassificationManufacturerData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionDescriptionListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 0805109..99252e7 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -4,20 +4,21 @@ import ( "github.com/enbility/cemd/api" serviceapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) -type UCEvCC struct { +type UCEVCC struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface } -var _ UCEvCCInterface = (*UCEvCC)(nil) +var _ UCEVCCInterface = (*UCEVCC)(nil) -func NewUCEvCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvCC { - uc := &UCEvCC{ +func NewUCEVCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVCC { + uc := &UCEVCC{ service: service, reader: reader, } @@ -27,11 +28,11 @@ func NewUCEvCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta return uc } -func (c *UCEvCC) UseCaseName() model.UseCaseNameType { +func (c *UCEVCC) UseCaseName() model.UseCaseNameType { return model.UseCaseNameTypeEVCommissioningAndConfiguration } -func (e *UCEvCC) AddFeatures() { +func (e *UCEVCC) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features @@ -40,8 +41,7 @@ func (e *UCEvCC) AddFeatures() { model.FeatureTypeTypeIdentification, model.FeatureTypeTypeDeviceClassification, model.FeatureTypeTypeElectricalConnection, - model.FeatureTypeTypeMeasurement, - model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeDeviceDiagnosis, } for _, feature := range clientFeatures { @@ -50,7 +50,7 @@ func (e *UCEvCC) AddFeatures() { } } -func (e *UCEvCC) AddUseCase() { +func (e *UCEVCC) AddUseCase() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( @@ -61,3 +61,27 @@ func (e *UCEvCC) AddUseCase() { true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) } + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCEVCC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3, 8}, + []model.FeatureTypeType{model.FeatureTypeTypeDeviceConfiguration}, + ) { + return false, nil + } + + return true, nil +} diff --git a/ucevcem/api.go b/ucevcem/api.go index bee13b6..cf8678a 100644 --- a/ucevcem/api.go +++ b/ucevcem/api.go @@ -8,26 +8,24 @@ import ( //go:generate mockery // interface for the EVSE Commissioning and Configuration UseCase -type UCEvCEMInterface interface { +type UCEVCEMInterface interface { api.UseCaseInterface + // return the number of ac connected phases of the EV or 0 if it is unknown + ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) + // Scenario 1 // return the last current measurement for each phase of the connected EV - EVCurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 2 // return the last power measurement for each phase of the connected EV - EVPowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 3 // return the charged energy measurement in Wh of the connected EV - EVChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) } - -const ( - // EV measurement data updated - UCEvCEMMeasurementDataUpdate api.UseCaseEventType = "ucEvCEMMeasurementDataUpdate" -) diff --git a/ucevcem/events.go b/ucevcem/events.go index 54615e9..7bc12d7 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -1,40 +1,37 @@ package ucevcem import ( + "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" - "github.com/enbility/spine-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) // handle SPINE events -func (e *UCEvCEM) HandleEvent(payload api.EventPayload) { - // only about events from an EVSE entity or device changes for this remote device +func (e *UCEVCEM) HandleEvent(payload spineapi.EventPayload) { + // only about events from an EV entity or device changes for this remote device - if payload.Entity == nil { + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { return } - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeEV { + if util.IsEvConnected(payload) { + e.evConnected(payload.Entity) return } switch payload.EventType { - case api.EventTypeEntityChange: - switch payload.ChangeType { - case api.ElementChangeAdd: - e.evConnected(payload.Ski, payload.Entity) - } - - case api.EventTypeDataChange: - if payload.ChangeType != api.ElementChangeUpdate { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { return } switch payload.Data.(type) { + case *model.ElectricalConnectionDescriptionListDataType: + e.evElectricalConnectionDescriptionDataUpdate(payload.Ski, payload.Entity) case *model.MeasurementDescriptionListDataType: - e.evMeasurementDescriptionDataUpdate(payload.Ski, payload.Entity) + e.evMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: e.evMeasurementDataUpdate(payload.Ski, payload.Entity) } @@ -42,7 +39,7 @@ func (e *UCEvCEM) HandleEvent(payload api.EventPayload) { } // an EV was connected -func (e *UCEvCEM) evConnected(ski string, entity api.EntityRemoteInterface) { +func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { // initialise features, e.g. subscriptions, descriptions if evMeasurement, err := util.Measurement(e.service, entity); err == nil { if _, err := evMeasurement.Subscribe(); err != nil { @@ -50,19 +47,28 @@ func (e *UCEvCEM) evConnected(ski string, entity api.EntityRemoteInterface) { } // get measurement descriptions - if err := evMeasurement.RequestDescriptions(); err != nil { + if _, err := evMeasurement.RequestDescriptions(); err != nil { logging.Log().Debug(err) } // get measurement constraints - if err := evMeasurement.RequestConstraints(); err != nil { + if _, err := evMeasurement.RequestConstraints(); err != nil { logging.Log().Debug(err) } } } +// the electrical connection description data of an EV was updated +func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + if _, err := e.ConnectedPhases(entity); err != nil { + return + } + + e.reader.SpineEvent(ski, entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) +} + // the measurement description data of an EV was updated -func (e *UCEvCEM) evMeasurementDescriptionDataUpdate(ski string, entity api.EntityRemoteInterface) { +func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { if evMeasurement, err := util.Measurement(e.service, entity); err == nil { // get measurement values if _, err := evMeasurement.RequestValues(); err != nil { @@ -72,6 +78,19 @@ func (e *UCEvCEM) evMeasurementDescriptionDataUpdate(ski string, entity api.Enti } // the measurement data of an EV was updated -func (e *UCEvCEM) evMeasurementDataUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvCEMMeasurementDataUpdate) +func (e *UCEVCEM) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 1 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVCEMCurrentMeasurementDataUpdate) + } + + // Scenario 2 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVCEMPowerMeasurementDataUpdate) + } + + // Scenario 3 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) + } } diff --git a/ucevcem/public.go b/ucevcem/public.go index 9420ff8..1866f37 100644 --- a/ucevcem/public.go +++ b/ucevcem/public.go @@ -10,13 +10,39 @@ import ( "github.com/enbility/spine-go/model" ) +// return the number of ac connected phases of the EV or 0 if it is unknown +func (e *UCEVCEM) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return 0, api.ErrNoEvEntity + } + + evElectricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return 0, features.ErrDataNotAvailable + } + + data, err := evElectricalConnection.GetDescriptions() + if err != nil { + return 0, features.ErrDataNotAvailable + } + + for _, item := range data { + if item.ElectricalConnectionId != nil && item.AcConnectedPhases != nil { + return *item.AcConnectedPhases, nil + } + } + + // default to 0 if the value is not available + return 0, nil +} + // return the last current measurement for each phase of the connected EV // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEvCEM) EVCurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if entity.EntityType() != model.EntityTypeTypeEV { +func (e *UCEVCEM) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return nil, api.ErrNoEvseEntity } @@ -74,7 +100,7 @@ func (e *UCEvCEM) EVCurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]f // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEvCEM) EVPowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCEVCEM) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity.EntityType() != model.EntityTypeTypeEV { return nil, api.ErrNoEvseEntity } @@ -134,7 +160,7 @@ func (e *UCEvCEM) EVPowerPerPhase(entity spineapi.EntityRemoteInterface) ([]floa // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEvCEM) EVChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCEVCEM) ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { if entity.EntityType() != model.EntityTypeTypeEV { return 0, api.ErrNoEvseEntity } diff --git a/ucevcem/public_test.go b/ucevcem/public_test.go index 5ad4dc8..427d4f9 100644 --- a/ucevcem/public_test.go +++ b/ucevcem/public_test.go @@ -1,85 +1,67 @@ package ucevcem import ( - "fmt" - "testing" - "time" - - "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - eebusmocks "github.com/enbility/eebus-go/mocks" - "github.com/enbility/eebus-go/service" eebusutil "github.com/enbility/eebus-go/util" - "github.com/enbility/ship-go/cert" - shipmocks "github.com/enbility/ship-go/mocks" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" ) -func TestEvCEMSuite(t *testing.T) { - suite.Run(t, new(EvCEMSuite)) -} +func (s *EVCEMSuite) Test_EVConnectedPhases() { + data, err := s.sut.ConnectedPhases(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), uint(0), data) -type EvCEMSuite struct { - suite.Suite + data, err = s.sut.ConnectedPhases(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), uint(0), data) - sut *UCEvCEM + data, err = s.sut.ConnectedPhases(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), uint(0), data) - service eebusapi.ServiceInterface + descData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } - remoteDevice spineapi.DeviceRemoteInterface - mockRemoteEntity *mocks.EntityRemoteInterface - evEntity spineapi.EntityRemoteInterface -} + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) -func (s *EvCEMSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { -} + data, err = s.sut.ConnectedPhases(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), uint(0), data) -func (s *EvCEMSuite) BeforeTest(suiteName, testName string) { - cert, _ := cert.CreateCertificate("test", "test", "DE", "test") - configuration, _ := eebusapi.NewConfiguration( - "test", "test", "test", "test", - model.DeviceTypeTypeEnergyManagementSystem, - []model.EntityTypeType{model.EntityTypeTypeCEM}, - 9999, cert, 230.0, time.Second*4) - - serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) - serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() - - s.service = service.NewService(configuration, serviceHandler) - _ = s.service.Setup() - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) - s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() - s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() - s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() - - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) - s.sut = NewUCEvCEM(s.service, s.service.LocalService(), s) - s.sut.AddFeatures() - s.sut.AddUseCase() - s.evEntity = entities[1] + descData = &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + AcConnectedPhases: eebusutil.Ptr(uint(1)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.ConnectedPhases(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), uint(1), data) } -func (s *EvCEMSuite) Test_EVCurrentsPerPhase() { - data, err := s.sut.EVCurrentsPerPhase(s.mockRemoteEntity) +func (s *EVCEMSuite) Test_EVCurrentsPerPhase() { + data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentsPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentsPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -99,7 +81,7 @@ func (s *EvCEMSuite) Test_EVCurrentsPerPhase() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -118,7 +100,7 @@ func (s *EvCEMSuite) Test_EVCurrentsPerPhase() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentsPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -134,21 +116,21 @@ func (s *EvCEMSuite) Test_EVCurrentsPerPhase() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVCurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentsPerPhase(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data[0]) } -func (s *EvCEMSuite) Test_EVPowerPerPhase_Power() { - data, err := s.sut.EVPowerPerPhase(s.mockRemoteEntity) +func (s *EVCEMSuite) Test_EVPowerPerPhase_Power() { + data, err := s.sut.PowerPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -168,7 +150,7 @@ func (s *EvCEMSuite) Test_EVPowerPerPhase_Power() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -187,7 +169,7 @@ func (s *EvCEMSuite) Test_EVPowerPerPhase_Power() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -203,17 +185,17 @@ func (s *EvCEMSuite) Test_EVPowerPerPhase_Power() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 80.0, data[0]) } -func (s *EvCEMSuite) Test_EVPowerPerPhase_Current() { - data, err := s.sut.EVPowerPerPhase(s.mockRemoteEntity) +func (s *EVCEMSuite) Test_EVPowerPerPhase_Current() { + data, err := s.sut.PowerPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -233,7 +215,7 @@ func (s *EvCEMSuite) Test_EVPowerPerPhase_Current() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -252,7 +234,7 @@ func (s *EvCEMSuite) Test_EVPowerPerPhase_Current() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -268,21 +250,21 @@ func (s *EvCEMSuite) Test_EVPowerPerPhase_Current() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVPowerPerPhase(s.evEntity) + data, err = s.sut.PowerPerPhase(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 2300.0, data[0]) } -func (s *EvCEMSuite) Test_EVChargedEnergy() { - data, err := s.sut.EVChargedEnergy(s.mockRemoteEntity) +func (s *EVCEMSuite) Test_EVChargedEnergy() { + data, err := s.sut.ChargedEnergy(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.EVChargedEnergy(s.evEntity) + data, err = s.sut.ChargedEnergy(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.EVChargedEnergy(s.evEntity) + data, err = s.sut.ChargedEnergy(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -301,7 +283,7 @@ func (s *EvCEMSuite) Test_EVChargedEnergy() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVChargedEnergy(s.evEntity) + data, err = s.sut.ChargedEnergy(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -317,117 +299,7 @@ func (s *EvCEMSuite) Test_EVChargedEnergy() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVChargedEnergy(s.evEntity) + data, err = s.sut.ChargedEnergy(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 80.0, data) } - -const remoteSki string = "testremoteski" - -func setupDevices( - eebusService eebusapi.ServiceInterface, t *testing.T) ( - spineapi.DeviceRemoteInterface, - []spineapi.EntityRemoteInterface) { - localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - - writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) - writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() - sender := spine.NewSender(writeHandler) - remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - - var clientRemoteFeatures = []struct { - featureType model.FeatureTypeType - supportedFcts []model.FunctionType - }{ - {model.FeatureTypeTypeElectricalConnection, - []model.FunctionType{ - model.FunctionTypeElectricalConnectionDescriptionListData, - model.FunctionTypeElectricalConnectionParameterDescriptionListData, - model.FunctionTypeElectricalConnectionPermittedValueSetListData, - }, - }, - { - model.FeatureTypeTypeMeasurement, - []model.FunctionType{ - model.FunctionTypeMeasurementDescriptionListData, - model.FunctionTypeMeasurementListData, - }, - }, - } - - remoteDeviceName := "remote" - - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { - supportedFcts := []model.FunctionPropertyType{} - for _, fct := range feature.supportedFcts { - supportedFct := model.FunctionPropertyType{ - Function: eebusutil.Ptr(fct), - PossibleOperations: &model.PossibleOperationsType{ - Read: &model.PossibleOperationsReadType{}, - }, - } - supportedFcts = append(supportedFcts, supportedFct) - } - - featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ - Description: &model.NetworkManagementFeatureDescriptionDataType{ - FeatureAddress: &model.FeatureAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - Feature: eebusutil.Ptr(model.AddressFeatureType(index)), - }, - FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(model.RoleTypeServer), - SupportedFunction: supportedFcts, - }, - } - featureInformations = append(featureInformations, featureInformation) - } - - detailedData := &model.NodeManagementDetailedDiscoveryDataType{ - DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ - Description: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - }, - }, - }, - EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - }, - EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), - }, - }, - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - }, - EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), - }, - }, - }, - FeatureInformation: featureInformations, - } - - entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) - if err != nil { - fmt.Println(err) - } - - localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) - - return remoteDevice, entities -} diff --git a/ucevcem/results.go b/ucevcem/results.go index b8e77f6..e60d387 100644 --- a/ucevcem/results.go +++ b/ucevcem/results.go @@ -4,5 +4,5 @@ import ( "github.com/enbility/spine-go/api" ) -func (e *UCEvCEM) HandleResult(errorMsg api.ResultMessage) { +func (e *UCEVCEM) HandleResult(errorMsg api.ResultMessage) { } diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go new file mode 100644 index 0000000..c9e1953 --- /dev/null +++ b/ucevcem/testhelper_test.go @@ -0,0 +1,180 @@ +package ucevcem + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEVCEMSuite(t *testing.T) { + suite.Run(t, new(EVCEMSuite)) +} + +type EVCEMSuite struct { + suite.Suite + + sut *UCEVCEM + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *EVCEMSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EVCEMSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEVCEM(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionDescriptionListData, + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, + { + model.FeatureTypeTypeMeasurement, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementListData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index d6a6695..d430068 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -4,20 +4,21 @@ import ( "github.com/enbility/cemd/api" serviceapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) -type UCEvCEM struct { +type UCEVCEM struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface } -var _ UCEvCEMInterface = (*UCEvCEM)(nil) +var _ UCEVCEMInterface = (*UCEVCEM)(nil) -func NewUCEvCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvCEM { - uc := &UCEvCEM{ +func NewUCEVCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVCEM { + uc := &UCEVCEM{ service: service, reader: reader, } @@ -27,11 +28,11 @@ func NewUCEvCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDet return uc } -func (c *UCEvCEM) UseCaseName() model.UseCaseNameType { +func (c *UCEVCEM) UseCaseName() model.UseCaseNameType { return model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging } -func (e *UCEvCEM) AddFeatures() { +func (e *UCEVCEM) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features @@ -39,7 +40,7 @@ func (e *UCEvCEM) AddFeatures() { f.AddResultHandler(e) } -func (e *UCEvCEM) AddUseCase() { +func (e *UCEVCEM) AddUseCase() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( @@ -50,3 +51,27 @@ func (e *UCEvCEM) AddUseCase() { true, []model.UseCaseScenarioSupportType{1, 2, 3}) } + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCEVCEM) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + nil, + []model.FeatureTypeType{model.FeatureTypeTypeDeviceDiagnosis}, + ) { + return false, nil + } + + return true, nil +} diff --git a/ucevsecc/api.go b/ucevsecc/api.go index 3c36ef0..70eb3ac 100644 --- a/ucevsecc/api.go +++ b/ucevsecc/api.go @@ -9,28 +9,14 @@ import ( //go:generate mockery // interface for the EVSE Commissioning and Configuration UseCase -type UCEvseCCInterface interface { +type UCEVSECCInterface interface { api.UseCaseInterface // the manufacturer data of an EVSE // returns deviceName, serialNumber, error - EVSEManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) // the operating state data of an EVSE // returns operatingState, lastErrorCode, error - EVSEOperatingState(entity spineapi.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) + OperatingState(entity spineapi.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) } - -const ( - // An EVSE was connected - UCEvseCCEventConnected api.UseCaseEventType = "ucEvseConnected" - - // An EVSE was disconnected - UCEvseCCEventDisconnected api.UseCaseEventType = "ucEvseDisonnected" - - // EVSE manufacturer data was updated - UCEvseCCEventManufacturerUpdate api.UseCaseEventType = "ucEvseManufacturerUpdate" - - // EVSE operation state was updated - UCEvseCCEventOperationStateUpdate api.UseCaseEventType = "ucEvseOperationStateUpdate" -) diff --git a/ucevsecc/events.go b/ucevsecc/events.go index f91391b..33b3145 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -1,40 +1,37 @@ package ucevsecc import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" - "github.com/enbility/spine-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) // handle SPINE events -func (e *UCEvseCC) HandleEvent(payload api.EventPayload) { +func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EVSE entity or device changes for this remote device - if payload.Entity == nil { + if util.IsDeviceDisconnected(payload) { + e.evseDisconnected(payload.Ski, payload.Entity) return } - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeEVSE { + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) { return } - switch payload.EventType { - case api.EventTypeDeviceChange: - if payload.ChangeType == api.ElementChangeRemove { - e.evseDisconnected(payload.Ski, payload.Entity) - } - - case api.EventTypeEntityChange: - switch payload.ChangeType { - case api.ElementChangeAdd: - e.evseConnected(payload.Ski, payload.Entity) - case api.ElementChangeRemove: - e.evseDisconnected(payload.Ski, payload.Entity) - } + if util.IsEvseConnected(payload) { + e.evseConnected(payload.Ski, payload.Entity) + return + } else if util.IsEvseDisconnected(payload) { + e.evseDisconnected(payload.Ski, payload.Entity) + return + } - case api.EventTypeDataChange: - if payload.ChangeType != api.ElementChangeUpdate { + switch payload.EventType { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { return } @@ -48,7 +45,7 @@ func (e *UCEvseCC) HandleEvent(payload api.EventPayload) { } // an EVSE was connected -func (e *UCEvseCC) evseConnected(ski string, entity api.EntityRemoteInterface) { +func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterface) { localDevice := e.service.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -60,20 +57,20 @@ func (e *UCEvseCC) evseConnected(ski string, entity api.EntityRemoteInterface) { _, _ = evseDeviceDiagnosis.RequestState() } - e.reader.SpineEvent(ski, entity, UCEvseCCEventConnected) + e.reader.SpineEvent(ski, entity, api.UCEVSECCEventConnected) } // an EVSE was disconnected -func (e *UCEvseCC) evseDisconnected(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvseCCEventDisconnected) +func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, api.UCEVSECCEventDisconnected) } // the manufacturer Data of an EVSE was updated -func (e *UCEvseCC) evseManufacturerDataUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvseCCEventManufacturerUpdate) +func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, api.UCEVSECCEventManufacturerUpdate) } // the operating State of an EVSE was updated -func (e *UCEvseCC) evseStateUpdate(ski string, entity api.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, UCEvseCCEventOperationStateUpdate) +func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInterface) { + e.reader.SpineEvent(ski, entity, api.UCEVSECCEventOperationStateUpdate) } diff --git a/ucevsecc/public.go b/ucevsecc/public.go index e2deb5c..7656b55 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -9,7 +9,7 @@ import ( // the manufacturer data of an EVSE // returns deviceName, serialNumber, error -func (e *UCEvseCC) EVSEManufacturerData( +func (e *UCEVSECC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( string, @@ -46,7 +46,7 @@ func (e *UCEvseCC) EVSEManufacturerData( // the operating state data of an EVSE // returns operatingState, lastErrorCode, error -func (e *UCEvseCC) EVSEOperatingState( +func (e *UCEVSECC) OperatingState( entity spineapi.EntityRemoteInterface, ) ( model.DeviceDiagnosisOperatingStateType, string, error, diff --git a/ucevsecc/public_test.go b/ucevsecc/public_test.go index 7458008..fbf493e 100644 --- a/ucevsecc/public_test.go +++ b/ucevsecc/public_test.go @@ -1,90 +1,23 @@ package ucevsecc import ( - "fmt" - "testing" - "time" - - "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - eebusmocks "github.com/enbility/eebus-go/mocks" - "github.com/enbility/eebus-go/service" eebusutil "github.com/enbility/eebus-go/util" - "github.com/enbility/ship-go/cert" - shipmocks "github.com/enbility/ship-go/mocks" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" ) -func TestEvCCSuite(t *testing.T) { - suite.Run(t, new(EvseCCSuite)) -} - -type EvseCCSuite struct { - suite.Suite - - sut *UCEvseCC - - service eebusapi.ServiceInterface - - remoteDevice spineapi.DeviceRemoteInterface - mockRemoteEntity *mocks.EntityRemoteInterface - evseEntity spineapi.EntityRemoteInterface -} - -func (s *EvseCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { -} - -func (s *EvseCCSuite) BeforeTest(suiteName, testName string) { - cert, _ := cert.CreateCertificate("test", "test", "DE", "test") - configuration, _ := eebusapi.NewConfiguration( - "test", "test", "test", "test", - model.DeviceTypeTypeEnergyManagementSystem, - []model.EntityTypeType{model.EntityTypeTypeCEM}, - 9999, cert, 230.0, time.Second*4) - - serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) - serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() - - s.service = service.NewService(configuration, serviceHandler) - _ = s.service.Setup() - - mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) - s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) - mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) - mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() - mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() - s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() - s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() - entityAddress := &model.EntityAddressType{} - s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() - - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) - s.sut = NewUCEvseCC(s.service, s.service.LocalService(), s) - s.sut.AddFeatures() - s.sut.AddUseCase() - s.evseEntity = entities[0] -} - -func (s *EvseCCSuite) Test_EVSEManufacturerData() { - device, serial, err := s.sut.EVSEManufacturerData(nil) +func (s *EVSECCSuite) Test_EVSEManufacturerData() { + device, serial, err := s.sut.ManufacturerData(nil) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) - device, serial, err = s.sut.EVSEManufacturerData(s.mockRemoteEntity) + device, serial, err = s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) - device, serial, err = s.sut.EVSEManufacturerData(s.evseEntity) + device, serial, err = s.sut.ManufacturerData(s.evseEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) @@ -95,7 +28,7 @@ func (s *EvseCCSuite) Test_EVSEManufacturerData() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.EVSEManufacturerData(s.evseEntity) + device, serial, err = s.sut.ManufacturerData(s.evseEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), "", device) assert.Equal(s.T(), "", serial) @@ -108,24 +41,24 @@ func (s *EvseCCSuite) Test_EVSEManufacturerData() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.EVSEManufacturerData(s.evseEntity) + device, serial, err = s.sut.ManufacturerData(s.evseEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), "test", device) assert.Equal(s.T(), "12345", serial) } -func (s *EvseCCSuite) Test_EVSEOperatingState() { - data, errCode, err := s.sut.EVSEOperatingState(nil) +func (s *EVSECCSuite) Test_EVSEOperatingState() { + data, errCode, err := s.sut.OperatingState(nil) assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) assert.Equal(s.T(), "", errCode) assert.Nil(s.T(), nil, err) - data, errCode, err = s.sut.EVSEOperatingState(s.mockRemoteEntity) + data, errCode, err = s.sut.OperatingState(s.mockRemoteEntity) assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) assert.Equal(s.T(), "", errCode) assert.NotNil(s.T(), err) - data, errCode, err = s.sut.EVSEOperatingState(s.evseEntity) + data, errCode, err = s.sut.OperatingState(s.evseEntity) assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) assert.Equal(s.T(), "", errCode) assert.NotNil(s.T(), err) @@ -136,7 +69,7 @@ func (s *EvseCCSuite) Test_EVSEOperatingState() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, errCode, err = s.sut.EVSEOperatingState(s.evseEntity) + data, errCode, err = s.sut.OperatingState(s.evseEntity) assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) assert.Equal(s.T(), "", errCode) assert.Nil(s.T(), err) @@ -149,114 +82,8 @@ func (s *EvseCCSuite) Test_EVSEOperatingState() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, errCode, err = s.sut.EVSEOperatingState(s.evseEntity) + data, errCode, err = s.sut.OperatingState(s.evseEntity) assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeStandby, data) assert.Equal(s.T(), "error", errCode) assert.Nil(s.T(), err) } - -const remoteSki string = "testremoteski" - -func setupDevices( - eebusService eebusapi.ServiceInterface, t *testing.T) ( - spineapi.DeviceRemoteInterface, - []spineapi.EntityRemoteInterface) { - localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - localEntity.AddFeature(f) - - writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) - writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() - sender := spine.NewSender(writeHandler) - remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - - var clientRemoteFeatures = []struct { - featureType model.FeatureTypeType - supportedFcts []model.FunctionType - }{ - {model.FeatureTypeTypeDeviceClassification, - []model.FunctionType{ - model.FunctionTypeDeviceClassificationManufacturerData, - }, - }, - {model.FeatureTypeTypeDeviceDiagnosis, - []model.FunctionType{ - model.FunctionTypeDeviceDiagnosisStateData, - }, - }, - } - - remoteDeviceName := "remote" - - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { - supportedFcts := []model.FunctionPropertyType{} - for _, fct := range feature.supportedFcts { - supportedFct := model.FunctionPropertyType{ - Function: eebusutil.Ptr(fct), - PossibleOperations: &model.PossibleOperationsType{ - Read: &model.PossibleOperationsReadType{}, - }, - } - supportedFcts = append(supportedFcts, supportedFct) - } - - featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ - Description: &model.NetworkManagementFeatureDescriptionDataType{ - FeatureAddress: &model.FeatureAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - Feature: eebusutil.Ptr(model.AddressFeatureType(index)), - }, - FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(model.RoleTypeServer), - SupportedFunction: supportedFcts, - }, - } - featureInformations = append(featureInformations, featureInformation) - } - - detailedData := &model.NodeManagementDetailedDiscoveryDataType{ - DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ - Description: &model.NetworkManagementDeviceDescriptionDataType{ - DeviceAddress: &model.DeviceAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - }, - }, - }, - EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1}, - }, - EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), - }, - }, - { - Description: &model.NetworkManagementEntityDescriptionDataType{ - EntityAddress: &model.EntityAddressType{ - Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), - Entity: []model.AddressEntityType{1, 1}, - }, - EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), - }, - }, - }, - FeatureInformation: featureInformations, - } - - entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) - if err != nil { - fmt.Println(err) - } - - localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) - - return remoteDevice, entities -} diff --git a/ucevsecc/results.go b/ucevsecc/results.go index 4df4bd3..eca7cac 100644 --- a/ucevsecc/results.go +++ b/ucevsecc/results.go @@ -4,5 +4,5 @@ import ( "github.com/enbility/spine-go/api" ) -func (e *UCEvseCC) HandleResult(errorMsg api.ResultMessage) { +func (e *UCEVSECC) HandleResult(errorMsg api.ResultMessage) { } diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go new file mode 100644 index 0000000..a283345 --- /dev/null +++ b/ucevsecc/testhelper_test.go @@ -0,0 +1,179 @@ +package ucevsecc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEVSECCSuite(t *testing.T) { + suite.Run(t, new(EVSECCSuite)) +} + +type EVSECCSuite struct { + suite.Suite + + sut *UCEVSECC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evseEntity spineapi.EntityRemoteInterface +} + +func (s *EVSECCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EVSECCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEVSECC(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evseEntity = entities[0] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeDeviceClassification, + []model.FunctionType{ + model.FunctionTypeDeviceClassificationManufacturerData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 05d9e15..bf9f306 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -4,20 +4,21 @@ import ( "github.com/enbility/cemd/api" serviceapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) -type UCEvseCC struct { +type UCEVSECC struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface } -var _ UCEvseCCInterface = (*UCEvseCC)(nil) +var _ UCEVSECCInterface = (*UCEVSECC)(nil) -func NewUCEvseCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEvseCC { - uc := &UCEvseCC{ +func NewUCEVSECC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVSECC { + uc := &UCEVSECC{ service: service, reader: reader, } @@ -27,11 +28,11 @@ func NewUCEvseCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDe return uc } -func (c *UCEvseCC) UseCaseName() model.UseCaseNameType { +func (c *UCEVSECC) UseCaseName() model.UseCaseNameType { return model.UseCaseNameTypeEVSECommissioningAndConfiguration } -func (e *UCEvseCC) AddFeatures() { +func (e *UCEVSECC) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features @@ -46,7 +47,7 @@ func (e *UCEvseCC) AddFeatures() { } } -func (e *UCEvseCC) AddUseCase() { +func (e *UCEVSECC) AddUseCase() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( @@ -57,3 +58,37 @@ func (e *UCEvseCC) AddUseCase() { true, []model.UseCaseScenarioSupportType{1, 2}) } + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCEVSECC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEVSE, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{2}, + []model.FeatureTypeType{model.FeatureTypeTypeDeviceDiagnosis}, + ) { + // Workaround for the Porsche Mobile Charger Connect that falsely reports + // the usecase to be on the EV actor + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{2}, + []model.FeatureTypeType{model.FeatureTypeTypeDeviceDiagnosis}, + ) { + return false, nil + } + + } + + return true, nil +} diff --git a/ucevsoc/api.go b/ucevsoc/api.go new file mode 100644 index 0000000..e574311 --- /dev/null +++ b/ucevsoc/api.go @@ -0,0 +1,25 @@ +package ucevsoc + +import ( + "github.com/enbility/cemd/api" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCEVSOCInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // Scenario 2 + + // this is automatically covered by the SPINE implementation + + // Scenario 3 + + // this is covered by the central CEM interface implementation + // use that one to set the CEM's operation state which will inform all remote devices + + // Scenario 4 +} diff --git a/ucevsoc/events.go b/ucevsoc/events.go new file mode 100644 index 0000000..fe532f4 --- /dev/null +++ b/ucevsoc/events.go @@ -0,0 +1,110 @@ +package ucevsoc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCEVSOC) HandleEvent(payload spineapi.EventPayload) { + // only about events from an EV entity or device changes for this remote device + + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + return + } + + if util.IsEvConnected(payload) { + e.evConnected(payload.Entity) + return + } + + switch payload.EventType { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.MeasurementListDataType: + e.evMeasurementDataUpdate(payload.Ski, payload.Entity) + case *model.ElectricalConnectionCharacteristicListDataType: + e.evElectricalConnectionCharacteristicsDataUpdate(payload.Ski, payload.Entity) + } + } +} + +// an EV was connected +func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, descriptions + if evMeasurement, err := util.Measurement(e.service, entity); err == nil { + if _, err := evMeasurement.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get measurement descriptions + if _, err := evMeasurement.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + + // get measurement constraints + if _, err := evMeasurement.RequestConstraints(); err != nil { + logging.Log().Debug(err) + } + } + + if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := evElectricalConnection.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + if _, err := evElectricalConnection.RequestCharacteristics(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the measurement data of an EV was updated +func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 1 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVSOCStateOfChargeDataUpdate) + } + + // Scenario 3 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfHealth); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVSOCStateOfHealthDataUpdate) + } + + // Scenario 4 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeTravelRange); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVSOCActualRangeDataUpdate) + } +} + +// the elecrical connection characteristic data of an EV was updated +func (e *UCEVSOC) evElectricalConnectionCharacteristicsDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evElectricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return + } + + data, err := evElectricalConnection.GetCharacteristics() + if err != nil { + return + } + + for _, item := range data { + if item.CharacteristicType == nil || item.Value == nil { + continue + } + + if *item.CharacteristicType == model.ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax { + e.reader.SpineEvent(ski, entity, api.UCEVSOCNominalCapacityDataUpdate) + return + } + } + +} diff --git a/ucevsoc/public.go b/ucevsoc/public.go new file mode 100644 index 0000000..3b3ef5f --- /dev/null +++ b/ucevsoc/public.go @@ -0,0 +1,41 @@ +package ucevsoc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the last known SoC of the connected EV +// +// only works with a current ISO15118-2 with VAS or ISO15118-20 +// communication between EVSE and EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCEVSOC) EVSoC(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return 0, api.ErrNoEvEntity + } + + evMeasurement, err := util.Measurement(e.service, entity) + if err != nil || evMeasurement == nil { + return 0, err + } + + data, err := evMeasurement.GetValuesForTypeCommodityScope(model.MeasurementTypeTypePercentage, model.CommodityTypeTypeElectricity, model.ScopeTypeTypeStateOfCharge) + if err != nil { + return 0, err + } + + // we assume there is only one value, nil is already checked + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} diff --git a/ucevsoc/public_test.go b/ucevsoc/public_test.go new file mode 100644 index 0000000..91ef75b --- /dev/null +++ b/ucevsoc/public_test.go @@ -0,0 +1,91 @@ +package ucevsoc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *EVSOCSuite) Test_EVSOC() { + data, err := s.sut.EVSoC(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.EVSoC(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeEVStateOfCharge), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVSoC(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measDesc := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePercentage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfCharge), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVSoC(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVSoC(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData = &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(80), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.EVSoC(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 80.0, data) +} diff --git a/ucevsoc/results.go b/ucevsoc/results.go new file mode 100644 index 0000000..480747c --- /dev/null +++ b/ucevsoc/results.go @@ -0,0 +1,8 @@ +package ucevsoc + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCEVSOC) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go new file mode 100644 index 0000000..3f47542 --- /dev/null +++ b/ucevsoc/testhelper_test.go @@ -0,0 +1,183 @@ +package ucevsoc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestEVSOCSuite(t *testing.T) { + suite.Run(t, new(EVSOCSuite)) +} + +type EVSOCSuite struct { + suite.Suite + + sut *UCEVSOC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *EVSOCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *EVSOCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCEVSOC(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeMeasurement, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go new file mode 100644 index 0000000..41a6fc2 --- /dev/null +++ b/ucevsoc/ucevsoc.go @@ -0,0 +1,98 @@ +package ucevsoc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCEVSOC struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCEVSOCInterface = (*UCEVSOC)(nil) + +func NewUCEVSOC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVSOC { + uc := &UCEVSOC{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCEVSOC) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeEVStateOfCharge +} + +func (e *UCEVSOC) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + f.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, false, false) + f.AddFunctionType(model.FunctionTypeMeasurementConstraintsListData, false, false) + f.AddFunctionType(model.FunctionTypeMeasurementListData, false, false) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, false, false) + + f.AddResultHandler(e) +} + +func (e *UCEVSOC) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "RC2", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCEVSOC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{model.FeatureTypeTypeMeasurement}, + ) { + return false, nil + } + + // check for required features + evMeasurement, err := util.Measurement(e.service, entity) + if err != nil || evMeasurement == nil { + return false, features.ErrFunctionNotSupported + } + + // check if measurement description contains an element with scope SOC + if _, err = evMeasurement.GetDescriptionsForScope(model.ScopeTypeTypeStateOfCharge); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucevsoc/ucevsoc_test.go b/ucevsoc/ucevsoc_test.go new file mode 100644 index 0000000..045910f --- /dev/null +++ b/ucevsoc/ucevsoc_test.go @@ -0,0 +1,58 @@ +package ucevsoc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *EVSOCSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeEVStateOfCharge), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfCharge), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/ucopev/api.go b/ucopev/api.go new file mode 100644 index 0000000..dbb12a6 --- /dev/null +++ b/ucopev/api.go @@ -0,0 +1,52 @@ +package ucopev + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCOPEVInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current loadcontrol obligation limits + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // send new LoadControlLimits to the remote EV + // + // parameters: + // - limits: a set of limits containing phase specific limit data + // + // Sets a maximum A limit for each phase that the EV may not exceed. + // Mainly used for implementing overload protection of the site or limiting the + // maximum charge power of EVs when the EV and EVSE communicate via IEC61851 + // and with ISO15118 if the EV does not support the Optimization of Self Consumption + // usecase. + // + // note: + // For obligations to work for optimizing solar excess power, the EV needs to + // have an energy demand. Recommendations work even if the EV does not have an active + // energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. + // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific + // and needs to have specific EVSE support for the specific EV brand. + // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. + EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) + + // Scenario 2 + + // this is automatically covered by the SPINE implementation + + // Scenario 3 + + // this is covered by the central CEM interface implementation + // use that one to set the CEM's operation state which will inform all remote devices +} diff --git a/ucopev/events.go b/ucopev/events.go new file mode 100644 index 0000000..e760af5 --- /dev/null +++ b/ucopev/events.go @@ -0,0 +1,95 @@ +package ucopev + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCOPEV) HandleEvent(payload spineapi.EventPayload) { + // only about events from an EV entity or device changes for this remote device + + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + return + } + + if util.IsEvConnected(payload) { + e.evConnected(payload.Entity) + return + } + + switch payload.EventType { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.LoadControlLimitDescriptionListDataType: + e.evLoadControlLimitDescriptionDataUpdate(payload.Entity) + case *model.LoadControlLimitListDataType: + e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) + } + } +} + +// an EV was connected +func (e *UCOPEV) evConnected(entity spineapi.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, descriptions + if evLoadControl, err := util.LoadControl(e.service, entity); err == nil { + if _, err := evLoadControl.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get measurement descriptions + if _, err := evLoadControl.RequestLimitDescriptions(); err != nil { + logging.Log().Debug(err) + } + + // get measurement constraints + if _, err := evLoadControl.RequestLimitConstraints(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit description data of an EV was updated +func (e *UCOPEV) evLoadControlLimitDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if evLoadControl, err := util.LoadControl(e.service, entity); err == nil { + // get measurement values + if _, err := evLoadControl.RequestLimitValues(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit data of an EV was updated +func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evLoadControl, err := util.LoadControl(e.service, entity) + if err != nil { + return + } + + data, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) + if err != nil { + return + } + + for _, item := range data { + if item.LimitId == nil { + continue + } + + _, err := evLoadControl.GetLimitValueForLimitId(*item.LimitId) + if err != nil { + continue + } + + e.reader.SpineEvent(ski, entity, api.UCOPEVLoadControlLimitDataUpdate) + return + } + +} diff --git a/ucopev/public.go b/ucopev/public.go new file mode 100644 index 0000000..38a8416 --- /dev/null +++ b/ucopev/public.go @@ -0,0 +1,39 @@ +package ucopev + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the current loadcontrol obligation limits +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCOPEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { + return util.EVLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation) +} + +// send new LoadControlLimits to the remote EV +// +// parameters: +// - limits: a set of limits containing phase specific limit data +// +// Sets a maximum A limit for each phase that the EV may not exceed. +// Mainly used for implementing overload protection of the site or limiting the +// maximum charge power of EVs when the EV and EVSE communicate via IEC61851 +// and with ISO15118 if the EV does not support the Optimization of Self Consumption +// usecase. +// +// note: +// For obligations to work for optimizing solar excess power, the EV needs to +// have an energy demand. Recommendations work even if the EV does not have an active +// energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. +// In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific +// and needs to have specific EVSE support for the specific EV brand. +// In ISO15118-20 this is a standard feature which does not need special support on the EVSE. +func (e *UCOPEV) EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { + return util.EVWriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation, limits) +} diff --git a/ucopev/results.go b/ucopev/results.go new file mode 100644 index 0000000..ee128d5 --- /dev/null +++ b/ucopev/results.go @@ -0,0 +1,8 @@ +package ucopev + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCOPEV) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go new file mode 100644 index 0000000..945965b --- /dev/null +++ b/ucopev/ucopev.go @@ -0,0 +1,93 @@ +package ucopev + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCOPEV struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCOPEVInterface = (*UCOPEV)(nil) + +func NewUCOPEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCOPEV { + uc := &UCOPEV{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCOPEV) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment +} + +func (e *UCOPEV) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddResultHandler(e) +} + +func (e *UCOPEV) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2, 3}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCOPEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3}, + []model.FeatureTypeType{model.FeatureTypeTypeLoadControl}, + ) { + return false, nil + } + + // check for required features + evLoadControl, err := util.LoadControl(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if loadcontrol limit descriptions contains a recommendation category + if _, err = evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucoscev/api.go b/ucoscev/api.go new file mode 100644 index 0000000..32f7e6b --- /dev/null +++ b/ucoscev/api.go @@ -0,0 +1,45 @@ +package ucoscev + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCOSCEVInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current loadcontrol obligation limits + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // send new LoadControlLimits to the remote EV + // + // parameters: + // - limits: a set of limits containing phase specific limit data + // + // recommendations: + // Sets a recommended charge power in A for each phase. This is mainly + // used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. + // The EV either needs to support the Optimization of Self Consumption usecase or + // the EVSE needs to be able map the recommendations into oligation limits which then + // works for all EVs communication either via IEC61851 or ISO15118. + EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) + + // Scenario 2 + + // this is automatically covered by the SPINE implementation + + // Scenario 3 + + // this is covered by the central CEM interface implementation + // use that one to set the CEM's operation state which will inform all remote devices +} diff --git a/ucoscev/events.go b/ucoscev/events.go new file mode 100644 index 0000000..46c0ad6 --- /dev/null +++ b/ucoscev/events.go @@ -0,0 +1,58 @@ +package ucoscev + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCOSCEV) HandleEvent(payload api.EventPayload) { + // most of the events are identical to OPEV, and OPEV is required to be used, + // we don't handle the same events in here + + if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + return + } + + switch payload.EventType { + case spineapi.EventTypeDataChange: + if payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.LoadControlLimitListDataType: + e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) + } + } +} + +// the load control limit data of an EV was updated +func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evLoadControl, err := util.LoadControl(e.service, entity) + if err != nil { + return + } + + data, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) + if err != nil { + return + } + + for _, item := range data { + if item.LimitId == nil { + continue + } + + _, err := evLoadControl.GetLimitValueForLimitId(*item.LimitId) + if err != nil { + continue + } + + e.reader.SpineEvent(ski, entity, api.UCOPEVLoadControlLimitDataUpdate) + return + } + +} diff --git a/ucoscev/public.go b/ucoscev/public.go new file mode 100644 index 0000000..828e88b --- /dev/null +++ b/ucoscev/public.go @@ -0,0 +1,32 @@ +package ucoscev + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the current loadcontrol recommendation limits +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCOSCEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { + return util.EVLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation) +} + +// send new LoadControlLimits to the remote EV +// +// parameters: +// - limits: a set of limits containing phase specific limit data +// +// recommendations: +// Sets a recommended charge power in A for each phase. This is mainly +// used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. +// The EV either needs to support the Optimization of Self Consumption usecase or +// the EVSE needs to be able map the recommendations into oligation limits which then +// works for all EVs communication either via IEC61851 or ISO15118. +func (e *UCOSCEV) EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { + return util.EVWriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation, limits) +} diff --git a/ucoscev/results.go b/ucoscev/results.go new file mode 100644 index 0000000..e1462bc --- /dev/null +++ b/ucoscev/results.go @@ -0,0 +1,8 @@ +package ucoscev + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCOSCEV) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go new file mode 100644 index 0000000..49b2944 --- /dev/null +++ b/ucoscev/testhelper_test.go @@ -0,0 +1,193 @@ +package ucoscev + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestOSCEVSuite(t *testing.T) { + suite.Run(t, new(OSCEVSuite)) +} + +type OSCEVSuite struct { + suite.Suite + + sut *UCOSCEV + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *OSCEVSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *OSCEVSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCOSCEV(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeLoadControlLimitDescriptionListData, + model.FunctionTypeLoadControlLimitConstraintsListData, + model.FunctionTypeLoadControlLimitListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeClient, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go new file mode 100644 index 0000000..261b1bc --- /dev/null +++ b/ucoscev/ucoscev.go @@ -0,0 +1,92 @@ +package ucoscev + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCOSCEV struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCOSCEVInterface = (*UCOSCEV)(nil) + +func NewUCOSCEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCOSCEV { + uc := &UCOSCEV{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCOSCEV) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging +} + +func (e *UCOSCEV) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddResultHandler(e) +} + +func (e *UCOSCEV) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "", + true, + []model.UseCaseScenarioSupportType{1, 2, 3}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCOSCEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false, api.ErrNoEvEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEV, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3}, + []model.FeatureTypeType{model.FeatureTypeTypeLoadControl}, + ) { + return false, nil + } + + // check if loadcontrol limit descriptions contains a recommendation category + evLoadControl, err := util.LoadControl(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + if _, err = evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucoscev/ucoscev_test.go b/ucoscev/ucoscev_test.go new file mode 100644 index 0000000..c0f31db --- /dev/null +++ b/ucoscev/ucoscev_test.go @@ -0,0 +1,58 @@ +package ucoscev + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *OSCEVSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeRecommendation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/util/features.go b/util/features.go index 3612185..8d38f68 100644 --- a/util/features.go +++ b/util/features.go @@ -33,6 +33,12 @@ func DeviceDiagnosis(service eebusapi.ServiceInterface, remoteEntity api.EntityR return features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) } +func DeviceDiagnosisServer(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceDiagnosis, error) { + localEntity := localCemEntity(service) + + return features.NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, localEntity, remoteEntity) +} + func ElectricalConnection(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.ElectricalConnection, error) { localEntity := localCemEntity(service) @@ -50,3 +56,21 @@ func Measurement(service eebusapi.ServiceInterface, remoteEntity api.EntityRemot return features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) } + +func LoadControl(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.LoadControl, error) { + localEntity := localCemEntity(service) + + return features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func TimeSeries(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.TimeSeries, error) { + localEntity := localCemEntity(service) + + return features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} + +func IncentiveTable(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.IncentiveTable, error) { + localEntity := localCemEntity(service) + + return features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) +} diff --git a/util/helper.go b/util/helper.go index 7989862..644dd3c 100644 --- a/util/helper.go +++ b/util/helper.go @@ -50,3 +50,53 @@ func EntityOfTypeForSki( return nil, features.ErrEntityNotFound } + +func IsPayloadForEntityType(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { + if payload.Entity == nil { + return false + } + + theEntityType := payload.Entity.EntityType() + return theEntityType == entityType +} + +func IsDeviceDisconnected(payload spineapi.EventPayload) bool { + return (payload.EventType == spineapi.EventTypeDeviceChange && + payload.ChangeType == spineapi.ElementChangeRemove) +} + +func IsEvseConnected(payload spineapi.EventPayload) bool { + if payload.EventType != spineapi.EventTypeEntityChange && + payload.ChangeType != spineapi.ElementChangeAdd { + return false + } + + return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) +} + +func IsEvseDisconnected(payload spineapi.EventPayload) bool { + if payload.EventType != spineapi.EventTypeEntityChange && + payload.ChangeType != spineapi.ElementChangeRemove { + return false + } + + return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) +} + +func IsEvConnected(payload spineapi.EventPayload) bool { + if payload.EventType != spineapi.EventTypeEntityChange && + payload.ChangeType != spineapi.ElementChangeAdd { + return false + } + + return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) +} + +func IsEvDisconnected(payload spineapi.EventPayload) bool { + if payload.EventType != spineapi.EventTypeEntityChange && + payload.ChangeType != spineapi.ElementChangeAdd { + return false + } + + return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) +} diff --git a/util/loadcontrol.go b/util/loadcontrol.go new file mode 100644 index 0000000..c92f679 --- /dev/null +++ b/util/loadcontrol.go @@ -0,0 +1,180 @@ +package util + +import ( + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the current loadcontrol limits for a categoriy +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func EVLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType) ([]float64, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return nil, api.ErrNoEvEntity + } + + evLoadControl, err := LoadControl(service, entity) + evElectricalConnection, err2 := ElectricalConnection(service, entity) + if err != nil || err2 != nil { + return nil, api.ErrNoEvEntity + } + + // find out the appropriate limitId for each phase value + // limitDescription contains the measurementId for each limitId + limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) + if err != nil { + return nil, features.ErrDataNotAvailable + } + + var result []float64 + + for i := 0; i < 3; i++ { + phaseName := PhaseNameMapping[i] + + // electricalParameterDescription contains the measured phase for each measurementId + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) + if err != nil || elParamDesc.MeasurementId == nil { + // there is no data for this phase, the phase may not exit + result = append(result, 0) + continue + } + + var limitDesc *model.LoadControlLimitDescriptionDataType + for _, desc := range limitDescriptions { + if desc.MeasurementId != nil && + elParamDesc.MeasurementId != nil && + *desc.MeasurementId == *elParamDesc.MeasurementId { + safeDesc := desc + limitDesc = &safeDesc + break + } + } + + if limitDesc == nil || limitDesc.LimitId == nil { + return nil, features.ErrDataNotAvailable + } + + limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) + if err != nil { + return nil, features.ErrDataNotAvailable + } + + var limitValue float64 + if limitIdData.Value == nil || (limitIdData.IsLimitActive != nil && !*limitIdData.IsLimitActive) { + // report maximum possible if no limit is available or the limit is not active + _, dataMax, _, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) + if err != nil { + return nil, features.ErrDataNotAvailable + } + + limitValue = dataMax + } else { + limitValue = limitIdData.Value.GetValue() + } + + result = append(result, limitValue) + } + + return result, nil +} + +// generic helper to be used in UCOPEV & UCOSCEV +// send new LoadControlLimits to the remote EV +// +// parameters: +// - limits: a set of limits for a given limit category containing phase specific limit data +// +// category obligations: +// Sets a maximum A limit for each phase that the EV may not exceed. +// Mainly used for implementing overload protection of the site or limiting the +// maximum charge power of EVs when the EV and EVSE communicate via IEC61851 +// and with ISO15118 if the EV does not support the Optimization of Self Consumption +// usecase. +// +// category recommendations: +// Sets a recommended charge power in A for each phase. This is mainly +// used if the EV and EVSE communicate via ISO15118 to support charging excess solar power. +// The EV either needs to support the Optimization of Self Consumption usecase or +// the EVSE needs to be able map the recommendations into oligation limits which then +// works for all EVs communication either via IEC61851 or ISO15118. +// +// notes: +// - For obligations to work for optimizing solar excess power, the EV needs to have an energy demand. +// - Recommendations work even if the EV does not have an active energy demand, given it communicated with the EVSE via ISO15118 and supports the usecase. +// - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. +// - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. +// - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. +func EVWriteLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return nil, api.ErrNoEvEntity + } + + evLoadControl, err := LoadControl(service, entity) + evElectricalConnection, err2 := ElectricalConnection(service, entity) + if err != nil || err2 != nil { + return nil, api.ErrNoEvseEntity + } + + var limitData []model.LoadControlLimitDataType + + for _, phaseLimit := range limits { + // find out the appropriate limitId for each phase value + // limitDescription contains the measurementId for each limitId + limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) + if err != nil { + continue + } + + // electricalParameterDescription contains the measured phase for each measurementId + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) + if err != nil || elParamDesc.MeasurementId == nil { + continue + } + + var limitDesc *model.LoadControlLimitDescriptionDataType + for _, desc := range limitDescriptions { + if desc.MeasurementId != nil && + elParamDesc.MeasurementId != nil && + *desc.MeasurementId == *elParamDesc.MeasurementId { + safeDesc := desc + limitDesc = &safeDesc + break + } + } + + if limitDesc == nil || limitDesc.LimitId == nil { + continue + } + + limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) + if err != nil { + continue + } + + // EEBus_UC_TS_OverloadProtectionByEvChargingCurrentCurtailment V1.01b 3.2.1.2.2.2 + // If omitted or set to "true", the timePeriod, value and isLimitActive element SHALL be writeable by a client. + if limitIdData.IsLimitChangeable != nil && !*limitIdData.IsLimitChangeable { + continue + } + + // electricalPermittedValueSet contains the allowed min, max and the default values per phase + limit := evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) + + newLimit := model.LoadControlLimitDataType{ + LimitId: limitDesc.LimitId, + IsLimitActive: eebusutil.Ptr(phaseLimit.IsActive), + Value: model.NewScaledNumberType(limit), + } + limitData = append(limitData, newLimit) + } + + msgCounter, err := evLoadControl.WriteLimitValues(limitData) + + return msgCounter, err +} diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go new file mode 100644 index 0000000..14c8390 --- /dev/null +++ b/util/loadcontrol_test.go @@ -0,0 +1,227 @@ +package util + +import ( + "testing" + + "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { + loadLimits := []api.LoadLimitsPhase{} + + msgCounter, err := EVWriteLoadControlLimits(s.service, s.mockRemoteEntity, model.LoadControlCategoryTypeObligation, loadLimits) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), msgCounter) + + msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), msgCounter) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), msgCounter) + + type dataStruct struct { + phases int + permittedDefaultExists bool + permittedDefaultValue float64 + permittedMinValue float64 + permittedMaxValue float64 + limits, limitsExpected []float64 + } + + tests := []struct { + name string + data []dataStruct + }{ + { + "1 Phase ISO15118", + []dataStruct{ + {1, true, 0.1, 2, 16, []float64{0}, []float64{0.1}}, + {1, true, 0.1, 2, 16, []float64{2.2}, []float64{2.2}}, + {1, true, 0.1, 2, 16, []float64{10}, []float64{10}}, + {1, true, 0.1, 2, 16, []float64{16}, []float64{16}}, + }, + }, + { + "3 Phase ISO15118", + []dataStruct{ + {3, true, 0.1, 2, 16, []float64{0, 0, 0}, []float64{0.1, 0.1, 0.1}}, + {3, true, 0.1, 2, 16, []float64{2.2, 2.2, 2.2}, []float64{2.2, 2.2, 2.2}}, + {3, true, 0.1, 2, 16, []float64{10, 10, 10}, []float64{10, 10, 10}}, + {3, true, 0.1, 2, 16, []float64{16, 16, 16}, []float64{16, 16, 16}}, + }, + }, + { + "1 Phase IEC61851", + []dataStruct{ + {1, true, 0, 6, 16, []float64{0}, []float64{0}}, + {1, true, 0, 6, 16, []float64{6}, []float64{6}}, + {1, true, 0, 6, 16, []float64{10}, []float64{10}}, + {1, true, 0, 6, 16, []float64{16}, []float64{16}}, + }, + }, + { + "3 Phase IEC61851", + []dataStruct{ + {3, true, 0, 6, 16, []float64{0, 0, 0}, []float64{0, 0, 0}}, + {3, true, 0, 6, 16, []float64{6, 6, 6}, []float64{6, 6, 6}}, + {3, true, 0, 6, 16, []float64{10, 10, 10}, []float64{10, 10, 10}}, + {3, true, 0, 6, 16, []float64{16, 16, 16}, []float64{16, 16, 16}}, + }, + }, + { + "3 Phase IEC61851 Elli", + []dataStruct{ + {3, false, 0, 6, 16, []float64{0, 0, 0}, []float64{0, 0, 0}}, + {3, false, 0, 6, 16, []float64{6, 6, 6}, []float64{6, 6, 6}}, + {3, false, 0, 6, 16, []float64{10, 10, 10}, []float64{10, 10, 10}}, + {3, false, 0, 6, 16, []float64{16, 16, 16}, []float64{16, 16, 16}}, + }, + }, + } + + for _, tc := range tests { + s.T().Run(tc.name, func(t *testing.T) { + dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} + permittedData := []model.ScaledNumberSetType{} + for _, data := range tc.data { + for phase := 0; phase < data.phases; phase++ { + item := model.ScaledNumberSetType{ + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(data.permittedMinValue), + Max: model.NewScaledNumberType(data.permittedMaxValue), + }, + }, + } + if data.permittedDefaultExists { + item.Value = []model.ScaledNumberType{*model.NewScaledNumberType(data.permittedDefaultValue)} + } + permittedData = append(permittedData, item) + + permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(phase)), + PermittedValueSet: permittedData, + } + dataSet = append(dataSet, permittedItem) + } + + permData := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: dataSet, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) + assert.Nil(s.T(), fErr) + + msgCounter, err := EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + assert.NotNil(t, err) + assert.Nil(t, msgCounter) + + limitDesc := []model.LoadControlLimitDescriptionDataType{} + for index := range data.limits { + id := model.LoadControlLimitIdType(index) + limitItem := model.LoadControlLimitDescriptionDataType{ + LimitId: eebusutil.Ptr(id), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(index)), + } + limitDesc = append(limitDesc, limitItem) + } + add := len(limitDesc) + for index := range data.limits { + id := model.LoadControlLimitIdType(index + add) + limitItem := model.LoadControlLimitDescriptionDataType{ + LimitId: eebusutil.Ptr(id), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeRecommendation), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(index)), + } + limitDesc = append(limitDesc, limitItem) + } + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: limitDesc, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + assert.NotNil(t, err) + assert.Nil(t, msgCounter) + + limitData := []model.LoadControlLimitDataType{} + for index := range limitDesc { + limitItem := model.LoadControlLimitDataType{ + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(index)), + IsLimitChangeable: eebusutil.Ptr(true), + IsLimitActive: eebusutil.Ptr(false), + Value: model.NewScaledNumberType(data.permittedMaxValue), + } + limitData = append(limitData, limitItem) + } + + limitListData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: limitData, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitListData, nil, nil) + assert.Nil(s.T(), fErr) + + msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + assert.NotNil(t, err) + assert.Nil(t, msgCounter) + + phaseLimitValues := []api.LoadLimitsPhase{} + for index, limit := range data.limits { + phase := PhaseNameMapping[index] + phaseLimitValues = append(phaseLimitValues, api.LoadLimitsPhase{ + Phase: phase, + IsActive: true, + Value: limit, + }) + + } + msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, phaseLimitValues) + assert.Nil(t, err) + assert.NotNil(t, msgCounter) + + msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeRecommendation, phaseLimitValues) + assert.Nil(t, err) + assert.NotNil(t, msgCounter) + } + }) + } +} diff --git a/util/measurement.go b/util/measurement.go new file mode 100644 index 0000000..5881dff --- /dev/null +++ b/util/measurement.go @@ -0,0 +1,31 @@ +package util + +import ( + eebusapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the measurement value for a scope or an error +func MeasurementValueForScope(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, scope model.ScopeTypeType) (float64, error) { + evMeasurement, err := Measurement(service, entity) + if err != nil { + return 0, features.ErrFunctionNotSupported + } + + if data, err := evMeasurement.GetDescriptionsForScope(scope); err == nil { + for _, item := range data { + if item.MeasurementId == nil { + continue + } + + if _, err := evMeasurement.GetValueForMeasurementId(*item.MeasurementId); err != nil { + continue + } + + } + } + + return 0, features.ErrDataNotAvailable +} diff --git a/util/testhelper_test.go b/util/testhelper_test.go new file mode 100644 index 0000000..efc31db --- /dev/null +++ b/util/testhelper_test.go @@ -0,0 +1,188 @@ +package util + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestLoadControlSuite(t *testing.T) { + suite.Run(t, new(LoadControlSuite)) +} + +type LoadControlSuite struct { + suite.Suite + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *LoadControlSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeLoadControlLimitDescriptionListData, + model.FunctionTypeLoadControlLimitConstraintsListData, + model.FunctionTypeLoadControlLimitListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeClient, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} From da4fc01200fe1711fc96a2edb22c5d6534585525 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 18:41:18 +0100 Subject: [PATCH 070/227] Fix build --- ucevcc/public.go | 26 -------------------------- ucoscev/events.go | 4 ++-- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/ucevcc/public.go b/ucevcc/public.go index 8b6b926..411eecb 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -225,32 +225,6 @@ func (e *UCEVCC) ManufacturerData( return deviceName, serialNumber, nil } -// return the number of ac connected phases of the EV or 0 if it is unknown -func (e *UCEVCC) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return 0, api.ErrNoEvEntity - } - - evElectricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil { - return 0, features.ErrDataNotAvailable - } - - data, err := evElectricalConnection.GetDescriptions() - if err != nil { - return 0, features.ErrDataNotAvailable - } - - for _, item := range data { - if item.ElectricalConnectionId != nil && item.AcConnectedPhases != nil { - return *item.AcConnectedPhases, nil - } - } - - // default to 0 if the value is not available - return 0, nil -} - // return the min, max, default limits for each phase of the connected EV // // possible errors: diff --git a/ucoscev/events.go b/ucoscev/events.go index 46c0ad6..170f1ce 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -8,7 +8,7 @@ import ( ) // handle SPINE events -func (e *UCOSCEV) HandleEvent(payload api.EventPayload) { +func (e *UCOSCEV) HandleEvent(payload spineapi.EventPayload) { // most of the events are identical to OPEV, and OPEV is required to be used, // we don't handle the same events in here @@ -36,7 +36,7 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entit return } - data, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) + data, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation) if err != nil { return } From 1cead6cf92c65a7064b4a65a695ed0a096069920 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 18:57:57 +0100 Subject: [PATCH 071/227] Fix tests --- uccevc/public_scen3_test.go | 2 +- uccevc/uccevc_test.go | 35 +++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/uccevc/public_scen3_test.go b/uccevc/public_scen3_test.go index 7884f3d..1a57228 100644 --- a/uccevc/public_scen3_test.go +++ b/uccevc/public_scen3_test.go @@ -85,7 +85,7 @@ func (s *UCCEVCSuite) Test_EVWriteIncentives() { assert.Nil(s.T(), fErr) err = s.sut.WriteIncentives(s.evEntity, data) - assert.NotNil(s.T(), err) + assert.Nil(s.T(), err) type dataStruct struct { error bool diff --git a/uccevc/uccevc_test.go b/uccevc/uccevc_test.go index 4b4a2e7..5f2799e 100644 --- a/uccevc/uccevc_test.go +++ b/uccevc/uccevc_test.go @@ -21,9 +21,9 @@ func (s *UCCEVCSuite) Test_IsUseCaseSupported() { Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), UseCaseSupport: []model.UseCaseSupportType{ { - UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeOptimizationOfSelfConsumptionDuringEVCharging), + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeCoordinatedEVCharging), UseCaseAvailable: eebusutil.Ptr(true), - ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3}, + ScenarioSupport: []model.UseCaseScenarioSupportType{2, 3, 4, 5, 6, 7, 8}, }, }, }, @@ -39,17 +39,36 @@ func (s *UCCEVCSuite) Test_IsUseCaseSupported() { assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) - descData := &model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + timeDescData := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), - LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeRecommendation), + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeConstraints), }, }, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) - fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.IncentiveTableDescriptionDataType{ + IncentiveTableDescription: []model.IncentiveTableDescriptionType{ + { + TariffDescription: &model.TariffDescriptionDataType{ + TariffId: eebusutil.Ptr(model.TariffIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeSimpleIncentiveTable), + }, + }, + }, + } + + rFeature = s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeIncentiveTableDescriptionData, descData, nil, nil) assert.Nil(s.T(), fErr) data, err = s.sut.IsUseCaseSupported(s.evEntity) From c647b781f2451290436648921138193c681774b0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 21:16:00 +0100 Subject: [PATCH 072/227] Update spine and eebus --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1e40345..0744737 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240222173124-444cee6d0e50 + github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 + github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 1afc580..e7525da 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240222173124-444cee6d0e50 h1:yD7LEdrQpiny+h0FsCnjJ5+CO0bMvDLw6vtu6mnXaB8= -github.com/enbility/eebus-go v0.0.0-20240222173124-444cee6d0e50/go.mod h1:w0fBLO6XWA5/5+VD0fs/RHFDH+izTyRYPVLDeq5sc8o= +github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 h1:7+EgHCkVBaEVDvvTlwj57hLqv9IlsdJG5/8exNwIeDI= +github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00/go.mod h1:4Rqu0NjVY73per2BlwfG1KZPIkrpRtBi5cWwB57gCMQ= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718 h1:qX9JNqBrxH8COazT+jnLpr7vEZt/8CVshy0r+k9uQcM= -github.com/enbility/spine-go v0.0.0-20240221113047-17b5853bd718/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 h1:q87C8Fb76kRbbMqG3vCM52yxjYxbclY23+rbBo2nq+Y= +github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From e193c0d1c7842603e49dbf6f28cf8e56b59ca807 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 21:17:57 +0100 Subject: [PATCH 073/227] Fix issues and improve test coverage of UCCEVC --- uccevc/events.go | 35 ++++++------ uccevc/public_scen1_test.go | 49 +---------------- uccevc/public_scen2_test.go | 64 ++++++++++++++++++++-- uccevc/public_scen3.go | 19 ++++++- uccevc/public_scen3_test.go | 60 +++++++++++++++++++- uccevc/public_scen4_test.go | 106 +++++++++++++++++++++++++++++++++++- uccevc/testhelper_test.go | 12 ++++ util/helper.go | 32 +++++------ 8 files changed, 286 insertions(+), 91 deletions(-) diff --git a/uccevc/events.go b/uccevc/events.go index 2f272f5..fb72f22 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -21,25 +21,26 @@ func (e *UCCEVC) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } + if payload.EventType != spineapi.EventTypeDataChange { + return + } + + if payload.ChangeType != spineapi.ElementChangeUpdate { + return + } - switch payload.Data.(type) { - case *model.TimeSeriesDescriptionListDataType: - e.evTimeSeriesDescriptionDataUpdate(payload.Ski, payload.Entity) + switch payload.Data.(type) { + case *model.TimeSeriesDescriptionListDataType: + e.evTimeSeriesDescriptionDataUpdate(payload.Ski, payload.Entity) - case *model.TimeSeriesListDataType: - e.evTimeSeriesDataUpdate(payload.Ski, payload.Entity) + case *model.TimeSeriesListDataType: + e.evTimeSeriesDataUpdate(payload.Ski, payload.Entity) - case *model.IncentiveTableDescriptionDataType: - e.evIncentiveTableDescriptionDataUpdate(payload.Ski, payload.Entity) + case *model.IncentiveTableDescriptionDataType: + e.evIncentiveTableDescriptionDataUpdate(payload.Ski, payload.Entity) - case *model.IncentiveDataType: - e.evIncentiveTableDataUpdate(payload.Ski, payload.Entity) - } + case *model.IncentiveDataType: + e.evIncentiveTableDataUpdate(payload.Ski, payload.Entity) } } @@ -194,13 +195,13 @@ func (e *UCCEVC) evCheckIncentiveTableDescriptionUpdateRequired(entity spineapi. } data, err := evIncentiveTable.GetDescriptionsForScope(model.ScopeTypeTypeSimpleIncentiveTable) - if err != nil { + if err != nil || len(data) == 0 { return false } // only use the first description and therein the first tariff item := data[0].TariffDescription - if item.UpdateRequired != nil { + if item != nil && item.UpdateRequired != nil { return *item.UpdateRequired } diff --git a/uccevc/public_scen1_test.go b/uccevc/public_scen1_test.go index c606299..9fea86f 100644 --- a/uccevc/public_scen1_test.go +++ b/uccevc/public_scen1_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCCEVCSuite) Test_EVChargeStrategy() { +func (s *UCCEVCSuite) Test_ChargeStrategy() { data := s.sut.ChargeStrategy(s.mockRemoteEntity) assert.Equal(s.T(), api.EVChargeStrategyTypeUnknown, data) @@ -158,7 +158,7 @@ func (s *UCCEVCSuite) Test_EVChargeStrategy() { assert.Equal(s.T(), api.EVChargeStrategyTypeTimedCharging, data) } -func (s *UCCEVCSuite) Test_EVEnergySingleDemand() { +func (s *UCCEVCSuite) Test_EnergySingleDemand() { demand, err := s.sut.EnergyDemand(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, demand.MinDemand) @@ -175,49 +175,6 @@ func (s *UCCEVCSuite) Test_EVEnergySingleDemand() { assert.Equal(s.T(), 0.0, demand.DurationUntilStart) assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) - descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), - KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), - }, - }, - } - - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) - fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) - assert.Nil(s.T(), fErr) - - demand, err = s.sut.EnergyDemand(s.evEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, demand.MinDemand) - assert.Equal(s.T(), 0.0, demand.OptDemand) - assert.Equal(s.T(), 0.0, demand.MaxDemand) - assert.Equal(s.T(), 0.0, demand.DurationUntilStart) - assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) - - keyData := &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), - Value: &model.DeviceConfigurationKeyValueValueType{ - String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), - }, - }, - }, - } - - fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) - assert.Nil(s.T(), fErr) - - demand, err = s.sut.EnergyDemand(s.evEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, demand.MinDemand) - assert.Equal(s.T(), 0.0, demand.OptDemand) - assert.Equal(s.T(), 0.0, demand.MaxDemand) - assert.Equal(s.T(), 0.0, demand.DurationUntilStart) - assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) - timeDescData := &model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ { @@ -228,7 +185,7 @@ func (s *UCCEVCSuite) Test_EVEnergySingleDemand() { } rTimeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) - fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDescData, nil, nil) + fErr := rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDescData, nil, nil) assert.Nil(s.T(), fErr) timeData := &model.TimeSeriesListDataType{ diff --git a/uccevc/public_scen2_test.go b/uccevc/public_scen2_test.go index 3df03d3..ee47088 100644 --- a/uccevc/public_scen2_test.go +++ b/uccevc/public_scen2_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCCEVCSuite) Test_EVGetTimeSlotConstraints() { +func (s *UCCEVCSuite) Test_TimeSlotConstraints() { constraints, err := s.sut.TimeSlotConstraints(s.mockRemoteEntity) assert.Equal(s.T(), uint(0), constraints.MinSlots) assert.Equal(s.T(), uint(0), constraints.MaxSlots) @@ -54,7 +54,7 @@ func (s *UCCEVCSuite) Test_EVGetTimeSlotConstraints() { assert.Equal(s.T(), err, nil) } -func (s *UCCEVCSuite) Test_EVWritePowerLimits() { +func (s *UCCEVCSuite) Test_WritePowerLimits() { data := []api.DurationSlotValue{} err := s.sut.WritePowerLimits(s.mockRemoteEntity, data) @@ -63,6 +63,62 @@ func (s *UCCEVCSuite) Test_EVWritePowerLimits() { err = s.sut.WritePowerLimits(s.evEntity, data) assert.NotNil(s.T(), err) + elParamDesc := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamDesc, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WritePowerLimits(s.evEntity, data) + assert.NotNil(s.T(), err) + + elPermDesc := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, elPermDesc, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WritePowerLimits(s.evEntity, data) + assert.NotNil(s.T(), err) + + elPermDesc = &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + PermittedValueSet: []model.ScaledNumberSetType{ + { + Range: []model.ScaledNumberRangeType{ + { + Max: model.NewScaledNumberType(16), + }, + }, + }, + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, elPermDesc, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WritePowerLimits(s.evEntity, data) + assert.NotNil(s.T(), err) + descData := &model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ { @@ -72,8 +128,8 @@ func (s *UCCEVCSuite) Test_EVWritePowerLimits() { }, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) - fErr := rFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, descData, nil, nil) + rFeature = s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) err = s.sut.WritePowerLimits(s.evEntity, data) diff --git a/uccevc/public_scen3.go b/uccevc/public_scen3.go index 2428da4..c4b99a2 100644 --- a/uccevc/public_scen3.go +++ b/uccevc/public_scen3.go @@ -48,6 +48,10 @@ func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (ap // // SPINE UC CoordinatedEVCharging 2.4.3 func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInterface, data []api.IncentiveTariffDescription) error { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return api.ErrEVDisconnected + } + evIncentiveTable, err := util.IncentiveTable(e.service, entity) if err != nil { logging.Log().Error("incentivetable feature not found") @@ -106,8 +110,9 @@ func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInt }, } - if data != nil { - descData = []model.IncentiveTableDescriptionType{} + if len(data) > 0 && len(data[0].Tiers) > 0 { + newDescData := []model.IncentiveTableDescriptionType{} + allDataPresent := false for index, tariff := range data { tariffDesc := descriptions[0].TariffDescription @@ -152,12 +157,20 @@ func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInt } newTier.IncentiveDescription = incentiveDescription + if len(newTier.BoundaryDescription) > 0 && + len(newTier.IncentiveDescription) > 0 { + allDataPresent = true + } tierData = append(tierData, newTier) } newTariff.Tier = tierData - descData = append(descData, newTariff) + newDescData = append(newDescData, newTariff) + } + + if allDataPresent { + descData = newDescData } } diff --git a/uccevc/public_scen3_test.go b/uccevc/public_scen3_test.go index 1a57228..2d12abe 100644 --- a/uccevc/public_scen3_test.go +++ b/uccevc/public_scen3_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCCEVCSuite) Test_EVGetIncentiveConstraints() { +func (s *UCCEVCSuite) Test_IncentiveConstraints() { constraints, err := s.sut.IncentiveConstraints(s.mockRemoteEntity) assert.Equal(s.T(), uint(0), constraints.MinSlots) assert.Equal(s.T(), uint(0), constraints.MaxSlots) @@ -60,7 +60,63 @@ func (s *UCCEVCSuite) Test_EVGetIncentiveConstraints() { assert.Equal(s.T(), err, nil) } -func (s *UCCEVCSuite) Test_EVWriteIncentives() { +func (s *UCCEVCSuite) Test_WriteIncentiveTableDescriptions() { + data := []api.IncentiveTariffDescription{} + + err := s.sut.WriteIncentiveTableDescriptions(s.mockRemoteEntity, data) + assert.NotNil(s.T(), err) + + err = s.sut.WriteIncentiveTableDescriptions(s.evEntity, data) + assert.NotNil(s.T(), err) + + descData := &model.IncentiveTableDescriptionDataType{ + IncentiveTableDescription: []model.IncentiveTableDescriptionType{ + { + TariffDescription: &model.TariffDescriptionDataType{ + TariffId: util.Ptr(model.TariffIdType(0)), + ScopeType: util.Ptr(model.ScopeTypeTypeSimpleIncentiveTable), + }, + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeIncentiveTableDescriptionData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + err = s.sut.WriteIncentiveTableDescriptions(s.evEntity, data) + assert.Nil(s.T(), err) + + data = []api.IncentiveTariffDescription{ + { + Tiers: []api.IncentiveTableDescriptionTier{ + { + Id: 0, + Type: model.TierTypeTypeDynamicCost, + Boundaries: []api.TierBoundaryDescription{ + { + Id: 0, + Type: model.TierBoundaryTypeTypePowerBoundary, + Unit: model.UnitOfMeasurementTypeW, + }, + }, + Incentives: []api.IncentiveDescription{ + { + Id: 0, + Type: model.IncentiveTypeTypeAbsoluteCost, + Currency: model.CurrencyTypeEur, + }, + }, + }, + }, + }, + } + + err = s.sut.WriteIncentiveTableDescriptions(s.evEntity, data) + assert.Nil(s.T(), err) +} + +func (s *UCCEVCSuite) Test_WriteIncentives() { data := []api.DurationSlotValue{} err := s.sut.WriteIncentives(s.mockRemoteEntity, data) diff --git a/uccevc/public_scen4_test.go b/uccevc/public_scen4_test.go index 489c712..1e67f9f 100644 --- a/uccevc/public_scen4_test.go +++ b/uccevc/public_scen4_test.go @@ -7,7 +7,81 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCCEVCSuite) Test_EVChargePlan() { +func (s *UCCEVCSuite) Test_ChargePlanConstaints() { + _, err := s.sut.ChargePlanConstraints(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.ChargePlanConstraints(s.evEntity) + assert.NotNil(s.T(), err) + + descData := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), + TimeSeriesType: util.Ptr(model.TimeSeriesTypeTypeConstraints), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.ChargePlanConstraints(s.evEntity) + assert.NotNil(s.T(), err) + + data := &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeTimeSeriesListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.ChargePlanConstraints(s.evEntity) + assert.NotNil(s.T(), err) + + data = &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: util.Ptr(model.TimeSeriesIdType(1)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT5M36S")), + MaxValue: model.NewScaledNumberType(4201), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT30S"), + EndTime: model.NewAbsoluteOrRelativeTimeType("PT1M"), + }, + MaxValue: model.NewScaledNumberType(4201), + }, + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeTimeSeriesListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.ChargePlanConstraints(s.evEntity) + assert.Nil(s.T(), err) + +} + +func (s *UCCEVCSuite) Test_ChargePlan() { _, err := s.sut.ChargePlan(s.mockRemoteEntity) assert.NotNil(s.T(), err) @@ -47,6 +121,27 @@ func (s *UCCEVCSuite) Test_EVChargePlan() { timeData := &model.TimeSeriesListDataType{ TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(1)), + TimePeriod: &model.TimePeriodType{}, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + Duration: eebusutil.Ptr(model.DurationType("PT5M36S")), + MaxValue: model.NewScaledNumberType(4201), + }, + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT30S"), + EndTime: model.NewAbsoluteOrRelativeTimeType("PT1M"), + }, + Value: model.NewScaledNumberType(5), + MinValue: model.NewScaledNumberType(0), + MaxValue: model.NewScaledNumberType(10), + }, + }, + }, { TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(2)), TimePeriod: &model.TimePeriodType{ @@ -60,8 +155,13 @@ func (s *UCCEVCSuite) Test_EVChargePlan() { }, { TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(1)), - Duration: eebusutil.Ptr(model.DurationType("P1D")), - MaxValue: model.NewScaledNumberType(0), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT30S"), + EndTime: model.NewAbsoluteOrRelativeTimeType("PT1M"), + }, + Value: model.NewScaledNumberType(5), + MinValue: model.NewScaledNumberType(0), + MaxValue: model.NewScaledNumberType(10), }, }, }, diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index 11d76b8..8bfa6db 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -62,6 +62,9 @@ func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + ops := map[model.FunctionType]spineapi.OperationsInterface{} + mockRemoteFeature.EXPECT().Operations().Return(ops).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface @@ -87,6 +90,8 @@ func setupDevices( localEntity.AddFeature(f) f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeClient) localEntity.AddFeature(f) + f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() @@ -121,6 +126,13 @@ func setupDevices( model.FunctionTypeIncentiveTableData, }, }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, } remoteDeviceName := "remote" diff --git a/util/helper.go b/util/helper.go index 644dd3c..e95e335 100644 --- a/util/helper.go +++ b/util/helper.go @@ -66,37 +66,37 @@ func IsDeviceDisconnected(payload spineapi.EventPayload) bool { } func IsEvseConnected(payload spineapi.EventPayload) bool { - if payload.EventType != spineapi.EventTypeEntityChange && - payload.ChangeType != spineapi.ElementChangeAdd { - return false + if payload.EventType == spineapi.EventTypeEntityChange && + payload.ChangeType == spineapi.ElementChangeAdd { + return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) } - return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) + return false } func IsEvseDisconnected(payload spineapi.EventPayload) bool { - if payload.EventType != spineapi.EventTypeEntityChange && - payload.ChangeType != spineapi.ElementChangeRemove { - return false + if payload.EventType == spineapi.EventTypeEntityChange && + payload.ChangeType == spineapi.ElementChangeRemove { + return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) } - return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) + return false } func IsEvConnected(payload spineapi.EventPayload) bool { - if payload.EventType != spineapi.EventTypeEntityChange && - payload.ChangeType != spineapi.ElementChangeAdd { - return false + if payload.EventType == spineapi.EventTypeEntityChange && + payload.ChangeType == spineapi.ElementChangeAdd { + return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) } - return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + return false } func IsEvDisconnected(payload spineapi.EventPayload) bool { - if payload.EventType != spineapi.EventTypeEntityChange && - payload.ChangeType != spineapi.ElementChangeAdd { - return false + if payload.EventType == spineapi.EventTypeEntityChange && + payload.ChangeType == spineapi.ElementChangeAdd { + return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) } - return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + return false } From 49e515914f1f17f3d9d900aea992d1efecdffcdb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 22 Feb 2024 21:23:48 +0100 Subject: [PATCH 074/227] Add missing tests --- uccevc/events_test.go | 136 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 uccevc/events_test.go diff --git a/uccevc/events_test.go b/uccevc/events_test.go new file mode 100644 index 0000000..64dddf2 --- /dev/null +++ b/uccevc/events_test.go @@ -0,0 +1,136 @@ +package uccevc + +import ( + "time" + + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCCEVCSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.TimeSeriesDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.TimeSeriesListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.IncentiveTableDescriptionDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.IncentiveDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { + s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + + timeDesc := &model.TimeSeriesDescriptionListDataType{ + TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeConstraints), + UpdateRequired: eebusutil.Ptr(true), + }, + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(1)), + TimeSeriesType: eebusutil.Ptr(model.TimeSeriesTypeTypeSingleDemand), + }, + }, + } + + rTimeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeServer) + fErr := rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDesc, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + + timeData := &model.TimeSeriesListDataType{ + TimeSeriesData: []model.TimeSeriesDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(1)), + TimePeriod: &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeType("PT0S"), + }, + TimeSeriesSlot: []model.TimeSeriesSlotType{ + { + TimeSeriesSlotId: eebusutil.Ptr(model.TimeSeriesSlotIdType(0)), + MinValue: model.NewScaledNumberType(1000), + Value: model.NewScaledNumberType(10000), + MaxValue: model.NewScaledNumberType(100000), + }, + }, + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesListData, timeData, nil, nil) + assert.Nil(s.T(), fErr) + + demand, err := s.sut.EnergyDemand(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1000.0, demand.MinDemand) + assert.Equal(s.T(), 10000.0, demand.OptDemand) + assert.Equal(s.T(), 100000.0, demand.MaxDemand) + assert.Equal(s.T(), 0.0, demand.DurationUntilStart) + assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) + + s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + + constData := &model.TimeSeriesConstraintsListDataType{ + TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ + { + TimeSeriesId: eebusutil.Ptr(model.TimeSeriesIdType(0)), + SlotCountMin: eebusutil.Ptr(model.TimeSeriesSlotCountType(1)), + SlotCountMax: eebusutil.Ptr(model.TimeSeriesSlotCountType(10)), + SlotDurationMin: model.NewDurationType(1 * time.Minute), + SlotDurationMax: model.NewDurationType(60 * time.Minute), + SlotDurationStepSize: model.NewDurationType(1 * time.Minute), + }, + }, + } + + fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesConstraintsListData, constData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + + incConstData := &model.IncentiveTableConstraintsDataType{ + IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ + { + IncentiveSlotConstraints: &model.TimeTableConstraintsDataType{ + SlotCountMin: eebusutil.Ptr(model.TimeSlotCountType(1)), + SlotCountMax: eebusutil.Ptr(model.TimeSlotCountType(10)), + }, + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeIncentiveTableConstraintsData, incConstData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + +} From 074f64d8e2fa31a6d116389c012661d769532326 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 10:15:28 +0100 Subject: [PATCH 075/227] add minor comment --- uccevc/uccevc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index 40f99e9..cf15720 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -41,6 +41,7 @@ func (e *UCCEVC) AddFeatures() { f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) f.AddResultHandler(e) + // server features f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddResultHandler(e) } From 19b5f3fec6375c4befaeba70653fd810f8cfd9eb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 11:50:15 +0100 Subject: [PATCH 076/227] UCEVCC updates and test coverage - move indetification type into api - add tests - fix util evDisconnect check - other minor fixes and improvements --- api/types.go | 9 ++ ucevcc/api.go | 12 +-- ucevcc/events.go | 10 ++- ucevcc/events_test.go | 179 ++++++++++++++++++++++++++++++++++++++ ucevcc/public.go | 6 +- ucevcc/public_test.go | 24 ++--- ucevcc/results.go | 8 +- ucevcc/results_test.go | 58 ++++++++++++ ucevcc/testhelper_test.go | 33 ++++--- ucevcc/ucevcc_test.go | 63 ++++++++++++++ util/helper.go | 2 +- 11 files changed, 362 insertions(+), 42 deletions(-) create mode 100644 ucevcc/events_test.go create mode 100644 ucevcc/results_test.go create mode 100644 ucevcc/ucevcc_test.go diff --git a/api/types.go b/api/types.go index 2da8378..d8e01ac 100644 --- a/api/types.go +++ b/api/types.go @@ -26,6 +26,15 @@ type LoadLimitsPhase struct { Value float64 } +// identification +type IdentificationItem struct { + // the identification value + Value string + + // the type of the identification value, e.g. + ValueType model.IdentificationTypeType +} + type EVChargeStrategyType string const ( diff --git a/ucevcc/api.go b/ucevcc/api.go index 59b77bc..0d1ddcc 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -3,7 +3,6 @@ package ucevcc import ( "github.com/enbility/cemd/api" spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" ) //go:generate mockery @@ -34,7 +33,7 @@ type UCEVCCInterface interface { // return the identifications of the currently connected EV or nil if not available // these can be multiple, e.g. PCID, Mac Address, RFID - Identifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) + Identifications(entity spineapi.EntityRemoteInterface) ([]api.IdentificationItem, error) // Scenario 5 @@ -52,12 +51,3 @@ type UCEVCCInterface interface { // is the EV in sleep mode EVInSleepMode(entity spineapi.EntityRemoteInterface) (bool, error) } - -// EV identification -type IdentificationItem struct { - // the identification value - Value string - - // the type of the identification value, e.g. - ValueType model.IdentificationTypeType -} diff --git a/ucevcc/events.go b/ucevcc/events.go index ccf0112..5567f2d 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -172,8 +172,16 @@ func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRe // the manufacturer data of an EV was updated func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + evDeviceClassification, err := util.DeviceClassification(e.service, entity) + if err != nil { + return + } + // Scenario 5 - e.reader.SpineEvent(ski, entity, api.UCEVCCManufacturerDataUpdate) + if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVCCManufacturerDataUpdate) + } + } // the electrical connection parameter description data of an EV was updated diff --git a/ucevcc/events_test.go b/ucevcc/events_test.go new file mode 100644 index 0000000..1462536 --- /dev/null +++ b/ucevcc/events_test.go @@ -0,0 +1,179 @@ +package ucevcc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVCCSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDeviceChange + payload.ChangeType = spineapi.ElementChangeRemove + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeRemove + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceClassificationManufacturerDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.ElectricalConnectionParameterDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.ElectricalConnectionPermittedValueSetListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.IdentificationListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { + s.sut.evConfigurationDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evConfigurationDataUpdate(remoteSki, s.evEntity) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evConfigurationDataUpdate(remoteSki, s.evEntity) + + data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{ + String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), + }), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{ + Boolean: eebusutil.Ptr(false), + }), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evConfigurationDataUpdate(remoteSki, s.evEntity) +} + +func (s *UCEVCCSuite) Test_evIdentificationDataUpdate() { + s.sut.evIdentificationDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evIdentificationDataUpdate(remoteSki, s.evEntity) + + data := &model.IdentificationListDataType{ + IdentificationData: []model.IdentificationDataType{ + { + IdentificationId: eebusutil.Ptr(model.IdentificationIdType(0)), + IdentificationType: eebusutil.Ptr(model.IdentificationTypeTypeEui48), + }, + { + IdentificationId: eebusutil.Ptr(model.IdentificationIdType(1)), + IdentificationType: eebusutil.Ptr(model.IdentificationTypeTypeEui48), + IdentificationValue: eebusutil.Ptr(model.IdentificationValueType("test")), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeIdentification, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeIdentificationListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evIdentificationDataUpdate(remoteSki, s.evEntity) +} + +func (s *UCEVCCSuite) Test_evManufacturerDataUpdate() { + s.sut.evManufacturerDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evManufacturerDataUpdate(remoteSki, s.evEntity) + + data := &model.DeviceClassificationManufacturerDataType{ + BrandName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evManufacturerDataUpdate(remoteSki, s.evEntity) +} + +func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { + s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.evEntity) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.evEntity) + + permData := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.evEntity) +} diff --git a/ucevcc/public.go b/ucevcc/public.go index 411eecb..2cc9487 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -153,7 +153,7 @@ func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterfa // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]IdentificationItem, error) { +func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.IdentificationItem, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return nil, api.ErrNoEvEntity } @@ -168,9 +168,9 @@ func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]Ident return nil, err } - var ids []IdentificationItem + var ids []api.IdentificationItem for _, identification := range identifications { - item := IdentificationItem{} + item := api.IdentificationItem{} typ := identification.IdentificationType if typ != nil { diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index 6ca99f3..da19465 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *EVCCSuite) Test_EVCurrentChargeState() { +func (s *UCEVCCSuite) Test_EVCurrentChargeState() { data, err := s.sut.CurrentChargeState(s.mockRemoteEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeUnplugged, data) @@ -76,7 +76,7 @@ func (s *EVCCSuite) Test_EVCurrentChargeState() { assert.Equal(s.T(), api.EVChargeStateTypeUnknown, data) } -func (s *EVCCSuite) Test_EVConnected() { +func (s *UCEVCCSuite) Test_EVConnected() { data := s.sut.EVConnected(nil) assert.Equal(s.T(), false, data) @@ -98,7 +98,7 @@ func (s *EVCCSuite) Test_EVConnected() { assert.Equal(s.T(), true, data) } -func (s *EVCCSuite) Test_EVCommunicationStandard() { +func (s *UCEVCCSuite) Test_EVCommunicationStandard() { data, err := s.sut.CommunicationStandard(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) @@ -159,7 +159,7 @@ func (s *EVCCSuite) Test_EVCommunicationStandard() { assert.Equal(s.T(), string(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), data) } -func (s *EVCCSuite) Test_EVAsymmetricChargingSupported() { +func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupported() { data, err := s.sut.AsymmetricChargingSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -220,18 +220,18 @@ func (s *EVCCSuite) Test_EVAsymmetricChargingSupported() { assert.Equal(s.T(), true, data) } -func (s *EVCCSuite) Test_EVIdentification() { +func (s *UCEVCCSuite) Test_EVIdentification() { data, err := s.sut.Identifications(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), []IdentificationItem(nil), data) + assert.Equal(s.T(), []api.IdentificationItem(nil), data) data, err = s.sut.Identifications(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), []IdentificationItem(nil), data) + assert.Equal(s.T(), []api.IdentificationItem(nil), data) data, err = s.sut.Identifications(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), []IdentificationItem(nil), data) + assert.Equal(s.T(), []api.IdentificationItem(nil), data) idData := &model.IdentificationListDataType{ IdentificationData: []model.IdentificationDataType{ @@ -249,11 +249,11 @@ func (s *EVCCSuite) Test_EVIdentification() { data, err = s.sut.Identifications(s.evEntity) assert.Nil(s.T(), err) - resultData := []IdentificationItem{{Value: "test", ValueType: model.IdentificationTypeTypeEui64}} + resultData := []api.IdentificationItem{{Value: "test", ValueType: model.IdentificationTypeTypeEui64}} assert.Equal(s.T(), resultData, data) } -func (s *EVCCSuite) Test_EVManufacturerData() { +func (s *UCEVCCSuite) Test_EVManufacturerData() { device, serial, err := s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) @@ -294,7 +294,7 @@ func (s *EVCCSuite) Test_EVManufacturerData() { assert.Equal(s.T(), "12345", serial) } -func (s *EVCCSuite) Test_EVCurrentLimits() { +func (s *UCEVCCSuite) Test_EVCurrentLimits() { minData, maxData, defaultData, err := s.sut.CurrentLimits(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), minData) @@ -450,7 +450,7 @@ func (s *EVCCSuite) Test_EVCurrentLimits() { } } -func (s *EVCCSuite) Test_EVInSleepMode() { +func (s *UCEVCCSuite) Test_EVInSleepMode() { data, err := s.sut.EVInSleepMode(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) diff --git a/ucevcc/results.go b/ucevcc/results.go index 1619e4c..6748b0a 100644 --- a/ucevcc/results.go +++ b/ucevcc/results.go @@ -16,8 +16,7 @@ func (e *UCEVCC) HandleResult(errorMsg api.ResultMessage) { } // handle errors coming from the remote EVSE entity - switch errorMsg.FeatureLocal.Type() { - case model.FeatureTypeTypeDeviceDiagnosis: + if errorMsg.FeatureLocal.Type() == model.FeatureTypeTypeDeviceDiagnosis { e.handleResultDeviceDiagnosis(errorMsg) } } @@ -25,7 +24,10 @@ func (e *UCEVCC) HandleResult(errorMsg api.ResultMessage) { // Handle DeviceDiagnosis Results func (e *UCEVCC) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { // is this an error for a heartbeat message? - if *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { + if resultMsg.DeviceRemote == nil || + resultMsg.Result == nil || + resultMsg.Result.ErrorNumber == nil || + *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { return } diff --git a/ucevcc/results_test.go b/ucevcc/results_test.go new file mode 100644 index 0000000..7f2abd6 --- /dev/null +++ b/ucevcc/results_test.go @@ -0,0 +1,58 @@ +package ucevcc + +import ( + "errors" + + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +func (s *UCEVCCSuite) Test_Results() { + localDevice := s.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + localFeature := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + + errorMsg := spineapi.ResultMessage{ + DeviceRemote: s.remoteDevice, + EntityRemote: s.mockRemoteEntity, + FeatureLocal: localFeature, + Result: &model.ResultDataType{ + ErrorNumber: eebusutil.Ptr(model.ErrorNumberTypeNoError), + }, + } + s.sut.HandleResult(errorMsg) + + errorMsg.EntityRemote = s.evEntity + s.sut.HandleResult(errorMsg) + + errorMsg.Result = &model.ResultDataType{ + ErrorNumber: eebusutil.Ptr(model.ErrorNumberTypeGeneralError), + Description: eebusutil.Ptr(model.DescriptionType("test error")), + } + errorMsg.MsgCounterReference = model.MsgCounterType(500) + + s.mockSender. + EXPECT(). + DatagramForMsgCounter(errorMsg.MsgCounterReference). + Return(model.DatagramType{}, errors.New("test")).Once() + + s.sut.HandleResult(errorMsg) + + datagram := model.DatagramType{ + Payload: model.PayloadType{ + Cmd: []model.CmdType{ + { + DeviceDiagnosisHeartbeatData: &model.DeviceDiagnosisHeartbeatDataType{}, + }, + }, + }, + } + s.mockSender. + EXPECT(). + DatagramForMsgCounter(errorMsg.MsgCounterReference). + Return(datagram, nil).Once() + + s.sut.HandleResult(errorMsg) + +} diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index 67083b2..bd15f76 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -11,7 +11,6 @@ import ( "github.com/enbility/eebus-go/service" eebusutil "github.com/enbility/eebus-go/util" "github.com/enbility/ship-go/cert" - shipmocks "github.com/enbility/ship-go/mocks" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" @@ -21,10 +20,10 @@ import ( ) func TestEVCCSuite(t *testing.T) { - suite.Run(t, new(EVCCSuite)) + suite.Run(t, new(UCEVCCSuite)) } -type EVCCSuite struct { +type UCEVCCSuite struct { suite.Suite sut *UCEVCC @@ -32,14 +31,15 @@ type EVCCSuite struct { service eebusapi.ServiceInterface remoteDevice spineapi.DeviceRemoteInterface + mockSender *mocks.SenderInterface mockRemoteEntity *mocks.EntityRemoteInterface evEntity spineapi.EntityRemoteInterface } -func (s *EVCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } -func (s *EVCCSuite) BeforeTest(suiteName, testName string) { +func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := eebusapi.NewConfiguration( "test", "test", "test", "test", @@ -62,10 +62,11 @@ func (s *EVCCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface - s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.remoteDevice, s.mockSender, entities = setupDevices(s.service, s.T()) s.sut = NewUCEVCC(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() @@ -77,6 +78,7 @@ const remoteSki string = "testremoteski" func setupDevices( eebusService eebusapi.ServiceInterface, t *testing.T) ( spineapi.DeviceRemoteInterface, + *mocks.SenderInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) @@ -92,10 +94,17 @@ func setupDevices( f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) localEntity.AddFeature(f) - writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) - writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() - sender := spine.NewSender(writeHandler) - remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + // writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + // writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + // sender := spine.NewSender(writeHandler) + mockSender := mocks.NewSenderInterface(t) + defaultMsgCounter := model.MsgCounterType(100) + mockSender. + EXPECT(). + Request(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(&defaultMsgCounter, nil). + Maybe() + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, mockSender) var clientRemoteFeatures = []struct { featureType model.FeatureTypeType @@ -120,6 +129,8 @@ func setupDevices( {model.FeatureTypeTypeElectricalConnection, []model.FunctionType{ model.FunctionTypeElectricalConnectionDescriptionListData, + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, }, }, {model.FeatureTypeTypeDeviceDiagnosis, @@ -197,5 +208,5 @@ func setupDevices( localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) - return remoteDevice, entities + return remoteDevice, mockSender, entities } diff --git a/ucevcc/ucevcc_test.go b/ucevcc/ucevcc_test.go new file mode 100644 index 0000000..727bf1e --- /dev/null +++ b/ucevcc/ucevcc_test.go @@ -0,0 +1,63 @@ +package ucevcc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVCCSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeEVCommissioningAndConfiguration), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 3, 4, 5, 6, 7, 8}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData = &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeEVCommissioningAndConfiguration), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}, + }, + }, + }, + }, + } + + fErr = nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/util/helper.go b/util/helper.go index e95e335..8f45584 100644 --- a/util/helper.go +++ b/util/helper.go @@ -94,7 +94,7 @@ func IsEvConnected(payload spineapi.EventPayload) bool { func IsEvDisconnected(payload spineapi.EventPayload) bool { if payload.EventType == spineapi.EventTypeEntityChange && - payload.ChangeType == spineapi.ElementChangeAdd { + payload.ChangeType == spineapi.ElementChangeRemove { return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) } From e379a8e0f4632c6dba4d53cfb5ec1698899d8fc2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 12:18:52 +0100 Subject: [PATCH 077/227] Update UCEVCEM tess and fix measurement issue --- ucevcem/events.go | 25 ++++----- ucevcem/events_test.go | 109 +++++++++++++++++++++++++++++++++++++ ucevcem/public_test.go | 10 ++-- ucevcem/testhelper_test.go | 9 +-- ucevcem/ucevcem.go | 2 +- ucevcem/ucevcem_test.go | 41 ++++++++++++++ util/measurement.go | 4 +- 7 files changed, 174 insertions(+), 26 deletions(-) create mode 100644 ucevcem/events_test.go create mode 100644 ucevcem/ucevcem_test.go diff --git a/ucevcem/events.go b/ucevcem/events.go index 7bc12d7..bbd14b6 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -21,20 +21,17 @@ func (e *UCEVCEM) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } - - switch payload.Data.(type) { - case *model.ElectricalConnectionDescriptionListDataType: - e.evElectricalConnectionDescriptionDataUpdate(payload.Ski, payload.Entity) - case *model.MeasurementDescriptionListDataType: - e.evMeasurementDescriptionDataUpdate(payload.Entity) - case *model.MeasurementListDataType: - e.evMeasurementDataUpdate(payload.Ski, payload.Entity) - } + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + switch payload.Data.(type) { + case *model.ElectricalConnectionDescriptionListDataType: + e.evElectricalConnectionDescriptionDataUpdate(payload.Ski, payload.Entity) + case *model.MeasurementDescriptionListDataType: + e.evMeasurementDescriptionDataUpdate(payload.Entity) + case *model.MeasurementListDataType: + e.evMeasurementDataUpdate(payload.Ski, payload.Entity) } } diff --git a/ucevcem/events_test.go b/ucevcem/events_test.go new file mode 100644 index 0000000..9896201 --- /dev/null +++ b/ucevcem/events_test.go @@ -0,0 +1,109 @@ +package ucevcem + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVCEMSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.ElectricalConnectionDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCEVCEMSuite) Test_evElectricalConnectionDescriptionDataUpdate() { + s.sut.evElectricalConnectionDescriptionDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evElectricalConnectionDescriptionDataUpdate(remoteSki, s.evEntity) + + descData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + AcConnectedPhases: eebusutil.Ptr(uint(1)), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalConnectionDescriptionDataUpdate(remoteSki, s.evEntity) +} + +func (s *UCEVCEMSuite) Test_evMeasurementDataUpdate() { + s.sut.evMeasurementDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeCharge), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(200), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(3000), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) +} diff --git a/ucevcem/public_test.go b/ucevcem/public_test.go index 427d4f9..8cc92d3 100644 --- a/ucevcem/public_test.go +++ b/ucevcem/public_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *EVCEMSuite) Test_EVConnectedPhases() { +func (s *UCEVCEMSuite) Test_EVConnectedPhases() { data, err := s.sut.ConnectedPhases(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), uint(0), data) @@ -52,7 +52,7 @@ func (s *EVCEMSuite) Test_EVConnectedPhases() { assert.Equal(s.T(), uint(1), data) } -func (s *EVCEMSuite) Test_EVCurrentsPerPhase() { +func (s *UCEVCEMSuite) Test_EVCurrentsPerPhase() { data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -121,7 +121,7 @@ func (s *EVCEMSuite) Test_EVCurrentsPerPhase() { assert.Equal(s.T(), 10.0, data[0]) } -func (s *EVCEMSuite) Test_EVPowerPerPhase_Power() { +func (s *UCEVCEMSuite) Test_EVPowerPerPhase_Power() { data, err := s.sut.PowerPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -190,7 +190,7 @@ func (s *EVCEMSuite) Test_EVPowerPerPhase_Power() { assert.Equal(s.T(), 80.0, data[0]) } -func (s *EVCEMSuite) Test_EVPowerPerPhase_Current() { +func (s *UCEVCEMSuite) Test_EVPowerPerPhase_Current() { data, err := s.sut.PowerPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -255,7 +255,7 @@ func (s *EVCEMSuite) Test_EVPowerPerPhase_Current() { assert.Equal(s.T(), 2300.0, data[0]) } -func (s *EVCEMSuite) Test_EVChargedEnergy() { +func (s *UCEVCEMSuite) Test_EVChargedEnergy() { data, err := s.sut.ChargedEnergy(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index c9e1953..f17a6b5 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -21,10 +21,10 @@ import ( ) func TestEVCEMSuite(t *testing.T) { - suite.Run(t, new(EVCEMSuite)) + suite.Run(t, new(UCEVCEMSuite)) } -type EVCEMSuite struct { +type UCEVCEMSuite struct { suite.Suite sut *UCEVCEM @@ -36,10 +36,10 @@ type EVCEMSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *EVCEMSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVCEMSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } -func (s *EVCEMSuite) BeforeTest(suiteName, testName string) { +func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := eebusapi.NewConfiguration( "test", "test", "test", "test", @@ -59,6 +59,7 @@ func (s *EVCEMSuite) BeforeTest(suiteName, testName string) { mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index d430068..8454ee9 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -68,7 +68,7 @@ func (e *UCEVCEM) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (boo model.UseCaseActorTypeEV, e.UseCaseName(), nil, - []model.FeatureTypeType{model.FeatureTypeTypeDeviceDiagnosis}, + nil, ) { return false, nil } diff --git a/ucevcem/ucevcem_test.go b/ucevcem/ucevcem_test.go new file mode 100644 index 0000000..5be10a9 --- /dev/null +++ b/ucevcem/ucevcem_test.go @@ -0,0 +1,41 @@ +package ucevcem + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVCEMSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeMeasurementOfElectricityDuringEVCharging), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/util/measurement.go b/util/measurement.go index 5881dff..9127565 100644 --- a/util/measurement.go +++ b/util/measurement.go @@ -20,8 +20,8 @@ func MeasurementValueForScope(service eebusapi.ServiceInterface, entity spineapi continue } - if _, err := evMeasurement.GetValueForMeasurementId(*item.MeasurementId); err != nil { - continue + if value, err := evMeasurement.GetValueForMeasurementId(*item.MeasurementId); err == nil { + return value, nil } } From 8456a9859a01c3217d296003cc0a68d6ec6a9998 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 12:29:17 +0100 Subject: [PATCH 078/227] Update UCEVSECC and tests --- ucevsecc/events.go | 40 +++++++++++++------- ucevsecc/events_test.go | 74 +++++++++++++++++++++++++++++++++++++ ucevsecc/public_test.go | 4 +- ucevsecc/testhelper_test.go | 9 +++-- ucevsecc/ucevsecc_test.go | 41 ++++++++++++++++++++ 5 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 ucevsecc/events_test.go create mode 100644 ucevsecc/ucevsecc_test.go diff --git a/ucevsecc/events.go b/ucevsecc/events.go index 33b3145..f077fcb 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -29,18 +29,16 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } - - switch payload.Data.(type) { - case *model.DeviceClassificationManufacturerDataType: - e.evseManufacturerDataUpdate(payload.Ski, payload.Entity) - case *model.DeviceDiagnosisStateDataType: - e.evseStateUpdate(payload.Ski, payload.Entity) - } + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.DeviceClassificationManufacturerDataType: + e.evseManufacturerDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceDiagnosisStateDataType: + e.evseStateUpdate(payload.Ski, payload.Entity) } } @@ -67,10 +65,24 @@ func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInte // the manufacturer Data of an EVSE was updated func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, api.UCEVSECCEventManufacturerUpdate) + evDeviceClassification, err := util.DeviceClassification(e.service, entity) + if err != nil { + return + } + + if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVSECCEventManufacturerUpdate) + } } // the operating State of an EVSE was updated func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, api.UCEVSECCEventOperationStateUpdate) + evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) + if err != nil { + return + } + + if _, err := evDeviceDiagnosis.GetState(); err == nil { + e.reader.SpineEvent(ski, entity, api.UCEVSECCEventOperationStateUpdate) + } } diff --git a/ucevsecc/events_test.go b/ucevsecc/events_test.go new file mode 100644 index 0000000..75229af --- /dev/null +++ b/ucevsecc/events_test.go @@ -0,0 +1,74 @@ +package ucevsecc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVSECCSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evseEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDeviceChange + payload.ChangeType = spineapi.ElementChangeRemove + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeRemove + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.DeviceClassificationManufacturerDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceDiagnosisStateDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCEVSECCSuite) Test_evseManufacturerDataUpdate() { + s.sut.evseManufacturerDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evseManufacturerDataUpdate(remoteSki, s.evseEntity) + + data := &model.DeviceClassificationManufacturerDataType{ + BrandName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evseEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evseManufacturerDataUpdate(remoteSki, s.evseEntity) +} + +func (s *UCEVSECCSuite) Test_evseStateUpdate() { + s.sut.evseStateUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evseStateUpdate(remoteSki, s.evseEntity) + + data := &model.DeviceDiagnosisStateDataType{ + OperatingState: eebusutil.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evseEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evseStateUpdate(remoteSki, s.evseEntity) +} diff --git a/ucevsecc/public_test.go b/ucevsecc/public_test.go index fbf493e..3b92c8c 100644 --- a/ucevsecc/public_test.go +++ b/ucevsecc/public_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *EVSECCSuite) Test_EVSEManufacturerData() { +func (s *UCEVSECCSuite) Test_EVSEManufacturerData() { device, serial, err := s.sut.ManufacturerData(nil) assert.NotNil(s.T(), err) assert.Equal(s.T(), "", device) @@ -47,7 +47,7 @@ func (s *EVSECCSuite) Test_EVSEManufacturerData() { assert.Equal(s.T(), "12345", serial) } -func (s *EVSECCSuite) Test_EVSEOperatingState() { +func (s *UCEVSECCSuite) Test_EVSEOperatingState() { data, errCode, err := s.sut.OperatingState(nil) assert.Equal(s.T(), model.DeviceDiagnosisOperatingStateTypeNormalOperation, data) assert.Equal(s.T(), "", errCode) diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index a283345..47226ff 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -21,10 +21,10 @@ import ( ) func TestEVSECCSuite(t *testing.T) { - suite.Run(t, new(EVSECCSuite)) + suite.Run(t, new(UCEVSECCSuite)) } -type EVSECCSuite struct { +type UCEVSECCSuite struct { suite.Suite sut *UCEVSECC @@ -36,10 +36,10 @@ type EVSECCSuite struct { evseEntity spineapi.EntityRemoteInterface } -func (s *EVSECCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVSECCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } -func (s *EVSECCSuite) BeforeTest(suiteName, testName string) { +func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := eebusapi.NewConfiguration( "test", "test", "test", "test", @@ -62,6 +62,7 @@ func (s *EVSECCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface diff --git a/ucevsecc/ucevsecc_test.go b/ucevsecc/ucevsecc_test.go new file mode 100644 index 0000000..564d20b --- /dev/null +++ b/ucevsecc/ucevsecc_test.go @@ -0,0 +1,41 @@ +package ucevsecc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVSECCSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evseEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeEVSECommissioningAndConfiguration), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{2}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evseEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} From e117a9597adee0d1ea1127a2514057d5cf78a93e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 13:11:51 +0100 Subject: [PATCH 079/227] Update UCEVSoC - Add tests - Remove support for scenarios with no real EV supporting it --- ucevsoc/api.go | 13 ++---- ucevsoc/events.go | 55 +++++-------------------- ucevsoc/events_test.go | 82 ++++++++++++++++++++++++++++++++++++++ ucevsoc/public.go | 2 +- ucevsoc/public_test.go | 14 +++---- ucevsoc/testhelper_test.go | 9 +++-- ucevsoc/ucevsoc.go | 11 ++--- ucevsoc/ucevsoc_test.go | 2 +- 8 files changed, 112 insertions(+), 76 deletions(-) create mode 100644 ucevsoc/events_test.go diff --git a/ucevsoc/api.go b/ucevsoc/api.go index e574311..f3aa873 100644 --- a/ucevsoc/api.go +++ b/ucevsoc/api.go @@ -2,6 +2,7 @@ package ucevsoc import ( "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" ) //go:generate mockery @@ -12,14 +13,8 @@ type UCEVSOCInterface interface { // Scenario 1 - // Scenario 2 + // return the EVscurrent state of charge of the EV or an error it is unknown + StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) - // this is automatically covered by the SPINE implementation - - // Scenario 3 - - // this is covered by the central CEM interface implementation - // use that one to set the CEM's operation state which will inform all remote devices - - // Scenario 4 + // Scenario 2 to 4 are not supported, as there is no EV supporting this as of today } diff --git a/ucevsoc/events.go b/ucevsoc/events.go index fe532f4..2b76368 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -21,18 +21,16 @@ func (e *UCEVSOC) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } - switch payload.Data.(type) { - case *model.MeasurementListDataType: - e.evMeasurementDataUpdate(payload.Ski, payload.Entity) - case *model.ElectricalConnectionCharacteristicListDataType: - e.evElectricalConnectionCharacteristicsDataUpdate(payload.Ski, payload.Entity) - } + // the codefactor warning is invalid, as .(type) check can not be replaced with if then + //revive:disable-next-line + switch payload.Data.(type) { + case *model.MeasurementListDataType: + e.evMeasurementDataUpdate(payload.Ski, payload.Entity) } } @@ -54,16 +52,6 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { logging.Log().Debug(err) } } - - if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { - if _, err := evElectricalConnection.Subscribe(); err != nil { - logging.Log().Debug(err) - } - - if _, err := evElectricalConnection.RequestCharacteristics(); err != nil { - logging.Log().Debug(err) - } - } } // the measurement data of an EV was updated @@ -83,28 +71,3 @@ func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemo e.reader.SpineEvent(ski, entity, api.UCEVSOCActualRangeDataUpdate) } } - -// the elecrical connection characteristic data of an EV was updated -func (e *UCEVSOC) evElectricalConnectionCharacteristicsDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evElectricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil { - return - } - - data, err := evElectricalConnection.GetCharacteristics() - if err != nil { - return - } - - for _, item := range data { - if item.CharacteristicType == nil || item.Value == nil { - continue - } - - if *item.CharacteristicType == model.ElectricalConnectionCharacteristicTypeTypeEnergyCapacityNominalMax { - e.reader.SpineEvent(ski, entity, api.UCEVSOCNominalCapacityDataUpdate) - return - } - } - -} diff --git a/ucevsoc/events_test.go b/ucevsoc/events_test.go new file mode 100644 index 0000000..81bc92f --- /dev/null +++ b/ucevsoc/events_test.go @@ -0,0 +1,82 @@ +package ucevsoc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCEVSOCSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.MeasurementListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCEVSOCSuite) Test_evMeasurementDataUpdate() { + s.sut.evMeasurementDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfCharge), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfHealth), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeTravelRange), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(200), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(3000), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) +} diff --git a/ucevsoc/public.go b/ucevsoc/public.go index 3b3ef5f..e666e61 100644 --- a/ucevsoc/public.go +++ b/ucevsoc/public.go @@ -16,7 +16,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEVSOC) EVSoC(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCEVSOC) StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return 0, api.ErrNoEvEntity } diff --git a/ucevsoc/public_test.go b/ucevsoc/public_test.go index 91ef75b..ce89c2f 100644 --- a/ucevsoc/public_test.go +++ b/ucevsoc/public_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *EVSOCSuite) Test_EVSOC() { - data, err := s.sut.EVSoC(s.mockRemoteEntity) +func (s *UCEVSOCSuite) Test_StateOfCharge() { + data, err := s.sut.StateOfCharge(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.EVSoC(s.evEntity) + data, err = s.sut.StateOfCharge(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -35,7 +35,7 @@ func (s *EVSOCSuite) Test_EVSOC() { fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVSoC(s.evEntity) + data, err = s.sut.StateOfCharge(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -54,7 +54,7 @@ func (s *EVSOCSuite) Test_EVSOC() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVSoC(s.evEntity) + data, err = s.sut.StateOfCharge(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -69,7 +69,7 @@ func (s *EVSOCSuite) Test_EVSOC() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVSoC(s.evEntity) + data, err = s.sut.StateOfCharge(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -85,7 +85,7 @@ func (s *EVSOCSuite) Test_EVSOC() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVSoC(s.evEntity) + data, err = s.sut.StateOfCharge(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 80.0, data) } diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 3f47542..a02fccd 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -21,10 +21,10 @@ import ( ) func TestEVSOCSuite(t *testing.T) { - suite.Run(t, new(EVSOCSuite)) + suite.Run(t, new(UCEVSOCSuite)) } -type EVSOCSuite struct { +type UCEVSOCSuite struct { suite.Suite sut *UCEVSOC @@ -36,10 +36,10 @@ type EVSOCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *EVSOCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVSOCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } -func (s *EVSOCSuite) BeforeTest(suiteName, testName string) { +func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := eebusapi.NewConfiguration( "test", "test", "test", "test", @@ -62,6 +62,7 @@ func (s *EVSOCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index 41a6fc2..bb650e2 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -43,11 +43,6 @@ func (e *UCEVSOC) AddFeatures() { f.AddFunctionType(model.FunctionTypeMeasurementConstraintsListData, false, false) f.AddFunctionType(model.FunctionTypeMeasurementListData, false, false) f.AddResultHandler(e) - - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, false, false) - - f.AddResultHandler(e) } func (e *UCEVSOC) AddUseCase() { @@ -56,10 +51,10 @@ func (e *UCEVSOC) AddUseCase() { localEntity.AddUseCaseSupport( model.UseCaseActorTypeCEM, e.UseCaseName(), - model.SpecificationVersionType("1.0.1"), - "RC2", + model.SpecificationVersionType("1.0.0"), + "RC1", true, - []model.UseCaseScenarioSupportType{1, 2, 3, 4}) + []model.UseCaseScenarioSupportType{1}) } // returns if the entity supports the usecase diff --git a/ucevsoc/ucevsoc_test.go b/ucevsoc/ucevsoc_test.go index 045910f..96e919d 100644 --- a/ucevsoc/ucevsoc_test.go +++ b/ucevsoc/ucevsoc_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *EVSOCSuite) Test_IsUseCaseSupported() { +func (s *UCEVSOCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) From fe37eeff2262807018d462873405d53627b3c179 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 13:28:11 +0100 Subject: [PATCH 080/227] Some renaming and tests for UCOPEV --- ucopev/api.go | 4 +- ucopev/events.go | 20 ++--- ucopev/events_test.go | 69 ++++++++++++++ ucopev/public.go | 8 +- ucopev/public_test.go | 22 +++++ ucopev/testhelper_test.go | 185 ++++++++++++++++++++++++++++++++++++++ ucopev/ucopev_test.go | 58 ++++++++++++ ucoscev/public.go | 4 +- util/loadcontrol.go | 4 +- util/loadcontrol_test.go | 16 ++-- 10 files changed, 361 insertions(+), 29 deletions(-) create mode 100644 ucopev/events_test.go create mode 100644 ucopev/public_test.go create mode 100644 ucopev/testhelper_test.go create mode 100644 ucopev/ucopev_test.go diff --git a/ucopev/api.go b/ucopev/api.go index dbb12a6..b31c67e 100644 --- a/ucopev/api.go +++ b/ucopev/api.go @@ -19,7 +19,7 @@ type UCOPEVInterface interface { // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) + LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) // send new LoadControlLimits to the remote EV // @@ -39,7 +39,7 @@ type UCOPEVInterface interface { // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific // and needs to have specific EVSE support for the specific EV brand. // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. - EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) + WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) // Scenario 2 diff --git a/ucopev/events.go b/ucopev/events.go index e760af5..1b8c2e3 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -21,18 +21,16 @@ func (e *UCOPEV) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } - switch payload.Data.(type) { - case *model.LoadControlLimitDescriptionListDataType: - e.evLoadControlLimitDescriptionDataUpdate(payload.Entity) - case *model.LoadControlLimitListDataType: - e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) - } + switch payload.Data.(type) { + case *model.LoadControlLimitDescriptionListDataType: + e.evLoadControlLimitDescriptionDataUpdate(payload.Entity) + case *model.LoadControlLimitListDataType: + e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) } } diff --git a/ucopev/events_test.go b/ucopev/events_test.go new file mode 100644 index 0000000..5f761a9 --- /dev/null +++ b/ucopev/events_test.go @@ -0,0 +1,69 @@ +package ucopev + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCOPEVSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.LoadControlLimitDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + + data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) +} diff --git a/ucopev/public.go b/ucopev/public.go index 38a8416..d418304 100644 --- a/ucopev/public.go +++ b/ucopev/public.go @@ -12,8 +12,8 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCOPEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { - return util.EVLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation) +func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { + return util.LoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation) } // send new LoadControlLimits to the remote EV @@ -34,6 +34,6 @@ func (e *UCOPEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]f // In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific // and needs to have specific EVSE support for the specific EV brand. // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. -func (e *UCOPEV) EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - return util.EVWriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation, limits) +func (e *UCOPEV) WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { + return util.WriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation, limits) } diff --git a/ucopev/public_test.go b/ucopev/public_test.go new file mode 100644 index 0000000..0751dd8 --- /dev/null +++ b/ucopev/public_test.go @@ -0,0 +1,22 @@ +package ucopev + +import ( + "github.com/enbility/cemd/api" + "github.com/stretchr/testify/assert" +) + +func (s *UCOPEVSuite) Test_Public() { + // The actual tests of the functionality is located in the util package + + _, err := s.sut.LoadControlLimits(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.LoadControlLimits(s.evEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteLoadControlLimits(s.mockRemoteEntity, []api.LoadLimitsPhase{}) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteLoadControlLimits(s.evEntity, []api.LoadLimitsPhase{}) + assert.NotNil(s.T(), err) +} diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go new file mode 100644 index 0000000..a3bdcc2 --- /dev/null +++ b/ucopev/testhelper_test.go @@ -0,0 +1,185 @@ +package ucopev + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestUCOPEVSuite(t *testing.T) { + suite.Run(t, new(UCOPEVSuite)) +} + +type UCOPEVSuite struct { + suite.Suite + + sut *UCOPEV + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + evEntity spineapi.EntityRemoteInterface +} + +func (s *UCOPEVSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.sut = NewUCOPEV(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + s.evEntity = entities[1] +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + []spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeLoadControlLimitDescriptionListData, + model.FunctionTypeLoadControlLimitConstraintsListData, + model.FunctionTypeLoadControlLimitListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisStateData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1, 1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEV), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities +} diff --git a/ucopev/ucopev_test.go b/ucopev/ucopev_test.go new file mode 100644 index 0000000..df62ae2 --- /dev/null +++ b/ucopev/ucopev_test.go @@ -0,0 +1,58 @@ +package ucopev + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCOPEVSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEV), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeOverloadProtectionByEVChargingCurrentCurtailment), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.evEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/ucoscev/public.go b/ucoscev/public.go index 828e88b..76e824e 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -13,7 +13,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCOSCEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { - return util.EVLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation) + return util.LoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation) } // send new LoadControlLimits to the remote EV @@ -28,5 +28,5 @@ func (e *UCOSCEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([] // the EVSE needs to be able map the recommendations into oligation limits which then // works for all EVs communication either via IEC61851 or ISO15118. func (e *UCOSCEV) EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - return util.EVWriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation, limits) + return util.WriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation, limits) } diff --git a/util/loadcontrol.go b/util/loadcontrol.go index c92f679..56f8d87 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -14,7 +14,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func EVLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType) ([]float64, error) { +func LoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType) ([]float64, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return nil, api.ErrNoEvEntity } @@ -110,7 +110,7 @@ func EVLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.Enti // - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. // - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. // - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. -func EVWriteLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { +func WriteLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return nil, api.ErrNoEvEntity } diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 14c8390..17507fc 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -12,11 +12,11 @@ import ( func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { loadLimits := []api.LoadLimitsPhase{} - msgCounter, err := EVWriteLoadControlLimits(s.service, s.mockRemoteEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err := WriteLoadControlLimits(s.service, s.mockRemoteEntity, model.LoadControlCategoryTypeObligation, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) - msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -47,7 +47,7 @@ func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -145,7 +145,7 @@ func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err := EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err := WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -178,7 +178,7 @@ func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -200,7 +200,7 @@ func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitListData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -214,11 +214,11 @@ func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { }) } - msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) - msgCounter, err = EVWriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeRecommendation, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeRecommendation, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) } From 70279d242ff714f357c429efae55173c78d46467 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 14:31:32 +0100 Subject: [PATCH 081/227] UCOSCEV api renaming and added tests --- ucoscev/api.go | 4 +-- ucoscev/events.go | 18 +++++----- ucoscev/events_test.go | 69 ++++++++++++++++++++++++++++++++++++++ ucoscev/public.go | 4 +-- ucoscev/public_test.go | 22 ++++++++++++ ucoscev/testhelper_test.go | 11 +++--- ucoscev/ucoscev_test.go | 2 +- 7 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 ucoscev/events_test.go create mode 100644 ucoscev/public_test.go diff --git a/ucoscev/api.go b/ucoscev/api.go index 32f7e6b..6efc856 100644 --- a/ucoscev/api.go +++ b/ucoscev/api.go @@ -19,7 +19,7 @@ type UCOSCEVInterface interface { // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) + LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) // send new LoadControlLimits to the remote EV // @@ -32,7 +32,7 @@ type UCOSCEVInterface interface { // The EV either needs to support the Optimization of Self Consumption usecase or // the EVSE needs to be able map the recommendations into oligation limits which then // works for all EVs communication either via IEC61851 or ISO15118. - EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) + WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) // Scenario 2 diff --git a/ucoscev/events.go b/ucoscev/events.go index 170f1ce..d88f7fd 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -16,16 +16,16 @@ func (e *UCOSCEV) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } - switch payload.Data.(type) { - case *model.LoadControlLimitListDataType: - e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) - } + // the codefactor warning is invalid, as .(type) check can not be replaced with if then + //revive:disable-next-line + switch payload.Data.(type) { + case *model.LoadControlLimitListDataType: + e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) } } diff --git a/ucoscev/events_test.go b/ucoscev/events_test.go new file mode 100644 index 0000000..22f0447 --- /dev/null +++ b/ucoscev/events_test.go @@ -0,0 +1,69 @@ +package ucoscev + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCOSCEVSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.evEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.LoadControlLimitDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.mockRemoteEntity) + + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeRecommendation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + + data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) +} diff --git a/ucoscev/public.go b/ucoscev/public.go index 76e824e..8d5575f 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -12,7 +12,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCOSCEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { return util.LoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation) } @@ -27,6 +27,6 @@ func (e *UCOSCEV) EVLoadControlLimits(entity spineapi.EntityRemoteInterface) ([] // The EV either needs to support the Optimization of Self Consumption usecase or // the EVSE needs to be able map the recommendations into oligation limits which then // works for all EVs communication either via IEC61851 or ISO15118. -func (e *UCOSCEV) EVWriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { +func (e *UCOSCEV) WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { return util.WriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation, limits) } diff --git a/ucoscev/public_test.go b/ucoscev/public_test.go new file mode 100644 index 0000000..fd571ea --- /dev/null +++ b/ucoscev/public_test.go @@ -0,0 +1,22 @@ +package ucoscev + +import ( + "github.com/enbility/cemd/api" + "github.com/stretchr/testify/assert" +) + +func (s *UCOSCEVSuite) Test_Public() { + // The actual tests of the functionality is located in the util package + + _, err := s.sut.LoadControlLimits(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.LoadControlLimits(s.evEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteLoadControlLimits(s.mockRemoteEntity, []api.LoadLimitsPhase{}) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteLoadControlLimits(s.evEntity, []api.LoadLimitsPhase{}) + assert.NotNil(s.T(), err) +} diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index 49b2944..19a44e2 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -20,11 +20,11 @@ import ( "github.com/stretchr/testify/suite" ) -func TestOSCEVSuite(t *testing.T) { - suite.Run(t, new(OSCEVSuite)) +func TestUCOSCEVSuite(t *testing.T) { + suite.Run(t, new(UCOSCEVSuite)) } -type OSCEVSuite struct { +type UCOSCEVSuite struct { suite.Suite sut *UCOSCEV @@ -36,10 +36,10 @@ type OSCEVSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *OSCEVSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCOSCEVSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } -func (s *OSCEVSuite) BeforeTest(suiteName, testName string) { +func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := eebusapi.NewConfiguration( "test", "test", "test", "test", @@ -62,6 +62,7 @@ func (s *OSCEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface diff --git a/ucoscev/ucoscev_test.go b/ucoscev/ucoscev_test.go index c0f31db..36ed875 100644 --- a/ucoscev/ucoscev_test.go +++ b/ucoscev/ucoscev_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *OSCEVSuite) Test_IsUseCaseSupported() { +func (s *UCOSCEVSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) From 5426d59b12988ee1e0377913ceb0a1cbb33ccba9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 14:37:35 +0100 Subject: [PATCH 082/227] Fix codefactor issues --- uccevc/public_scen2_test.go | 4 ++-- uccevc/public_scen3_test.go | 3 +-- ucevcc/events.go | 36 +++++++++++++++++------------------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/uccevc/public_scen2_test.go b/uccevc/public_scen2_test.go index ee47088..dbe3d98 100644 --- a/uccevc/public_scen2_test.go +++ b/uccevc/public_scen2_test.go @@ -212,9 +212,9 @@ func (s *UCCEVCSuite) Test_WritePowerLimits() { if data.error { assert.NotNil(t, err) continue - } else { - assert.Nil(t, err) } + + assert.Nil(t, err) } }) } diff --git a/uccevc/public_scen3_test.go b/uccevc/public_scen3_test.go index 2d12abe..2c086a9 100644 --- a/uccevc/public_scen3_test.go +++ b/uccevc/public_scen3_test.go @@ -222,10 +222,9 @@ func (s *UCCEVCSuite) Test_WriteIncentives() { if data.error { assert.NotNil(t, err) continue - } else { - assert.Nil(t, err) } + assert.Nil(t, err) } }) } diff --git a/ucevcc/events.go b/ucevcc/events.go index 5567f2d..0d22a46 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -29,26 +29,24 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { return } - switch payload.EventType { - case spineapi.EventTypeDataChange: - if payload.ChangeType != spineapi.ElementChangeUpdate { - return - } + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } - switch payload.Data.(type) { - case *model.DeviceConfigurationKeyValueDescriptionListDataType: - e.evConfigurationDescriptionDataUpdate(payload.Ski, payload.Entity) - case *model.DeviceConfigurationKeyValueListDataType: - e.evConfigurationDataUpdate(payload.Ski, payload.Entity) - case *model.DeviceClassificationManufacturerDataType: - e.evManufacturerDataUpdate(payload.Ski, payload.Entity) - case *model.ElectricalConnectionParameterDescriptionListDataType: - e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) - case *model.ElectricalConnectionPermittedValueSetListDataType: - e.evElectricalPermittedValuesUpdate(payload.Ski, payload.Entity) - case *model.IdentificationListDataType: - e.evIdentificationDataUpdate(payload.Ski, payload.Entity) - } + switch payload.Data.(type) { + case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.evConfigurationDescriptionDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceConfigurationKeyValueListDataType: + e.evConfigurationDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceClassificationManufacturerDataType: + e.evManufacturerDataUpdate(payload.Ski, payload.Entity) + case *model.ElectricalConnectionParameterDescriptionListDataType: + e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) + case *model.ElectricalConnectionPermittedValueSetListDataType: + e.evElectricalPermittedValuesUpdate(payload.Ski, payload.Entity) + case *model.IdentificationListDataType: + e.evIdentificationDataUpdate(payload.Ski, payload.Entity) } } From e060b8493b0792ca436a838e8c109e277b05141c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 15:30:40 +0100 Subject: [PATCH 083/227] Add tests for util package and remove unused code --- util/features_test.go | 45 ++++++++++++++ util/helper.go | 44 -------------- util/helper_test.go | 94 +++++++++++++++++++++++++++++ util/loadcontrol_test.go | 127 ++++++++++++++++++++++++++++++++++++++- util/measurement_test.go | 50 +++++++++++++++ util/testhelper_test.go | 24 +++++--- 6 files changed, 329 insertions(+), 55 deletions(-) create mode 100644 util/features_test.go create mode 100644 util/helper_test.go create mode 100644 util/measurement_test.go diff --git a/util/features_test.go b/util/features_test.go new file mode 100644 index 0000000..ff08f9e --- /dev/null +++ b/util/features_test.go @@ -0,0 +1,45 @@ +package util + +import "github.com/stretchr/testify/assert" + +func (s *UtilSuite) Test_Features() { + feature1, err := DeviceClassification(s.service, s.evEntity) + assert.Nil(s.T(), feature1) + assert.NotNil(s.T(), err) + + feature2, err := DeviceConfiguration(s.service, s.evEntity) + assert.Nil(s.T(), feature2) + assert.NotNil(s.T(), err) + + feature3, err := DeviceDiagnosis(s.service, s.evEntity) + assert.Nil(s.T(), feature3) + assert.NotNil(s.T(), err) + + feature4, err := DeviceDiagnosisServer(s.service, s.evEntity) + assert.Nil(s.T(), feature4) + assert.NotNil(s.T(), err) + + feature5, err := ElectricalConnection(s.service, s.evseEntity) + assert.Nil(s.T(), feature5) + assert.NotNil(s.T(), err) + + feature6, err := Identification(s.service, s.evEntity) + assert.Nil(s.T(), feature6) + assert.NotNil(s.T(), err) + + feature7, err := Measurement(s.service, s.evseEntity) + assert.Nil(s.T(), feature7) + assert.NotNil(s.T(), err) + + feature8, err := LoadControl(s.service, s.evseEntity) + assert.Nil(s.T(), feature8) + assert.NotNil(s.T(), err) + + feature9, err := TimeSeries(s.service, s.evEntity) + assert.Nil(s.T(), feature9) + assert.NotNil(s.T(), err) + + feature10, err := IncentiveTable(s.service, s.evEntity) + assert.Nil(s.T(), feature10) + assert.NotNil(s.T(), err) +} diff --git a/util/helper.go b/util/helper.go index 8f45584..f36ab2b 100644 --- a/util/helper.go +++ b/util/helper.go @@ -1,56 +1,12 @@ package util import ( - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) var PhaseNameMapping = []model.ElectricalConnectionPhaseNameType{model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC} -// check if the given usecase, actor is supported by the remote device -func IsUsecaseSupported( - usecase model.UseCaseNameType, - actor model.UseCaseActorType, - remoteDevice spineapi.DeviceRemoteInterface) bool { - uci := remoteDevice.UseCases() - - for _, element := range uci { - if *element.Actor != actor { - continue - } - for _, uc := range element.UseCaseSupport { - if uc.UseCaseName != nil && *uc.UseCaseName == usecase { - return true - } - } - } - - return false -} - -// return the remote entity of a given type and device ski -func EntityOfTypeForSki( - service api.ServiceInterface, - entityType model.EntityTypeType, - ski string) (spineapi.EntityRemoteInterface, error) { - rDevice := service.LocalDevice().RemoteDeviceForSki(ski) - - if rDevice == nil { - return nil, features.ErrEntityNotFound - } - - entities := rDevice.Entities() - for _, entity := range entities { - if entity.EntityType() == entityType { - return entity, nil - } - } - - return nil, features.ErrEntityNotFound -} - func IsPayloadForEntityType(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { if payload.Entity == nil { return false diff --git a/util/helper_test.go b/util/helper_test.go new file mode 100644 index 0000000..47b8d38 --- /dev/null +++ b/util/helper_test.go @@ -0,0 +1,94 @@ +package util + +import ( + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UtilSuite) Test_IsPayloadForEntityType() { + payload := spineapi.EventPayload{} + result := IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + result = IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + Entity: s.evEntity, + } + result = IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + assert.Equal(s.T(), true, result) +} + +func (s *UtilSuite) Test_IsDeviceDisconnected() { + payload := spineapi.EventPayload{} + result := IsDeviceDisconnected(payload) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + EventType: spineapi.EventTypeDeviceChange, + ChangeType: spineapi.ElementChangeRemove, + } + result = IsDeviceDisconnected(payload) + assert.Equal(s.T(), true, result) +} + +func (s *UtilSuite) Test_IsEvseConnected() { + payload := spineapi.EventPayload{} + result := IsEvseConnected(payload) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + Entity: s.evseEntity, + EventType: spineapi.EventTypeEntityChange, + ChangeType: spineapi.ElementChangeAdd, + } + result = IsEvseConnected(payload) + assert.Equal(s.T(), true, result) +} + +func (s *UtilSuite) Test_IsEvseDisconnected() { + payload := spineapi.EventPayload{} + result := IsEvseDisconnected(payload) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + Entity: s.evseEntity, + EventType: spineapi.EventTypeEntityChange, + ChangeType: spineapi.ElementChangeRemove, + } + result = IsEvseDisconnected(payload) + assert.Equal(s.T(), true, result) +} + +func (s *UtilSuite) Test_IsEvConnected() { + payload := spineapi.EventPayload{} + result := IsEvConnected(payload) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + Entity: s.evEntity, + EventType: spineapi.EventTypeEntityChange, + ChangeType: spineapi.ElementChangeAdd, + } + result = IsEvConnected(payload) + assert.Equal(s.T(), true, result) +} + +func (s *UtilSuite) Test_IsEvDisconnected() { + payload := spineapi.EventPayload{} + result := IsEvDisconnected(payload) + assert.Equal(s.T(), false, result) + + payload = spineapi.EventPayload{ + Entity: s.evEntity, + EventType: spineapi.EventTypeEntityChange, + ChangeType: spineapi.ElementChangeRemove, + } + result = IsEvDisconnected(payload) + assert.Equal(s.T(), true, result) +} diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 17507fc..fd71e6b 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -9,7 +9,132 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *LoadControlSuite) Test_EVWriteLoadControlLimits() { +func (s *UtilSuite) Test_LoadControlLimits() { + var data []float64 + var err error + category := model.LoadControlCategoryTypeObligation + + data, err = LoadControlLimits(s.service, s.mockRemoteEntity, category) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = LoadControlLimits(s.service, s.evEntity, category) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = LoadControlLimits(s.service, s.evEntity, category) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{0.0, 0.0, 0.0}, data) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = LoadControlLimits(s.service, s.evEntity, category) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), + Value: model.NewScaledNumberType(16), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = LoadControlLimits(s.service, s.evEntity, category) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + permData := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + PermittedValueSet: []model.ScaledNumberSetType{ + { + Value: []model.ScaledNumberType{ + *model.NewScaledNumberType(0), + }, + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(6), + Max: model.NewScaledNumberType(16), + }, + }, + }, + }, + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = LoadControlLimits(s.service, s.evEntity, category) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{16.0, 16.0, 16.0}, data) +} + +func (s *UtilSuite) Test_WriteLoadControlLimits() { loadLimits := []api.LoadLimitsPhase{} msgCounter, err := WriteLoadControlLimits(s.service, s.mockRemoteEntity, model.LoadControlCategoryTypeObligation, loadLimits) diff --git a/util/measurement_test.go b/util/measurement_test.go new file mode 100644 index 0000000..aeff957 --- /dev/null +++ b/util/measurement_test.go @@ -0,0 +1,50 @@ +package util + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UtilSuite) Test_MeasurementValueForScope() { + value, err := MeasurementValueForScope(s.service, s.mockRemoteEntity, model.ScopeTypeTypeACPower) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, value) + + value, err = MeasurementValueForScope(s.service, s.evEntity, model.ScopeTypeTypeACPower) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, value) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + value, err = MeasurementValueForScope(s.service, s.evEntity, model.ScopeTypeTypeACPower) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, value) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(80), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + value, err = MeasurementValueForScope(s.service, s.evEntity, model.ScopeTypeTypeACPower) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 80.0, value) +} diff --git a/util/testhelper_test.go b/util/testhelper_test.go index efc31db..0866d4a 100644 --- a/util/testhelper_test.go +++ b/util/testhelper_test.go @@ -20,24 +20,25 @@ import ( "github.com/stretchr/testify/suite" ) -func TestLoadControlSuite(t *testing.T) { - suite.Run(t, new(LoadControlSuite)) +func TestUtilSuite(t *testing.T) { + suite.Run(t, new(UtilSuite)) } -type LoadControlSuite struct { +type UtilSuite struct { suite.Suite service eebusapi.ServiceInterface remoteDevice spineapi.DeviceRemoteInterface mockRemoteEntity *mocks.EntityRemoteInterface + evseEntity spineapi.EntityRemoteInterface evEntity spineapi.EntityRemoteInterface } -func (s *LoadControlSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UtilSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } -func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { +func (s *UtilSuite) BeforeTest(suiteName, testName string) { cert, _ := cert.CreateCertificate("test", "test", "DE", "test") configuration, _ := eebusapi.NewConfiguration( "test", "test", "test", "test", @@ -60,10 +61,12 @@ func (s *LoadControlSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() var entities []spineapi.EntityRemoteInterface s.remoteDevice, entities = setupDevices(s.service, s.T()) + s.evseEntity = entities[0] s.evEntity = entities[1] } @@ -78,9 +81,9 @@ func setupDevices( f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) @@ -108,10 +111,11 @@ func setupDevices( model.FunctionTypeElectricalConnectionPermittedValueSetListData, }, }, - {model.FeatureTypeTypeDeviceDiagnosis, - model.RoleTypeClient, + {model.FeatureTypeTypeMeasurement, + model.RoleTypeServer, []model.FunctionType{ - model.FunctionTypeDeviceDiagnosisStateData, + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementListData, }, }, } From f3abcced3a9c38ccf93f5033e450e8a41fbe9b29 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 16:45:00 +0100 Subject: [PATCH 084/227] Minor refactor --- api/types.go | 3 +-- uccevc/events.go | 5 +++-- uccevc/public.go | 16 ++++++++++++++++ uccevc/public_scen1.go | 6 +++--- uccevc/public_scen2.go | 8 ++++---- uccevc/public_scen3.go | 12 ++++++------ uccevc/public_scen4.go | 8 ++++---- uccevc/uccevc.go | 4 ++-- ucevcc/events.go | 11 ++++++----- ucevcc/public.go | 38 ++++++++++++++++++++++++++++---------- ucevcc/ucevcc.go | 2 +- ucevcem/events.go | 3 ++- ucevcem/public.go | 26 ++++++++++++++++++-------- ucevcem/public_test.go | 16 ---------------- ucevcem/ucevcem.go | 4 ++-- ucevsecc/events.go | 7 ++++--- ucevsecc/public.go | 4 ++-- ucevsecc/ucevsecc.go | 2 +- ucevsoc/events.go | 3 ++- ucevsoc/public.go | 2 +- ucevsoc/ucevsoc.go | 2 +- ucopev/events.go | 5 +++-- ucopev/public.go | 4 ++-- ucopev/ucopev.go | 2 +- ucoscev/public.go | 4 ++-- ucoscev/ucoscev.go | 2 +- util/helper.go | 26 ++++---------------------- util/helper_test.go | 40 ++++++---------------------------------- util/loadcontrol.go | 25 +++++++++++++++++-------- util/loadcontrol_test.go | 32 ++++++++++++++++++-------------- 30 files changed, 161 insertions(+), 161 deletions(-) create mode 100644 uccevc/public.go diff --git a/api/types.go b/api/types.go index d8e01ac..e87ba33 100644 --- a/api/types.go +++ b/api/types.go @@ -299,8 +299,7 @@ const ( UCOSCEVLoadControlLimitDataUpdate UseCaseEventType = "ucOSCEVLoadControlLimitDataUpdate" ) -var ErrNoEvseEntity = errors.New("entity is not an EVSE") -var ErrNoEvEntity = errors.New("entity is not an EV") +var ErrNoCompatibleEntity = errors.New("entity is not an compatible entity") var ErrEVDisconnected = errors.New("ev is disconnected") var ErrNotSupported = errors.New("function is not supported") diff --git a/uccevc/events.go b/uccevc/events.go index fb72f22..05fcba4 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -12,11 +12,12 @@ import ( func (e *UCCEVC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + entityType := model.EntityTypeTypeEV + if !util.IsPayloadForEntityType(payload, entityType) { return } - if util.IsEvConnected(payload) { + if util.IsEntityTypeConnected(payload, entityType) { e.evConnected(payload.Entity) return } diff --git a/uccevc/public.go b/uccevc/public.go new file mode 100644 index 0000000..1fda8f1 --- /dev/null +++ b/uccevc/public.go @@ -0,0 +1,16 @@ +package uccevc + +import ( + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// helper + +func (e *UCCEVC) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false + } + + return true +} diff --git a/uccevc/public_scen1.go b/uccevc/public_scen1.go index f881899..572bd57 100644 --- a/uccevc/public_scen1.go +++ b/uccevc/public_scen1.go @@ -12,7 +12,7 @@ import ( // returns the current charging strategy func (e *UCCEVC) ChargeStrategy(entity spineapi.EntityRemoteInterface) api.EVChargeStrategyType { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + if !e.isCompatibleEntity(entity) { return api.EVChargeStrategyTypeUnknown } @@ -73,8 +73,8 @@ func (e *UCCEVC) ChargeStrategy(entity spineapi.EntityRemoteInterface) api.EVCha func (e *UCCEVC) EnergyDemand(entity spineapi.EntityRemoteInterface) (api.Demand, error) { demand := api.Demand{} - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return demand, api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return demand, api.ErrNoCompatibleEntity } evTimeSeries, err := util.TimeSeries(e.service, entity) diff --git a/uccevc/public_scen2.go b/uccevc/public_scen2.go index 0a78c5c..0402470 100644 --- a/uccevc/public_scen2.go +++ b/uccevc/public_scen2.go @@ -17,8 +17,8 @@ import ( func (e *UCCEVC) TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api.TimeSlotConstraints, error) { result := api.TimeSlotConstraints{} - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return result, api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return result, api.ErrNoCompatibleEntity } evTimeSeries, err := util.TimeSeries(e.service, entity) @@ -62,8 +62,8 @@ func (e *UCCEVC) TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api // send power limits to the EV // if no data is provided, default power limits with the max possible value for 7 days will be sent func (e *UCCEVC) WritePowerLimits(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return api.ErrNoCompatibleEntity } evTimeSeries, err := util.TimeSeries(e.service, entity) diff --git a/uccevc/public_scen3.go b/uccevc/public_scen3.go index c4b99a2..db85210 100644 --- a/uccevc/public_scen3.go +++ b/uccevc/public_scen3.go @@ -17,8 +17,8 @@ import ( func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (api.IncentiveSlotConstraints, error) { result := api.IncentiveSlotConstraints{} - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return result, api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return result, api.ErrNoCompatibleEntity } evIncentiveTable, err := util.IncentiveTable(e.service, entity) @@ -48,8 +48,8 @@ func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (ap // // SPINE UC CoordinatedEVCharging 2.4.3 func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInterface, data []api.IncentiveTariffDescription) error { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return api.ErrNoCompatibleEntity } evIncentiveTable, err := util.IncentiveTable(e.service, entity) @@ -186,8 +186,8 @@ func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInt // send incentives to the EV // if no data is provided, default incentives with the same price for 7 days will be sent func (e *UCCEVC) WriteIncentives(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return api.ErrNoCompatibleEntity } evIncentiveTable, err := util.IncentiveTable(e.service, entity) diff --git a/uccevc/public_scen4.go b/uccevc/public_scen4.go index 7487967..8ca8ead 100644 --- a/uccevc/public_scen4.go +++ b/uccevc/public_scen4.go @@ -13,8 +13,8 @@ import ( func (e *UCCEVC) ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([]api.DurationSlotValue, error) { constraints := []api.DurationSlotValue{} - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return constraints, api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return constraints, api.ErrNoCompatibleEntity } evTimeSeries, err := util.TimeSeries(e.service, entity) @@ -68,8 +68,8 @@ func (e *UCCEVC) ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([ func (e *UCCEVC) ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePlan, error) { plan := api.ChargePlan{} - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return plan, api.ErrEVDisconnected + if !e.isCompatibleEntity(entity) { + return plan, api.ErrNoCompatibleEntity } evTimeSeries, err := util.TimeSeries(e.service, entity) diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index cf15720..d9aaca4 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -64,8 +64,8 @@ func (e *UCCEVC) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCCEVC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/ucevcc/events.go b/ucevcc/events.go index 0d22a46..cf87d3c 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -17,14 +17,15 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { return } - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + entityType := model.EntityTypeTypeEV + if !util.IsPayloadForEntityType(payload, entityType) { return } - if util.IsEvConnected(payload) { + if util.IsEntityTypeConnected(payload, entityType) { e.evConnected(payload.Ski, payload.Entity) return - } else if util.IsEvDisconnected(payload) { + } else if util.IsEntityTypeDisconnected(payload, entityType) { e.evDisconnected(payload.Ski, payload.Entity) return } @@ -36,7 +37,7 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { switch payload.Data.(type) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: - e.evConfigurationDescriptionDataUpdate(payload.Ski, payload.Entity) + e.evConfigurationDescriptionDataUpdate(payload.Entity) case *model.DeviceConfigurationKeyValueListDataType: e.evConfigurationDataUpdate(payload.Ski, payload.Entity) case *model.DeviceClassificationManufacturerDataType: @@ -121,7 +122,7 @@ func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterfac } // the configuration key description data of an EV was updated -func (e *UCEVCC) evConfigurationDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCEVCC) evConfigurationDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { // key value descriptions received, now get the data if _, err := evDeviceConfiguration.RequestKeyValues(); err != nil { diff --git a/ucevcc/public.go b/ucevcc/public.go index 2cc9487..0e9a6fd 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -70,8 +70,8 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( entity spineapi.EntityRemoteInterface, keyname model.DeviceConfigurationKeyNameType, valueType model.DeviceConfigurationKeyValueTypeType) (any, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return nil, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity } evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity) @@ -115,6 +115,10 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { unknown := api.UCEVCCCommunicationStandardUnknown + if !e.isCompatibleEntity(entity) { + return unknown, api.ErrNoCompatibleEntity + } + data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) if err != nil { return unknown, err @@ -134,6 +138,10 @@ func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (s // possible errors: // - ErrDataNotAvailable if that information is not (yet) available func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity + } + data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeBoolean) if err != nil { return false, err @@ -154,8 +162,8 @@ func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterfa // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.IdentificationItem, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return nil, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity } evIdentification, err := util.Identification(e.service, entity) @@ -200,8 +208,8 @@ func (e *UCEVCC) ManufacturerData( deviceName := "" serialNumber := "" - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return deviceName, serialNumber, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return deviceName, serialNumber, api.ErrNoCompatibleEntity } evDeviceClassification, err := util.DeviceClassification(e.service, entity) @@ -231,8 +239,8 @@ func (e *UCEVCC) ManufacturerData( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return nil, nil, nil, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return nil, nil, nil, api.ErrNoCompatibleEntity } evElectricalConnection, err := util.ElectricalConnection(e.service, entity) @@ -275,8 +283,8 @@ func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64 func (e *UCEVCC) EVInSleepMode( entity spineapi.EntityRemoteInterface, ) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvseEntity + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity } evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) @@ -296,3 +304,13 @@ func (e *UCEVCC) EVInSleepMode( return false, nil } + +// helper + +func (e *UCEVCC) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false + } + + return true +} diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 99252e7..2419360 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -69,7 +69,7 @@ func (e *UCEVCC) AddUseCase() { // - and others func (e *UCEVCC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvEntity + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/ucevcem/events.go b/ucevcem/events.go index bbd14b6..f9b97a4 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -16,7 +16,8 @@ func (e *UCEVCEM) HandleEvent(payload spineapi.EventPayload) { return } - if util.IsEvConnected(payload) { + entityType := model.EntityTypeTypeEV + if util.IsEntityTypeConnected(payload, entityType) { e.evConnected(payload.Entity) return } diff --git a/ucevcem/public.go b/ucevcem/public.go index 1866f37..7cb4fe4 100644 --- a/ucevcem/public.go +++ b/ucevcem/public.go @@ -12,8 +12,8 @@ import ( // return the number of ac connected phases of the EV or 0 if it is unknown func (e *UCEVCEM) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return 0, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity } evElectricalConnection, err := util.ElectricalConnection(e.service, entity) @@ -42,8 +42,8 @@ func (e *UCEVCEM) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCEM) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return nil, api.ErrNoEvseEntity + if !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity } evMeasurement, err := util.Measurement(e.service, entity) @@ -101,8 +101,8 @@ func (e *UCEVCEM) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]flo // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCEM) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if entity.EntityType() != model.EntityTypeTypeEV { - return nil, api.ErrNoEvseEntity + if !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity } evMeasurement, err := util.Measurement(e.service, entity) @@ -161,8 +161,8 @@ func (e *UCEVCEM) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float6 // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCEM) ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity.EntityType() != model.EntityTypeTypeEV { - return 0, api.ErrNoEvseEntity + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity } evMeasurement, err := util.Measurement(e.service, entity) @@ -186,3 +186,13 @@ func (e *UCEVCEM) ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, return value.GetValue(), err } + +// helper + +func (e *UCEVCEM) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + return false + } + + return true +} diff --git a/ucevcem/public_test.go b/ucevcem/public_test.go index 8cc92d3..6d30003 100644 --- a/ucevcem/public_test.go +++ b/ucevcem/public_test.go @@ -15,10 +15,6 @@ func (s *UCEVCEMSuite) Test_EVConnectedPhases() { assert.NotNil(s.T(), err) assert.Equal(s.T(), uint(0), data) - data, err = s.sut.ConnectedPhases(s.evEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), uint(0), data) - descData := &model.ElectricalConnectionDescriptionListDataType{ ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ { @@ -61,10 +57,6 @@ func (s *UCEVCEMSuite) Test_EVCurrentsPerPhase() { assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.CurrentsPerPhase(s.evEntity) - assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - paramDesc := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ { @@ -130,10 +122,6 @@ func (s *UCEVCEMSuite) Test_EVPowerPerPhase_Power() { assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.PowerPerPhase(s.evEntity) - assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - paramDesc := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ { @@ -264,10 +252,6 @@ func (s *UCEVCEMSuite) Test_EVChargedEnergy() { assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.ChargedEnergy(s.evEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, data) - measDesc := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ { diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index 8454ee9..61c49a6 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -58,8 +58,8 @@ func (e *UCEVCEM) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVCEM) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvEntity + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/ucevsecc/events.go b/ucevsecc/events.go index f077fcb..5bff2db 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -17,14 +17,15 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { return } - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) { + entityType := model.EntityTypeTypeEVSE + if !util.IsPayloadForEntityType(payload, entityType) { return } - if util.IsEvseConnected(payload) { + if util.IsEntityTypeConnected(payload, entityType) { e.evseConnected(payload.Ski, payload.Entity) return - } else if util.IsEvseDisconnected(payload) { + } else if util.IsEntityTypeDisconnected(payload, entityType) { e.evseDisconnected(payload.Ski, payload.Entity) return } diff --git a/ucevsecc/public.go b/ucevsecc/public.go index 7656b55..3c2560a 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -20,7 +20,7 @@ func (e *UCEVSECC) ManufacturerData( serialNumber := "" if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { - return deviceName, serialNumber, api.ErrNoEvseEntity + return deviceName, serialNumber, api.ErrNoCompatibleEntity } evseDeviceClassification, err := util.DeviceClassification(e.service, entity) @@ -55,7 +55,7 @@ func (e *UCEVSECC) OperatingState( lastErrorCode := "" if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { - return operatingState, lastErrorCode, api.ErrNoEvseEntity + return operatingState, lastErrorCode, api.ErrNoCompatibleEntity } evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index bf9f306..a522529 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -66,7 +66,7 @@ func (e *UCEVSECC) AddUseCase() { // - and others func (e *UCEVSECC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { - return false, api.ErrNoEvEntity + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 2b76368..31ed580 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -16,7 +16,8 @@ func (e *UCEVSOC) HandleEvent(payload spineapi.EventPayload) { return } - if util.IsEvConnected(payload) { + entityType := model.EntityTypeTypeEV + if util.IsEntityTypeConnected(payload, entityType) { e.evConnected(payload.Entity) return } diff --git a/ucevsoc/public.go b/ucevsoc/public.go index e666e61..2342187 100644 --- a/ucevsoc/public.go +++ b/ucevsoc/public.go @@ -18,7 +18,7 @@ import ( // - and others func (e *UCEVSOC) StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return 0, api.ErrNoEvEntity + return 0, api.ErrNoCompatibleEntity } evMeasurement, err := util.Measurement(e.service, entity) diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index bb650e2..f465f14 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -64,7 +64,7 @@ func (e *UCEVSOC) AddUseCase() { // - and others func (e *UCEVSOC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvEntity + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/ucopev/events.go b/ucopev/events.go index 1b8c2e3..1129896 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -12,11 +12,12 @@ import ( func (e *UCOPEV) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + entityType := model.EntityTypeTypeEV + if !util.IsPayloadForEntityType(payload, entityType) { return } - if util.IsEvConnected(payload) { + if util.IsEntityTypeConnected(payload, entityType) { e.evConnected(payload.Entity) return } diff --git a/ucopev/public.go b/ucopev/public.go index d418304..0355925 100644 --- a/ucopev/public.go +++ b/ucopev/public.go @@ -13,7 +13,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { - return util.LoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation) + return util.LoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeObligation) } // send new LoadControlLimits to the remote EV @@ -35,5 +35,5 @@ func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]flo // and needs to have specific EVSE support for the specific EV brand. // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. func (e *UCOPEV) WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - return util.WriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeObligation, limits) + return util.WriteLoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeObligation, limits) } diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 945965b..fb838af 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -64,7 +64,7 @@ func (e *UCOPEV) AddUseCase() { // - and others func (e *UCOPEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvEntity + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/ucoscev/public.go b/ucoscev/public.go index 8d5575f..a3badf0 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -13,7 +13,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { - return util.LoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation) + return util.LoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeRecommendation) } // send new LoadControlLimits to the remote EV @@ -28,5 +28,5 @@ func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]fl // the EVSE needs to be able map the recommendations into oligation limits which then // works for all EVs communication either via IEC61851 or ISO15118. func (e *UCOSCEV) WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - return util.WriteLoadControlLimits(e.service, entity, model.LoadControlCategoryTypeRecommendation, limits) + return util.WriteLoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeRecommendation, limits) } diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 261b1bc..c36c171 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -64,7 +64,7 @@ func (e *UCOSCEV) AddUseCase() { // - and others func (e *UCOSCEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false, api.ErrNoEvEntity + return false, api.ErrNoCompatibleEntity } // check if the usecase and mandatory scenarios are supported and diff --git a/util/helper.go b/util/helper.go index f36ab2b..3855608 100644 --- a/util/helper.go +++ b/util/helper.go @@ -21,37 +21,19 @@ func IsDeviceDisconnected(payload spineapi.EventPayload) bool { payload.ChangeType == spineapi.ElementChangeRemove) } -func IsEvseConnected(payload spineapi.EventPayload) bool { +func IsEntityTypeConnected(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { if payload.EventType == spineapi.EventTypeEntityChange && payload.ChangeType == spineapi.ElementChangeAdd { - return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) + return IsPayloadForEntityType(payload, entityType) } return false } -func IsEvseDisconnected(payload spineapi.EventPayload) bool { +func IsEntityTypeDisconnected(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { if payload.EventType == spineapi.EventTypeEntityChange && payload.ChangeType == spineapi.ElementChangeRemove { - return IsPayloadForEntityType(payload, model.EntityTypeTypeEVSE) - } - - return false -} - -func IsEvConnected(payload spineapi.EventPayload) bool { - if payload.EventType == spineapi.EventTypeEntityChange && - payload.ChangeType == spineapi.ElementChangeAdd { - return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) - } - - return false -} - -func IsEvDisconnected(payload spineapi.EventPayload) bool { - if payload.EventType == spineapi.EventTypeEntityChange && - payload.ChangeType == spineapi.ElementChangeRemove { - return IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + return IsPayloadForEntityType(payload, entityType) } return false diff --git a/util/helper_test.go b/util/helper_test.go index 47b8d38..b813314 100644 --- a/util/helper_test.go +++ b/util/helper_test.go @@ -37,9 +37,9 @@ func (s *UtilSuite) Test_IsDeviceDisconnected() { assert.Equal(s.T(), true, result) } -func (s *UtilSuite) Test_IsEvseConnected() { +func (s *UtilSuite) Test_IsEntityTypeConnected() { payload := spineapi.EventPayload{} - result := IsEvseConnected(payload) + result := IsEntityTypeConnected(payload, model.EntityTypeTypeEVSE) assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ @@ -47,13 +47,13 @@ func (s *UtilSuite) Test_IsEvseConnected() { EventType: spineapi.EventTypeEntityChange, ChangeType: spineapi.ElementChangeAdd, } - result = IsEvseConnected(payload) + result = IsEntityTypeConnected(payload, model.EntityTypeTypeEVSE) assert.Equal(s.T(), true, result) } -func (s *UtilSuite) Test_IsEvseDisconnected() { +func (s *UtilSuite) Test_IsEntityTypeDisconnected() { payload := spineapi.EventPayload{} - result := IsEvseDisconnected(payload) + result := IsEntityTypeDisconnected(payload, model.EntityTypeTypeEVSE) assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ @@ -61,34 +61,6 @@ func (s *UtilSuite) Test_IsEvseDisconnected() { EventType: spineapi.EventTypeEntityChange, ChangeType: spineapi.ElementChangeRemove, } - result = IsEvseDisconnected(payload) - assert.Equal(s.T(), true, result) -} - -func (s *UtilSuite) Test_IsEvConnected() { - payload := spineapi.EventPayload{} - result := IsEvConnected(payload) - assert.Equal(s.T(), false, result) - - payload = spineapi.EventPayload{ - Entity: s.evEntity, - EventType: spineapi.EventTypeEntityChange, - ChangeType: spineapi.ElementChangeAdd, - } - result = IsEvConnected(payload) - assert.Equal(s.T(), true, result) -} - -func (s *UtilSuite) Test_IsEvDisconnected() { - payload := spineapi.EventPayload{} - result := IsEvDisconnected(payload) - assert.Equal(s.T(), false, result) - - payload = spineapi.EventPayload{ - Entity: s.evEntity, - EventType: spineapi.EventTypeEntityChange, - ChangeType: spineapi.ElementChangeRemove, - } - result = IsEvDisconnected(payload) + result = IsEntityTypeDisconnected(payload, model.EntityTypeTypeEVSE) assert.Equal(s.T(), true, result) } diff --git a/util/loadcontrol.go b/util/loadcontrol.go index 56f8d87..c202fa1 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -14,15 +14,19 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func LoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType) ([]float64, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return nil, api.ErrNoEvEntity +func LoadControlLimits( + service eebusapi.ServiceInterface, + entity spineapi.EntityRemoteInterface, + entityType model.EntityTypeType, + category model.LoadControlCategoryType) ([]float64, error) { + if entity == nil || entity.EntityType() != entityType { + return nil, api.ErrNoCompatibleEntity } evLoadControl, err := LoadControl(service, entity) evElectricalConnection, err2 := ElectricalConnection(service, entity) if err != nil || err2 != nil { - return nil, api.ErrNoEvEntity + return nil, api.ErrNoCompatibleEntity } // find out the appropriate limitId for each phase value @@ -110,15 +114,20 @@ func LoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.Entity // - In ISO15118-2 the usecase is only supported via VAS extensions which are vendor specific and needs to have specific EVSE support for the specific EV brand. // - In ISO15118-20 this is a standard feature which does not need special support on the EVSE. // - Min power data is only provided via IEC61851 or using VAS in ISO15118-2. -func WriteLoadControlLimits(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, category model.LoadControlCategoryType, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return nil, api.ErrNoEvEntity +func WriteLoadControlLimits( + service eebusapi.ServiceInterface, + entity spineapi.EntityRemoteInterface, + entityType model.EntityTypeType, + category model.LoadControlCategoryType, + limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { + if entity == nil || entity.EntityType() != entityType { + return nil, api.ErrNoCompatibleEntity } evLoadControl, err := LoadControl(service, entity) evElectricalConnection, err2 := ElectricalConnection(service, entity) if err != nil || err2 != nil { - return nil, api.ErrNoEvseEntity + return nil, api.ErrNoCompatibleEntity } var limitData []model.LoadControlLimitDataType diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index fd71e6b..4150b18 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -13,12 +13,13 @@ func (s *UtilSuite) Test_LoadControlLimits() { var data []float64 var err error category := model.LoadControlCategoryTypeObligation + entityType := model.EntityTypeTypeEV - data, err = LoadControlLimits(s.service, s.mockRemoteEntity, category) + data, err = LoadControlLimits(s.service, s.mockRemoteEntity, entityType, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = LoadControlLimits(s.service, s.evEntity, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -46,7 +47,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{0.0, 0.0, 0.0}, data) @@ -77,7 +78,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -100,7 +101,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -129,7 +130,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{16.0, 16.0, 16.0}, data) } @@ -137,11 +138,14 @@ func (s *UtilSuite) Test_LoadControlLimits() { func (s *UtilSuite) Test_WriteLoadControlLimits() { loadLimits := []api.LoadLimitsPhase{} - msgCounter, err := WriteLoadControlLimits(s.service, s.mockRemoteEntity, model.LoadControlCategoryTypeObligation, loadLimits) + category := model.LoadControlCategoryTypeObligation + entityType := model.EntityTypeTypeEV + + msgCounter, err := WriteLoadControlLimits(s.service, s.mockRemoteEntity, entityType, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -172,7 +176,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -270,7 +274,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err := WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err := WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -303,7 +307,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -325,7 +329,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitListData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -339,11 +343,11 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { }) } - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeObligation, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, model.LoadControlCategoryTypeRecommendation, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) } From b80fb1e150b4fc2e2a26ef84a0b970edab37d0a9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 17:53:21 +0100 Subject: [PATCH 085/227] Add MGCP use case --- api/types.go | 32 +++ grid/api.go | 11 - grid/events.go | 140 ------------ grid/grid.go | 45 ---- grid/public.go | 255 --------------------- grid/results.go | 8 - grid/solution.go | 97 -------- ucmgcp/api.go | 64 ++++++ ucmgcp/events.go | 136 +++++++++++ ucmgcp/events_test.go | 111 +++++++++ ucmgcp/public.go | 315 ++++++++++++++++++++++++++ ucmgcp/public_test.go | 464 ++++++++++++++++++++++++++++++++++++++ ucmgcp/results.go | 8 + ucmgcp/testhelper_test.go | 183 +++++++++++++++ ucmgcp/ucmgcp.go | 112 +++++++++ ucmgcp/ucmgcp_test.go | 82 +++++++ 16 files changed, 1507 insertions(+), 556 deletions(-) delete mode 100644 grid/api.go delete mode 100644 grid/events.go delete mode 100644 grid/grid.go delete mode 100644 grid/public.go delete mode 100644 grid/results.go delete mode 100644 grid/solution.go create mode 100644 ucmgcp/api.go create mode 100644 ucmgcp/events.go create mode 100644 ucmgcp/events_test.go create mode 100644 ucmgcp/public.go create mode 100644 ucmgcp/public_test.go create mode 100644 ucmgcp/results.go create mode 100644 ucmgcp/testhelper_test.go create mode 100644 ucmgcp/ucmgcp.go create mode 100644 ucmgcp/ucmgcp_test.go diff --git a/api/types.go b/api/types.go index e87ba33..2974757 100644 --- a/api/types.go +++ b/api/types.go @@ -282,6 +282,38 @@ const ( // Use Case EVSOC, Scenario 4 UCEVSOCActualRangeDataUpdate UseCaseEventType = "ucEVSOCActualRangeDataUpdate" + // MGCP + + // Grid momentary power consumption/production data updated + // + // Use Case MGCP, Scenario 2 + UCMGCPPowerTotalMeasurementDataUpdate UseCaseEventType = "ucMGCPPowerTotalMeasurementDataUpdate" + + // MTotal grid feed in energy data updated + // + // Use Case MGCP, Scenario 3 + UCMGCPGridFeedInMeasurementDataUpdate UseCaseEventType = "ucMGCPGridFeedInMeasurementDataUpdate" + + // Total grid consumed energy data updated + // + // Use Case MGCP, Scenario 4 + UCMGCPGridConsumptionMeasurementDataUpdate UseCaseEventType = "ucMGCPGridConsumptionMeasurementDataUpdate" + + // Grid momentary current consumption/production phase detail data updated + // + // Use Case MGCP, Scenario 5 + UCMGCPCurrentMeasurementDataUpdate UseCaseEventType = "ucMGCPCurrentMeasurementDataUpdate" + + // Grid voltage phase detail data updated + // + // Use Case MGCP, Scenario 6 + UCMGCPVoltageMeasurementDataUpdate UseCaseEventType = "ucMGCPVoltageMeasurementDataUpdate" + + // Grid frequency data updated + // + // Use Case MGCP, Scenario 7 + UCMGCPFrequencyMeasurementDataUpdate UseCaseEventType = "ucMGCPFrequencyMeasurementDataUpdate" + // UCOPEV // EV load control obligation limit data updated diff --git a/grid/api.go b/grid/api.go deleted file mode 100644 index 209ab16..0000000 --- a/grid/api.go +++ /dev/null @@ -1,11 +0,0 @@ -package grid - -type GridInterface interface { - PowerLimitationFactor() (float64, error) - MomentaryPowerConsumptionOrProduction() (float64, error) - TotalFeedInEnergy() (float64, error) - TotalConsumedEnergy() (float64, error) - MomentaryCurrentConsumptionOrProduction() ([]float64, error) - Voltage() ([]float64, error) - Frequency() (float64, error) -} diff --git a/grid/events.go b/grid/events.go deleted file mode 100644 index 6952947..0000000 --- a/grid/events.go +++ /dev/null @@ -1,140 +0,0 @@ -package grid - -import ( - "github.com/enbility/eebus-go/features" - "github.com/enbility/ship-go/logging" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -// Internal EventHandler Interface for the CEM -func (e *Grid) HandleEvent(payload api.EventPayload) { - // we only care about the registered SKI - if payload.Ski != e.ski { - return - } - - // we care only about events for this remote device - if payload.Device != nil && payload.Device.Ski() != e.ski { - return - } - - switch payload.EventType { - case api.EventTypeDeviceChange: - switch payload.ChangeType { - case api.ElementChangeRemove: - e.gridDisconnected() - } - - case api.EventTypeEntityChange: - entityType := payload.Entity.EntityType() - - switch payload.ChangeType { - case api.ElementChangeAdd: - switch entityType { - case model.EntityTypeTypeGridConnectionPointOfPremises: - e.gridConnected(payload.Ski, payload.Entity) - } - case api.ElementChangeRemove: - switch entityType { - case model.EntityTypeTypeGridConnectionPointOfPremises: - e.gridDisconnected() - } - } - - case api.EventTypeDataChange: - if payload.ChangeType == api.ElementChangeUpdate { - switch payload.Data.(type) { - - case *model.DeviceConfigurationKeyValueDescriptionListDataType: - // key value descriptions received, now get the data - if _, err := e.gridDeviceConfiguration.RequestKeyValues(); err != nil { - logging.Log().Error("Error getting configuration key values:", err) - } - - case *model.MeasurementDescriptionListDataType: - if _, err := e.gridMeasurement.RequestValues(); err != nil { - logging.Log().Error("Error getting measurement list values:", err) - } - } - - } - - } -} - -// process required steps when a grid device is connected -func (e *Grid) gridConnected(ski string, entity api.EntityRemoteInterface) { - e.gridEntity = entity - localDevice := e.service.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f1, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - e.gridDeviceConfiguration = f1 - - f2, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - e.gridElectricalConnection = f2 - - f3, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - e.gridMeasurement = f3 - - // subscribe - if _, err := e.gridDeviceConfiguration.Subscribe(); err != nil { - logging.Log().Error(err) - return - } - if _, err := e.gridElectricalConnection.Subscribe(); err != nil { - logging.Log().Error(err) - return - } - if _, err := e.gridMeasurement.Subscribe(); err != nil { - logging.Log().Error(err) - return - } - - // get configuration data - if _, err := e.gridDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log().Error(err) - return - } - - // get electrical connection parameter - if _, err := e.gridElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Error(err) - return - } - - if _, err := e.gridElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log().Error(err) - return - } - - // get measurement parameters - if _, err := e.gridMeasurement.RequestDescriptions(); err != nil { - logging.Log().Error(err) - return - } - - if _, err := e.gridMeasurement.RequestConstraints(); err != nil { - logging.Log().Error(err) - return - } -} - -// a grid device was disconnected -func (e *Grid) gridDisconnected() { - e.gridEntity = nil - - e.gridDeviceConfiguration = nil - e.gridElectricalConnection = nil - e.gridMeasurement = nil -} diff --git a/grid/grid.go b/grid/grid.go deleted file mode 100644 index 49d917a..0000000 --- a/grid/grid.go +++ /dev/null @@ -1,45 +0,0 @@ -package grid - -import ( - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/util" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" -) - -type Grid struct { - entity spineapi.EntityLocalInterface - - service api.ServiceInterface - - gridEntity spineapi.EntityRemoteInterface - - gridDeviceConfiguration *features.DeviceConfiguration - gridElectricalConnection *features.ElectricalConnection - gridMeasurement *features.Measurement - - ski string -} - -var _ GridInterface = (*Grid)(nil) - -// Add Grid support -func NewGrid(service api.ServiceInterface, details *shipapi.ServiceDetails) *Grid { - ski := util.NormalizeSKI(details.SKI()) - - localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - grid := &Grid{ - service: service, - entity: localEntity, - ski: ski, - } - _ = spine.Events.Subscribe(grid) - - service.RegisterRemoteSKI(ski, true) - - return grid -} diff --git a/grid/public.go b/grid/public.go deleted file mode 100644 index 62294d9..0000000 --- a/grid/public.go +++ /dev/null @@ -1,255 +0,0 @@ -package grid - -import ( - "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" - "github.com/enbility/spine-go/model" -) - -// return the power limitation factor -// -// possible errors: -// - ErrDataNotAvailable if that information is not (yet) available -// - ErrNotSupported if getting the communication standard is not supported -// - and others -func (g *Grid) PowerLimitationFactor() (float64, error) { - if g.gridEntity == nil { - return 0, util.ErrDeviceDisconnected - } - - if g.gridMeasurement == nil { - return 0, features.ErrDataNotAvailable - } - - keyname := model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor - - // check if device configuration description has curtailment limit factor key name - _, err := g.gridDeviceConfiguration.GetDescriptionForKeyName(keyname) - if err != nil { - return 0, err - } - - data, err := g.gridDeviceConfiguration.GetKeyValueForKeyName(keyname, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) - if err != nil { - return 0, err - } - - if data == nil { - return 0, features.ErrDataNotAvailable - } - - value := data.(*model.ScaledNumberType) - return value.GetValue(), nil -} - -// return the momentary power consumption (positive) or production (negative) -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (g *Grid) MomentaryPowerConsumptionOrProduction() (float64, error) { - measurement := model.MeasurementTypeTypePower - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACPowerTotal - data, err := g.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // we assume there is only one value - mId := data[0].MeasurementId - value := data[0].Value - if mId == nil || value == nil { - return 0, features.ErrDataNotAvailable - } - - // according to UC_TS_MonitoringOfGridConnectionPoint 3.2.2.2.4.1 - // positive values are with description "PositiveEnergyDirection" value "consume" - // but we verify this - desc, err := g.gridElectricalConnection.GetDescriptionForMeasurementId(*mId) - if err != nil { - return 0, err - } - - // if energy direction is not consume, invert it - if desc.PositiveEnergyDirection != nil && *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - return -1 * value.GetValue(), nil - } - - return value.GetValue(), nil -} - -// return the total feed-in energy -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (g *Grid) TotalFeedInEnergy() (float64, error) { - measurement := model.MeasurementTypeTypeEnergy - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeGridFeedIn - data, err := g.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // we assume thre is only one result - value := data[0].Value - if value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), nil -} - -// return the total consumed energy -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (g *Grid) TotalConsumedEnergy() (float64, error) { - measurement := model.MeasurementTypeTypeEnergy - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeGridConsumption - data, err := g.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // we assume thre is only one result - value := data[0].Value - if value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), nil -} - -// return the momentary current consumption (positive) or production (negative) per phase -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (g *Grid) MomentaryCurrentConsumptionOrProduction() ([]float64, error) { - measurement := model.MeasurementTypeTypeCurrent - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACCurrent - values, err := g.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range values { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := g.gridElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - // according to UC_TS_MonitoringOfGridConnectionPoint 3.2.1.3.2.4 - // positive values are with description "PositiveEnergyDirection" value "consume" - // but we should verify this - if desc, err := g.gridElectricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { - // if energy direction is not consume, invert it - if desc.PositiveEnergyDirection != nil && *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - value = -1 * value - } - } - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil -} - -// return the voltage per phase -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (g *Grid) Voltage() ([]float64, error) { - measurement := model.MeasurementTypeTypeVoltage - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACVoltage - data, err := g.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range data { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := g.gridElectricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil -} - -// return the frequence -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (g *Grid) Frequency() (float64, error) { - measurement := model.MeasurementTypeTypeFrequency - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACFrequency - item, err := g.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // take the first item - value := item[0].Value - if value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), nil -} - -// helper - -func (g *Grid) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - if g.gridEntity == nil { - return nil, util.ErrDeviceDisconnected - } - - if g.gridMeasurement == nil { - return nil, features.ErrDataNotAvailable - } - - return g.gridMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) -} diff --git a/grid/results.go b/grid/results.go deleted file mode 100644 index 756c16f..0000000 --- a/grid/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package grid - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *Grid) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/grid/solution.go b/grid/solution.go deleted file mode 100644 index bb68e8a..0000000 --- a/grid/solution.go +++ /dev/null @@ -1,97 +0,0 @@ -package grid - -import ( - "sync" - - "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -type GridSolution struct { - *api.Solution - - remoteDevices map[string]*Grid - - mux sync.Mutex -} - -var _ api.SolutionInterface = (*GridSolution)(nil) - -func NewGridScenario(service eebusapi.ServiceInterface) *GridSolution { - return &GridSolution{ - Solution: api.NewSolution(service), - remoteDevices: make(map[string]*Grid), - } -} - -// adds all the supported features to the local entity -func (e *GridSolution) AddFeatures() { - localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - // client features - var clientFeatures = []model.FeatureTypeType{ - model.FeatureTypeTypeDeviceConfiguration, - model.FeatureTypeTypeElectricalConnection, - model.FeatureTypeTypeMeasurement, - } - for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) - } -} - -// add supported grid usecases -func (e *GridSolution) AddUseCases() { - localEntity := e.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeMonitoringAppliance, - model.UseCaseNameTypeMonitoringOfGridConnectionPoint, - model.SpecificationVersionType("1.0.0"), - "RC5", - true, - []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) -} - -func (e *GridSolution) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { - // TODO: grid should be stored per remote SKI and - // only be set for the SKI if the device supports it - e.mux.Lock() - defer e.mux.Unlock() - - if em, ok := e.remoteDevices[details.SKI()]; ok { - return em - } - - grid := NewGrid(e.Service, details) - e.remoteDevices[details.SKI()] = grid - return grid -} - -func (e *GridSolution) UnRegisterRemoteDevice(remoteDeviceSki string) { - e.mux.Lock() - defer e.mux.Unlock() - - delete(e.remoteDevices, remoteDeviceSki) - - e.Service.RegisterRemoteSKI(remoteDeviceSki, false) -} - -func (e *GridSolution) HandleResult(errorMsg spineapi.ResultMessage) { - e.mux.Lock() - defer e.mux.Unlock() - - if errorMsg.DeviceRemote == nil { - return - } - - em, ok := e.remoteDevices[errorMsg.DeviceRemote.Ski()] - if !ok { - return - } - - em.HandleResult(errorMsg) -} diff --git a/ucmgcp/api.go b/ucmgcp/api.go new file mode 100644 index 0000000..de2613f --- /dev/null +++ b/ucmgcp/api.go @@ -0,0 +1,64 @@ +package ucmgcp + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +//go:generate mockery + +// interface for the Monitoring of Grid Connection Point UseCase +type UCMGCPInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current power limitation factor + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 2 + + // return the momentary power consumption or production at the grid connection point + // + // - positive values are used for consumption + // - negative values are used for production + MomentaryPowerConsumptionOrProduction(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 3 + + // return the total feed in energy at the grid connection point + // + // - negative values are used for production + TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 4 + + // return the total consumption energy at the grid connection point + // + // - positive values are used for consumption + TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 5 + + // return the momentary current consumption or production at the grid connection point + // + // - positive values are used for consumption + // - negative values are used for production + MomentaryCurrentConsumptionOrProduction(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 6 + + // return the voltage phase details at the grid connection point + // + Voltage(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 7 + + // return frequency at the grid connection point + // + Frequency(entity spineapi.EntityRemoteInterface) (float64, error) +} diff --git a/ucmgcp/events.go b/ucmgcp/events.go new file mode 100644 index 0000000..4198b7c --- /dev/null +++ b/ucmgcp/events.go @@ -0,0 +1,136 @@ +package ucmgcp + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCMGCP) HandleEvent(payload spineapi.EventPayload) { + // only about events from an SGMW entity or device changes for this remote device + + entityType := model.EntityTypeTypeGridConnectionPointOfPremises + if !util.IsPayloadForEntityType(payload, entityType) { + return + } + + if util.IsEntityTypeConnected(payload, entityType) { + e.gridConnected(payload.Ski, payload.Entity) + return + } + + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.gridConfigurationDescriptionDataUpdate(payload.Entity) + case *model.MeasurementDescriptionListDataType: + e.gridMeasurementDescriptionDataUpdate(payload.Entity) + case *model.MeasurementListDataType: + e.gridMeasurementDataUpdate(payload.Ski, payload.Entity) + } +} + +// process required steps when a grid device is connected +func (e *UCMGCP) gridConnected(ski string, entity spineapi.EntityRemoteInterface) { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if _, err := deviceConfiguration.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get configuration data + if _, err := deviceConfiguration.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + } + + if electricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := electricalConnection.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get electrical connection parameter + if _, err := electricalConnection.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := electricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Error(err) + } + } + + if measurement, err := util.Measurement(e.service, entity); err == nil { + if _, err := measurement.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get measurement parameters + if _, err := measurement.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := measurement.RequestConstraints(); err != nil { + logging.Log().Error(err) + } + } +} + +// the configuration key description data of an SMGW was updated +func (e *UCMGCP) gridConfigurationDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + // key value descriptions received, now get the data + if _, err := deviceConfiguration.RequestKeyValues(); err != nil { + logging.Log().Error("Error getting configuration key values:", err) + } + } +} + +// the measurement descriptiondata of an SMGW was updated +func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if measurement, err := util.Measurement(e.service, entity); err == nil { + // measurement descriptions received, now get the data + if _, err := measurement.RequestValues(); err != nil { + logging.Log().Error("Error getting measurement list values:", err) + } + } +} + +// the measurement data of an SMGW was updated +func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 2 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMGCPPowerTotalMeasurementDataUpdate) + } + + // Scenario 3 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridFeedIn); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMGCPGridFeedInMeasurementDataUpdate) + } + + // Scenario 4 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridConsumption); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) + } + + // Scenario 5 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMGCPCurrentMeasurementDataUpdate) + } + + // Scenario 6 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMGCPVoltageMeasurementDataUpdate) + } + + // Scenario 7 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMGCPFrequencyMeasurementDataUpdate) + } + +} diff --git a/ucmgcp/events_test.go b/ucmgcp/events_test.go new file mode 100644 index 0000000..0a788eb --- /dev/null +++ b/ucmgcp/events_test.go @@ -0,0 +1,111 @@ +package ucmgcp + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCMGCPSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.smgwEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCMGCPSuite) Test_gridMeasurementDataUpdate() { + s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeGridFeedIn), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeGridConsumption), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(3)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(4)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(5)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACFrequency), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(3)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(4)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(5)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) + +} diff --git a/ucmgcp/public.go b/ucmgcp/public.go new file mode 100644 index 0000000..7237a15 --- /dev/null +++ b/ucmgcp/public.go @@ -0,0 +1,315 @@ +package ucmgcp + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// Scenario 1 + +// return the current power limitation factor +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement, err := util.Measurement(e.service, entity) + if err != nil || measurement == nil { + return 0, err + } + + keyname := model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return 0, err + } + + // check if device configuration description has curtailment limit factor key name + _, err = deviceConfiguration.GetDescriptionForKeyName(keyname) + if err != nil { + return 0, err + } + + data, err := deviceConfiguration.GetKeyValueForKeyName(keyname, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) + if err != nil { + return 0, err + } + + if data == nil { + return 0, features.ErrDataNotAvailable + } + + value := data.(*model.ScaledNumberType) + return value.GetValue(), nil +} + +// Scenario 2 + +// return the momentary power consumption or production at the grid connection point +// +// - positive values are used for consumption +// - negative values are used for production +func (e *UCMGCP) MomentaryPowerConsumptionOrProduction(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypePower + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACPowerTotal + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume there is only one value + mId := data[0].MeasurementId + value := data[0].Value + if mId == nil || value == nil { + return 0, features.ErrDataNotAvailable + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return 0, err + } + + // according to UC_TS_MonitoringOfGridConnectionPoint 3.2.2.2.4.1 + // positive values are with description "PositiveEnergyDirection" value "consume" + // but we verify this + desc, err := electricalConnection.GetDescriptionForMeasurementId(*mId) + if err != nil { + return 0, err + } + + // if energy direction is not consume, report an error + if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { + return 0, features.ErrMissingData + } + + return value.GetValue(), nil +} + +// Scenario 3 + +// return the total feed in energy at the grid connection point +// +// - negative values are used for production +func (e *UCMGCP) TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeEnergy + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeGridFeedIn + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume thre is only one result + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// Scenario 4 + +// return the total consumption energy at the grid connection point +// +// - positive values are used for consumption +func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeEnergy + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeGridConsumption + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume thre is only one result + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// Scenario 5 + +// return the momentary current consumption or production at the grid connection point +// +// - positive values are used for consumption +// - negative values are used for production +func (e *UCMGCP) MomentaryCurrentConsumptionOrProduction(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeCurrent + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACCurrent + values, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return nil, err + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return nil, err + } + + var phaseA, phaseB, phaseC float64 + + for _, item := range values { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || param.AcMeasuredPhases == nil { + continue + } + + value := item.Value.GetValue() + + // according to UC_TS_MonitoringOfGridConnectionPoint 3.2.1.3.2.4 + // positive values are with description "PositiveEnergyDirection" value "consume" + // but we should verify this + if desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { + // if energy direction is not consume, invert it + if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { + return nil, err + } + } + + switch *param.AcMeasuredPhases { + case model.ElectricalConnectionPhaseNameTypeA: + phaseA = value + case model.ElectricalConnectionPhaseNameTypeB: + phaseB = value + case model.ElectricalConnectionPhaseNameTypeC: + phaseC = value + } + } + + return []float64{phaseA, phaseB, phaseC}, nil +} + +// Scenario 6 + +// return the voltage phase details at the grid connection point +func (e *UCMGCP) Voltage(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeVoltage + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACVoltage + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return nil, err + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return nil, err + } + + var phaseA, phaseB, phaseC float64 + + for _, item := range data { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || param.AcMeasuredPhases == nil { + continue + } + + value := item.Value.GetValue() + + switch *param.AcMeasuredPhases { + case model.ElectricalConnectionPhaseNameTypeA: + phaseA = value + case model.ElectricalConnectionPhaseNameTypeB: + phaseB = value + case model.ElectricalConnectionPhaseNameTypeC: + phaseC = value + } + } + + return []float64{phaseA, phaseB, phaseC}, nil +} + +// Scenario 7 + +// return frequency at the grid connection point +func (e *UCMGCP) Frequency(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeFrequency + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACFrequency + item, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // take the first item + value := item[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// helper + +func (e *UCMGCP) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || + (entity.EntityType() != model.EntityTypeTypeCEM && entity.EntityType() != model.EntityTypeTypeGridConnectionPointOfPremises) { + return false + } + + return true +} + +func (e *UCMGCP) getValuesForTypeCommodityScope( + entity spineapi.EntityRemoteInterface, + measurement model.MeasurementTypeType, + commodity model.CommodityTypeType, + scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { + if entity == nil || !e.isCompatibleEntity(entity) { + return nil, api.ErrNoCompatibleEntity + } + + measurementFeature, err := util.Measurement(e.service, entity) + if err != nil || measurementFeature == nil { + return nil, err + } + + return measurementFeature.GetValuesForTypeCommodityScope(measurement, commodity, scope) +} diff --git a/ucmgcp/public_test.go b/ucmgcp/public_test.go new file mode 100644 index 0000000..0b83913 --- /dev/null +++ b/ucmgcp/public_test.go @@ -0,0 +1,464 @@ +package ucmgcp + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCMGCPSuite) Test_PowerLimitationFactor() { + data, err := s.sut.PowerLimitationFactor(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.PowerLimitationFactor(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.PowerLimitationFactor(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(10), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.PowerLimitationFactor(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMGCPSuite) Test_MomentaryPowerConsumptionOrProduction() { + data, err := s.sut.MomentaryPowerConsumptionOrProduction(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + elDescData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + PositiveEnergyDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMGCPSuite) Test_TotalFeedInEnergy() { + data, err := s.sut.TotalFeedInEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalFeedInEnergy(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeGridFeedIn), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalFeedInEnergy(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalFeedInEnergy(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMGCPSuite) Test_TotalConsumedEnergy() { + data, err := s.sut.TotalConsumedEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalConsumedEnergy(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeGridConsumption), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalConsumedEnergy(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalConsumedEnergy(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMGCPSuite) Test_MomentaryCurrentConsumptionOrProduction() { + data, err := s.sut.MomentaryCurrentConsumptionOrProduction(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + elDescData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + PositiveEnergyDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{10, 10, 10}, data) + +} + +func (s *UCMGCPSuite) Test_Voltage() { + data, err := s.sut.Voltage(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.Voltage(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Voltage(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(230), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(230), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(230), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Voltage(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{0, 0, 0}, data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Voltage(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{230, 230, 230}, data) +} + +func (s *UCMGCPSuite) Test_Frequency() { + data, err := s.sut.Frequency(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.Frequency(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACFrequency), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Frequency(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(50), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Frequency(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 50.0, data) +} diff --git a/ucmgcp/results.go b/ucmgcp/results.go new file mode 100644 index 0000000..f04162b --- /dev/null +++ b/ucmgcp/results.go @@ -0,0 +1,8 @@ +package ucmgcp + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCMGCP) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go new file mode 100644 index 0000000..17c5bfc --- /dev/null +++ b/ucmgcp/testhelper_test.go @@ -0,0 +1,183 @@ +package ucmgcp + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestMGCPSuite(t *testing.T) { + suite.Run(t, new(UCMGCPSuite)) +} + +type UCMGCPSuite struct { + suite.Suite + + sut *UCMGCP + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + smgwEntity spineapi.EntityRemoteInterface +} + +func (s *UCMGCPSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + s.remoteDevice, s.smgwEntity = setupDevices(s.service, s.T()) + s.sut = NewUCMGCP(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeMeasurement, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementConstraintsListData, + model.FunctionTypeMeasurementListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionDescriptionListData, + }, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeGridConnectionPointOfPremises), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go new file mode 100644 index 0000000..c31c768 --- /dev/null +++ b/ucmgcp/ucmgcp.go @@ -0,0 +1,112 @@ +package ucmgcp + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCMGCP struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCMGCPInterface = (*UCMGCP)(nil) + +func NewUCMGCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCMGCP { + uc := &UCMGCP{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCMGCP) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeMonitoringOfGridConnectionPoint +} + +func (e *UCMGCP) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } +} + +func (e *UCMGCP) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeMonitoringAppliance, + e.UseCaseName(), + model.SpecificationVersionType("1.0.0"), + "release", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCMGCP) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeGridConnectionPoint, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{2, 3, 4}, + []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + ) { + return false, nil + } + + // check if measurement description contain data for the required scope + measurement, err := util.Measurement(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + _, err1 := measurement.GetDescriptionsForScope(model.ScopeTypeTypeACPower) + _, err2 := measurement.GetDescriptionsForScope(model.ScopeTypeTypeGridFeedIn) + _, err3 := measurement.GetDescriptionsForScope(model.ScopeTypeTypeGridConsumption) + if err1 != nil || err2 != nil || err3 != nil { + return false, features.ErrDataNotAvailable + } + + // check if electrical connection descriptions is provided + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + if _, err = electricalConnection.GetDescriptions(); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucmgcp/ucmgcp_test.go b/ucmgcp/ucmgcp_test.go new file mode 100644 index 0000000..5949462 --- /dev/null +++ b/ucmgcp/ucmgcp_test.go @@ -0,0 +1,82 @@ +package ucmgcp + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCMGCPSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeGridConnectionPoint), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeMonitoringOfGridConnectionPoint), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{2, 3, 4}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeGridFeedIn), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeGridConsumption), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.smgwEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + elData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + elFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.smgwEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} From 7efe3690ab7b46e0a29398c148b78e74d65066fd Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 19:20:10 +0100 Subject: [PATCH 086/227] Minor code update to MGCP --- ucmgcp/events.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 4198b7c..5ef6082 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -18,7 +18,7 @@ func (e *UCMGCP) HandleEvent(payload spineapi.EventPayload) { } if util.IsEntityTypeConnected(payload, entityType) { - e.gridConnected(payload.Ski, payload.Entity) + e.gridConnected(payload.Entity) return } @@ -38,7 +38,7 @@ func (e *UCMGCP) HandleEvent(payload spineapi.EventPayload) { } // process required steps when a grid device is connected -func (e *UCMGCP) gridConnected(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCMGCP) gridConnected(entity spineapi.EntityRemoteInterface) { if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { if _, err := deviceConfiguration.Subscribe(); err != nil { logging.Log().Error(err) From e8b516e2b3bb4facfb0a94e53086b31f5f9f366b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 23 Feb 2024 19:42:54 +0100 Subject: [PATCH 087/227] Add VAPD use case --- api/types.go | 21 ++++ inverterpvvis/api.go | 7 -- inverterpvvis/events.go | 157 ------------------------------ inverterpvvis/invertervis.go | 44 --------- inverterpvvis/public.go | 106 -------------------- inverterpvvis/results.go | 8 -- inverterpvvis/scenario.go | 97 ------------------ ucvapd/api.go | 28 ++++++ ucvapd/events.go | 131 +++++++++++++++++++++++++ ucvapd/events_test.go | 81 +++++++++++++++ ucvapd/public.go | 131 +++++++++++++++++++++++++ ucvapd/public_test.go | 137 ++++++++++++++++++++++++++ ucvapd/results.go | 8 ++ ucvapd/testhelper_test.go | 184 +++++++++++++++++++++++++++++++++++ ucvapd/ucvapd.go | 124 +++++++++++++++++++++++ ucvapd/ucvapd_test.go | 111 +++++++++++++++++++++ 16 files changed, 956 insertions(+), 419 deletions(-) delete mode 100644 inverterpvvis/api.go delete mode 100644 inverterpvvis/events.go delete mode 100644 inverterpvvis/invertervis.go delete mode 100644 inverterpvvis/public.go delete mode 100644 inverterpvvis/results.go delete mode 100644 inverterpvvis/scenario.go create mode 100644 ucvapd/api.go create mode 100644 ucvapd/events.go create mode 100644 ucvapd/events_test.go create mode 100644 ucvapd/public.go create mode 100644 ucvapd/public_test.go create mode 100644 ucvapd/results.go create mode 100644 ucvapd/testhelper_test.go create mode 100644 ucvapd/ucvapd.go create mode 100644 ucvapd/ucvapd_test.go diff --git a/api/types.go b/api/types.go index 2974757..b73173d 100644 --- a/api/types.go +++ b/api/types.go @@ -329,6 +329,27 @@ const ( // // Note: the referred data may be updated together with UCOPEVLoadControlLimitDataUpdate UCOSCEVLoadControlLimitDataUpdate UseCaseEventType = "ucOSCEVLoadControlLimitDataUpdate" + + // UCVAPD + + // PV System total power data updated + // + // Use Case VAPD, Scenario 1 + // + // Note: the referred data may be updated together with UCVAPDYieldTotalMeasurementDataUpdate + UCVAPDPowerTotalMeasurementDataUpdate UseCaseEventType = "ucVAPDPowerTotalMeasurementDataUpdate" + + // PV System nominal peak power data updated + // + // Use Case VAPD, Scenario 2 + UCVAPDPeakPowerDataUpdate UseCaseEventType = "ucVAPDPeakPowerDataUpdate" + + // PV System total yield data updated + // + // Use Case VAPD, Scenario 3 + // + // Note: the referred data may be updated together with UCVAPDPowerTotalMeasurementDataUpdate + UCVAPDYieldTotalMeasurementDataUpdate UseCaseEventType = "ucVAPDYieldTotalMeasurementDataUpdate" ) var ErrNoCompatibleEntity = errors.New("entity is not an compatible entity") diff --git a/inverterpvvis/api.go b/inverterpvvis/api.go deleted file mode 100644 index 2ded92a..0000000 --- a/inverterpvvis/api.go +++ /dev/null @@ -1,7 +0,0 @@ -package inverterpvvis - -type InverterPVVisInterface interface { - CurrentProductionPower() (float64, error) - NominalPeakPower() (float64, error) - TotalPVYield() (float64, error) -} diff --git a/inverterpvvis/events.go b/inverterpvvis/events.go deleted file mode 100644 index 1bdf0ca..0000000 --- a/inverterpvvis/events.go +++ /dev/null @@ -1,157 +0,0 @@ -package inverterpvvis - -import ( - "github.com/enbility/eebus-go/features" - "github.com/enbility/ship-go/logging" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -// Internal EventHandler Interface for the CEM -func (i *InverterPVVis) HandleEvent(payload api.EventPayload) { - // we only care about the registered SKI - if payload.Ski != i.ski { - return - } - - // we care only about events for this remote device - if payload.Device != nil && payload.Device.Ski() != i.ski { - return - } - - switch payload.EventType { - case api.EventTypeDeviceChange: - switch payload.ChangeType { - case api.ElementChangeRemove: - i.inverterDisconnected() - } - - case api.EventTypeEntityChange: - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeBatterySystem { - return - } - - switch payload.ChangeType { - case api.ElementChangeAdd: - i.inverterConnected(payload.Ski, payload.Entity) - - case api.ElementChangeRemove: - i.inverterDisconnected() - } - - case api.EventTypeDataChange: - if payload.ChangeType != api.ElementChangeUpdate { - return - } - - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeBatterySystem { - return - } - - switch payload.Data.(type) { - case *model.DeviceConfigurationKeyValueDescriptionListDataType: - if i.inverterDeviceConfiguration == nil { - break - } - - // key value descriptions received, now get the data - if _, err := i.inverterDeviceConfiguration.RequestKeyValues(); err != nil { - logging.Log().Error("Error getting configuration key values:", err) - } - - case *model.ElectricalConnectionParameterDescriptionListDataType: - if i.inverterElectricalConnection == nil { - break - } - if _, err := i.inverterElectricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log().Error("Error getting electrical permitted values:", err) - } - - case *model.ElectricalConnectionDescriptionListDataType: - if i.inverterElectricalConnection == nil { - break - } - if _, err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Error("Error getting electrical permitted values:", err) - } - - case *model.MeasurementDescriptionListDataType: - if i.inverterMeasurement == nil { - break - } - if _, err := i.inverterMeasurement.RequestValues(); err != nil { - logging.Log().Error("Error getting measurement list values:", err) - } - } - } -} - -// process required steps when a pv device entity is connected -func (e *InverterPVVis) inverterConnected(ski string, entity api.EntityRemoteInterface) { - e.inverterEntity = entity - localDevice := e.service.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f1, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - e.inverterElectricalConnection = f1 - - f2, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - e.inverterMeasurement = f2 - - f3, err := features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - e.inverterDeviceConfiguration = f3 - - // subscribe - if _, err := e.inverterDeviceConfiguration.Subscribe(); err != nil { - logging.Log().Error(err) - } - if _, err := e.inverterElectricalConnection.Subscribe(); err != nil { - logging.Log().Error(err) - } - if _, err := e.inverterMeasurement.Subscribe(); err != nil { - logging.Log().Error(err) - } - - // get device configuration data - if _, err := e.inverterDeviceConfiguration.RequestDescriptions(); err != nil { - logging.Log().Error(err) - } - - // get electrical connection parameter - if _, err := e.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Error(err) - } - - if _, err := e.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log().Error(err) - } - - // get measurement parameters - if _, err := e.inverterMeasurement.RequestDescriptions(); err != nil { - logging.Log().Error(err) - } - - if _, err := e.inverterMeasurement.RequestConstraints(); err != nil { - logging.Log().Error(err) - } -} - -// a pv device entity was disconnected -func (e *InverterPVVis) inverterDisconnected() { - e.inverterMeasurement = nil - - e.inverterElectricalConnection = nil - e.inverterMeasurement = nil - e.inverterDeviceConfiguration = nil -} diff --git a/inverterpvvis/invertervis.go b/inverterpvvis/invertervis.go deleted file mode 100644 index ac89ce5..0000000 --- a/inverterpvvis/invertervis.go +++ /dev/null @@ -1,44 +0,0 @@ -package inverterpvvis - -import ( - eebusapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/util" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" -) - -type InverterPVVis struct { - entity spineapi.EntityLocalInterface - - service eebusapi.ServiceInterface - - inverterEntity spineapi.EntityRemoteInterface - inverterDeviceConfiguration *features.DeviceConfiguration - inverterElectricalConnection *features.ElectricalConnection - inverterMeasurement *features.Measurement - - ski string -} - -var _ InverterPVVisInterface = (*InverterPVVis)(nil) - -// Add InverterPVVis support -func NewInverterPVVis(service eebusapi.ServiceInterface, details *shipapi.ServiceDetails) *InverterPVVis { - ski := util.NormalizeSKI(details.SKI()) - - localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - inverter := &InverterPVVis{ - service: service, - entity: localEntity, - ski: ski, - } - _ = spine.Events.Subscribe(inverter) - - service.RegisterRemoteSKI(ski, true) - - return inverter -} diff --git a/inverterpvvis/public.go b/inverterpvvis/public.go deleted file mode 100644 index 8ebd01c..0000000 --- a/inverterpvvis/public.go +++ /dev/null @@ -1,106 +0,0 @@ -package inverterpvvis - -import ( - "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" - "github.com/enbility/spine-go/model" -) - -// return the current photovoltaic production power (W) -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (i *InverterPVVis) CurrentProductionPower() (float64, error) { - measurement := model.MeasurementTypeTypePower - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACPowerTotal - - data, err := i.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // we assume there is only one value - mId := data[0].MeasurementId - value := data[0].Value - if mId == nil || value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), nil -} - -// return the nominal photovoltaic peak power (W) -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (i *InverterPVVis) NominalPeakPower() (float64, error) { - if i.inverterEntity == nil { - return 0, util.ErrDeviceDisconnected - } - - if i.inverterDeviceConfiguration == nil { - return 0, features.ErrDataNotAvailable - } - - _, err := i.inverterDeviceConfiguration.GetDescriptionForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem) - if err != nil { - return 0, err - } - - data, err := i.inverterDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) - if err != nil { - return 0, err - } - - if data == nil { - return 0, features.ErrDataNotAvailable - } - - value := data.(*model.ScaledNumberType) - - if value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), nil -} - -// return the total photovoltaic yield (Wh) -// -// possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available -// - and others -func (i *InverterPVVis) TotalPVYield() (float64, error) { - measurement := model.MeasurementTypeTypeEnergy - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACYieldTotal - data, err := i.getValuesForTypeCommodityScope(measurement, commodity, scope) - if err != nil { - return 0, err - } - - // we assume thre is only one result - value := data[0].Value - if value == nil { - return 0, features.ErrDataNotAvailable - } - - return value.GetValue(), nil -} - -// helper - -func (i *InverterPVVis) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - if i.inverterEntity == nil { - return nil, util.ErrDeviceDisconnected - } - - if i.inverterMeasurement == nil { - return nil, features.ErrDataNotAvailable - } - - return i.inverterMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) -} diff --git a/inverterpvvis/results.go b/inverterpvvis/results.go deleted file mode 100644 index 48657e6..0000000 --- a/inverterpvvis/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package inverterpvvis - -import ( - "github.com/enbility/spine-go/api" -) - -func (i *InverterPVVis) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/inverterpvvis/scenario.go b/inverterpvvis/scenario.go deleted file mode 100644 index edabf11..0000000 --- a/inverterpvvis/scenario.go +++ /dev/null @@ -1,97 +0,0 @@ -package inverterpvvis - -import ( - "sync" - - "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -type InverterPVVisScenarioImpl struct { - *api.Solution - - remoteDevices map[string]*InverterPVVis - - mux sync.Mutex -} - -var _ api.SolutionInterface = (*InverterPVVisScenarioImpl)(nil) - -func NewInverterVisScenario(service eebusapi.ServiceInterface) *InverterPVVisScenarioImpl { - return &InverterPVVisScenarioImpl{ - Solution: api.NewSolution(service), - remoteDevices: make(map[string]*InverterPVVis), - } -} - -// adds all the supported features to the local entity -func (i *InverterPVVisScenarioImpl) AddFeatures() { - localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - // client features - var clientFeatures = []model.FeatureTypeType{ - model.FeatureTypeTypeElectricalConnection, - model.FeatureTypeTypeMeasurement, - } - - for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(i) - } -} - -// add supported inverter usecases -func (i *InverterPVVisScenarioImpl) AddUseCases() { - localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeVisualizationAppliance, - model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData, - model.SpecificationVersionType("1.0.0"), - "RC1", - true, - []model.UseCaseScenarioSupportType{1, 2, 3}) -} - -func (i *InverterPVVisScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { - // TODO: invertervis should be stored per remote SKI and - // only be set for the SKI if the device supports it - i.mux.Lock() - defer i.mux.Unlock() - - if em, ok := i.remoteDevices[details.SKI()]; ok { - return em - } - - inverter := NewInverterPVVis(i.Service, details) - i.remoteDevices[details.SKI()] = inverter - return inverter -} - -func (i *InverterPVVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { - i.mux.Lock() - defer i.mux.Unlock() - - delete(i.remoteDevices, remoteDeviceSki) - - i.Service.RegisterRemoteSKI(remoteDeviceSki, false) -} - -func (i *InverterPVVisScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { - i.mux.Lock() - defer i.mux.Unlock() - - if errorMsg.DeviceRemote == nil { - return - } - - em, ok := i.remoteDevices[errorMsg.DeviceRemote.Ski()] - if !ok { - return - } - - em.HandleResult(errorMsg) -} diff --git a/ucvapd/api.go b/ucvapd/api.go new file mode 100644 index 0000000..555c64f --- /dev/null +++ b/ucvapd/api.go @@ -0,0 +1,28 @@ +package ucvapd + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +//go:generate mockery + +// interface for the EVSE Commissioning and Configuration UseCase +type UCVAPDInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current production power + CurrentProductionPower(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 2 + + // return the nominal peak power + NominalPeakPower(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 3 + + // return total PV yield + TotalPVYield(entity spineapi.EntityRemoteInterface) (float64, error) +} diff --git a/ucvapd/events.go b/ucvapd/events.go new file mode 100644 index 0000000..523da08 --- /dev/null +++ b/ucvapd/events.go @@ -0,0 +1,131 @@ +package ucvapd + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCVAPD) HandleEvent(payload spineapi.EventPayload) { + // only about events from an SGMW entity or device changes for this remote device + + entityType := model.EntityTypeTypePVSystem + if !util.IsPayloadForEntityType(payload, entityType) { + return + } + + if util.IsEntityTypeConnected(payload, entityType) { + e.inverterConnected(payload.Entity) + return + } + + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.inverterConfigurationDescriptionDataUpdate(payload.Entity) + case *model.DeviceConfigurationKeyValueListDataType: + e.inverterConfigurationDataUpdate(payload.Ski, payload.Entity) + case *model.MeasurementDescriptionListDataType: + e.inverterMeasurementDescriptionDataUpdate(payload.Entity) + case *model.MeasurementListDataType: + e.inverterMeasurementDataUpdate(payload.Ski, payload.Entity) + } +} + +// process required steps when a grid device is connected +func (e *UCVAPD) inverterConnected(entity spineapi.EntityRemoteInterface) { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if _, err := deviceConfiguration.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get configuration data + if _, err := deviceConfiguration.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + } + + if electricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := electricalConnection.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get electrical connection parameter + if _, err := electricalConnection.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := electricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := electricalConnection.RequestPermittedValueSets(); err != nil { + logging.Log().Error(err) + } + } + + if measurement, err := util.Measurement(e.service, entity); err == nil { + if _, err := measurement.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get measurement parameters + if _, err := measurement.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := measurement.RequestConstraints(); err != nil { + logging.Log().Error(err) + } + } +} + +// the configuration key description data of an SMGW was updated +func (e *UCVAPD) inverterConfigurationDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + // key value descriptions received, now get the data + if _, err := deviceConfiguration.RequestKeyValues(); err != nil { + logging.Log().Error("Error getting configuration key values:", err) + } + } +} + +// the measurement data of an SMGW was updated +func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 1 + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if _, err := deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVAPDPeakPowerDataUpdate) + } + } +} + +// the measurement descriptiondata of an SMGW was updated +func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if measurement, err := util.Measurement(e.service, entity); err == nil { + // measurement descriptions received, now get the data + if _, err := measurement.RequestValues(); err != nil { + logging.Log().Error("Error getting measurement list values:", err) + } + } +} + +// the measurement data of an SMGW was updated +func (e *UCVAPD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 2 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVAPDPowerTotalMeasurementDataUpdate) + } + + // Scenario 3 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACYieldTotal); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVAPDYieldTotalMeasurementDataUpdate) + } +} diff --git a/ucvapd/events_test.go b/ucvapd/events_test.go new file mode 100644 index 0000000..7aa3004 --- /dev/null +++ b/ucvapd/events_test.go @@ -0,0 +1,81 @@ +package ucvapd + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCVAPDSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.pvEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { + s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACYieldTotal), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) +} diff --git a/ucvapd/public.go b/ucvapd/public.go new file mode 100644 index 0000000..45b4ac8 --- /dev/null +++ b/ucvapd/public.go @@ -0,0 +1,131 @@ +package ucvapd + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the current photovoltaic production power (W) +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCVAPD) CurrentProductionPower(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypePower + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACPowerTotal + + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume there is only one value + mId := data[0].MeasurementId + value := data[0].Value + if mId == nil || value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// return the nominal photovoltaic peak power (W) +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCVAPD) NominalPeakPower(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil { + return 0, features.ErrFunctionNotSupported + } + + keyName := model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem + if _, err = deviceConfiguration.GetDescriptionForKeyName(keyName); err != nil { + return 0, err + } + + data, err := deviceConfiguration.GetKeyValueForKeyName(keyName, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) + if err != nil { + return 0, err + } + + if data == nil { + return 0, features.ErrDataNotAvailable + } + + value := data.(*model.ScaledNumberType) + + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// return the total photovoltaic yield (Wh) +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCVAPD) TotalPVYield(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeEnergy + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACYieldTotal + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume thre is only one result + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// helper + +func (e *UCVAPD) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || + (entity.EntityType() != model.EntityTypeTypePVSystem) { + return false + } + + return true +} + +func (e *UCVAPD) getValuesForTypeCommodityScope( + entity spineapi.EntityRemoteInterface, + measurement model.MeasurementTypeType, + commodity model.CommodityTypeType, + scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { + if entity == nil { + return nil, util.ErrDeviceDisconnected + } + + measurementF, err := util.Measurement(e.service, entity) + if err != nil { + return nil, features.ErrFunctionNotSupported + } + + return measurementF.GetValuesForTypeCommodityScope(measurement, commodity, scope) +} diff --git a/ucvapd/public_test.go b/ucvapd/public_test.go new file mode 100644 index 0000000..a8308cb --- /dev/null +++ b/ucvapd/public_test.go @@ -0,0 +1,137 @@ +package ucvapd + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCVAPDSuite) Test_CurrentProductionPower() { + data, err := s.sut.CurrentProductionPower(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.CurrentProductionPower(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentProductionPower(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentProductionPower(s.pvEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCVAPDSuite) Test_NominalPeakPower() { + data, err := s.sut.NominalPeakPower(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.NominalPeakPower(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + confData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem), + }, + }, + } + + confFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := confFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, confData, nil, nil) + assert.Nil(s.T(), fErr) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(10), + }, + }, + }, + } + fErr = confFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.NominalPeakPower(s.pvEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCVAPDSuite) Test_TotalPVYield() { + data, err := s.sut.TotalPVYield(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalPVYield(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACYieldTotal), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalPVYield(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalPVYield(s.pvEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} diff --git a/ucvapd/results.go b/ucvapd/results.go new file mode 100644 index 0000000..5a4b0e4 --- /dev/null +++ b/ucvapd/results.go @@ -0,0 +1,8 @@ +package ucvapd + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCVAPD) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go new file mode 100644 index 0000000..f59c18a --- /dev/null +++ b/ucvapd/testhelper_test.go @@ -0,0 +1,184 @@ +package ucvapd + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestVAPDSuite(t *testing.T) { + suite.Run(t, new(UCVAPDSuite)) +} + +type UCVAPDSuite struct { + suite.Suite + + sut *UCVAPD + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + pvEntity spineapi.EntityRemoteInterface +} + +func (s *UCVAPDSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + s.remoteDevice, s.pvEntity = setupDevices(s.service, s.T()) + s.sut = NewUCVAPDV(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + var clientRemoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeMeasurement, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementConstraintsListData, + model.FunctionTypeMeasurementListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + } + + remoteDeviceName := "remote" + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range clientRemoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypePVSystem), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go new file mode 100644 index 0000000..402019c --- /dev/null +++ b/ucvapd/ucvapd.go @@ -0,0 +1,124 @@ +package ucvapd + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCVAPD struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCVAPDInterface = (*UCVAPD)(nil) + +func NewUCVAPDV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCVAPD { + uc := &UCVAPD{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCVAPD) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData +} + +func (e *UCVAPD) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddResultHandler(e) +} + +func (e *UCVAPD) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "RC1", + true, + []model.UseCaseScenarioSupportType{1, 2, 3}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCVAPD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypePVSystem, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3}, + []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + ) { + return false, nil + } + + // check for required features + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if device configuration descriptions contains a required key name + if _, err = deviceConfiguration.GetDescriptionForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem); err != nil { + return false, err + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if electrical connection descriptions and parameter descriptions are available name + if _, err = electricalConnection.GetDescriptions(); err != nil { + return false, err + } + if _, err = electricalConnection.GetParameterDescriptions(); err != nil { + return false, err + } + + // check for required features + measurement, err := util.Measurement(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if measurement descriptions contains a required scope + if _, err = measurement.GetDescriptionsForScope(model.ScopeTypeTypeACPowerTotal); err != nil { + return false, err + } + if _, err = measurement.GetDescriptionsForScope(model.ScopeTypeTypeACYieldTotal); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucvapd/ucvapd_test.go b/ucvapd/ucvapd_test.go new file mode 100644 index 0000000..68c8ef4 --- /dev/null +++ b/ucvapd/ucvapd_test.go @@ -0,0 +1,111 @@ +package ucvapd + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCVAPDSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.pvEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypePVSystem), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeVisualizationOfAggregatedPhotovoltaicData), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + confData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem), + }, + }, + } + + confFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr = confFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, confData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + elData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + elFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.pvEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACYieldTotal), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.pvEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) + +} From 8a8742f64997ea291d5fc28a15360fe4b4756d57 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 14:02:55 +0100 Subject: [PATCH 088/227] Fix wrong or missing features, functions, tests --- api/types.go | 81 ++++++++++++++++++++++++++++++------- uccevc/api.go | 2 +- uccevc/testhelper_test.go | 31 ++++---------- uccevc/uccevc.go | 16 +++++--- ucevcc/events.go | 4 +- ucevcc/testhelper_test.go | 31 ++++---------- ucevcc/ucevcc.go | 1 - ucevcem/api.go | 2 +- ucevcem/testhelper_test.go | 20 ++++----- ucevcem/ucevcem.go | 10 ++++- ucevsecc/testhelper_test.go | 20 ++++----- ucevsoc/api.go | 2 +- ucevsoc/events.go | 6 +-- ucevsoc/testhelper_test.go | 25 ++++-------- ucevsoc/ucevsoc.go | 13 +++--- ucmgcp/testhelper_test.go | 26 ++++-------- ucopev/api.go | 2 +- ucopev/testhelper_test.go | 25 ++++-------- ucopev/ucopev.go | 14 +++++-- ucoscev/api.go | 2 +- ucoscev/testhelper_test.go | 27 +++++-------- ucoscev/ucoscev.go | 14 +++++-- ucvapd/api.go | 2 +- ucvapd/events.go | 4 -- ucvapd/testhelper_test.go | 28 ++++--------- ucvapd/ucvapd.go | 15 ++++--- 26 files changed, 206 insertions(+), 217 deletions(-) diff --git a/api/types.go b/api/types.go index b73173d..e072eb5 100644 --- a/api/types.go +++ b/api/types.go @@ -189,12 +189,15 @@ const ( // EV communication standard data was updated // // Use Case EVCC, Scenario 2 - UCEVCCCommunicationStandardDataUpdate UseCaseEventType = "ucEVCCCommunicationStandardDataUpdate" + // Note: the referred data may be updated together with all other configuration items of this use case + UCEVCCCommunicationStandardConfigurationDataUpdate UseCaseEventType = "ucEVCCCommunicationStandardConfigurationDataUpdate" // EV asymmetric charging data was updated // // Use Case EVCC, Scenario 3 - UCEVCCAsymmetricChargingDataUpdate UseCaseEventType = "ucEVCCAsymmetricChargingDataUpdate" + // + // Note: the referred data may be updated together with all other configuration items of this use case + UCEVCCAsymmetricChargingConfigurationDataUpdate UseCaseEventType = "ucEVCCAsymmetricChargingConfigurationDataUpdate" // EV identificationdata was updated // @@ -221,25 +224,29 @@ const ( // EV number of connected phases data updated // // Use Case EVCEM, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCEVCEMNumberOfConnectedPhasesDataUpdate UseCaseEventType = "ucEVCEMNumberOfConnectedPhasesDataUpdate" // EV current measurement data updated // // Use Case EVCEM, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCEVCEMCurrentMeasurementDataUpdate UseCaseEventType = "ucEVCEMCurrentMeasurementDataUpdate" // EV power measurement data updated // // Use Case EVCEM, Scenario 2 // - // Note: the referred data may be updated together with UCEVCEMCurrentMeasurementDataUpdate + // Note: the referred data may be updated together with all other measurement items of this use case UCEVCEMPowerMeasurementDataUpdate UseCaseEventType = "ucEVCEMCurrentMeasurementDataUpdate" // EV charging energy measurement data updated // // Use Case EVCEM, Scenario 3 // - // Note: the referred data may be updated together with UCEVCEMCurrentMeasurementDataUpdate + // Note: the referred data may be updated together with all other measurement items of this use case UCEVCEMChargingEnergyMeasurementDataUpdate UseCaseEventType = "UCEVCEMChargingEnergyMeasurementDataUpdate" // UCEVSECC @@ -265,60 +272,78 @@ const ( // EV state of charge data was updated // // Use Case EVSOC, Scenario 1 - UCEVSOCStateOfChargeDataUpdate UseCaseEventType = "ucEVSOCStateOfChargeDataUpdate" + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCEVSOCStateOfChargeMeasurementDataUpdate UseCaseEventType = "ucEVSOCStateOfChargeMeasurementDataUpdate" // EV nominal capacity data was updated // // Use Case EVSOC, Scenario 2 - UCEVSOCNominalCapacityDataUpdate UseCaseEventType = "ucEVSOCNominalCapacityDataUpdate" + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCEVSOCNominalCapacityMeasurementDataUpdate UseCaseEventType = "ucEVSOCNominalCapacityMeasurementDataUpdate" // EV state of health data was updated // // Use Case EVSOC, Scenario 3 - UCEVSOCStateOfHealthDataUpdate UseCaseEventType = "ucEVSOCStateOfHealthDataUpdate" + // + // Note: the referred data may be updated together with all other measurement items of this use case + EVSOCStateOfHealthMeasurementDataUpdate UseCaseEventType = "ucEVSOCStateOfHealthMeasurementDataUpdate" // EV actual range data was updated // // Use Case EVSOC, Scenario 4 - UCEVSOCActualRangeDataUpdate UseCaseEventType = "ucEVSOCActualRangeDataUpdate" + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCEVSOCActualRangeMeasurementDataUpdate UseCaseEventType = "ucEVSOCActualRangeMeasurementDataUpdate" // MGCP // Grid momentary power consumption/production data updated // // Use Case MGCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPPowerTotalMeasurementDataUpdate UseCaseEventType = "ucMGCPPowerTotalMeasurementDataUpdate" // MTotal grid feed in energy data updated // // Use Case MGCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPGridFeedInMeasurementDataUpdate UseCaseEventType = "ucMGCPGridFeedInMeasurementDataUpdate" // Total grid consumed energy data updated // // Use Case MGCP, Scenario 4 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPGridConsumptionMeasurementDataUpdate UseCaseEventType = "ucMGCPGridConsumptionMeasurementDataUpdate" // Grid momentary current consumption/production phase detail data updated // // Use Case MGCP, Scenario 5 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPCurrentMeasurementDataUpdate UseCaseEventType = "ucMGCPCurrentMeasurementDataUpdate" // Grid voltage phase detail data updated // // Use Case MGCP, Scenario 6 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPVoltageMeasurementDataUpdate UseCaseEventType = "ucMGCPVoltageMeasurementDataUpdate" // Grid frequency data updated // // Use Case MGCP, Scenario 7 + // + // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPFrequencyMeasurementDataUpdate UseCaseEventType = "ucMGCPFrequencyMeasurementDataUpdate" // UCOPEV // EV load control obligation limit data updated - // - // Use Case OPEV, Scenario 1 UCOPEVLoadControlLimitDataUpdate UseCaseEventType = "ucOPEVLoadControlLimitDataUpdate" // UCOSCEV @@ -326,17 +351,45 @@ const ( // EV load control recommendation limit data updated // // Use Case OSCEV, Scenario 1 - // - // Note: the referred data may be updated together with UCOPEVLoadControlLimitDataUpdate UCOSCEVLoadControlLimitDataUpdate UseCaseEventType = "ucOSCEVLoadControlLimitDataUpdate" + // UCVABD + + // Battery System (dis)charge power data updated + // + // Use Case VABD, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCVABDPowerTotalMeasurementDataUpdate UseCaseEventType = "ucVABDPowerTotalMeasurementDataUpdate" + + // Battery System cumulated charge energy data updated + // + // Use Case VABD, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCVABDChargeMeasurementDataUpdate UseCaseEventType = "ucVABDChargeMeasurementDataUpdate" + + // Battery System cumulated discharge energy data updated + // + // Use Case VABD, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCVABDDischargeMeasurementDataUpdate UseCaseEventType = "ucVABDDischargeMeasurementDataUpdate" + + // Battery System state of charge data updated + // + // Use Case VABD, Scenario 4 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCVABDStateOfChargeMeasurementDataUpdate UseCaseEventType = "ucVABDStateOfChargeMeasurementDataUpdate" + // UCVAPD // PV System total power data updated // // Use Case VAPD, Scenario 1 // - // Note: the referred data may be updated together with UCVAPDYieldTotalMeasurementDataUpdate + // Note: the referred data may be updated together with all other measurement items of this use case UCVAPDPowerTotalMeasurementDataUpdate UseCaseEventType = "ucVAPDPowerTotalMeasurementDataUpdate" // PV System nominal peak power data updated @@ -348,7 +401,7 @@ const ( // // Use Case VAPD, Scenario 3 // - // Note: the referred data may be updated together with UCVAPDPowerTotalMeasurementDataUpdate + // Note: the referred data may be updated together with all other measurement items of this use case UCVAPDYieldTotalMeasurementDataUpdate UseCaseEventType = "ucVAPDYieldTotalMeasurementDataUpdate" ) diff --git a/uccevc/api.go b/uccevc/api.go index 7a99922..cffa9f1 100644 --- a/uccevc/api.go +++ b/uccevc/api.go @@ -7,7 +7,7 @@ import ( //go:generate mockery -// interface for the EVSE Commissioning and Configuration UseCase +// interface for the Coordinated EV Charging UseCase type UCCEVCInterface interface { api.UseCaseInterface diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index 8bfa6db..46aa6e4 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -66,12 +66,12 @@ func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { mockRemoteFeature.EXPECT().Operations().Return(ops).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) s.sut = NewUCCEVC(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evEntity = entities[1] } @@ -82,36 +82,25 @@ func setupDevices( spineapi.DeviceRemoteInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeTimeSeries, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeIncentiveTable, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType - role model.RoleType supportedFcts []model.FunctionType }{ {model.FeatureTypeTypeDeviceConfiguration, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, model.FunctionTypeDeviceConfigurationKeyValueListData, }, }, {model.FeatureTypeTypeTimeSeries, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeTimeSeriesConstraintsListData, model.FunctionTypeTimeSeriesDescriptionListData, @@ -119,7 +108,6 @@ func setupDevices( }, }, {model.FeatureTypeTypeIncentiveTable, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeIncentiveTableConstraintsData, model.FunctionTypeIncentiveTableDescriptionData, @@ -127,7 +115,6 @@ func setupDevices( }, }, {model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeElectricalConnectionParameterDescriptionListData, model.FunctionTypeElectricalConnectionPermittedValueSetListData, @@ -135,10 +122,8 @@ func setupDevices( }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ @@ -158,7 +143,7 @@ func setupDevices( Feature: eebusutil.Ptr(model.AddressFeatureType(index)), }, FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(feature.role), + Role: eebusutil.Ptr(model.RoleTypeServer), SupportedFunction: supportedFcts, }, } diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index d9aaca4..0b45510 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -38,12 +38,16 @@ func (e *UCCEVC) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - f.AddResultHandler(e) - - // server features - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeTimeSeries, + model.FeatureTypeTypeIncentiveTable, + model.FeatureTypeTypeElectricalConnection, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } } func (e *UCCEVC) AddUseCase() { diff --git a/ucevcc/events.go b/ucevcc/events.go index cf87d3c..a645924 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -140,12 +140,12 @@ func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRem // Scenario 2 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCCCommunicationStandardDataUpdate) + e.reader.SpineEvent(ski, entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) } // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCCAsymmetricChargingDataUpdate) + e.reader.SpineEvent(ski, entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) } } diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index bd15f76..346856a 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -64,12 +64,12 @@ func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, s.mockSender, entities = setupDevices(s.service, s.T()) s.sut = NewUCEVCC(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + s.remoteDevice, s.mockSender, entities = setupDevices(s.service, s.T()) s.evEntity = entities[1] } @@ -81,22 +81,7 @@ func setupDevices( *mocks.SenderInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeIdentification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - localEntity.AddFeature(f) - - // writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) - // writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() - // sender := spine.NewSender(writeHandler) + mockSender := mocks.NewSenderInterface(t) defaultMsgCounter := model.MsgCounterType(100) mockSender. @@ -106,7 +91,9 @@ func setupDevices( Maybe() remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, mockSender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType supportedFcts []model.FunctionType }{ @@ -140,10 +127,8 @@ func setupDevices( }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 2419360..2d4f977 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -43,7 +43,6 @@ func (e *UCEVCC) AddFeatures() { model.FeatureTypeTypeElectricalConnection, model.FeatureTypeTypeDeviceDiagnosis, } - for _, feature := range clientFeatures { f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) f.AddResultHandler(e) diff --git a/ucevcem/api.go b/ucevcem/api.go index cf8678a..b186894 100644 --- a/ucevcem/api.go +++ b/ucevcem/api.go @@ -7,7 +7,7 @@ import ( //go:generate mockery -// interface for the EVSE Commissioning and Configuration UseCase +// interface for the EV Charging Electricity Measurement UseCase type UCEVCEMInterface interface { api.UseCaseInterface diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index f17a6b5..d3b2cfa 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -61,12 +61,12 @@ func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) s.sut = NewUCEVCEM(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evEntity = entities[1] } @@ -77,19 +77,15 @@ func setupDevices( spineapi.DeviceRemoteInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType supportedFcts []model.FunctionType }{ @@ -109,10 +105,8 @@ func setupDevices( }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index 61c49a6..9cfc32a 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -36,8 +36,14 @@ func (e *UCEVCEM) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } } func (e *UCEVCEM) AddUseCase() { diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index 47226ff..51eff6e 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -64,12 +64,12 @@ func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) s.sut = NewUCEVSECC(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evseEntity = entities[0] } @@ -80,19 +80,15 @@ func setupDevices( spineapi.DeviceRemoteInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType supportedFcts []model.FunctionType }{ @@ -108,10 +104,8 @@ func setupDevices( }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ diff --git a/ucevsoc/api.go b/ucevsoc/api.go index f3aa873..745a786 100644 --- a/ucevsoc/api.go +++ b/ucevsoc/api.go @@ -7,7 +7,7 @@ import ( //go:generate mockery -// interface for the EVSE Commissioning and Configuration UseCase +// interface for the EV State Of Charge UseCase type UCEVSOCInterface interface { api.UseCaseInterface diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 31ed580..7841698 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -59,16 +59,16 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSOCStateOfChargeDataUpdate) + e.reader.SpineEvent(ski, entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfHealth); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSOCStateOfHealthDataUpdate) + e.reader.SpineEvent(ski, entity, api.EVSOCStateOfHealthMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeTravelRange); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSOCActualRangeDataUpdate) + e.reader.SpineEvent(ski, entity, api.UCEVSOCActualRangeMeasurementDataUpdate) } } diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index a02fccd..89a66b1 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -64,12 +64,12 @@ func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) s.sut = NewUCEVSOC(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evEntity = entities[1] } @@ -80,42 +80,33 @@ func setupDevices( spineapi.DeviceRemoteInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType - role model.RoleType supportedFcts []model.FunctionType }{ {model.FeatureTypeTypeMeasurement, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeMeasurementDescriptionListData, model.FunctionTypeMeasurementListData, }, }, {model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeElectricalConnectionParameterDescriptionListData, }, }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ @@ -135,7 +126,7 @@ func setupDevices( Feature: eebusutil.Ptr(model.AddressFeatureType(index)), }, FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(feature.role), + Role: eebusutil.Ptr(model.RoleTypeServer), SupportedFunction: supportedFcts, }, } diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index f465f14..fa23a8a 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -38,11 +38,14 @@ func (e *UCEVSOC) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - f.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, false, false) - f.AddFunctionType(model.FunctionTypeMeasurementConstraintsListData, false, false) - f.AddFunctionType(model.FunctionTypeMeasurementListData, false, false) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } } func (e *UCEVSOC) AddUseCase() { diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index 17c5bfc..8ccdbec 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -64,10 +64,11 @@ func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.remoteDevice, s.smgwEntity = setupDevices(s.service, s.T()) s.sut = NewUCMGCP(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + s.remoteDevice, s.smgwEntity = setupDevices(s.service, s.T()) } const remoteSki string = "testremoteski" @@ -77,27 +78,19 @@ func setupDevices( spineapi.DeviceRemoteInterface, spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType - role model.RoleType supportedFcts []model.FunctionType }{ {model.FeatureTypeTypeMeasurement, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeMeasurementDescriptionListData, model.FunctionTypeMeasurementConstraintsListData, @@ -105,25 +98,20 @@ func setupDevices( }, }, {model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeElectricalConnectionParameterDescriptionListData, model.FunctionTypeElectricalConnectionDescriptionListData, }, }, {model.FeatureTypeTypeDeviceConfiguration, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, model.FunctionTypeDeviceConfigurationKeyValueListData, }, }, } - - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ @@ -143,7 +131,7 @@ func setupDevices( Feature: eebusutil.Ptr(model.AddressFeatureType(index)), }, FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(feature.role), + Role: eebusutil.Ptr(model.RoleTypeServer), SupportedFunction: supportedFcts, }, } diff --git a/ucopev/api.go b/ucopev/api.go index b31c67e..f98120d 100644 --- a/ucopev/api.go +++ b/ucopev/api.go @@ -8,7 +8,7 @@ import ( //go:generate mockery -// interface for the EVSE Commissioning and Configuration UseCase +// interface for the Overload Protection by EV Charging Current Curtailment UseCase type UCOPEVInterface interface { api.UseCaseInterface diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index a3bdcc2..7417bb9 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -64,12 +64,12 @@ func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) s.sut = NewUCOPEV(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evEntity = entities[1] } @@ -80,25 +80,19 @@ func setupDevices( spineapi.DeviceRemoteInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType - role model.RoleType supportedFcts []model.FunctionType }{ {model.FeatureTypeTypeLoadControl, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeLoadControlLimitDescriptionListData, model.FunctionTypeLoadControlLimitConstraintsListData, @@ -106,17 +100,14 @@ func setupDevices( }, }, {model.FeatureTypeTypeDeviceDiagnosis, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeDeviceDiagnosisStateData, }, }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ @@ -136,7 +127,7 @@ func setupDevices( Feature: eebusutil.Ptr(model.AddressFeatureType(index)), }, FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(feature.role), + Role: eebusutil.Ptr(model.RoleTypeServer), SupportedFunction: supportedFcts, }, } diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index fb838af..0dd57f8 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -38,10 +38,18 @@ func (e *UCOPEV) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeElectricalConnection, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + // server features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, false, false) f.AddResultHandler(e) } diff --git a/ucoscev/api.go b/ucoscev/api.go index 6efc856..c9c238a 100644 --- a/ucoscev/api.go +++ b/ucoscev/api.go @@ -8,7 +8,7 @@ import ( //go:generate mockery -// interface for the EVSE Commissioning and Configuration UseCase +// interface for the Optimization of Self-Consumption During EV Charging UseCase type UCOSCEVInterface interface { api.UseCaseInterface diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index 19a44e2..a8b4007 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -64,12 +64,13 @@ func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - var entities []spineapi.EntityRemoteInterface - - s.remoteDevice, entities = setupDevices(s.service, s.T()) s.sut = NewUCOSCEV(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + var entities []spineapi.EntityRemoteInterface + + s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evEntity = entities[1] } @@ -80,21 +81,15 @@ func setupDevices( spineapi.DeviceRemoteInterface, []spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType role model.RoleType supportedFcts []model.FunctionType @@ -116,16 +111,12 @@ func setupDevices( }, {model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient, - []model.FunctionType{ - model.FunctionTypeDeviceDiagnosisStateData, - }, + []model.FunctionType{}, }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index c36c171..b10fccd 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -38,10 +38,18 @@ func (e *UCOSCEV) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeElectricalConnection, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + // server features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, false, false) f.AddResultHandler(e) } diff --git a/ucvapd/api.go b/ucvapd/api.go index 555c64f..5fb4053 100644 --- a/ucvapd/api.go +++ b/ucvapd/api.go @@ -7,7 +7,7 @@ import ( //go:generate mockery -// interface for the EVSE Commissioning and Configuration UseCase +// interface for the Visualization of Aggregated Photovoltaic Data UseCase type UCVAPDInterface interface { api.UseCaseInterface diff --git a/ucvapd/events.go b/ucvapd/events.go index 523da08..6209a99 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -65,10 +65,6 @@ func (e *UCVAPD) inverterConnected(entity spineapi.EntityRemoteInterface) { if _, err := electricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Error(err) } - - if _, err := electricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log().Error(err) - } } if measurement, err := util.Measurement(e.service, entity); err == nil { diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index f59c18a..575c8d6 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -64,10 +64,11 @@ func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.remoteDevice, s.pvEntity = setupDevices(s.service, s.T()) - s.sut = NewUCVAPDV(s.service, s.service.LocalService(), s) + s.sut = NewUCVAPD(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() + + s.remoteDevice, s.pvEntity = setupDevices(s.service, s.T()) } const remoteSki string = "testremoteski" @@ -77,27 +78,19 @@ func setupDevices( spineapi.DeviceRemoteInterface, spineapi.EntityRemoteInterface) { localDevice := eebusService.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - localEntity.AddFeature(f) - f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + remoteDeviceName := "remote" + + var remoteFeatures = []struct { featureType model.FeatureTypeType - role model.RoleType supportedFcts []model.FunctionType }{ {model.FeatureTypeTypeMeasurement, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeMeasurementDescriptionListData, model.FunctionTypeMeasurementConstraintsListData, @@ -105,15 +98,12 @@ func setupDevices( }, }, {model.FeatureTypeTypeElectricalConnection, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeElectricalConnectionParameterDescriptionListData, model.FunctionTypeElectricalConnectionDescriptionListData, - model.FunctionTypeElectricalConnectionPermittedValueSetListData, }, }, {model.FeatureTypeTypeDeviceConfiguration, - model.RoleTypeServer, []model.FunctionType{ model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, model.FunctionTypeDeviceConfigurationKeyValueListData, @@ -121,10 +111,8 @@ func setupDevices( }, } - remoteDeviceName := "remote" - var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ @@ -144,7 +132,7 @@ func setupDevices( Feature: eebusutil.Ptr(model.AddressFeatureType(index)), }, FeatureType: eebusutil.Ptr(feature.featureType), - Role: eebusutil.Ptr(feature.role), + Role: eebusutil.Ptr(model.RoleTypeServer), SupportedFunction: supportedFcts, }, } diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 402019c..322bea5 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -19,7 +19,7 @@ type UCVAPD struct { var _ UCVAPDInterface = (*UCVAPD)(nil) -func NewUCVAPDV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCVAPD { +func NewUCVAPD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCVAPD { uc := &UCVAPD{ service: service, reader: reader, @@ -38,11 +38,16 @@ func (e *UCVAPD) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - f.AddResultHandler(e) } func (e *UCVAPD) AddUseCase() { From edfeb85ac8e7101574165d0ad387f6d6fc6f63dc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 14:03:38 +0100 Subject: [PATCH 089/227] Add VABD --- inverterbatteryvis/api.go | 8 - inverterbatteryvis/events.go | 132 ---------------- inverterbatteryvis/invertervis.go | 43 ------ inverterbatteryvis/results.go | 8 - inverterbatteryvis/solution.go | 97 ------------ ucvabd/api.go | 33 ++++ ucvabd/events.go | 102 +++++++++++++ ucvabd/events_test.go | 97 ++++++++++++ {inverterbatteryvis => ucvabd}/public.go | 60 ++++++-- ucvabd/public_test.go | 187 +++++++++++++++++++++++ ucvabd/results.go | 8 + ucvabd/testhelper_test.go | 166 ++++++++++++++++++++ ucvabd/ucvabd.go | 117 ++++++++++++++ ucvabd/ucvabd_test.go | 94 ++++++++++++ 14 files changed, 850 insertions(+), 302 deletions(-) delete mode 100644 inverterbatteryvis/api.go delete mode 100644 inverterbatteryvis/events.go delete mode 100644 inverterbatteryvis/invertervis.go delete mode 100644 inverterbatteryvis/results.go delete mode 100644 inverterbatteryvis/solution.go create mode 100644 ucvabd/api.go create mode 100644 ucvabd/events.go create mode 100644 ucvabd/events_test.go rename {inverterbatteryvis => ucvabd}/public.go (55%) create mode 100644 ucvabd/public_test.go create mode 100644 ucvabd/results.go create mode 100644 ucvabd/testhelper_test.go create mode 100644 ucvabd/ucvabd.go create mode 100644 ucvabd/ucvabd_test.go diff --git a/inverterbatteryvis/api.go b/inverterbatteryvis/api.go deleted file mode 100644 index f38ebdf..0000000 --- a/inverterbatteryvis/api.go +++ /dev/null @@ -1,8 +0,0 @@ -package inverterbatteryvis - -type InverterBatteryVisInterface interface { - CurrentDisChargePower() (float64, error) - TotalChargeEnergy() (float64, error) - TotalDischargeEnergy() (float64, error) - CurrentStateOfCharge() (float64, error) -} diff --git a/inverterbatteryvis/events.go b/inverterbatteryvis/events.go deleted file mode 100644 index cd4b52b..0000000 --- a/inverterbatteryvis/events.go +++ /dev/null @@ -1,132 +0,0 @@ -package inverterbatteryvis - -import ( - "github.com/enbility/eebus-go/features" - "github.com/enbility/ship-go/logging" - "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -// Internal EventHandler Interface for the CEM -func (i *InverterBatteryVis) HandleEvent(payload api.EventPayload) { - // we only care about the registered SKI - if payload.Ski != i.ski { - return - } - - // we care only about events for this remote device - if payload.Device != nil && payload.Device.Ski() != i.ski { - return - } - - switch payload.EventType { - case api.EventTypeDeviceChange: - switch payload.ChangeType { - case api.ElementChangeRemove: - i.inverterDisconnected() - } - - case api.EventTypeEntityChange: - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeBatterySystem { - return - } - - switch payload.ChangeType { - case api.ElementChangeAdd: - i.inverterConnected(payload.Ski, payload.Entity) - - case api.ElementChangeRemove: - i.inverterDisconnected() - } - - case api.EventTypeDataChange: - if payload.ChangeType != api.ElementChangeUpdate { - return - } - - entityType := payload.Entity.EntityType() - if entityType != model.EntityTypeTypeBatterySystem { - return - } - - switch payload.Data.(type) { - case *model.ElectricalConnectionParameterDescriptionListDataType: - if i.inverterElectricalConnection == nil { - break - } - if _, err := i.inverterElectricalConnection.RequestPermittedValueSets(); err != nil { - logging.Log().Error("Error getting electrical permitted values:", err) - } - - case *model.ElectricalConnectionDescriptionListDataType: - if i.inverterElectricalConnection == nil { - break - } - if _, err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Error("Error getting electrical permitted values:", err) - } - - case *model.MeasurementDescriptionListDataType: - if i.inverterMeasurement == nil { - break - } - if _, err := i.inverterMeasurement.RequestValues(); err != nil { - logging.Log().Error("Error getting measurement list values:", err) - } - } - } -} - -// process required steps when a battery device entity is connected -func (i *InverterBatteryVis) inverterConnected(ski string, entity api.EntityRemoteInterface) { - i.inverterEntity = entity - localDevice := i.service.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - f1, err := features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - i.inverterElectricalConnection = f1 - - f2, err := features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity) - if err != nil { - return - } - i.inverterMeasurement = f2 - - // subscribe - if _, err := i.inverterElectricalConnection.Subscribe(); err != nil { - logging.Log().Error(err) - } - if _, err := i.inverterMeasurement.Subscribe(); err != nil { - logging.Log().Error(err) - } - - // get electrical connection parameter - if _, err := i.inverterElectricalConnection.RequestDescriptions(); err != nil { - logging.Log().Error(err) - } - - if _, err := i.inverterElectricalConnection.RequestParameterDescriptions(); err != nil { - logging.Log().Error(err) - } - - // get measurement parameters - if _, err := i.inverterMeasurement.RequestDescriptions(); err != nil { - logging.Log().Error(err) - } - - if _, err := i.inverterMeasurement.RequestConstraints(); err != nil { - logging.Log().Error(err) - } -} - -// a battery device entity was disconnected -func (i *InverterBatteryVis) inverterDisconnected() { - i.inverterEntity = nil - - i.inverterElectricalConnection = nil - i.inverterMeasurement = nil -} diff --git a/inverterbatteryvis/invertervis.go b/inverterbatteryvis/invertervis.go deleted file mode 100644 index 7ee8e16..0000000 --- a/inverterbatteryvis/invertervis.go +++ /dev/null @@ -1,43 +0,0 @@ -package inverterbatteryvis - -import ( - "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/util" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" - "github.com/enbility/spine-go/spine" -) - -type InverterBatteryVis struct { - entity spineapi.EntityLocalInterface - - service api.ServiceInterface - - inverterEntity spineapi.EntityRemoteInterface - inverterElectricalConnection *features.ElectricalConnection - inverterMeasurement *features.Measurement - - ski string -} - -var _ InverterBatteryVisInterface = (*InverterBatteryVis)(nil) - -// Add InverterBatteryVis support -func NewInverterBatteryVis(service api.ServiceInterface, details *shipapi.ServiceDetails) *InverterBatteryVis { - ski := util.NormalizeSKI(details.SKI()) - - localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - inverter := &InverterBatteryVis{ - service: service, - entity: localEntity, - ski: ski, - } - _ = spine.Events.Subscribe(inverter) - - service.RegisterRemoteSKI(ski, true) - - return inverter -} diff --git a/inverterbatteryvis/results.go b/inverterbatteryvis/results.go deleted file mode 100644 index 7a679ed..0000000 --- a/inverterbatteryvis/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package inverterbatteryvis - -import ( - "github.com/enbility/spine-go/api" -) - -func (i *InverterBatteryVis) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/inverterbatteryvis/solution.go b/inverterbatteryvis/solution.go deleted file mode 100644 index 5bad74d..0000000 --- a/inverterbatteryvis/solution.go +++ /dev/null @@ -1,97 +0,0 @@ -package inverterbatteryvis - -import ( - "sync" - - "github.com/enbility/cemd/api" - eebusapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -type InverterBatteryVisScenarioImpl struct { - *api.Solution - - remoteDevices map[string]*InverterBatteryVis - - mux sync.Mutex -} - -var _ api.SolutionInterface = (*InverterBatteryVisScenarioImpl)(nil) - -func NewInverterVisScenario(service eebusapi.ServiceInterface) *InverterBatteryVisScenarioImpl { - return &InverterBatteryVisScenarioImpl{ - Solution: api.NewSolution(service), - remoteDevices: make(map[string]*InverterBatteryVis), - } -} - -// adds all the supported features to the local entity -func (i *InverterBatteryVisScenarioImpl) AddFeatures() { - localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - // client features - var clientFeatures = []model.FeatureTypeType{ - model.FeatureTypeTypeElectricalConnection, - model.FeatureTypeTypeMeasurement, - } - - for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(i) - } -} - -// add supported inverter usecases -func (i *InverterBatteryVisScenarioImpl) AddUseCases() { - localEntity := i.Service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - localEntity.AddUseCaseSupport( - model.UseCaseActorTypeVisualizationAppliance, - model.UseCaseNameTypeVisualizationOfAggregatedBatteryData, - model.SpecificationVersionType("1.0.0"), - "RC1", - true, - []model.UseCaseScenarioSupportType{1, 2, 3, 4}) -} - -func (i *InverterBatteryVisScenarioImpl) RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any { - // TODO: invertervis should be stored per remote SKI and - // only be set for the SKI if the device supports it - i.mux.Lock() - defer i.mux.Unlock() - - if em, ok := i.remoteDevices[details.SKI()]; ok { - return em - } - - inverter := NewInverterBatteryVis(i.Service, details) - i.remoteDevices[details.SKI()] = inverter - return inverter -} - -func (i *InverterBatteryVisScenarioImpl) UnRegisterRemoteDevice(remoteDeviceSki string) { - i.mux.Lock() - defer i.mux.Unlock() - - delete(i.remoteDevices, remoteDeviceSki) - - i.Service.RegisterRemoteSKI(remoteDeviceSki, false) -} - -func (i *InverterBatteryVisScenarioImpl) HandleResult(errorMsg spineapi.ResultMessage) { - i.mux.Lock() - defer i.mux.Unlock() - - if errorMsg.DeviceRemote == nil { - return - } - - em, ok := i.remoteDevices[errorMsg.DeviceRemote.Ski()] - if !ok { - return - } - - em.HandleResult(errorMsg) -} diff --git a/ucvabd/api.go b/ucvabd/api.go new file mode 100644 index 0000000..b559b30 --- /dev/null +++ b/ucvabd/api.go @@ -0,0 +1,33 @@ +package ucvabd + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +//go:generate mockery + +// interface for the Visualization of Aggregated Battery Data UseCase +type UCVABDInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current (dis)charging power + CurrentChargePower(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 2 + + // return the cumulated battery system charge energy + TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 3 + + // return the cumulated battery system discharge energy + TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 4 + + // return the current state of charge of the battery system + CurrentStateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) +} diff --git a/ucvabd/events.go b/ucvabd/events.go new file mode 100644 index 0000000..7a73469 --- /dev/null +++ b/ucvabd/events.go @@ -0,0 +1,102 @@ +package ucvabd + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCVABD) HandleEvent(payload spineapi.EventPayload) { + // only about events from an SGMW entity or device changes for this remote device + + entityType := model.EntityTypeTypeElectricityStorageSystem + if !util.IsPayloadForEntityType(payload, entityType) { + return + } + + if util.IsEntityTypeConnected(payload, entityType) { + e.inverterConnected(payload.Entity) + return + } + + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.MeasurementDescriptionListDataType: + e.inverterMeasurementDescriptionDataUpdate(payload.Entity) + case *model.MeasurementListDataType: + e.inverterMeasurementDataUpdate(payload.Ski, payload.Entity) + } +} + +// process required steps when a grid device is connected +func (e *UCVABD) inverterConnected(entity spineapi.EntityRemoteInterface) { + if electricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := electricalConnection.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get electrical connection parameter + if _, err := electricalConnection.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := electricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Error(err) + } + } + + if measurement, err := util.Measurement(e.service, entity); err == nil { + if _, err := measurement.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get measurement parameters + if _, err := measurement.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := measurement.RequestConstraints(); err != nil { + logging.Log().Error(err) + } + } +} + +// the measurement descriptiondata of an SMGW was updated +func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if measurement, err := util.Measurement(e.service, entity); err == nil { + // measurement descriptions received, now get the data + if _, err := measurement.RequestValues(); err != nil { + logging.Log().Error("Error getting measurement list values:", err) + } + } +} + +// the measurement data of an SMGW was updated +func (e *UCVABD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 1 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVABDPowerTotalMeasurementDataUpdate) + } + + // Scenario 2 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVABDChargeMeasurementDataUpdate) + } + + // Scenario 3 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeDischarge); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVABDDischargeMeasurementDataUpdate) + } + + // Scenario 4 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { + e.reader.SpineEvent(ski, entity, api.UCVABDStateOfChargeMeasurementDataUpdate) + } +} diff --git a/ucvabd/events_test.go b/ucvabd/events_test.go new file mode 100644 index 0000000..43b1c67 --- /dev/null +++ b/ucvabd/events_test.go @@ -0,0 +1,97 @@ +package ucvabd + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCVABDSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.batteryEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCVABDSuite) Test_inverterMeasurementDataUpdate() { + s.sut.inverterMeasurementDataUpdate(remoteSki, s.batteryEntity) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeCharge), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeDischarge), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(3)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfCharge), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.inverterMeasurementDataUpdate(remoteSki, s.batteryEntity) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(3)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.inverterMeasurementDataUpdate(remoteSki, s.batteryEntity) +} diff --git a/inverterbatteryvis/public.go b/ucvabd/public.go similarity index 55% rename from inverterbatteryvis/public.go rename to ucvabd/public.go index 6e9ab51..07540ee 100644 --- a/inverterbatteryvis/public.go +++ b/ucvabd/public.go @@ -1,8 +1,10 @@ -package inverterbatteryvis +package ucvabd import ( + "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -14,12 +16,16 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVis) CurrentDisChargePower() (float64, error) { +func (e *UCVABD) CurrentChargePower(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + measurement := model.MeasurementTypeTypePower commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACPowerTotal - data, err := i.getValuesForTypeCommodityScope(measurement, commodity, scope) + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) if err != nil { return 0, err } @@ -39,11 +45,15 @@ func (i *InverterBatteryVis) CurrentDisChargePower() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVis) TotalChargeEnergy() (float64, error) { +func (e *UCVABD) TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeCharge - data, err := i.getValuesForTypeCommodityScope(measurement, commodity, scope) + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) if err != nil { return 0, err } @@ -62,11 +72,15 @@ func (i *InverterBatteryVis) TotalChargeEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVis) TotalDischargeEnergy() (float64, error) { +func (e *UCVABD) TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeDischarge - data, err := i.getValuesForTypeCommodityScope(measurement, commodity, scope) + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) if err != nil { return 0, err } @@ -85,11 +99,15 @@ func (i *InverterBatteryVis) TotalDischargeEnergy() (float64, error) { // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (i *InverterBatteryVis) CurrentStateOfCharge() (float64, error) { +func (e *UCVABD) CurrentStateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { + if !e.isCompatibleEntity(entity) { + return 0, api.ErrNoCompatibleEntity + } + measurement := model.MeasurementTypeTypePercentage commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeStateOfCharge - data, err := i.getValuesForTypeCommodityScope(measurement, commodity, scope) + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) if err != nil { return 0, err } @@ -105,14 +123,28 @@ func (i *InverterBatteryVis) CurrentStateOfCharge() (float64, error) { // helper -func (i *InverterBatteryVis) getValuesForTypeCommodityScope(measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - if i.inverterEntity == nil { +func (e *UCVABD) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { + if entity == nil || + (entity.EntityType() != model.EntityTypeTypeElectricityStorageSystem) { + return false + } + + return true +} + +func (e *UCVABD) getValuesForTypeCommodityScope( + entity spineapi.EntityRemoteInterface, + measurement model.MeasurementTypeType, + commodity model.CommodityTypeType, + scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { + if entity == nil { return nil, util.ErrDeviceDisconnected } - if i.inverterMeasurement == nil { - return nil, features.ErrDataNotAvailable + measurementF, err := util.Measurement(e.service, entity) + if err != nil { + return nil, features.ErrFunctionNotSupported } - return i.inverterMeasurement.GetValuesForTypeCommodityScope(measurement, commodity, scope) + return measurementF.GetValuesForTypeCommodityScope(measurement, commodity, scope) } diff --git a/ucvabd/public_test.go b/ucvabd/public_test.go new file mode 100644 index 0000000..2293142 --- /dev/null +++ b/ucvabd/public_test.go @@ -0,0 +1,187 @@ +package ucvabd + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCVABDSuite) Test_CurrentChargePower() { + data, err := s.sut.CurrentChargePower(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.CurrentChargePower(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentChargePower(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentChargePower(s.batteryEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCVABDSuite) Test_TotalChargeEnergy() { + data, err := s.sut.TotalChargeEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalChargeEnergy(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeCharge), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalChargeEnergy(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalChargeEnergy(s.batteryEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCVABDSuite) Test_TotalDischargeEnergy() { + data, err := s.sut.TotalDischargeEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalDischargeEnergy(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeDischarge), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalDischargeEnergy(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalDischargeEnergy(s.batteryEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCVABDSuite) Test_CurrentStateOfCharge() { + data, err := s.sut.CurrentStateOfCharge(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.CurrentStateOfCharge(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePercentage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfCharge), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentStateOfCharge(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.CurrentStateOfCharge(s.batteryEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} diff --git a/ucvabd/results.go b/ucvabd/results.go new file mode 100644 index 0000000..4525487 --- /dev/null +++ b/ucvabd/results.go @@ -0,0 +1,8 @@ +package ucvabd + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCVABD) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go new file mode 100644 index 0000000..c2faebb --- /dev/null +++ b/ucvabd/testhelper_test.go @@ -0,0 +1,166 @@ +package ucvabd + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestVABDSuite(t *testing.T) { + suite.Run(t, new(UCVABDSuite)) +} + +type UCVABDSuite struct { + suite.Suite + + sut *UCVABD + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + batteryEntity spineapi.EntityRemoteInterface +} + +func (s *UCVABDSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *UCVABDSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + s.sut = NewUCVABD(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + + s.remoteDevice, s.batteryEntity = setupDevices(s.service, s.T()) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeMeasurement, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementConstraintsListData, + model.FunctionTypeMeasurementListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionDescriptionListData, + }, + }, + } + + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeElectricityStorageSystem), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go new file mode 100644 index 0000000..db33a55 --- /dev/null +++ b/ucvabd/ucvabd.go @@ -0,0 +1,117 @@ +package ucvabd + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCVABD struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface +} + +var _ UCVABDInterface = (*UCVABD)(nil) + +func NewUCVABD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCVABD { + uc := &UCVABD{ + service: service, + reader: reader, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCVABD) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeVisualizationOfAggregatedBatteryData +} + +func (e *UCVABD) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } +} + +func (e *UCVABD) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.1"), + "RC1", + true, + []model.UseCaseScenarioSupportType{1, 2, 3}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCVABD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !e.isCompatibleEntity(entity) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypePVSystem, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 4}, + []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + ) { + return false, nil + } + + // check for required features + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if electrical connection descriptions and parameter descriptions are available name + if _, err = electricalConnection.GetDescriptions(); err != nil { + return false, err + } + if _, err = electricalConnection.GetParameterDescriptions(); err != nil { + return false, err + } + + // check for required features + measurement, err := util.Measurement(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + // check if measurement descriptions contains a required scope + if _, err = measurement.GetDescriptionsForScope(model.ScopeTypeTypeACPowerTotal); err != nil { + return false, err + } + if _, err = measurement.GetDescriptionsForScope(model.ScopeTypeTypeStateOfCharge); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucvabd/ucvabd_test.go b/ucvabd/ucvabd_test.go new file mode 100644 index 0000000..55e540c --- /dev/null +++ b/ucvabd/ucvabd_test.go @@ -0,0 +1,94 @@ +package ucvabd + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCVABDSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.batteryEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypePVSystem), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeVisualizationOfAggregatedBatteryData), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 4}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + elData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + elFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.batteryEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeStateOfCharge), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.batteryEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.batteryEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) + +} From 4f95add296bb42ff1dee05cbcce7dec4e9138d19 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 14:05:33 +0100 Subject: [PATCH 090/227] Remove rest of old APIs --- api/api.go | 9 --------- api/types.go | 13 ------------- uccevc/public_scen2.go | 2 +- ucevcc/public.go | 1 - 4 files changed, 1 insertion(+), 24 deletions(-) diff --git a/api/api.go b/api/api.go index 1f315c8..55b14ae 100644 --- a/api/api.go +++ b/api/api.go @@ -1,7 +1,6 @@ package api import ( - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -53,11 +52,3 @@ type UseCaseEventReaderInterface interface { // Inform about a new usecase specific event SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event UseCaseEventType) } - -// Implemented by *Solutions, used by Cem -type SolutionInterface interface { - RegisterRemoteDevice(details *shipapi.ServiceDetails, dataProvider any) any - UnRegisterRemoteDevice(remoteDeviceSki string) - AddFeatures() - AddUseCases() -} diff --git a/api/types.go b/api/types.go index e072eb5..0b1f5af 100644 --- a/api/types.go +++ b/api/types.go @@ -4,7 +4,6 @@ import ( "errors" "time" - "github.com/enbility/eebus-go/api" "github.com/enbility/spine-go/model" ) @@ -406,15 +405,3 @@ const ( ) var ErrNoCompatibleEntity = errors.New("entity is not an compatible entity") -var ErrEVDisconnected = errors.New("ev is disconnected") -var ErrNotSupported = errors.New("function is not supported") - -type Solution struct { - Service api.ServiceInterface -} - -func NewSolution(service api.ServiceInterface) *Solution { - return &Solution{ - Service: service, - } -} diff --git a/uccevc/public_scen2.go b/uccevc/public_scen2.go index 0402470..cfaf6f5 100644 --- a/uccevc/public_scen2.go +++ b/uccevc/public_scen2.go @@ -93,7 +93,7 @@ func (e *UCCEVC) WritePowerLimits(entity spineapi.EntityRemoteInterface, data [] desc, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) if err != nil { - return api.ErrNotSupported + return features.ErrDataNotAvailable } timeSeriesSlots := []model.TimeSeriesSlotType{} diff --git a/ucevcc/public.go b/ucevcc/public.go index 0e9a6fd..fa904be 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -110,7 +110,6 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( // // possible errors: // - ErrDataNotAvailable if that information is not (yet) available -// - ErrNotSupported if getting the communication standard is not supported // - and others func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { unknown := api.UCEVCCCommunicationStandardUnknown From 5e22ae287b1f793c0b76a1b69f058404470149eb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 14:25:32 +0100 Subject: [PATCH 091/227] Add CEM test --- cem/cem_test.go | 124 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 cem/cem_test.go diff --git a/cem/cem_test.go b/cem/cem_test.go new file mode 100644 index 0000000..8e7fb9a --- /dev/null +++ b/cem/cem_test.go @@ -0,0 +1,124 @@ +package cem + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/ucevsecc" + eebusapi "github.com/enbility/eebus-go/api" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func Test_CEM(t *testing.T) { + certificate, err := cert.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-10") + assert.Nil(t, err) + + configuration, err := eebusapi.NewConfiguration( + "Demo", + "Demo", + "HEMS", + "123456789", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 7654, + certificate, + 230, + time.Second*4) + assert.Nil(t, err) + + demo := &DemoCem{} + cem := NewCEM(configuration, demo, demo) + assert.NotNil(t, cem) + + err = cem.Setup() + assert.Nil(t, err) + + ucEvseCC := ucevsecc.NewUCEVSECC(cem.Service, cem.Service.LocalService(), demo) + cem.AddUseCase(ucEvseCC) + + cem.Start() + cem.Shutdown() + +} + +type DemoCem struct { +} + +// UseCaseEventReaderInterface + +func (d *DemoCem) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { + +} + +// eebusapi.ServiceReaderInterface + +// report the Ship ID of a newly trusted connection +func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { + // we should associated the Ship ID with the SKI and store it + // so the next connection can start trusted + logging.Log().Info("SKI", ski, "has Ship ID:", shipID) +} + +func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} + +func (d *DemoCem) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} + +func (d *DemoCem) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface, entries []shipapi.RemoteService) { +} + +func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} + +func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} + +func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } + +// Logging interface + +func (d *DemoCem) log(level string, args ...interface{}) { + t := time.Now() + fmt.Printf("%s: %s %s", t.Format(time.RFC3339), level, fmt.Sprintln(args...)) +} + +func (d *DemoCem) logf(level, format string, args ...interface{}) { + t := time.Now() + fmt.Printf("%s: %s %s\n", t.Format(time.RFC3339), level, fmt.Sprintf(format, args...)) +} + +func (d *DemoCem) Trace(args ...interface{}) { + d.log("TRACE", args...) +} + +func (d *DemoCem) Tracef(format string, args ...interface{}) { + d.logf("TRACE", format, args...) +} + +func (d *DemoCem) Debug(args ...interface{}) { + d.log("DEBUG", args...) +} + +func (d *DemoCem) Debugf(format string, args ...interface{}) { + d.logf("DEBUG", format, args...) +} + +func (d *DemoCem) Info(args ...interface{}) { + d.log("INFO", args...) +} + +func (d *DemoCem) Infof(format string, args ...interface{}) { + d.logf("INFO", format, args...) +} + +func (d *DemoCem) Error(args ...interface{}) { + d.log("ERROR", args...) +} + +func (d *DemoCem) Errorf(format string, args ...interface{}) { + d.logf("ERROR", format, args...) +} From f66633ad5731232a539e786e879b5654fc8a249a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 14:26:08 +0100 Subject: [PATCH 092/227] Rename democem file --- cmd/democem/{evsecc.go => eventreader.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmd/democem/{evsecc.go => eventreader.go} (100%) diff --git a/cmd/democem/evsecc.go b/cmd/democem/eventreader.go similarity index 100% rename from cmd/democem/evsecc.go rename to cmd/democem/eventreader.go From 4a80e95f297737916a77288ef9c01fb973521896 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 15:39:12 +0100 Subject: [PATCH 093/227] Update spine --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0744737..f3b4d5c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 + github.com/enbility/spine-go v0.0.0-20240224142225-40e4dd17a99e github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index e7525da..de6c21b 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 h1:7+EgHCkVBaEVD github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00/go.mod h1:4Rqu0NjVY73per2BlwfG1KZPIkrpRtBi5cWwB57gCMQ= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6 h1:q87C8Fb76kRbbMqG3vCM52yxjYxbclY23+rbBo2nq+Y= -github.com/enbility/spine-go v0.0.0-20240222195713-7c2bdbeb85d6/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240224142225-40e4dd17a99e h1:myGrJB826m7jsA5HsS2DTAFQvbAQeo6i/0VazX1ng7k= +github.com/enbility/spine-go v0.0.0-20240224142225-40e4dd17a99e/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From d37069443137d3b37fe5f96161c1424d08b4ba42 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 15:39:44 +0100 Subject: [PATCH 094/227] Various updates in UCMGCP --- ucmgcp/api.go | 6 +++--- ucmgcp/public.go | 15 ++++++++++----- ucmgcp/public_test.go | 38 +++++++++++++++++++------------------- ucmgcp/testhelper_test.go | 1 + 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/ucmgcp/api.go b/ucmgcp/api.go index de2613f..f654df0 100644 --- a/ucmgcp/api.go +++ b/ucmgcp/api.go @@ -26,7 +26,7 @@ type UCMGCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - MomentaryPowerConsumptionOrProduction(entity spineapi.EntityRemoteInterface) (float64, error) + MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 @@ -48,13 +48,13 @@ type UCMGCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - MomentaryCurrentConsumptionOrProduction(entity spineapi.EntityRemoteInterface) ([]float64, error) + MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 6 // return the voltage phase details at the grid connection point // - Voltage(entity spineapi.EntityRemoteInterface) ([]float64, error) + Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 7 diff --git a/ucmgcp/public.go b/ucmgcp/public.go index 7237a15..0ea3d13 100644 --- a/ucmgcp/public.go +++ b/ucmgcp/public.go @@ -1,6 +1,8 @@ package ucmgcp import ( + "slices" + "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" @@ -57,7 +59,7 @@ func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (f // // - positive values are used for consumption // - negative values are used for production -func (e *UCMGCP) MomentaryPowerConsumptionOrProduction(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMGCP) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !e.isCompatibleEntity(entity) { return 0, api.ErrNoCompatibleEntity } @@ -158,7 +160,7 @@ func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (flo // // - positive values are used for consumption // - negative values are used for production -func (e *UCMGCP) MomentaryCurrentConsumptionOrProduction(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMGCP) MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !e.isCompatibleEntity(entity) { return nil, api.ErrNoCompatibleEntity } @@ -216,7 +218,7 @@ func (e *UCMGCP) MomentaryCurrentConsumptionOrProduction(entity spineapi.EntityR // Scenario 6 // return the voltage phase details at the grid connection point -func (e *UCMGCP) Voltage(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMGCP) Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !e.isCompatibleEntity(entity) { return nil, api.ErrNoCompatibleEntity } @@ -289,8 +291,11 @@ func (e *UCMGCP) Frequency(entity spineapi.EntityRemoteInterface) (float64, erro // helper func (e *UCMGCP) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - if entity == nil || - (entity.EntityType() != model.EntityTypeTypeCEM && entity.EntityType() != model.EntityTypeTypeGridConnectionPointOfPremises) { + validEntityTypes := []model.EntityTypeType{ + model.EntityTypeTypeCEM, + model.EntityTypeTypeGridConnectionPointOfPremises, + } + if entity == nil || !slices.Contains(validEntityTypes, entity.EntityType()) { return false } diff --git a/ucmgcp/public_test.go b/ucmgcp/public_test.go index 0b83913..b7dfbf6 100644 --- a/ucmgcp/public_test.go +++ b/ucmgcp/public_test.go @@ -51,12 +51,12 @@ func (s *UCMGCPSuite) Test_PowerLimitationFactor() { assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_MomentaryPowerConsumptionOrProduction() { - data, err := s.sut.MomentaryPowerConsumptionOrProduction(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_MomentaryTotalPower() { + data, err := s.sut.MomentaryTotalPower(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryTotalPower(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -75,7 +75,7 @@ func (s *UCMGCPSuite) Test_MomentaryPowerConsumptionOrProduction() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryTotalPower(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -91,7 +91,7 @@ func (s *UCMGCPSuite) Test_MomentaryPowerConsumptionOrProduction() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryTotalPower(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -108,7 +108,7 @@ func (s *UCMGCPSuite) Test_MomentaryPowerConsumptionOrProduction() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryTotalPower(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -124,7 +124,7 @@ func (s *UCMGCPSuite) Test_MomentaryPowerConsumptionOrProduction() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPowerConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryTotalPower(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } @@ -219,12 +219,12 @@ func (s *UCMGCPSuite) Test_TotalConsumedEnergy() { assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_MomentaryCurrentConsumptionOrProduction() { - data, err := s.sut.MomentaryCurrentConsumptionOrProduction(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_MomentaryCurrents() { + data, err := s.sut.MomentaryCurrents(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryCurrents(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -255,7 +255,7 @@ func (s *UCMGCPSuite) Test_MomentaryCurrentConsumptionOrProduction() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryCurrents(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -279,7 +279,7 @@ func (s *UCMGCPSuite) Test_MomentaryCurrentConsumptionOrProduction() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryCurrents(s.smgwEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), data) @@ -319,18 +319,18 @@ func (s *UCMGCPSuite) Test_MomentaryCurrentConsumptionOrProduction() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrentConsumptionOrProduction(s.smgwEntity) + data, err = s.sut.MomentaryCurrents(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) } -func (s *UCMGCPSuite) Test_Voltage() { - data, err := s.sut.Voltage(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_Voltages() { + data, err := s.sut.Voltages(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.Voltage(s.smgwEntity) + data, err = s.sut.Voltages(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -361,7 +361,7 @@ func (s *UCMGCPSuite) Test_Voltage() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltage(s.smgwEntity) + data, err = s.sut.Voltages(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -385,7 +385,7 @@ func (s *UCMGCPSuite) Test_Voltage() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltage(s.smgwEntity) + data, err = s.sut.Voltages(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{0, 0, 0}, data) @@ -413,7 +413,7 @@ func (s *UCMGCPSuite) Test_Voltage() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltage(s.smgwEntity) + data, err = s.sut.Voltages(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{230, 230, 230}, data) } diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index 8ccdbec..47b20d7 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -164,6 +164,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) From 494761c2c7bc6d761fdf4250bc90649e13126308 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 15:40:11 +0100 Subject: [PATCH 095/227] Fix remote device not knowing its address in tests --- uccevc/testhelper_test.go | 1 + ucevcc/testhelper_test.go | 6 ++++++ ucevcem/testhelper_test.go | 1 + ucevsecc/testhelper_test.go | 1 + ucevsoc/testhelper_test.go | 1 + ucopev/testhelper_test.go | 1 + ucoscev/testhelper_test.go | 1 + ucvabd/testhelper_test.go | 1 + ucvapd/testhelper_test.go | 1 + 9 files changed, 14 insertions(+) diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index 46aa6e4..c8db988 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -185,6 +185,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index 346856a..debdf02 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -89,6 +89,11 @@ func setupDevices( Request(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(&defaultMsgCounter, nil). Maybe() + mockSender. + EXPECT(). + Subscribe(mock.Anything, mock.Anything, mock.Anything). + Return(&defaultMsgCounter, nil). + Maybe() remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, mockSender) remoteDeviceName := "remote" @@ -190,6 +195,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index d3b2cfa..997c455 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -168,6 +168,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index 51eff6e..3b74855 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -167,6 +167,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 89a66b1..4d8ac43 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -168,6 +168,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index 7417bb9..18312b4 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -169,6 +169,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index a8b4007..f0e8d08 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -178,6 +178,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go index c2faebb..9af425c 100644 --- a/ucvabd/testhelper_test.go +++ b/ucvabd/testhelper_test.go @@ -159,6 +159,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index 575c8d6..7ced303 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -165,6 +165,7 @@ func setupDevices( if err != nil { fmt.Println(err) } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) From a99f9a431ce5614e33c3e1a4b437ae2eba785a90 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 16:32:09 +0100 Subject: [PATCH 096/227] Various minor refactoring and fixes --- api/types.go | 8 ++++---- uccevc/events.go | 5 ++--- uccevc/public.go | 16 ---------------- uccevc/public_scen1.go | 4 ++-- uccevc/public_scen2.go | 4 ++-- uccevc/public_scen3.go | 6 +++--- uccevc/public_scen4.go | 4 ++-- uccevc/uccevc.go | 8 +++++++- ucevcc/events.go | 7 +++---- ucevcc/public.go | 24 +++++++----------------- ucevcc/ucevcc.go | 9 ++++++++- ucevcem/events.go | 5 ++--- ucevcem/public.go | 18 ++++-------------- ucevcem/ucevcem.go | 9 ++++++++- ucevsecc/events.go | 7 +++---- ucevsecc/public.go | 4 ++-- ucevsecc/ucevsecc.go | 9 ++++++++- ucevsoc/events.go | 5 ++--- ucevsoc/public.go | 2 +- ucevsoc/ucevsoc.go | 8 +++++++- ucmgcp/events.go | 9 ++++----- ucmgcp/public.go | 30 ++++++++---------------------- ucmgcp/ucmgcp.go | 8 +++++++- ucopev/events.go | 5 ++--- ucopev/public.go | 4 ++-- ucopev/ucopev.go | 8 +++++++- ucoscev/events.go | 2 +- ucoscev/public.go | 4 ++-- ucoscev/ucoscev.go | 12 ++++++++++++ ucvabd/events.go | 5 ++--- ucvabd/public.go | 17 ++++------------- ucvabd/ucvabd.go | 8 +++++++- ucvapd/events.go | 5 ++--- ucvapd/public.go | 15 +++------------ ucvapd/ucvapd.go | 7 ++++++- util/helper.go | 17 +++++++++-------- util/helper_test.go | 21 +++++++++++---------- util/loadcontrol.go | 8 ++++---- util/loadcontrol_test.go | 32 ++++++++++++++++---------------- 39 files changed, 186 insertions(+), 193 deletions(-) delete mode 100644 uccevc/public.go diff --git a/api/types.go b/api/types.go index 0b1f5af..1fc3aa6 100644 --- a/api/types.go +++ b/api/types.go @@ -319,19 +319,19 @@ const ( // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPGridConsumptionMeasurementDataUpdate UseCaseEventType = "ucMGCPGridConsumptionMeasurementDataUpdate" - // Grid momentary current consumption/production phase detail data updated + // Phase specific momentary current consumption/production phase detail data updated // // Use Case MGCP, Scenario 5 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPCurrentMeasurementDataUpdate UseCaseEventType = "ucMGCPCurrentMeasurementDataUpdate" + UCMGCPCurrentsMeasurementDataUpdate UseCaseEventType = "ucMGCPCurrentsMeasurementDataUpdate" - // Grid voltage phase detail data updated + // Phase specific voltage at the grid connection point // // Use Case MGCP, Scenario 6 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPVoltageMeasurementDataUpdate UseCaseEventType = "ucMGCPVoltageMeasurementDataUpdate" + UCMGCPVoltagesMeasurementDataUpdate UseCaseEventType = "ucMGCPVoltagesMeasurementDataUpdate" // Grid frequency data updated // diff --git a/uccevc/events.go b/uccevc/events.go index 05fcba4..c86a67f 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -12,12 +12,11 @@ import ( func (e *UCCEVC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - entityType := model.EntityTypeTypeEV - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.evConnected(payload.Entity) return } diff --git a/uccevc/public.go b/uccevc/public.go deleted file mode 100644 index 1fda8f1..0000000 --- a/uccevc/public.go +++ /dev/null @@ -1,16 +0,0 @@ -package uccevc - -import ( - spineapi "github.com/enbility/spine-go/api" - "github.com/enbility/spine-go/model" -) - -// helper - -func (e *UCCEVC) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false - } - - return true -} diff --git a/uccevc/public_scen1.go b/uccevc/public_scen1.go index 572bd57..7e4e1e0 100644 --- a/uccevc/public_scen1.go +++ b/uccevc/public_scen1.go @@ -12,7 +12,7 @@ import ( // returns the current charging strategy func (e *UCCEVC) ChargeStrategy(entity spineapi.EntityRemoteInterface) api.EVChargeStrategyType { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return api.EVChargeStrategyTypeUnknown } @@ -73,7 +73,7 @@ func (e *UCCEVC) ChargeStrategy(entity spineapi.EntityRemoteInterface) api.EVCha func (e *UCCEVC) EnergyDemand(entity spineapi.EntityRemoteInterface) (api.Demand, error) { demand := api.Demand{} - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return demand, api.ErrNoCompatibleEntity } diff --git a/uccevc/public_scen2.go b/uccevc/public_scen2.go index cfaf6f5..85af930 100644 --- a/uccevc/public_scen2.go +++ b/uccevc/public_scen2.go @@ -17,7 +17,7 @@ import ( func (e *UCCEVC) TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api.TimeSlotConstraints, error) { result := api.TimeSlotConstraints{} - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return result, api.ErrNoCompatibleEntity } @@ -62,7 +62,7 @@ func (e *UCCEVC) TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api // send power limits to the EV // if no data is provided, default power limits with the max possible value for 7 days will be sent func (e *UCCEVC) WritePowerLimits(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return api.ErrNoCompatibleEntity } diff --git a/uccevc/public_scen3.go b/uccevc/public_scen3.go index db85210..b0f1b66 100644 --- a/uccevc/public_scen3.go +++ b/uccevc/public_scen3.go @@ -17,7 +17,7 @@ import ( func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (api.IncentiveSlotConstraints, error) { result := api.IncentiveSlotConstraints{} - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return result, api.ErrNoCompatibleEntity } @@ -48,7 +48,7 @@ func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (ap // // SPINE UC CoordinatedEVCharging 2.4.3 func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInterface, data []api.IncentiveTariffDescription) error { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return api.ErrNoCompatibleEntity } @@ -186,7 +186,7 @@ func (e *UCCEVC) WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInt // send incentives to the EV // if no data is provided, default incentives with the same price for 7 days will be sent func (e *UCCEVC) WriteIncentives(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return api.ErrNoCompatibleEntity } diff --git a/uccevc/public_scen4.go b/uccevc/public_scen4.go index 8ca8ead..4bd4964 100644 --- a/uccevc/public_scen4.go +++ b/uccevc/public_scen4.go @@ -13,7 +13,7 @@ import ( func (e *UCCEVC) ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([]api.DurationSlotValue, error) { constraints := []api.DurationSlotValue{} - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return constraints, api.ErrNoCompatibleEntity } @@ -68,7 +68,7 @@ func (e *UCCEVC) ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([ func (e *UCCEVC) ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePlan, error) { plan := api.ChargePlan{} - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return plan, api.ErrNoCompatibleEntity } diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index 0b45510..92cb30b 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -15,6 +15,8 @@ type UCCEVC struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCCEVCInterface = (*UCCEVC)(nil) @@ -25,6 +27,10 @@ func NewUCCEVC(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEV, + } + _ = spine.Events.Subscribe(uc) return uc @@ -68,7 +74,7 @@ func (e *UCCEVC) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCCEVC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucevcc/events.go b/ucevcc/events.go index a645924..245fa44 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -17,15 +17,14 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { return } - entityType := model.EntityTypeTypeEV - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.evConnected(payload.Ski, payload.Entity) return - } else if util.IsEntityTypeDisconnected(payload, entityType) { + } else if util.IsEntityDisconnected(payload) { e.evDisconnected(payload.Ski, payload.Entity) return } diff --git a/ucevcc/public.go b/ucevcc/public.go index fa904be..a1dff1f 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -70,7 +70,7 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( entity spineapi.EntityRemoteInterface, keyname model.DeviceConfigurationKeyNameType, valueType model.DeviceConfigurationKeyValueTypeType) (any, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -114,7 +114,7 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { unknown := api.UCEVCCCommunicationStandardUnknown - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return unknown, api.ErrNoCompatibleEntity } @@ -137,7 +137,7 @@ func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (s // possible errors: // - ErrDataNotAvailable if that information is not (yet) available func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } @@ -161,7 +161,7 @@ func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterfa // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.IdentificationItem, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -207,7 +207,7 @@ func (e *UCEVCC) ManufacturerData( deviceName := "" serialNumber := "" - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return deviceName, serialNumber, api.ErrNoCompatibleEntity } @@ -238,7 +238,7 @@ func (e *UCEVCC) ManufacturerData( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, nil, nil, api.ErrNoCompatibleEntity } @@ -282,7 +282,7 @@ func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64 func (e *UCEVCC) EVInSleepMode( entity spineapi.EntityRemoteInterface, ) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } @@ -303,13 +303,3 @@ func (e *UCEVCC) EVInSleepMode( return false, nil } - -// helper - -func (e *UCEVCC) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false - } - - return true -} diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 2d4f977..d30b139 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -2,6 +2,7 @@ package ucevcc import ( "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" @@ -13,6 +14,8 @@ type UCEVCC struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCEVCCInterface = (*UCEVCC)(nil) @@ -23,6 +26,10 @@ func NewUCEVCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEV, + } + _ = spine.Events.Subscribe(uc) return uc @@ -67,7 +74,7 @@ func (e *UCEVCC) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVCC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucevcem/events.go b/ucevcem/events.go index f9b97a4..5f11ee0 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -12,12 +12,11 @@ import ( func (e *UCEVCEM) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - entityType := model.EntityTypeTypeEV - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.evConnected(payload.Entity) return } diff --git a/ucevcem/public.go b/ucevcem/public.go index 7cb4fe4..759d5fb 100644 --- a/ucevcem/public.go +++ b/ucevcem/public.go @@ -12,7 +12,7 @@ import ( // return the number of ac connected phases of the EV or 0 if it is unknown func (e *UCEVCEM) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -42,7 +42,7 @@ func (e *UCEVCEM) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCEM) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -101,7 +101,7 @@ func (e *UCEVCEM) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]flo // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCEM) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -161,7 +161,7 @@ func (e *UCEVCEM) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float6 // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVCEM) ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -186,13 +186,3 @@ func (e *UCEVCEM) ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, return value.GetValue(), err } - -// helper - -func (e *UCEVCEM) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { - return false - } - - return true -} diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index 9cfc32a..813c036 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -2,6 +2,7 @@ package ucevcem import ( "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" @@ -13,6 +14,8 @@ type UCEVCEM struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCEVCEMInterface = (*UCEVCEM)(nil) @@ -23,6 +26,10 @@ func NewUCEVCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDet reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEV, + } + _ = spine.Events.Subscribe(uc) return uc @@ -64,7 +71,7 @@ func (e *UCEVCEM) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVCEM) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucevsecc/events.go b/ucevsecc/events.go index 5bff2db..a09286e 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -17,15 +17,14 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { return } - entityType := model.EntityTypeTypeEVSE - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.evseConnected(payload.Ski, payload.Entity) return - } else if util.IsEntityTypeDisconnected(payload, entityType) { + } else if util.IsEntityDisconnected(payload) { e.evseDisconnected(payload.Ski, payload.Entity) return } diff --git a/ucevsecc/public.go b/ucevsecc/public.go index 3c2560a..de8ec31 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -19,7 +19,7 @@ func (e *UCEVSECC) ManufacturerData( deviceName := "" serialNumber := "" - if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return deviceName, serialNumber, api.ErrNoCompatibleEntity } @@ -54,7 +54,7 @@ func (e *UCEVSECC) OperatingState( operatingState := model.DeviceDiagnosisOperatingStateTypeNormalOperation lastErrorCode := "" - if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return operatingState, lastErrorCode, api.ErrNoCompatibleEntity } diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index a522529..2096256 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -2,6 +2,7 @@ package ucevsecc import ( "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" @@ -13,6 +14,8 @@ type UCEVSECC struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCEVSECCInterface = (*UCEVSECC)(nil) @@ -23,6 +26,10 @@ func NewUCEVSECC(service serviceapi.ServiceInterface, details *shipapi.ServiceDe reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEVSE, + } + _ = spine.Events.Subscribe(uc) return uc @@ -65,7 +72,7 @@ func (e *UCEVSECC) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVSECC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEVSE { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 7841698..15dd278 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -12,12 +12,11 @@ import ( func (e *UCEVSOC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - entityType := model.EntityTypeTypeEV - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.evConnected(payload.Entity) return } diff --git a/ucevsoc/public.go b/ucevsoc/public.go index 2342187..9334255 100644 --- a/ucevsoc/public.go +++ b/ucevsoc/public.go @@ -17,7 +17,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCEVSOC) StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index fa23a8a..38056f3 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -15,6 +15,8 @@ type UCEVSOC struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCEVSOCInterface = (*UCEVSOC)(nil) @@ -25,6 +27,10 @@ func NewUCEVSOC(service serviceapi.ServiceInterface, details *shipapi.ServiceDet reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEV, + } + _ = spine.Events.Subscribe(uc) return uc @@ -66,7 +72,7 @@ func (e *UCEVSOC) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCEVSOC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 5ef6082..74ed2eb 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -12,12 +12,11 @@ import ( func (e *UCMGCP) HandleEvent(payload spineapi.EventPayload) { // only about events from an SGMW entity or device changes for this remote device - entityType := model.EntityTypeTypeGridConnectionPointOfPremises - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.gridConnected(payload.Entity) return } @@ -120,12 +119,12 @@ func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRem // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPCurrentMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity, api.UCMGCPCurrentsMeasurementDataUpdate) } // Scenario 6 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPVoltageMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity, api.UCMGCPVoltagesMeasurementDataUpdate) } // Scenario 7 diff --git a/ucmgcp/public.go b/ucmgcp/public.go index 0ea3d13..d2e80dd 100644 --- a/ucmgcp/public.go +++ b/ucmgcp/public.go @@ -1,8 +1,6 @@ package ucmgcp import ( - "slices" - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" @@ -18,7 +16,7 @@ import ( // - ErrDataNotAvailable if no such limit is (yet) available // - and others func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -60,7 +58,7 @@ func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (f // - positive values are used for consumption // - negative values are used for production func (e *UCMGCP) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -106,7 +104,7 @@ func (e *UCMGCP) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (flo // // - negative values are used for production func (e *UCMGCP) TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -133,7 +131,7 @@ func (e *UCMGCP) TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float // // - positive values are used for consumption func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -161,7 +159,7 @@ func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (flo // - positive values are used for consumption // - negative values are used for production func (e *UCMGCP) MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -219,7 +217,7 @@ func (e *UCMGCP) MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]flo // return the voltage phase details at the grid connection point func (e *UCMGCP) Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -267,7 +265,7 @@ func (e *UCMGCP) Voltages(entity spineapi.EntityRemoteInterface) ([]float64, err // return frequency at the grid connection point func (e *UCMGCP) Frequency(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -290,24 +288,12 @@ func (e *UCMGCP) Frequency(entity spineapi.EntityRemoteInterface) (float64, erro // helper -func (e *UCMGCP) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - validEntityTypes := []model.EntityTypeType{ - model.EntityTypeTypeCEM, - model.EntityTypeTypeGridConnectionPointOfPremises, - } - if entity == nil || !slices.Contains(validEntityTypes, entity.EntityType()) { - return false - } - - return true -} - func (e *UCMGCP) getValuesForTypeCommodityScope( entity spineapi.EntityRemoteInterface, measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - if entity == nil || !e.isCompatibleEntity(entity) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index c31c768..2a36082 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -15,6 +15,8 @@ type UCMGCP struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCMGCPInterface = (*UCMGCP)(nil) @@ -25,6 +27,10 @@ func NewUCMGCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeCEM, + model.EntityTypeTypeGridConnectionPointOfPremises, + } _ = spine.Events.Subscribe(uc) return uc @@ -67,7 +73,7 @@ func (e *UCMGCP) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCMGCP) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucopev/events.go b/ucopev/events.go index 1129896..e814110 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -12,12 +12,11 @@ import ( func (e *UCOPEV) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - entityType := model.EntityTypeTypeEV - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.evConnected(payload.Entity) return } diff --git a/ucopev/public.go b/ucopev/public.go index 0355925..664cfb5 100644 --- a/ucopev/public.go +++ b/ucopev/public.go @@ -13,7 +13,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { - return util.LoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeObligation) + return util.LoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeObligation) } // send new LoadControlLimits to the remote EV @@ -35,5 +35,5 @@ func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]flo // and needs to have specific EVSE support for the specific EV brand. // In ISO15118-20 this is a standard feature which does not need special support on the EVSE. func (e *UCOPEV) WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - return util.WriteLoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeObligation, limits) + return util.WriteLoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeObligation, limits) } diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 0dd57f8..2a73869 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -15,6 +15,8 @@ type UCOPEV struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCOPEVInterface = (*UCOPEV)(nil) @@ -25,6 +27,10 @@ func NewUCOPEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEV, + } + _ = spine.Events.Subscribe(uc) return uc @@ -71,7 +77,7 @@ func (e *UCOPEV) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCOPEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucoscev/events.go b/ucoscev/events.go index d88f7fd..4325e2e 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -12,7 +12,7 @@ func (e *UCOSCEV) HandleEvent(payload spineapi.EventPayload) { // most of the events are identical to OPEV, and OPEV is required to be used, // we don't handle the same events in here - if !util.IsPayloadForEntityType(payload, model.EntityTypeTypeEV) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } diff --git a/ucoscev/public.go b/ucoscev/public.go index a3badf0..646e685 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -13,7 +13,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { - return util.LoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeRecommendation) + return util.LoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeRecommendation) } // send new LoadControlLimits to the remote EV @@ -28,5 +28,5 @@ func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]fl // the EVSE needs to be able map the recommendations into oligation limits which then // works for all EVs communication either via IEC61851 or ISO15118. func (e *UCOSCEV) WriteLoadControlLimits(entity spineapi.EntityRemoteInterface, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - return util.WriteLoadControlLimits(e.service, entity, model.EntityTypeTypeEV, model.LoadControlCategoryTypeRecommendation, limits) + return util.WriteLoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeRecommendation, limits) } diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index b10fccd..64efb90 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -15,6 +15,8 @@ type UCOSCEV struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCOSCEVInterface = (*UCOSCEV)(nil) @@ -25,6 +27,16 @@ func NewUCOSCEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDet reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeCompressor, + model.EntityTypeTypeElectricalImmersionHeater, + model.EntityTypeTypeEVSE, + model.EntityTypeTypeHeatPumpAppliance, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, + } + _ = spine.Events.Subscribe(uc) return uc diff --git a/ucvabd/events.go b/ucvabd/events.go index 7a73469..6b16102 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -12,12 +12,11 @@ import ( func (e *UCVABD) HandleEvent(payload spineapi.EventPayload) { // only about events from an SGMW entity or device changes for this remote device - entityType := model.EntityTypeTypeElectricityStorageSystem - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.inverterConnected(payload.Entity) return } diff --git a/ucvabd/public.go b/ucvabd/public.go index 07540ee..00d8bf0 100644 --- a/ucvabd/public.go +++ b/ucvabd/public.go @@ -17,7 +17,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVABD) CurrentChargePower(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -46,7 +46,7 @@ func (e *UCVABD) CurrentChargePower(entity spineapi.EntityRemoteInterface) (floa // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVABD) TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -73,7 +73,7 @@ func (e *UCVABD) TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVABD) TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -100,7 +100,7 @@ func (e *UCVABD) TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (fl // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVABD) CurrentStateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -123,15 +123,6 @@ func (e *UCVABD) CurrentStateOfCharge(entity spineapi.EntityRemoteInterface) (fl // helper -func (e *UCVABD) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - if entity == nil || - (entity.EntityType() != model.EntityTypeTypeElectricityStorageSystem) { - return false - } - - return true -} - func (e *UCVABD) getValuesForTypeCommodityScope( entity spineapi.EntityRemoteInterface, measurement model.MeasurementTypeType, diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index db33a55..2403c63 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -15,6 +15,8 @@ type UCVABD struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCVABDInterface = (*UCVABD)(nil) @@ -25,6 +27,10 @@ func NewUCVABD(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeElectricityStorageSystem, + } + _ = spine.Events.Subscribe(uc) return uc @@ -67,7 +73,7 @@ func (e *UCVABD) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCVABD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucvapd/events.go b/ucvapd/events.go index 6209a99..7ea89d6 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -12,12 +12,11 @@ import ( func (e *UCVAPD) HandleEvent(payload spineapi.EventPayload) { // only about events from an SGMW entity or device changes for this remote device - entityType := model.EntityTypeTypePVSystem - if !util.IsPayloadForEntityType(payload, entityType) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } - if util.IsEntityTypeConnected(payload, entityType) { + if util.IsEntityConnected(payload) { e.inverterConnected(payload.Entity) return } diff --git a/ucvapd/public.go b/ucvapd/public.go index 45b4ac8..d84ef39 100644 --- a/ucvapd/public.go +++ b/ucvapd/public.go @@ -14,7 +14,7 @@ import ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVAPD) CurrentProductionPower(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -43,7 +43,7 @@ func (e *UCVAPD) CurrentProductionPower(entity spineapi.EntityRemoteInterface) ( // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVAPD) NominalPeakPower(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -81,7 +81,7 @@ func (e *UCVAPD) NominalPeakPower(entity spineapi.EntityRemoteInterface) (float6 // - ErrDataNotAvailable if no such measurement is (yet) available // - and others func (e *UCVAPD) TotalPVYield(entity spineapi.EntityRemoteInterface) (float64, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -104,15 +104,6 @@ func (e *UCVAPD) TotalPVYield(entity spineapi.EntityRemoteInterface) (float64, e // helper -func (e *UCVAPD) isCompatibleEntity(entity spineapi.EntityRemoteInterface) bool { - if entity == nil || - (entity.EntityType() != model.EntityTypeTypePVSystem) { - return false - } - - return true -} - func (e *UCVAPD) getValuesForTypeCommodityScope( entity spineapi.EntityRemoteInterface, measurement model.MeasurementTypeType, diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 322bea5..15908a2 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -15,6 +15,8 @@ type UCVAPD struct { service serviceapi.ServiceInterface reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType } var _ UCVAPDInterface = (*UCVAPD)(nil) @@ -25,6 +27,9 @@ func NewUCVAPD(service serviceapi.ServiceInterface, details *shipapi.ServiceDeta reader: reader, } + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypePVSystem, + } _ = spine.Events.Subscribe(uc) return uc @@ -68,7 +73,7 @@ func (e *UCVAPD) AddUseCase() { // - ErrDataNotAvailable if that information is not (yet) available // - and others func (e *UCVAPD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { - if !e.isCompatibleEntity(entity) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/util/helper.go b/util/helper.go index 3855608..6d3e5cd 100644 --- a/util/helper.go +++ b/util/helper.go @@ -1,19 +1,20 @@ package util import ( + "slices" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) var PhaseNameMapping = []model.ElectricalConnectionPhaseNameType{model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC} -func IsPayloadForEntityType(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { - if payload.Entity == nil { +func IsCompatibleEntity(entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType) bool { + if entity == nil { return false } - theEntityType := payload.Entity.EntityType() - return theEntityType == entityType + return slices.Contains(entityTypes, entity.EntityType()) } func IsDeviceDisconnected(payload spineapi.EventPayload) bool { @@ -21,19 +22,19 @@ func IsDeviceDisconnected(payload spineapi.EventPayload) bool { payload.ChangeType == spineapi.ElementChangeRemove) } -func IsEntityTypeConnected(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { +func IsEntityConnected(payload spineapi.EventPayload) bool { if payload.EventType == spineapi.EventTypeEntityChange && payload.ChangeType == spineapi.ElementChangeAdd { - return IsPayloadForEntityType(payload, entityType) + return true } return false } -func IsEntityTypeDisconnected(payload spineapi.EventPayload, entityType model.EntityTypeType) bool { +func IsEntityDisconnected(payload spineapi.EventPayload) bool { if payload.EventType == spineapi.EventTypeEntityChange && payload.ChangeType == spineapi.ElementChangeRemove { - return IsPayloadForEntityType(payload, entityType) + return true } return false diff --git a/util/helper_test.go b/util/helper_test.go index b813314..dcc0d79 100644 --- a/util/helper_test.go +++ b/util/helper_test.go @@ -6,21 +6,22 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UtilSuite) Test_IsPayloadForEntityType() { +func (s *UtilSuite) Test_IsCompatibleEntity() { payload := spineapi.EventPayload{} - result := IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + validEntityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} + result := IsCompatibleEntity(payload.Entity, validEntityTypes) assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ Entity: s.mockRemoteEntity, } - result = IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + result = IsCompatibleEntity(payload.Entity, validEntityTypes) assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ Entity: s.evEntity, } - result = IsPayloadForEntityType(payload, model.EntityTypeTypeEV) + result = IsCompatibleEntity(payload.Entity, validEntityTypes) assert.Equal(s.T(), true, result) } @@ -37,9 +38,9 @@ func (s *UtilSuite) Test_IsDeviceDisconnected() { assert.Equal(s.T(), true, result) } -func (s *UtilSuite) Test_IsEntityTypeConnected() { +func (s *UtilSuite) Test_IsEntityConnected() { payload := spineapi.EventPayload{} - result := IsEntityTypeConnected(payload, model.EntityTypeTypeEVSE) + result := IsEntityConnected(payload) assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ @@ -47,13 +48,13 @@ func (s *UtilSuite) Test_IsEntityTypeConnected() { EventType: spineapi.EventTypeEntityChange, ChangeType: spineapi.ElementChangeAdd, } - result = IsEntityTypeConnected(payload, model.EntityTypeTypeEVSE) + result = IsEntityConnected(payload) assert.Equal(s.T(), true, result) } -func (s *UtilSuite) Test_IsEntityTypeDisconnected() { +func (s *UtilSuite) Test_IsEntityDisconnected() { payload := spineapi.EventPayload{} - result := IsEntityTypeDisconnected(payload, model.EntityTypeTypeEVSE) + result := IsEntityDisconnected(payload) assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ @@ -61,6 +62,6 @@ func (s *UtilSuite) Test_IsEntityTypeDisconnected() { EventType: spineapi.EventTypeEntityChange, ChangeType: spineapi.ElementChangeRemove, } - result = IsEntityTypeDisconnected(payload, model.EntityTypeTypeEVSE) + result = IsEntityDisconnected(payload) assert.Equal(s.T(), true, result) } diff --git a/util/loadcontrol.go b/util/loadcontrol.go index c202fa1..8c7bafa 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -17,9 +17,9 @@ import ( func LoadControlLimits( service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, - entityType model.EntityTypeType, + entityTypes []model.EntityTypeType, category model.LoadControlCategoryType) ([]float64, error) { - if entity == nil || entity.EntityType() != entityType { + if entity == nil || !IsCompatibleEntity(entity, entityTypes) { return nil, api.ErrNoCompatibleEntity } @@ -117,10 +117,10 @@ func LoadControlLimits( func WriteLoadControlLimits( service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, - entityType model.EntityTypeType, + entityTypes []model.EntityTypeType, category model.LoadControlCategoryType, limits []api.LoadLimitsPhase) (*model.MsgCounterType, error) { - if entity == nil || entity.EntityType() != entityType { + if entity == nil || !IsCompatibleEntity(entity, entityTypes) { return nil, api.ErrNoCompatibleEntity } diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 4150b18..82442ac 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -13,13 +13,13 @@ func (s *UtilSuite) Test_LoadControlLimits() { var data []float64 var err error category := model.LoadControlCategoryTypeObligation - entityType := model.EntityTypeTypeEV + entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} - data, err = LoadControlLimits(s.service, s.mockRemoteEntity, entityType, category) + data, err = LoadControlLimits(s.service, s.mockRemoteEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -47,7 +47,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{0.0, 0.0, 0.0}, data) @@ -78,7 +78,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -101,7 +101,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -130,7 +130,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityType, category) + data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{16.0, 16.0, 16.0}, data) } @@ -139,13 +139,13 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { loadLimits := []api.LoadLimitsPhase{} category := model.LoadControlCategoryTypeObligation - entityType := model.EntityTypeTypeEV + entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} - msgCounter, err := WriteLoadControlLimits(s.service, s.mockRemoteEntity, entityType, category, loadLimits) + msgCounter, err := WriteLoadControlLimits(s.service, s.mockRemoteEntity, entityTypes, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -176,7 +176,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -274,7 +274,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err := WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) + msgCounter, err := WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -307,7 +307,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -329,7 +329,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitListData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -343,11 +343,11 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { }) } - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityType, category, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) } From 3f68bb216e1a7498809dff41b6ff9e477fee7288 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 24 Feb 2024 16:48:42 +0100 Subject: [PATCH 097/227] Add UCMPC --- api/types.go | 51 ++++ ucmpc/api.go | 61 +++++ ucmpc/events.go | 115 +++++++++ ucmpc/events_test.go | 116 +++++++++ ucmpc/public.go | 313 +++++++++++++++++++++++ ucmpc/public_test.go | 525 +++++++++++++++++++++++++++++++++++++++ ucmpc/results.go | 8 + ucmpc/testhelper_test.go | 166 +++++++++++++ ucmpc/ucmcp.go | 124 +++++++++ ucmpc/ucmcp_test.go | 89 +++++++ 10 files changed, 1568 insertions(+) create mode 100644 ucmpc/api.go create mode 100644 ucmpc/events.go create mode 100644 ucmpc/events_test.go create mode 100644 ucmpc/public.go create mode 100644 ucmpc/public_test.go create mode 100644 ucmpc/results.go create mode 100644 ucmpc/testhelper_test.go create mode 100644 ucmpc/ucmcp.go create mode 100644 ucmpc/ucmcp_test.go diff --git a/api/types.go b/api/types.go index 1fc3aa6..ea3b45e 100644 --- a/api/types.go +++ b/api/types.go @@ -340,6 +340,57 @@ const ( // Note: the referred data may be updated together with all other measurement items of this use case UCMGCPFrequencyMeasurementDataUpdate UseCaseEventType = "ucMGCPFrequencyMeasurementDataUpdate" + // UCMPC + + // Total momentary active power consumption or production + // + // Use Case MCP, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCPowerTotalMeasurementDataUpdate UseCaseEventType = "ucMPCPowerTotalMeasurementDataUpdate" + + // Phase specific momentary active power consumption or production + // + // Use Case MCP, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCPowerPerPhaseMeasurementDataUpdate UseCaseEventType = "ucMPCPowerPerPhaseMeasurementDataUpdate" + + // Total energy consumed + // + // Use Case MCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCEnergyConsumedMeasurementDataUpdate UseCaseEventType = "ucMPCEnergyConsumedMeasurementDataUpdate" + + // Total energy produced + // + // Use Case MCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCEnergyProcudedMeasurementDataUpdate UseCaseEventType = "ucMPCEnergyProcudedMeasurementDataUpdate" + + // Phase specific momentary current consumption or production + // + // Use Case MCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCCurrentsMeasurementDataUpdate UseCaseEventType = "ucMPCCurrentsMeasurementDataUpdate" + + // Phase specific voltage + // + // Use Case MCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCVoltagesMeasurementDataUpdate UseCaseEventType = "ucMPCVoltagesMeasurementDataUpdate" + + // Power network frequency data updated + // + // Use Case MCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMPCFrequencyMeasurementDataUpdate UseCaseEventType = "ucMPCFrequencyMeasurementDataUpdate" + // UCOPEV // EV load control obligation limit data updated diff --git a/ucmpc/api.go b/ucmpc/api.go new file mode 100644 index 0000000..0cc2b2c --- /dev/null +++ b/ucmpc/api.go @@ -0,0 +1,61 @@ +package ucmpc + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +//go:generate mockery + +// interface for the Monitoring of Power Consumption UseCase +type UCMCPInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the momentary active power consumption or production + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) + + // return the momentary active phase specific power consumption or production per phase + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + MomentaryPhasePower(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 2 + + // return the total consumption energy + // + // - positive values are used for consumption + TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + + // return the total feed in energy + // + // - negative values are used for production + TotalProducedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + + // Scenario 3 + + // return the momentary phase specific current consumption or production + // + // - positive values are used for consumption + // - negative values are used for production + MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 4 + + // return the phase specific voltage details + // + Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) + + // Scenario 5 + + // return frequency + // + Frequency(entity spineapi.EntityRemoteInterface) (float64, error) +} diff --git a/ucmpc/events.go b/ucmpc/events.go new file mode 100644 index 0000000..bed1d60 --- /dev/null +++ b/ucmpc/events.go @@ -0,0 +1,115 @@ +package ucmpc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCMPC) HandleEvent(payload spineapi.EventPayload) { + // only about events from an SGMW entity or device changes for this remote device + + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { + return + } + + if util.IsEntityConnected(payload) { + e.deviceConnected(payload.Entity) + return + } + + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.MeasurementDescriptionListDataType: + e.deviceMeasurementDescriptionDataUpdate(payload.Entity) + case *model.MeasurementListDataType: + e.deviceMeasurementDataUpdate(payload.Ski, payload.Entity) + } +} + +// process required steps when a device is connected +func (e *UCMPC) deviceConnected(entity spineapi.EntityRemoteInterface) { + if electricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := electricalConnection.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get electrical connection parameter + if _, err := electricalConnection.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := electricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Error(err) + } + } + + if measurement, err := util.Measurement(e.service, entity); err == nil { + if _, err := measurement.Subscribe(); err != nil { + logging.Log().Error(err) + } + + // get measurement parameters + if _, err := measurement.RequestDescriptions(); err != nil { + logging.Log().Error(err) + } + + if _, err := measurement.RequestConstraints(); err != nil { + logging.Log().Error(err) + } + } +} + +// the measurement descriptiondata of a device was updated +func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if measurement, err := util.Measurement(e.service, entity); err == nil { + // measurement descriptions received, now get the data + if _, err := measurement.RequestValues(); err != nil { + logging.Log().Error("Error getting measurement list values:", err) + } + } +} + +// the measurement data of a device was updated +func (e *UCMPC) deviceMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + // Scenario 1 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCPowerTotalMeasurementDataUpdate) + } + + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) + } + + // Scenario 2 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) + } + + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyProduced); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) + } + + // Scenario 3 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCCurrentsMeasurementDataUpdate) + } + + // Scenario 4 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCVoltagesMeasurementDataUpdate) + } + + // Scenario 5 + if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { + e.reader.SpineEvent(ski, entity, api.UCMPCFrequencyMeasurementDataUpdate) + } + +} diff --git a/ucmpc/events_test.go b/ucmpc/events_test.go new file mode 100644 index 0000000..e77b10a --- /dev/null +++ b/ucmpc/events_test.go @@ -0,0 +1,116 @@ +package ucmpc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCMPCSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.monitoredEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.MeasurementDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.MeasurementListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCMPCSuite) Test_deviceMeasurementDataUpdate() { + s.sut.deviceMeasurementDataUpdate(remoteSki, s.monitoredEntity) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACEnergyConsumed), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(3)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACEnergyProduced), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(4)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(5)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(6)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACFrequency), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.deviceMeasurementDataUpdate(remoteSki, s.monitoredEntity) + + data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(3)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(4)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(5)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(6)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.deviceMeasurementDataUpdate(remoteSki, s.monitoredEntity) + +} diff --git a/ucmpc/public.go b/ucmpc/public.go new file mode 100644 index 0000000..6229e74 --- /dev/null +++ b/ucmpc/public.go @@ -0,0 +1,313 @@ +package ucmpc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + "github.com/enbility/eebus-go/features" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// Scenario 1 + +// return the momentary active power consumption or production +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCMPC) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypePower + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACPowerTotal + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume there is only one value + mId := data[0].MeasurementId + value := data[0].Value + if mId == nil || value == nil { + return 0, features.ErrDataNotAvailable + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return 0, err + } + + desc, err := electricalConnection.GetDescriptionForMeasurementId(*mId) + if err != nil { + return 0, err + } + + // if energy direction is not consume, report an error + if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { + return 0, features.ErrMissingData + } + + return value.GetValue(), nil +} + +// return the momentary active phase specific power consumption or production per phase +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCMPC) MomentaryPhasePower(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypePower + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACPower + values, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return nil, err + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return nil, err + } + + var phaseA, phaseB, phaseC float64 + + for _, item := range values { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || param.AcMeasuredPhases == nil { + continue + } + + value := item.Value.GetValue() + + if desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { + // if energy direction is not consume, invert it + if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { + return nil, err + } + } + + switch *param.AcMeasuredPhases { + case model.ElectricalConnectionPhaseNameTypeA: + phaseA = value + case model.ElectricalConnectionPhaseNameTypeB: + phaseB = value + case model.ElectricalConnectionPhaseNameTypeC: + phaseC = value + } + } + + return []float64{phaseA, phaseB, phaseC}, nil +} + +// Scenario 2 + +// return the total consumption energy +// +// - positive values are used for consumption +func (e *UCMPC) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeEnergy + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACEnergyConsumed + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume thre is only one result + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// return the total feed in energy +// +// - negative values are used for production +func (e *UCMPC) TotalProducedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeEnergy + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACEnergyProduced + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // we assume thre is only one result + value := data[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// Scenario 3 + +// return the momentary phase specific current consumption or production +// +// - positive values are used for consumption +// - negative values are used for production +func (e *UCMPC) MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeCurrent + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACCurrent + values, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return nil, err + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return nil, err + } + + var phaseA, phaseB, phaseC float64 + + for _, item := range values { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || param.AcMeasuredPhases == nil { + continue + } + + value := item.Value.GetValue() + + if desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { + // if energy direction is not consume, invert it + if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { + return nil, err + } + } + + switch *param.AcMeasuredPhases { + case model.ElectricalConnectionPhaseNameTypeA: + phaseA = value + case model.ElectricalConnectionPhaseNameTypeB: + phaseB = value + case model.ElectricalConnectionPhaseNameTypeC: + phaseC = value + } + } + + return []float64{phaseA, phaseB, phaseC}, nil +} + +// Scenario 4 + +// return the phase specific voltage details +func (e *UCMPC) Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeVoltage + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACVoltage + data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return nil, err + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return nil, err + } + + var phaseA, phaseB, phaseC float64 + + for _, item := range data { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || param.AcMeasuredPhases == nil { + continue + } + + value := item.Value.GetValue() + + switch *param.AcMeasuredPhases { + case model.ElectricalConnectionPhaseNameTypeA: + phaseA = value + case model.ElectricalConnectionPhaseNameTypeB: + phaseB = value + case model.ElectricalConnectionPhaseNameTypeC: + phaseC = value + } + } + + return []float64{phaseA, phaseB, phaseC}, nil +} + +// Scenario 5 + +// return frequency +func (e *UCMPC) Frequency(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + measurement := model.MeasurementTypeTypeFrequency + commodity := model.CommodityTypeTypeElectricity + scope := model.ScopeTypeTypeACFrequency + item, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + if err != nil { + return 0, err + } + + // take the first item + value := item[0].Value + if value == nil { + return 0, features.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// helper + +func (e *UCMPC) getValuesForTypeCommodityScope( + entity spineapi.EntityRemoteInterface, + measurement model.MeasurementTypeType, + commodity model.CommodityTypeType, + scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + measurementFeature, err := util.Measurement(e.service, entity) + if err != nil || measurementFeature == nil { + return nil, err + } + + return measurementFeature.GetValuesForTypeCommodityScope(measurement, commodity, scope) +} diff --git a/ucmpc/public_test.go b/ucmpc/public_test.go new file mode 100644 index 0000000..0221068 --- /dev/null +++ b/ucmpc/public_test.go @@ -0,0 +1,525 @@ +package ucmpc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCMPCSuite) Test_MomentaryTotalPower() { + data, err := s.sut.MomentaryTotalPower(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + elDescData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + PositiveEnergyDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMPCSuite) Test_MomentaryPhasePower() { + data, err := s.sut.MomentaryPhasePower(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + elDescData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + PositiveEnergyDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{10, 10, 10}, data) + +} + +func (s *UCMPCSuite) Test_TotalConsumedEnergy() { + data, err := s.sut.TotalConsumedEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalConsumedEnergy(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACEnergyConsumed), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalConsumedEnergy(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalConsumedEnergy(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMPCSuite) Test_TotalProducedEnergy() { + data, err := s.sut.TotalProducedEnergy(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.TotalProducedEnergy(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACEnergyProduced), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalProducedEnergy(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.TotalProducedEnergy(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 10.0, data) +} + +func (s *UCMPCSuite) Test_MomentaryCurrents() { + data, err := s.sut.MomentaryCurrents(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACCurrent), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + elDescData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + PositiveEnergyDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{10, 10, 10}, data) + +} + +func (s *UCMPCSuite) Test_Voltages() { + data, err := s.sut.Voltages(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = s.sut.Voltages(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACVoltage), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Voltages(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(230), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(230), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(230), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Voltages(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{0, 0, 0}, data) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Voltages(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{230, 230, 230}, data) +} + +func (s *UCMPCSuite) Test_Frequency() { + data, err := s.sut.Frequency(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.Frequency(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACFrequency), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Frequency(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(50), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.Frequency(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 50.0, data) +} diff --git a/ucmpc/results.go b/ucmpc/results.go new file mode 100644 index 0000000..ca18d6a --- /dev/null +++ b/ucmpc/results.go @@ -0,0 +1,8 @@ +package ucmpc + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCMPC) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go new file mode 100644 index 0000000..8226b29 --- /dev/null +++ b/ucmpc/testhelper_test.go @@ -0,0 +1,166 @@ +package ucmpc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestMOCSuite(t *testing.T) { + suite.Run(t, new(UCMPCSuite)) +} + +type UCMPCSuite struct { + suite.Suite + + sut *UCMPC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface +} + +func (s *UCMPCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +} + +func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + s.sut = NewUCMCP(s.service, s.service.LocalService(), s) + s.sut.AddFeatures() + s.sut.AddUseCase() + + s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeMeasurement, + []model.FunctionType{ + model.FunctionTypeMeasurementDescriptionListData, + model.FunctionTypeMeasurementConstraintsListData, + model.FunctionTypeMeasurementListData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionDescriptionListData, + }, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(model.RoleTypeServer), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go new file mode 100644 index 0000000..db81e11 --- /dev/null +++ b/ucmpc/ucmcp.go @@ -0,0 +1,124 @@ +package ucmpc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + serviceapi "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features" + shipapi "github.com/enbility/ship-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCMPC struct { + service serviceapi.ServiceInterface + + reader api.UseCaseEventReaderInterface + + validEntityTypes []model.EntityTypeType +} + +var _ UCMCPInterface = (*UCMPC)(nil) + +func NewUCMCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCMPC { + uc := &UCMPC{ + service: service, + reader: reader, + } + + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeCompressor, + model.EntityTypeTypeElectricalImmersionHeater, + model.EntityTypeTypeEVSE, + model.EntityTypeTypeHeatPumpAppliance, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCMPC) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeMonitoringOfPowerConsumption +} + +func (e *UCMPC) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + } + for _, feature := range clientFeatures { + f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + f.AddResultHandler(e) + } +} + +func (e *UCMPC) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeMonitoringAppliance, + e.UseCaseName(), + model.SpecificationVersionType("1.0.0"), + "release", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCMPC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeMonitoredUnit, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1}, + []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + ) { + return false, nil + } + + // check if measurement description contain data for the required scope + measurement, err := util.Measurement(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + if _, err := measurement.GetDescriptionsForScope(model.ScopeTypeTypeACPowerTotal); err != nil { + return false, features.ErrDataNotAvailable + } + + // check if electrical connection descriptions is provided + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil { + return false, features.ErrFunctionNotSupported + } + + if _, err = electricalConnection.GetDescriptions(); err != nil { + return false, err + } + + if _, err = electricalConnection.GetParameterDescriptions(); err != nil { + return false, err + } + + return true, nil +} diff --git a/ucmpc/ucmcp_test.go b/ucmpc/ucmcp_test.go new file mode 100644 index 0000000..d8f490d --- /dev/null +++ b/ucmpc/ucmcp_test.go @@ -0,0 +1,89 @@ +package ucmpc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCMPCSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeMonitoredUnit), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeMonitoringOfPowerConsumption), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + }, + } + + measurementFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + elData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + elFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + }, + }, + } + + fErr = elFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} From 1ce16ff7e1d24bd854a5aa0492881b3807ecd246 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 14:04:33 +0100 Subject: [PATCH 098/227] Various updates - Remove code duplications - Rename APIs for simplicity and consitence --- ucevcc/api.go | 2 +- ucevcc/public.go | 20 ++-- ucevcc/public_test.go | 8 +- ucevcem/api.go | 4 +- ucevcem/events.go | 2 +- ucevcem/public.go | 4 +- ucevcem/public_test.go | 16 +-- ucmgcp/api.go | 10 +- ucmgcp/public.go | 199 +++++++++---------------------- ucmgcp/public_test.go | 62 +++++----- ucmpc/api.go | 12 +- ucmpc/public.go | 246 ++++++++++----------------------------- ucmpc/public_test.go | 77 ++++++------ ucvabd/api.go | 8 +- ucvabd/public.go | 8 +- ucvabd/public_test.go | 32 ++--- ucvapd/api.go | 6 +- ucvapd/public.go | 11 +- ucvapd/public_test.go | 22 ++-- util/features_test.go | 14 +-- util/helper_test.go | 2 +- util/loadcontrol_test.go | 32 ++--- util/measurement.go | 89 +++++++++++++- util/measurement_test.go | 159 ++++++++++++++++++++++++- util/testhelper_test.go | 4 +- 25 files changed, 530 insertions(+), 519 deletions(-) diff --git a/ucevcc/api.go b/ucevcc/api.go index 0d1ddcc..59f3388 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -49,5 +49,5 @@ type UCEVCCInterface interface { // Scenario 7 // is the EV in sleep mode - EVInSleepMode(entity spineapi.EntityRemoteInterface) (bool, error) + IsInSleepMode(entity spineapi.EntityRemoteInterface) (bool, error) } diff --git a/ucevcc/public.go b/ucevcc/public.go index a1dff1f..3bf2a56 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -119,16 +119,15 @@ func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (s } data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) - if err != nil { - return unknown, err + if err != nil || data == nil { + return unknown, features.ErrDataNotAvailable } - if data == nil { + value, ok := data.(*model.DeviceConfigurationKeyValueStringType) + if !ok || value == nil { return unknown, features.ErrDataNotAvailable } - value := data.(*model.DeviceConfigurationKeyValueStringType) - return string(*value), nil } @@ -142,16 +141,15 @@ func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterfa } data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeBoolean) - if err != nil { - return false, err + if err != nil || data == nil { + return false, features.ErrDataNotAvailable } - if data == nil { + value, ok := data.(*bool) + if !ok || value == nil { return false, features.ErrDataNotAvailable } - value := data.(*bool) - return bool(*value), nil } @@ -279,7 +277,7 @@ func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64 // is the EV in sleep mode // returns operatingState, lastErrorCode, error -func (e *UCEVCC) EVInSleepMode( +func (e *UCEVCC) IsInSleepMode( entity spineapi.EntityRemoteInterface, ) (bool, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index da19465..d9ca525 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -451,11 +451,11 @@ func (s *UCEVCCSuite) Test_EVCurrentLimits() { } func (s *UCEVCCSuite) Test_EVInSleepMode() { - data, err := s.sut.EVInSleepMode(s.mockRemoteEntity) + data, err := s.sut.IsInSleepMode(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) - data, err = s.sut.EVInSleepMode(s.evEntity) + data, err = s.sut.IsInSleepMode(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -465,7 +465,7 @@ func (s *UCEVCCSuite) Test_EVInSleepMode() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVInSleepMode(s.evEntity) + data, err = s.sut.IsInSleepMode(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), false, data) @@ -476,7 +476,7 @@ func (s *UCEVCCSuite) Test_EVInSleepMode() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.EVInSleepMode(s.evEntity) + data, err = s.sut.IsInSleepMode(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), true, data) } diff --git a/ucevcem/api.go b/ucevcem/api.go index b186894..c306d56 100644 --- a/ucevcem/api.go +++ b/ucevcem/api.go @@ -12,7 +12,7 @@ type UCEVCEMInterface interface { api.UseCaseInterface // return the number of ac connected phases of the EV or 0 if it is unknown - ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) + PhasesConnected(entity spineapi.EntityRemoteInterface) (uint, error) // Scenario 1 @@ -27,5 +27,5 @@ type UCEVCEMInterface interface { // Scenario 3 // return the charged energy measurement in Wh of the connected EV - ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucevcem/events.go b/ucevcem/events.go index 5f11ee0..30b2f3c 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -57,7 +57,7 @@ func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { // the electrical connection description data of an EV was updated func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - if _, err := e.ConnectedPhases(entity); err != nil { + if _, err := e.PhasesConnected(entity); err != nil { return } diff --git a/ucevcem/public.go b/ucevcem/public.go index 759d5fb..abdd47b 100644 --- a/ucevcem/public.go +++ b/ucevcem/public.go @@ -11,7 +11,7 @@ import ( ) // return the number of ac connected phases of the EV or 0 if it is unknown -func (e *UCEVCEM) ConnectedPhases(entity spineapi.EntityRemoteInterface) (uint, error) { +func (e *UCEVCEM) PhasesConnected(entity spineapi.EntityRemoteInterface) (uint, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -160,7 +160,7 @@ func (e *UCEVCEM) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float6 // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEVCEM) ChargedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCEVCEM) EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } diff --git a/ucevcem/public_test.go b/ucevcem/public_test.go index 6d30003..c954bf4 100644 --- a/ucevcem/public_test.go +++ b/ucevcem/public_test.go @@ -7,11 +7,11 @@ import ( ) func (s *UCEVCEMSuite) Test_EVConnectedPhases() { - data, err := s.sut.ConnectedPhases(s.mockRemoteEntity) + data, err := s.sut.PhasesConnected(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), uint(0), data) - data, err = s.sut.ConnectedPhases(s.evEntity) + data, err = s.sut.PhasesConnected(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), uint(0), data) @@ -27,7 +27,7 @@ func (s *UCEVCEMSuite) Test_EVConnectedPhases() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.ConnectedPhases(s.evEntity) + data, err = s.sut.PhasesConnected(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), uint(0), data) @@ -43,7 +43,7 @@ func (s *UCEVCEMSuite) Test_EVConnectedPhases() { fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.ConnectedPhases(s.evEntity) + data, err = s.sut.PhasesConnected(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), uint(1), data) } @@ -244,11 +244,11 @@ func (s *UCEVCEMSuite) Test_EVPowerPerPhase_Current() { } func (s *UCEVCEMSuite) Test_EVChargedEnergy() { - data, err := s.sut.ChargedEnergy(s.mockRemoteEntity) + data, err := s.sut.EnergyCharged(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.ChargedEnergy(s.evEntity) + data, err = s.sut.EnergyCharged(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -267,7 +267,7 @@ func (s *UCEVCEMSuite) Test_EVChargedEnergy() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.ChargedEnergy(s.evEntity) + data, err = s.sut.EnergyCharged(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -283,7 +283,7 @@ func (s *UCEVCEMSuite) Test_EVChargedEnergy() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.ChargedEnergy(s.evEntity) + data, err = s.sut.EnergyCharged(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 80.0, data) } diff --git a/ucmgcp/api.go b/ucmgcp/api.go index f654df0..c975769 100644 --- a/ucmgcp/api.go +++ b/ucmgcp/api.go @@ -26,21 +26,21 @@ type UCMGCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) + Power(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 // return the total feed in energy at the grid connection point // // - negative values are used for production - TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyFeedIn(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 4 // return the total consumption energy at the grid connection point // // - positive values are used for consumption - TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 5 @@ -48,13 +48,13 @@ type UCMGCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) + CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 6 // return the voltage phase details at the grid connection point // - Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) + VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 7 diff --git a/ucmgcp/public.go b/ucmgcp/public.go index d2e80dd..bb86279 100644 --- a/ucmgcp/public.go +++ b/ucmgcp/public.go @@ -47,7 +47,11 @@ func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (f return 0, features.ErrDataNotAvailable } - value := data.(*model.ScaledNumberType) + value, ok := data.(*model.ScaledNumberType) + if !ok || value == nil { + return 0, features.ErrDataNotAvailable + } + return value.GetValue(), nil } @@ -57,45 +61,28 @@ func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (f // // - positive values are used for consumption // - negative values are used for production -func (e *UCMGCP) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMGCP) Power(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypePower - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACPowerTotal - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypePower, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACPowerTotal, + model.EnergyDirectionTypeConsume, + nil, + ) if err != nil { return 0, err } - - // we assume there is only one value - mId := data[0].MeasurementId - value := data[0].Value - if mId == nil || value == nil { + if len(values) != 1 { return 0, features.ErrDataNotAvailable } - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return 0, err - } - - // according to UC_TS_MonitoringOfGridConnectionPoint 3.2.2.2.4.1 - // positive values are with description "PositiveEnergyDirection" value "consume" - // but we verify this - desc, err := electricalConnection.GetDescriptionForMeasurementId(*mId) - if err != nil { - return 0, err - } - - // if energy direction is not consume, report an error - if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - return 0, features.ErrMissingData - } - - return value.GetValue(), nil + return values[0], nil } // Scenario 3 @@ -103,7 +90,7 @@ func (e *UCMGCP) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (flo // return the total feed in energy at the grid connection point // // - negative values are used for production -func (e *UCMGCP) TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMGCP) EnergyFeedIn(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -111,13 +98,16 @@ func (e *UCMGCP) TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeGridFeedIn - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.GetValuesForTypeCommodityScope(e.service, entity, measurement, commodity, scope) if err != nil { return 0, err } + if len(values) == 0 { + return 0, features.ErrDataNotAvailable + } // we assume thre is only one result - value := data[0].Value + value := values[0].Value if value == nil { return 0, features.ErrDataNotAvailable } @@ -130,7 +120,7 @@ func (e *UCMGCP) TotalFeedInEnergy(entity spineapi.EntityRemoteInterface) (float // return the total consumption energy at the grid connection point // // - positive values are used for consumption -func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMGCP) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -138,13 +128,16 @@ func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (flo measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeGridConsumption - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.GetValuesForTypeCommodityScope(e.service, entity, measurement, commodity, scope) if err != nil { return 0, err } + if len(values) == 0 { + return 0, features.ErrDataNotAvailable + } // we assume thre is only one result - value := data[0].Value + value := values[0].Value if value == nil { return 0, features.ErrDataNotAvailable } @@ -158,107 +151,39 @@ func (e *UCMGCP) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (flo // // - positive values are used for consumption // - negative values are used for production -func (e *UCMGCP) MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMGCP) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypeCurrent - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACCurrent - values, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) - if err != nil { - return nil, err - } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range values { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - // according to UC_TS_MonitoringOfGridConnectionPoint 3.2.1.3.2.4 - // positive values are with description "PositiveEnergyDirection" value "consume" - // but we should verify this - if desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { - // if energy direction is not consume, invert it - if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - return nil, err - } - } - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil + return util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypeCurrent, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACCurrent, + model.EnergyDirectionTypeConsume, + util.PhaseNameMapping, + ) } // Scenario 6 // return the voltage phase details at the grid connection point -func (e *UCMGCP) Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMGCP) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypeVoltage - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACVoltage - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) - if err != nil { - return nil, err - } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range data { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil + return util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypeVoltage, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACVoltage, + "", + util.PhaseNameMapping, + ) } // Scenario 7 @@ -272,35 +197,19 @@ func (e *UCMGCP) Frequency(entity spineapi.EntityRemoteInterface) (float64, erro measurement := model.MeasurementTypeTypeFrequency commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACFrequency - item, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.GetValuesForTypeCommodityScope(e.service, entity, measurement, commodity, scope) if err != nil { return 0, err } + if len(values) == 0 { + return 0, features.ErrDataNotAvailable + } // take the first item - value := item[0].Value + value := values[0].Value if value == nil { return 0, features.ErrDataNotAvailable } return value.GetValue(), nil } - -// helper - -func (e *UCMGCP) getValuesForTypeCommodityScope( - entity spineapi.EntityRemoteInterface, - measurement model.MeasurementTypeType, - commodity model.CommodityTypeType, - scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return nil, api.ErrNoCompatibleEntity - } - - measurementFeature, err := util.Measurement(e.service, entity) - if err != nil || measurementFeature == nil { - return nil, err - } - - return measurementFeature.GetValuesForTypeCommodityScope(measurement, commodity, scope) -} diff --git a/ucmgcp/public_test.go b/ucmgcp/public_test.go index b7dfbf6..6ab7757 100644 --- a/ucmgcp/public_test.go +++ b/ucmgcp/public_test.go @@ -51,12 +51,12 @@ func (s *UCMGCPSuite) Test_PowerLimitationFactor() { assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_MomentaryTotalPower() { - data, err := s.sut.MomentaryTotalPower(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_Power() { + data, err := s.sut.Power(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.MomentaryTotalPower(s.smgwEntity) + data, err = s.sut.Power(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -75,7 +75,7 @@ func (s *UCMGCPSuite) Test_MomentaryTotalPower() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.smgwEntity) + data, err = s.sut.Power(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -91,7 +91,7 @@ func (s *UCMGCPSuite) Test_MomentaryTotalPower() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.smgwEntity) + data, err = s.sut.Power(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -108,7 +108,7 @@ func (s *UCMGCPSuite) Test_MomentaryTotalPower() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.smgwEntity) + data, err = s.sut.Power(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -124,17 +124,17 @@ func (s *UCMGCPSuite) Test_MomentaryTotalPower() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.smgwEntity) + data, err = s.sut.Power(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_TotalFeedInEnergy() { - data, err := s.sut.TotalFeedInEnergy(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_EnergyFeedIn() { + data, err := s.sut.EnergyFeedIn(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalFeedInEnergy(s.smgwEntity) + data, err = s.sut.EnergyFeedIn(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -153,7 +153,7 @@ func (s *UCMGCPSuite) Test_TotalFeedInEnergy() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalFeedInEnergy(s.smgwEntity) + data, err = s.sut.EnergyFeedIn(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -169,17 +169,17 @@ func (s *UCMGCPSuite) Test_TotalFeedInEnergy() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalFeedInEnergy(s.smgwEntity) + data, err = s.sut.EnergyFeedIn(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_TotalConsumedEnergy() { - data, err := s.sut.TotalConsumedEnergy(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_EnergyConsumed() { + data, err := s.sut.EnergyConsumed(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalConsumedEnergy(s.smgwEntity) + data, err = s.sut.EnergyConsumed(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -198,7 +198,7 @@ func (s *UCMGCPSuite) Test_TotalConsumedEnergy() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalConsumedEnergy(s.smgwEntity) + data, err = s.sut.EnergyConsumed(s.smgwEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -214,17 +214,17 @@ func (s *UCMGCPSuite) Test_TotalConsumedEnergy() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalConsumedEnergy(s.smgwEntity) + data, err = s.sut.EnergyConsumed(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_MomentaryCurrents() { - data, err := s.sut.MomentaryCurrents(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_CurrentsPerPhase() { + data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.MomentaryCurrents(s.smgwEntity) + data, err = s.sut.CurrentsPerPhase(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -255,7 +255,7 @@ func (s *UCMGCPSuite) Test_MomentaryCurrents() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrents(s.smgwEntity) + data, err = s.sut.CurrentsPerPhase(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -279,9 +279,9 @@ func (s *UCMGCPSuite) Test_MomentaryCurrents() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrents(s.smgwEntity) + data, err = s.sut.CurrentsPerPhase(s.smgwEntity) assert.Nil(s.T(), err) - assert.NotNil(s.T(), data) + assert.Equal(s.T(), 0, len(data)) elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -319,18 +319,18 @@ func (s *UCMGCPSuite) Test_MomentaryCurrents() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrents(s.smgwEntity) + data, err = s.sut.CurrentsPerPhase(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) } -func (s *UCMGCPSuite) Test_Voltages() { - data, err := s.sut.Voltages(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_VoltagePerPhase() { + data, err := s.sut.VoltagePerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.Voltages(s.smgwEntity) + data, err = s.sut.VoltagePerPhase(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -361,7 +361,7 @@ func (s *UCMGCPSuite) Test_Voltages() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltages(s.smgwEntity) + data, err = s.sut.VoltagePerPhase(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -385,9 +385,9 @@ func (s *UCMGCPSuite) Test_Voltages() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltages(s.smgwEntity) + data, err = s.sut.VoltagePerPhase(s.smgwEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), []float64{0, 0, 0}, data) + assert.Equal(s.T(), 0, len(data)) elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -413,7 +413,7 @@ func (s *UCMGCPSuite) Test_Voltages() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltages(s.smgwEntity) + data, err = s.sut.VoltagePerPhase(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{230, 230, 230}, data) } diff --git a/ucmpc/api.go b/ucmpc/api.go index 0cc2b2c..599e026 100644 --- a/ucmpc/api.go +++ b/ucmpc/api.go @@ -18,26 +18,26 @@ type UCMCPInterface interface { // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) + Power(entity spineapi.EntityRemoteInterface) (float64, error) // return the momentary active phase specific power consumption or production per phase // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - MomentaryPhasePower(entity spineapi.EntityRemoteInterface) ([]float64, error) + PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 2 // return the total consumption energy // // - positive values are used for consumption - TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, error) // return the total feed in energy // // - negative values are used for production - TotalProducedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyProduced(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 @@ -45,13 +45,13 @@ type UCMCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) + CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 4 // return the phase specific voltage details // - Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) + VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 5 diff --git a/ucmpc/public.go b/ucmpc/public.go index 6229e74..082ffc8 100644 --- a/ucmpc/public.go +++ b/ucmpc/public.go @@ -15,42 +15,27 @@ import ( // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others -func (e *UCMPC) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMPC) Power(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypePower - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACPowerTotal - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypePower, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACPowerTotal, + model.EnergyDirectionTypeConsume, + nil, + ) if err != nil { return 0, err } - - // we assume there is only one value - mId := data[0].MeasurementId - value := data[0].Value - if mId == nil || value == nil { + if len(values) != 1 { return 0, features.ErrDataNotAvailable } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return 0, err - } - - desc, err := electricalConnection.GetDescriptionForMeasurementId(*mId) - if err != nil { - return 0, err - } - - // if energy direction is not consume, report an error - if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - return 0, features.ErrMissingData - } - - return value.GetValue(), nil + return values[0], nil } // return the momentary active phase specific power consumption or production per phase @@ -58,56 +43,20 @@ func (e *UCMPC) MomentaryTotalPower(entity spineapi.EntityRemoteInterface) (floa // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others -func (e *UCMPC) MomentaryPhasePower(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMPC) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypePower - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACPower - values, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) - if err != nil { - return nil, err - } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range values { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - if desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { - // if energy direction is not consume, invert it - if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - return nil, err - } - } - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil + return util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypePower, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACPower, + model.EnergyDirectionTypeConsume, + util.PhaseNameMapping, + ) } // Scenario 2 @@ -115,7 +64,7 @@ func (e *UCMPC) MomentaryPhasePower(entity spineapi.EntityRemoteInterface) ([]fl // return the total consumption energy // // - positive values are used for consumption -func (e *UCMPC) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMPC) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -123,13 +72,16 @@ func (e *UCMPC) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (floa measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACEnergyConsumed - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.GetValuesForTypeCommodityScope(e.service, entity, measurement, commodity, scope) if err != nil { return 0, err } + if len(values) == 0 { + return 0, features.ErrDataNotAvailable + } // we assume thre is only one result - value := data[0].Value + value := values[0].Value if value == nil { return 0, features.ErrDataNotAvailable } @@ -140,7 +92,7 @@ func (e *UCMPC) TotalConsumedEnergy(entity spineapi.EntityRemoteInterface) (floa // return the total feed in energy // // - negative values are used for production -func (e *UCMPC) TotalProducedEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCMPC) EnergyProduced(entity spineapi.EntityRemoteInterface) (float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -148,13 +100,16 @@ func (e *UCMPC) TotalProducedEnergy(entity spineapi.EntityRemoteInterface) (floa measurement := model.MeasurementTypeTypeEnergy commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACEnergyProduced - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.GetValuesForTypeCommodityScope(e.service, entity, measurement, commodity, scope) if err != nil { return 0, err } + if len(values) == 0 { + return 0, features.ErrDataNotAvailable + } // we assume thre is only one result - value := data[0].Value + value := values[0].Value if value == nil { return 0, features.ErrDataNotAvailable } @@ -168,104 +123,39 @@ func (e *UCMPC) TotalProducedEnergy(entity spineapi.EntityRemoteInterface) (floa // // - positive values are used for consumption // - negative values are used for production -func (e *UCMPC) MomentaryCurrents(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMPC) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypeCurrent - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACCurrent - values, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) - if err != nil { - return nil, err - } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range values { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - if desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId); err == nil { - // if energy direction is not consume, invert it - if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != model.EnergyDirectionTypeConsume { - return nil, err - } - } - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil + return util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypeCurrent, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACCurrent, + model.EnergyDirectionTypeConsume, + util.PhaseNameMapping, + ) } // Scenario 4 // return the phase specific voltage details -func (e *UCMPC) Voltages(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMPC) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } - measurement := model.MeasurementTypeTypeVoltage - commodity := model.CommodityTypeTypeElectricity - scope := model.ScopeTypeTypeACVoltage - data, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) - if err != nil { - return nil, err - } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return nil, err - } - - var phaseA, phaseB, phaseC float64 - - for _, item := range data { - if item.Value == nil || item.MeasurementId == nil { - continue - } - - param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) - if err != nil || param.AcMeasuredPhases == nil { - continue - } - - value := item.Value.GetValue() - - switch *param.AcMeasuredPhases { - case model.ElectricalConnectionPhaseNameTypeA: - phaseA = value - case model.ElectricalConnectionPhaseNameTypeB: - phaseB = value - case model.ElectricalConnectionPhaseNameTypeC: - phaseC = value - } - } - - return []float64{phaseA, phaseB, phaseC}, nil + return util.MeasurementValuesForTypeCommodityScope( + e.service, + entity, + model.MeasurementTypeTypeVoltage, + model.CommodityTypeTypeElectricity, + model.ScopeTypeTypeACVoltage, + "", + util.PhaseNameMapping, + ) } // Scenario 5 @@ -279,35 +169,21 @@ func (e *UCMPC) Frequency(entity spineapi.EntityRemoteInterface) (float64, error measurement := model.MeasurementTypeTypeFrequency commodity := model.CommodityTypeTypeElectricity scope := model.ScopeTypeTypeACFrequency - item, err := e.getValuesForTypeCommodityScope(entity, measurement, commodity, scope) + values, err := util.GetValuesForTypeCommodityScope(e.service, entity, measurement, commodity, scope) + if err != nil { return 0, err } + if len(values) == 0 { + return 0, features.ErrDataNotAvailable + } // take the first item - value := item[0].Value + value := values[0].Value + if value == nil { return 0, features.ErrDataNotAvailable } return value.GetValue(), nil } - -// helper - -func (e *UCMPC) getValuesForTypeCommodityScope( - entity spineapi.EntityRemoteInterface, - measurement model.MeasurementTypeType, - commodity model.CommodityTypeType, - scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return nil, api.ErrNoCompatibleEntity - } - - measurementFeature, err := util.Measurement(e.service, entity) - if err != nil || measurementFeature == nil { - return nil, err - } - - return measurementFeature.GetValuesForTypeCommodityScope(measurement, commodity, scope) -} diff --git a/ucmpc/public_test.go b/ucmpc/public_test.go index 0221068..dbf97a4 100644 --- a/ucmpc/public_test.go +++ b/ucmpc/public_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCMPCSuite) Test_MomentaryTotalPower() { - data, err := s.sut.MomentaryTotalPower(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_Power() { + data, err := s.sut.Power(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + data, err = s.sut.Power(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -30,7 +30,7 @@ func (s *UCMPCSuite) Test_MomentaryTotalPower() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + data, err = s.sut.Power(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -46,7 +46,7 @@ func (s *UCMPCSuite) Test_MomentaryTotalPower() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + data, err = s.sut.Power(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -63,7 +63,7 @@ func (s *UCMPCSuite) Test_MomentaryTotalPower() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + data, err = s.sut.Power(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -79,17 +79,17 @@ func (s *UCMPCSuite) Test_MomentaryTotalPower() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryTotalPower(s.monitoredEntity) + data, err = s.sut.Power(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } -func (s *UCMPCSuite) Test_MomentaryPhasePower() { - data, err := s.sut.MomentaryPhasePower(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_PowerPerPhase() { + data, err := s.sut.PowerPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + data, err = s.sut.PowerPerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -120,7 +120,7 @@ func (s *UCMPCSuite) Test_MomentaryPhasePower() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + data, err = s.sut.PowerPerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -144,9 +144,9 @@ func (s *UCMPCSuite) Test_MomentaryPhasePower() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + data, err = s.sut.PowerPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) - assert.NotNil(s.T(), data) + assert.Equal(s.T(), 0, len(data)) elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -184,18 +184,17 @@ func (s *UCMPCSuite) Test_MomentaryPhasePower() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryPhasePower(s.monitoredEntity) + data, err = s.sut.PowerPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) - } -func (s *UCMPCSuite) Test_TotalConsumedEnergy() { - data, err := s.sut.TotalConsumedEnergy(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_EnergyConsumed() { + data, err := s.sut.EnergyConsumed(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalConsumedEnergy(s.monitoredEntity) + data, err = s.sut.EnergyConsumed(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -214,7 +213,7 @@ func (s *UCMPCSuite) Test_TotalConsumedEnergy() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalConsumedEnergy(s.monitoredEntity) + data, err = s.sut.EnergyConsumed(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -230,17 +229,17 @@ func (s *UCMPCSuite) Test_TotalConsumedEnergy() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalConsumedEnergy(s.monitoredEntity) + data, err = s.sut.EnergyConsumed(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } -func (s *UCMPCSuite) Test_TotalProducedEnergy() { - data, err := s.sut.TotalProducedEnergy(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_EnergyProduced() { + data, err := s.sut.EnergyProduced(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalProducedEnergy(s.monitoredEntity) + data, err = s.sut.EnergyProduced(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -259,7 +258,7 @@ func (s *UCMPCSuite) Test_TotalProducedEnergy() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalProducedEnergy(s.monitoredEntity) + data, err = s.sut.EnergyProduced(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -275,17 +274,17 @@ func (s *UCMPCSuite) Test_TotalProducedEnergy() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalProducedEnergy(s.monitoredEntity) + data, err = s.sut.EnergyProduced(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } -func (s *UCMPCSuite) Test_MomentaryCurrents() { - data, err := s.sut.MomentaryCurrents(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_CurrentsPerPhase() { + data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -316,7 +315,7 @@ func (s *UCMPCSuite) Test_MomentaryCurrents() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -340,9 +339,9 @@ func (s *UCMPCSuite) Test_MomentaryCurrents() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) - assert.NotNil(s.T(), data) + assert.Equal(s.T(), 0, len(data)) elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -380,18 +379,18 @@ func (s *UCMPCSuite) Test_MomentaryCurrents() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.MomentaryCurrents(s.monitoredEntity) + data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) } -func (s *UCMPCSuite) Test_Voltages() { - data, err := s.sut.Voltages(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_VoltagePerPhase() { + data, err := s.sut.VoltagePerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.Voltages(s.monitoredEntity) + data, err = s.sut.VoltagePerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -422,7 +421,7 @@ func (s *UCMPCSuite) Test_Voltages() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltages(s.monitoredEntity) + data, err = s.sut.VoltagePerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -446,9 +445,9 @@ func (s *UCMPCSuite) Test_Voltages() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltages(s.monitoredEntity) + data, err = s.sut.VoltagePerPhase(s.monitoredEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), []float64{0, 0, 0}, data) + assert.Equal(s.T(), 0, len(data)) elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -474,7 +473,7 @@ func (s *UCMPCSuite) Test_Voltages() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.Voltages(s.monitoredEntity) + data, err = s.sut.VoltagePerPhase(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{230, 230, 230}, data) } diff --git a/ucvabd/api.go b/ucvabd/api.go index b559b30..248ac6d 100644 --- a/ucvabd/api.go +++ b/ucvabd/api.go @@ -14,20 +14,20 @@ type UCVABDInterface interface { // Scenario 1 // return the current (dis)charging power - CurrentChargePower(entity spineapi.EntityRemoteInterface) (float64, error) + Power(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 2 // return the cumulated battery system charge energy - TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 // return the cumulated battery system discharge energy - TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) + EnergyDischarged(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 4 // return the current state of charge of the battery system - CurrentStateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) + StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucvabd/public.go b/ucvabd/public.go index 00d8bf0..03a8bc5 100644 --- a/ucvabd/public.go +++ b/ucvabd/public.go @@ -16,7 +16,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVABD) CurrentChargePower(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVABD) Power(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -45,7 +45,7 @@ func (e *UCVABD) CurrentChargePower(entity spineapi.EntityRemoteInterface) (floa // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVABD) TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVABD) EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -72,7 +72,7 @@ func (e *UCVABD) TotalChargeEnergy(entity spineapi.EntityRemoteInterface) (float // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVABD) TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVABD) EnergyDischarged(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -99,7 +99,7 @@ func (e *UCVABD) TotalDischargeEnergy(entity spineapi.EntityRemoteInterface) (fl // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVABD) CurrentStateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVABD) StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } diff --git a/ucvabd/public_test.go b/ucvabd/public_test.go index 2293142..5dab7eb 100644 --- a/ucvabd/public_test.go +++ b/ucvabd/public_test.go @@ -7,11 +7,11 @@ import ( ) func (s *UCVABDSuite) Test_CurrentChargePower() { - data, err := s.sut.CurrentChargePower(s.mockRemoteEntity) + data, err := s.sut.Power(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.CurrentChargePower(s.batteryEntity) + data, err = s.sut.Power(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -30,7 +30,7 @@ func (s *UCVABDSuite) Test_CurrentChargePower() { fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargePower(s.batteryEntity) + data, err = s.sut.Power(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -46,17 +46,17 @@ func (s *UCVABDSuite) Test_CurrentChargePower() { fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargePower(s.batteryEntity) + data, err = s.sut.Power(s.batteryEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } func (s *UCVABDSuite) Test_TotalChargeEnergy() { - data, err := s.sut.TotalChargeEnergy(s.mockRemoteEntity) + data, err := s.sut.EnergyCharged(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalChargeEnergy(s.batteryEntity) + data, err = s.sut.EnergyCharged(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -75,7 +75,7 @@ func (s *UCVABDSuite) Test_TotalChargeEnergy() { fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalChargeEnergy(s.batteryEntity) + data, err = s.sut.EnergyCharged(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -91,17 +91,17 @@ func (s *UCVABDSuite) Test_TotalChargeEnergy() { fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalChargeEnergy(s.batteryEntity) + data, err = s.sut.EnergyCharged(s.batteryEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } func (s *UCVABDSuite) Test_TotalDischargeEnergy() { - data, err := s.sut.TotalDischargeEnergy(s.mockRemoteEntity) + data, err := s.sut.EnergyDischarged(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalDischargeEnergy(s.batteryEntity) + data, err = s.sut.EnergyDischarged(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -120,7 +120,7 @@ func (s *UCVABDSuite) Test_TotalDischargeEnergy() { fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalDischargeEnergy(s.batteryEntity) + data, err = s.sut.EnergyDischarged(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -136,17 +136,17 @@ func (s *UCVABDSuite) Test_TotalDischargeEnergy() { fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalDischargeEnergy(s.batteryEntity) + data, err = s.sut.EnergyDischarged(s.batteryEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } func (s *UCVABDSuite) Test_CurrentStateOfCharge() { - data, err := s.sut.CurrentStateOfCharge(s.mockRemoteEntity) + data, err := s.sut.StateOfCharge(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.CurrentStateOfCharge(s.batteryEntity) + data, err = s.sut.StateOfCharge(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -165,7 +165,7 @@ func (s *UCVABDSuite) Test_CurrentStateOfCharge() { fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentStateOfCharge(s.batteryEntity) + data, err = s.sut.StateOfCharge(s.batteryEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -181,7 +181,7 @@ func (s *UCVABDSuite) Test_CurrentStateOfCharge() { fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentStateOfCharge(s.batteryEntity) + data, err = s.sut.StateOfCharge(s.batteryEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } diff --git a/ucvapd/api.go b/ucvapd/api.go index 5fb4053..89a1d6c 100644 --- a/ucvapd/api.go +++ b/ucvapd/api.go @@ -14,15 +14,15 @@ type UCVAPDInterface interface { // Scenario 1 // return the current production power - CurrentProductionPower(entity spineapi.EntityRemoteInterface) (float64, error) + Power(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 2 // return the nominal peak power - NominalPeakPower(entity spineapi.EntityRemoteInterface) (float64, error) + PowerNominalPeak(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 // return total PV yield - TotalPVYield(entity spineapi.EntityRemoteInterface) (float64, error) + PVYieldTotal(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucvapd/public.go b/ucvapd/public.go index d84ef39..d28c2e8 100644 --- a/ucvapd/public.go +++ b/ucvapd/public.go @@ -13,7 +13,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVAPD) CurrentProductionPower(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVAPD) Power(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -42,7 +42,7 @@ func (e *UCVAPD) CurrentProductionPower(entity spineapi.EntityRemoteInterface) ( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVAPD) NominalPeakPower(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVAPD) PowerNominalPeak(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } @@ -66,9 +66,8 @@ func (e *UCVAPD) NominalPeakPower(entity spineapi.EntityRemoteInterface) (float6 return 0, features.ErrDataNotAvailable } - value := data.(*model.ScaledNumberType) - - if value == nil { + value, ok := data.(*model.ScaledNumberType) + if !ok || value == nil { return 0, features.ErrDataNotAvailable } @@ -80,7 +79,7 @@ func (e *UCVAPD) NominalPeakPower(entity spineapi.EntityRemoteInterface) (float6 // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCVAPD) TotalPVYield(entity spineapi.EntityRemoteInterface) (float64, error) { +func (e *UCVAPD) PVYieldTotal(entity spineapi.EntityRemoteInterface) (float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0, api.ErrNoCompatibleEntity } diff --git a/ucvapd/public_test.go b/ucvapd/public_test.go index a8308cb..ace8ccc 100644 --- a/ucvapd/public_test.go +++ b/ucvapd/public_test.go @@ -7,11 +7,11 @@ import ( ) func (s *UCVAPDSuite) Test_CurrentProductionPower() { - data, err := s.sut.CurrentProductionPower(s.mockRemoteEntity) + data, err := s.sut.Power(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.CurrentProductionPower(s.pvEntity) + data, err = s.sut.Power(s.pvEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -30,7 +30,7 @@ func (s *UCVAPDSuite) Test_CurrentProductionPower() { fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentProductionPower(s.pvEntity) + data, err = s.sut.Power(s.pvEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -46,17 +46,17 @@ func (s *UCVAPDSuite) Test_CurrentProductionPower() { fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentProductionPower(s.pvEntity) + data, err = s.sut.Power(s.pvEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } func (s *UCVAPDSuite) Test_NominalPeakPower() { - data, err := s.sut.NominalPeakPower(s.mockRemoteEntity) + data, err := s.sut.PowerNominalPeak(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.NominalPeakPower(s.pvEntity) + data, err = s.sut.PowerNominalPeak(s.pvEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -86,17 +86,17 @@ func (s *UCVAPDSuite) Test_NominalPeakPower() { fErr = confFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.NominalPeakPower(s.pvEntity) + data, err = s.sut.PowerNominalPeak(s.pvEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } func (s *UCVAPDSuite) Test_TotalPVYield() { - data, err := s.sut.TotalPVYield(s.mockRemoteEntity) + data, err := s.sut.PVYieldTotal(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) - data, err = s.sut.TotalPVYield(s.pvEntity) + data, err = s.sut.PVYieldTotal(s.pvEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -115,7 +115,7 @@ func (s *UCVAPDSuite) Test_TotalPVYield() { fErr := measurementFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalPVYield(s.pvEntity) + data, err = s.sut.PVYieldTotal(s.pvEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data) @@ -131,7 +131,7 @@ func (s *UCVAPDSuite) Test_TotalPVYield() { fErr = measurementFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.TotalPVYield(s.pvEntity) + data, err = s.sut.PVYieldTotal(s.pvEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data) } diff --git a/util/features_test.go b/util/features_test.go index ff08f9e..3b043f9 100644 --- a/util/features_test.go +++ b/util/features_test.go @@ -3,19 +3,19 @@ package util import "github.com/stretchr/testify/assert" func (s *UtilSuite) Test_Features() { - feature1, err := DeviceClassification(s.service, s.evEntity) + feature1, err := DeviceClassification(s.service, s.monitoredEntity) assert.Nil(s.T(), feature1) assert.NotNil(s.T(), err) - feature2, err := DeviceConfiguration(s.service, s.evEntity) + feature2, err := DeviceConfiguration(s.service, s.monitoredEntity) assert.Nil(s.T(), feature2) assert.NotNil(s.T(), err) - feature3, err := DeviceDiagnosis(s.service, s.evEntity) + feature3, err := DeviceDiagnosis(s.service, s.monitoredEntity) assert.Nil(s.T(), feature3) assert.NotNil(s.T(), err) - feature4, err := DeviceDiagnosisServer(s.service, s.evEntity) + feature4, err := DeviceDiagnosisServer(s.service, s.monitoredEntity) assert.Nil(s.T(), feature4) assert.NotNil(s.T(), err) @@ -23,7 +23,7 @@ func (s *UtilSuite) Test_Features() { assert.Nil(s.T(), feature5) assert.NotNil(s.T(), err) - feature6, err := Identification(s.service, s.evEntity) + feature6, err := Identification(s.service, s.monitoredEntity) assert.Nil(s.T(), feature6) assert.NotNil(s.T(), err) @@ -35,11 +35,11 @@ func (s *UtilSuite) Test_Features() { assert.Nil(s.T(), feature8) assert.NotNil(s.T(), err) - feature9, err := TimeSeries(s.service, s.evEntity) + feature9, err := TimeSeries(s.service, s.monitoredEntity) assert.Nil(s.T(), feature9) assert.NotNil(s.T(), err) - feature10, err := IncentiveTable(s.service, s.evEntity) + feature10, err := IncentiveTable(s.service, s.monitoredEntity) assert.Nil(s.T(), feature10) assert.NotNil(s.T(), err) } diff --git a/util/helper_test.go b/util/helper_test.go index dcc0d79..fa87223 100644 --- a/util/helper_test.go +++ b/util/helper_test.go @@ -19,7 +19,7 @@ func (s *UtilSuite) Test_IsCompatibleEntity() { assert.Equal(s.T(), false, result) payload = spineapi.EventPayload{ - Entity: s.evEntity, + Entity: s.monitoredEntity, } result = IsCompatibleEntity(payload.Entity, validEntityTypes) assert.Equal(s.T(), true, result) diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 82442ac..ec7eadd 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -19,7 +19,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -43,11 +43,11 @@ func (s *UtilSuite) Test_LoadControlLimits() { }, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{0.0, 0.0, 0.0}, data) @@ -74,11 +74,11 @@ func (s *UtilSuite) Test_LoadControlLimits() { }, } - rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -101,7 +101,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -130,7 +130,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.evEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{16.0, 16.0, 16.0}, data) } @@ -145,7 +145,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -172,11 +172,11 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { }, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) assert.NotNil(s.T(), err) assert.Nil(s.T(), msgCounter) @@ -274,7 +274,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err := WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) + msgCounter, err := WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -303,11 +303,11 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { LoadControlLimitDescriptionData: limitDesc, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -329,7 +329,7 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitListData, nil, nil) assert.Nil(s.T(), fErr) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, loadLimits) + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) assert.NotNil(t, err) assert.Nil(t, msgCounter) @@ -343,11 +343,11 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { }) } - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) - msgCounter, err = WriteLoadControlLimits(s.service, s.evEntity, entityTypes, category, phaseLimitValues) + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) } diff --git a/util/measurement.go b/util/measurement.go index 9127565..f6c347e 100644 --- a/util/measurement.go +++ b/util/measurement.go @@ -1,6 +1,8 @@ package util import ( + "slices" + eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" spineapi "github.com/enbility/spine-go/api" @@ -8,24 +10,101 @@ import ( ) // return the measurement value for a scope or an error -func MeasurementValueForScope(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, scope model.ScopeTypeType) (float64, error) { - evMeasurement, err := Measurement(service, entity) +func MeasurementValueForScope( + service eebusapi.ServiceInterface, + entity spineapi.EntityRemoteInterface, + scope model.ScopeTypeType) (float64, error) { + measurementF, err := Measurement(service, entity) if err != nil { return 0, features.ErrFunctionNotSupported } - if data, err := evMeasurement.GetDescriptionsForScope(scope); err == nil { + if data, err := measurementF.GetDescriptionsForScope(scope); err == nil { for _, item := range data { if item.MeasurementId == nil { continue } - if value, err := evMeasurement.GetValueForMeasurementId(*item.MeasurementId); err == nil { + if value, err := measurementF.GetValueForMeasurementId(*item.MeasurementId); err == nil { return value, nil } - } } return 0, features.ErrDataNotAvailable } + +// return the phase specific voltage details +func MeasurementValuesForTypeCommodityScope( + service eebusapi.ServiceInterface, + entity spineapi.EntityRemoteInterface, + measurementType model.MeasurementTypeType, + commodityType model.CommodityTypeType, + scopeType model.ScopeTypeType, + energyDirection model.EnergyDirectionType, + validPhaseNameTypes []model.ElectricalConnectionPhaseNameType, +) ([]float64, error) { + + measurement := measurementType + commodity := commodityType + scope := scopeType + data, err := GetValuesForTypeCommodityScope(service, entity, measurement, commodity, scope) + if err != nil { + return nil, err + } + + electricalConnection, err := ElectricalConnection(service, entity) + if err != nil || electricalConnection == nil { + return nil, err + } + + var result []float64 + + for _, item := range data { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + if validPhaseNameTypes != nil { + param, err := electricalConnection.GetParameterDescriptionForMeasurementId(*item.MeasurementId) + if err != nil || + param.AcMeasuredPhases == nil || + !slices.Contains(validPhaseNameTypes, *param.AcMeasuredPhases) { + continue + } + } + + if energyDirection != "" { + desc, err := electricalConnection.GetDescriptionForMeasurementId(*item.MeasurementId) + if err != nil { + continue + } + + // if energy direction is not consume + if desc.PositiveEnergyDirection == nil || *desc.PositiveEnergyDirection != energyDirection { + return nil, err + } + } + + value := item.Value.GetValue() + + result = append(result, value) + } + + return result, nil +} + +func GetValuesForTypeCommodityScope( + service eebusapi.ServiceInterface, + entity spineapi.EntityRemoteInterface, + measurement model.MeasurementTypeType, + commodity model.CommodityTypeType, + scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { + + measurementFeature, err := Measurement(service, entity) + if err != nil || measurementFeature == nil { + return nil, err + } + + return measurementFeature.GetValuesForTypeCommodityScope(measurement, commodity, scope) +} diff --git a/util/measurement_test.go b/util/measurement_test.go index aeff957..98df790 100644 --- a/util/measurement_test.go +++ b/util/measurement_test.go @@ -11,7 +11,7 @@ func (s *UtilSuite) Test_MeasurementValueForScope() { assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, value) - value, err = MeasurementValueForScope(s.service, s.evEntity, model.ScopeTypeTypeACPower) + value, err = MeasurementValueForScope(s.service, s.monitoredEntity, model.ScopeTypeTypeACPower) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, value) @@ -24,11 +24,11 @@ func (s *UtilSuite) Test_MeasurementValueForScope() { }, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - value, err = MeasurementValueForScope(s.service, s.evEntity, model.ScopeTypeTypeACPower) + value, err = MeasurementValueForScope(s.service, s.monitoredEntity, model.ScopeTypeTypeACPower) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, value) @@ -44,7 +44,158 @@ func (s *UtilSuite) Test_MeasurementValueForScope() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - value, err = MeasurementValueForScope(s.service, s.evEntity, model.ScopeTypeTypeACPower) + value, err = MeasurementValueForScope(s.service, s.monitoredEntity, model.ScopeTypeTypeACPower) assert.Nil(s.T(), err) assert.Equal(s.T(), 80.0, value) } + +func (s *UtilSuite) Test_MeasurementValuesForTypeCommodityScope() { + measurementType := model.MeasurementTypeTypePower + commodityType := model.CommodityTypeTypeElectricity + scopeType := model.ScopeTypeTypeACPower + energyDirection := model.EnergyDirectionTypeConsume + + data, err := MeasurementValuesForTypeCommodityScope( + s.service, + s.mockRemoteEntity, + measurementType, + commodityType, + scopeType, + energyDirection, + PhaseNameMapping, + ) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = MeasurementValuesForTypeCommodityScope( + s.service, + s.monitoredEntity, + measurementType, + commodityType, + scopeType, + energyDirection, + PhaseNameMapping, + ) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + MeasurementType: eebusutil.Ptr(model.MeasurementTypeTypePower), + CommodityType: eebusutil.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = MeasurementValuesForTypeCommodityScope( + s.service, + s.monitoredEntity, + measurementType, + commodityType, + scopeType, + energyDirection, + PhaseNameMapping, + ) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(10), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(10), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = MeasurementValuesForTypeCommodityScope( + s.service, + s.monitoredEntity, + measurementType, + commodityType, + scopeType, + energyDirection, + PhaseNameMapping, + ) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 0, len(data)) + + elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil) + assert.Nil(s.T(), fErr) + + elDescData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + PositiveEnergyDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + }, + }, + } + + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = MeasurementValuesForTypeCommodityScope( + s.service, + s.monitoredEntity, + measurementType, + commodityType, + scopeType, + energyDirection, + PhaseNameMapping, + ) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{10, 10, 10}, data) + +} diff --git a/util/testhelper_test.go b/util/testhelper_test.go index 0866d4a..0b8ca79 100644 --- a/util/testhelper_test.go +++ b/util/testhelper_test.go @@ -32,7 +32,7 @@ type UtilSuite struct { remoteDevice spineapi.DeviceRemoteInterface mockRemoteEntity *mocks.EntityRemoteInterface evseEntity spineapi.EntityRemoteInterface - evEntity spineapi.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface } func (s *UtilSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { @@ -67,7 +67,7 @@ func (s *UtilSuite) BeforeTest(suiteName, testName string) { s.remoteDevice, entities = setupDevices(s.service, s.T()) s.evseEntity = entities[0] - s.evEntity = entities[1] + s.monitoredEntity = entities[1] } const remoteSki string = "testremoteski" From 4a2ceb72e9f4c27a518ac452834ee83e47d4d8b7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 14:05:39 +0100 Subject: [PATCH 099/227] Update spine --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f3b4d5c..0bfc414 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240224142225-40e4dd17a99e + github.com/enbility/spine-go v0.0.0-20240225125500-721bfea8f484 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index de6c21b..726b3ff 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 h1:7+EgHCkVBaEVD github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00/go.mod h1:4Rqu0NjVY73per2BlwfG1KZPIkrpRtBi5cWwB57gCMQ= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240224142225-40e4dd17a99e h1:myGrJB826m7jsA5HsS2DTAFQvbAQeo6i/0VazX1ng7k= -github.com/enbility/spine-go v0.0.0-20240224142225-40e4dd17a99e/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240225125500-721bfea8f484 h1:avTaHvFo/x8Iw+xlrnkSrQjPz16hlVZ7MECbfH71uJ4= +github.com/enbility/spine-go v0.0.0-20240225125500-721bfea8f484/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From a5ecbc0be1e04a8318432fdc1fa3021984abb564 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 14:50:48 +0100 Subject: [PATCH 100/227] Add handling and events for device (dis-)connections - Updated UseCaseEventReaderInterface interface to provide the device --- api/api.go | 2 +- api/types.go | 8 +++ cem/cem.go | 9 ++- cem/cem_test.go | 109 ++++++++++++------------------------ cem/events.go | 21 +++++++ cem/events_test.go | 20 +++++++ cmd/democem/democem.go | 2 +- cmd/democem/eventreader.go | 2 +- go.mod | 6 ++ uccevc/events.go | 14 ++--- uccevc/testhelper_test.go | 2 +- ucevcc/events.go | 14 ++--- ucevcc/testhelper_test.go | 2 +- ucevcem/events.go | 8 +-- ucevcem/testhelper_test.go | 2 +- ucevsecc/events.go | 8 +-- ucevsecc/testhelper_test.go | 2 +- ucevsoc/events.go | 6 +- ucevsoc/testhelper_test.go | 2 +- ucmgcp/events.go | 12 ++-- ucmgcp/testhelper_test.go | 2 +- ucmpc/events.go | 14 ++--- ucmpc/testhelper_test.go | 2 +- ucopev/events.go | 2 +- ucopev/testhelper_test.go | 2 +- ucoscev/events.go | 2 +- ucoscev/testhelper_test.go | 2 +- ucvabd/events.go | 8 +-- ucvabd/testhelper_test.go | 2 +- ucvapd/events.go | 6 +- ucvapd/testhelper_test.go | 2 +- util/helper.go | 17 ++++-- util/helper_test.go | 18 ++++++ 33 files changed, 192 insertions(+), 138 deletions(-) create mode 100644 cem/events.go create mode 100644 cem/events_test.go diff --git a/api/api.go b/api/api.go index 55b14ae..335a580 100644 --- a/api/api.go +++ b/api/api.go @@ -50,5 +50,5 @@ type UseCaseInterface interface { // implemented by the actual CEM, used by UCEvseCCInterface implementation type UseCaseEventReaderInterface interface { // Inform about a new usecase specific event - SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event UseCaseEventType) + SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event UseCaseEventType) } diff --git a/api/types.go b/api/types.go index ea3b45e..7a1104a 100644 --- a/api/types.go +++ b/api/types.go @@ -151,6 +151,14 @@ const ( type UseCaseEventType string const ( + // CEM + + // Sent when a paired remote device was connected + DeviceConnected UseCaseEventType = "deviceConnected" + + // Sent when a paired remote device was disconnected + DeviceDisconnected UseCaseEventType = "deviceDisconnected" + // UCCEVC // EV provided an energy demand diff --git a/cem/cem.go b/cem/cem.go index 4d0d636..4c4b5b3 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -14,13 +14,20 @@ type Cem struct { Currency model.CurrencyType + reader api.UseCaseEventReaderInterface + usecases []api.UseCaseInterface } -func NewCEM(serviceDescription *eebusapi.Configuration, serviceHandler eebusapi.ServiceReaderInterface, log logging.LoggingInterface) *Cem { +func NewCEM( + serviceDescription *eebusapi.Configuration, + serviceHandler eebusapi.ServiceReaderInterface, + reader api.UseCaseEventReaderInterface, + log logging.LoggingInterface) *Cem { cem := &Cem{ Service: service.NewService(serviceDescription, serviceHandler), Currency: model.CurrencyTypeEur, + reader: reader, } cem.Service.SetLogging(log) diff --git a/cem/cem_test.go b/cem/cem_test.go index 8e7fb9a..473ce4b 100644 --- a/cem/cem_test.go +++ b/cem/cem_test.go @@ -1,7 +1,6 @@ package cem import ( - "fmt" "testing" "time" @@ -12,13 +11,28 @@ import ( "github.com/enbility/ship-go/cert" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" ) -func Test_CEM(t *testing.T) { +func TestCemSuite(t *testing.T) { + suite.Run(t, new(CemSuite)) +} + +type CemSuite struct { + suite.Suite + + sut *Cem + mockRemoteDevice *mocks.DeviceRemoteInterface +} + +func (s *CemSuite) BeforeTest(suiteName, testName string) { + s.mockRemoteDevice = mocks.NewDeviceRemoteInterface(s.T()) + certificate, err := cert.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-10") - assert.Nil(t, err) + assert.Nil(s.T(), err) configuration, err := eebusapi.NewConfiguration( "Demo", @@ -31,94 +45,45 @@ func Test_CEM(t *testing.T) { certificate, 230, time.Second*4) - assert.Nil(t, err) - - demo := &DemoCem{} - cem := NewCEM(configuration, demo, demo) - assert.NotNil(t, cem) - - err = cem.Setup() - assert.Nil(t, err) - - ucEvseCC := ucevsecc.NewUCEVSECC(cem.Service, cem.Service.LocalService(), demo) - cem.AddUseCase(ucEvseCC) - - cem.Start() - cem.Shutdown() + assert.Nil(s.T(), err) + noLogging := &logging.NoLogging{} + s.sut = NewCEM(configuration, s, s, noLogging) + assert.NotNil(s.T(), s.sut) } +func (s *CemSuite) Test_CEM() { + err := s.sut.Setup() + assert.Nil(s.T(), err) + + ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s.sut.Service.LocalService(), s) + s.sut.AddUseCase(ucEvseCC) -type DemoCem struct { + s.sut.Start() + s.sut.Shutdown() } // UseCaseEventReaderInterface - -func (d *DemoCem) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { - +func (d *CemSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } // eebusapi.ServiceReaderInterface // report the Ship ID of a newly trusted connection -func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { +func (d *CemSuite) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { // we should associated the Ship ID with the SKI and store it // so the next connection can start trusted logging.Log().Info("SKI", ski, "has Ship ID:", shipID) } -func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} - -func (d *DemoCem) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} - -func (d *DemoCem) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface, entries []shipapi.RemoteService) { -} - -func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} +func (d *CemSuite) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} -func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} +func (d *CemSuite) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} -func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } - -// Logging interface - -func (d *DemoCem) log(level string, args ...interface{}) { - t := time.Now() - fmt.Printf("%s: %s %s", t.Format(time.RFC3339), level, fmt.Sprintln(args...)) +func (d *CemSuite) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface, entries []shipapi.RemoteService) { } -func (d *DemoCem) logf(level, format string, args ...interface{}) { - t := time.Now() - fmt.Printf("%s: %s %s\n", t.Format(time.RFC3339), level, fmt.Sprintf(format, args...)) -} +func (h *CemSuite) ServiceShipIDUpdate(ski string, shipdID string) {} -func (d *DemoCem) Trace(args ...interface{}) { - d.log("TRACE", args...) -} +func (h *CemSuite) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} -func (d *DemoCem) Tracef(format string, args ...interface{}) { - d.logf("TRACE", format, args...) -} - -func (d *DemoCem) Debug(args ...interface{}) { - d.log("DEBUG", args...) -} - -func (d *DemoCem) Debugf(format string, args ...interface{}) { - d.logf("DEBUG", format, args...) -} - -func (d *DemoCem) Info(args ...interface{}) { - d.log("INFO", args...) -} - -func (d *DemoCem) Infof(format string, args ...interface{}) { - d.logf("INFO", format, args...) -} - -func (d *DemoCem) Error(args ...interface{}) { - d.log("ERROR", args...) -} - -func (d *DemoCem) Errorf(format string, args ...interface{}) { - d.logf("ERROR", format, args...) -} +func (h *CemSuite) AllowWaitingForTrust(ski string) bool { return true } diff --git a/cem/events.go b/cem/events.go new file mode 100644 index 0000000..e820fb8 --- /dev/null +++ b/cem/events.go @@ -0,0 +1,21 @@ +package cem + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + spineapi "github.com/enbility/spine-go/api" +) + +// handle SPINE events +func (h *Cem) HandleEvent(payload spineapi.EventPayload) { + + if util.IsDeviceConnected(payload) { + h.reader.SpineEvent(payload.Ski, payload.Device, nil, api.DeviceConnected) + return + } + + if util.IsDeviceDisconnected(payload) { + h.reader.SpineEvent(payload.Ski, payload.Device, nil, api.DeviceDisconnected) + return + } +} diff --git a/cem/events_test.go b/cem/events_test.go new file mode 100644 index 0000000..2cbf322 --- /dev/null +++ b/cem/events_test.go @@ -0,0 +1,20 @@ +package cem + +import ( + spineapi "github.com/enbility/spine-go/api" +) + +func (s *CemSuite) Test_Events() { + payload := spineapi.EventPayload{ + Device: s.mockRemoteDevice, + } + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDeviceChange + payload.ChangeType = spineapi.ElementChangeRemove + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDeviceChange + payload.ChangeType = spineapi.ElementChangeRemove + s.sut.HandleEvent(payload) +} diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index 1d1d08c..ea41843 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -13,7 +13,7 @@ type DemoCem struct { func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { demo := &DemoCem{} - demo.cem = cem.NewCEM(configuration, demo, demo) + demo.cem = cem.NewCEM(configuration, demo, demo, demo) return demo } diff --git a/cmd/democem/eventreader.go b/cmd/democem/eventreader.go index 124ee51..7fe8015 100644 --- a/cmd/democem/eventreader.go +++ b/cmd/democem/eventreader.go @@ -8,5 +8,5 @@ import ( var _ api.UseCaseEventReaderInterface = (*DemoCem)(nil) // Handle incomfing usecase specific event -func (h *DemoCem) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (h *DemoCem) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } diff --git a/go.mod b/go.mod index 0bfc414..f79bb1d 100644 --- a/go.mod +++ b/go.mod @@ -30,3 +30,9 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// replace github.com/enbility/eebus-go => ../eebus-go + +// replace github.com/enbility/ship-go => ../ship-go + +// replace github.com/enbility/spine-go => ../spine-go diff --git a/uccevc/events.go b/uccevc/events.go index c86a67f..67a91f7 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -113,7 +113,7 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.reader.SpineEvent(ski, entity, api.UCCEVCEnergyDemandProvided) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCEnergyDemandProvided) _, err = e.TimeSlotConstraints(entity) if err != nil { @@ -127,18 +127,18 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.reader.SpineEvent(ski, entity, api.UCCEVPowerLimitsRequested) - e.reader.SpineEvent(ski, entity, api.UCCEVCIncentivesRequested) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVPowerLimitsRequested) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCIncentivesRequested) } // the load control limit data of an EV was updated func (e *UCCEVC) evTimeSeriesDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if _, err := e.ChargePlan(entity); err == nil { - e.reader.SpineEvent(ski, entity, api.UCCEVCChargePlanProvided) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCChargePlanProvided) } if _, err := e.ChargePlanConstraints(entity); err == nil { - e.reader.SpineEvent(ski, entity, api.UCCEVCChargePlanConstraintsProvided) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCChargePlanConstraintsProvided) } } @@ -156,12 +156,12 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spinea return } - e.reader.SpineEvent(ski, entity, api.UCCEVCIncentiveDescriptionsRequired) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCIncentiveDescriptionsRequired) } // the load control limit data of an EV was updated func (e *UCCEVC) evIncentiveTableDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, api.UCCEVCIncentiveTableDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCIncentiveTableDataUpdate) } // check timeSeries descriptions if constraints element has updateRequired set to true diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index c8db988..1836b99 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -36,7 +36,7 @@ type UCCEVCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCCEVCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCCEVCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevcc/events.go b/ucevcc/events.go index 245fa44..9412c9c 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -112,12 +112,12 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - e.reader.SpineEvent(ski, entity, api.UCEVCCEventConnected) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCEventConnected) } // an EV was disconnected func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, api.UCEVCCEventDisconnected) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCEventDisconnected) } // the configuration key description data of an EV was updated @@ -139,12 +139,12 @@ func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRem // Scenario 2 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) } // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) } } @@ -162,7 +162,7 @@ func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRe continue } - e.reader.SpineEvent(ski, entity, api.UCEVCCIdentificationDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCIdentificationDataUpdate) return } } @@ -177,7 +177,7 @@ func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemo // Scenario 5 if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCCManufacturerDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCManufacturerDataUpdate) } } @@ -209,5 +209,5 @@ func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.E } // Scenario 6 - e.reader.SpineEvent(ski, entity, api.UCEVCCChargingPowerLimitsDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCChargingPowerLimitsDataUpdate) } diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index debdf02..dc34839 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVCCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCEVCCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVCCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevcem/events.go b/ucevcem/events.go index 30b2f3c..746dfb2 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -61,7 +61,7 @@ func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity return } - e.reader.SpineEvent(ski, entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) } // the measurement description data of an EV was updated @@ -78,16 +78,16 @@ func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemot func (e *UCEVCEM) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCEMCurrentMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMCurrentMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCEMPowerMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMPowerMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) } } diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index 997c455..235e948 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVCEMSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCEVCEMSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVCEMSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevsecc/events.go b/ucevsecc/events.go index a09286e..74ec91f 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -55,12 +55,12 @@ func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterfa _, _ = evseDeviceDiagnosis.RequestState() } - e.reader.SpineEvent(ski, entity, api.UCEVSECCEventConnected) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventConnected) } // an EVSE was disconnected func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity, api.UCEVSECCEventDisconnected) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventDisconnected) } // the manufacturer Data of an EVSE was updated @@ -71,7 +71,7 @@ func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.Entity } if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSECCEventManufacturerUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventManufacturerUpdate) } } @@ -83,6 +83,6 @@ func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInter } if _, err := evDeviceDiagnosis.GetState(); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSECCEventOperationStateUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventOperationStateUpdate) } } diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index 3b74855..4576e95 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVSECCSuite struct { evseEntity spineapi.EntityRemoteInterface } -func (s *UCEVSECCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVSECCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 15dd278..45f4a52 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -58,16 +58,16 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfHealth); err == nil { - e.reader.SpineEvent(ski, entity, api.EVSOCStateOfHealthMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.EVSOCStateOfHealthMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeTravelRange); err == nil { - e.reader.SpineEvent(ski, entity, api.UCEVSOCActualRangeMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSOCActualRangeMeasurementDataUpdate) } } diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 4d8ac43..838e2ae 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVSOCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCEVSOCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVSOCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 74ed2eb..bcf23a8 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -104,32 +104,32 @@ func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemo func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPPowerTotalMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPPowerTotalMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridFeedIn); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPGridFeedInMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPGridFeedInMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridConsumption); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPCurrentsMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPCurrentsMeasurementDataUpdate) } // Scenario 6 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPVoltagesMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPVoltagesMeasurementDataUpdate) } // Scenario 7 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMGCPFrequencyMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPFrequencyMeasurementDataUpdate) } } diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index 47b20d7..4ab868f 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -36,7 +36,7 @@ type UCMGCPSuite struct { smgwEntity spineapi.EntityRemoteInterface } -func (s *UCMGCPSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCMGCPSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { diff --git a/ucmpc/events.go b/ucmpc/events.go index bed1d60..c3a7ee1 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -81,35 +81,35 @@ func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRem func (e *UCMPC) deviceMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCPowerTotalMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCPowerTotalMeasurementDataUpdate) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyProduced); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCCurrentsMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCCurrentsMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCVoltagesMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCVoltagesMeasurementDataUpdate) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.reader.SpineEvent(ski, entity, api.UCMPCFrequencyMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCFrequencyMeasurementDataUpdate) } } diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index 8226b29..c5484bf 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -36,7 +36,7 @@ type UCMPCSuite struct { monitoredEntity spineapi.EntityRemoteInterface } -func (s *UCMPCSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCMPCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucopev/events.go b/ucopev/events.go index e814110..d00d3c6 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -86,7 +86,7 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entity continue } - e.reader.SpineEvent(ski, entity, api.UCOPEVLoadControlLimitDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) return } diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index 18312b4..05916c1 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -36,7 +36,7 @@ type UCOPEVSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCOPEVSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCOPEVSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { diff --git a/ucoscev/events.go b/ucoscev/events.go index 4325e2e..9a0efbd 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -51,7 +51,7 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entit continue } - e.reader.SpineEvent(ski, entity, api.UCOPEVLoadControlLimitDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) return } diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index f0e8d08..8ddb525 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -36,7 +36,7 @@ type UCOSCEVSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCOSCEVSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCOSCEVSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { diff --git a/ucvabd/events.go b/ucvabd/events.go index 6b16102..de091e3 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -81,21 +81,21 @@ func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVABD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVABDPowerTotalMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDPowerTotalMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVABDChargeMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeDischarge); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVABDDischargeMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDDischargeMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVABDStateOfChargeMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDStateOfChargeMeasurementDataUpdate) } } diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go index 9af425c..4d02078 100644 --- a/ucvabd/testhelper_test.go +++ b/ucvabd/testhelper_test.go @@ -36,7 +36,7 @@ type UCVABDSuite struct { batteryEntity spineapi.EntityRemoteInterface } -func (s *UCVABDSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCVABDSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCVABDSuite) BeforeTest(suiteName, testName string) { diff --git a/ucvapd/events.go b/ucvapd/events.go index 7ea89d6..6e360c5 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -97,7 +97,7 @@ func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.Ent // Scenario 1 if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { if _, err := deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVAPDPeakPowerDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVAPDPeakPowerDataUpdate) } } } @@ -116,11 +116,11 @@ func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVAPD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVAPDPowerTotalMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVAPDPowerTotalMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACYieldTotal); err == nil { - e.reader.SpineEvent(ski, entity, api.UCVAPDYieldTotalMeasurementDataUpdate) + e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVAPDYieldTotalMeasurementDataUpdate) } } diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index 7ced303..eaa1cf8 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -36,7 +36,7 @@ type UCVAPDSuite struct { pvEntity spineapi.EntityRemoteInterface } -func (s *UCVAPDSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCVAPDSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { } func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { diff --git a/util/helper.go b/util/helper.go index 6d3e5cd..64e7283 100644 --- a/util/helper.go +++ b/util/helper.go @@ -17,13 +17,21 @@ func IsCompatibleEntity(entity spineapi.EntityRemoteInterface, entityTypes []mod return slices.Contains(entityTypes, entity.EntityType()) } +func IsDeviceConnected(payload spineapi.EventPayload) bool { + return payload.Device != nil && + payload.EventType == spineapi.EventTypeDeviceChange && + payload.ChangeType == spineapi.ElementChangeAdd +} + func IsDeviceDisconnected(payload spineapi.EventPayload) bool { - return (payload.EventType == spineapi.EventTypeDeviceChange && - payload.ChangeType == spineapi.ElementChangeRemove) + return payload.Device != nil && + payload.EventType == spineapi.EventTypeDeviceChange && + payload.ChangeType == spineapi.ElementChangeRemove } func IsEntityConnected(payload spineapi.EventPayload) bool { - if payload.EventType == spineapi.EventTypeEntityChange && + if payload.Entity != nil && + payload.EventType == spineapi.EventTypeEntityChange && payload.ChangeType == spineapi.ElementChangeAdd { return true } @@ -32,7 +40,8 @@ func IsEntityConnected(payload spineapi.EventPayload) bool { } func IsEntityDisconnected(payload spineapi.EventPayload) bool { - if payload.EventType == spineapi.EventTypeEntityChange && + if payload.Entity != nil && + payload.EventType == spineapi.EventTypeEntityChange && payload.ChangeType == spineapi.ElementChangeRemove { return true } diff --git a/util/helper_test.go b/util/helper_test.go index fa87223..fb325ea 100644 --- a/util/helper_test.go +++ b/util/helper_test.go @@ -2,6 +2,7 @@ package util import ( spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) @@ -25,12 +26,29 @@ func (s *UtilSuite) Test_IsCompatibleEntity() { assert.Equal(s.T(), true, result) } +func (s *UtilSuite) Test_IsDeviceConnected() { + payload := spineapi.EventPayload{} + result := IsDeviceConnected(payload) + assert.Equal(s.T(), false, result) + + device := mocks.NewDeviceRemoteInterface(s.T()) + payload = spineapi.EventPayload{ + Device: device, + EventType: spineapi.EventTypeDeviceChange, + ChangeType: spineapi.ElementChangeAdd, + } + result = IsDeviceConnected(payload) + assert.Equal(s.T(), true, result) +} + func (s *UtilSuite) Test_IsDeviceDisconnected() { payload := spineapi.EventPayload{} result := IsDeviceDisconnected(payload) assert.Equal(s.T(), false, result) + device := mocks.NewDeviceRemoteInterface(s.T()) payload = spineapi.EventPayload{ + Device: device, EventType: spineapi.EventTypeDeviceChange, ChangeType: spineapi.ElementChangeRemove, } From 4afa6cefaf6437c345e6d23e9e59c9b2d8297897 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 15:36:43 +0100 Subject: [PATCH 101/227] Use generic Events --- api/api.go | 16 +++-- api/types.go | 113 ++++++++++++++++++------------------ cem/cem.go | 4 +- cem/cem_test.go | 4 +- cem/events.go | 4 +- cmd/democem/democem.go | 4 +- cmd/democem/eventreader.go | 6 +- cmd/democem/logging.go | 50 ---------------- cmd/democem/service.go | 4 -- uccevc/events.go | 14 ++--- uccevc/testhelper_test.go | 2 +- uccevc/uccevc.go | 4 +- ucevcc/events.go | 14 ++--- ucevcc/testhelper_test.go | 2 +- ucevcc/ucevcc.go | 4 +- ucevcem/events.go | 8 +-- ucevcem/testhelper_test.go | 2 +- ucevcem/ucevcem.go | 4 +- ucevsecc/events.go | 8 +-- ucevsecc/testhelper_test.go | 2 +- ucevsecc/ucevsecc.go | 4 +- ucevsoc/events.go | 6 +- ucevsoc/testhelper_test.go | 2 +- ucevsoc/ucevsoc.go | 4 +- ucmgcp/events.go | 12 ++-- ucmgcp/testhelper_test.go | 2 +- ucmgcp/ucmgcp.go | 4 +- ucmpc/events.go | 14 ++--- ucmpc/testhelper_test.go | 2 +- ucmpc/ucmcp.go | 4 +- ucopev/events.go | 2 +- ucopev/testhelper_test.go | 2 +- ucopev/ucopev.go | 4 +- ucoscev/events.go | 2 +- ucoscev/testhelper_test.go | 2 +- ucoscev/ucoscev.go | 4 +- ucvabd/events.go | 8 +-- ucvabd/testhelper_test.go | 2 +- ucvabd/ucvabd.go | 4 +- ucvapd/events.go | 6 +- ucvapd/testhelper_test.go | 2 +- ucvapd/ucvapd.go | 4 +- util/testhelper_test.go | 2 +- 43 files changed, 158 insertions(+), 209 deletions(-) delete mode 100644 cmd/democem/logging.go diff --git a/api/api.go b/api/api.go index 335a580..e7ecf80 100644 --- a/api/api.go +++ b/api/api.go @@ -41,14 +41,12 @@ type UseCaseInterface interface { IsUseCaseSupported(remoteEntity spineapi.EntityRemoteInterface) (bool, error) } -// interface for informing the cem about specific events -// for each supported usecase +// interface for informing the HEMS about specific events // -// UseCaseEventType values can be found in the api definition of each -// supported usecase -// -// implemented by the actual CEM, used by UCEvseCCInterface implementation -type UseCaseEventReaderInterface interface { - // Inform about a new usecase specific event - SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event UseCaseEventType) +// implemented by the actual HEMS +type EventReaderInterface interface { + // Inform about a new cem or usecase specific event + // + // used by use case implementations + Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event EventType) } diff --git a/api/types.go b/api/types.go index 7a1104a..e53b5a3 100644 --- a/api/types.go +++ b/api/types.go @@ -147,84 +147,87 @@ const ( UCEVCCCommunicationStandardUnknown string = "unknown" ) -// type for usecase specfic event names -type UseCaseEventType string +// type for cem and usecase specfic event names +type EventType string const ( // CEM - // Sent when a paired remote device was connected - DeviceConnected UseCaseEventType = "deviceConnected" + // A paired remote device was connected + DeviceConnected EventType = "deviceConnected" - // Sent when a paired remote device was disconnected - DeviceDisconnected UseCaseEventType = "deviceDisconnected" + // A paired remote device was disconnected + DeviceDisconnected EventType = "deviceDisconnected" + + // Visible remote eebus services list was updated + VisibleRemoteServicesUpdated EventType = "visibleRemoteServicesUpdated" // UCCEVC // EV provided an energy demand - UCCEVCEnergyDemandProvided UseCaseEventType = "ucCEVCEnergyDemandProvided" + UCCEVCEnergyDemandProvided EventType = "ucCEVCEnergyDemandProvided" // EV provided a charge plan - UCCEVCChargePlanProvided UseCaseEventType = "ucCEVCChargePlanProvided" + UCCEVCChargePlanProvided EventType = "ucCEVCChargePlanProvided" // EV provided a charge plan constraints - UCCEVCChargePlanConstraintsProvided UseCaseEventType = "ucCEVCChargePlanConstraintsProvided" + UCCEVCChargePlanConstraintsProvided EventType = "ucCEVCChargePlanConstraintsProvided" - UCCEVCIncentiveDescriptionsRequired UseCaseEventType = "ucCEVCIncentiveDescriptionsRequired" + UCCEVCIncentiveDescriptionsRequired EventType = "ucCEVCIncentiveDescriptionsRequired" // EV incentive table data updated - UCCEVCIncentiveTableDataUpdate UseCaseEventType = "ucCEVCIncentiveTableDataUpdate" + UCCEVCIncentiveTableDataUpdate EventType = "ucCEVCIncentiveTableDataUpdate" // EV requested power limits - UCCEVPowerLimitsRequested UseCaseEventType = "ucCEVPowerLimitsRequested" + UCCEVPowerLimitsRequested EventType = "ucCEVPowerLimitsRequested" // EV requested incentives - UCCEVCIncentivesRequested UseCaseEventType = "ucCEVCIncentivesRequested" + UCCEVCIncentivesRequested EventType = "ucCEVCIncentivesRequested" // UCEVCC // An EV was connected // // Use Case EVCC, Scenario 1 - UCEVCCEventConnected UseCaseEventType = "ucEVCCEventConnected" + UCEVCCEventConnected EventType = "ucEVCCEventConnected" // An EV was disconnected // // Use Case EVCC, Scenario 8 - UCEVCCEventDisconnected UseCaseEventType = "ucEVCCEventDisconnected" + UCEVCCEventDisconnected EventType = "ucEVCCEventDisconnected" // EV communication standard data was updated // // Use Case EVCC, Scenario 2 // Note: the referred data may be updated together with all other configuration items of this use case - UCEVCCCommunicationStandardConfigurationDataUpdate UseCaseEventType = "ucEVCCCommunicationStandardConfigurationDataUpdate" + UCEVCCCommunicationStandardConfigurationDataUpdate EventType = "ucEVCCCommunicationStandardConfigurationDataUpdate" // EV asymmetric charging data was updated // // Use Case EVCC, Scenario 3 // // Note: the referred data may be updated together with all other configuration items of this use case - UCEVCCAsymmetricChargingConfigurationDataUpdate UseCaseEventType = "ucEVCCAsymmetricChargingConfigurationDataUpdate" + UCEVCCAsymmetricChargingConfigurationDataUpdate EventType = "ucEVCCAsymmetricChargingConfigurationDataUpdate" // EV identificationdata was updated // // Use Case EVCC, Scenario 4 - UCEVCCIdentificationDataUpdate UseCaseEventType = "ucEVCCIdentificationDataUpdate" + UCEVCCIdentificationDataUpdate EventType = "ucEVCCIdentificationDataUpdate" // EV manufacturer data was updated // // Use Case EVCC, Scenario 5 - UCEVCCManufacturerDataUpdate UseCaseEventType = "ucEVCCManufacturerDataUpdate" + UCEVCCManufacturerDataUpdate EventType = "ucEVCCManufacturerDataUpdate" // EV charging power limits // // Use Case EVCC, Scenario 6 - UCEVCCChargingPowerLimitsDataUpdate UseCaseEventType = "ucEVCCChargingPowerLimitsDataUpdate" + UCEVCCChargingPowerLimitsDataUpdate EventType = "ucEVCCChargingPowerLimitsDataUpdate" // EV permitted power limits updated // // Use Case EVCC, Scenario 7 - UCEVCCSleepModeDataUpdate UseCaseEventType = "ucEVCCSleepModeDataUpdate" + UCEVCCSleepModeDataUpdate EventType = "ucEVCCSleepModeDataUpdate" // UCEVCEM @@ -233,46 +236,46 @@ const ( // Use Case EVCEM, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMNumberOfConnectedPhasesDataUpdate UseCaseEventType = "ucEVCEMNumberOfConnectedPhasesDataUpdate" + UCEVCEMNumberOfConnectedPhasesDataUpdate EventType = "ucEVCEMNumberOfConnectedPhasesDataUpdate" // EV current measurement data updated // // Use Case EVCEM, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMCurrentMeasurementDataUpdate UseCaseEventType = "ucEVCEMCurrentMeasurementDataUpdate" + UCEVCEMCurrentMeasurementDataUpdate EventType = "ucEVCEMCurrentMeasurementDataUpdate" // EV power measurement data updated // // Use Case EVCEM, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMPowerMeasurementDataUpdate UseCaseEventType = "ucEVCEMCurrentMeasurementDataUpdate" + UCEVCEMPowerMeasurementDataUpdate EventType = "ucEVCEMCurrentMeasurementDataUpdate" // EV charging energy measurement data updated // // Use Case EVCEM, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMChargingEnergyMeasurementDataUpdate UseCaseEventType = "UCEVCEMChargingEnergyMeasurementDataUpdate" + UCEVCEMChargingEnergyMeasurementDataUpdate EventType = "UCEVCEMChargingEnergyMeasurementDataUpdate" // UCEVSECC // An EVSE was connected - UCEVSECCEventConnected UseCaseEventType = "ucEVSEConnected" + UCEVSECCEventConnected EventType = "ucEVSEConnected" // An EVSE was disconnected - UCEVSECCEventDisconnected UseCaseEventType = "ucEVSEDisonnected" + UCEVSECCEventDisconnected EventType = "ucEVSEDisonnected" // EVSE manufacturer data was updated // // Use Case EVSECC, Scenario 1 - UCEVSECCEventManufacturerUpdate UseCaseEventType = "ucEVSEManufacturerUpdate" + UCEVSECCEventManufacturerUpdate EventType = "ucEVSEManufacturerUpdate" // EVSE operation state was updated // // Use Case EVSECC, Scenario 2 - UCEVSECCEventOperationStateUpdate UseCaseEventType = "ucEVSEOperationStateUpdate" + UCEVSECCEventOperationStateUpdate EventType = "ucEVSEOperationStateUpdate" // UCEVSOC @@ -281,28 +284,28 @@ const ( // Use Case EVSOC, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVSOCStateOfChargeMeasurementDataUpdate UseCaseEventType = "ucEVSOCStateOfChargeMeasurementDataUpdate" + UCEVSOCStateOfChargeMeasurementDataUpdate EventType = "ucEVSOCStateOfChargeMeasurementDataUpdate" // EV nominal capacity data was updated // // Use Case EVSOC, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVSOCNominalCapacityMeasurementDataUpdate UseCaseEventType = "ucEVSOCNominalCapacityMeasurementDataUpdate" + UCEVSOCNominalCapacityMeasurementDataUpdate EventType = "ucEVSOCNominalCapacityMeasurementDataUpdate" // EV state of health data was updated // // Use Case EVSOC, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - EVSOCStateOfHealthMeasurementDataUpdate UseCaseEventType = "ucEVSOCStateOfHealthMeasurementDataUpdate" + EVSOCStateOfHealthMeasurementDataUpdate EventType = "ucEVSOCStateOfHealthMeasurementDataUpdate" // EV actual range data was updated // // Use Case EVSOC, Scenario 4 // // Note: the referred data may be updated together with all other measurement items of this use case - UCEVSOCActualRangeMeasurementDataUpdate UseCaseEventType = "ucEVSOCActualRangeMeasurementDataUpdate" + UCEVSOCActualRangeMeasurementDataUpdate EventType = "ucEVSOCActualRangeMeasurementDataUpdate" // MGCP @@ -311,42 +314,42 @@ const ( // Use Case MGCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPPowerTotalMeasurementDataUpdate UseCaseEventType = "ucMGCPPowerTotalMeasurementDataUpdate" + UCMGCPPowerTotalMeasurementDataUpdate EventType = "ucMGCPPowerTotalMeasurementDataUpdate" // MTotal grid feed in energy data updated // // Use Case MGCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPGridFeedInMeasurementDataUpdate UseCaseEventType = "ucMGCPGridFeedInMeasurementDataUpdate" + UCMGCPGridFeedInMeasurementDataUpdate EventType = "ucMGCPGridFeedInMeasurementDataUpdate" // Total grid consumed energy data updated // // Use Case MGCP, Scenario 4 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPGridConsumptionMeasurementDataUpdate UseCaseEventType = "ucMGCPGridConsumptionMeasurementDataUpdate" + UCMGCPGridConsumptionMeasurementDataUpdate EventType = "ucMGCPGridConsumptionMeasurementDataUpdate" // Phase specific momentary current consumption/production phase detail data updated // // Use Case MGCP, Scenario 5 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPCurrentsMeasurementDataUpdate UseCaseEventType = "ucMGCPCurrentsMeasurementDataUpdate" + UCMGCPCurrentsMeasurementDataUpdate EventType = "ucMGCPCurrentsMeasurementDataUpdate" // Phase specific voltage at the grid connection point // // Use Case MGCP, Scenario 6 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPVoltagesMeasurementDataUpdate UseCaseEventType = "ucMGCPVoltagesMeasurementDataUpdate" + UCMGCPVoltagesMeasurementDataUpdate EventType = "ucMGCPVoltagesMeasurementDataUpdate" // Grid frequency data updated // // Use Case MGCP, Scenario 7 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPFrequencyMeasurementDataUpdate UseCaseEventType = "ucMGCPFrequencyMeasurementDataUpdate" + UCMGCPFrequencyMeasurementDataUpdate EventType = "ucMGCPFrequencyMeasurementDataUpdate" // UCMPC @@ -355,61 +358,61 @@ const ( // Use Case MCP, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCPowerTotalMeasurementDataUpdate UseCaseEventType = "ucMPCPowerTotalMeasurementDataUpdate" + UCMPCPowerTotalMeasurementDataUpdate EventType = "ucMPCPowerTotalMeasurementDataUpdate" // Phase specific momentary active power consumption or production // // Use Case MCP, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCPowerPerPhaseMeasurementDataUpdate UseCaseEventType = "ucMPCPowerPerPhaseMeasurementDataUpdate" + UCMPCPowerPerPhaseMeasurementDataUpdate EventType = "ucMPCPowerPerPhaseMeasurementDataUpdate" // Total energy consumed // // Use Case MCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCEnergyConsumedMeasurementDataUpdate UseCaseEventType = "ucMPCEnergyConsumedMeasurementDataUpdate" + UCMPCEnergyConsumedMeasurementDataUpdate EventType = "ucMPCEnergyConsumedMeasurementDataUpdate" // Total energy produced // // Use Case MCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCEnergyProcudedMeasurementDataUpdate UseCaseEventType = "ucMPCEnergyProcudedMeasurementDataUpdate" + UCMPCEnergyProcudedMeasurementDataUpdate EventType = "ucMPCEnergyProcudedMeasurementDataUpdate" // Phase specific momentary current consumption or production // // Use Case MCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCCurrentsMeasurementDataUpdate UseCaseEventType = "ucMPCCurrentsMeasurementDataUpdate" + UCMPCCurrentsMeasurementDataUpdate EventType = "ucMPCCurrentsMeasurementDataUpdate" // Phase specific voltage // // Use Case MCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCVoltagesMeasurementDataUpdate UseCaseEventType = "ucMPCVoltagesMeasurementDataUpdate" + UCMPCVoltagesMeasurementDataUpdate EventType = "ucMPCVoltagesMeasurementDataUpdate" // Power network frequency data updated // // Use Case MCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCFrequencyMeasurementDataUpdate UseCaseEventType = "ucMPCFrequencyMeasurementDataUpdate" + UCMPCFrequencyMeasurementDataUpdate EventType = "ucMPCFrequencyMeasurementDataUpdate" // UCOPEV // EV load control obligation limit data updated - UCOPEVLoadControlLimitDataUpdate UseCaseEventType = "ucOPEVLoadControlLimitDataUpdate" + UCOPEVLoadControlLimitDataUpdate EventType = "ucOPEVLoadControlLimitDataUpdate" // UCOSCEV // EV load control recommendation limit data updated // // Use Case OSCEV, Scenario 1 - UCOSCEVLoadControlLimitDataUpdate UseCaseEventType = "ucOSCEVLoadControlLimitDataUpdate" + UCOSCEVLoadControlLimitDataUpdate EventType = "ucOSCEVLoadControlLimitDataUpdate" // UCVABD @@ -418,28 +421,28 @@ const ( // Use Case VABD, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDPowerTotalMeasurementDataUpdate UseCaseEventType = "ucVABDPowerTotalMeasurementDataUpdate" + UCVABDPowerTotalMeasurementDataUpdate EventType = "ucVABDPowerTotalMeasurementDataUpdate" // Battery System cumulated charge energy data updated // // Use Case VABD, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDChargeMeasurementDataUpdate UseCaseEventType = "ucVABDChargeMeasurementDataUpdate" + UCVABDChargeMeasurementDataUpdate EventType = "ucVABDChargeMeasurementDataUpdate" // Battery System cumulated discharge energy data updated // // Use Case VABD, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDDischargeMeasurementDataUpdate UseCaseEventType = "ucVABDDischargeMeasurementDataUpdate" + UCVABDDischargeMeasurementDataUpdate EventType = "ucVABDDischargeMeasurementDataUpdate" // Battery System state of charge data updated // // Use Case VABD, Scenario 4 // // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDStateOfChargeMeasurementDataUpdate UseCaseEventType = "ucVABDStateOfChargeMeasurementDataUpdate" + UCVABDStateOfChargeMeasurementDataUpdate EventType = "ucVABDStateOfChargeMeasurementDataUpdate" // UCVAPD @@ -448,19 +451,19 @@ const ( // Use Case VAPD, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case - UCVAPDPowerTotalMeasurementDataUpdate UseCaseEventType = "ucVAPDPowerTotalMeasurementDataUpdate" + UCVAPDPowerTotalMeasurementDataUpdate EventType = "ucVAPDPowerTotalMeasurementDataUpdate" // PV System nominal peak power data updated // // Use Case VAPD, Scenario 2 - UCVAPDPeakPowerDataUpdate UseCaseEventType = "ucVAPDPeakPowerDataUpdate" + UCVAPDPeakPowerDataUpdate EventType = "ucVAPDPeakPowerDataUpdate" // PV System total yield data updated // // Use Case VAPD, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case - UCVAPDYieldTotalMeasurementDataUpdate UseCaseEventType = "ucVAPDYieldTotalMeasurementDataUpdate" + UCVAPDYieldTotalMeasurementDataUpdate EventType = "ucVAPDYieldTotalMeasurementDataUpdate" ) var ErrNoCompatibleEntity = errors.New("entity is not an compatible entity") diff --git a/cem/cem.go b/cem/cem.go index 4c4b5b3..21a9f59 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -14,7 +14,7 @@ type Cem struct { Currency model.CurrencyType - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface usecases []api.UseCaseInterface } @@ -22,7 +22,7 @@ type Cem struct { func NewCEM( serviceDescription *eebusapi.Configuration, serviceHandler eebusapi.ServiceReaderInterface, - reader api.UseCaseEventReaderInterface, + reader api.EventReaderInterface, log logging.LoggingInterface) *Cem { cem := &Cem{ Service: service.NewService(serviceDescription, serviceHandler), diff --git a/cem/cem_test.go b/cem/cem_test.go index 473ce4b..1b7aa8d 100644 --- a/cem/cem_test.go +++ b/cem/cem_test.go @@ -62,8 +62,8 @@ func (s *CemSuite) Test_CEM() { s.sut.Shutdown() } -// UseCaseEventReaderInterface -func (d *CemSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +// ReaderInterface +func (d *CemSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } // eebusapi.ServiceReaderInterface diff --git a/cem/events.go b/cem/events.go index e820fb8..21cd5c3 100644 --- a/cem/events.go +++ b/cem/events.go @@ -10,12 +10,12 @@ import ( func (h *Cem) HandleEvent(payload spineapi.EventPayload) { if util.IsDeviceConnected(payload) { - h.reader.SpineEvent(payload.Ski, payload.Device, nil, api.DeviceConnected) + h.reader.Event(payload.Ski, payload.Device, nil, api.DeviceConnected) return } if util.IsDeviceDisconnected(payload) { - h.reader.SpineEvent(payload.Ski, payload.Device, nil, api.DeviceDisconnected) + h.reader.Event(payload.Ski, payload.Device, nil, api.DeviceDisconnected) return } } diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index ea41843..a0228f5 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -4,6 +4,7 @@ import ( "github.com/enbility/cemd/cem" "github.com/enbility/cemd/ucevsecc" eebusapi "github.com/enbility/eebus-go/api" + "github.com/enbility/ship-go/logging" ) type DemoCem struct { @@ -13,7 +14,8 @@ type DemoCem struct { func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { demo := &DemoCem{} - demo.cem = cem.NewCEM(configuration, demo, demo, demo) + noLogging := &logging.NoLogging{} + demo.cem = cem.NewCEM(configuration, demo, demo, noLogging) return demo } diff --git a/cmd/democem/eventreader.go b/cmd/democem/eventreader.go index 7fe8015..6e66307 100644 --- a/cmd/democem/eventreader.go +++ b/cmd/democem/eventreader.go @@ -5,8 +5,8 @@ import ( spineapi "github.com/enbility/spine-go/api" ) -var _ api.UseCaseEventReaderInterface = (*DemoCem)(nil) +var _ api.EventReaderInterface = (*DemoCem)(nil) -// Handle incomfing usecase specific event -func (h *DemoCem) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +// Handle incoming usecase specific events +func (h *DemoCem) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } diff --git a/cmd/democem/logging.go b/cmd/democem/logging.go deleted file mode 100644 index 957219f..0000000 --- a/cmd/democem/logging.go +++ /dev/null @@ -1,50 +0,0 @@ -package democem - -import ( - "fmt" - "time" -) - -// Logging interface - -func (d *DemoCem) log(level string, args ...interface{}) { - t := time.Now() - fmt.Printf("%s: %s %s", t.Format(time.RFC3339), level, fmt.Sprintln(args...)) -} - -func (d *DemoCem) logf(level, format string, args ...interface{}) { - t := time.Now() - fmt.Printf("%s: %s %s\n", t.Format(time.RFC3339), level, fmt.Sprintf(format, args...)) -} - -func (d *DemoCem) Trace(args ...interface{}) { - d.log("TRACE", args...) -} - -func (d *DemoCem) Tracef(format string, args ...interface{}) { - d.logf("TRACE", format, args...) -} - -func (d *DemoCem) Debug(args ...interface{}) { - d.log("DEBUG", args...) -} - -func (d *DemoCem) Debugf(format string, args ...interface{}) { - d.logf("DEBUG", format, args...) -} - -func (d *DemoCem) Info(args ...interface{}) { - d.log("INFO", args...) -} - -func (d *DemoCem) Infof(format string, args ...interface{}) { - d.logf("INFO", format, args...) -} - -func (d *DemoCem) Error(args ...interface{}) { - d.log("ERROR", args...) -} - -func (d *DemoCem) Errorf(format string, args ...interface{}) { - d.logf("ERROR", format, args...) -} diff --git a/cmd/democem/service.go b/cmd/democem/service.go index b2ce40b..e82e8d8 100644 --- a/cmd/democem/service.go +++ b/cmd/democem/service.go @@ -3,14 +3,10 @@ package democem import ( eebusapi "github.com/enbility/eebus-go/api" shipapi "github.com/enbility/ship-go/api" - "github.com/enbility/ship-go/logging" ) // report the Ship ID of a newly trusted connection func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { - // we should associated the Ship ID with the SKI and store it - // so the next connection can start trusted - logging.Log().Info("SKI", ski, "has Ship ID:", shipID) } func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} diff --git a/uccevc/events.go b/uccevc/events.go index 67a91f7..d891edc 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -113,7 +113,7 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCEnergyDemandProvided) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVCEnergyDemandProvided) _, err = e.TimeSlotConstraints(entity) if err != nil { @@ -127,18 +127,18 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVPowerLimitsRequested) - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCIncentivesRequested) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVPowerLimitsRequested) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVCIncentivesRequested) } // the load control limit data of an EV was updated func (e *UCCEVC) evTimeSeriesDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if _, err := e.ChargePlan(entity); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCChargePlanProvided) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVCChargePlanProvided) } if _, err := e.ChargePlanConstraints(entity); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCChargePlanConstraintsProvided) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVCChargePlanConstraintsProvided) } } @@ -156,12 +156,12 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spinea return } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCIncentiveDescriptionsRequired) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVCIncentiveDescriptionsRequired) } // the load control limit data of an EV was updated func (e *UCCEVC) evIncentiveTableDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCCEVCIncentiveTableDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCCEVCIncentiveTableDataUpdate) } // check timeSeries descriptions if constraints element has updateRequired set to true diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index 1836b99..0a819c6 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -36,7 +36,7 @@ type UCCEVCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCCEVCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCCEVCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index 92cb30b..d715b29 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -14,14 +14,14 @@ import ( type UCCEVC struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCCEVCInterface = (*UCCEVC)(nil) -func NewUCCEVC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCCEVC { +func NewUCCEVC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCCEVC { uc := &UCCEVC{ service: service, reader: reader, diff --git a/ucevcc/events.go b/ucevcc/events.go index 9412c9c..81c34a8 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -112,12 +112,12 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCEventConnected) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCEventConnected) } // an EV was disconnected func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCEventDisconnected) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCEventDisconnected) } // the configuration key description data of an EV was updated @@ -139,12 +139,12 @@ func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRem // Scenario 2 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) } // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) } } @@ -162,7 +162,7 @@ func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRe continue } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCIdentificationDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCIdentificationDataUpdate) return } } @@ -177,7 +177,7 @@ func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemo // Scenario 5 if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCManufacturerDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCManufacturerDataUpdate) } } @@ -209,5 +209,5 @@ func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.E } // Scenario 6 - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCCChargingPowerLimitsDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCCChargingPowerLimitsDataUpdate) } diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index dc34839..f1922f3 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVCCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCEVCCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVCCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index d30b139..54b6f67 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -13,14 +13,14 @@ import ( type UCEVCC struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCEVCCInterface = (*UCEVCC)(nil) -func NewUCEVCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVCC { +func NewUCEVCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVCC { uc := &UCEVCC{ service: service, reader: reader, diff --git a/ucevcem/events.go b/ucevcem/events.go index 746dfb2..c0c8d03 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -61,7 +61,7 @@ func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity return } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) } // the measurement description data of an EV was updated @@ -78,16 +78,16 @@ func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemot func (e *UCEVCEM) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMCurrentMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMCurrentMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMPowerMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMPowerMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) } } diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index 235e948..a99558a 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVCEMSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCEVCEMSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVCEMSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index 813c036..6f59425 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -13,14 +13,14 @@ import ( type UCEVCEM struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCEVCEMInterface = (*UCEVCEM)(nil) -func NewUCEVCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVCEM { +func NewUCEVCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVCEM { uc := &UCEVCEM{ service: service, reader: reader, diff --git a/ucevsecc/events.go b/ucevsecc/events.go index 74ec91f..940e360 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -55,12 +55,12 @@ func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterfa _, _ = evseDeviceDiagnosis.RequestState() } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventConnected) + e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventConnected) } // an EVSE was disconnected func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventDisconnected) + e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventDisconnected) } // the manufacturer Data of an EVSE was updated @@ -71,7 +71,7 @@ func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.Entity } if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventManufacturerUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventManufacturerUpdate) } } @@ -83,6 +83,6 @@ func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInter } if _, err := evDeviceDiagnosis.GetState(); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSECCEventOperationStateUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventOperationStateUpdate) } } diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index 4576e95..b240a02 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVSECCSuite struct { evseEntity spineapi.EntityRemoteInterface } -func (s *UCEVSECCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVSECCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 2096256..45f511a 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -13,14 +13,14 @@ import ( type UCEVSECC struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCEVSECCInterface = (*UCEVSECC)(nil) -func NewUCEVSECC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVSECC { +func NewUCEVSECC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVSECC { uc := &UCEVSECC{ service: service, reader: reader, diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 45f4a52..4c6c665 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -58,16 +58,16 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfHealth); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.EVSOCStateOfHealthMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.EVSOCStateOfHealthMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeTravelRange); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCEVSOCActualRangeMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCEVSOCActualRangeMeasurementDataUpdate) } } diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 838e2ae..86dd141 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -36,7 +36,7 @@ type UCEVSOCSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCEVSOCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCEVSOCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index 38056f3..ab248af 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -14,14 +14,14 @@ import ( type UCEVSOC struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCEVSOCInterface = (*UCEVSOC)(nil) -func NewUCEVSOC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCEVSOC { +func NewUCEVSOC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVSOC { uc := &UCEVSOC{ service: service, reader: reader, diff --git a/ucmgcp/events.go b/ucmgcp/events.go index bcf23a8..134a2f4 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -104,32 +104,32 @@ func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemo func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPPowerTotalMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPPowerTotalMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridFeedIn); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPGridFeedInMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPGridFeedInMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridConsumption); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPCurrentsMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPCurrentsMeasurementDataUpdate) } // Scenario 6 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPVoltagesMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPVoltagesMeasurementDataUpdate) } // Scenario 7 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMGCPFrequencyMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPFrequencyMeasurementDataUpdate) } } diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index 4ab868f..60d2d64 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -36,7 +36,7 @@ type UCMGCPSuite struct { smgwEntity spineapi.EntityRemoteInterface } -func (s *UCMGCPSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCMGCPSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index 2a36082..d7034ad 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -14,14 +14,14 @@ import ( type UCMGCP struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCMGCPInterface = (*UCMGCP)(nil) -func NewUCMGCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCMGCP { +func NewUCMGCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCMGCP { uc := &UCMGCP{ service: service, reader: reader, diff --git a/ucmpc/events.go b/ucmpc/events.go index c3a7ee1..3debfe7 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -81,35 +81,35 @@ func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRem func (e *UCMPC) deviceMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCPowerTotalMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCPowerTotalMeasurementDataUpdate) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyProduced); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCCurrentsMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCCurrentsMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCVoltagesMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCVoltagesMeasurementDataUpdate) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCMPCFrequencyMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCMPCFrequencyMeasurementDataUpdate) } } diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index c5484bf..df35441 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -36,7 +36,7 @@ type UCMPCSuite struct { monitoredEntity spineapi.EntityRemoteInterface } -func (s *UCMPCSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCMPCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index db81e11..9ba12e3 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -14,14 +14,14 @@ import ( type UCMPC struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCMCPInterface = (*UCMPC)(nil) -func NewUCMCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCMPC { +func NewUCMCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCMPC { uc := &UCMPC{ service: service, reader: reader, diff --git a/ucopev/events.go b/ucopev/events.go index d00d3c6..50b4409 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -86,7 +86,7 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entity continue } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) return } diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index 05916c1..059ffa5 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -36,7 +36,7 @@ type UCOPEVSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCOPEVSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCOPEVSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 2a73869..4911495 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -14,14 +14,14 @@ import ( type UCOPEV struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCOPEVInterface = (*UCOPEV)(nil) -func NewUCOPEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCOPEV { +func NewUCOPEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCOPEV { uc := &UCOPEV{ service: service, reader: reader, diff --git a/ucoscev/events.go b/ucoscev/events.go index 9a0efbd..62cbb58 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -51,7 +51,7 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entit continue } - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) return } diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index 8ddb525..2790863 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -36,7 +36,7 @@ type UCOSCEVSuite struct { evEntity spineapi.EntityRemoteInterface } -func (s *UCOSCEVSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCOSCEVSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 64efb90..cbb0d57 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -14,14 +14,14 @@ import ( type UCOSCEV struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCOSCEVInterface = (*UCOSCEV)(nil) -func NewUCOSCEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCOSCEV { +func NewUCOSCEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCOSCEV { uc := &UCOSCEV{ service: service, reader: reader, diff --git a/ucvabd/events.go b/ucvabd/events.go index de091e3..26e5bc2 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -81,21 +81,21 @@ func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVABD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDPowerTotalMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVABDPowerTotalMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDChargeMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVABDChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeDischarge); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDDischargeMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVABDDischargeMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVABDStateOfChargeMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVABDStateOfChargeMeasurementDataUpdate) } } diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go index 4d02078..4264b4a 100644 --- a/ucvabd/testhelper_test.go +++ b/ucvabd/testhelper_test.go @@ -36,7 +36,7 @@ type UCVABDSuite struct { batteryEntity spineapi.EntityRemoteInterface } -func (s *UCVABDSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCVABDSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCVABDSuite) BeforeTest(suiteName, testName string) { diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index 2403c63..29f32e6 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -14,14 +14,14 @@ import ( type UCVABD struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCVABDInterface = (*UCVABD)(nil) -func NewUCVABD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCVABD { +func NewUCVABD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCVABD { uc := &UCVABD{ service: service, reader: reader, diff --git a/ucvapd/events.go b/ucvapd/events.go index 6e360c5..ac3467f 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -97,7 +97,7 @@ func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.Ent // Scenario 1 if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { if _, err := deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVAPDPeakPowerDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVAPDPeakPowerDataUpdate) } } } @@ -116,11 +116,11 @@ func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVAPD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVAPDPowerTotalMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVAPDPowerTotalMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACYieldTotal); err == nil { - e.reader.SpineEvent(ski, entity.Device(), entity, api.UCVAPDYieldTotalMeasurementDataUpdate) + e.reader.Event(ski, entity.Device(), entity, api.UCVAPDYieldTotalMeasurementDataUpdate) } } diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index eaa1cf8..291da91 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -36,7 +36,7 @@ type UCVAPDSuite struct { pvEntity spineapi.EntityRemoteInterface } -func (s *UCVAPDSuite) SpineEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UCVAPDSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 15908a2..e25ae44 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -14,14 +14,14 @@ import ( type UCVAPD struct { service serviceapi.ServiceInterface - reader api.UseCaseEventReaderInterface + reader api.EventReaderInterface validEntityTypes []model.EntityTypeType } var _ UCVAPDInterface = (*UCVAPD)(nil) -func NewUCVAPD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.UseCaseEventReaderInterface) *UCVAPD { +func NewUCVAPD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCVAPD { uc := &UCVAPD{ service: service, reader: reader, diff --git a/util/testhelper_test.go b/util/testhelper_test.go index 0b8ca79..19e4c6c 100644 --- a/util/testhelper_test.go +++ b/util/testhelper_test.go @@ -35,7 +35,7 @@ type UtilSuite struct { monitoredEntity spineapi.EntityRemoteInterface } -func (s *UtilSuite) SpineEvent(ski string, entity spineapi.EntityRemoteInterface, event api.UseCaseEventType) { +func (s *UtilSuite) Event(ski string, entity spineapi.EntityRemoteInterface, event api.EventType) { } func (s *UtilSuite) BeforeTest(suiteName, testName string) { From 44f7a9c229dd05d7a52fe2ffc3eb47262f6a88b1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 15:41:49 +0100 Subject: [PATCH 102/227] Fix typo --- ucmpc/testhelper_test.go | 2 +- ucmpc/ucmcp.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index df35441..d55549a 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCMCP(s.service, s.service.LocalService(), s) + s.sut = NewUCMPC(s.service, s.service.LocalService(), s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index 9ba12e3..3fb361b 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -21,7 +21,7 @@ type UCMPC struct { var _ UCMCPInterface = (*UCMPC)(nil) -func NewUCMCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCMPC { +func NewUCMPC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCMPC { uc := &UCMPC{ service: service, reader: reader, From 00088e9de2427e76c9f367d02245abf2e206390a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 16:28:53 +0100 Subject: [PATCH 103/227] Simplify UseCase generation --- cem/cem_test.go | 2 +- cmd/democem/democem.go | 2 +- uccevc/testhelper_test.go | 2 +- uccevc/uccevc.go | 3 +-- ucevcc/testhelper_test.go | 2 +- ucevcc/ucevcc.go | 3 +-- ucevcem/testhelper_test.go | 2 +- ucevcem/ucevcem.go | 3 +-- ucevsecc/testhelper_test.go | 2 +- ucevsecc/ucevsecc.go | 3 +-- ucevsoc/testhelper_test.go | 2 +- ucevsoc/ucevsoc.go | 3 +-- ucmgcp/testhelper_test.go | 2 +- ucmgcp/ucmgcp.go | 3 +-- ucmpc/testhelper_test.go | 2 +- ucmpc/ucmcp.go | 3 +-- ucopev/testhelper_test.go | 2 +- ucopev/ucopev.go | 3 +-- ucoscev/testhelper_test.go | 2 +- ucoscev/ucoscev.go | 3 +-- ucvabd/testhelper_test.go | 2 +- ucvabd/ucvabd.go | 3 +-- ucvapd/testhelper_test.go | 2 +- ucvapd/ucvapd.go | 3 +-- 24 files changed, 24 insertions(+), 35 deletions(-) diff --git a/cem/cem_test.go b/cem/cem_test.go index 1b7aa8d..fec2cbc 100644 --- a/cem/cem_test.go +++ b/cem/cem_test.go @@ -55,7 +55,7 @@ func (s *CemSuite) Test_CEM() { err := s.sut.Setup() assert.Nil(s.T(), err) - ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s.sut.Service.LocalService(), s) + ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s) s.sut.AddUseCase(ucEvseCC) s.sut.Start() diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index a0228f5..bd917de 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -25,7 +25,7 @@ func (d *DemoCem) Setup() error { return err } - evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.cem.Service.LocalService(), d) + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d) d.cem.AddUseCase(evsecc) d.cem.Start() diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index 0a819c6..afc296f 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -66,7 +66,7 @@ func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { mockRemoteFeature.EXPECT().Operations().Return(ops).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCCEVC(s.service, s.service.LocalService(), s) + s.sut = NewUCCEVC(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index d715b29..f6b538c 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCCEVC struct { var _ UCCEVCInterface = (*UCCEVC)(nil) -func NewUCCEVC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCCEVC { +func NewUCCEVC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCCEVC { uc := &UCCEVC{ service: service, reader: reader, diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index f1922f3..660399b 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVCC(s.service, s.service.LocalService(), s) + s.sut = NewUCEVCC(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 54b6f67..0aecd16 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -4,7 +4,6 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -20,7 +19,7 @@ type UCEVCC struct { var _ UCEVCCInterface = (*UCEVCC)(nil) -func NewUCEVCC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVCC { +func NewUCEVCC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVCC { uc := &UCEVCC{ service: service, reader: reader, diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index a99558a..986e6c5 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -61,7 +61,7 @@ func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVCEM(s.service, s.service.LocalService(), s) + s.sut = NewUCEVCEM(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index 6f59425..ba05393 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -4,7 +4,6 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -20,7 +19,7 @@ type UCEVCEM struct { var _ UCEVCEMInterface = (*UCEVCEM)(nil) -func NewUCEVCEM(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVCEM { +func NewUCEVCEM(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVCEM { uc := &UCEVCEM{ service: service, reader: reader, diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index b240a02..19dbe26 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVSECC(s.service, s.service.LocalService(), s) + s.sut = NewUCEVSECC(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 45f511a..6397b85 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -4,7 +4,6 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -20,7 +19,7 @@ type UCEVSECC struct { var _ UCEVSECCInterface = (*UCEVSECC)(nil) -func NewUCEVSECC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVSECC { +func NewUCEVSECC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVSECC { uc := &UCEVSECC{ service: service, reader: reader, diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 86dd141..9ad2ed8 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVSOC(s.service, s.service.LocalService(), s) + s.sut = NewUCEVSOC(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index ab248af..5b5e129 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCEVSOC struct { var _ UCEVSOCInterface = (*UCEVSOC)(nil) -func NewUCEVSOC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCEVSOC { +func NewUCEVSOC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVSOC { uc := &UCEVSOC{ service: service, reader: reader, diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index 60d2d64..1e5352b 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCMGCP(s.service, s.service.LocalService(), s) + s.sut = NewUCMGCP(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index d7034ad..b05aec6 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCMGCP struct { var _ UCMGCPInterface = (*UCMGCP)(nil) -func NewUCMGCP(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCMGCP { +func NewUCMGCP(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCMGCP { uc := &UCMGCP{ service: service, reader: reader, diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index d55549a..0e2a5a8 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCMPC(s.service, s.service.LocalService(), s) + s.sut = NewUCMPC(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index 3fb361b..a47fe04 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCMPC struct { var _ UCMCPInterface = (*UCMPC)(nil) -func NewUCMPC(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCMPC { +func NewUCMPC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCMPC { uc := &UCMPC{ service: service, reader: reader, diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index 059ffa5..b9040ec 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCOPEV(s.service, s.service.LocalService(), s) + s.sut = NewUCOPEV(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 4911495..7cc5795 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCOPEV struct { var _ UCOPEVInterface = (*UCOPEV)(nil) -func NewUCOPEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCOPEV { +func NewUCOPEV(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCOPEV { uc := &UCOPEV{ service: service, reader: reader, diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index 2790863..26780be 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCOSCEV(s.service, s.service.LocalService(), s) + s.sut = NewUCOSCEV(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index cbb0d57..07eaac0 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCOSCEV struct { var _ UCOSCEVInterface = (*UCOSCEV)(nil) -func NewUCOSCEV(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCOSCEV { +func NewUCOSCEV(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCOSCEV { uc := &UCOSCEV{ service: service, reader: reader, diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go index 4264b4a..c435b80 100644 --- a/ucvabd/testhelper_test.go +++ b/ucvabd/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCVABDSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCVABD(s.service, s.service.LocalService(), s) + s.sut = NewUCVABD(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index 29f32e6..362475c 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCVABD struct { var _ UCVABDInterface = (*UCVABD)(nil) -func NewUCVABD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCVABD { +func NewUCVABD(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCVABD { uc := &UCVABD{ service: service, reader: reader, diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index 291da91..9b3e9e3 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCVAPD(s.service, s.service.LocalService(), s) + s.sut = NewUCVAPD(s.service, s) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index e25ae44..0e33a95 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -5,7 +5,6 @@ import ( "github.com/enbility/cemd/util" serviceapi "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features" - shipapi "github.com/enbility/ship-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" @@ -21,7 +20,7 @@ type UCVAPD struct { var _ UCVAPDInterface = (*UCVAPD)(nil) -func NewUCVAPD(service serviceapi.ServiceInterface, details *shipapi.ServiceDetails, reader api.EventReaderInterface) *UCVAPD { +func NewUCVAPD(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCVAPD { uc := &UCVAPD{ service: service, reader: reader, From 5c989f09b302f6e48688e4851257d844b65cbce7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 16:59:34 +0100 Subject: [PATCH 104/227] Add MGCP Scenario 1 event handling --- api/types.go | 8 ++++++++ ucmgcp/events.go | 9 +++++++++ ucmgcp/events_test.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/api/types.go b/api/types.go index e53b5a3..b3a2a98 100644 --- a/api/types.go +++ b/api/types.go @@ -309,6 +309,14 @@ const ( // MGCP + // Grid maximum allowed feed-in power as percentage value of the cumulated + // nominal peak power of all electricity producting PV systems was updated + // + // Use Case MGCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + UCMGCPPVFeedInPowerLimitationFactorDataUpdate EventType = "ucMGCPPVFeedInPowerLimitationFactorDataUpdate" + // Grid momentary power consumption/production data updated // // Use Case MGCP, Scenario 2 diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 134a2f4..300a9b1 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -29,6 +29,8 @@ func (e *UCMGCP) HandleEvent(payload spineapi.EventPayload) { switch payload.Data.(type) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: e.gridConfigurationDescriptionDataUpdate(payload.Entity) + case *model.DeviceConfigurationKeyValueListDataType: + e.gridConfigurationDataUpdate(payload.Ski, payload.Entity) case *model.MeasurementDescriptionListDataType: e.gridMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: @@ -90,6 +92,13 @@ func (e *UCMGCP) gridConfigurationDescriptionDataUpdate(entity spineapi.EntityRe } } +// the configuration key data of an SMGW was updated +func (e *UCMGCP) gridConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + if _, err := e.PowerLimitationFactor(entity); err == nil { + e.reader.Event(ski, entity.Device(), entity, api.UCMGCPPVFeedInPowerLimitationFactorDataUpdate) + } +} + // the measurement descriptiondata of an SMGW was updated func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { if measurement, err := util.Measurement(e.service, entity); err == nil { diff --git a/ucmgcp/events_test.go b/ucmgcp/events_test.go index 0a788eb..e66d29e 100644 --- a/ucmgcp/events_test.go +++ b/ucmgcp/events_test.go @@ -36,6 +36,39 @@ func (s *UCMGCPSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCMGCPSuite) Test_gridConfigurationDataUpdate() { + s.sut.gridConfigurationDataUpdate(remoteSki, s.smgwEntity) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.smgwEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(10), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.gridConfigurationDataUpdate(remoteSki, s.smgwEntity) +} + func (s *UCMGCPSuite) Test_gridMeasurementDataUpdate() { s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) From 5a0a771e48864250129ece1558df979aeb67b480 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 16:59:49 +0100 Subject: [PATCH 105/227] Fix a few comments in UCOPEV --- ucopev/events.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ucopev/events.go b/ucopev/events.go index 50b4409..ee335d8 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -42,12 +42,12 @@ func (e *UCOPEV) evConnected(entity spineapi.EntityRemoteInterface) { logging.Log().Debug(err) } - // get measurement descriptions + // get descriptions if _, err := evLoadControl.RequestLimitDescriptions(); err != nil { logging.Log().Debug(err) } - // get measurement constraints + // get constraints if _, err := evLoadControl.RequestLimitConstraints(); err != nil { logging.Log().Debug(err) } @@ -57,7 +57,7 @@ func (e *UCOPEV) evConnected(entity spineapi.EntityRemoteInterface) { // the load control limit description data of an EV was updated func (e *UCOPEV) evLoadControlLimitDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { if evLoadControl, err := util.LoadControl(e.service, entity); err == nil { - // get measurement values + // get values if _, err := evLoadControl.RequestLimitValues(); err != nil { logging.Log().Debug(err) } From 8dd39ddb544e9509d7c100b5f7285436c663e6a4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 18:58:23 +0100 Subject: [PATCH 106/227] Update spine and eebus --- go.mod | 10 ++-------- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index f79bb1d..3ef4931 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 + github.com/enbility/eebus-go v0.0.0-20240225175714-2fe6960db55d github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240225125500-721bfea8f484 + github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def github.com/stretchr/testify v1.8.4 ) @@ -30,9 +30,3 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// replace github.com/enbility/eebus-go => ../eebus-go - -// replace github.com/enbility/ship-go => ../ship-go - -// replace github.com/enbility/spine-go => ../spine-go diff --git a/go.sum b/go.sum index 726b3ff..6014cc9 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00 h1:7+EgHCkVBaEVDvvTlwj57hLqv9IlsdJG5/8exNwIeDI= -github.com/enbility/eebus-go v0.0.0-20240222201321-3f45bddf9e00/go.mod h1:4Rqu0NjVY73per2BlwfG1KZPIkrpRtBi5cWwB57gCMQ= +github.com/enbility/eebus-go v0.0.0-20240225175714-2fe6960db55d h1:QfxCmsPYMK7RVkswwLpwXR9c3mnGMPCP4HlVHvZYvRs= +github.com/enbility/eebus-go v0.0.0-20240225175714-2fe6960db55d/go.mod h1:x7Z5n4MHueb8ScMUflOGGy3lw4flNc3APkux9E7lS2o= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240225125500-721bfea8f484 h1:avTaHvFo/x8Iw+xlrnkSrQjPz16hlVZ7MECbfH71uJ4= -github.com/enbility/spine-go v0.0.0-20240225125500-721bfea8f484/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def h1:glevkOCgXd7IBiu69mTvv7sxgYc8W6B37yDBfcVYqhs= +github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 16d35a201528fd07ed6d09749961b3986fbb0dd2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 18:58:44 +0100 Subject: [PATCH 107/227] Fix typo --- ucmpc/testhelper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index 0e2a5a8..7fd0ce1 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/suite" ) -func TestMOCSuite(t *testing.T) { +func TestMPCSuite(t *testing.T) { suite.Run(t, new(UCMPCSuite)) } From 54bd8cf67ef661687e0875faec982e3ed48a74be Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 19:09:31 +0100 Subject: [PATCH 108/227] Change from Event interface to callback --- api/api.go | 15 +++++---------- api/types.go | 23 +++++++++++++++++++++++ cem/cem.go | 6 +++--- cem/cem_test.go | 6 +++--- cem/events.go | 4 ++-- cmd/democem/democem.go | 4 ++-- cmd/democem/eventreader.go | 12 ------------ go.mod | 6 ++++++ uccevc/events.go | 14 +++++++------- uccevc/testhelper_test.go | 2 +- uccevc/uccevc.go | 6 +++--- ucevcc/events.go | 14 +++++++------- ucevcc/testhelper_test.go | 2 +- ucevcc/ucevcc.go | 6 +++--- ucevcem/events.go | 8 ++++---- ucevcem/testhelper_test.go | 2 +- ucevcem/ucevcem.go | 6 +++--- ucevsecc/events.go | 8 ++++---- ucevsecc/testhelper_test.go | 2 +- ucevsecc/ucevsecc.go | 6 +++--- ucevsoc/events.go | 6 +++--- ucevsoc/testhelper_test.go | 2 +- ucevsoc/ucevsoc.go | 6 +++--- ucmgcp/events.go | 14 +++++++------- ucmgcp/testhelper_test.go | 2 +- ucmgcp/ucmgcp.go | 6 +++--- ucmpc/events.go | 14 +++++++------- ucmpc/testhelper_test.go | 2 +- ucmpc/ucmcp.go | 6 +++--- ucopev/events.go | 2 +- ucopev/testhelper_test.go | 2 +- ucopev/ucopev.go | 6 +++--- ucoscev/events.go | 2 +- ucoscev/testhelper_test.go | 2 +- ucoscev/ucoscev.go | 6 +++--- ucvabd/events.go | 8 ++++---- ucvabd/testhelper_test.go | 2 +- ucvabd/ucvabd.go | 6 +++--- ucvapd/events.go | 6 +++--- ucvapd/testhelper_test.go | 2 +- ucvapd/ucvapd.go | 6 +++--- 41 files changed, 136 insertions(+), 124 deletions(-) delete mode 100644 cmd/democem/eventreader.go diff --git a/api/api.go b/api/api.go index e7ecf80..785ac65 100644 --- a/api/api.go +++ b/api/api.go @@ -7,6 +7,11 @@ import ( //go:generate mockery +// Event handler callback +// +// Used by Cem and Use Case implementations +type EventHandlerCB func(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event EventType) + // Implemented by CEM type CemInterface interface { // Setup the EEBUS service @@ -40,13 +45,3 @@ type UseCaseInterface interface { // - and others IsUseCaseSupported(remoteEntity spineapi.EntityRemoteInterface) (bool, error) } - -// interface for informing the HEMS about specific events -// -// implemented by the actual HEMS -type EventReaderInterface interface { - // Inform about a new cem or usecase specific event - // - // used by use case implementations - Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event EventType) -} diff --git a/api/types.go b/api/types.go index b3a2a98..f06268d 100644 --- a/api/types.go +++ b/api/types.go @@ -307,6 +307,29 @@ const ( // Note: the referred data may be updated together with all other measurement items of this use case UCEVSOCActualRangeMeasurementDataUpdate EventType = "ucEVSOCActualRangeMeasurementDataUpdate" + // LPC + + // Load control obligation limit data updated + // + // Use Case LPC, Scenario 1 + UCLPCLoadControlLimitDataUpdate EventType = "ucLPCLoadControlLimitDataUpdate" + + // Failsafe limit for the consumed active (real) power of the + // Controllable System data updated + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + UCLPCFailSafeConsumptionActivePowerLimitDataUpdate EventType = "ucLPCFailSafeConsumptionActivePowerLimitDataUpdate" + + // Minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" data updated + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + UCLPCFailsafeDurationMinimumDataUpdate EventType = "ucLPCFailsafeDurationMinimumDataUpdate" + // MGCP // Grid maximum allowed feed-in power as percentage value of the cumulated diff --git a/cem/cem.go b/cem/cem.go index 21a9f59..5b6b4e2 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -14,7 +14,7 @@ type Cem struct { Currency model.CurrencyType - reader api.EventReaderInterface + eventCB api.EventHandlerCB usecases []api.UseCaseInterface } @@ -22,12 +22,12 @@ type Cem struct { func NewCEM( serviceDescription *eebusapi.Configuration, serviceHandler eebusapi.ServiceReaderInterface, - reader api.EventReaderInterface, + eventCB api.EventHandlerCB, log logging.LoggingInterface) *Cem { cem := &Cem{ Service: service.NewService(serviceDescription, serviceHandler), Currency: model.CurrencyTypeEur, - reader: reader, + eventCB: eventCB, } cem.Service.SetLogging(log) diff --git a/cem/cem_test.go b/cem/cem_test.go index fec2cbc..cd51a70 100644 --- a/cem/cem_test.go +++ b/cem/cem_test.go @@ -48,14 +48,14 @@ func (s *CemSuite) BeforeTest(suiteName, testName string) { assert.Nil(s.T(), err) noLogging := &logging.NoLogging{} - s.sut = NewCEM(configuration, s, s, noLogging) + s.sut = NewCEM(configuration, s, s.eventCB, noLogging) assert.NotNil(s.T(), s.sut) } func (s *CemSuite) Test_CEM() { err := s.sut.Setup() assert.Nil(s.T(), err) - ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s) + ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s.eventCB) s.sut.AddUseCase(ucEvseCC) s.sut.Start() @@ -63,7 +63,7 @@ func (s *CemSuite) Test_CEM() { } // ReaderInterface -func (d *CemSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +func (d *CemSuite) eventCB(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } // eebusapi.ServiceReaderInterface diff --git a/cem/events.go b/cem/events.go index 21cd5c3..1f1b6fd 100644 --- a/cem/events.go +++ b/cem/events.go @@ -10,12 +10,12 @@ import ( func (h *Cem) HandleEvent(payload spineapi.EventPayload) { if util.IsDeviceConnected(payload) { - h.reader.Event(payload.Ski, payload.Device, nil, api.DeviceConnected) + h.eventCB(payload.Ski, payload.Device, nil, api.DeviceConnected) return } if util.IsDeviceDisconnected(payload) { - h.reader.Event(payload.Ski, payload.Device, nil, api.DeviceDisconnected) + h.eventCB(payload.Ski, payload.Device, nil, api.DeviceDisconnected) return } } diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index bd917de..7055675 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -15,7 +15,7 @@ func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { demo := &DemoCem{} noLogging := &logging.NoLogging{} - demo.cem = cem.NewCEM(configuration, demo, demo, noLogging) + demo.cem = cem.NewCEM(configuration, demo, demo.eventCB, noLogging) return demo } @@ -25,7 +25,7 @@ func (d *DemoCem) Setup() error { return err } - evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d) + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.eventCB) d.cem.AddUseCase(evsecc) d.cem.Start() diff --git a/cmd/democem/eventreader.go b/cmd/democem/eventreader.go deleted file mode 100644 index 6e66307..0000000 --- a/cmd/democem/eventreader.go +++ /dev/null @@ -1,12 +0,0 @@ -package democem - -import ( - "github.com/enbility/cemd/api" - spineapi "github.com/enbility/spine-go/api" -) - -var _ api.EventReaderInterface = (*DemoCem)(nil) - -// Handle incoming usecase specific events -func (h *DemoCem) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { -} diff --git a/go.mod b/go.mod index 3ef4931..833a302 100644 --- a/go.mod +++ b/go.mod @@ -30,3 +30,9 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// replace github.com/enbility/eebus-go => ../eebus-go + +// replace github.com/enbility/ship-go => ../ship-go + +// replace github.com/enbility/spine-go => ../spine-go diff --git a/uccevc/events.go b/uccevc/events.go index d891edc..f2fdcf3 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -113,7 +113,7 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.reader.Event(ski, entity.Device(), entity, api.UCCEVCEnergyDemandProvided) + e.eventCB(ski, entity.Device(), entity, api.UCCEVCEnergyDemandProvided) _, err = e.TimeSlotConstraints(entity) if err != nil { @@ -127,18 +127,18 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.reader.Event(ski, entity.Device(), entity, api.UCCEVPowerLimitsRequested) - e.reader.Event(ski, entity.Device(), entity, api.UCCEVCIncentivesRequested) + e.eventCB(ski, entity.Device(), entity, api.UCCEVPowerLimitsRequested) + e.eventCB(ski, entity.Device(), entity, api.UCCEVCIncentivesRequested) } // the load control limit data of an EV was updated func (e *UCCEVC) evTimeSeriesDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if _, err := e.ChargePlan(entity); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCCEVCChargePlanProvided) + e.eventCB(ski, entity.Device(), entity, api.UCCEVCChargePlanProvided) } if _, err := e.ChargePlanConstraints(entity); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCCEVCChargePlanConstraintsProvided) + e.eventCB(ski, entity.Device(), entity, api.UCCEVCChargePlanConstraintsProvided) } } @@ -156,12 +156,12 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spinea return } - e.reader.Event(ski, entity.Device(), entity, api.UCCEVCIncentiveDescriptionsRequired) + e.eventCB(ski, entity.Device(), entity, api.UCCEVCIncentiveDescriptionsRequired) } // the load control limit data of an EV was updated func (e *UCCEVC) evIncentiveTableDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.Event(ski, entity.Device(), entity, api.UCCEVCIncentiveTableDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCCEVCIncentiveTableDataUpdate) } // check timeSeries descriptions if constraints element has updateRequired set to true diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index afc296f..00385a8 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -66,7 +66,7 @@ func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { mockRemoteFeature.EXPECT().Operations().Return(ops).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCCEVC(s.service, s) + s.sut = NewUCCEVC(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index f6b538c..045a001 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -13,17 +13,17 @@ import ( type UCCEVC struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCCEVCInterface = (*UCCEVC)(nil) -func NewUCCEVC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCCEVC { +func NewUCCEVC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCCEVC { uc := &UCCEVC{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucevcc/events.go b/ucevcc/events.go index 81c34a8..6c0c8e8 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -112,12 +112,12 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCEventConnected) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCEventConnected) } // an EV was disconnected func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCEventDisconnected) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCEventDisconnected) } // the configuration key description data of an EV was updated @@ -139,12 +139,12 @@ func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRem // Scenario 2 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) } // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) } } @@ -162,7 +162,7 @@ func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRe continue } - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCIdentificationDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCIdentificationDataUpdate) return } } @@ -177,7 +177,7 @@ func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemo // Scenario 5 if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCManufacturerDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCManufacturerDataUpdate) } } @@ -209,5 +209,5 @@ func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.E } // Scenario 6 - e.reader.Event(ski, entity.Device(), entity, api.UCEVCCChargingPowerLimitsDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCCChargingPowerLimitsDataUpdate) } diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index 660399b..8f84e19 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVCC(s.service, s) + s.sut = NewUCEVCC(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 0aecd16..7fe722f 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -12,17 +12,17 @@ import ( type UCEVCC struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCEVCCInterface = (*UCEVCC)(nil) -func NewUCEVCC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVCC { +func NewUCEVCC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVCC { uc := &UCEVCC{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucevcem/events.go b/ucevcem/events.go index c0c8d03..a3ff29a 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -61,7 +61,7 @@ func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity return } - e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) } // the measurement description data of an EV was updated @@ -78,16 +78,16 @@ func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemot func (e *UCEVCEM) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMCurrentMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCEMCurrentMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMPowerMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCEMPowerMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) } } diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index 986e6c5..1b9f52e 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -61,7 +61,7 @@ func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVCEM(s.service, s) + s.sut = NewUCEVCEM(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index ba05393..b0d54ba 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -12,17 +12,17 @@ import ( type UCEVCEM struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCEVCEMInterface = (*UCEVCEM)(nil) -func NewUCEVCEM(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVCEM { +func NewUCEVCEM(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVCEM { uc := &UCEVCEM{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucevsecc/events.go b/ucevsecc/events.go index 940e360..c94dfd9 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -55,12 +55,12 @@ func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterfa _, _ = evseDeviceDiagnosis.RequestState() } - e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventConnected) + e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventConnected) } // an EVSE was disconnected func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventDisconnected) + e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventDisconnected) } // the manufacturer Data of an EVSE was updated @@ -71,7 +71,7 @@ func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.Entity } if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventManufacturerUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventManufacturerUpdate) } } @@ -83,6 +83,6 @@ func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInter } if _, err := evDeviceDiagnosis.GetState(); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVSECCEventOperationStateUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventOperationStateUpdate) } } diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index 19dbe26..951c4f0 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVSECC(s.service, s) + s.sut = NewUCEVSECC(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 6397b85..90c6233 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -12,17 +12,17 @@ import ( type UCEVSECC struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCEVSECCInterface = (*UCEVSECC)(nil) -func NewUCEVSECC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVSECC { +func NewUCEVSECC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSECC { uc := &UCEVSECC{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 4c6c665..ef8eff6 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -58,16 +58,16 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfHealth); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.EVSOCStateOfHealthMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.EVSOCStateOfHealthMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeTravelRange); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCEVSOCActualRangeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCEVSOCActualRangeMeasurementDataUpdate) } } diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 9ad2ed8..6918e8c 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCEVSOC(s.service, s) + s.sut = NewUCEVSOC(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index 5b5e129..cd72d2b 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -13,17 +13,17 @@ import ( type UCEVSOC struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCEVSOCInterface = (*UCEVSOC)(nil) -func NewUCEVSOC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCEVSOC { +func NewUCEVSOC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSOC { uc := &UCEVSOC{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 300a9b1..d29738d 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -95,7 +95,7 @@ func (e *UCMGCP) gridConfigurationDescriptionDataUpdate(entity spineapi.EntityRe // the configuration key data of an SMGW was updated func (e *UCMGCP) gridConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if _, err := e.PowerLimitationFactor(entity); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPPVFeedInPowerLimitationFactorDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPPVFeedInPowerLimitationFactorDataUpdate) } } @@ -113,32 +113,32 @@ func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemo func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPPowerTotalMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridFeedIn); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPGridFeedInMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPGridFeedInMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridConsumption); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPCurrentsMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPCurrentsMeasurementDataUpdate) } // Scenario 6 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPVoltagesMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPVoltagesMeasurementDataUpdate) } // Scenario 7 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMGCPFrequencyMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMGCPFrequencyMeasurementDataUpdate) } } diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index 1e5352b..ccef3e4 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCMGCP(s.service, s) + s.sut = NewUCMGCP(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index b05aec6..02f9d4b 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -13,17 +13,17 @@ import ( type UCMGCP struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCMGCPInterface = (*UCMGCP)(nil) -func NewUCMGCP(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCMGCP { +func NewUCMGCP(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMGCP { uc := &UCMGCP{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucmpc/events.go b/ucmpc/events.go index 3debfe7..b488bd6 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -81,35 +81,35 @@ func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRem func (e *UCMPC) deviceMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCPowerTotalMeasurementDataUpdate) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyProduced); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCCurrentsMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCCurrentsMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCVoltagesMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCVoltagesMeasurementDataUpdate) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCMPCFrequencyMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCMPCFrequencyMeasurementDataUpdate) } } diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index 7fd0ce1..57a1d3f 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCMPC(s.service, s) + s.sut = NewUCMPC(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index a47fe04..b0e4abe 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -13,17 +13,17 @@ import ( type UCMPC struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCMCPInterface = (*UCMPC)(nil) -func NewUCMPC(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCMPC { +func NewUCMPC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMPC { uc := &UCMPC{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucopev/events.go b/ucopev/events.go index ee335d8..42e9b37 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -86,7 +86,7 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entity continue } - e.reader.Event(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) return } diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index b9040ec..5c73af2 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCOPEV(s.service, s) + s.sut = NewUCOPEV(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 7cc5795..cbb9b14 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -13,17 +13,17 @@ import ( type UCOPEV struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCOPEVInterface = (*UCOPEV)(nil) -func NewUCOPEV(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCOPEV { +func NewUCOPEV(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOPEV { uc := &UCOPEV{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucoscev/events.go b/ucoscev/events.go index 62cbb58..2aae367 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -51,7 +51,7 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entit continue } - e.reader.Event(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) return } diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index 26780be..a89256a 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCOSCEV(s.service, s) + s.sut = NewUCOSCEV(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 07eaac0..e0c7cac 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -13,17 +13,17 @@ import ( type UCOSCEV struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCOSCEVInterface = (*UCOSCEV)(nil) -func NewUCOSCEV(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCOSCEV { +func NewUCOSCEV(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOSCEV { uc := &UCOSCEV{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucvabd/events.go b/ucvabd/events.go index 26e5bc2..a0903d0 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -81,21 +81,21 @@ func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVABD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVABDPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVABDPowerTotalMeasurementDataUpdate) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVABDChargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVABDChargeMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeDischarge); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVABDDischargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVABDDischargeMeasurementDataUpdate) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVABDStateOfChargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVABDStateOfChargeMeasurementDataUpdate) } } diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go index c435b80..f26937b 100644 --- a/ucvabd/testhelper_test.go +++ b/ucvabd/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCVABDSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCVABD(s.service, s) + s.sut = NewUCVABD(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index 362475c..aa27331 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -13,17 +13,17 @@ import ( type UCVABD struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCVABDInterface = (*UCVABD)(nil) -func NewUCVABD(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCVABD { +func NewUCVABD(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVABD { uc := &UCVABD{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ diff --git a/ucvapd/events.go b/ucvapd/events.go index ac3467f..b820069 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -97,7 +97,7 @@ func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.Ent // Scenario 1 if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { if _, err := deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVAPDPeakPowerDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVAPDPeakPowerDataUpdate) } } } @@ -116,11 +116,11 @@ func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVAPD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVAPDPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVAPDPowerTotalMeasurementDataUpdate) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACYieldTotal); err == nil { - e.reader.Event(ski, entity.Device(), entity, api.UCVAPDYieldTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, api.UCVAPDYieldTotalMeasurementDataUpdate) } } diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index 9b3e9e3..7ad9022 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -64,7 +64,7 @@ func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() - s.sut = NewUCVAPD(s.service, s) + s.sut = NewUCVAPD(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 0e33a95..6db5ba7 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -13,17 +13,17 @@ import ( type UCVAPD struct { service serviceapi.ServiceInterface - reader api.EventReaderInterface + eventCB api.EventHandlerCB validEntityTypes []model.EntityTypeType } var _ UCVAPDInterface = (*UCVAPD)(nil) -func NewUCVAPD(service serviceapi.ServiceInterface, reader api.EventReaderInterface) *UCVAPD { +func NewUCVAPD(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVAPD { uc := &UCVAPD{ service: service, - reader: reader, + eventCB: eventCB, } uc.validEntityTypes = []model.EntityTypeType{ From e4636ae14a46c11ed2a4cb5f44e9117916ad4030 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 19:13:11 +0100 Subject: [PATCH 109/227] Add missing file --- cmd/democem/eventcb.go | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 cmd/democem/eventcb.go diff --git a/cmd/democem/eventcb.go b/cmd/democem/eventcb.go new file mode 100644 index 0000000..065f567 --- /dev/null +++ b/cmd/democem/eventcb.go @@ -0,0 +1,10 @@ +package democem + +import ( + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" +) + +// Handle incoming usecase specific events +func (h *DemoCem) eventCB(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} From 12a5784a9a6e6b0b88181dc0877602ef547405a4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 21:47:55 +0100 Subject: [PATCH 110/227] Restructure and align event type definitions --- api/types.go | 347 ----------------------------------------- cem/events.go | 5 +- cem/types.go | 12 ++ go.mod | 6 - uccevc/events.go | 17 +- uccevc/types.go | 36 +++++ ucevcc/api.go | 4 +- ucevcc/events.go | 29 +++- ucevcc/public.go | 6 +- ucevcc/public_test.go | 28 ++-- ucevcc/types.go | 52 ++++++ ucevcem/api.go | 2 +- ucevcem/events.go | 9 +- ucevcem/public.go | 2 +- ucevcem/public_test.go | 10 +- ucevcem/types.go | 33 ++++ ucevsecc/events.go | 9 +- ucevsecc/types.go | 21 +++ ucevsoc/events.go | 13 +- ucevsoc/types.go | 12 ++ ucmgcp/api.go | 2 +- ucmgcp/events.go | 15 +- ucmgcp/public.go | 2 +- ucmgcp/public_test.go | 12 +- ucmgcp/types.go | 55 +++++++ ucmpc/api.go | 2 +- ucmpc/events.go | 15 +- ucmpc/public.go | 2 +- ucmpc/public_test.go | 12 +- ucmpc/types.go | 54 +++++++ ucopev/events.go | 3 +- ucopev/types.go | 8 + ucoscev/events.go | 4 +- ucoscev/types.go | 10 ++ ucvabd/events.go | 9 +- ucvabd/types.go | 33 ++++ ucvapd/events.go | 7 +- ucvapd/types.go | 24 +++ 38 files changed, 454 insertions(+), 468 deletions(-) create mode 100644 cem/types.go create mode 100644 uccevc/types.go create mode 100644 ucevcc/types.go create mode 100644 ucevcem/types.go create mode 100644 ucevsecc/types.go create mode 100644 ucevsoc/types.go create mode 100644 ucmgcp/types.go create mode 100644 ucmpc/types.go create mode 100644 ucopev/types.go create mode 100644 ucoscev/types.go create mode 100644 ucvabd/types.go create mode 100644 ucvapd/types.go diff --git a/api/types.go b/api/types.go index f06268d..5b421d1 100644 --- a/api/types.go +++ b/api/types.go @@ -150,351 +150,4 @@ const ( // type for cem and usecase specfic event names type EventType string -const ( - // CEM - - // A paired remote device was connected - DeviceConnected EventType = "deviceConnected" - - // A paired remote device was disconnected - DeviceDisconnected EventType = "deviceDisconnected" - - // Visible remote eebus services list was updated - VisibleRemoteServicesUpdated EventType = "visibleRemoteServicesUpdated" - - // UCCEVC - - // EV provided an energy demand - UCCEVCEnergyDemandProvided EventType = "ucCEVCEnergyDemandProvided" - - // EV provided a charge plan - UCCEVCChargePlanProvided EventType = "ucCEVCChargePlanProvided" - - // EV provided a charge plan constraints - UCCEVCChargePlanConstraintsProvided EventType = "ucCEVCChargePlanConstraintsProvided" - - UCCEVCIncentiveDescriptionsRequired EventType = "ucCEVCIncentiveDescriptionsRequired" - - // EV incentive table data updated - UCCEVCIncentiveTableDataUpdate EventType = "ucCEVCIncentiveTableDataUpdate" - - // EV requested power limits - UCCEVPowerLimitsRequested EventType = "ucCEVPowerLimitsRequested" - - // EV requested incentives - UCCEVCIncentivesRequested EventType = "ucCEVCIncentivesRequested" - - // UCEVCC - - // An EV was connected - // - // Use Case EVCC, Scenario 1 - UCEVCCEventConnected EventType = "ucEVCCEventConnected" - - // An EV was disconnected - // - // Use Case EVCC, Scenario 8 - UCEVCCEventDisconnected EventType = "ucEVCCEventDisconnected" - - // EV communication standard data was updated - // - // Use Case EVCC, Scenario 2 - // Note: the referred data may be updated together with all other configuration items of this use case - UCEVCCCommunicationStandardConfigurationDataUpdate EventType = "ucEVCCCommunicationStandardConfigurationDataUpdate" - - // EV asymmetric charging data was updated - // - // Use Case EVCC, Scenario 3 - // - // Note: the referred data may be updated together with all other configuration items of this use case - UCEVCCAsymmetricChargingConfigurationDataUpdate EventType = "ucEVCCAsymmetricChargingConfigurationDataUpdate" - - // EV identificationdata was updated - // - // Use Case EVCC, Scenario 4 - UCEVCCIdentificationDataUpdate EventType = "ucEVCCIdentificationDataUpdate" - - // EV manufacturer data was updated - // - // Use Case EVCC, Scenario 5 - UCEVCCManufacturerDataUpdate EventType = "ucEVCCManufacturerDataUpdate" - - // EV charging power limits - // - // Use Case EVCC, Scenario 6 - UCEVCCChargingPowerLimitsDataUpdate EventType = "ucEVCCChargingPowerLimitsDataUpdate" - - // EV permitted power limits updated - // - // Use Case EVCC, Scenario 7 - UCEVCCSleepModeDataUpdate EventType = "ucEVCCSleepModeDataUpdate" - - // UCEVCEM - - // EV number of connected phases data updated - // - // Use Case EVCEM, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMNumberOfConnectedPhasesDataUpdate EventType = "ucEVCEMNumberOfConnectedPhasesDataUpdate" - - // EV current measurement data updated - // - // Use Case EVCEM, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMCurrentMeasurementDataUpdate EventType = "ucEVCEMCurrentMeasurementDataUpdate" - - // EV power measurement data updated - // - // Use Case EVCEM, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMPowerMeasurementDataUpdate EventType = "ucEVCEMCurrentMeasurementDataUpdate" - - // EV charging energy measurement data updated - // - // Use Case EVCEM, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVCEMChargingEnergyMeasurementDataUpdate EventType = "UCEVCEMChargingEnergyMeasurementDataUpdate" - - // UCEVSECC - - // An EVSE was connected - UCEVSECCEventConnected EventType = "ucEVSEConnected" - - // An EVSE was disconnected - UCEVSECCEventDisconnected EventType = "ucEVSEDisonnected" - - // EVSE manufacturer data was updated - // - // Use Case EVSECC, Scenario 1 - UCEVSECCEventManufacturerUpdate EventType = "ucEVSEManufacturerUpdate" - - // EVSE operation state was updated - // - // Use Case EVSECC, Scenario 2 - UCEVSECCEventOperationStateUpdate EventType = "ucEVSEOperationStateUpdate" - - // UCEVSOC - - // EV state of charge data was updated - // - // Use Case EVSOC, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVSOCStateOfChargeMeasurementDataUpdate EventType = "ucEVSOCStateOfChargeMeasurementDataUpdate" - - // EV nominal capacity data was updated - // - // Use Case EVSOC, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVSOCNominalCapacityMeasurementDataUpdate EventType = "ucEVSOCNominalCapacityMeasurementDataUpdate" - - // EV state of health data was updated - // - // Use Case EVSOC, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - EVSOCStateOfHealthMeasurementDataUpdate EventType = "ucEVSOCStateOfHealthMeasurementDataUpdate" - - // EV actual range data was updated - // - // Use Case EVSOC, Scenario 4 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCEVSOCActualRangeMeasurementDataUpdate EventType = "ucEVSOCActualRangeMeasurementDataUpdate" - - // LPC - - // Load control obligation limit data updated - // - // Use Case LPC, Scenario 1 - UCLPCLoadControlLimitDataUpdate EventType = "ucLPCLoadControlLimitDataUpdate" - - // Failsafe limit for the consumed active (real) power of the - // Controllable System data updated - // - // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case - UCLPCFailSafeConsumptionActivePowerLimitDataUpdate EventType = "ucLPCFailSafeConsumptionActivePowerLimitDataUpdate" - - // Minimum time the Controllable System remains in "failsafe state" unless conditions - // specified in this Use Case permit leaving the "failsafe state" data updated - // - // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case - UCLPCFailsafeDurationMinimumDataUpdate EventType = "ucLPCFailsafeDurationMinimumDataUpdate" - - // MGCP - - // Grid maximum allowed feed-in power as percentage value of the cumulated - // nominal peak power of all electricity producting PV systems was updated - // - // Use Case MGCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPPVFeedInPowerLimitationFactorDataUpdate EventType = "ucMGCPPVFeedInPowerLimitationFactorDataUpdate" - - // Grid momentary power consumption/production data updated - // - // Use Case MGCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPPowerTotalMeasurementDataUpdate EventType = "ucMGCPPowerTotalMeasurementDataUpdate" - - // MTotal grid feed in energy data updated - // - // Use Case MGCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPGridFeedInMeasurementDataUpdate EventType = "ucMGCPGridFeedInMeasurementDataUpdate" - - // Total grid consumed energy data updated - // - // Use Case MGCP, Scenario 4 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPGridConsumptionMeasurementDataUpdate EventType = "ucMGCPGridConsumptionMeasurementDataUpdate" - - // Phase specific momentary current consumption/production phase detail data updated - // - // Use Case MGCP, Scenario 5 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPCurrentsMeasurementDataUpdate EventType = "ucMGCPCurrentsMeasurementDataUpdate" - - // Phase specific voltage at the grid connection point - // - // Use Case MGCP, Scenario 6 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPVoltagesMeasurementDataUpdate EventType = "ucMGCPVoltagesMeasurementDataUpdate" - - // Grid frequency data updated - // - // Use Case MGCP, Scenario 7 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMGCPFrequencyMeasurementDataUpdate EventType = "ucMGCPFrequencyMeasurementDataUpdate" - - // UCMPC - - // Total momentary active power consumption or production - // - // Use Case MCP, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCPowerTotalMeasurementDataUpdate EventType = "ucMPCPowerTotalMeasurementDataUpdate" - - // Phase specific momentary active power consumption or production - // - // Use Case MCP, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCPowerPerPhaseMeasurementDataUpdate EventType = "ucMPCPowerPerPhaseMeasurementDataUpdate" - - // Total energy consumed - // - // Use Case MCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCEnergyConsumedMeasurementDataUpdate EventType = "ucMPCEnergyConsumedMeasurementDataUpdate" - - // Total energy produced - // - // Use Case MCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCEnergyProcudedMeasurementDataUpdate EventType = "ucMPCEnergyProcudedMeasurementDataUpdate" - - // Phase specific momentary current consumption or production - // - // Use Case MCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCCurrentsMeasurementDataUpdate EventType = "ucMPCCurrentsMeasurementDataUpdate" - - // Phase specific voltage - // - // Use Case MCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCVoltagesMeasurementDataUpdate EventType = "ucMPCVoltagesMeasurementDataUpdate" - - // Power network frequency data updated - // - // Use Case MCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCMPCFrequencyMeasurementDataUpdate EventType = "ucMPCFrequencyMeasurementDataUpdate" - - // UCOPEV - - // EV load control obligation limit data updated - UCOPEVLoadControlLimitDataUpdate EventType = "ucOPEVLoadControlLimitDataUpdate" - - // UCOSCEV - - // EV load control recommendation limit data updated - // - // Use Case OSCEV, Scenario 1 - UCOSCEVLoadControlLimitDataUpdate EventType = "ucOSCEVLoadControlLimitDataUpdate" - - // UCVABD - - // Battery System (dis)charge power data updated - // - // Use Case VABD, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDPowerTotalMeasurementDataUpdate EventType = "ucVABDPowerTotalMeasurementDataUpdate" - - // Battery System cumulated charge energy data updated - // - // Use Case VABD, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDChargeMeasurementDataUpdate EventType = "ucVABDChargeMeasurementDataUpdate" - - // Battery System cumulated discharge energy data updated - // - // Use Case VABD, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDDischargeMeasurementDataUpdate EventType = "ucVABDDischargeMeasurementDataUpdate" - - // Battery System state of charge data updated - // - // Use Case VABD, Scenario 4 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCVABDStateOfChargeMeasurementDataUpdate EventType = "ucVABDStateOfChargeMeasurementDataUpdate" - - // UCVAPD - - // PV System total power data updated - // - // Use Case VAPD, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCVAPDPowerTotalMeasurementDataUpdate EventType = "ucVAPDPowerTotalMeasurementDataUpdate" - - // PV System nominal peak power data updated - // - // Use Case VAPD, Scenario 2 - UCVAPDPeakPowerDataUpdate EventType = "ucVAPDPeakPowerDataUpdate" - - // PV System total yield data updated - // - // Use Case VAPD, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case - UCVAPDYieldTotalMeasurementDataUpdate EventType = "ucVAPDYieldTotalMeasurementDataUpdate" -) - var ErrNoCompatibleEntity = errors.New("entity is not an compatible entity") diff --git a/cem/events.go b/cem/events.go index 1f1b6fd..399be90 100644 --- a/cem/events.go +++ b/cem/events.go @@ -1,7 +1,6 @@ package cem import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" spineapi "github.com/enbility/spine-go/api" ) @@ -10,12 +9,12 @@ import ( func (h *Cem) HandleEvent(payload spineapi.EventPayload) { if util.IsDeviceConnected(payload) { - h.eventCB(payload.Ski, payload.Device, nil, api.DeviceConnected) + h.eventCB(payload.Ski, payload.Device, nil, DeviceConnected) return } if util.IsDeviceDisconnected(payload) { - h.eventCB(payload.Ski, payload.Device, nil, api.DeviceDisconnected) + h.eventCB(payload.Ski, payload.Device, nil, DeviceDisconnected) return } } diff --git a/cem/types.go b/cem/types.go new file mode 100644 index 0000000..1505ac8 --- /dev/null +++ b/cem/types.go @@ -0,0 +1,12 @@ +package cem + +import "github.com/enbility/cemd/api" + +const ( + + // A paired remote device was connected + DeviceConnected api.EventType = "deviceConnected" + + // A paired remote device was disconnected + DeviceDisconnected api.EventType = "deviceDisconnected" +) diff --git a/go.mod b/go.mod index 833a302..3ef4931 100644 --- a/go.mod +++ b/go.mod @@ -30,9 +30,3 @@ require ( golang.org/x/tools v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// replace github.com/enbility/eebus-go => ../eebus-go - -// replace github.com/enbility/ship-go => ../ship-go - -// replace github.com/enbility/spine-go => ../spine-go diff --git a/uccevc/events.go b/uccevc/events.go index f2fdcf3..bf436e4 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -1,7 +1,6 @@ package uccevc import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -113,7 +112,7 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.eventCB(ski, entity.Device(), entity, api.UCCEVCEnergyDemandProvided) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyDemand) _, err = e.TimeSlotConstraints(entity) if err != nil { @@ -127,18 +126,17 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E return } - e.eventCB(ski, entity.Device(), entity, api.UCCEVPowerLimitsRequested) - e.eventCB(ski, entity.Device(), entity, api.UCCEVCIncentivesRequested) + e.eventCB(ski, entity.Device(), entity, DataRequestedPowerLimitsAndIncentives) } // the load control limit data of an EV was updated func (e *UCCEVC) evTimeSeriesDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if _, err := e.ChargePlan(entity); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCCEVCChargePlanProvided) + e.eventCB(ski, entity.Device(), entity, DataUpdateChargePlan) } if _, err := e.ChargePlanConstraints(entity); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCCEVCChargePlanConstraintsProvided) + e.eventCB(ski, entity.Device(), entity, DataUpdateTimeSlotConstraints) } } @@ -152,16 +150,15 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spinea } // check if we are required to update the plan - if !e.evCheckIncentiveTableDescriptionUpdateRequired(entity) { - return + if e.evCheckIncentiveTableDescriptionUpdateRequired(entity) { + e.eventCB(ski, entity.Device(), entity, DataRequestedIncentiveTableDescription) } - e.eventCB(ski, entity.Device(), entity, api.UCCEVCIncentiveDescriptionsRequired) } // the load control limit data of an EV was updated func (e *UCCEVC) evIncentiveTableDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.eventCB(ski, entity.Device(), entity, api.UCCEVCIncentiveTableDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateIncentiveTable) } // check timeSeries descriptions if constraints element has updateRequired set to true diff --git a/uccevc/types.go b/uccevc/types.go new file mode 100644 index 0000000..11ca359 --- /dev/null +++ b/uccevc/types.go @@ -0,0 +1,36 @@ +package uccevc + +import "github.com/enbility/cemd/api" + +const ( + // Scenario 1 + + // EV provided an energy demand + DataUpdateEnergyDemand api.EventType = "DataUpdateEnergyDemand" + + // Scenario 2 + + // EV provided a charge plan constraints + DataUpdateTimeSlotConstraints api.EventType = "DataUpdateTimeSlotConstraints" + + // Scenario 3 + + // EV incentive table data updated + DataUpdateIncentiveTable api.EventType = "DataUpdateIncentiveTable" + + // EV requested an incentive table, call to WriteIncentiveTableDescriptions required + DataRequestedIncentiveTableDescription api.EventType = "DataRequestedIncentiveTableDescription" + + // Scenario 2 & 3 + + // EV requested power limits, call to WritePowerLimits and WriteIncentives required + DataRequestedPowerLimitsAndIncentives api.EventType = "DataRequestedPowerLimitsAndIncentives" + + // Scenario 4 + + // EV provided a charge plan + DataUpdateChargePlanConstraints api.EventType = "DataUpdateChargePlanConstraints" + + // EV provided a charge plan + DataUpdateChargePlan api.EventType = "DataUpdateChargePlan" +) diff --git a/ucevcc/api.go b/ucevcc/api.go index 59f3388..088f2d2 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -12,7 +12,7 @@ type UCEVCCInterface interface { api.UseCaseInterface // return the current charge state of the EV - CurrentChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) + ChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) // Scenario 1 & 8 @@ -27,7 +27,7 @@ type UCEVCCInterface interface { // Scenario 3 // return if the EV supports asymmetric charging - AsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) + AsymmetricChargingSupport(entity spineapi.EntityRemoteInterface) (bool, error) // Scenario 4 diff --git a/ucevcc/events.go b/ucevcc/events.go index 6c0c8e8..4f6a23d 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -1,7 +1,6 @@ package ucevcc import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -39,6 +38,8 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { e.evConfigurationDescriptionDataUpdate(payload.Entity) case *model.DeviceConfigurationKeyValueListDataType: e.evConfigurationDataUpdate(payload.Ski, payload.Entity) + case *model.DeviceDiagnosisOperatingStateType: + e.evOperatingStateDataUpdate(payload.Ski, payload.Entity) case *model.DeviceClassificationManufacturerDataType: e.evManufacturerDataUpdate(payload.Ski, payload.Entity) case *model.ElectricalConnectionParameterDescriptionListDataType: @@ -112,12 +113,12 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - e.eventCB(ski, entity.Device(), entity, api.UCEVCCEventConnected) + e.eventCB(ski, entity.Device(), entity, EvConnected) } // an EV was disconnected func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.eventCB(ski, entity.Device(), entity, api.UCEVCCEventDisconnected) + e.eventCB(ski, entity.Device(), entity, EvDisconnected) } // the configuration key description data of an EV was updated @@ -139,12 +140,24 @@ func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRem // Scenario 2 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVCCCommunicationStandardConfigurationDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateCommunicationStandard) } // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVCCAsymmetricChargingConfigurationDataUpdate) + e.eventCB(ski, entity.Device(), entity, AsymmetricChargingSupportDataUpdate) + } +} + +// the operating state of an EV was updated +func (e *UCEVCC) evOperatingStateDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { + deviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) + if err != nil { + return + } + + if _, err := deviceDiagnosis.GetState(); err == nil { + e.eventCB(ski, entity.Device(), entity, DataUpdateIdentifications) } } @@ -162,7 +175,7 @@ func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRe continue } - e.eventCB(ski, entity.Device(), entity, api.UCEVCCIdentificationDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateIdentifications) return } } @@ -177,7 +190,7 @@ func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemo // Scenario 5 if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVCCManufacturerDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateManufacturerData) } } @@ -209,5 +222,5 @@ func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.E } // Scenario 6 - e.eventCB(ski, entity.Device(), entity, api.UCEVCCChargingPowerLimitsDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentLimits) } diff --git a/ucevcc/public.go b/ucevcc/public.go index 3bf2a56..33f6636 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -9,7 +9,7 @@ import ( ) // return the current charge state of the EV -func (e *UCEVCC) CurrentChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) { +func (e *UCEVCC) ChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) { if entity == nil || entity.EntityType() != model.EntityTypeTypeEV { return api.EVChargeStateTypeUnplugged, nil } @@ -53,7 +53,7 @@ func (e *UCEVCC) EVConnected(entity spineapi.EntityRemoteInterface) bool { } // getting current charge state should work - if _, err := e.CurrentChargeState(entity); err != nil { + if _, err := e.ChargeState(entity); err != nil { return false } @@ -135,7 +135,7 @@ func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (s // // possible errors: // - ErrDataNotAvailable if that information is not (yet) available -func (e *UCEVCC) AsymmetricChargingSupported(entity spineapi.EntityRemoteInterface) (bool, error) { +func (e *UCEVCC) AsymmetricChargingSupport(entity spineapi.EntityRemoteInterface) (bool, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return false, api.ErrNoCompatibleEntity } diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index d9ca525..1d2f6ac 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -10,12 +10,12 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCEVCCSuite) Test_EVCurrentChargeState() { - data, err := s.sut.CurrentChargeState(s.mockRemoteEntity) +func (s *UCEVCCSuite) Test_ChargeState() { + data, err := s.sut.ChargeState(s.mockRemoteEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeUnplugged, data) - data, err = s.sut.CurrentChargeState(s.evEntity) + data, err = s.sut.ChargeState(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeUnknown, data) @@ -27,7 +27,7 @@ func (s *UCEVCCSuite) Test_EVCurrentChargeState() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargeState(s.evEntity) + data, err = s.sut.ChargeState(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeActive, data) @@ -38,7 +38,7 @@ func (s *UCEVCCSuite) Test_EVCurrentChargeState() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargeState(s.evEntity) + data, err = s.sut.ChargeState(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypePaused, data) @@ -49,7 +49,7 @@ func (s *UCEVCCSuite) Test_EVCurrentChargeState() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargeState(s.evEntity) + data, err = s.sut.ChargeState(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeError, data) @@ -60,7 +60,7 @@ func (s *UCEVCCSuite) Test_EVCurrentChargeState() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargeState(s.evEntity) + data, err = s.sut.ChargeState(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeFinished, data) @@ -71,7 +71,7 @@ func (s *UCEVCCSuite) Test_EVCurrentChargeState() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, stateData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentChargeState(s.evEntity) + data, err = s.sut.ChargeState(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), api.EVChargeStateTypeUnknown, data) } @@ -159,12 +159,12 @@ func (s *UCEVCCSuite) Test_EVCommunicationStandard() { assert.Equal(s.T(), string(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), data) } -func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupported() { - data, err := s.sut.AsymmetricChargingSupported(s.mockRemoteEntity) +func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupport() { + data, err := s.sut.AsymmetricChargingSupport(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) - data, err = s.sut.AsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupport(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -181,7 +181,7 @@ func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupported() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.AsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupport(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -197,7 +197,7 @@ func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupported() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.AsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupport(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), false, data) @@ -215,7 +215,7 @@ func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupported() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, devData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.AsymmetricChargingSupported(s.evEntity) + data, err = s.sut.AsymmetricChargingSupport(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), true, data) } diff --git a/ucevcc/types.go b/ucevcc/types.go new file mode 100644 index 0000000..bd9f1a9 --- /dev/null +++ b/ucevcc/types.go @@ -0,0 +1,52 @@ +package ucevcc + +import "github.com/enbility/cemd/api" + +const ( + + // An EV was connected + // + // Use Case EVCC, Scenario 1 + EvConnected api.EventType = "EvConnected" + + // An EV was disconnected + // + // Use Case EVCC, Scenario 8 + EvDisconnected api.EventType = "EvDisconnected" + + // EV charge state data was updated + DataUpdateChargeState api.EventType = "DataUpdateChargeState" + + // EV communication standard data was updated + // + // Use Case EVCC, Scenario 2 + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateCommunicationStandard api.EventType = "DataUpdateCommunicationStandard" + + // EV asymmetric charging data was updated + // + // Use Case EVCC, Scenario 3 + // + // Note: the referred data may be updated together with all other configuration items of this use case + AsymmetricChargingSupportDataUpdate api.EventType = "AsymmetricChargingSupportDataUpdate" + + // EV identificationdata was updated + // + // Use Case EVCC, Scenario 4 + DataUpdateIdentifications api.EventType = "DataUpdateIdentifications" + + // EV manufacturer data was updated + // + // Use Case EVCC, Scenario 5 + DataUpdateManufacturerData api.EventType = "DataUpdateManufacturerData" + + // EV charging power limits + // + // Use Case EVCC, Scenario 6 + DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" + + // EV permitted power limits updated + // + // Use Case EVCC, Scenario 7 + DataUpdateIsInSleepMode api.EventType = "DataUpdateIsInSleepMode" +) diff --git a/ucevcem/api.go b/ucevcem/api.go index c306d56..d987cc9 100644 --- a/ucevcem/api.go +++ b/ucevcem/api.go @@ -17,7 +17,7 @@ type UCEVCEMInterface interface { // Scenario 1 // return the last current measurement for each phase of the connected EV - CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 2 diff --git a/ucevcem/events.go b/ucevcem/events.go index a3ff29a..ba51aa1 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -1,7 +1,6 @@ package ucevcem import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -61,7 +60,7 @@ func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity return } - e.eventCB(ski, entity.Device(), entity, api.UCEVCEMNumberOfConnectedPhasesDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePhasesConnected) } // the measurement description data of an EV was updated @@ -78,16 +77,16 @@ func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemot func (e *UCEVCEM) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVCEMCurrentMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentPerPhase) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVCEMPowerMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePowerPerPhase) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVCEMChargingEnergyMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyCharged) } } diff --git a/ucevcem/public.go b/ucevcem/public.go index abdd47b..329a38d 100644 --- a/ucevcem/public.go +++ b/ucevcem/public.go @@ -41,7 +41,7 @@ func (e *UCEVCEM) PhasesConnected(entity spineapi.EntityRemoteInterface) (uint, // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEVCEM) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCEVCEM) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } diff --git a/ucevcem/public_test.go b/ucevcem/public_test.go index c954bf4..4bab63f 100644 --- a/ucevcem/public_test.go +++ b/ucevcem/public_test.go @@ -48,12 +48,12 @@ func (s *UCEVCEMSuite) Test_EVConnectedPhases() { assert.Equal(s.T(), uint(1), data) } -func (s *UCEVCEMSuite) Test_EVCurrentsPerPhase() { - data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) +func (s *UCEVCEMSuite) Test_EVCurrentPerPhase() { + data, err := s.sut.CurrentPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.CurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -92,7 +92,7 @@ func (s *UCEVCEMSuite) Test_EVCurrentsPerPhase() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, measDesc, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentPerPhase(s.evEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -108,7 +108,7 @@ func (s *UCEVCEMSuite) Test_EVCurrentsPerPhase() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.evEntity) + data, err = s.sut.CurrentPerPhase(s.evEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 10.0, data[0]) } diff --git a/ucevcem/types.go b/ucevcem/types.go new file mode 100644 index 0000000..586b465 --- /dev/null +++ b/ucevcem/types.go @@ -0,0 +1,33 @@ +package ucevcem + +import "github.com/enbility/cemd/api" + +const ( + // EV number of connected phases data updated + // + // Use Case EVCEM, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePhasesConnected api.EventType = "DataUpdatePhasesConnected" + + // EV current measurement data updated + // + // Use Case EVCEM, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateCurrentPerPhase api.EventType = "DataUpdateCurrentPerPhase" + + // EV power measurement data updated + // + // Use Case EVCEM, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePowerPerPhase api.EventType = "DataUpdatePowerPerPhase" + + // EV charging energy measurement data updated + // + // Use Case EVCEM, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyCharged api.EventType = "DataUpdateEnergyCharged" +) diff --git a/ucevsecc/events.go b/ucevsecc/events.go index c94dfd9..02cbfae 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -1,7 +1,6 @@ package ucevsecc import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/eebus-go/features" spineapi "github.com/enbility/spine-go/api" @@ -55,12 +54,12 @@ func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterfa _, _ = evseDeviceDiagnosis.RequestState() } - e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventConnected) + e.eventCB(ski, entity.Device(), entity, EvseConnected) } // an EVSE was disconnected func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventDisconnected) + e.eventCB(ski, entity.Device(), entity, EvseDisconnected) } // the manufacturer Data of an EVSE was updated @@ -71,7 +70,7 @@ func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.Entity } if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventManufacturerUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateManufacturerData) } } @@ -83,6 +82,6 @@ func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInter } if _, err := evDeviceDiagnosis.GetState(); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVSECCEventOperationStateUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateOperatingState) } } diff --git a/ucevsecc/types.go b/ucevsecc/types.go new file mode 100644 index 0000000..fe7cb91 --- /dev/null +++ b/ucevsecc/types.go @@ -0,0 +1,21 @@ +package ucevsecc + +import "github.com/enbility/cemd/api" + +const ( + // An EVSE was connected + EvseConnected api.EventType = "EvseConnected" + + // An EVSE was disconnected + EvseDisconnected api.EventType = "EvseDisconnected" + + // EVSE manufacturer data was updated + // + // Use Case EVSECC, Scenario 1 + DataUpdateManufacturerData api.EventType = "DataUpdateManufacturerData" + + // EVSE operation state was updated + // + // Use Case EVSECC, Scenario 2 + DataUpdateOperatingState api.EventType = "DataUpdateOperatingState" +) diff --git a/ucevsoc/events.go b/ucevsoc/events.go index ef8eff6..8cc2736 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -1,7 +1,6 @@ package ucevsoc import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -58,16 +57,6 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVSOCStateOfChargeMeasurementDataUpdate) - } - - // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfHealth); err == nil { - e.eventCB(ski, entity.Device(), entity, api.EVSOCStateOfHealthMeasurementDataUpdate) - } - - // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeTravelRange); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCEVSOCActualRangeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateStateOfCharge) } } diff --git a/ucevsoc/types.go b/ucevsoc/types.go new file mode 100644 index 0000000..c2e4d11 --- /dev/null +++ b/ucevsoc/types.go @@ -0,0 +1,12 @@ +package ucevsoc + +import "github.com/enbility/cemd/api" + +const ( + // EV state of charge data was updated + // + // Use Case EVSOC, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateStateOfCharge api.EventType = "DataUpdateStateOfCharge" +) diff --git a/ucmgcp/api.go b/ucmgcp/api.go index c975769..60df64c 100644 --- a/ucmgcp/api.go +++ b/ucmgcp/api.go @@ -48,7 +48,7 @@ type UCMGCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 6 diff --git a/ucmgcp/events.go b/ucmgcp/events.go index d29738d..5b20286 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -1,7 +1,6 @@ package ucmgcp import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -95,7 +94,7 @@ func (e *UCMGCP) gridConfigurationDescriptionDataUpdate(entity spineapi.EntityRe // the configuration key data of an SMGW was updated func (e *UCMGCP) gridConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { if _, err := e.PowerLimitationFactor(entity); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPPVFeedInPowerLimitationFactorDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePowerLimitationFactor) } } @@ -113,32 +112,32 @@ func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemo func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePower) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridFeedIn); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPGridFeedInMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyFeedIn) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridConsumption); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPGridConsumptionMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyConsumed) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPCurrentsMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentPerPhase) } // Scenario 6 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPVoltagesMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateVoltagePerPhase) } // Scenario 7 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMGCPFrequencyMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateFrequency) } } diff --git a/ucmgcp/public.go b/ucmgcp/public.go index bb86279..e81cd87 100644 --- a/ucmgcp/public.go +++ b/ucmgcp/public.go @@ -151,7 +151,7 @@ func (e *UCMGCP) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, // // - positive values are used for consumption // - negative values are used for production -func (e *UCMGCP) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } diff --git a/ucmgcp/public_test.go b/ucmgcp/public_test.go index 6ab7757..c4e2689 100644 --- a/ucmgcp/public_test.go +++ b/ucmgcp/public_test.go @@ -219,12 +219,12 @@ func (s *UCMGCPSuite) Test_EnergyConsumed() { assert.Equal(s.T(), 10.0, data) } -func (s *UCMGCPSuite) Test_CurrentsPerPhase() { - data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) +func (s *UCMGCPSuite) Test_CurrentPerPhase() { + data, err := s.sut.CurrentPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.CurrentsPerPhase(s.smgwEntity) + data, err = s.sut.CurrentPerPhase(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -255,7 +255,7 @@ func (s *UCMGCPSuite) Test_CurrentsPerPhase() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.smgwEntity) + data, err = s.sut.CurrentPerPhase(s.smgwEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -279,7 +279,7 @@ func (s *UCMGCPSuite) Test_CurrentsPerPhase() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.smgwEntity) + data, err = s.sut.CurrentPerPhase(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 0, len(data)) @@ -319,7 +319,7 @@ func (s *UCMGCPSuite) Test_CurrentsPerPhase() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.smgwEntity) + data, err = s.sut.CurrentPerPhase(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) diff --git a/ucmgcp/types.go b/ucmgcp/types.go new file mode 100644 index 0000000..e2cdd30 --- /dev/null +++ b/ucmgcp/types.go @@ -0,0 +1,55 @@ +package ucmgcp + +import "github.com/enbility/cemd/api" + +const ( + // Grid maximum allowed feed-in power as percentage value of the cumulated + // nominal peak power of all electricity producting PV systems was updated + // + // Use Case MGCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePowerLimitationFactor api.EventType = "DataUpdatePowerLimitationFactor" + + // Grid momentary power consumption/production data updated + // + // Use Case MGCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePower api.EventType = "DataUpdatePower" + + // MTotal grid feed in energy data updated + // + // Use Case MGCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyFeedIn api.EventType = "DataUpdateEnergyFeedIn" + + // Total grid consumed energy data updated + // + // Use Case MGCP, Scenario 4 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyConsumed api.EventType = "DataUpdateEnergyConsumed" + + // Phase specific momentary current consumption/production phase detail data updated + // + // Use Case MGCP, Scenario 5 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateCurrentPerPhase api.EventType = "DataUpdateCurrentPerPhase" + + // Phase specific voltage at the grid connection point + // + // Use Case MGCP, Scenario 6 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateVoltagePerPhase api.EventType = "DataUpdateVoltagePerPhase" + + // Grid frequency data updated + // + // Use Case MGCP, Scenario 7 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateFrequency api.EventType = "DataUpdateFrequency" +) diff --git a/ucmpc/api.go b/ucmpc/api.go index 599e026..84493ac 100644 --- a/ucmpc/api.go +++ b/ucmpc/api.go @@ -45,7 +45,7 @@ type UCMCPInterface interface { // // - positive values are used for consumption // - negative values are used for production - CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) + CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 4 diff --git a/ucmpc/events.go b/ucmpc/events.go index b488bd6..d02072a 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -1,7 +1,6 @@ package ucmpc import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -81,35 +80,35 @@ func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRem func (e *UCMPC) deviceMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePower) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCPowerPerPhaseMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePowerPerPhase) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCEnergyConsumedMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyConsumed) } if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyProduced); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCEnergyProcudedMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyProduced) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCCurrentsMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentsPerPhase) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCVoltagesMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateVoltagePerPhase) } // Scenario 5 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCMPCFrequencyMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateFrequency) } } diff --git a/ucmpc/public.go b/ucmpc/public.go index 082ffc8..29e498c 100644 --- a/ucmpc/public.go +++ b/ucmpc/public.go @@ -123,7 +123,7 @@ func (e *UCMPC) EnergyProduced(entity spineapi.EntityRemoteInterface) (float64, // // - positive values are used for consumption // - negative values are used for production -func (e *UCMPC) CurrentsPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCMPC) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { return nil, api.ErrNoCompatibleEntity } diff --git a/ucmpc/public_test.go b/ucmpc/public_test.go index dbf97a4..5b2c9a2 100644 --- a/ucmpc/public_test.go +++ b/ucmpc/public_test.go @@ -279,12 +279,12 @@ func (s *UCMPCSuite) Test_EnergyProduced() { assert.Equal(s.T(), 10.0, data) } -func (s *UCMPCSuite) Test_CurrentsPerPhase() { - data, err := s.sut.CurrentsPerPhase(s.mockRemoteEntity) +func (s *UCMPCSuite) Test_CurrentPerPhase() { + data, err := s.sut.CurrentPerPhase(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) + data, err = s.sut.CurrentPerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -315,7 +315,7 @@ func (s *UCMPCSuite) Test_CurrentsPerPhase() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) + data, err = s.sut.CurrentPerPhase(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -339,7 +339,7 @@ func (s *UCMPCSuite) Test_CurrentsPerPhase() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, measData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) + data, err = s.sut.CurrentPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 0, len(data)) @@ -379,7 +379,7 @@ func (s *UCMPCSuite) Test_CurrentsPerPhase() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.CurrentsPerPhase(s.monitoredEntity) + data, err = s.sut.CurrentPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) diff --git a/ucmpc/types.go b/ucmpc/types.go new file mode 100644 index 0000000..891f2ab --- /dev/null +++ b/ucmpc/types.go @@ -0,0 +1,54 @@ +package ucmpc + +import "github.com/enbility/cemd/api" + +const ( + // Total momentary active power consumption or production + // + // Use Case MCP, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePower api.EventType = "DataUpdatePower" + + // Phase specific momentary active power consumption or production + // + // Use Case MCP, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePowerPerPhase api.EventType = "DataUpdatePowerPerPhase" + + // Total energy consumed + // + // Use Case MCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyConsumed api.EventType = "DataUpdateEnergyConsumed" + + // Total energy produced + // + // Use Case MCP, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyProduced api.EventType = "DataUpdateEnergyProduced" + + // Phase specific momentary current consumption or production + // + // Use Case MCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateCurrentsPerPhase api.EventType = "DataUpdateCurrentsPerPhase" + + // Phase specific voltage + // + // Use Case MCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateVoltagePerPhase api.EventType = "DataUpdateVoltagePerPhase" + + // Power network frequency data updated + // + // Use Case MCP, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateFrequency api.EventType = "DataUpdateFrequency" +) diff --git a/ucopev/events.go b/ucopev/events.go index 42e9b37..855987e 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -1,7 +1,6 @@ package ucopev import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -86,7 +85,7 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entity continue } - e.eventCB(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateLimit) return } diff --git a/ucopev/types.go b/ucopev/types.go new file mode 100644 index 0000000..42bd961 --- /dev/null +++ b/ucopev/types.go @@ -0,0 +1,8 @@ +package ucopev + +import "github.com/enbility/cemd/api" + +const ( + // EV load control obligation limit data updated + DataUpdateLimit api.EventType = "DataUpdateLimit" +) diff --git a/ucoscev/events.go b/ucoscev/events.go index 2aae367..c19ae06 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -1,7 +1,6 @@ package ucoscev import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" @@ -51,8 +50,7 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entit continue } - e.eventCB(ski, entity.Device(), entity, api.UCOPEVLoadControlLimitDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateLimit) return } - } diff --git a/ucoscev/types.go b/ucoscev/types.go new file mode 100644 index 0000000..40c8a79 --- /dev/null +++ b/ucoscev/types.go @@ -0,0 +1,10 @@ +package ucoscev + +import "github.com/enbility/cemd/api" + +const ( + // EV load control recommendation limit data updated + // + // Use Case OSCEV, Scenario 1 + DataUpdateLimit api.EventType = "DataUpdateLimit" +) diff --git a/ucvabd/events.go b/ucvabd/events.go index a0903d0..128f59b 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -1,7 +1,6 @@ package ucvabd import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -81,21 +80,21 @@ func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVABD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVABDPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePower) } // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVABDChargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyCharged) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeDischarge); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVABDDischargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyDischarged) } // Scenario 4 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVABDStateOfChargeMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdateStateOfCharge) } } diff --git a/ucvabd/types.go b/ucvabd/types.go new file mode 100644 index 0000000..75dacdc --- /dev/null +++ b/ucvabd/types.go @@ -0,0 +1,33 @@ +package ucvabd + +import "github.com/enbility/cemd/api" + +const ( + // Battery System (dis)charge power data updated + // + // Use Case VABD, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePower api.EventType = "d" + + // Battery System cumulated charge energy data updated + // + // Use Case VABD, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyCharged api.EventType = "DataUpdateEnergyCharged" + + // Battery System cumulated discharge energy data updated + // + // Use Case VABD, Scenario 2 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateEnergyDischarged api.EventType = "DataUpdateEnergyDischarged" + + // Battery System state of charge data updated + // + // Use Case VABD, Scenario 4 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdateStateOfCharge api.EventType = "DataUpdateStateOfCharge" +) diff --git a/ucvapd/events.go b/ucvapd/events.go index b820069..b216998 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -1,7 +1,6 @@ package ucvapd import ( - "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -97,7 +96,7 @@ func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.Ent // Scenario 1 if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { if _, err := deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVAPDPeakPowerDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePowerNominalPeak) } } } @@ -116,11 +115,11 @@ func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity func (e *UCVAPD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 2 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVAPDPowerTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePower) } // Scenario 3 if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACYieldTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, api.UCVAPDYieldTotalMeasurementDataUpdate) + e.eventCB(ski, entity.Device(), entity, DataUpdatePVYieldTotal) } } diff --git a/ucvapd/types.go b/ucvapd/types.go new file mode 100644 index 0000000..0348918 --- /dev/null +++ b/ucvapd/types.go @@ -0,0 +1,24 @@ +package ucvapd + +import "github.com/enbility/cemd/api" + +const ( + // PV System total power data updated + // + // Use Case VAPD, Scenario 1 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePower api.EventType = "DataUpdatePower" + + // PV System nominal peak power data updated + // + // Use Case VAPD, Scenario 2 + DataUpdatePowerNominalPeak api.EventType = "DataUpdatePowerNominalPeak" + + // PV System total yield data updated + // + // Use Case VAPD, Scenario 3 + // + // Note: the referred data may be updated together with all other measurement items of this use case + DataUpdatePVYieldTotal api.EventType = "DataUpdatePVYieldTotal" +) From 577b756484cffb5f00981e2b2040bf431d7347f7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 25 Feb 2024 22:00:13 +0100 Subject: [PATCH 111/227] Minor UCVAPD test improvement --- ucvapd/events.go | 4 +++- ucvapd/events_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/ucvapd/events.go b/ucvapd/events.go index b216998..0bd7851 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -95,7 +95,9 @@ func (e *UCVAPD) inverterConfigurationDescriptionDataUpdate(entity spineapi.Enti func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { // Scenario 1 if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { - if _, err := deviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { + if _, err := deviceConfiguration.GetKeyValueForKeyName( + model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, + model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { e.eventCB(ski, entity.Device(), entity, DataUpdatePowerNominalPeak) } } diff --git a/ucvapd/events_test.go b/ucvapd/events_test.go index 7aa3004..55132a1 100644 --- a/ucvapd/events_test.go +++ b/ucvapd/events_test.go @@ -39,6 +39,42 @@ func (s *UCVAPDSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCVAPDSuite) Test_inverterConfigurationDataUpdate() { + s.sut.inverterConfigurationDataUpdate(remoteSki, s.pvEntity) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.pvEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.inverterConfigurationDataUpdate(remoteSki, s.pvEntity) + + data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(10), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.inverterConfigurationDataUpdate(remoteSki, s.pvEntity) +} + func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) From b760b6caebb5677469e3689e4c5e471be40a3fc8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 27 Feb 2024 19:07:09 +0100 Subject: [PATCH 112/227] Update ship, eebus and adopt changed APIs --- go.mod | 6 +++--- go.sum | 12 ++++++------ uccevc/public_scen1.go | 8 ++++---- uccevc/public_scen2.go | 8 ++++---- uccevc/public_scen3.go | 6 +++--- uccevc/public_scen4.go | 14 +++++++------- uccevc/uccevc.go | 11 +++++------ ucevcc/public.go | 24 ++++++++++++------------ ucevcem/public.go | 8 ++++---- ucevsecc/events.go | 8 ++------ ucevsecc/ucevsecc.go | 6 +++--- ucevsoc/public.go | 4 ++-- ucevsoc/ucevsoc.go | 9 ++++----- ucmgcp/public.go | 20 ++++++++++---------- ucmgcp/ucmgcp.go | 13 ++++++------- ucmpc/public.go | 16 ++++++++-------- ucmpc/ucmcp.go | 13 ++++++------- ucopev/ucopev.go | 9 ++++----- ucoscev/ucoscev.go | 9 ++++----- ucvabd/public.go | 12 ++++++------ ucvabd/ucvabd.go | 11 +++++------ ucvapd/public.go | 14 +++++++------- ucvapd/ucvapd.go | 13 ++++++------- util/features.go | 20 ++++++++++---------- util/loadcontrol.go | 9 ++++----- util/measurement.go | 5 ++--- 26 files changed, 137 insertions(+), 151 deletions(-) diff --git a/go.mod b/go.mod index 3ef4931..c3ba5e9 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240225175714-2fe6960db55d - github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 - github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def + github.com/enbility/eebus-go v0.0.0-20240227180445-1494dc3a8a7c + github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae + github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 6014cc9..a9c31a9 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240225175714-2fe6960db55d h1:QfxCmsPYMK7RVkswwLpwXR9c3mnGMPCP4HlVHvZYvRs= -github.com/enbility/eebus-go v0.0.0-20240225175714-2fe6960db55d/go.mod h1:x7Z5n4MHueb8ScMUflOGGy3lw4flNc3APkux9E7lS2o= -github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0 h1:Mmzfj5wl7Ihw0ldiz65RjjtYeUiX8M/dpGZxtS7kpRU= -github.com/enbility/ship-go v0.0.0-20240221110810-073f3ca03af0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def h1:glevkOCgXd7IBiu69mTvv7sxgYc8W6B37yDBfcVYqhs= -github.com/enbility/spine-go v0.0.0-20240225172022-5e415a161def/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/eebus-go v0.0.0-20240227180445-1494dc3a8a7c h1:cjj1dPM6PABDAGm3kWtAlFIhD/4omb/mxoEY5byjuSA= +github.com/enbility/eebus-go v0.0.0-20240227180445-1494dc3a8a7c/go.mod h1:vV6SJeQ6ScyQbNMCxerWG2NGOIVkGpskq3iU+acvaak= +github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae h1:KOowUYPF7qmNvKLt81SPOslrOKyph2LEV8EyZ8xjxd8= +github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 h1:/oXzUGRXtOlQRflZD6nMd3aFOvl2n78u6G1v0IzGz8U= +github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/uccevc/public_scen1.go b/uccevc/public_scen1.go index 7e4e1e0..88b319c 100644 --- a/uccevc/public_scen1.go +++ b/uccevc/public_scen1.go @@ -5,7 +5,7 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -79,17 +79,17 @@ func (e *UCCEVC) EnergyDemand(entity spineapi.EntityRemoteInterface) (api.Demand evTimeSeries, err := util.TimeSeries(e.service, entity) if err != nil { - return demand, features.ErrDataNotAvailable + return demand, eebusapi.ErrDataNotAvailable } data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeSingleDemand) if err != nil { - return demand, features.ErrDataNotAvailable + return demand, eebusapi.ErrDataNotAvailable } // we need at least a time series slot if data.TimeSeriesSlot == nil { - return demand, features.ErrDataNotAvailable + return demand, eebusapi.ErrDataNotAvailable } // get the value for the first slot, ignore all others, which diff --git a/uccevc/public_scen2.go b/uccevc/public_scen2.go index 85af930..9b65157 100644 --- a/uccevc/public_scen2.go +++ b/uccevc/public_scen2.go @@ -6,7 +6,7 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" eebusutil "github.com/enbility/eebus-go/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -23,7 +23,7 @@ func (e *UCCEVC) TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api evTimeSeries, err := util.TimeSeries(e.service, entity) if err != nil { - return result, features.ErrDataNotAvailable + return result, eebusapi.ErrDataNotAvailable } constraints, err := evTimeSeries.GetConstraints() @@ -68,7 +68,7 @@ func (e *UCCEVC) WritePowerLimits(entity spineapi.EntityRemoteInterface, data [] evTimeSeries, err := util.TimeSeries(e.service, entity) if err != nil { - return features.ErrDataNotAvailable + return eebusapi.ErrDataNotAvailable } if len(data) == 0 { @@ -93,7 +93,7 @@ func (e *UCCEVC) WritePowerLimits(entity spineapi.EntityRemoteInterface, data [] desc, err := evTimeSeries.GetDescriptionForType(model.TimeSeriesTypeTypeConstraints) if err != nil { - return features.ErrDataNotAvailable + return eebusapi.ErrDataNotAvailable } timeSeriesSlots := []model.TimeSeriesSlotType{} diff --git a/uccevc/public_scen3.go b/uccevc/public_scen3.go index b0f1b66..9e90da9 100644 --- a/uccevc/public_scen3.go +++ b/uccevc/public_scen3.go @@ -6,7 +6,7 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" eebusutil "github.com/enbility/eebus-go/util" "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" @@ -23,7 +23,7 @@ func (e *UCCEVC) IncentiveConstraints(entity spineapi.EntityRemoteInterface) (ap evIncentiveTable, err := util.IncentiveTable(e.service, entity) if err != nil { - return result, features.ErrDataNotAvailable + return result, eebusapi.ErrDataNotAvailable } constraints, err := evIncentiveTable.GetConstraints() @@ -192,7 +192,7 @@ func (e *UCCEVC) WriteIncentives(entity spineapi.EntityRemoteInterface, data []a evIncentiveTable, err := util.IncentiveTable(e.service, entity) if err != nil { - return features.ErrDataNotAvailable + return eebusapi.ErrDataNotAvailable } if len(data) == 0 { diff --git a/uccevc/public_scen4.go b/uccevc/public_scen4.go index 4bd4964..9b69968 100644 --- a/uccevc/public_scen4.go +++ b/uccevc/public_scen4.go @@ -5,7 +5,7 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -19,17 +19,17 @@ func (e *UCCEVC) ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([ evTimeSeries, err := util.TimeSeries(e.service, entity) if err != nil { - return constraints, features.ErrDataNotAvailable + return constraints, eebusapi.ErrDataNotAvailable } data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypeConstraints) if err != nil { - return constraints, features.ErrDataNotAvailable + return constraints, eebusapi.ErrDataNotAvailable } // we need at least a time series slot if data.TimeSeriesSlot == nil { - return constraints, features.ErrDataNotAvailable + return constraints, eebusapi.ErrDataNotAvailable } // get the values for all slots @@ -74,17 +74,17 @@ func (e *UCCEVC) ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePl evTimeSeries, err := util.TimeSeries(e.service, entity) if err != nil { - return plan, features.ErrDataNotAvailable + return plan, eebusapi.ErrDataNotAvailable } data, err := evTimeSeries.GetValueForType(model.TimeSeriesTypeTypePlan) if err != nil { - return plan, features.ErrDataNotAvailable + return plan, eebusapi.ErrDataNotAvailable } // we need at least a time series slot if data.TimeSeriesSlot == nil { - return plan, features.ErrDataNotAvailable + return plan, eebusapi.ErrDataNotAvailable } startAvailable := false diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index 045a001..10ae42d 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -3,15 +3,14 @@ package uccevc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCCEVC struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCCEVC struct { var _ UCCEVCInterface = (*UCCEVC)(nil) -func NewUCCEVC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCCEVC { +func NewUCCEVC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCCEVC { uc := &UCCEVC{ service: service, eventCB: eventCB, @@ -94,11 +93,11 @@ func (e *UCCEVC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check for required features evTimeSeries, err := util.TimeSeries(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } evIncentiveTable, err := util.IncentiveTable(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if timeseries descriptions contains constraints data diff --git a/ucevcc/public.go b/ucevcc/public.go index 33f6636..5ae6d5e 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -3,7 +3,7 @@ package ucevcc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -26,7 +26,7 @@ func (e *UCEVCC) ChargeState(entity spineapi.EntityRemoteInterface) (api.EVCharg operatingState := diagnosisState.OperatingState if operatingState == nil { - return api.EVChargeStateTypeUnknown, features.ErrDataNotAvailable + return api.EVChargeStateTypeUnknown, eebusapi.ErrDataNotAvailable } switch *operatingState { @@ -76,7 +76,7 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity) if err != nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } // check if device configuration descriptions has an communication standard key name @@ -91,7 +91,7 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( } if data == nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } return data, nil @@ -120,12 +120,12 @@ func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (s data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString) if err != nil || data == nil { - return unknown, features.ErrDataNotAvailable + return unknown, eebusapi.ErrDataNotAvailable } value, ok := data.(*model.DeviceConfigurationKeyValueStringType) if !ok || value == nil { - return unknown, features.ErrDataNotAvailable + return unknown, eebusapi.ErrDataNotAvailable } return string(*value), nil @@ -142,12 +142,12 @@ func (e *UCEVCC) AsymmetricChargingSupport(entity spineapi.EntityRemoteInterface data, err := e.deviceConfigurationValueForKeyName(entity, model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeBoolean) if err != nil || data == nil { - return false, features.ErrDataNotAvailable + return false, eebusapi.ErrDataNotAvailable } value, ok := data.(*bool) if !ok || value == nil { - return false, features.ErrDataNotAvailable + return false, eebusapi.ErrDataNotAvailable } return bool(*value), nil @@ -165,7 +165,7 @@ func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.I evIdentification, err := util.Identification(e.service, entity) if err != nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } identifications, err := evIdentification.GetValues() @@ -211,7 +211,7 @@ func (e *UCEVCC) ManufacturerData( evDeviceClassification, err := util.DeviceClassification(e.service, entity) if err != nil { - return deviceName, serialNumber, features.ErrDataNotAvailable + return deviceName, serialNumber, eebusapi.ErrDataNotAvailable } data, err := evDeviceClassification.GetManufacturerDetails() @@ -242,7 +242,7 @@ func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64 evElectricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return nil, nil, nil, features.ErrDataNotAvailable + return nil, nil, nil, eebusapi.ErrDataNotAvailable } var resultMin, resultMax, resultDefault []float64 @@ -269,7 +269,7 @@ func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64 } if len(resultMin) == 0 { - return nil, nil, nil, features.ErrDataNotAvailable + return nil, nil, nil, eebusapi.ErrDataNotAvailable } return resultMin, resultMax, resultDefault, nil diff --git a/ucevcem/public.go b/ucevcem/public.go index 329a38d..ed273c3 100644 --- a/ucevcem/public.go +++ b/ucevcem/public.go @@ -5,7 +5,7 @@ import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -18,12 +18,12 @@ func (e *UCEVCEM) PhasesConnected(entity spineapi.EntityRemoteInterface) (uint, evElectricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } data, err := evElectricalConnection.GetDescriptions() if err != nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } for _, item := range data { @@ -181,7 +181,7 @@ func (e *UCEVCEM) EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, // we assume there is only one result value := data[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), err diff --git a/ucevsecc/events.go b/ucevsecc/events.go index 02cbfae..d005583 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -2,7 +2,6 @@ package ucevsecc import ( "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -43,14 +42,11 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { // an EVSE was connected func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterface) { - localDevice := e.service.LocalDevice() - localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) - - if evseDeviceClassification, err := features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { + if evseDeviceClassification, err := util.DeviceClassification(e.service, entity); err == nil { _, _ = evseDeviceClassification.RequestManufacturerDetails() } - if evseDeviceDiagnosis, err := features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, entity); err == nil { + if evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity); err == nil { _, _ = evseDeviceDiagnosis.RequestState() } diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 90c6233..80a9608 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -3,14 +3,14 @@ package ucevsecc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCEVSECC struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -19,7 +19,7 @@ type UCEVSECC struct { var _ UCEVSECCInterface = (*UCEVSECC)(nil) -func NewUCEVSECC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSECC { +func NewUCEVSECC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSECC { uc := &UCEVSECC{ service: service, eventCB: eventCB, diff --git a/ucevsoc/public.go b/ucevsoc/public.go index 9334255..2b5acb8 100644 --- a/ucevsoc/public.go +++ b/ucevsoc/public.go @@ -3,7 +3,7 @@ package ucevsoc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -34,7 +34,7 @@ func (e *UCEVSOC) StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, // we assume there is only one value, nil is already checked value := data[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index cd72d2b..25bd1a3 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -3,15 +3,14 @@ package ucevsoc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCEVSOC struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCEVSOC struct { var _ UCEVSOCInterface = (*UCEVSOC)(nil) -func NewUCEVSOC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSOC { +func NewUCEVSOC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSOC { uc := &UCEVSOC{ service: service, eventCB: eventCB, @@ -89,7 +88,7 @@ func (e *UCEVSOC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (boo // check for required features evMeasurement, err := util.Measurement(e.service, entity) if err != nil || evMeasurement == nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if measurement description contains an element with scope SOC diff --git a/ucmgcp/public.go b/ucmgcp/public.go index e81cd87..de6de52 100644 --- a/ucmgcp/public.go +++ b/ucmgcp/public.go @@ -3,7 +3,7 @@ package ucmgcp import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -44,12 +44,12 @@ func (e *UCMGCP) PowerLimitationFactor(entity spineapi.EntityRemoteInterface) (f } if data == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } value, ok := data.(*model.ScaledNumberType) if !ok || value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -79,7 +79,7 @@ func (e *UCMGCP) Power(entity spineapi.EntityRemoteInterface) (float64, error) { return 0, err } if len(values) != 1 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return values[0], nil @@ -103,13 +103,13 @@ func (e *UCMGCP) EnergyFeedIn(entity spineapi.EntityRemoteInterface) (float64, e return 0, err } if len(values) == 0 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // we assume thre is only one result value := values[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -133,13 +133,13 @@ func (e *UCMGCP) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, return 0, err } if len(values) == 0 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // we assume thre is only one result value := values[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -202,13 +202,13 @@ func (e *UCMGCP) Frequency(entity spineapi.EntityRemoteInterface) (float64, erro return 0, err } if len(values) == 0 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // take the first item value := values[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index 02f9d4b..08cb737 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -3,15 +3,14 @@ package ucmgcp import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCMGCP struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCMGCP struct { var _ UCMGCPInterface = (*UCMGCP)(nil) -func NewUCMGCP(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMGCP { +func NewUCMGCP(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMGCP { uc := &UCMGCP{ service: service, eventCB: eventCB, @@ -93,20 +92,20 @@ func (e *UCMGCP) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check if measurement description contain data for the required scope measurement, err := util.Measurement(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } _, err1 := measurement.GetDescriptionsForScope(model.ScopeTypeTypeACPower) _, err2 := measurement.GetDescriptionsForScope(model.ScopeTypeTypeGridFeedIn) _, err3 := measurement.GetDescriptionsForScope(model.ScopeTypeTypeGridConsumption) if err1 != nil || err2 != nil || err3 != nil { - return false, features.ErrDataNotAvailable + return false, eebusapi.ErrDataNotAvailable } // check if electrical connection descriptions is provided electricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } if _, err = electricalConnection.GetDescriptions(); err != nil { diff --git a/ucmpc/public.go b/ucmpc/public.go index 29e498c..5f96ed3 100644 --- a/ucmpc/public.go +++ b/ucmpc/public.go @@ -3,7 +3,7 @@ package ucmpc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -33,7 +33,7 @@ func (e *UCMPC) Power(entity spineapi.EntityRemoteInterface) (float64, error) { return 0, err } if len(values) != 1 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return values[0], nil } @@ -77,13 +77,13 @@ func (e *UCMPC) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, return 0, err } if len(values) == 0 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // we assume thre is only one result value := values[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -105,13 +105,13 @@ func (e *UCMPC) EnergyProduced(entity spineapi.EntityRemoteInterface) (float64, return 0, err } if len(values) == 0 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // we assume thre is only one result value := values[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -175,14 +175,14 @@ func (e *UCMPC) Frequency(entity spineapi.EntityRemoteInterface) (float64, error return 0, err } if len(values) == 0 { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // take the first item value := values[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index b0e4abe..00fb285 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -3,15 +3,14 @@ package ucmpc import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCMPC struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCMPC struct { var _ UCMCPInterface = (*UCMPC)(nil) -func NewUCMPC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMPC { +func NewUCMPC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMPC { uc := &UCMPC{ service: service, eventCB: eventCB, @@ -98,17 +97,17 @@ func (e *UCMPC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, // check if measurement description contain data for the required scope measurement, err := util.Measurement(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } if _, err := measurement.GetDescriptionsForScope(model.ScopeTypeTypeACPowerTotal); err != nil { - return false, features.ErrDataNotAvailable + return false, eebusapi.ErrDataNotAvailable } // check if electrical connection descriptions is provided electricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } if _, err = electricalConnection.GetDescriptions(); err != nil { diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index cbb9b14..16e1587 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -3,15 +3,14 @@ package ucopev import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCOPEV struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCOPEV struct { var _ UCOPEVInterface = (*UCOPEV)(nil) -func NewUCOPEV(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOPEV { +func NewUCOPEV(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOPEV { uc := &UCOPEV{ service: service, eventCB: eventCB, @@ -94,7 +93,7 @@ func (e *UCOPEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check for required features evLoadControl, err := util.LoadControl(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if loadcontrol limit descriptions contains a recommendation category diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index e0c7cac..24d00fa 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -3,15 +3,14 @@ package ucoscev import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCOSCEV struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCOSCEV struct { var _ UCOSCEVInterface = (*UCOSCEV)(nil) -func NewUCOSCEV(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOSCEV { +func NewUCOSCEV(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOSCEV { uc := &UCOSCEV{ service: service, eventCB: eventCB, @@ -100,7 +99,7 @@ func (e *UCOSCEV) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (boo // check if loadcontrol limit descriptions contains a recommendation category evLoadControl, err := util.LoadControl(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } if _, err = evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation); err != nil { diff --git a/ucvabd/public.go b/ucvabd/public.go index 03a8bc5..48672f8 100644 --- a/ucvabd/public.go +++ b/ucvabd/public.go @@ -3,7 +3,7 @@ package ucvabd import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -34,7 +34,7 @@ func (e *UCVABD) Power(entity spineapi.EntityRemoteInterface) (float64, error) { mId := data[0].MeasurementId value := data[0].Value if mId == nil || value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -61,7 +61,7 @@ func (e *UCVABD) EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, // we assume thre is only one result value := data[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -88,7 +88,7 @@ func (e *UCVABD) EnergyDischarged(entity spineapi.EntityRemoteInterface) (float6 // we assume thre is only one result value := data[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -115,7 +115,7 @@ func (e *UCVABD) StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, // we assume thre is only one result value := data[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -134,7 +134,7 @@ func (e *UCVABD) getValuesForTypeCommodityScope( measurementF, err := util.Measurement(e.service, entity) if err != nil { - return nil, features.ErrFunctionNotSupported + return nil, eebusapi.ErrFunctionNotSupported } return measurementF.GetValuesForTypeCommodityScope(measurement, commodity, scope) diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index aa27331..d24cc07 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -3,15 +3,14 @@ package ucvabd import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCVABD struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCVABD struct { var _ UCVABDInterface = (*UCVABD)(nil) -func NewUCVABD(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVABD { +func NewUCVABD(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVABD { uc := &UCVABD{ service: service, eventCB: eventCB, @@ -93,7 +92,7 @@ func (e *UCVABD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check for required features electricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if electrical connection descriptions and parameter descriptions are available name @@ -107,7 +106,7 @@ func (e *UCVABD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check for required features measurement, err := util.Measurement(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if measurement descriptions contains a required scope diff --git a/ucvapd/public.go b/ucvapd/public.go index d28c2e8..0ae8d38 100644 --- a/ucvapd/public.go +++ b/ucvapd/public.go @@ -3,7 +3,7 @@ package ucvapd import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -31,7 +31,7 @@ func (e *UCVAPD) Power(entity spineapi.EntityRemoteInterface) (float64, error) { mId := data[0].MeasurementId value := data[0].Value if mId == nil || value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -49,7 +49,7 @@ func (e *UCVAPD) PowerNominalPeak(entity spineapi.EntityRemoteInterface) (float6 deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) if err != nil { - return 0, features.ErrFunctionNotSupported + return 0, eebusapi.ErrFunctionNotSupported } keyName := model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem @@ -63,12 +63,12 @@ func (e *UCVAPD) PowerNominalPeak(entity spineapi.EntityRemoteInterface) (float6 } if data == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } value, ok := data.(*model.ScaledNumberType) if !ok || value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -95,7 +95,7 @@ func (e *UCVAPD) PVYieldTotal(entity spineapi.EntityRemoteInterface) (float64, e // we assume thre is only one result value := data[0].Value if value == nil { - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } return value.GetValue(), nil @@ -114,7 +114,7 @@ func (e *UCVAPD) getValuesForTypeCommodityScope( measurementF, err := util.Measurement(e.service, entity) if err != nil { - return nil, features.ErrFunctionNotSupported + return nil, eebusapi.ErrFunctionNotSupported } return measurementF.GetValuesForTypeCommodityScope(measurement, commodity, scope) diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 6db5ba7..29d5ec9 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -3,15 +3,14 @@ package ucvapd import ( "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" - serviceapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" + eebusapi "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) type UCVAPD struct { - service serviceapi.ServiceInterface + service eebusapi.ServiceInterface eventCB api.EventHandlerCB @@ -20,7 +19,7 @@ type UCVAPD struct { var _ UCVAPDInterface = (*UCVAPD)(nil) -func NewUCVAPD(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVAPD { +func NewUCVAPD(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVAPD { uc := &UCVAPD{ service: service, eventCB: eventCB, @@ -94,7 +93,7 @@ func (e *UCVAPD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check for required features deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if device configuration descriptions contains a required key name @@ -104,7 +103,7 @@ func (e *UCVAPD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool electricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if electrical connection descriptions and parameter descriptions are available name @@ -118,7 +117,7 @@ func (e *UCVAPD) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool // check for required features measurement, err := util.Measurement(e.service, entity) if err != nil { - return false, features.ErrFunctionNotSupported + return false, eebusapi.ErrFunctionNotSupported } // check if measurement descriptions contains a required scope diff --git a/util/features.go b/util/features.go index 8d38f68..766443b 100644 --- a/util/features.go +++ b/util/features.go @@ -18,59 +18,59 @@ func localCemEntity(service eebusapi.ServiceInterface) api.EntityLocalInterface func DeviceClassification(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceClassification, error) { localEntity := localCemEntity(service) - return features.NewDeviceClassification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewDeviceClassification(localEntity, remoteEntity) } func DeviceConfiguration(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceConfiguration, error) { localEntity := localCemEntity(service) - return features.NewDeviceConfiguration(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewDeviceConfiguration(localEntity, remoteEntity) } func DeviceDiagnosis(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceDiagnosis, error) { localEntity := localCemEntity(service) - return features.NewDeviceDiagnosis(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewDeviceDiagnosis(localEntity, remoteEntity) } func DeviceDiagnosisServer(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.DeviceDiagnosis, error) { localEntity := localCemEntity(service) - return features.NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, localEntity, remoteEntity) + return features.NewDeviceDiagnosis(localEntity, remoteEntity) } func ElectricalConnection(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.ElectricalConnection, error) { localEntity := localCemEntity(service) - return features.NewElectricalConnection(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewElectricalConnection(localEntity, remoteEntity) } func Identification(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.Identification, error) { localEntity := localCemEntity(service) - return features.NewIdentification(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewIdentification(localEntity, remoteEntity) } func Measurement(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.Measurement, error) { localEntity := localCemEntity(service) - return features.NewMeasurement(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewMeasurement(localEntity, remoteEntity) } func LoadControl(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.LoadControl, error) { localEntity := localCemEntity(service) - return features.NewLoadControl(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewLoadControl(localEntity, remoteEntity) } func TimeSeries(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.TimeSeries, error) { localEntity := localCemEntity(service) - return features.NewTimeSeries(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewTimeSeries(localEntity, remoteEntity) } func IncentiveTable(service eebusapi.ServiceInterface, remoteEntity api.EntityRemoteInterface) (*features.IncentiveTable, error) { localEntity := localCemEntity(service) - return features.NewIncentiveTable(model.RoleTypeClient, model.RoleTypeServer, localEntity, remoteEntity) + return features.NewIncentiveTable(localEntity, remoteEntity) } diff --git a/util/loadcontrol.go b/util/loadcontrol.go index 8c7bafa..bcc3498 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -3,7 +3,6 @@ package util import ( "github.com/enbility/cemd/api" eebusapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" eebusutil "github.com/enbility/eebus-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" @@ -33,7 +32,7 @@ func LoadControlLimits( // limitDescription contains the measurementId for each limitId limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) if err != nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } var result []float64 @@ -61,12 +60,12 @@ func LoadControlLimits( } if limitDesc == nil || limitDesc.LimitId == nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) if err != nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } var limitValue float64 @@ -74,7 +73,7 @@ func LoadControlLimits( // report maximum possible if no limit is available or the limit is not active _, dataMax, _, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) if err != nil { - return nil, features.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } limitValue = dataMax diff --git a/util/measurement.go b/util/measurement.go index f6c347e..f87f82b 100644 --- a/util/measurement.go +++ b/util/measurement.go @@ -4,7 +4,6 @@ import ( "slices" eebusapi "github.com/enbility/eebus-go/api" - "github.com/enbility/eebus-go/features" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -16,7 +15,7 @@ func MeasurementValueForScope( scope model.ScopeTypeType) (float64, error) { measurementF, err := Measurement(service, entity) if err != nil { - return 0, features.ErrFunctionNotSupported + return 0, eebusapi.ErrFunctionNotSupported } if data, err := measurementF.GetDescriptionsForScope(scope); err == nil { @@ -31,7 +30,7 @@ func MeasurementValueForScope( } } - return 0, features.ErrDataNotAvailable + return 0, eebusapi.ErrDataNotAvailable } // return the phase specific voltage details From db1d22482d262b4dad7c4ceb5d4aa967dd33534d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 27 Feb 2024 19:15:45 +0100 Subject: [PATCH 113/227] Update README --- README.md | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f3fa16c..6925d56 100644 --- a/README.md +++ b/README.md @@ -12,25 +12,23 @@ The goal is to provide an EEBUS CEM implementation This library provides a foundation to implement energy management solutions using the [eebus-go](https://github.com/enbility/eebus-go) library. It is designed to be included either directly into go projects, or it will be able to run as a daemon for other systems interact with (to be implemented). -These EEBUS use cases are already supported: - -- E-Mobility: - - - EVSE Commissioning and Configuration V1.0.1 - - EV Commissioning and Configuration V1.0.1 - - EV Charging Electricity Measurement V1.0.1 - - EV State Of Charge V1.0.0 RC1 - - Optimization of Self Consumption During EV Charging V1.0.1b - - Overload Protection by EV Charging Current Curtailment V1.0.1b - -These use cases are currently planned to be supported in the future: - -- E-Mobility: - - - Coordinated EV Charging V1.0.1 - - EV Charging Summary V1.0.1 - -More use cases and scenarios will hopefully follow in the future as well. +## Packages + +- `api`: API interface definitions +- `cem`: Central CEM implementation which needs to be used by a HEMS implementation +- `cmd`: Example project +- `uccevc`: Use Case Coordinated EV Charging V1.0.1 +- `ucevcc`: Use Case EV Commissioning and Configuration V1.0.1 +- `ucevcem`: Use Case EV Charging Electricity Measurement V1.0.1 +- `ucevsecc`: Use Case EVSE Commissioning and Configuration V1.0.1 +- `ucevsoc`: Use Case EV State Of Charge V1.0.0 RC1 +- `ucmgcp`: Use Case Monitoring of Grid Connection Point V1.0.0 +- `ucmpc`: Use Case Monitoring of Power Consumption V1.0.0 +- `ucopev`: Use Case Overload Protection by EV Charging Current Curtailment V1.0.1b +- `ucoscev`: Use Case Optimization of Self Consumption During EV Charging V1.0.1b +- `ucvabd`: Use Case Visualization of Aggregated Battery Data V1.0.0 RC1 +- `ucvapd`: Use Case Visualization of Aggregated Photovoltaic Data V1.0.0 RC1 +- `util`: various internal helpers ## Usage From 14f6a7f827fdc2c33e32f044b0fe2eb791a37eb8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 28 Feb 2024 13:38:25 +0100 Subject: [PATCH 114/227] Update SHIP, SPINE, EEBUS --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index c3ba5e9..7fe34ce 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240227180445-1494dc3a8a7c - github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae - github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 + github.com/enbility/eebus-go v0.0.0-20240228123408-4437d35c805b + github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 + github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index a9c31a9..9e577e9 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240227180445-1494dc3a8a7c h1:cjj1dPM6PABDAGm3kWtAlFIhD/4omb/mxoEY5byjuSA= -github.com/enbility/eebus-go v0.0.0-20240227180445-1494dc3a8a7c/go.mod h1:vV6SJeQ6ScyQbNMCxerWG2NGOIVkGpskq3iU+acvaak= -github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae h1:KOowUYPF7qmNvKLt81SPOslrOKyph2LEV8EyZ8xjxd8= -github.com/enbility/ship-go v0.0.0-20240227162634-e4ac25eae5ae/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736 h1:/oXzUGRXtOlQRflZD6nMd3aFOvl2n78u6G1v0IzGz8U= -github.com/enbility/spine-go v0.0.0-20240226123143-abcf863b0736/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/eebus-go v0.0.0-20240228123408-4437d35c805b h1:qLTliujJ4yEJDwQQeZ5NuoD/vMpq+6yh4rYtV3YmEjI= +github.com/enbility/eebus-go v0.0.0-20240228123408-4437d35c805b/go.mod h1:JBSmPBFGRT4yE6/tmY3TGNGV01LsOM0kyyg1l1vTbnE= +github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 h1:+TxssP/V5+k3HBJl+huN/oZQ3D7R3TUU4S1GP+jkUuM= +github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 h1:Mz+/gSmp8QkZqR6hOyYh1zRUAL8vAT63bxSbYG4m+YM= +github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From e1f6b749e95f3c2f29ebcb26737b032665290806 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 28 Feb 2024 21:28:04 +0100 Subject: [PATCH 115/227] Write loadlimits should send all limits --- util/loadcontrol.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/util/loadcontrol.go b/util/loadcontrol.go index bcc3498..a648348 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -123,8 +123,8 @@ func WriteLoadControlLimits( return nil, api.ErrNoCompatibleEntity } - evLoadControl, err := LoadControl(service, entity) - evElectricalConnection, err2 := ElectricalConnection(service, entity) + loadControl, err := LoadControl(service, entity) + electricalConnection, err2 := ElectricalConnection(service, entity) if err != nil || err2 != nil { return nil, api.ErrNoCompatibleEntity } @@ -134,13 +134,13 @@ func WriteLoadControlLimits( for _, phaseLimit := range limits { // find out the appropriate limitId for each phase value // limitDescription contains the measurementId for each limitId - limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) + limitDescriptions, err := loadControl.GetLimitDescriptionsForCategory(category) if err != nil { continue } // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) + elParamDesc, err := electricalConnection.GetParameterDescriptionForMeasuredPhase(phaseLimit.Phase) if err != nil || elParamDesc.MeasurementId == nil { continue } @@ -160,7 +160,7 @@ func WriteLoadControlLimits( continue } - limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) + limitIdData, err := loadControl.GetLimitValueForLimitId(*limitDesc.LimitId) if err != nil { continue } @@ -172,7 +172,7 @@ func WriteLoadControlLimits( } // electricalPermittedValueSet contains the allowed min, max and the default values per phase - limit := evElectricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) + limit := electricalConnection.AdjustValueToBeWithinPermittedValuesForParameter(phaseLimit.Value, *elParamDesc.ParameterId) newLimit := model.LoadControlLimitDataType{ LimitId: limitDesc.LimitId, @@ -182,7 +182,26 @@ func WriteLoadControlLimits( limitData = append(limitData, newLimit) } - msgCounter, err := evLoadControl.WriteLimitValues(limitData) + currentLimits, err := loadControl.GetLimitValues() + if err != nil { + return nil, eebusapi.ErrDataNotAvailable + } + + for index, limit := range currentLimits { + if limit.LimitId == nil { + continue + } + + for _, newLimit := range limitData { + if newLimit.LimitId != limit.LimitId { + continue + } + + currentLimits[index] = newLimit + } + } + + msgCounter, err := loadControl.WriteLimitValues(currentLimits) return msgCounter, err } From d7f9134b790696097d5f592a8a635c9cd14db21f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 3 Mar 2024 19:39:18 +0100 Subject: [PATCH 116/227] Update api and event documentation --- uccevc/api.go | 35 +++++++++++++++++++++++++++++++++++ uccevc/types.go | 28 ++++++++++++++++++++++++++++ ucevcc/api.go | 24 ++++++++++++++++++++++++ ucevcc/types.go | 36 +++++++++++++++++++++++++++++++++++- ucevcem/api.go | 12 ++++++++++++ ucevcem/types.go | 16 ++++++++++++++++ ucevsecc/api.go | 8 ++++++++ ucevsecc/types.go | 20 ++++++++++++++++++++ ucevsoc/api.go | 3 +++ ucevsoc/types.go | 4 ++++ ucmgcp/api.go | 23 +++++++++++++++++++++++ ucmgcp/types.go | 30 +++++++++++++++++++++++++++++- ucmpc/api.go | 21 +++++++++++++++++++++ ucmpc/types.go | 28 ++++++++++++++++++++++++++++ ucopev/api.go | 4 ++++ ucopev/types.go | 4 ++++ ucoscev/api.go | 4 ++++ ucoscev/types.go | 4 ++++ ucvabd/api.go | 12 ++++++++++++ ucvabd/types.go | 16 ++++++++++++++++ ucvapd/api.go | 9 +++++++++ ucvapd/types.go | 12 ++++++++++++ 22 files changed, 351 insertions(+), 2 deletions(-) diff --git a/uccevc/api.go b/uccevc/api.go index cffa9f1..7c8f665 100644 --- a/uccevc/api.go +++ b/uccevc/api.go @@ -15,12 +15,20 @@ type UCCEVCInterface interface { // returns the current charging stratey // + // parameters: + // - entity: the entity of the EV + // // returns EVChargeStrategyTypeUnknown if it could not be determined, e.g. // if the vehicle communication is via IEC61851 or the EV doesn't provide // any information about its charging mode or plan ChargeStrategy(remoteEntity spineapi.EntityRemoteInterface) api.EVChargeStrategyType // returns the current energy demand + // + // parameters: + // - entity: the entity of the EV + // + // return values: // - EVDemand: details about the actual demands from the EV // - error: if no data is available // @@ -32,23 +40,50 @@ type UCCEVCInterface interface { TimeSlotConstraints(entity spineapi.EntityRemoteInterface) (api.TimeSlotConstraints, error) // send power limits to the EV + // + // parameters: + // - entity: the entity of the EV + // - data: the power limits + // // if no data is provided, default power limits with the max possible value for 7 days will be sent WritePowerLimits(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error // Scenario 3 + // return the current incentive constraints + // + // parameters: + // - entity: the entity of the EV IncentiveConstraints(entity spineapi.EntityRemoteInterface) (api.IncentiveSlotConstraints, error) + // send new incentives to the EV + // + // parameters: + // - entity: the entity of the EV + // - data: the incentive descriptions WriteIncentiveTableDescriptions(entity spineapi.EntityRemoteInterface, data []api.IncentiveTariffDescription) error // send incentives to the EV + // + // parameters: + // - entity: the entity of the EV + // - data: the incentives + // // if no data is provided, default incentives with the same price for 7 days will be sent WriteIncentives(entity spineapi.EntityRemoteInterface, data []api.DurationSlotValue) error // Scenario 4 + // return the current charge plan constraints + // + // parameters: + // - entity: the entity of the EV ChargePlanConstraints(entity spineapi.EntityRemoteInterface) ([]api.DurationSlotValue, error) + // return the current charge plan of the EV + // + // parameters: + // - entity: the entity of the EV ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePlan, error) // Scenario 5 & 6 diff --git a/uccevc/types.go b/uccevc/types.go index 11ca359..1ed5e24 100644 --- a/uccevc/types.go +++ b/uccevc/types.go @@ -6,31 +6,59 @@ const ( // Scenario 1 // EV provided an energy demand + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateEnergyDemand api.EventType = "DataUpdateEnergyDemand" // Scenario 2 // EV provided a charge plan constraints + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateTimeSlotConstraints api.EventType = "DataUpdateTimeSlotConstraints" // Scenario 3 // EV incentive table data updated + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateIncentiveTable api.EventType = "DataUpdateIncentiveTable" // EV requested an incentive table, call to WriteIncentiveTableDescriptions required + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataRequestedIncentiveTableDescription api.EventType = "DataRequestedIncentiveTableDescription" // Scenario 2 & 3 // EV requested power limits, call to WritePowerLimits and WriteIncentives required + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataRequestedPowerLimitsAndIncentives api.EventType = "DataRequestedPowerLimitsAndIncentives" // Scenario 4 // EV provided a charge plan + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateChargePlanConstraints api.EventType = "DataUpdateChargePlanConstraints" // EV provided a charge plan + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateChargePlan api.EventType = "DataUpdateChargePlan" ) diff --git a/ucevcc/api.go b/ucevcc/api.go index 088f2d2..03b6954 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -12,42 +12,66 @@ type UCEVCCInterface interface { api.UseCaseInterface // return the current charge state of the EV + // + // parameters: + // - entity: the entity of the EV ChargeState(entity spineapi.EntityRemoteInterface) (api.EVChargeStateType, error) // Scenario 1 & 8 // return if the EV is connected + // + // parameters: + // - entity: the entity of the EV EVConnected(entity spineapi.EntityRemoteInterface) bool // Scenario 2 // return the current communication standard type used to communicate between EVSE and EV + // + // parameters: + // - entity: the entity of the EV CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) // Scenario 3 // return if the EV supports asymmetric charging + // + // parameters: + // - entity: the entity of the EV AsymmetricChargingSupport(entity spineapi.EntityRemoteInterface) (bool, error) // Scenario 4 // return the identifications of the currently connected EV or nil if not available // these can be multiple, e.g. PCID, Mac Address, RFID + // + // parameters: + // - entity: the entity of the EV Identifications(entity spineapi.EntityRemoteInterface) ([]api.IdentificationItem, error) // Scenario 5 // the manufacturer data of an EVSE // returns deviceName, serialNumber, error + // + // parameters: + // - entity: the entity of the EV ManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) // Scenario 6 // return the min, max, default limits for each phase of the connected EV + // + // parameters: + // - entity: the entity of the EV CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) // Scenario 7 // is the EV in sleep mode + // + // parameters: + // - entity: the entity of the EV IsInSleepMode(entity spineapi.EntityRemoteInterface) (bool, error) } diff --git a/ucevcc/types.go b/ucevcc/types.go index bd9f1a9..36b524c 100644 --- a/ucevcc/types.go +++ b/ucevcc/types.go @@ -6,47 +6,81 @@ const ( // An EV was connected // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 1 EvConnected api.EventType = "EvConnected" // An EV was disconnected // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 8 EvDisconnected api.EventType = "EvDisconnected" // EV charge state data was updated + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateChargeState api.EventType = "DataUpdateChargeState" // EV communication standard data was updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 2 // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateCommunicationStandard api.EventType = "DataUpdateCommunicationStandard" // EV asymmetric charging data was updated // - // Use Case EVCC, Scenario 3 + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV // // Note: the referred data may be updated together with all other configuration items of this use case AsymmetricChargingSupportDataUpdate api.EventType = "AsymmetricChargingSupportDataUpdate" // EV identificationdata was updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 4 DataUpdateIdentifications api.EventType = "DataUpdateIdentifications" // EV manufacturer data was updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 5 DataUpdateManufacturerData api.EventType = "DataUpdateManufacturerData" // EV charging power limits // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 6 DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" // EV permitted power limits updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCC, Scenario 7 DataUpdateIsInSleepMode api.EventType = "DataUpdateIsInSleepMode" ) diff --git a/ucevcem/api.go b/ucevcem/api.go index d987cc9..988412e 100644 --- a/ucevcem/api.go +++ b/ucevcem/api.go @@ -12,20 +12,32 @@ type UCEVCEMInterface interface { api.UseCaseInterface // return the number of ac connected phases of the EV or 0 if it is unknown + // + // parameters: + // - entity: the entity of the EV PhasesConnected(entity spineapi.EntityRemoteInterface) (uint, error) // Scenario 1 // return the last current measurement for each phase of the connected EV + // + // parameters: + // - entity: the entity of the EV CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 2 // return the last power measurement for each phase of the connected EV + // + // parameters: + // - entity: the entity of the EV PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 3 // return the charged energy measurement in Wh of the connected EV + // + // parameters: + // - entity: the entity of the EV EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucevcem/types.go b/ucevcem/types.go index 586b465..c99be79 100644 --- a/ucevcem/types.go +++ b/ucevcem/types.go @@ -5,6 +5,10 @@ import "github.com/enbility/cemd/api" const ( // EV number of connected phases data updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCEM, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -12,6 +16,10 @@ const ( // EV current measurement data updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCEM, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -19,6 +27,10 @@ const ( // EV power measurement data updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCEM, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -26,6 +38,10 @@ const ( // EV charging energy measurement data updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVCEM, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case diff --git a/ucevsecc/api.go b/ucevsecc/api.go index 70eb3ac..9a37cc4 100644 --- a/ucevsecc/api.go +++ b/ucevsecc/api.go @@ -13,10 +13,18 @@ type UCEVSECCInterface interface { api.UseCaseInterface // the manufacturer data of an EVSE + // + // parameters: + // - entity: the entity of the EV + // // returns deviceName, serialNumber, error ManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) // the operating state data of an EVSE + // + // parameters: + // - entity: the entity of the EV + // // returns operatingState, lastErrorCode, error OperatingState(entity spineapi.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) } diff --git a/ucevsecc/types.go b/ucevsecc/types.go index fe7cb91..33e76ac 100644 --- a/ucevsecc/types.go +++ b/ucevsecc/types.go @@ -4,18 +4,38 @@ import "github.com/enbility/cemd/api" const ( // An EVSE was connected + // + // The callback with this message provides: + // - the device of the EVSE + // - the entity of the EVSE EvseConnected api.EventType = "EvseConnected" // An EVSE was disconnected + // + // The callback with this message provides: + // - the device of the EVSE + // - the entity of the EVSE EvseDisconnected api.EventType = "EvseDisconnected" // EVSE manufacturer data was updated // + // The callback with this message provides: + // - the device of the EVSE + // - the entity of the EVSE + // // Use Case EVSECC, Scenario 1 + // + // The entity of the message is the entity of the EVSE DataUpdateManufacturerData api.EventType = "DataUpdateManufacturerData" // EVSE operation state was updated // + // The callback with this message provides: + // - the device of the EVSE + // - the entity of the EVSE + // // Use Case EVSECC, Scenario 2 + // + // The entity of the message is the entity of the EVSE DataUpdateOperatingState api.EventType = "DataUpdateOperatingState" ) diff --git a/ucevsoc/api.go b/ucevsoc/api.go index 745a786..feb71bd 100644 --- a/ucevsoc/api.go +++ b/ucevsoc/api.go @@ -14,6 +14,9 @@ type UCEVSOCInterface interface { // Scenario 1 // return the EVscurrent state of charge of the EV or an error it is unknown + // + // parameters: + // - entity: the entity of the EV StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 2 to 4 are not supported, as there is no EV supporting this as of today diff --git a/ucevsoc/types.go b/ucevsoc/types.go index c2e4d11..388b959 100644 --- a/ucevsoc/types.go +++ b/ucevsoc/types.go @@ -5,6 +5,10 @@ import "github.com/enbility/cemd/api" const ( // EV state of charge data was updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case EVSOC, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case diff --git a/ucmgcp/api.go b/ucmgcp/api.go index 60df64c..9793c8f 100644 --- a/ucmgcp/api.go +++ b/ucmgcp/api.go @@ -15,6 +15,9 @@ type UCMGCPInterface interface { // return the current power limitation factor // + // parameters: + // - entity: the entity of the device (e.g. SMGW) + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others @@ -24,6 +27,10 @@ type UCMGCPInterface interface { // return the momentary power consumption or production at the grid connection point // + // parameters: + // - entity: the entity of the device (e.g. SMGW) + // + // return values: // - positive values are used for consumption // - negative values are used for production Power(entity spineapi.EntityRemoteInterface) (float64, error) @@ -32,6 +39,10 @@ type UCMGCPInterface interface { // return the total feed in energy at the grid connection point // + // parameters: + // - entity: the entity of the device (e.g. SMGW) + // + // return values: // - negative values are used for production EnergyFeedIn(entity spineapi.EntityRemoteInterface) (float64, error) @@ -39,6 +50,10 @@ type UCMGCPInterface interface { // return the total consumption energy at the grid connection point // + // parameters: + // - entity: the entity of the device (e.g. SMGW) + // + // return values: // - positive values are used for consumption EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, error) @@ -46,6 +61,10 @@ type UCMGCPInterface interface { // return the momentary current consumption or production at the grid connection point // + // parameters: + // - entity: the entity of the device (e.g. SMGW) + // + // return values: // - positive values are used for consumption // - negative values are used for production CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) @@ -54,11 +73,15 @@ type UCMGCPInterface interface { // return the voltage phase details at the grid connection point // + // parameters: + // - entity: the entity of the device (e.g. SMGW) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 7 // return frequency at the grid connection point // + // parameters: + // - entity: the entity of the device (e.g. SMGW) Frequency(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucmgcp/types.go b/ucmgcp/types.go index e2cdd30..c6df7b7 100644 --- a/ucmgcp/types.go +++ b/ucmgcp/types.go @@ -6,6 +6,10 @@ const ( // Grid maximum allowed feed-in power as percentage value of the cumulated // nominal peak power of all electricity producting PV systems was updated // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // // Use Case MGCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -13,12 +17,20 @@ const ( // Grid momentary power consumption/production data updated // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // // Use Case MGCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePower api.EventType = "DataUpdatePower" - // MTotal grid feed in energy data updated + // Total grid feed in energy data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 3 // @@ -27,6 +39,10 @@ const ( // Total grid consumed energy data updated // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // // Use Case MGCP, Scenario 4 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -34,6 +50,10 @@ const ( // Phase specific momentary current consumption/production phase detail data updated // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // // Use Case MGCP, Scenario 5 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -41,6 +61,10 @@ const ( // Phase specific voltage at the grid connection point // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // // Use Case MGCP, Scenario 6 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -48,6 +72,10 @@ const ( // Grid frequency data updated // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // // Use Case MGCP, Scenario 7 // // Note: the referred data may be updated together with all other measurement items of this use case diff --git a/ucmpc/api.go b/ucmpc/api.go index 84493ac..87a8b4f 100644 --- a/ucmpc/api.go +++ b/ucmpc/api.go @@ -15,6 +15,9 @@ type UCMCPInterface interface { // return the momentary active power consumption or production // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others @@ -22,6 +25,9 @@ type UCMCPInterface interface { // return the momentary active phase specific power consumption or production per phase // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others @@ -31,11 +37,18 @@ type UCMCPInterface interface { // return the total consumption energy // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // // - positive values are used for consumption EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, error) // return the total feed in energy // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // return values: // - negative values are used for production EnergyProduced(entity spineapi.EntityRemoteInterface) (float64, error) @@ -43,6 +56,10 @@ type UCMCPInterface interface { // return the momentary phase specific current consumption or production // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // return values // - positive values are used for consumption // - negative values are used for production CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) @@ -51,11 +68,15 @@ type UCMCPInterface interface { // return the phase specific voltage details // + // parameters: + // - entity: the entity of the device (e.g. EVSE) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) // Scenario 5 // return frequency // + // parameters: + // - entity: the entity of the device (e.g. EVSE) Frequency(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucmpc/types.go b/ucmpc/types.go index 891f2ab..a3cfcc0 100644 --- a/ucmpc/types.go +++ b/ucmpc/types.go @@ -5,6 +5,10 @@ import "github.com/enbility/cemd/api" const ( // Total momentary active power consumption or production // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -12,6 +16,10 @@ const ( // Phase specific momentary active power consumption or production // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -19,6 +27,10 @@ const ( // Total energy consumed // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -26,6 +38,10 @@ const ( // Total energy produced // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -33,6 +49,10 @@ const ( // Phase specific momentary current consumption or production // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -40,6 +60,10 @@ const ( // Phase specific voltage // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -47,6 +71,10 @@ const ( // Power network frequency data updated // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // // Use Case MCP, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case diff --git a/ucopev/api.go b/ucopev/api.go index f98120d..04dbe43 100644 --- a/ucopev/api.go +++ b/ucopev/api.go @@ -16,6 +16,9 @@ type UCOPEVInterface interface { // return the current loadcontrol obligation limits // + // parameters: + // - entity: the entity of the EV + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others @@ -24,6 +27,7 @@ type UCOPEVInterface interface { // send new LoadControlLimits to the remote EV // // parameters: + // - entity: the entity of the EV // - limits: a set of limits containing phase specific limit data // // Sets a maximum A limit for each phase that the EV may not exceed. diff --git a/ucopev/types.go b/ucopev/types.go index 42bd961..092a1f6 100644 --- a/ucopev/types.go +++ b/ucopev/types.go @@ -4,5 +4,9 @@ import "github.com/enbility/cemd/api" const ( // EV load control obligation limit data updated + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV DataUpdateLimit api.EventType = "DataUpdateLimit" ) diff --git a/ucoscev/api.go b/ucoscev/api.go index c9c238a..c32e14a 100644 --- a/ucoscev/api.go +++ b/ucoscev/api.go @@ -16,6 +16,9 @@ type UCOSCEVInterface interface { // return the current loadcontrol obligation limits // + // parameters: + // - entity: the entity of the EV + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others @@ -24,6 +27,7 @@ type UCOSCEVInterface interface { // send new LoadControlLimits to the remote EV // // parameters: + // - entity: the entity of the EV // - limits: a set of limits containing phase specific limit data // // recommendations: diff --git a/ucoscev/types.go b/ucoscev/types.go index 40c8a79..f56df60 100644 --- a/ucoscev/types.go +++ b/ucoscev/types.go @@ -5,6 +5,10 @@ import "github.com/enbility/cemd/api" const ( // EV load control recommendation limit data updated // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + // // Use Case OSCEV, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" ) diff --git a/ucvabd/api.go b/ucvabd/api.go index 248ac6d..ac86dce 100644 --- a/ucvabd/api.go +++ b/ucvabd/api.go @@ -14,20 +14,32 @@ type UCVABDInterface interface { // Scenario 1 // return the current (dis)charging power + // + // parameters: + // - entity: the entity of the inverter Power(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 2 // return the cumulated battery system charge energy + // + // parameters: + // - entity: the entity of the inverter EnergyCharged(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 // return the cumulated battery system discharge energy + // + // parameters: + // - entity: the entity of the inverter EnergyDischarged(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 4 // return the current state of charge of the battery system + // + // parameters: + // - entity: the entity of the inverter StateOfCharge(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucvabd/types.go b/ucvabd/types.go index 75dacdc..858a81d 100644 --- a/ucvabd/types.go +++ b/ucvabd/types.go @@ -5,6 +5,10 @@ import "github.com/enbility/cemd/api" const ( // Battery System (dis)charge power data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VABD, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -12,6 +16,10 @@ const ( // Battery System cumulated charge energy data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VABD, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -19,6 +27,10 @@ const ( // Battery System cumulated discharge energy data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VABD, Scenario 2 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -26,6 +38,10 @@ const ( // Battery System state of charge data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VABD, Scenario 4 // // Note: the referred data may be updated together with all other measurement items of this use case diff --git a/ucvapd/api.go b/ucvapd/api.go index 89a1d6c..293a84a 100644 --- a/ucvapd/api.go +++ b/ucvapd/api.go @@ -14,15 +14,24 @@ type UCVAPDInterface interface { // Scenario 1 // return the current production power + // + // parameters: + // - entity: the entity of the inverter Power(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 2 // return the nominal peak power + // + // parameters: + // - entity: the entity of the inverter PowerNominalPeak(entity spineapi.EntityRemoteInterface) (float64, error) // Scenario 3 // return total PV yield + // + // parameters: + // - entity: the entity of the inverter PVYieldTotal(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/ucvapd/types.go b/ucvapd/types.go index 0348918..4488008 100644 --- a/ucvapd/types.go +++ b/ucvapd/types.go @@ -5,6 +5,10 @@ import "github.com/enbility/cemd/api" const ( // PV System total power data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VAPD, Scenario 1 // // Note: the referred data may be updated together with all other measurement items of this use case @@ -12,11 +16,19 @@ const ( // PV System nominal peak power data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VAPD, Scenario 2 DataUpdatePowerNominalPeak api.EventType = "DataUpdatePowerNominalPeak" // PV System total yield data updated // + // The callback with this message provides: + // - the device of the inverter + // - the entity of the inverter + // // Use Case VAPD, Scenario 3 // // Note: the referred data may be updated together with all other measurement items of this use case From f62f1ce27adb6d5ddda2d3c219bf12e26c5a97ba Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 3 Mar 2024 19:39:35 +0100 Subject: [PATCH 117/227] Fix warning --- ucevcc/events.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ucevcc/events.go b/ucevcc/events.go index 4f6a23d..7d233d0 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -43,7 +43,7 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { case *model.DeviceClassificationManufacturerDataType: e.evManufacturerDataUpdate(payload.Ski, payload.Entity) case *model.ElectricalConnectionParameterDescriptionListDataType: - e.evElectricalParamerDescriptionUpdate(payload.Ski, payload.Entity) + e.evElectricalParamerDescriptionUpdate(payload.Entity) case *model.ElectricalConnectionPermittedValueSetListDataType: e.evElectricalPermittedValuesUpdate(payload.Ski, payload.Entity) case *model.IdentificationListDataType: @@ -196,7 +196,7 @@ func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemo } // the electrical connection parameter description data of an EV was updated -func (e *UCEVCC) evElectricalParamerDescriptionUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCEVCC) evElectricalParamerDescriptionUpdate(entity spineapi.EntityRemoteInterface) { if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { if _, err := evElectricalConnection.RequestPermittedValueSets(); err != nil { logging.Log().Error("Error getting electrical permitted values:", err) From a84bc743b2cb6184870d70006b2dab141f8b6593 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 3 Mar 2024 19:45:56 +0100 Subject: [PATCH 118/227] Change return value type for communicationStandard - move type to usecase api - do not use string on this level, but spine model isntead --- api/types.go | 5 ----- ucevcc/api.go | 3 ++- ucevcc/public.go | 6 +++--- ucevcc/public_test.go | 10 +++++----- ucevcc/types.go | 10 +++++++++- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/api/types.go b/api/types.go index 5b421d1..f5b220b 100644 --- a/api/types.go +++ b/api/types.go @@ -142,11 +142,6 @@ type DurationSlotValue struct { Value float64 // Energy Cost or Power Limit } -// value if the UCEVCC communication standard is unknown -const ( - UCEVCCCommunicationStandardUnknown string = "unknown" -) - // type for cem and usecase specfic event names type EventType string diff --git a/ucevcc/api.go b/ucevcc/api.go index 03b6954..b41702d 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -3,6 +3,7 @@ package ucevcc import ( "github.com/enbility/cemd/api" spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" ) //go:generate mockery @@ -31,7 +32,7 @@ type UCEVCCInterface interface { // // parameters: // - entity: the entity of the EV - CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) + CommunicationStandard(entity spineapi.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error) // Scenario 3 diff --git a/ucevcc/public.go b/ucevcc/public.go index 5ae6d5e..7d7e93b 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -111,8 +111,8 @@ func (e *UCEVCC) deviceConfigurationValueForKeyName( // possible errors: // - ErrDataNotAvailable if that information is not (yet) available // - and others -func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (string, error) { - unknown := api.UCEVCCCommunicationStandardUnknown +func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error) { + unknown := UCEVCCCommunicationStandardUnknown if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return unknown, api.ErrNoCompatibleEntity @@ -128,7 +128,7 @@ func (e *UCEVCC) CommunicationStandard(entity spineapi.EntityRemoteInterface) (s return unknown, eebusapi.ErrDataNotAvailable } - return string(*value), nil + return *value, nil } // return if the EV supports asymmetric charging diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index 1d2f6ac..1b3c643 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -101,11 +101,11 @@ func (s *UCEVCCSuite) Test_EVConnected() { func (s *UCEVCCSuite) Test_EVCommunicationStandard() { data, err := s.sut.CommunicationStandard(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) + assert.Equal(s.T(), UCEVCCCommunicationStandardUnknown, data) data, err = s.sut.CommunicationStandard(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) + assert.Equal(s.T(), UCEVCCCommunicationStandardUnknown, data) descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -122,7 +122,7 @@ func (s *UCEVCCSuite) Test_EVCommunicationStandard() { data, err = s.sut.CommunicationStandard(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) + assert.Equal(s.T(), UCEVCCCommunicationStandardUnknown, data) descData = &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -138,7 +138,7 @@ func (s *UCEVCCSuite) Test_EVCommunicationStandard() { data, err = s.sut.CommunicationStandard(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), api.UCEVCCCommunicationStandardUnknown, data) + assert.Equal(s.T(), UCEVCCCommunicationStandardUnknown, data) devData := &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ @@ -156,7 +156,7 @@ func (s *UCEVCCSuite) Test_EVCommunicationStandard() { data, err = s.sut.CommunicationStandard(s.evEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), string(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), data) + assert.Equal(s.T(), model.DeviceConfigurationKeyValueStringTypeISO151182ED2, data) } func (s *UCEVCCSuite) Test_EVAsymmetricChargingSupport() { diff --git a/ucevcc/types.go b/ucevcc/types.go index 36b524c..2da5fc1 100644 --- a/ucevcc/types.go +++ b/ucevcc/types.go @@ -1,6 +1,14 @@ package ucevcc -import "github.com/enbility/cemd/api" +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/spine-go/model" +) + +// value if the UCEVCC communication standard is unknown +const ( + UCEVCCCommunicationStandardUnknown model.DeviceConfigurationKeyValueStringType = "unknown" +) const ( From d0e529ed6be7b3537702e4828319151c7a3f4ced Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 3 Mar 2024 20:12:06 +0100 Subject: [PATCH 119/227] Fix loadcontrol tests --- util/loadcontrol_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index ec7eadd..98b521b 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -245,6 +245,14 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} permittedData := []model.ScaledNumberSetType{} for _, data := range tc.data { + // clean up data + remoteLoadControlF := s.monitoredEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + assert.NotNil(s.T(), remoteLoadControlF) + + emptyLimits := model.LoadControlLimitListDataType{} + errT := remoteLoadControlF.UpdateData(model.FunctionTypeLoadControlLimitListData, &emptyLimits, nil, nil) + assert.Nil(s.T(), errT) + for phase := 0; phase < data.phases; phase++ { item := model.ScaledNumberSetType{ Range: []model.ScaledNumberRangeType{ @@ -330,8 +338,8 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { assert.Nil(s.T(), fErr) msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) - assert.NotNil(t, err) - assert.Nil(t, msgCounter) + assert.Nil(t, err) + assert.NotNil(t, msgCounter) phaseLimitValues := []api.LoadLimitsPhase{} for index, limit := range data.limits { From e18d4265b5ec281a65ed664bec2c67f593cf5a29 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 4 Mar 2024 12:16:38 +0100 Subject: [PATCH 120/227] Add missing event subscription on cem --- cem/cem.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cem/cem.go b/cem/cem.go index 5b6b4e2..310229b 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -6,6 +6,7 @@ import ( "github.com/enbility/eebus-go/service" "github.com/enbility/ship-go/logging" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) // Generic CEM implementation @@ -32,6 +33,8 @@ func NewCEM( cem.Service.SetLogging(log) + _ = spine.Events.Subscribe(cem) + return cem } From d46d7436de874166307557b28fa7a8968fb18a74 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 14:26:45 +0100 Subject: [PATCH 121/227] Update event handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Separate device and use case specific callbacks - Device events don’t have an entity and provide information about a device being connected or not - UseCase events always have an entity and a device associated - Minor test coverage improvement --- api/api.go | 11 ++++-- cem/cem.go | 4 +-- cem/cem_test.go | 11 +++--- cem/events.go | 4 +-- cmd/democem/democem.go | 4 +-- cmd/democem/eventcb.go | 5 ++- uccevc/events.go | 46 ++++++++++++------------- uccevc/events_test.go | 18 ++++++---- uccevc/uccevc.go | 4 +-- ucevcc/events.go | 69 ++++++++++++++++++-------------------- ucevcc/events_test.go | 74 +++++++++++++++++++++++++++++++++-------- ucevcc/types.go | 4 ++- ucevcc/ucevcc.go | 4 +-- ucevcem/events.go | 24 ++++++------- ucevcem/events_test.go | 26 +++++++++++---- ucevcem/ucevcem.go | 4 +-- ucevsecc/events.go | 34 +++++++++---------- ucevsecc/events_test.go | 24 +++++++++---- ucevsecc/ucevsecc.go | 4 +-- ucevsoc/events.go | 8 ++--- ucevsoc/events_test.go | 14 +++++--- ucevsoc/ucevsoc.go | 4 +-- ucmgcp/events.go | 36 ++++++++++---------- ucmgcp/events_test.go | 21 ++++++++---- ucmgcp/ucmgcp.go | 4 +-- ucmpc/events.go | 32 +++++++++--------- ucmpc/events_test.go | 12 ++++--- ucmpc/ucmcp.go | 4 +-- ucopev/events.go | 8 ++--- ucopev/events_test.go | 14 +++++--- ucopev/ucopev.go | 4 +-- ucoscev/events.go | 8 ++--- ucoscev/events_test.go | 14 +++++--- ucoscev/ucoscev.go | 4 +-- ucvabd/events.go | 20 +++++------ ucvabd/events_test.go | 11 ++++-- ucvabd/ucvabd.go | 4 +-- ucvapd/events.go | 20 +++++------ ucvapd/events_test.go | 22 ++++++++---- ucvapd/ucvapd.go | 4 +-- 40 files changed, 386 insertions(+), 256 deletions(-) diff --git a/api/api.go b/api/api.go index 785ac65..9e668c4 100644 --- a/api/api.go +++ b/api/api.go @@ -7,10 +7,15 @@ import ( //go:generate mockery -// Event handler callback +// Device event callback // -// Used by Cem and Use Case implementations -type EventHandlerCB func(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event EventType) +// Used by CEM implementation +type DeviceEventCallback func(ski string, device spineapi.DeviceRemoteInterface, event EventType) + +// Entity event callback +// +// Used by Use Case implementations +type EntityEventCallback func(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event EventType) // Implemented by CEM type CemInterface interface { diff --git a/cem/cem.go b/cem/cem.go index 310229b..d5c6065 100644 --- a/cem/cem.go +++ b/cem/cem.go @@ -15,7 +15,7 @@ type Cem struct { Currency model.CurrencyType - eventCB api.EventHandlerCB + eventCB api.DeviceEventCallback usecases []api.UseCaseInterface } @@ -23,7 +23,7 @@ type Cem struct { func NewCEM( serviceDescription *eebusapi.Configuration, serviceHandler eebusapi.ServiceReaderInterface, - eventCB api.EventHandlerCB, + eventCB api.DeviceEventCallback, log logging.LoggingInterface) *Cem { cem := &Cem{ Service: service.NewService(serviceDescription, serviceHandler), diff --git a/cem/cem_test.go b/cem/cem_test.go index cd51a70..35a43eb 100644 --- a/cem/cem_test.go +++ b/cem/cem_test.go @@ -48,22 +48,25 @@ func (s *CemSuite) BeforeTest(suiteName, testName string) { assert.Nil(s.T(), err) noLogging := &logging.NoLogging{} - s.sut = NewCEM(configuration, s, s.eventCB, noLogging) + s.sut = NewCEM(configuration, s, s.deviceEventCB, noLogging) assert.NotNil(s.T(), s.sut) } func (s *CemSuite) Test_CEM() { err := s.sut.Setup() assert.Nil(s.T(), err) - ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s.eventCB) + ucEvseCC := ucevsecc.NewUCEVSECC(s.sut.Service, s.entityEventCB) s.sut.AddUseCase(ucEvseCC) s.sut.Start() s.sut.Shutdown() } -// ReaderInterface -func (d *CemSuite) eventCB(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +// Callbacks +func (d *CemSuite) deviceEventCB(ski string, device spineapi.DeviceRemoteInterface, event api.EventType) { +} + +func (d *CemSuite) entityEventCB(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } // eebusapi.ServiceReaderInterface diff --git a/cem/events.go b/cem/events.go index 399be90..6b1a1f7 100644 --- a/cem/events.go +++ b/cem/events.go @@ -9,12 +9,12 @@ import ( func (h *Cem) HandleEvent(payload spineapi.EventPayload) { if util.IsDeviceConnected(payload) { - h.eventCB(payload.Ski, payload.Device, nil, DeviceConnected) + h.eventCB(payload.Ski, payload.Device, DeviceConnected) return } if util.IsDeviceDisconnected(payload) { - h.eventCB(payload.Ski, payload.Device, nil, DeviceDisconnected) + h.eventCB(payload.Ski, payload.Device, DeviceDisconnected) return } } diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index 7055675..c9e7b4d 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -15,7 +15,7 @@ func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { demo := &DemoCem{} noLogging := &logging.NoLogging{} - demo.cem = cem.NewCEM(configuration, demo, demo.eventCB, noLogging) + demo.cem = cem.NewCEM(configuration, demo, demo.deviceEventCB, noLogging) return demo } @@ -25,7 +25,7 @@ func (d *DemoCem) Setup() error { return err } - evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.eventCB) + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(evsecc) d.cem.Start() diff --git a/cmd/democem/eventcb.go b/cmd/democem/eventcb.go index 065f567..5430272 100644 --- a/cmd/democem/eventcb.go +++ b/cmd/democem/eventcb.go @@ -6,5 +6,8 @@ import ( ) // Handle incoming usecase specific events -func (h *DemoCem) eventCB(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +func (h *DemoCem) deviceEventCB(ski string, device spineapi.DeviceRemoteInterface, event api.EventType) { +} + +func (h *DemoCem) entityEventCB(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { } diff --git a/uccevc/events.go b/uccevc/events.go index bf436e4..0709918 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -30,16 +30,16 @@ func (e *UCCEVC) HandleEvent(payload spineapi.EventPayload) { switch payload.Data.(type) { case *model.TimeSeriesDescriptionListDataType: - e.evTimeSeriesDescriptionDataUpdate(payload.Ski, payload.Entity) + e.evTimeSeriesDescriptionDataUpdate(payload) case *model.TimeSeriesListDataType: - e.evTimeSeriesDataUpdate(payload.Ski, payload.Entity) + e.evTimeSeriesDataUpdate(payload) case *model.IncentiveTableDescriptionDataType: - e.evIncentiveTableDescriptionDataUpdate(payload.Ski, payload.Entity) + e.evIncentiveTableDescriptionDataUpdate(payload) case *model.IncentiveDataType: - e.evIncentiveTableDataUpdate(payload.Ski, payload.Entity) + e.evIncentiveTableDataUpdate(payload) } } @@ -94,8 +94,8 @@ func (e *UCCEVC) evConnected(entity spineapi.EntityRemoteInterface) { } // the time series description data of an EV was updated -func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - if evTimeSeries, err := util.TimeSeries(e.service, entity); err == nil { +func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(payload spineapi.EventPayload) { + if evTimeSeries, err := util.TimeSeries(e.service, payload.Entity); err == nil { // get time series values if _, err := evTimeSeries.RequestValues(); err != nil { logging.Log().Debug(err) @@ -103,46 +103,46 @@ func (e *UCCEVC) evTimeSeriesDescriptionDataUpdate(ski string, entity spineapi.E } // check if we are required to update the plan - if !e.evCheckTimeSeriesDescriptionConstraintsUpdateRequired(entity) { + if !e.evCheckTimeSeriesDescriptionConstraintsUpdateRequired(payload.Entity) { return } - _, err := e.EnergyDemand(entity) + _, err := e.EnergyDemand(payload.Entity) if err != nil { return } - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyDemand) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyDemand) - _, err = e.TimeSlotConstraints(entity) + _, err = e.TimeSlotConstraints(payload.Entity) if err != nil { logging.Log().Error("Error getting timeseries constraints:", err) return } - _, err = e.IncentiveConstraints(entity) + _, err = e.IncentiveConstraints(payload.Entity) if err != nil { logging.Log().Error("Error getting incentive constraints:", err) return } - e.eventCB(ski, entity.Device(), entity, DataRequestedPowerLimitsAndIncentives) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataRequestedPowerLimitsAndIncentives) } // the load control limit data of an EV was updated -func (e *UCCEVC) evTimeSeriesDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - if _, err := e.ChargePlan(entity); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateChargePlan) +func (e *UCCEVC) evTimeSeriesDataUpdate(payload spineapi.EventPayload) { + if _, err := e.ChargePlan(payload.Entity); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateChargePlan) } - if _, err := e.ChargePlanConstraints(entity); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateTimeSlotConstraints) + if _, err := e.ChargePlanConstraints(payload.Entity); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateTimeSlotConstraints) } } // the incentive table description data of an EV was updated -func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - if evIncentiveTable, err := util.IncentiveTable(e.service, entity); err == nil { +func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(payload spineapi.EventPayload) { + if evIncentiveTable, err := util.IncentiveTable(e.service, payload.Entity); err == nil { // get time series values if _, err := evIncentiveTable.RequestValues(); err != nil { logging.Log().Debug(err) @@ -150,15 +150,15 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(ski string, entity spinea } // check if we are required to update the plan - if e.evCheckIncentiveTableDescriptionUpdateRequired(entity) { - e.eventCB(ski, entity.Device(), entity, DataRequestedIncentiveTableDescription) + if e.evCheckIncentiveTableDescriptionUpdateRequired(payload.Entity) { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataRequestedIncentiveTableDescription) } } // the load control limit data of an EV was updated -func (e *UCCEVC) evIncentiveTableDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - e.eventCB(ski, entity.Device(), entity, DataUpdateIncentiveTable) +func (e *UCCEVC) evIncentiveTableDataUpdate(payload spineapi.EventPayload) { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateIncentiveTable) } // check timeSeries descriptions if constraints element has updateRequired set to true diff --git a/uccevc/events_test.go b/uccevc/events_test.go index 64dddf2..4293d1f 100644 --- a/uccevc/events_test.go +++ b/uccevc/events_test.go @@ -42,9 +42,15 @@ func (s *UCCEVCSuite) Test_Events() { } func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { - s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evTimeSeriesDescriptionDataUpdate(payload) - s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evTimeSeriesDescriptionDataUpdate(payload) timeDesc := &model.TimeSeriesDescriptionListDataType{ TimeSeriesDescriptionData: []model.TimeSeriesDescriptionDataType{ @@ -64,7 +70,7 @@ func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { fErr := rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesDescriptionListData, timeDesc, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + s.sut.evTimeSeriesDescriptionDataUpdate(payload) timeData := &model.TimeSeriesListDataType{ TimeSeriesData: []model.TimeSeriesDataType{ @@ -96,7 +102,7 @@ func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { assert.Equal(s.T(), 0.0, demand.DurationUntilStart) assert.Equal(s.T(), 0.0, demand.DurationUntilEnd) - s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + s.sut.evTimeSeriesDescriptionDataUpdate(payload) constData := &model.TimeSeriesConstraintsListDataType{ TimeSeriesConstraintsData: []model.TimeSeriesConstraintsDataType{ @@ -114,7 +120,7 @@ func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { fErr = rTimeFeature.UpdateData(model.FunctionTypeTimeSeriesConstraintsListData, constData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + s.sut.evTimeSeriesDescriptionDataUpdate(payload) incConstData := &model.IncentiveTableConstraintsDataType{ IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ @@ -131,6 +137,6 @@ func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeIncentiveTableConstraintsData, incConstData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evTimeSeriesDescriptionDataUpdate(remoteSki, s.evEntity) + s.sut.evTimeSeriesDescriptionDataUpdate(payload) } diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index 10ae42d..2b3cd4e 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -12,14 +12,14 @@ import ( type UCCEVC struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCCEVCInterface = (*UCCEVC)(nil) -func NewUCCEVC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCCEVC { +func NewUCCEVC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCCEVC { uc := &UCCEVC{ service: service, eventCB: eventCB, diff --git a/ucevcc/events.go b/ucevcc/events.go index 7d233d0..3c00bc3 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -11,20 +11,15 @@ import ( func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EV entity or device changes for this remote device - if util.IsDeviceDisconnected(payload) { - e.evDisconnected(payload.Ski, payload.Entity) - return - } - if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } if util.IsEntityConnected(payload) { - e.evConnected(payload.Ski, payload.Entity) + e.evConnected(payload) return } else if util.IsEntityDisconnected(payload) { - e.evDisconnected(payload.Ski, payload.Entity) + e.evDisconnected(payload) return } @@ -37,24 +32,24 @@ func (e *UCEVCC) HandleEvent(payload spineapi.EventPayload) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: e.evConfigurationDescriptionDataUpdate(payload.Entity) case *model.DeviceConfigurationKeyValueListDataType: - e.evConfigurationDataUpdate(payload.Ski, payload.Entity) + e.evConfigurationDataUpdate(payload) case *model.DeviceDiagnosisOperatingStateType: - e.evOperatingStateDataUpdate(payload.Ski, payload.Entity) + e.evOperatingStateDataUpdate(payload) case *model.DeviceClassificationManufacturerDataType: - e.evManufacturerDataUpdate(payload.Ski, payload.Entity) + e.evManufacturerDataUpdate(payload) case *model.ElectricalConnectionParameterDescriptionListDataType: e.evElectricalParamerDescriptionUpdate(payload.Entity) case *model.ElectricalConnectionPermittedValueSetListDataType: - e.evElectricalPermittedValuesUpdate(payload.Ski, payload.Entity) + e.evElectricalPermittedValuesUpdate(payload) case *model.IdentificationListDataType: - e.evIdentificationDataUpdate(payload.Ski, payload.Entity) + e.evIdentificationDataUpdate(payload) } } // an EV was connected -func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCEVCC) evConnected(payload spineapi.EventPayload) { // initialise features, e.g. subscriptions, descriptions - if evDeviceClassification, err := util.DeviceClassification(e.service, entity); err == nil { + if evDeviceClassification, err := util.DeviceClassification(e.service, payload.Entity); err == nil { if _, err := evDeviceClassification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -65,7 +60,7 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if evDeviceConfiguration, err := util.DeviceConfiguration(e.service, payload.Entity); err == nil { if _, err := evDeviceConfiguration.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -75,7 +70,7 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - if evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity); err == nil { + if evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, payload.Entity); err == nil { if _, err := evDeviceDiagnosis.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -86,7 +81,7 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if evElectricalConnection, err := util.ElectricalConnection(e.service, payload.Entity); err == nil { if _, err := evElectricalConnection.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -102,7 +97,7 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - if evIdentification, err := util.Identification(e.service, entity); err == nil { + if evIdentification, err := util.Identification(e.service, payload.Entity); err == nil { if _, err := evIdentification.Subscribe(); err != nil { logging.Log().Debug(err) } @@ -113,12 +108,12 @@ func (e *UCEVCC) evConnected(ski string, entity spineapi.EntityRemoteInterface) } } - e.eventCB(ski, entity.Device(), entity, EvConnected) + e.eventCB(payload.Ski, payload.Device, payload.Entity, EvConnected) } // an EV was disconnected -func (e *UCEVCC) evDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.eventCB(ski, entity.Device(), entity, EvDisconnected) +func (e *UCEVCC) evDisconnected(payload spineapi.EventPayload) { + e.eventCB(payload.Ski, payload.Device, payload.Entity, EvDisconnected) } // the configuration key description data of an EV was updated @@ -132,38 +127,38 @@ func (e *UCEVCC) evConfigurationDescriptionDataUpdate(entity spineapi.EntityRemo } // the configuration key data of an EV was updated -func (e *UCEVCC) evConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evDeviceConfiguration, err := util.DeviceConfiguration(e.service, entity) +func (e *UCEVCC) evConfigurationDataUpdate(payload spineapi.EventPayload) { + evDeviceConfiguration, err := util.DeviceConfiguration(e.service, payload.Entity) if err != nil { return } // Scenario 2 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateCommunicationStandard) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCommunicationStandard) } // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.eventCB(ski, entity.Device(), entity, AsymmetricChargingSupportDataUpdate) + e.eventCB(payload.Ski, payload.Device, payload.Entity, AsymmetricChargingSupportDataUpdate) } } // the operating state of an EV was updated -func (e *UCEVCC) evOperatingStateDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - deviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) +func (e *UCEVCC) evOperatingStateDataUpdate(payload spineapi.EventPayload) { + deviceDiagnosis, err := util.DeviceDiagnosis(e.service, payload.Entity) if err != nil { return } if _, err := deviceDiagnosis.GetState(); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateIdentifications) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateIdentifications) } } // the identification data of an EV was updated -func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evIdentification, err := util.Identification(e.service, entity) +func (e *UCEVCC) evIdentificationDataUpdate(payload spineapi.EventPayload) { + evIdentification, err := util.Identification(e.service, payload.Entity) if err != nil { return } @@ -175,22 +170,22 @@ func (e *UCEVCC) evIdentificationDataUpdate(ski string, entity spineapi.EntityRe continue } - e.eventCB(ski, entity.Device(), entity, DataUpdateIdentifications) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateIdentifications) return } } } // the manufacturer data of an EV was updated -func (e *UCEVCC) evManufacturerDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evDeviceClassification, err := util.DeviceClassification(e.service, entity) +func (e *UCEVCC) evManufacturerDataUpdate(payload spineapi.EventPayload) { + evDeviceClassification, err := util.DeviceClassification(e.service, payload.Entity) if err != nil { return } // Scenario 5 if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateManufacturerData) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateManufacturerData) } } @@ -205,8 +200,8 @@ func (e *UCEVCC) evElectricalParamerDescriptionUpdate(entity spineapi.EntityRemo } // the electrical connection permitted value sets data of an EV was updated -func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evElectricalConnection, err := util.ElectricalConnection(e.service, entity) +func (e *UCEVCC) evElectricalPermittedValuesUpdate(payload spineapi.EventPayload) { + evElectricalConnection, err := util.ElectricalConnection(e.service, payload.Entity) if err != nil { return } @@ -222,5 +217,5 @@ func (e *UCEVCC) evElectricalPermittedValuesUpdate(ski string, entity spineapi.E } // Scenario 6 - e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentLimits) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentLimits) } diff --git a/ucevcc/events_test.go b/ucevcc/events_test.go index 1462536..4f00e45 100644 --- a/ucevcc/events_test.go +++ b/ucevcc/events_test.go @@ -54,9 +54,15 @@ func (s *UCEVCCSuite) Test_Events() { } func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { - s.sut.evConfigurationDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evConfigurationDataUpdate(payload) - s.sut.evConfigurationDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evConfigurationDataUpdate(payload) descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -75,7 +81,7 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evConfigurationDataUpdate(remoteSki, s.evEntity) + s.sut.evConfigurationDataUpdate(payload) data := &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ @@ -97,13 +103,41 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evConfigurationDataUpdate(remoteSki, s.evEntity) + s.sut.evConfigurationDataUpdate(payload) +} + +func (s *UCEVCCSuite) Test_evOperatingStateDataUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evOperatingStateDataUpdate(payload) + + payload.Entity = s.evEntity + s.sut.evOperatingStateDataUpdate(payload) + + data := &model.DeviceDiagnosisStateDataType{ + OperatingState: eebusutil.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evOperatingStateDataUpdate(payload) } func (s *UCEVCCSuite) Test_evIdentificationDataUpdate() { - s.sut.evIdentificationDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evIdentificationDataUpdate(payload) - s.sut.evIdentificationDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evIdentificationDataUpdate(payload) data := &model.IdentificationListDataType{ IdentificationData: []model.IdentificationDataType{ @@ -123,13 +157,19 @@ func (s *UCEVCCSuite) Test_evIdentificationDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeIdentificationListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evIdentificationDataUpdate(remoteSki, s.evEntity) + s.sut.evIdentificationDataUpdate(payload) } func (s *UCEVCCSuite) Test_evManufacturerDataUpdate() { - s.sut.evManufacturerDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evManufacturerDataUpdate(payload) - s.sut.evManufacturerDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evManufacturerDataUpdate(payload) data := &model.DeviceClassificationManufacturerDataType{ BrandName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), @@ -139,13 +179,19 @@ func (s *UCEVCCSuite) Test_evManufacturerDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evManufacturerDataUpdate(remoteSki, s.evEntity) + s.sut.evManufacturerDataUpdate(payload) } func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { - s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evElectricalPermittedValuesUpdate(payload) - s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evElectricalPermittedValuesUpdate(payload) paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -161,7 +207,7 @@ func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.evEntity) + s.sut.evElectricalPermittedValuesUpdate(payload) permData := &model.ElectricalConnectionPermittedValueSetListDataType{ ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ @@ -175,5 +221,5 @@ func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evElectricalPermittedValuesUpdate(remoteSki, s.evEntity) + s.sut.evElectricalPermittedValuesUpdate(payload) } diff --git a/ucevcc/types.go b/ucevcc/types.go index 2da5fc1..2a5b7f9 100644 --- a/ucevcc/types.go +++ b/ucevcc/types.go @@ -24,9 +24,11 @@ const ( // An EV was disconnected // // The callback with this message provides: - // - the device of the EVSE the EV is connected to + // - the device of the EVSE the EV was connected to // - the entity of the EV // + // Note: The ev entity is no longer connected to the device! + // // Use Case EVCC, Scenario 8 EvDisconnected api.EventType = "EvDisconnected" diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 7fe722f..8d36795 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -12,14 +12,14 @@ import ( type UCEVCC struct { service serviceapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCEVCCInterface = (*UCEVCC)(nil) -func NewUCEVCC(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVCC { +func NewUCEVCC(service serviceapi.ServiceInterface, eventCB api.EntityEventCallback) *UCEVCC { uc := &UCEVCC{ service: service, eventCB: eventCB, diff --git a/ucevcem/events.go b/ucevcem/events.go index ba51aa1..79e99a4 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -26,11 +26,11 @@ func (e *UCEVCEM) HandleEvent(payload spineapi.EventPayload) { } switch payload.Data.(type) { case *model.ElectricalConnectionDescriptionListDataType: - e.evElectricalConnectionDescriptionDataUpdate(payload.Ski, payload.Entity) + e.evElectricalConnectionDescriptionDataUpdate(payload) case *model.MeasurementDescriptionListDataType: e.evMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: - e.evMeasurementDataUpdate(payload.Ski, payload.Entity) + e.evMeasurementDataUpdate(payload) } } @@ -55,12 +55,12 @@ func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { } // the electrical connection description data of an EV was updated -func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - if _, err := e.PhasesConnected(entity); err != nil { +func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(payload spineapi.EventPayload) { + if _, err := e.PhasesConnected(payload.Entity); err != nil { return } - e.eventCB(ski, entity.Device(), entity, DataUpdatePhasesConnected) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePhasesConnected) } // the measurement description data of an EV was updated @@ -74,19 +74,19 @@ func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemot } // the measurement data of an EV was updated -func (e *UCEVCEM) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCEVCEM) evMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentPerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACCurrent); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentPerPhase) } // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePowerPerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPower); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePowerPerPhase) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyCharged) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeCharge); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyCharged) } } diff --git a/ucevcem/events_test.go b/ucevcem/events_test.go index 9896201..6aab04a 100644 --- a/ucevcem/events_test.go +++ b/ucevcem/events_test.go @@ -37,9 +37,15 @@ func (s *UCEVCEMSuite) Test_Events() { } func (s *UCEVCEMSuite) Test_evElectricalConnectionDescriptionDataUpdate() { - s.sut.evElectricalConnectionDescriptionDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evElectricalConnectionDescriptionDataUpdate(payload) - s.sut.evElectricalConnectionDescriptionDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evElectricalConnectionDescriptionDataUpdate(payload) descData := &model.ElectricalConnectionDescriptionListDataType{ ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ @@ -54,13 +60,19 @@ func (s *UCEVCEMSuite) Test_evElectricalConnectionDescriptionDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evElectricalConnectionDescriptionDataUpdate(remoteSki, s.evEntity) + s.sut.evElectricalConnectionDescriptionDataUpdate(payload) } func (s *UCEVCEMSuite) Test_evMeasurementDataUpdate() { - s.sut.evMeasurementDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evMeasurementDataUpdate(payload) - s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evMeasurementDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -83,7 +95,7 @@ func (s *UCEVCEMSuite) Test_evMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + s.sut.evMeasurementDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -105,5 +117,5 @@ func (s *UCEVCEMSuite) Test_evMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + s.sut.evMeasurementDataUpdate(payload) } diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index b0d54ba..231c150 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -12,14 +12,14 @@ import ( type UCEVCEM struct { service serviceapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCEVCEMInterface = (*UCEVCEM)(nil) -func NewUCEVCEM(service serviceapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVCEM { +func NewUCEVCEM(service serviceapi.ServiceInterface, eventCB api.EntityEventCallback) *UCEVCEM { uc := &UCEVCEM{ service: service, eventCB: eventCB, diff --git a/ucevsecc/events.go b/ucevsecc/events.go index d005583..0b6881b 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -11,7 +11,7 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EVSE entity or device changes for this remote device if util.IsDeviceDisconnected(payload) { - e.evseDisconnected(payload.Ski, payload.Entity) + e.evseDisconnected(payload) return } @@ -20,10 +20,10 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { } if util.IsEntityConnected(payload) { - e.evseConnected(payload.Ski, payload.Entity) + e.evseConnected(payload) return } else if util.IsEntityDisconnected(payload) { - e.evseDisconnected(payload.Ski, payload.Entity) + e.evseDisconnected(payload) return } @@ -34,50 +34,50 @@ func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { switch payload.Data.(type) { case *model.DeviceClassificationManufacturerDataType: - e.evseManufacturerDataUpdate(payload.Ski, payload.Entity) + e.evseManufacturerDataUpdate(payload) case *model.DeviceDiagnosisStateDataType: - e.evseStateUpdate(payload.Ski, payload.Entity) + e.evseStateUpdate(payload) } } // an EVSE was connected -func (e *UCEVSECC) evseConnected(ski string, entity spineapi.EntityRemoteInterface) { - if evseDeviceClassification, err := util.DeviceClassification(e.service, entity); err == nil { +func (e *UCEVSECC) evseConnected(payload spineapi.EventPayload) { + if evseDeviceClassification, err := util.DeviceClassification(e.service, payload.Entity); err == nil { _, _ = evseDeviceClassification.RequestManufacturerDetails() } - if evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity); err == nil { + if evseDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, payload.Entity); err == nil { _, _ = evseDeviceDiagnosis.RequestState() } - e.eventCB(ski, entity.Device(), entity, EvseConnected) + e.eventCB(payload.Ski, payload.Device, payload.Entity, EvseConnected) } // an EVSE was disconnected -func (e *UCEVSECC) evseDisconnected(ski string, entity spineapi.EntityRemoteInterface) { - e.eventCB(ski, entity.Device(), entity, EvseDisconnected) +func (e *UCEVSECC) evseDisconnected(payload spineapi.EventPayload) { + e.eventCB(payload.Ski, payload.Device, payload.Entity, EvseDisconnected) } // the manufacturer Data of an EVSE was updated -func (e *UCEVSECC) evseManufacturerDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evDeviceClassification, err := util.DeviceClassification(e.service, entity) +func (e *UCEVSECC) evseManufacturerDataUpdate(payload spineapi.EventPayload) { + evDeviceClassification, err := util.DeviceClassification(e.service, payload.Entity) if err != nil { return } if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateManufacturerData) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateManufacturerData) } } // the operating State of an EVSE was updated -func (e *UCEVSECC) evseStateUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) +func (e *UCEVSECC) evseStateUpdate(payload spineapi.EventPayload) { + evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, payload.Entity) if err != nil { return } if _, err := evDeviceDiagnosis.GetState(); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateOperatingState) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateOperatingState) } } diff --git a/ucevsecc/events_test.go b/ucevsecc/events_test.go index 75229af..3db4437 100644 --- a/ucevsecc/events_test.go +++ b/ucevsecc/events_test.go @@ -42,9 +42,15 @@ func (s *UCEVSECCSuite) Test_Events() { } func (s *UCEVSECCSuite) Test_evseManufacturerDataUpdate() { - s.sut.evseManufacturerDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evseManufacturerDataUpdate(payload) - s.sut.evseManufacturerDataUpdate(remoteSki, s.evseEntity) + payload.Entity = s.evseEntity + s.sut.evseManufacturerDataUpdate(payload) data := &model.DeviceClassificationManufacturerDataType{ BrandName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), @@ -54,13 +60,19 @@ func (s *UCEVSECCSuite) Test_evseManufacturerDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evseManufacturerDataUpdate(remoteSki, s.evseEntity) + s.sut.evseManufacturerDataUpdate(payload) } func (s *UCEVSECCSuite) Test_evseStateUpdate() { - s.sut.evseStateUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evseStateUpdate(payload) - s.sut.evseStateUpdate(remoteSki, s.evseEntity) + payload.Entity = s.evseEntity + s.sut.evseStateUpdate(payload) data := &model.DeviceDiagnosisStateDataType{ OperatingState: eebusutil.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), @@ -70,5 +82,5 @@ func (s *UCEVSECCSuite) Test_evseStateUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceDiagnosisStateData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evseStateUpdate(remoteSki, s.evseEntity) + s.sut.evseStateUpdate(payload) } diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 80a9608..ca4ee5c 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -12,14 +12,14 @@ import ( type UCEVSECC struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCEVSECCInterface = (*UCEVSECC)(nil) -func NewUCEVSECC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSECC { +func NewUCEVSECC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCEVSECC { uc := &UCEVSECC{ service: service, eventCB: eventCB, diff --git a/ucevsoc/events.go b/ucevsoc/events.go index 8cc2736..d88f39a 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -29,7 +29,7 @@ func (e *UCEVSOC) HandleEvent(payload spineapi.EventPayload) { //revive:disable-next-line switch payload.Data.(type) { case *model.MeasurementListDataType: - e.evMeasurementDataUpdate(payload.Ski, payload.Entity) + e.evMeasurementDataUpdate(payload) } } @@ -54,9 +54,9 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { } // the measurement data of an EV was updated -func (e *UCEVSOC) evMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCEVSOC) evMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateStateOfCharge) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeStateOfCharge); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateStateOfCharge) } } diff --git a/ucevsoc/events_test.go b/ucevsoc/events_test.go index 81bc92f..87f9931 100644 --- a/ucevsoc/events_test.go +++ b/ucevsoc/events_test.go @@ -31,9 +31,15 @@ func (s *UCEVSOCSuite) Test_Events() { } func (s *UCEVSOCSuite) Test_evMeasurementDataUpdate() { - s.sut.evMeasurementDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evMeasurementDataUpdate(payload) - s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evMeasurementDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -56,7 +62,7 @@ func (s *UCEVSOCSuite) Test_evMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + s.sut.evMeasurementDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -78,5 +84,5 @@ func (s *UCEVSOCSuite) Test_evMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evMeasurementDataUpdate(remoteSki, s.evEntity) + s.sut.evMeasurementDataUpdate(payload) } diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index 25bd1a3..0331ad9 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -12,14 +12,14 @@ import ( type UCEVSOC struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCEVSOCInterface = (*UCEVSOC)(nil) -func NewUCEVSOC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCEVSOC { +func NewUCEVSOC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCEVSOC { uc := &UCEVSOC{ service: service, eventCB: eventCB, diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 5b20286..9315a69 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -29,11 +29,11 @@ func (e *UCMGCP) HandleEvent(payload spineapi.EventPayload) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: e.gridConfigurationDescriptionDataUpdate(payload.Entity) case *model.DeviceConfigurationKeyValueListDataType: - e.gridConfigurationDataUpdate(payload.Ski, payload.Entity) + e.gridConfigurationDataUpdate(payload) case *model.MeasurementDescriptionListDataType: e.gridMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: - e.gridMeasurementDataUpdate(payload.Ski, payload.Entity) + e.gridMeasurementDataUpdate(payload) } } @@ -92,9 +92,9 @@ func (e *UCMGCP) gridConfigurationDescriptionDataUpdate(entity spineapi.EntityRe } // the configuration key data of an SMGW was updated -func (e *UCMGCP) gridConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - if _, err := e.PowerLimitationFactor(entity); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePowerLimitationFactor) +func (e *UCMGCP) gridConfigurationDataUpdate(payload spineapi.EventPayload) { + if _, err := e.PowerLimitationFactor(payload.Entity); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePowerLimitationFactor) } } @@ -109,35 +109,35 @@ func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemo } // the measurement data of an SMGW was updated -func (e *UCMGCP) gridMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCMGCP) gridMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePower) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridFeedIn); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyFeedIn) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeGridFeedIn); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyFeedIn) } // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeGridConsumption); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyConsumed) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeGridConsumption); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyConsumed) } // Scenario 5 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentPerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACCurrent); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentPerPhase) } // Scenario 6 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateVoltagePerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACVoltage); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateVoltagePerPhase) } // Scenario 7 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateFrequency) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACFrequency); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFrequency) } } diff --git a/ucmgcp/events_test.go b/ucmgcp/events_test.go index e66d29e..bb2f6c8 100644 --- a/ucmgcp/events_test.go +++ b/ucmgcp/events_test.go @@ -37,7 +37,12 @@ func (s *UCMGCPSuite) Test_Events() { } func (s *UCMGCPSuite) Test_gridConfigurationDataUpdate() { - s.sut.gridConfigurationDataUpdate(remoteSki, s.smgwEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.smgwEntity, + } + s.sut.gridConfigurationDataUpdate(payload) descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -66,11 +71,16 @@ func (s *UCMGCPSuite) Test_gridConfigurationDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.gridConfigurationDataUpdate(remoteSki, s.smgwEntity) + s.sut.gridConfigurationDataUpdate(payload) } func (s *UCMGCPSuite) Test_gridMeasurementDataUpdate() { - s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.smgwEntity, + } + s.sut.gridMeasurementDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -105,7 +115,7 @@ func (s *UCMGCPSuite) Test_gridMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) + s.sut.gridMeasurementDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -139,6 +149,5 @@ func (s *UCMGCPSuite) Test_gridMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.gridMeasurementDataUpdate(remoteSki, s.smgwEntity) - + s.sut.gridMeasurementDataUpdate(payload) } diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index 08cb737..7d7bae3 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -12,14 +12,14 @@ import ( type UCMGCP struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCMGCPInterface = (*UCMGCP)(nil) -func NewUCMGCP(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMGCP { +func NewUCMGCP(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCMGCP { uc := &UCMGCP{ service: service, eventCB: eventCB, diff --git a/ucmpc/events.go b/ucmpc/events.go index d02072a..128852e 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -29,7 +29,7 @@ func (e *UCMPC) HandleEvent(payload spineapi.EventPayload) { case *model.MeasurementDescriptionListDataType: e.deviceMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: - e.deviceMeasurementDataUpdate(payload.Ski, payload.Entity) + e.deviceMeasurementDataUpdate(payload) } } @@ -77,38 +77,38 @@ func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRem } // the measurement data of a device was updated -func (e *UCMPC) deviceMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCMPC) deviceMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePower) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPower); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePowerPerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPower); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePowerPerPhase) } // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyConsumed) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyConsumed) } - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACEnergyProduced); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyProduced) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACEnergyProduced); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyProduced) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACCurrent); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateCurrentsPerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACCurrent); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentsPerPhase) } // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACVoltage); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateVoltagePerPhase) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACVoltage); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateVoltagePerPhase) } // Scenario 5 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACFrequency); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateFrequency) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACFrequency); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFrequency) } } diff --git a/ucmpc/events_test.go b/ucmpc/events_test.go index e77b10a..ab6ba42 100644 --- a/ucmpc/events_test.go +++ b/ucmpc/events_test.go @@ -34,7 +34,12 @@ func (s *UCMPCSuite) Test_Events() { } func (s *UCMPCSuite) Test_deviceMeasurementDataUpdate() { - s.sut.deviceMeasurementDataUpdate(remoteSki, s.monitoredEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.deviceMeasurementDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -73,7 +78,7 @@ func (s *UCMPCSuite) Test_deviceMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.deviceMeasurementDataUpdate(remoteSki, s.monitoredEntity) + s.sut.deviceMeasurementDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -111,6 +116,5 @@ func (s *UCMPCSuite) Test_deviceMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.deviceMeasurementDataUpdate(remoteSki, s.monitoredEntity) - + s.sut.deviceMeasurementDataUpdate(payload) } diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index 00fb285..27b7b56 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -12,14 +12,14 @@ import ( type UCMPC struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCMCPInterface = (*UCMPC)(nil) -func NewUCMPC(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCMPC { +func NewUCMPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCMPC { uc := &UCMPC{ service: service, eventCB: eventCB, diff --git a/ucopev/events.go b/ucopev/events.go index 855987e..66e584b 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -29,7 +29,7 @@ func (e *UCOPEV) HandleEvent(payload spineapi.EventPayload) { case *model.LoadControlLimitDescriptionListDataType: e.evLoadControlLimitDescriptionDataUpdate(payload.Entity) case *model.LoadControlLimitListDataType: - e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) + e.evLoadControlLimitDataUpdate(payload) } } @@ -64,8 +64,8 @@ func (e *UCOPEV) evLoadControlLimitDescriptionDataUpdate(entity spineapi.EntityR } // the load control limit data of an EV was updated -func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evLoadControl, err := util.LoadControl(e.service, entity) +func (e *UCOPEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { + evLoadControl, err := util.LoadControl(e.service, payload.Entity) if err != nil { return } @@ -85,7 +85,7 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entity continue } - e.eventCB(ski, entity.Device(), entity, DataUpdateLimit) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) return } diff --git a/ucopev/events_test.go b/ucopev/events_test.go index 5f761a9..fa2f91c 100644 --- a/ucopev/events_test.go +++ b/ucopev/events_test.go @@ -34,9 +34,15 @@ func (s *UCOPEVSuite) Test_Events() { } func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evLoadControlLimitDataUpdate(payload) - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evLoadControlLimitDataUpdate(payload) descData := &model.LoadControlLimitDescriptionListDataType{ LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ @@ -51,7 +57,7 @@ func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + s.sut.evLoadControlLimitDataUpdate(payload) data := &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ @@ -65,5 +71,5 @@ func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + s.sut.evLoadControlLimitDataUpdate(payload) } diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 16e1587..a5e02c0 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -12,14 +12,14 @@ import ( type UCOPEV struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCOPEVInterface = (*UCOPEV)(nil) -func NewUCOPEV(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOPEV { +func NewUCOPEV(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCOPEV { uc := &UCOPEV{ service: service, eventCB: eventCB, diff --git a/ucoscev/events.go b/ucoscev/events.go index c19ae06..eeeb905 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -24,13 +24,13 @@ func (e *UCOSCEV) HandleEvent(payload spineapi.EventPayload) { //revive:disable-next-line switch payload.Data.(type) { case *model.LoadControlLimitListDataType: - e.evLoadControlLimitDataUpdate(payload.Ski, payload.Entity) + e.evLoadControlLimitDataUpdate(payload) } } // the load control limit data of an EV was updated -func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { - evLoadControl, err := util.LoadControl(e.service, entity) +func (e *UCOSCEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { + evLoadControl, err := util.LoadControl(e.service, payload.Entity) if err != nil { return } @@ -50,7 +50,7 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(ski string, entity spineapi.Entit continue } - e.eventCB(ski, entity.Device(), entity, DataUpdateLimit) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) return } } diff --git a/ucoscev/events_test.go b/ucoscev/events_test.go index 22f0447..0535794 100644 --- a/ucoscev/events_test.go +++ b/ucoscev/events_test.go @@ -34,9 +34,15 @@ func (s *UCOSCEVSuite) Test_Events() { } func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.mockRemoteEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evLoadControlLimitDataUpdate(payload) - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + payload.Entity = s.evEntity + s.sut.evLoadControlLimitDataUpdate(payload) descData := &model.LoadControlLimitDescriptionListDataType{ LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ @@ -51,7 +57,7 @@ func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + s.sut.evLoadControlLimitDataUpdate(payload) data := &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ @@ -65,5 +71,5 @@ func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.evLoadControlLimitDataUpdate(remoteSki, s.evEntity) + s.sut.evLoadControlLimitDataUpdate(payload) } diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 24d00fa..9f45b8d 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -12,14 +12,14 @@ import ( type UCOSCEV struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCOSCEVInterface = (*UCOSCEV)(nil) -func NewUCOSCEV(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCOSCEV { +func NewUCOSCEV(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCOSCEV { uc := &UCOSCEV{ service: service, eventCB: eventCB, diff --git a/ucvabd/events.go b/ucvabd/events.go index 128f59b..54ddcde 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -29,7 +29,7 @@ func (e *UCVABD) HandleEvent(payload spineapi.EventPayload) { case *model.MeasurementDescriptionListDataType: e.inverterMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: - e.inverterMeasurementDataUpdate(payload.Ski, payload.Entity) + e.inverterMeasurementDataUpdate(payload) } } @@ -77,24 +77,24 @@ func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity } // the measurement data of an SMGW was updated -func (e *UCVABD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCVABD) inverterMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePower) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyCharged) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeCharge); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyCharged) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeDischarge); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateEnergyDischarged) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeDischarge); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyDischarged) } // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeStateOfCharge); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdateStateOfCharge) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeStateOfCharge); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateStateOfCharge) } } diff --git a/ucvabd/events_test.go b/ucvabd/events_test.go index 43b1c67..40c06f3 100644 --- a/ucvabd/events_test.go +++ b/ucvabd/events_test.go @@ -40,7 +40,12 @@ func (s *UCVABDSuite) Test_Events() { } func (s *UCVABDSuite) Test_inverterMeasurementDataUpdate() { - s.sut.inverterMeasurementDataUpdate(remoteSki, s.batteryEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.batteryEntity, + } + s.sut.inverterMeasurementDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -67,7 +72,7 @@ func (s *UCVABDSuite) Test_inverterMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterMeasurementDataUpdate(remoteSki, s.batteryEntity) + s.sut.inverterMeasurementDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -93,5 +98,5 @@ func (s *UCVABDSuite) Test_inverterMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterMeasurementDataUpdate(remoteSki, s.batteryEntity) + s.sut.inverterMeasurementDataUpdate(payload) } diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index d24cc07..f967f31 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -12,14 +12,14 @@ import ( type UCVABD struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCVABDInterface = (*UCVABD)(nil) -func NewUCVABD(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVABD { +func NewUCVABD(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCVABD { uc := &UCVABD{ service: service, eventCB: eventCB, diff --git a/ucvapd/events.go b/ucvapd/events.go index 0bd7851..a97af4d 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -29,11 +29,11 @@ func (e *UCVAPD) HandleEvent(payload spineapi.EventPayload) { case *model.DeviceConfigurationKeyValueDescriptionListDataType: e.inverterConfigurationDescriptionDataUpdate(payload.Entity) case *model.DeviceConfigurationKeyValueListDataType: - e.inverterConfigurationDataUpdate(payload.Ski, payload.Entity) + e.inverterConfigurationDataUpdate(payload) case *model.MeasurementDescriptionListDataType: e.inverterMeasurementDescriptionDataUpdate(payload.Entity) case *model.MeasurementListDataType: - e.inverterMeasurementDataUpdate(payload.Ski, payload.Entity) + e.inverterMeasurementDataUpdate(payload) } } @@ -92,13 +92,13 @@ func (e *UCVAPD) inverterConfigurationDescriptionDataUpdate(entity spineapi.Enti } // the measurement data of an SMGW was updated -func (e *UCVAPD) inverterConfigurationDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCVAPD) inverterConfigurationDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, payload.Entity); err == nil { if _, err := deviceConfiguration.GetKeyValueForKeyName( model.DeviceConfigurationKeyNameTypePeakPowerOfPVSystem, model.DeviceConfigurationKeyValueTypeTypeScaledNumber); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePowerNominalPeak) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePowerNominalPeak) } } } @@ -114,14 +114,14 @@ func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity } // the measurement data of an SMGW was updated -func (e *UCVAPD) inverterMeasurementDataUpdate(ski string, entity spineapi.EntityRemoteInterface) { +func (e *UCVAPD) inverterMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACPowerTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePower) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, entity, model.ScopeTypeTypeACYieldTotal); err == nil { - e.eventCB(ski, entity.Device(), entity, DataUpdatePVYieldTotal) + if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACYieldTotal); err == nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePVYieldTotal) } } diff --git a/ucvapd/events_test.go b/ucvapd/events_test.go index 55132a1..43bea8e 100644 --- a/ucvapd/events_test.go +++ b/ucvapd/events_test.go @@ -40,7 +40,12 @@ func (s *UCVAPDSuite) Test_Events() { } func (s *UCVAPDSuite) Test_inverterConfigurationDataUpdate() { - s.sut.inverterConfigurationDataUpdate(remoteSki, s.pvEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.pvEntity, + } + s.sut.inverterConfigurationDataUpdate(payload) descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -56,7 +61,7 @@ func (s *UCVAPDSuite) Test_inverterConfigurationDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterConfigurationDataUpdate(remoteSki, s.pvEntity) + s.sut.inverterConfigurationDataUpdate(payload) data := &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ @@ -72,11 +77,16 @@ func (s *UCVAPDSuite) Test_inverterConfigurationDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterConfigurationDataUpdate(remoteSki, s.pvEntity) + s.sut.inverterConfigurationDataUpdate(payload) } func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { - s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.pvEntity, + } + s.sut.inverterConfigurationDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -95,7 +105,7 @@ func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) + s.sut.inverterConfigurationDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -113,5 +123,5 @@ func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterMeasurementDataUpdate(remoteSki, s.pvEntity) + s.sut.inverterConfigurationDataUpdate(payload) } diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 29d5ec9..56ac644 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -12,14 +12,14 @@ import ( type UCVAPD struct { service eebusapi.ServiceInterface - eventCB api.EventHandlerCB + eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType } var _ UCVAPDInterface = (*UCVAPD)(nil) -func NewUCVAPD(service eebusapi.ServiceInterface, eventCB api.EventHandlerCB) *UCVAPD { +func NewUCVAPD(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCVAPD { uc := &UCVAPD{ service: service, eventCB: eventCB, From f4fdd6b27e473863bf498234e93b7c45d125a31f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 14:38:22 +0100 Subject: [PATCH 122/227] Update SHIP, SPINE, EEBUS --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7fe34ce..64953b4 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240228123408-4437d35c805b - github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 - github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 + github.com/enbility/eebus-go v0.0.0-20240307133249-f618388b91ab + github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 + github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 9e577e9..6293bcc 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240228123408-4437d35c805b h1:qLTliujJ4yEJDwQQeZ5NuoD/vMpq+6yh4rYtV3YmEjI= -github.com/enbility/eebus-go v0.0.0-20240228123408-4437d35c805b/go.mod h1:JBSmPBFGRT4yE6/tmY3TGNGV01LsOM0kyyg1l1vTbnE= -github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9 h1:+TxssP/V5+k3HBJl+huN/oZQ3D7R3TUU4S1GP+jkUuM= -github.com/enbility/ship-go v0.0.0-20240228111631-eaf1f283f9b9/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3 h1:Mz+/gSmp8QkZqR6hOyYh1zRUAL8vAT63bxSbYG4m+YM= -github.com/enbility/spine-go v0.0.0-20240228085027-5102eacf33f3/go.mod h1:ylOWWOrDGUPXK7fhpt5tyVb/gTCDqkRtKwuL5qQML04= +github.com/enbility/eebus-go v0.0.0-20240307133249-f618388b91ab h1:cMFmwx+gJvPqvte4DrO9R7NKVGr36aL17dJFRazOVZ4= +github.com/enbility/eebus-go v0.0.0-20240307133249-f618388b91ab/go.mod h1:2hzhshvg6dtI+YvnRWh03aAL5lEygAVOKzg8Pl/EehQ= +github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= +github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 h1:S6WIvMkyboVoZv1FSMABJ2mTZNjvKzbje9xL1JVUxsM= +github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 997ad4f0de1658c3fafa5b4bb7ad6ca2a9490cd0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 18:12:07 +0100 Subject: [PATCH 123/227] Update UCVAPD tests --- ucvapd/events_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ucvapd/events_test.go b/ucvapd/events_test.go index 43bea8e..5b10b39 100644 --- a/ucvapd/events_test.go +++ b/ucvapd/events_test.go @@ -86,7 +86,7 @@ func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { Device: s.remoteDevice, Entity: s.pvEntity, } - s.sut.inverterConfigurationDataUpdate(payload) + s.sut.inverterMeasurementDataUpdate(payload) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ @@ -105,7 +105,9 @@ func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterConfigurationDataUpdate(payload) + s.sut.inverterMeasurementDescriptionDataUpdate(payload.Entity) + + s.sut.inverterMeasurementDataUpdate(payload) data := &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ @@ -123,5 +125,5 @@ func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) assert.Nil(s.T(), fErr) - s.sut.inverterConfigurationDataUpdate(payload) + s.sut.inverterMeasurementDataUpdate(payload) } From 6ce38e7775b877e2a219b381e839ea59b8cb1f6a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 19:30:23 +0100 Subject: [PATCH 124/227] UCEVSECC should not care about device disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UC’s should not care about a device disconnect, as this is handled on the CEM side. If a device is disconnected all usecases are inactive and not functional anyway. --- ucevsecc/events.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ucevsecc/events.go b/ucevsecc/events.go index 0b6881b..3892297 100644 --- a/ucevsecc/events.go +++ b/ucevsecc/events.go @@ -10,11 +10,6 @@ import ( func (e *UCEVSECC) HandleEvent(payload spineapi.EventPayload) { // only about events from an EVSE entity or device changes for this remote device - if util.IsDeviceDisconnected(payload) { - e.evseDisconnected(payload) - return - } - if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } From 51890f46533b7b348f63be9f5160d2df17dd5de2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 7 Mar 2024 20:16:14 +0100 Subject: [PATCH 125/227] Clarify implemented actor for some use cases --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6925d56..e7fcaf3 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ This library provides a foundation to implement energy management solutions usin - `ucevsecc`: Use Case EVSE Commissioning and Configuration V1.0.1 - `ucevsoc`: Use Case EV State Of Charge V1.0.0 RC1 - `ucmgcp`: Use Case Monitoring of Grid Connection Point V1.0.0 -- `ucmpc`: Use Case Monitoring of Power Consumption V1.0.0 +- `ucmpc`: Use Case Monitoring of Power Consumption V1.0.0 as a Monitoring Appliance - `ucopev`: Use Case Overload Protection by EV Charging Current Curtailment V1.0.1b - `ucoscev`: Use Case Optimization of Self Consumption During EV Charging V1.0.1b -- `ucvabd`: Use Case Visualization of Aggregated Battery Data V1.0.0 RC1 -- `ucvapd`: Use Case Visualization of Aggregated Photovoltaic Data V1.0.0 RC1 +- `ucvabd`: Use Case Visualization of Aggregated Battery Data V1.0.0 RC1 as a Visualization Appliance +- `ucvapd`: Use Case Visualization of Aggregated Photovoltaic Data V1.0.0 RC1 as a Visualization Appliance - `util`: various internal helpers ## Usage From 7073b59f675d7d1deb1ec478f05c4f1ddfd9b63a Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 8 Mar 2024 17:43:58 +0100 Subject: [PATCH 126/227] Update load limits helper As partials are used, do not copy all the other limit data into the dataset --- util/loadcontrol.go | 21 +-------------------- util/loadcontrol_test.go | 4 ++-- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/util/loadcontrol.go b/util/loadcontrol.go index a648348..5820113 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -182,26 +182,7 @@ func WriteLoadControlLimits( limitData = append(limitData, newLimit) } - currentLimits, err := loadControl.GetLimitValues() - if err != nil { - return nil, eebusapi.ErrDataNotAvailable - } - - for index, limit := range currentLimits { - if limit.LimitId == nil { - continue - } - - for _, newLimit := range limitData { - if newLimit.LimitId != limit.LimitId { - continue - } - - currentLimits[index] = newLimit - } - } - - msgCounter, err := loadControl.WriteLimitValues(currentLimits) + msgCounter, err := loadControl.WriteLimitValues(limitData) return msgCounter, err } diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 98b521b..4b652c0 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -338,8 +338,8 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { assert.Nil(s.T(), fErr) msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, loadLimits) - assert.Nil(t, err) - assert.NotNil(t, msgCounter) + assert.NotNil(t, err) + assert.Nil(t, msgCounter) phaseLimitValues := []api.LoadLimitsPhase{} for index, limit := range data.limits { From 38e0ce187b59032f67b3466a451ee78efa6345cc Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 8 Mar 2024 17:44:16 +0100 Subject: [PATCH 127/227] Update SPINE and EEBUS --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 64953b4..f93e245 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240307133249-f618388b91ab + github.com/enbility/eebus-go v0.0.0-20240308163532-f56210e7c21d github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 + github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 6293bcc..fab3795 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240307133249-f618388b91ab h1:cMFmwx+gJvPqvte4DrO9R7NKVGr36aL17dJFRazOVZ4= -github.com/enbility/eebus-go v0.0.0-20240307133249-f618388b91ab/go.mod h1:2hzhshvg6dtI+YvnRWh03aAL5lEygAVOKzg8Pl/EehQ= +github.com/enbility/eebus-go v0.0.0-20240308163532-f56210e7c21d h1:yWtEm9eK6Yq0KLa2crlg0RzaJlTdrAfemQNDr9/dc6g= +github.com/enbility/eebus-go v0.0.0-20240308163532-f56210e7c21d/go.mod h1:B1BjnH8qExoU/WSlGVxHJ54fZfiiLvuriuqp+gswwoI= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1 h1:S6WIvMkyboVoZv1FSMABJ2mTZNjvKzbje9xL1JVUxsM= -github.com/enbility/spine-go v0.0.0-20240307132753-7fea429a63c1/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 h1:d+KO3JsZsqaixFEr0z2pSQWNKAGrapQ+w9TH+/6RbHA= +github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From f2220fabd898580a3d2a554e517598e4a42f8fa9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 12:32:09 +0100 Subject: [PATCH 128/227] Update UCOPEV and UCOSCEV interface --- api/types.go | 17 +++++++++++++---- ucopev/api.go | 5 ++++- ucopev/public.go | 11 +++++++++-- ucoscev/api.go | 7 +++++-- ucoscev/public.go | 10 ++++++++-- util/loadcontrol.go | 35 +++++++++++++++++++++++------------ util/loadcontrol_test.go | 8 +++++--- 7 files changed, 67 insertions(+), 26 deletions(-) diff --git a/api/types.go b/api/types.go index f5b220b..a54f1ce 100644 --- a/api/types.go +++ b/api/types.go @@ -18,11 +18,20 @@ const ( EVChargeStateTypeFinished EVChargeStateType = "finished" ) -// Defines a phase specific limit +// Defines a phase specific limit data set type LoadLimitsPhase struct { - Phase model.ElectricalConnectionPhaseNameType - IsActive bool - Value float64 + Phase model.ElectricalConnectionPhaseNameType // the phase + IsChangeable bool // if the value can be changed via write, ignored when writing data + IsActive bool // if the limit is active + Value float64 // the limit in A +} + +// Defines a limit data set +type LoadLimit struct { + Duration time.Duration // the duration of the limit, + IsChangeable bool // if the value can be changed via write, ignored when writing data + IsActive bool // if the limit is active + Value float64 // the limit in A } // identification diff --git a/ucopev/api.go b/ucopev/api.go index 04dbe43..ccfbc46 100644 --- a/ucopev/api.go +++ b/ucopev/api.go @@ -19,10 +19,13 @@ type UCOPEVInterface interface { // parameters: // - entity: the entity of the EV // + // return values: + // - limits: per phase data + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) + LoadControlLimits(entity spineapi.EntityRemoteInterface) (limits []api.LoadLimitsPhase, resultErr error) // send new LoadControlLimits to the remote EV // diff --git a/ucopev/public.go b/ucopev/public.go index 664cfb5..2b85ff9 100644 --- a/ucopev/public.go +++ b/ucopev/public.go @@ -9,10 +9,17 @@ import ( // return the current loadcontrol obligation limits // +// parameters: +// - entity: the entity of the EV +// +// return values: +// - limits: per phase data +// // possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available +// - ErrDataNotAvailable if no such limit is (yet) available // - and others -func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ( + limits []api.LoadLimitsPhase, resultErr error) { return util.LoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeObligation) } diff --git a/ucoscev/api.go b/ucoscev/api.go index c32e14a..e64baef 100644 --- a/ucoscev/api.go +++ b/ucoscev/api.go @@ -14,15 +14,18 @@ type UCOSCEVInterface interface { // Scenario 1 - // return the current loadcontrol obligation limits + // return the current loadcontrol recommendation limits // // parameters: // - entity: the entity of the EV // + // return values: + // - limits: per phase data + // // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) + LoadControlLimits(entity spineapi.EntityRemoteInterface) (limits []api.LoadLimitsPhase, resultErr error) // send new LoadControlLimits to the remote EV // diff --git a/ucoscev/public.go b/ucoscev/public.go index 646e685..2c1f96c 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -9,10 +9,16 @@ import ( // return the current loadcontrol recommendation limits // +// parameters: +// - entity: the entity of the EV +// +// return values: +// - limits: per phase data +// // possible errors: -// - ErrDataNotAvailable if no such measurement is (yet) available +// - ErrDataNotAvailable if no such limit is (yet) available // - and others -func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ([]float64, error) { +func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) (limits []api.LoadLimitsPhase, resultErr error) { return util.LoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeRecommendation) } diff --git a/util/loadcontrol.go b/util/loadcontrol.go index 5820113..d0b41c2 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -17,34 +17,38 @@ func LoadControlLimits( service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType, - category model.LoadControlCategoryType) ([]float64, error) { + category model.LoadControlCategoryType) (limits []api.LoadLimitsPhase, resultErr error) { + + limits = nil + resultErr = api.ErrNoCompatibleEntity if entity == nil || !IsCompatibleEntity(entity, entityTypes) { - return nil, api.ErrNoCompatibleEntity + return } evLoadControl, err := LoadControl(service, entity) evElectricalConnection, err2 := ElectricalConnection(service, entity) if err != nil || err2 != nil { - return nil, api.ErrNoCompatibleEntity + return } + resultErr = eebusapi.ErrDataNotAvailable // find out the appropriate limitId for each phase value // limitDescription contains the measurementId for each limitId limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) if err != nil { - return nil, eebusapi.ErrDataNotAvailable + return } - var result []float64 + var result []api.LoadLimitsPhase - for i := 0; i < 3; i++ { + for i := 0; i < len(PhaseNameMapping); i++ { phaseName := PhaseNameMapping[i] // electricalParameterDescription contains the measured phase for each measurementId elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) if err != nil || elParamDesc.MeasurementId == nil { - // there is no data for this phase, the phase may not exit - result = append(result, 0) + // there is no data for this phase, the phase may not exist + result = append(result, api.LoadLimitsPhase{Phase: phaseName}) continue } @@ -60,12 +64,12 @@ func LoadControlLimits( } if limitDesc == nil || limitDesc.LimitId == nil { - return nil, eebusapi.ErrDataNotAvailable + return } limitIdData, err := evLoadControl.GetLimitValueForLimitId(*limitDesc.LimitId) if err != nil { - return nil, eebusapi.ErrDataNotAvailable + return } var limitValue float64 @@ -73,7 +77,7 @@ func LoadControlLimits( // report maximum possible if no limit is available or the limit is not active _, dataMax, _, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) if err != nil { - return nil, eebusapi.ErrDataNotAvailable + return } limitValue = dataMax @@ -81,7 +85,14 @@ func LoadControlLimits( limitValue = limitIdData.Value.GetValue() } - result = append(result, limitValue) + newLimit := api.LoadLimitsPhase{ + Phase: phaseName, + IsChangeable: (limitIdData.IsLimitChangeable != nil && *limitIdData.IsLimitChangeable), + IsActive: (limitIdData.IsLimitActive != nil && *limitIdData.IsLimitActive), + Value: limitValue, + } + + result = append(result, newLimit) } return result, nil diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 4b652c0..66f30ae 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -10,7 +10,7 @@ import ( ) func (s *UtilSuite) Test_LoadControlLimits() { - var data []float64 + var data []api.LoadLimitsPhase var err error category := model.LoadControlCategoryTypeObligation entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} @@ -49,7 +49,8 @@ func (s *UtilSuite) Test_LoadControlLimits() { data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.Nil(s.T(), err) - assert.Equal(s.T(), []float64{0.0, 0.0, 0.0}, data) + assert.Equal(s.T(), 3, len(data)) + assert.Equal(s.T(), 0.0, data[0].Value) paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -132,7 +133,8 @@ func (s *UtilSuite) Test_LoadControlLimits() { data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) assert.Nil(s.T(), err) - assert.Equal(s.T(), []float64{16.0, 16.0, 16.0}, data) + assert.Equal(s.T(), 3, len(data)) + assert.Equal(s.T(), 16.0, data[0].Value) } func (s *UtilSuite) Test_WriteLoadControlLimits() { From a082a92c92f7febd24aae7adbf69cd9970e440cb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 12:33:41 +0100 Subject: [PATCH 129/227] Add UCLPC and UCLPCServer - UCLPC provides the client role implementation, e.g. for communicating with EVSE, Heatpumps etc. - UCLPC provides the server role implementation, e.g. for communicating with a SMGW --- README.md | 2 + go.mod | 4 +- go.sum | 8 +- uclpc/api.go | 94 ++++++++ uclpc/events.go | 108 +++++++++ uclpc/events_test.go | 78 ++++++ uclpc/public.go | 327 +++++++++++++++++++++++++ uclpc/public_test.go | 385 ++++++++++++++++++++++++++++++ uclpc/results.go | 8 + uclpc/testhelper_test.go | 180 ++++++++++++++ uclpc/types.go | 38 +++ uclpc/uclpc.go | 125 ++++++++++ uclpc/uclpc_test.go | 41 ++++ uclpcserver/api.go | 91 +++++++ uclpcserver/events.go | 64 +++++ uclpcserver/events_test.go | 42 ++++ uclpcserver/public.go | 235 ++++++++++++++++++ uclpcserver/public_test.go | 87 +++++++ uclpcserver/results.go | 8 + uclpcserver/testhelper_test.go | 193 +++++++++++++++ uclpcserver/types.go | 38 +++ uclpcserver/uclpc.go | 197 +++++++++++++++ uclpcserver/uclpc_test.go | 41 ++++ util/deviceconfiguration.go | 105 ++++++++ util/deviceconfiguration_test.go | 100 ++++++++ util/electricalconnection.go | 69 ++++++ util/electricalconnection_test.go | 68 ++++++ util/loadcontrol.go | 65 +++++ util/loadcontrol_test.go | 51 ++++ util/testhelper_test.go | 13 + 30 files changed, 2859 insertions(+), 6 deletions(-) create mode 100644 uclpc/api.go create mode 100644 uclpc/events.go create mode 100644 uclpc/events_test.go create mode 100644 uclpc/public.go create mode 100644 uclpc/public_test.go create mode 100644 uclpc/results.go create mode 100644 uclpc/testhelper_test.go create mode 100644 uclpc/types.go create mode 100644 uclpc/uclpc.go create mode 100644 uclpc/uclpc_test.go create mode 100644 uclpcserver/api.go create mode 100644 uclpcserver/events.go create mode 100644 uclpcserver/events_test.go create mode 100644 uclpcserver/public.go create mode 100644 uclpcserver/public_test.go create mode 100644 uclpcserver/results.go create mode 100644 uclpcserver/testhelper_test.go create mode 100644 uclpcserver/types.go create mode 100644 uclpcserver/uclpc.go create mode 100644 uclpcserver/uclpc_test.go create mode 100644 util/deviceconfiguration.go create mode 100644 util/deviceconfiguration_test.go create mode 100644 util/electricalconnection.go create mode 100644 util/electricalconnection_test.go diff --git a/README.md b/README.md index e7fcaf3..9c0e62c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ This library provides a foundation to implement energy management solutions usin - `ucevcem`: Use Case EV Charging Electricity Measurement V1.0.1 - `ucevsecc`: Use Case EVSE Commissioning and Configuration V1.0.1 - `ucevsoc`: Use Case EV State Of Charge V1.0.0 RC1 +- `uclpc`: Use Case Limitation of Power Consumption V1.0.0 as a Energy Guard +- `uclpcserver`: Use Case Limitation of Power Consumption V1.0.0 as a Controllable System - `ucmgcp`: Use Case Monitoring of Grid Connection Point V1.0.0 - `ucmpc`: Use Case Monitoring of Power Consumption V1.0.0 as a Monitoring Appliance - `ucopev`: Use Case Overload Protection by EV Charging Current Curtailment V1.0.1b diff --git a/go.mod b/go.mod index f93e245..d85ed88 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240308163532-f56210e7c21d + github.com/enbility/eebus-go v0.0.0-20240309182657-90c4d4f160f0 github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 + github.com/enbility/spine-go v0.0.0-20240310083958-60c482a7d94f github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index fab3795..2fe089f 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240308163532-f56210e7c21d h1:yWtEm9eK6Yq0KLa2crlg0RzaJlTdrAfemQNDr9/dc6g= -github.com/enbility/eebus-go v0.0.0-20240308163532-f56210e7c21d/go.mod h1:B1BjnH8qExoU/WSlGVxHJ54fZfiiLvuriuqp+gswwoI= +github.com/enbility/eebus-go v0.0.0-20240309182657-90c4d4f160f0 h1:gX45I8v5bel3KyFDsqgtUEZx7ZOh/i4hEI4tkxBefFY= +github.com/enbility/eebus-go v0.0.0-20240309182657-90c4d4f160f0/go.mod h1:+tNdP83/zUufClY2y4r3iCeE+mHpDalbWgo5VmRT3fs= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10 h1:d+KO3JsZsqaixFEr0z2pSQWNKAGrapQ+w9TH+/6RbHA= -github.com/enbility/spine-go v0.0.0-20240308162838-77fe773efb10/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240310083958-60c482a7d94f h1:JEFBR3+G/1TGEBmTLZv9VZBAI3O4UtxrQ44sqIhx418= +github.com/enbility/spine-go v0.0.0-20240310083958-60c482a7d94f/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/uclpc/api.go b/uclpc/api.go new file mode 100644 index 0000000..8fbef89 --- /dev/null +++ b/uclpc/api.go @@ -0,0 +1,94 @@ +package uclpc + +import ( + "time" + + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +//go:generate mockery + +// interface for the Limitation of Power Consumption UseCase as a server +type UCLCPInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current loadcontrol limit data + // + // parameters: + // - entity: the entity of the e.g. EVSE + // + // return values: + // - limit: load limit data + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + LoadControlLimit(entity spineapi.EntityRemoteInterface) (limit api.LoadLimit, resultErr error) + + // send new LoadControlLimits + // + // parameters: + // - entity: the entity of the e.g. EVSE + // - limit: load limit data + WriteLoadControlLimit(entity spineapi.EntityRemoteInterface, limit api.LoadLimit) (*model.MsgCounterType, error) + + // Scenario 2 + + // return Failsafe limit for the consumed active (real) power of the + // Controllable System. This limit becomes activated in "init" state or "failsafe state". + // + // parameters: + // - entity: the entity of the e.g. EVSE + // + // return values: + // - positive values are used for consumption + FailsafeConsumptionActivePowerLimit(entity spineapi.EntityRemoteInterface) (float64, error) + + // send new Failsafe Consumption Active Power Limit + // + // parameters: + // - entity: the entity of the e.g. EVSE + // - value: the new limit in W + WriteFailsafeConsumptionActivePowerLimit(entity spineapi.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) + + // return minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" + // + // parameters: + // - entity: the entity of the e.g. EVSE + // + // return values: + // - negative values are used for production + FailsafeDurationMinimum(entity spineapi.EntityRemoteInterface) (time.Duration, error) + + // send new Failsafe Duration Minimum + // + // parameters: + // - entity: the entity of the e.g. EVSE + // - duration: the duration, between 2h and 24h + WriteFailsafeDurationMinimum(entity spineapi.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) + + // Scenario 3 + + // this is automatically covered by the SPINE implementation + + // Scenario 4 + + // return nominal maximum active (real) power the Controllable System is + // able to consume according to the device label or data sheet. + // + // parameters: + // - entity: the entity of the e.g. EVSE + PowerConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) + + // return nominal maximum active (real) power the Controllable System is + // allowed to consume due to the customer's contract. + // + // parameters: + // - entity: the entity of the e.g. EVSE + ContractualConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) +} diff --git a/uclpc/events.go b/uclpc/events.go new file mode 100644 index 0000000..60a216a --- /dev/null +++ b/uclpc/events.go @@ -0,0 +1,108 @@ +package uclpc + +import ( + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCLPC) HandleEvent(payload spineapi.EventPayload) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { + return + } + + if util.IsEntityConnected(payload) { + e.connected(payload.Entity) + return + } + + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.LoadControlLimitDescriptionListDataType: + e.loadControlLimitDescriptionDataUpdate(payload.Entity) + case *model.LoadControlLimitListDataType: + e.loadControlLimitDataUpdate(payload) + case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.configurationDescriptionDataUpdate(payload.Entity) + case *model.DeviceConfigurationKeyValueListDataType: + e.configurationDataUpdate(payload) + } +} + +// the remote device was connected +func (e *UCLPC) connected(entity spineapi.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, descriptions + if loadControl, err := util.LoadControl(e.service, entity); err == nil { + if _, err := loadControl.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get descriptions + if _, err := loadControl.RequestLimitDescriptions(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit description data was updated +func (e *UCLPC) loadControlLimitDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if loadControl, err := util.LoadControl(e.service, entity); err == nil { + // get values + if _, err := loadControl.RequestLimitValues(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit data was updated +func (e *UCLPC) loadControlLimitDataUpdate(payload spineapi.EventPayload) { + loadControl, err := util.LoadControl(e.service, payload.Entity) + if err != nil { + return + } + + data, err := loadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) + if err != nil { + return + } + + for _, item := range data { + if item.LimitId == nil { + continue + } + + _, err := loadControl.GetLimitValueForLimitId(*item.LimitId) + if err != nil { + continue + } + + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) + return + } +} + +// the configuration key description data of an SMGW was updated +func (e *UCLPC) configurationDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + // key value descriptions received, now get the data + if _, err := deviceConfiguration.RequestKeyValues(); err != nil { + logging.Log().Error("Error getting configuration key values:", err) + } + } +} + +// the configuration key data of an SMGW was updated +func (e *UCLPC) configurationDataUpdate(payload spineapi.EventPayload) { + if _, err := e.FailsafeConsumptionActivePowerLimit(payload.Entity); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeConsumptionActivePowerLimit) + } + if _, err := e.FailsafeDurationMinimum(payload.Entity); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) + } +} diff --git a/uclpc/events_test.go b/uclpc/events_test.go new file mode 100644 index 0000000..7599da7 --- /dev/null +++ b/uclpc/events_test.go @@ -0,0 +1,78 @@ +package uclpc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPCSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.monitoredEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.LoadControlLimitDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCLPCSuite) Test_loadControlLimitDataUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.loadControlLimitDataUpdate(payload) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.loadControlLimitDataUpdate(payload) + + data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.loadControlLimitDataUpdate(payload) +} diff --git a/uclpc/public.go b/uclpc/public.go new file mode 100644 index 0000000..5f4dee5 --- /dev/null +++ b/uclpc/public.go @@ -0,0 +1,327 @@ +package uclpc + +import ( + "errors" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// Scenario 1 + +// return the current loadcontrol limit data +// +// parameters: +// - entity: the entity of the e.g. EVSE +// +// return values: +// - limit: load limit data +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCLPC) LoadControlLimit(entity spineapi.EntityRemoteInterface) ( + limit api.LoadLimit, resultErr error) { + limit = api.LoadLimit{ + Value: 0.0, + IsChangeable: false, + IsActive: false, + } + + resultErr = api.ErrNoCompatibleEntity + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return + } + + resultErr = eebusapi.ErrDataNotAvailable + loadControl, err := util.LoadControl(e.service, entity) + if err != nil || loadControl == nil { + return + } + + limitDescriptions, err := loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit) + if err != nil || len(limitDescriptions) != 1 { + return + } + + value, err := loadControl.GetLimitValueForLimitId(*limitDescriptions[0].LimitId) + if err != nil || value.Value == nil { + return + } + + limit.Value = value.Value.GetValue() + limit.IsChangeable = (value.IsLimitChangeable != nil && *value.IsLimitChangeable) + limit.IsActive = (value.IsLimitActive != nil && *value.IsLimitActive) + if value.TimePeriod != nil && value.TimePeriod.EndTime != nil { + if duration, err := value.TimePeriod.EndTime.GetTimeDuration(); err == nil { + limit.Duration = duration + } + } + + resultErr = nil + + return +} + +// send new LoadControlLimits +// +// parameters: +// - entity: the entity of the e.g. EVSE +// - limit: load limit data +func (e *UCLPC) WriteLoadControlLimit( + entity spineapi.EntityRemoteInterface, + limit api.LoadLimit) (*model.MsgCounterType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + loadControl, err := util.LoadControl(e.service, entity) + if err != nil { + return nil, api.ErrNoCompatibleEntity + } + + var limitData []model.LoadControlLimitDataType + + limitDescriptions, err := loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit, + ) + if err != nil || + len(limitDescriptions) != 1 || + limitDescriptions[0].LimitId == nil { + return nil, eebusapi.ErrMetadataNotAvailable + } + + limitDesc := limitDescriptions[0] + + if _, err := loadControl.GetLimitValueForLimitId(*limitDesc.LimitId); err != nil { + return nil, eebusapi.ErrDataNotAvailable + } + + currentLimits, err := loadControl.GetLimitValues() + if err != nil { + return nil, eebusapi.ErrDataNotAvailable + } + + for index, item := range currentLimits { + if item.LimitId == nil || + *item.LimitId != *limitDesc.LimitId { + continue + } + + // EEBus_UC_TS_LimitationOfPowerConsumption V1.0.0 3.2.2.2.2.2 + // If set to "true", the timePeriod, value and isLimitActive Elements SHALL be writeable by a client. + if item.IsLimitChangeable != nil && !*item.IsLimitChangeable { + return nil, eebusapi.ErrNotSupported + } + + newLimit := model.LoadControlLimitDataType{ + LimitId: limitDesc.LimitId, + IsLimitActive: eebusutil.Ptr(limit.IsActive), + Value: model.NewScaledNumberType(limit.Value), + } + if limit.Duration > 0 { + newLimit.TimePeriod = &model.TimePeriodType{ + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(limit.Duration), + } + } + + currentLimits[index] = newLimit + break + } + + msgCounter, err := loadControl.WriteLimitValues(limitData) + + return msgCounter, err +} + +// Scenario 2 + +// return Failsafe limit for the consumed active (real) power of the +// Controllable System. This limit becomes activated in "init" state or "failsafe state". +func (e *UCLPC) FailsafeConsumptionActivePowerLimit(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetKeyValueForKeyName(keyname, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) + if err != nil || data == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + value, ok := data.(*model.ScaledNumberType) + if !ok || value == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// send new Failsafe Consumption Active Power Limit +// +// parameters: +// - entity: the entity of the e.g. EVSE +// - value: the new limit in W +func (e *UCLPC) WriteFailsafeConsumptionActivePowerLimit(entity spineapi.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetDescriptionForKeyName(keyname) + if err != nil || data == nil || data.KeyId == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + keyData := []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: data.KeyId, + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(value), + }, + }, + } + + msgCounter, err := deviceConfiguration.WriteKeyValues(keyData) + + return msgCounter, err +} + +// return minimum time the Controllable System remains in "failsafe state" unless conditions +// specified in this Use Case permit leaving the "failsafe state" +func (e *UCLPC) FailsafeDurationMinimum(entity spineapi.EntityRemoteInterface) (time.Duration, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetKeyValueForKeyName(keyname, model.DeviceConfigurationKeyValueTypeTypeDuration) + if err != nil || data == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + value, ok := data.(*model.DurationType) + if !ok || value == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + return value.GetTimeDuration() +} + +// send new Failsafe Duration Minimum +// +// parameters: +// - entity: the entity of the e.g. EVSE +// - duration: the duration, between 2h and 24h +func (e *UCLPC) WriteFailsafeDurationMinimum(entity spineapi.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + if duration < time.Duration(time.Hour*2) || duration > time.Duration(time.Hour*24) { + return nil, errors.New("duration outside of allowed range") + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetDescriptionForKeyName(keyname) + if err != nil || data == nil || data.KeyId == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + keyData := []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: data.KeyId, + Value: &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(duration), + }, + }, + } + + msgCounter, err := deviceConfiguration.WriteKeyValues(keyData) + + return msgCounter, err +} + +// Scenario 4 + +// return nominal maximum active (real) power the Controllable System is +// able to consume according to the device label or data sheet. +func (e *UCLPC) PowerConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return 0, err + } + + data, err := electricalConnection.GetCharacteristicForContextType( + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax, + ) + if err != nil || data.Value == nil { + return 0, err + } + + return data.Value.GetValue(), nil +} + +// return nominal maximum active (real) power the Controllable System is +// allowed to consume due to the customer's contract. +func (e *UCLPC) ContractualConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return 0, err + } + + data, err := electricalConnection.GetCharacteristicForContextType( + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax, + ) + if err != nil || data.Value == nil { + return 0, err + } + + return data.Value.GetValue(), nil +} diff --git a/uclpc/public_test.go b/uclpc/public_test.go new file mode 100644 index 0000000..08dc723 --- /dev/null +++ b/uclpc/public_test.go @@ -0,0 +1,385 @@ +package uclpc + +import ( + "time" + + "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPCSuite) Test_LoadControlLimit() { + data, err := s.sut.LoadControlLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data.Value) + assert.Equal(s.T(), false, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) + + data, err = s.sut.LoadControlLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data.Value) + assert.Equal(s.T(), false, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.LoadControlLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data.Value) + assert.Equal(s.T(), false, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitChangeable: eebusutil.Ptr(true), + IsLimitActive: eebusutil.Ptr(false), + Value: model.NewScaledNumberType(6000), + TimePeriod: &model.TimePeriodType{ + EndTime: model.NewAbsoluteOrRelativeTimeType("PT2H"), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.LoadControlLimit(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 6000.0, data.Value) + assert.Equal(s.T(), true, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) +} + +func (s *UCLPCSuite) Test_WriteLoadControlLimit() { + limit := api.LoadLimit{ + Value: 6000, + IsActive: true, + Duration: 0, + } + _, err := s.sut.WriteLoadControlLimit(s.mockRemoteEntity, limit) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitChangeable: eebusutil.Ptr(true), + IsLimitActive: eebusutil.Ptr(false), + Value: model.NewScaledNumberType(6000), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) + + limit.Duration = time.Duration(time.Hour * 2) + _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) +} + +func (s *UCLPCSuite) Test_FailsafeConsumptionActivePowerLimit() { + data, err := s.sut.FailsafeConsumptionActivePowerLimit(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.FailsafeConsumptionActivePowerLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeConsumptionActivePowerLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeConsumptionActivePowerLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + keyData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(4000), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeConsumptionActivePowerLimit(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 4000.0, data) +} + +func (s *UCLPCSuite) Test_WriteFailsafeConsumptionActivePowerLimit() { + _, err := s.sut.WriteFailsafeConsumptionActivePowerLimit(s.mockRemoteEntity, 6000) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteFailsafeConsumptionActivePowerLimit(s.monitoredEntity, 6000) + assert.NotNil(s.T(), err) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeConsumptionActivePowerLimit(s.monitoredEntity, 6000) + assert.Nil(s.T(), err) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeConsumptionActivePowerLimit(s.monitoredEntity, 6000) + assert.Nil(s.T(), err) +} + +func (s *UCLPCSuite) Test_FailsafeDurationMinimum() { + data, err := s.sut.FailsafeDurationMinimum(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + keyData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(time.Hour * 2), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), time.Duration(time.Hour*2), data) +} + +func (s *UCLPCSuite) Test_WriteFailsafeDurationMinimum() { + _, err := s.sut.WriteFailsafeDurationMinimum(s.mockRemoteEntity, time.Duration(time.Hour*2)) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*2)) + assert.NotNil(s.T(), err) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*2)) + assert.Nil(s.T(), err) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*2)) + assert.Nil(s.T(), err) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*1)) + assert.NotNil(s.T(), err) +} + +func (s *UCLPCSuite) Test_PowerConsumptionNominalMax() { + data, err := s.sut.PowerConsumptionNominalMax(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.PowerConsumptionNominalMax(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + charData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax), + Value: model.NewScaledNumberType(8000), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionCharacteristicListData, charData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.PowerConsumptionNominalMax(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 8000.0, data) +} + +func (s *UCLPCSuite) Test_ContractualConsumptionNominalMax() { + data, err := s.sut.ContractualConsumptionNominalMax(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.ContractualConsumptionNominalMax(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + charData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax), + Value: model.NewScaledNumberType(7000), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionCharacteristicListData, charData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.ContractualConsumptionNominalMax(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 7000.0, data) +} diff --git a/uclpc/results.go b/uclpc/results.go new file mode 100644 index 0000000..d6a459f --- /dev/null +++ b/uclpc/results.go @@ -0,0 +1,8 @@ +package uclpc + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCLPC) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/uclpc/testhelper_test.go b/uclpc/testhelper_test.go new file mode 100644 index 0000000..5889ece --- /dev/null +++ b/uclpc/testhelper_test.go @@ -0,0 +1,180 @@ +package uclpc + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestLPCSuite(t *testing.T) { + suite.Run(t, new(UCLPCSuite)) +} + +type UCLPCSuite struct { + suite.Suite + + sut *UCLPC + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface +} + +func (s *UCLPCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} + +func (s *UCLPCSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + s.sut = NewUCLPC(s.service, s.Event) + s.sut.AddFeatures() + s.sut.AddUseCase() + + s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeLoadControlLimitDescriptionListData, + model.FunctionTypeLoadControlLimitListData, + }, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisHeartbeatData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionCharacteristicListData, + }, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/uclpc/types.go b/uclpc/types.go new file mode 100644 index 0000000..a059175 --- /dev/null +++ b/uclpc/types.go @@ -0,0 +1,38 @@ +package uclpc + +import "github.com/enbility/cemd/api" + +const ( + // Load control obligation limit data updated + // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // + // Use Case LPC, Scenario 1 + DataUpdateLimit api.EventType = "DataUpdateLimit" + + // Failsafe limit for the consumed active (real) power of the + // Controllable System data updated + // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeConsumptionActivePowerLimit api.EventType = "DataUpdateFailsafeConsumptionActivePowerLimit" + + // Minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" data updated + // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" +) diff --git a/uclpc/uclpc.go b/uclpc/uclpc.go new file mode 100644 index 0000000..c9be12b --- /dev/null +++ b/uclpc/uclpc.go @@ -0,0 +1,125 @@ +package uclpc + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCLPC struct { + service eebusapi.ServiceInterface + + eventCB api.EntityEventCallback + + validEntityTypes []model.EntityTypeType +} + +var _ UCLCPInterface = (*UCLPC)(nil) + +func NewUCLPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPC { + uc := &UCLPC{ + service: service, + eventCB: eventCB, + } + + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeCompressor, + model.EntityTypeTypeEVSE, + model.EntityTypeTypeHeatPumpAppliance, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCLPC) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeLimitationOfPowerConsumption +} + +func (e *UCLPC) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) + f.AddResultHandler(e) + + // server features + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + f.AddResultHandler(e) +} + +func (e *UCLPC) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.0"), + "release", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4}) +} + +func (e *UCLPC) InitializeDataStructures() {} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCLPC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEnergyGuard, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceDiagnosis, + model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + }, + ) { + return false, nil + } + + if _, err := util.DeviceDiagnosis(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + if _, err := util.LoadControl(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + if _, err := util.DeviceConfiguration(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + if _, err := util.ElectricalConnection(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + return true, nil +} diff --git a/uclpc/uclpc_test.go b/uclpc/uclpc_test.go new file mode 100644 index 0000000..b1bd0d9 --- /dev/null +++ b/uclpc/uclpc_test.go @@ -0,0 +1,41 @@ +package uclpc + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPCSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEnergyGuard), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeLimitationOfPowerConsumption), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/uclpcserver/api.go b/uclpcserver/api.go new file mode 100644 index 0000000..e054f9d --- /dev/null +++ b/uclpcserver/api.go @@ -0,0 +1,91 @@ +package uclpcserver + +import ( + "time" + + "github.com/enbility/cemd/api" +) + +//go:generate mockery + +// interface for the Monitoring of Power Consumption UseCase +type UCLCPServerInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current loadcontrol limit data + // + // return values: + // - limit: load limit data + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + LoadControlLimit() (api.LoadLimit, error) + + // set the current loadcontrol limit data + SetLoadControlLimit(limit api.LoadLimit) (resultErr error) + + // Scenario 2 + + // return Failsafe limit for the consumed active (real) power of the + // Controllable System. This limit becomes activated in "init" state or "failsafe state". + // + // return values: + // - value: the power limit in W + // - changeable: boolean if the client service can change the limit + FailsafeConsumptionActivePowerLimit() (value float64, isChangeable bool, resultErr error) + + // set Failsafe limit for the consumed active (real) power of the + // Controllable System. This limit becomes activated in "init" state or "failsafe state". + // + // parameters: + // - value: the power limit in W + // - changeable: boolean if the client service can change the limit + SetFailsafeConsumptionActivePowerLimit(value float64, changeable bool) (resultErr error) + + // return minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" + // + // return values: + // - value: the power limit in W + // - changeable: boolean if the client service can change the limit + FailsafeDurationMinimum() (duration time.Duration, isChangeable bool, resultErr error) + + // set minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" + // + // parameters: + // - duration: has to be >= 2h and <= 24h + // - changeable: boolean if the client service can change this value + SetFailsafeDurationMinimum(duration time.Duration, changeable bool) (resultErr error) + + // Scenario 3 + + // this is automatically covered by the SPINE implementation + + // Scenario 4 + + // return nominal maximum active (real) power the Controllable System is + // able to consume according to the device label or data sheet. + PowerConsumptionNominalMax() (float64, error) + + // set nominal maximum active (real) power the Controllable System is + // able to consume according to the device label or data sheet. + // + // parameters: + // - value: nominal max power consumption in W + SetPowerConsumptionNominalMax(value float64) (resultErr error) + + // return nominal maximum active (real) power the Controllable System is + // allowed to consume due to the customer's contract. + ContractualConsumptionNominalMax() (float64, error) + + // set nominal maximum active (real) power the Controllable System is + // allowed to consume due to the customer's contract. + // + // parameters: + // - value: contractual nominal max power consumption in W + SetContractualConsumptionNominalMax(value float64) (resultErr error) +} diff --git a/uclpcserver/events.go b/uclpcserver/events.go new file mode 100644 index 0000000..98817ff --- /dev/null +++ b/uclpcserver/events.go @@ -0,0 +1,64 @@ +package uclpcserver + +import ( + "github.com/enbility/cemd/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { + return + } + + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + if localEntity == nil || + payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate || + payload.CmdClassifier == nil || + *payload.CmdClassifier != model.CmdClassifierTypeWrite { + return + } + + // the codefactor warning is invalid, as .(type) check can not be replaced with if then + //revive:disable-next-line + switch payload.Data.(type) { + case *model.LoadControlLimitListDataType: + serverF := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + + if payload.Function != model.FunctionTypeLoadControlLimitListData || + payload.LocalFeature != serverF { + return + } + + e.loadControlLimitDataUpdate(payload) + case *model.DeviceConfigurationKeyValueListDataType: + serverF := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + if payload.Function != model.FunctionTypeDeviceConfigurationKeyValueListData || + payload.LocalFeature != serverF { + return + } + + e.configurationDataUpdate(payload) + } +} + +// the load control limit data was updated +func (e *UCLPCServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { + if _, err := e.LoadControlLimit(); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) + } +} + +// the configuration key data of an SMGW was updated +func (e *UCLPCServer) configurationDataUpdate(payload spineapi.EventPayload) { + if _, _, err := e.FailsafeConsumptionActivePowerLimit(); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeConsumptionActivePowerLimit) + } + if _, _, err := e.FailsafeDurationMinimum(); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) + } +} diff --git a/uclpcserver/events_test.go b/uclpcserver/events_test.go new file mode 100644 index 0000000..280f2d8 --- /dev/null +++ b/uclpcserver/events_test.go @@ -0,0 +1,42 @@ +package uclpcserver + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +func (s *UCLPCServerSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.monitoredEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.CmdClassifier = eebusutil.Ptr(model.CmdClassifierTypeWrite) + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Function = model.FunctionTypeLoadControlLimitListData + payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) + s.sut.HandleEvent(payload) + + payload.LocalFeature = s.loadControlFeature + s.sut.HandleEvent(payload) + + payload.Function = model.FunctionTypeDeviceConfigurationKeyValueListData + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) + + payload.LocalFeature = s.deviceConfigurationFeature + s.sut.HandleEvent(payload) +} diff --git a/uclpcserver/public.go b/uclpcserver/public.go new file mode 100644 index 0000000..089bc65 --- /dev/null +++ b/uclpcserver/public.go @@ -0,0 +1,235 @@ +package uclpcserver + +import ( + "errors" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" +) + +// Scenario 1 + +// return the current loadcontrol limit data +// +// return values: +// - limit: load limit data +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCLPCServer) LoadControlLimit() (limit api.LoadLimit, resultErr error) { + limit = api.LoadLimit{ + Value: 0.0, + IsChangeable: false, + IsActive: false, + Duration: 0, + } + resultErr = eebusapi.ErrDataNotAvailable + + description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit, + ) + if description.LimitId == nil { + return + } + + value := util.GetLocalLimitValueForLimitId(e.service, *description.LimitId) + if value.LimitId == nil || value.Value == nil { + return + } + + limit.Value = value.Value.GetValue() + limit.IsChangeable = (value.IsLimitChangeable != nil && *value.IsLimitChangeable) + limit.IsActive = (value.IsLimitActive != nil && *value.IsLimitActive) + if value.TimePeriod != nil && value.TimePeriod.EndTime != nil { + if duration, err := value.TimePeriod.EndTime.GetTimeDuration(); err == nil { + limit.Duration = duration + } + } + + return limit, nil +} + +// set the current loadcontrol limit data +func (e *UCLPCServer) SetLoadControlLimit(limit api.LoadLimit) (resultErr error) { + resultErr = eebusapi.ErrDataNotAvailable + + description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit, + ) + if description.LimitId == nil { + return + } + + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + loadControl := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + if loadControl == nil { + return + } + + limitData := model.LoadControlLimitDataType{ + LimitId: description.LimitId, + IsLimitChangeable: eebusutil.Ptr(limit.IsChangeable), + IsLimitActive: eebusutil.Ptr(limit.IsActive), + Value: model.NewScaledNumberType(limit.Value), + } + if limit.Duration > 0 { + limitData.TimePeriod = &model.TimePeriodType{ + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(limit.Duration), + } + } + limits := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{limitData}, + } + + loadControl.SetData(model.FunctionTypeLoadControlLimitListData, limits) + + return nil +} + +// Scenario 2 + +// return Failsafe limit for the consumed active (real) power of the +// Controllable System. This limit becomes activated in "init" state or "failsafe state". +func (e *UCLPCServer) FailsafeConsumptionActivePowerLimit() (limit float64, isChangeable bool, resultErr error) { + limit = 0 + isChangeable = false + resultErr = eebusapi.ErrDataNotAvailable + + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + keyData := util.GetLocalDeviceConfigurationKeyValueForKeyName(e.service, keyName) + if keyData.KeyId == nil || keyData.Value == nil || keyData.Value.ScaledNumber == nil { + return + } + + limit = keyData.Value.ScaledNumber.GetValue() + isChangeable = (keyData.IsValueChangeable != nil && *keyData.IsValueChangeable) + resultErr = nil + return +} + +// set Failsafe limit for the consumed active (real) power of the +// Controllable System. This limit becomes activated in "init" state or "failsafe state". +func (e *UCLPCServer) SetFailsafeConsumptionActivePowerLimit(value float64, changeable bool) error { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + keyValue := model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(value), + } + + return util.SetLocalDeviceConfigurationKeyValue(e.service, keyName, changeable, keyValue) +} + +// return minimum time the Controllable System remains in "failsafe state" unless conditions +// specified in this Use Case permit leaving the "failsafe state" +func (e *UCLPCServer) FailsafeDurationMinimum() (duration time.Duration, isChangeable bool, resultErr error) { + duration = 0 + isChangeable = false + resultErr = eebusapi.ErrDataNotAvailable + + keyName := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + keyData := util.GetLocalDeviceConfigurationKeyValueForKeyName(e.service, keyName) + if keyData.KeyId == nil || keyData.Value == nil || keyData.Value.Duration == nil { + return + } + + durationValue, err := keyData.Value.Duration.GetTimeDuration() + if err != nil { + return + } + + duration = durationValue + isChangeable = (keyData.IsValueChangeable != nil && *keyData.IsValueChangeable) + resultErr = nil + return +} + +// set minimum time the Controllable System remains in "failsafe state" unless conditions +// specified in this Use Case permit leaving the "failsafe state" +// +// parameters: +// - duration: has to be >= 2h and <= 24h +// - changeable: boolean if the client service can change this value +func (e *UCLPCServer) SetFailsafeDurationMinimum(duration time.Duration, changeable bool) error { + if duration < time.Duration(time.Hour*2) || duration > time.Duration(time.Hour*24) { + return errors.New("duration outside of allowed range") + } + keyName := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + keyValue := model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(duration), + } + + return util.SetLocalDeviceConfigurationKeyValue(e.service, keyName, changeable, keyValue) +} + +// Scenario 4 + +// return nominal maximum active (real) power the Controllable System is +// able to consume according to the device label or data sheet. +func (e *UCLPCServer) PowerConsumptionNominalMax() (value float64, resultErr error) { + value = 0 + resultErr = eebusapi.ErrDataNotAvailable + + charData := util.GetLocalElectricalConnectionCharacteristicForContextType( + e.service, + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax, + ) + if charData.CharacteristicId == nil || charData.Value == nil { + return + } + + return charData.Value.GetValue(), nil +} + +// set nominal maximum active (real) power the Controllable System is +// able to consume according to the device label or data sheet. +func (e *UCLPCServer) SetPowerConsumptionNominalMax(value float64) error { + return util.SetLocalElectricalConnectionCharacteristicForContextType( + e.service, + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax, + value, + ) +} + +// return nominal maximum active (real) power the Controllable System is +// allowed to consume due to the customer's contract. +func (e *UCLPCServer) ContractualConsumptionNominalMax() (value float64, resultErr error) { + value = 0 + resultErr = eebusapi.ErrDataNotAvailable + + charData := util.GetLocalElectricalConnectionCharacteristicForContextType( + e.service, + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax, + ) + if charData.CharacteristicId == nil || charData.Value == nil { + return + } + + return charData.Value.GetValue(), nil +} + +// set nominal maximum active (real) power the Controllable System is +// allowed to consume due to the customer's contract. +func (e *UCLPCServer) SetContractualConsumptionNominalMax(value float64) error { + return util.SetLocalElectricalConnectionCharacteristicForContextType( + e.service, + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax, + value, + ) +} diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go new file mode 100644 index 0000000..3eba46f --- /dev/null +++ b/uclpcserver/public_test.go @@ -0,0 +1,87 @@ +package uclpcserver + +import ( + "time" + + "github.com/enbility/cemd/api" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPCServerSuite) Test_LoadControlLimit() { + limit, err := s.sut.LoadControlLimit() + assert.Equal(s.T(), 0.0, limit.Value) + assert.NotNil(s.T(), err) + + newLimit := api.LoadLimit{ + Duration: time.Duration(time.Hour * 2), + IsActive: true, + IsChangeable: true, + Value: 16, + } + err = s.sut.SetLoadControlLimit(newLimit) + assert.Nil(s.T(), err) + + limit, err = s.sut.LoadControlLimit() + assert.Equal(s.T(), 16.0, limit.Value) + assert.Nil(s.T(), err) +} + +func (s *UCLPCServerSuite) Test_FailsafeConsumptionActivePowerLimit() { + limit, changeable, err := s.sut.FailsafeConsumptionActivePowerLimit() + assert.Equal(s.T(), 0.0, limit) + assert.Equal(s.T(), false, changeable) + assert.NotNil(s.T(), err) + + err = s.sut.SetFailsafeConsumptionActivePowerLimit(10, true) + assert.Nil(s.T(), err) + + limit, changeable, err = s.sut.FailsafeConsumptionActivePowerLimit() + assert.Equal(s.T(), 10.0, limit) + assert.Equal(s.T(), true, changeable) + assert.Nil(s.T(), err) +} + +func (s *UCLPCServerSuite) Test_FailsafeDurationMinimum() { + // The actual tests of the functionality is located in the util package + duration, changeable, err := s.sut.FailsafeDurationMinimum() + assert.Equal(s.T(), time.Duration(0), duration) + assert.Equal(s.T(), false, changeable) + assert.NotNil(s.T(), err) + + err = s.sut.SetFailsafeDurationMinimum(time.Duration(time.Hour*1), true) + assert.NotNil(s.T(), err) + + err = s.sut.SetFailsafeDurationMinimum(time.Duration(time.Hour*2), true) + assert.Nil(s.T(), err) + + duration, changeable, err = s.sut.FailsafeDurationMinimum() + assert.Equal(s.T(), time.Duration(time.Hour*2), duration) + assert.Equal(s.T(), true, changeable) + assert.Nil(s.T(), err) +} + +func (s *UCLPCServerSuite) Test_PowerConsumptionNominalMax() { + value, err := s.sut.PowerConsumptionNominalMax() + assert.Equal(s.T(), 0.0, value) + assert.NotNil(s.T(), err) + + err = s.sut.SetPowerConsumptionNominalMax(10) + assert.Nil(s.T(), err) + + value, err = s.sut.PowerConsumptionNominalMax() + assert.Equal(s.T(), 10.0, value) + assert.Nil(s.T(), err) +} + +func (s *UCLPCServerSuite) Test_ContractualConsumptionNominalMax() { + value, err := s.sut.ContractualConsumptionNominalMax() + assert.Equal(s.T(), 0.0, value) + assert.NotNil(s.T(), err) + + err = s.sut.SetContractualConsumptionNominalMax(10) + assert.Nil(s.T(), err) + + value, err = s.sut.ContractualConsumptionNominalMax() + assert.Equal(s.T(), 10.0, value) + assert.Nil(s.T(), err) +} diff --git a/uclpcserver/results.go b/uclpcserver/results.go new file mode 100644 index 0000000..a7cb991 --- /dev/null +++ b/uclpcserver/results.go @@ -0,0 +1,8 @@ +package uclpcserver + +import ( + "github.com/enbility/spine-go/api" +) + +func (e *UCLPCServer) HandleResult(errorMsg api.ResultMessage) { +} diff --git a/uclpcserver/testhelper_test.go b/uclpcserver/testhelper_test.go new file mode 100644 index 0000000..6828838 --- /dev/null +++ b/uclpcserver/testhelper_test.go @@ -0,0 +1,193 @@ +package uclpcserver + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestLPCServerSuite(t *testing.T) { + suite.Run(t, new(UCLPCServerSuite)) +} + +type UCLPCServerSuite struct { + suite.Suite + + sut *UCLPCServer + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface + loadControlFeature, + deviceConfigurationFeature spineapi.FeatureLocalInterface +} + +func (s *UCLPCServerSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} + +func (s *UCLPCServerSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + + s.sut = NewUCLPC(s.service, s.Event) + s.sut.AddFeatures() + s.sut.AddUseCase() + + localEntity := s.sut.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + s.loadControlFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + s.deviceConfigurationFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionPermittedValueSetListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisHeartbeatData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeClient, + []model.FunctionType{}, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeGridGuard), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/uclpcserver/types.go b/uclpcserver/types.go new file mode 100644 index 0000000..418f176 --- /dev/null +++ b/uclpcserver/types.go @@ -0,0 +1,38 @@ +package uclpcserver + +import "github.com/enbility/cemd/api" + +const ( + // Load control obligation limit data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // + // Use Case LPC, Scenario 1 + DataUpdateLimit api.EventType = "DataUpdateLimit" + + // Failsafe limit for the consumed active (real) power of the + // Controllable System data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeConsumptionActivePowerLimit api.EventType = "DataUpdateFailsafeConsumptionActivePowerLimit" + + // Minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" +) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go new file mode 100644 index 0000000..9c37081 --- /dev/null +++ b/uclpcserver/uclpc.go @@ -0,0 +1,197 @@ +package uclpcserver + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCLPCServer struct { + service eebusapi.ServiceInterface + + eventCB api.EntityEventCallback + + validEntityTypes []model.EntityTypeType +} + +var _ UCLCPServerInterface = (*UCLPCServer)(nil) + +func NewUCLPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPCServer { + uc := &UCLPCServer{ + service: service, + eventCB: eventCB, + } + + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeGridGuard, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCLPCServer) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeLimitationOfPowerConsumption +} + +func (e *UCLPCServer) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + f.AddResultHandler(e) + + // server features + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + f.AddResultHandler(e) + + var limitId model.LoadControlLimitIdType = 0 + // get the highest limitId + if desc, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( + f, model.FunctionTypeLoadControlLimitDescriptionListData); err == nil && desc.LoadControlLimitDescriptionData != nil { + for _, desc := range desc.LoadControlLimitDescriptionData { + if desc.LimitId != nil && *desc.LimitId >= limitId { + limitId++ + } + } + } + + loadControlDesc := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, loadControlDesc) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) + f.AddResultHandler(e) + + var configId model.DeviceConfigurationKeyIdType = 0 + // get the heighest keyId + if desc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( + f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData); err == nil && desc.DeviceConfigurationKeyValueDescriptionData != nil { + for _, desc := range desc.DeviceConfigurationKeyValueDescriptionData { + if desc.KeyId != nil && *desc.KeyId >= configId { + configId++ + } + } + } + + deviceConfigDesc := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), + }, + }, + } + f.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, deviceConfigDesc) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, true) + f.AddResultHandler(e) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) + f.AddResultHandler(e) + + var elCharId model.ElectricalConnectionCharacteristicIdType = 0 + // get the heighest CharacteristicId + if desc, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( + f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicListData != nil { + for _, desc := range desc.ElectricalConnectionCharacteristicListData { + if desc.CharacteristicId != nil && *desc.CharacteristicId >= elCharId { + elCharId++ + } + } + } + + // ElectricalConnectionId and ParameterId should be identical to the ones used + // in a MPC Server role implementation, which is not done here (yet) + elCharData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(elCharId), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(elCharId + 1), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + }, + } + f.SetData(model.FunctionTypeElectricalConnectionCharacteristicListData, elCharData) +} + +func (e *UCLPCServer) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeCEM, + e.UseCaseName(), + model.SpecificationVersionType("1.0.0"), + "release", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4}) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCLPCServer) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEnergyGuard, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceDiagnosis, + }, + ) { + return false, nil + } + + if _, err := util.DeviceDiagnosis(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + return true, nil +} diff --git a/uclpcserver/uclpc_test.go b/uclpcserver/uclpc_test.go new file mode 100644 index 0000000..d2bbbf2 --- /dev/null +++ b/uclpcserver/uclpc_test.go @@ -0,0 +1,41 @@ +package uclpcserver + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPCServerSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEnergyGuard), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeLimitationOfPowerConsumption), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/util/deviceconfiguration.go b/util/deviceconfiguration.go new file mode 100644 index 0000000..f1ae01f --- /dev/null +++ b/util/deviceconfiguration.go @@ -0,0 +1,105 @@ +package util + +import ( + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +func GetLocalDeviceConfigurationDescriptionForKeyName( + service eebusapi.ServiceInterface, + keyName model.DeviceConfigurationKeyNameType, +) (description model.DeviceConfigurationKeyValueDescriptionDataType) { + description = model.DeviceConfigurationKeyValueDescriptionDataType{} + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + deviceConfiguration := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + if deviceConfiguration == nil { + return + } + + data, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( + deviceConfiguration, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) + if err != nil || data == nil || data.DeviceConfigurationKeyValueDescriptionData == nil { + return + } + + for _, desc := range data.DeviceConfigurationKeyValueDescriptionData { + if desc.KeyName != nil && *desc.KeyName == keyName { + description = desc + break + } + } + + return +} + +func GetLocalDeviceConfigurationKeyValueForKeyName( + service eebusapi.ServiceInterface, + keyName model.DeviceConfigurationKeyNameType, +) (keyData model.DeviceConfigurationKeyValueDataType) { + keyData = model.DeviceConfigurationKeyValueDataType{} + + description := GetLocalDeviceConfigurationDescriptionForKeyName(service, keyName) + if description.KeyId == nil { + return + } + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + deviceConfiguration := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + if deviceConfiguration == nil { + return + } + + data, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueListDataType]( + deviceConfiguration, model.FunctionTypeDeviceConfigurationKeyValueListData) + if err != nil || data == nil || data.DeviceConfigurationKeyValueData == nil { + return + } + + for _, item := range data.DeviceConfigurationKeyValueData { + if item.KeyId != nil && *item.KeyId == *description.KeyId { + keyData = item + break + } + } + + return +} + +func SetLocalDeviceConfigurationKeyValue( + service eebusapi.ServiceInterface, + keyName model.DeviceConfigurationKeyNameType, + changeable bool, + value model.DeviceConfigurationKeyValueValueType, +) (resultErr error) { + resultErr = eebusapi.ErrDataNotAvailable + + description := GetLocalDeviceConfigurationDescriptionForKeyName(service, keyName) + if description.KeyId == nil { + return + } + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + deviceConfiguration := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + if deviceConfiguration == nil { + return + } + + keyData := model.DeviceConfigurationKeyValueDataType{ + KeyId: eebusutil.Ptr(*description.KeyId), + IsValueChangeable: eebusutil.Ptr(changeable), + Value: eebusutil.Ptr(value), + } + keysData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{keyData}, + } + + deviceConfiguration.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, keysData) + + return nil +} diff --git a/util/deviceconfiguration_test.go b/util/deviceconfiguration_test.go new file mode 100644 index 0000000..1604484 --- /dev/null +++ b/util/deviceconfiguration_test.go @@ -0,0 +1,100 @@ +package util + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UtilSuite) Test_GetLocalDeviceConfigurationDescriptionForKeyName() { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + + data := GetLocalDeviceConfigurationDescriptionForKeyName(s.service, keyName) + assert.Nil(s.T(), data.KeyId) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(keyName), + }, + }, + } + feature.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData) + + data = GetLocalDeviceConfigurationDescriptionForKeyName(s.service, keyName) + assert.NotNil(s.T(), data.KeyId) +} + +func (s *UtilSuite) Test_GetLocalDeviceConfigurationKeyValueForId() { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + + data := GetLocalDeviceConfigurationKeyValueForKeyName(s.service, keyName) + assert.Nil(s.T(), data.KeyId) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(keyName), + }, + }, + } + feature.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData) + + data = GetLocalDeviceConfigurationKeyValueForKeyName(s.service, keyName) + assert.Nil(s.T(), data.KeyId) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + }, + }, + } + + feature.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData) + + data = GetLocalDeviceConfigurationKeyValueForKeyName(s.service, keyName) + assert.NotNil(s.T(), data.KeyId) +} + +func (s *UtilSuite) Test_SetLocalDeviceConfigurationKeyValue() { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + changeable := false + value := model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(10), + } + + err := SetLocalDeviceConfigurationKeyValue(s.service, keyName, changeable, value) + assert.NotNil(s.T(), err) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(keyName), + }, + }, + } + feature.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData) + + err = SetLocalDeviceConfigurationKeyValue(s.service, keyName, changeable, value) + assert.Nil(s.T(), err) + + data := GetLocalDeviceConfigurationKeyValueForKeyName(s.service, keyName) + assert.NotNil(s.T(), data.KeyId) + assert.Equal(s.T(), uint(0), uint(*data.KeyId)) + assert.NotNil(s.T(), data.Value) + assert.NotNil(s.T(), data.Value.ScaledNumber) + assert.Equal(s.T(), 10.0, data.Value.ScaledNumber.GetValue()) +} diff --git a/util/electricalconnection.go b/util/electricalconnection.go new file mode 100644 index 0000000..86a9fff --- /dev/null +++ b/util/electricalconnection.go @@ -0,0 +1,69 @@ +package util + +import ( + eebusapi "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +func GetLocalElectricalConnectionCharacteristicForContextType( + service eebusapi.ServiceInterface, + context model.ElectricalConnectionCharacteristicContextType, + charType model.ElectricalConnectionCharacteristicTypeType, +) (charData model.ElectricalConnectionCharacteristicDataType) { + charData = model.ElectricalConnectionCharacteristicDataType{} + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + electricalConnection := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + if electricalConnection == nil { + return + } + + function := model.FunctionTypeElectricalConnectionCharacteristicListData + data, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( + electricalConnection, function) + if err != nil || data == nil || data.ElectricalConnectionCharacteristicListData == nil { + return + } + + for _, item := range data.ElectricalConnectionCharacteristicListData { + if item.CharacteristicContext != nil && *item.CharacteristicContext == context && + item.CharacteristicType != nil && *item.CharacteristicType == charType { + charData = item + break + } + } + + return +} + +func SetLocalElectricalConnectionCharacteristicForContextType( + service eebusapi.ServiceInterface, + context model.ElectricalConnectionCharacteristicContextType, + charType model.ElectricalConnectionCharacteristicTypeType, + value float64, +) (resultErr error) { + resultErr = eebusapi.ErrDataNotAvailable + + charData := GetLocalElectricalConnectionCharacteristicForContextType(service, context, charType) + if charData.CharacteristicId == nil { + return + } + charData.Value = model.NewScaledNumberType(value) + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + electricalConnection := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + if electricalConnection == nil { + return + } + function := model.FunctionTypeElectricalConnectionCharacteristicListData + + listData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{charData}, + } + electricalConnection.SetData(function, listData) + + return nil +} diff --git a/util/electricalconnection_test.go b/util/electricalconnection_test.go new file mode 100644 index 0000000..32d7be3 --- /dev/null +++ b/util/electricalconnection_test.go @@ -0,0 +1,68 @@ +package util + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UtilSuite) Test_GetLocalElectricalConnectionCharacteristicForContextType() { + context := model.ElectricalConnectionCharacteristicContextTypeEntity + charType := model.ElectricalConnectionCharacteristicTypeTypeApparentPowerConsumptionNominalMax + + data := GetLocalElectricalConnectionCharacteristicForContextType(s.service, context, charType) + assert.Nil(s.T(), data.CharacteristicId) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + + charData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), + CharacteristicContext: eebusutil.Ptr(context), + CharacteristicType: eebusutil.Ptr(charType), + }, + }, + } + feature.SetData(model.FunctionTypeElectricalConnectionCharacteristicListData, charData) + + data = GetLocalElectricalConnectionCharacteristicForContextType(s.service, context, charType) + assert.NotNil(s.T(), data.CharacteristicId) +} + +func (s *UtilSuite) Test_SetLocalElectricalConnectionCharacteristicForContextType() { + context := model.ElectricalConnectionCharacteristicContextTypeEntity + charType := model.ElectricalConnectionCharacteristicTypeTypeApparentPowerConsumptionNominalMax + value := 10.0 + + err := SetLocalElectricalConnectionCharacteristicForContextType(s.service, context, charType, value) + assert.NotNil(s.T(), err) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + + charData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), + CharacteristicContext: eebusutil.Ptr(context), + CharacteristicType: eebusutil.Ptr(charType), + }, + }, + } + feature.SetData(model.FunctionTypeElectricalConnectionCharacteristicListData, charData) + + err = SetLocalElectricalConnectionCharacteristicForContextType(s.service, context, charType, value) + assert.Nil(s.T(), err) + + data := GetLocalElectricalConnectionCharacteristicForContextType(s.service, context, charType) + assert.NotNil(s.T(), data.CharacteristicId) + assert.Equal(s.T(), uint(0), uint(*data.CharacteristicId)) + assert.NotNil(s.T(), data.Value) + assert.Equal(s.T(), 10.0, data.Value.GetValue()) +} diff --git a/util/loadcontrol.go b/util/loadcontrol.go index d0b41c2..8a850de 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -6,6 +6,7 @@ import ( eebusutil "github.com/enbility/eebus-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" ) // return the current loadcontrol limits for a categoriy @@ -197,3 +198,67 @@ func WriteLoadControlLimits( return msgCounter, err } + +func GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + service eebusapi.ServiceInterface, + limitType model.LoadControlLimitTypeType, + limitCategory model.LoadControlCategoryType, + limitDirection model.EnergyDirectionType, + scopeType model.ScopeTypeType, +) (description model.LoadControlLimitDescriptionDataType) { + description = model.LoadControlLimitDescriptionDataType{} + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + loadControl := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + if loadControl == nil { + return + } + + data, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( + loadControl, model.FunctionTypeLoadControlLimitDescriptionListData) + if err != nil || data == nil || data.LoadControlLimitDescriptionData == nil { + return + } + + for _, desc := range data.LoadControlLimitDescriptionData { + if desc.LimitType != nil && *desc.LimitType == limitType && + desc.LimitCategory != nil && *desc.LimitCategory == limitCategory && + desc.LimitDirection != nil && *desc.LimitDirection == limitDirection && + desc.ScopeType != nil && *desc.ScopeType == scopeType { + description = desc + break + } + } + + return description +} + +func GetLocalLimitValueForLimitId( + service eebusapi.ServiceInterface, + limitId model.LoadControlLimitIdType, +) (value model.LoadControlLimitDataType) { + value = model.LoadControlLimitDataType{} + + localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + loadControl := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + if loadControl == nil { + return + } + + values, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitListDataType]( + loadControl, model.FunctionTypeLoadControlLimitListData) + if err != nil || values == nil || values.LoadControlLimitData == nil { + return + } + + for _, item := range values.LoadControlLimitData { + if item.LimitId != nil && *item.LimitId == limitId { + value = item + break + } + } + + return +} diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 66f30ae..f79f74b 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -364,3 +364,54 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { }) } } + +func (s *UtilSuite) Test_GetLocalLimitDescriptionsForTypeCategoryDirectionScope() { + limitType := model.LoadControlLimitTypeTypeSignDependentAbsValueLimit + limitCategory := model.LoadControlCategoryTypeObligation + limitDirection := model.EnergyDirectionTypeConsume + limitScopeType := model.ScopeTypeTypeActivePowerLimit + + data := GetLocalLimitDescriptionsForTypeCategoryDirectionScope(s.service, limitType, limitCategory, limitDirection, limitScopeType) + assert.Nil(s.T(), data.LimitId) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + + desc := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitType: eebusutil.Ptr(limitType), + LimitCategory: eebusutil.Ptr(limitCategory), + LimitDirection: eebusutil.Ptr(limitDirection), + ScopeType: eebusutil.Ptr(limitScopeType), + }, + }, + } + feature.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, desc) + + data = GetLocalLimitDescriptionsForTypeCategoryDirectionScope(s.service, limitType, limitCategory, limitDirection, limitScopeType) + assert.NotNil(s.T(), data.LimitId) +} + +func (s *UtilSuite) Test_GetLocalLimitValueForLimitId() { + limitId := model.LoadControlLimitIdType(0) + + data := GetLocalLimitValueForLimitId(s.service, limitId) + assert.Nil(s.T(), data.LimitId) + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + + desc := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + }, + }, + } + feature.SetData(model.FunctionTypeLoadControlLimitListData, desc) + + data = GetLocalLimitValueForLimitId(s.service, limitId) + assert.NotNil(s.T(), data.LimitId) +} diff --git a/util/testhelper_test.go b/util/testhelper_test.go index 19e4c6c..5bea0a0 100644 --- a/util/testhelper_test.go +++ b/util/testhelper_test.go @@ -85,6 +85,19 @@ func setupDevices( localEntity.AddFeature(f) f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) localEntity.AddFeature(f) + f = spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionPermittedValueSetListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) + localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() From ca4e330424685fef24d1146611417294db56ca6c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 13:55:02 +0100 Subject: [PATCH 130/227] Add helper to change use case availability --- api/api.go | 7 +++++-- uccevc/uccevc.go | 6 ++++++ ucevcc/ucevcc.go | 6 ++++++ ucevcem/ucevcem.go | 6 ++++++ ucevsecc/ucevsecc.go | 6 ++++++ ucevsoc/ucevsoc.go | 6 ++++++ uclpc/uclpc.go | 6 +++++- uclpcserver/uclpc.go | 6 ++++++ ucmgcp/ucmgcp.go | 6 ++++++ ucmpc/ucmcp.go | 6 ++++++ ucopev/ucopev.go | 6 ++++++ ucoscev/ucoscev.go | 6 ++++++ ucvabd/ucvabd.go | 6 ++++++ 13 files changed, 76 insertions(+), 3 deletions(-) diff --git a/api/api.go b/api/api.go index 9e668c4..7adb7f9 100644 --- a/api/api.go +++ b/api/api.go @@ -32,7 +32,7 @@ type CemInterface interface { AddUseCase(usecase UseCaseInterface) } -// Implemented by each UseCase +// Implemented by each Use Case type UseCaseInterface interface { // provide the usecase name UseCaseName() model.UseCaseNameType @@ -40,9 +40,12 @@ type UseCaseInterface interface { // add the features AddFeatures() - // add the usecase + // add the use case AddUseCase() + // update availability of the use case + UpdateUseCaseAvailability(available bool) + // returns if the entity supports the usecase // // possible errors: diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index 2b3cd4e..c8d3594 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -66,6 +66,12 @@ func (e *UCCEVC) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3}) } +func (e *UCCEVC) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 8d36795..3899078 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -67,6 +67,12 @@ func (e *UCEVCC) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) } +func (e *UCEVCC) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index 231c150..cf98446 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -64,6 +64,12 @@ func (e *UCEVCEM) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3}) } +func (e *UCEVCEM) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index ca4ee5c..96ef6b8 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -65,6 +65,12 @@ func (e *UCEVSECC) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2}) } +func (e *UCEVSECC) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index 0331ad9..2280ece 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -64,6 +64,12 @@ func (e *UCEVSOC) AddUseCase() { []model.UseCaseScenarioSupportType{1}) } +func (e *UCEVSOC) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/uclpc/uclpc.go b/uclpc/uclpc.go index c9be12b..13b5a0c 100644 --- a/uclpc/uclpc.go +++ b/uclpc/uclpc.go @@ -77,7 +77,11 @@ func (e *UCLPC) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3, 4}) } -func (e *UCLPC) InitializeDataStructures() {} +func (e *UCLPC) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} // returns if the entity supports the usecase // diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 9c37081..61012ae 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -166,6 +166,12 @@ func (e *UCLPCServer) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3, 4}) } +func (e *UCLPCServer) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index 7d7bae3..71ad484 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -65,6 +65,12 @@ func (e *UCMGCP) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7}) } +func (e *UCMGCP) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index 27b7b56..a4096b4 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -70,6 +70,12 @@ func (e *UCMPC) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5}) } +func (e *UCMPC) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index a5e02c0..5f9bc02 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -69,6 +69,12 @@ func (e *UCOPEV) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3}) } +func (e *UCOPEV) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 9f45b8d..5394a35 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -75,6 +75,12 @@ func (e *UCOSCEV) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3}) } +func (e *UCOSCEV) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index f967f31..e861142 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -65,6 +65,12 @@ func (e *UCVABD) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3}) } +func (e *UCVABD) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: From 791c891e7f8c11365f8f41fb82f2e67912d691f2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 13:56:11 +0100 Subject: [PATCH 131/227] Add missing implementation --- ucvapd/ucvapd.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 56ac644..0ffbc1f 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -65,6 +65,12 @@ func (e *UCVAPD) AddUseCase() { []model.UseCaseScenarioSupportType{1, 2, 3}) } +func (e *UCVAPD) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) +} + // returns if the entity supports the usecase // // possible errors: From 3d10fe3d5ec40c8980236ec0d5674428e467eb77 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 14:24:24 +0100 Subject: [PATCH 132/227] Add tests --- uccevc/uccevc_test.go | 4 ++++ ucevcc/ucevcc_test.go | 4 ++++ ucevcem/ucevcem_test.go | 4 ++++ ucevsecc/ucevsecc_test.go | 4 ++++ ucevsoc/ucevsoc_test.go | 4 ++++ uclpc/uclpc_test.go | 4 ++++ uclpcserver/uclpc_test.go | 4 ++++ ucmgcp/ucmgcp_test.go | 4 ++++ ucmpc/ucmcp_test.go | 4 ++++ ucopev/ucopev_test.go | 4 ++++ ucoscev/ucoscev_test.go | 4 ++++ ucvabd/ucvabd_test.go | 4 ++++ ucvapd/ucvapd_test.go | 4 ++++ 13 files changed, 52 insertions(+) diff --git a/uccevc/uccevc_test.go b/uccevc/uccevc_test.go index 5f2799e..400dba8 100644 --- a/uccevc/uccevc_test.go +++ b/uccevc/uccevc_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCCEVCSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCCEVCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucevcc/ucevcc_test.go b/ucevcc/ucevcc_test.go index 727bf1e..3400cb2 100644 --- a/ucevcc/ucevcc_test.go +++ b/ucevcc/ucevcc_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCEVCCSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCEVCCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucevcem/ucevcem_test.go b/ucevcem/ucevcem_test.go index 5be10a9..978ae3d 100644 --- a/ucevcem/ucevcem_test.go +++ b/ucevcem/ucevcem_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCEVCEMSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCEVCEMSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucevsecc/ucevsecc_test.go b/ucevsecc/ucevsecc_test.go index 564d20b..54b7b8c 100644 --- a/ucevsecc/ucevsecc_test.go +++ b/ucevsecc/ucevsecc_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCEVSECCSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCEVSECCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucevsoc/ucevsoc_test.go b/ucevsoc/ucevsoc_test.go index 96e919d..78b8608 100644 --- a/ucevsoc/ucevsoc_test.go +++ b/ucevsoc/ucevsoc_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCEVSOCSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCEVSOCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/uclpc/uclpc_test.go b/uclpc/uclpc_test.go index b1bd0d9..56188de 100644 --- a/uclpc/uclpc_test.go +++ b/uclpc/uclpc_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCLPCSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCLPCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/uclpcserver/uclpc_test.go b/uclpcserver/uclpc_test.go index d2bbbf2..e4c8414 100644 --- a/uclpcserver/uclpc_test.go +++ b/uclpcserver/uclpc_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCLPCServerSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCLPCServerSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucmgcp/ucmgcp_test.go b/ucmgcp/ucmgcp_test.go index 5949462..aae33cc 100644 --- a/ucmgcp/ucmgcp_test.go +++ b/ucmgcp/ucmgcp_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCMGCPSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCMGCPSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucmpc/ucmcp_test.go b/ucmpc/ucmcp_test.go index d8f490d..02ff864 100644 --- a/ucmpc/ucmcp_test.go +++ b/ucmpc/ucmcp_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCMPCSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCMPCSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucopev/ucopev_test.go b/ucopev/ucopev_test.go index df62ae2..26905a2 100644 --- a/ucopev/ucopev_test.go +++ b/ucopev/ucopev_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCOPEVSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCOPEVSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucoscev/ucoscev_test.go b/ucoscev/ucoscev_test.go index 36ed875..0d26823 100644 --- a/ucoscev/ucoscev_test.go +++ b/ucoscev/ucoscev_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCOSCEVSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCOSCEVSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucvabd/ucvabd_test.go b/ucvabd/ucvabd_test.go index 55e540c..678fc42 100644 --- a/ucvabd/ucvabd_test.go +++ b/ucvabd/ucvabd_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCVABDSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCVABDSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) diff --git a/ucvapd/ucvapd_test.go b/ucvapd/ucvapd_test.go index 68c8ef4..a9e0c84 100644 --- a/ucvapd/ucvapd_test.go +++ b/ucvapd/ucvapd_test.go @@ -6,6 +6,10 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UCVAPDSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + func (s *UCVAPDSuite) Test_IsUseCaseSupported() { data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) assert.NotNil(s.T(), err) From e7ca4b192516759752bc03d37e67b4da18ad838b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 14:56:07 +0100 Subject: [PATCH 133/227] Add mocks --- .mockery.yaml | 17 +- mocks/CemInterface.go | 177 ++++++++ mocks/DeviceEventCallback.go | 72 ++++ mocks/EntityEventCallback.go | 73 ++++ mocks/UCCEVCInterface.go | 706 +++++++++++++++++++++++++++++++ mocks/UCEVCCInterface.go | 704 +++++++++++++++++++++++++++++++ mocks/UCEVCEMInterface.go | 463 +++++++++++++++++++++ mocks/UCEVSECCInterface.go | 361 ++++++++++++++++ mocks/UCEVSOCInterface.go | 291 +++++++++++++ mocks/UCLCPInterface.go | 696 +++++++++++++++++++++++++++++++ mocks/UCLCPServerInterface.go | 760 ++++++++++++++++++++++++++++++++++ mocks/UCMCPInterface.go | 633 ++++++++++++++++++++++++++++ mocks/UCMGCPInterface.go | 631 ++++++++++++++++++++++++++++ mocks/UCOPEVInterface.go | 354 ++++++++++++++++ mocks/UCOSCEVInterface.go | 354 ++++++++++++++++ mocks/UCVABDInterface.go | 459 ++++++++++++++++++++ mocks/UCVAPDInterface.go | 403 ++++++++++++++++++ mocks/UseCaseInterface.go | 235 +++++++++++ 18 files changed, 7387 insertions(+), 2 deletions(-) create mode 100644 mocks/CemInterface.go create mode 100644 mocks/DeviceEventCallback.go create mode 100644 mocks/EntityEventCallback.go create mode 100644 mocks/UCCEVCInterface.go create mode 100644 mocks/UCEVCCInterface.go create mode 100644 mocks/UCEVCEMInterface.go create mode 100644 mocks/UCEVSECCInterface.go create mode 100644 mocks/UCEVSOCInterface.go create mode 100644 mocks/UCLCPInterface.go create mode 100644 mocks/UCLCPServerInterface.go create mode 100644 mocks/UCMCPInterface.go create mode 100644 mocks/UCMGCPInterface.go create mode 100644 mocks/UCOPEVInterface.go create mode 100644 mocks/UCOSCEVInterface.go create mode 100644 mocks/UCVABDInterface.go create mode 100644 mocks/UCVAPDInterface.go create mode 100644 mocks/UseCaseInterface.go diff --git a/.mockery.yaml b/.mockery.yaml index 732f4e6..33fe715 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -1,10 +1,23 @@ with-expecter: true inpackage: false -dir: "{{ .InterfaceDir }}/mocks/" +dir: "{{ .InterfaceDir }}/../mocks" mockname: "{{.InterfaceName}}" outpkg: "mocks" filename: "{{.InterfaceName}}.go" all: true packages: github.com/enbility/cemd/api: - github.com/enbility/cemd/evsecc: + github.com/enbility/cemd/cem: + github.com/enbility/cemd/uccevc: + github.com/enbility/cemd/ucevcc: + github.com/enbility/cemd/ucevcem: + github.com/enbility/cemd/ucevsecc: + github.com/enbility/cemd/ucevsoc: + github.com/enbility/cemd/uclpc: + github.com/enbility/cemd/uclpcserver: + github.com/enbility/cemd/ucmgcp: + github.com/enbility/cemd/ucmpc: + github.com/enbility/cemd/ucopev: + github.com/enbility/cemd/ucoscev: + github.com/enbility/cemd/ucvabd: + github.com/enbility/cemd/ucvapd: diff --git a/mocks/CemInterface.go b/mocks/CemInterface.go new file mode 100644 index 0000000..f834515 --- /dev/null +++ b/mocks/CemInterface.go @@ -0,0 +1,177 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/cemd/api" + mock "github.com/stretchr/testify/mock" +) + +// CemInterface is an autogenerated mock type for the CemInterface type +type CemInterface struct { + mock.Mock +} + +type CemInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *CemInterface) EXPECT() *CemInterface_Expecter { + return &CemInterface_Expecter{mock: &_m.Mock} +} + +// AddUseCase provides a mock function with given fields: usecase +func (_m *CemInterface) AddUseCase(usecase api.UseCaseInterface) { + _m.Called(usecase) +} + +// CemInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type CemInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +// - usecase api.UseCaseInterface +func (_e *CemInterface_Expecter) AddUseCase(usecase interface{}) *CemInterface_AddUseCase_Call { + return &CemInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase", usecase)} +} + +func (_c *CemInterface_AddUseCase_Call) Run(run func(usecase api.UseCaseInterface)) *CemInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.UseCaseInterface)) + }) + return _c +} + +func (_c *CemInterface_AddUseCase_Call) Return() *CemInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *CemInterface_AddUseCase_Call) RunAndReturn(run func(api.UseCaseInterface)) *CemInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// Setup provides a mock function with given fields: +func (_m *CemInterface) Setup() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Setup") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CemInterface_Setup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Setup' +type CemInterface_Setup_Call struct { + *mock.Call +} + +// Setup is a helper method to define mock.On call +func (_e *CemInterface_Expecter) Setup() *CemInterface_Setup_Call { + return &CemInterface_Setup_Call{Call: _e.mock.On("Setup")} +} + +func (_c *CemInterface_Setup_Call) Run(run func()) *CemInterface_Setup_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *CemInterface_Setup_Call) Return(_a0 error) *CemInterface_Setup_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *CemInterface_Setup_Call) RunAndReturn(run func() error) *CemInterface_Setup_Call { + _c.Call.Return(run) + return _c +} + +// Shutdown provides a mock function with given fields: +func (_m *CemInterface) Shutdown() { + _m.Called() +} + +// CemInterface_Shutdown_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Shutdown' +type CemInterface_Shutdown_Call struct { + *mock.Call +} + +// Shutdown is a helper method to define mock.On call +func (_e *CemInterface_Expecter) Shutdown() *CemInterface_Shutdown_Call { + return &CemInterface_Shutdown_Call{Call: _e.mock.On("Shutdown")} +} + +func (_c *CemInterface_Shutdown_Call) Run(run func()) *CemInterface_Shutdown_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *CemInterface_Shutdown_Call) Return() *CemInterface_Shutdown_Call { + _c.Call.Return() + return _c +} + +func (_c *CemInterface_Shutdown_Call) RunAndReturn(run func()) *CemInterface_Shutdown_Call { + _c.Call.Return(run) + return _c +} + +// Start provides a mock function with given fields: +func (_m *CemInterface) Start() { + _m.Called() +} + +// CemInterface_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type CemInterface_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +func (_e *CemInterface_Expecter) Start() *CemInterface_Start_Call { + return &CemInterface_Start_Call{Call: _e.mock.On("Start")} +} + +func (_c *CemInterface_Start_Call) Run(run func()) *CemInterface_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *CemInterface_Start_Call) Return() *CemInterface_Start_Call { + _c.Call.Return() + return _c +} + +func (_c *CemInterface_Start_Call) RunAndReturn(run func()) *CemInterface_Start_Call { + _c.Call.Return(run) + return _c +} + +// NewCemInterface creates a new instance of CemInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCemInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *CemInterface { + mock := &CemInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/DeviceEventCallback.go b/mocks/DeviceEventCallback.go new file mode 100644 index 0000000..7dc91db --- /dev/null +++ b/mocks/DeviceEventCallback.go @@ -0,0 +1,72 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" +) + +// DeviceEventCallback is an autogenerated mock type for the DeviceEventCallback type +type DeviceEventCallback struct { + mock.Mock +} + +type DeviceEventCallback_Expecter struct { + mock *mock.Mock +} + +func (_m *DeviceEventCallback) EXPECT() *DeviceEventCallback_Expecter { + return &DeviceEventCallback_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ski, device, event +func (_m *DeviceEventCallback) Execute(ski string, device api.DeviceRemoteInterface, event cemdapi.EventType) { + _m.Called(ski, device, event) +} + +// DeviceEventCallback_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type DeviceEventCallback_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ski string +// - device api.DeviceRemoteInterface +// - event cemdapi.EventType +func (_e *DeviceEventCallback_Expecter) Execute(ski interface{}, device interface{}, event interface{}) *DeviceEventCallback_Execute_Call { + return &DeviceEventCallback_Execute_Call{Call: _e.mock.On("Execute", ski, device, event)} +} + +func (_c *DeviceEventCallback_Execute_Call) Run(run func(ski string, device api.DeviceRemoteInterface, event cemdapi.EventType)) *DeviceEventCallback_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(api.DeviceRemoteInterface), args[2].(cemdapi.EventType)) + }) + return _c +} + +func (_c *DeviceEventCallback_Execute_Call) Return() *DeviceEventCallback_Execute_Call { + _c.Call.Return() + return _c +} + +func (_c *DeviceEventCallback_Execute_Call) RunAndReturn(run func(string, api.DeviceRemoteInterface, cemdapi.EventType)) *DeviceEventCallback_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewDeviceEventCallback creates a new instance of DeviceEventCallback. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDeviceEventCallback(t interface { + mock.TestingT + Cleanup(func()) +}) *DeviceEventCallback { + mock := &DeviceEventCallback{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/EntityEventCallback.go b/mocks/EntityEventCallback.go new file mode 100644 index 0000000..6922b4f --- /dev/null +++ b/mocks/EntityEventCallback.go @@ -0,0 +1,73 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" +) + +// EntityEventCallback is an autogenerated mock type for the EntityEventCallback type +type EntityEventCallback struct { + mock.Mock +} + +type EntityEventCallback_Expecter struct { + mock *mock.Mock +} + +func (_m *EntityEventCallback) EXPECT() *EntityEventCallback_Expecter { + return &EntityEventCallback_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ski, device, entity, event +func (_m *EntityEventCallback) Execute(ski string, device api.DeviceRemoteInterface, entity api.EntityRemoteInterface, event cemdapi.EventType) { + _m.Called(ski, device, entity, event) +} + +// EntityEventCallback_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type EntityEventCallback_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ski string +// - device api.DeviceRemoteInterface +// - entity api.EntityRemoteInterface +// - event cemdapi.EventType +func (_e *EntityEventCallback_Expecter) Execute(ski interface{}, device interface{}, entity interface{}, event interface{}) *EntityEventCallback_Execute_Call { + return &EntityEventCallback_Execute_Call{Call: _e.mock.On("Execute", ski, device, entity, event)} +} + +func (_c *EntityEventCallback_Execute_Call) Run(run func(ski string, device api.DeviceRemoteInterface, entity api.EntityRemoteInterface, event cemdapi.EventType)) *EntityEventCallback_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(api.DeviceRemoteInterface), args[2].(api.EntityRemoteInterface), args[3].(cemdapi.EventType)) + }) + return _c +} + +func (_c *EntityEventCallback_Execute_Call) Return() *EntityEventCallback_Execute_Call { + _c.Call.Return() + return _c +} + +func (_c *EntityEventCallback_Execute_Call) RunAndReturn(run func(string, api.DeviceRemoteInterface, api.EntityRemoteInterface, cemdapi.EventType)) *EntityEventCallback_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewEntityEventCallback creates a new instance of EntityEventCallback. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEntityEventCallback(t interface { + mock.TestingT + Cleanup(func()) +}) *EntityEventCallback { + mock := &EntityEventCallback{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCCEVCInterface.go b/mocks/UCCEVCInterface.go new file mode 100644 index 0000000..b66a267 --- /dev/null +++ b/mocks/UCCEVCInterface.go @@ -0,0 +1,706 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCCEVCInterface is an autogenerated mock type for the UCCEVCInterface type +type UCCEVCInterface struct { + mock.Mock +} + +type UCCEVCInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCCEVCInterface) EXPECT() *UCCEVCInterface_Expecter { + return &UCCEVCInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCCEVCInterface) AddFeatures() { + _m.Called() +} + +// UCCEVCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCCEVCInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCCEVCInterface_Expecter) AddFeatures() *UCCEVCInterface_AddFeatures_Call { + return &UCCEVCInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCCEVCInterface_AddFeatures_Call) Run(run func()) *UCCEVCInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCCEVCInterface_AddFeatures_Call) Return() *UCCEVCInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCCEVCInterface_AddFeatures_Call) RunAndReturn(run func()) *UCCEVCInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCCEVCInterface) AddUseCase() { + _m.Called() +} + +// UCCEVCInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCCEVCInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCCEVCInterface_Expecter) AddUseCase() *UCCEVCInterface_AddUseCase_Call { + return &UCCEVCInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCCEVCInterface_AddUseCase_Call) Run(run func()) *UCCEVCInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCCEVCInterface_AddUseCase_Call) Return() *UCCEVCInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCCEVCInterface_AddUseCase_Call) RunAndReturn(run func()) *UCCEVCInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// ChargePlan provides a mock function with given fields: entity +func (_m *UCCEVCInterface) ChargePlan(entity api.EntityRemoteInterface) (cemdapi.ChargePlan, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ChargePlan") + } + + var r0 cemdapi.ChargePlan + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.ChargePlan, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.ChargePlan); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.ChargePlan) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCCEVCInterface_ChargePlan_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChargePlan' +type UCCEVCInterface_ChargePlan_Call struct { + *mock.Call +} + +// ChargePlan is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) ChargePlan(entity interface{}) *UCCEVCInterface_ChargePlan_Call { + return &UCCEVCInterface_ChargePlan_Call{Call: _e.mock.On("ChargePlan", entity)} +} + +func (_c *UCCEVCInterface_ChargePlan_Call) Run(run func(entity api.EntityRemoteInterface)) *UCCEVCInterface_ChargePlan_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_ChargePlan_Call) Return(_a0 cemdapi.ChargePlan, _a1 error) *UCCEVCInterface_ChargePlan_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCCEVCInterface_ChargePlan_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.ChargePlan, error)) *UCCEVCInterface_ChargePlan_Call { + _c.Call.Return(run) + return _c +} + +// ChargePlanConstraints provides a mock function with given fields: entity +func (_m *UCCEVCInterface) ChargePlanConstraints(entity api.EntityRemoteInterface) ([]cemdapi.DurationSlotValue, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ChargePlanConstraints") + } + + var r0 []cemdapi.DurationSlotValue + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]cemdapi.DurationSlotValue, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []cemdapi.DurationSlotValue); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]cemdapi.DurationSlotValue) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCCEVCInterface_ChargePlanConstraints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChargePlanConstraints' +type UCCEVCInterface_ChargePlanConstraints_Call struct { + *mock.Call +} + +// ChargePlanConstraints is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) ChargePlanConstraints(entity interface{}) *UCCEVCInterface_ChargePlanConstraints_Call { + return &UCCEVCInterface_ChargePlanConstraints_Call{Call: _e.mock.On("ChargePlanConstraints", entity)} +} + +func (_c *UCCEVCInterface_ChargePlanConstraints_Call) Run(run func(entity api.EntityRemoteInterface)) *UCCEVCInterface_ChargePlanConstraints_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_ChargePlanConstraints_Call) Return(_a0 []cemdapi.DurationSlotValue, _a1 error) *UCCEVCInterface_ChargePlanConstraints_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCCEVCInterface_ChargePlanConstraints_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]cemdapi.DurationSlotValue, error)) *UCCEVCInterface_ChargePlanConstraints_Call { + _c.Call.Return(run) + return _c +} + +// ChargeStrategy provides a mock function with given fields: remoteEntity +func (_m *UCCEVCInterface) ChargeStrategy(remoteEntity api.EntityRemoteInterface) cemdapi.EVChargeStrategyType { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for ChargeStrategy") + } + + var r0 cemdapi.EVChargeStrategyType + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.EVChargeStrategyType); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(cemdapi.EVChargeStrategyType) + } + + return r0 +} + +// UCCEVCInterface_ChargeStrategy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChargeStrategy' +type UCCEVCInterface_ChargeStrategy_Call struct { + *mock.Call +} + +// ChargeStrategy is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) ChargeStrategy(remoteEntity interface{}) *UCCEVCInterface_ChargeStrategy_Call { + return &UCCEVCInterface_ChargeStrategy_Call{Call: _e.mock.On("ChargeStrategy", remoteEntity)} +} + +func (_c *UCCEVCInterface_ChargeStrategy_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCCEVCInterface_ChargeStrategy_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_ChargeStrategy_Call) Return(_a0 cemdapi.EVChargeStrategyType) *UCCEVCInterface_ChargeStrategy_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCCEVCInterface_ChargeStrategy_Call) RunAndReturn(run func(api.EntityRemoteInterface) cemdapi.EVChargeStrategyType) *UCCEVCInterface_ChargeStrategy_Call { + _c.Call.Return(run) + return _c +} + +// EnergyDemand provides a mock function with given fields: remoteEntity +func (_m *UCCEVCInterface) EnergyDemand(remoteEntity api.EntityRemoteInterface) (cemdapi.Demand, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for EnergyDemand") + } + + var r0 cemdapi.Demand + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.Demand, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.Demand); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(cemdapi.Demand) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCCEVCInterface_EnergyDemand_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyDemand' +type UCCEVCInterface_EnergyDemand_Call struct { + *mock.Call +} + +// EnergyDemand is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) EnergyDemand(remoteEntity interface{}) *UCCEVCInterface_EnergyDemand_Call { + return &UCCEVCInterface_EnergyDemand_Call{Call: _e.mock.On("EnergyDemand", remoteEntity)} +} + +func (_c *UCCEVCInterface_EnergyDemand_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCCEVCInterface_EnergyDemand_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_EnergyDemand_Call) Return(_a0 cemdapi.Demand, _a1 error) *UCCEVCInterface_EnergyDemand_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCCEVCInterface_EnergyDemand_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.Demand, error)) *UCCEVCInterface_EnergyDemand_Call { + _c.Call.Return(run) + return _c +} + +// IncentiveConstraints provides a mock function with given fields: entity +func (_m *UCCEVCInterface) IncentiveConstraints(entity api.EntityRemoteInterface) (cemdapi.IncentiveSlotConstraints, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for IncentiveConstraints") + } + + var r0 cemdapi.IncentiveSlotConstraints + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.IncentiveSlotConstraints, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.IncentiveSlotConstraints); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.IncentiveSlotConstraints) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCCEVCInterface_IncentiveConstraints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IncentiveConstraints' +type UCCEVCInterface_IncentiveConstraints_Call struct { + *mock.Call +} + +// IncentiveConstraints is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) IncentiveConstraints(entity interface{}) *UCCEVCInterface_IncentiveConstraints_Call { + return &UCCEVCInterface_IncentiveConstraints_Call{Call: _e.mock.On("IncentiveConstraints", entity)} +} + +func (_c *UCCEVCInterface_IncentiveConstraints_Call) Run(run func(entity api.EntityRemoteInterface)) *UCCEVCInterface_IncentiveConstraints_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_IncentiveConstraints_Call) Return(_a0 cemdapi.IncentiveSlotConstraints, _a1 error) *UCCEVCInterface_IncentiveConstraints_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCCEVCInterface_IncentiveConstraints_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.IncentiveSlotConstraints, error)) *UCCEVCInterface_IncentiveConstraints_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCCEVCInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCCEVCInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCCEVCInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCCEVCInterface_IsUseCaseSupported_Call { + return &UCCEVCInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCCEVCInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCCEVCInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCCEVCInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCCEVCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCCEVCInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// TimeSlotConstraints provides a mock function with given fields: entity +func (_m *UCCEVCInterface) TimeSlotConstraints(entity api.EntityRemoteInterface) (cemdapi.TimeSlotConstraints, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for TimeSlotConstraints") + } + + var r0 cemdapi.TimeSlotConstraints + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.TimeSlotConstraints, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.TimeSlotConstraints); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.TimeSlotConstraints) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCCEVCInterface_TimeSlotConstraints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TimeSlotConstraints' +type UCCEVCInterface_TimeSlotConstraints_Call struct { + *mock.Call +} + +// TimeSlotConstraints is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCCEVCInterface_Expecter) TimeSlotConstraints(entity interface{}) *UCCEVCInterface_TimeSlotConstraints_Call { + return &UCCEVCInterface_TimeSlotConstraints_Call{Call: _e.mock.On("TimeSlotConstraints", entity)} +} + +func (_c *UCCEVCInterface_TimeSlotConstraints_Call) Run(run func(entity api.EntityRemoteInterface)) *UCCEVCInterface_TimeSlotConstraints_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCCEVCInterface_TimeSlotConstraints_Call) Return(_a0 cemdapi.TimeSlotConstraints, _a1 error) *UCCEVCInterface_TimeSlotConstraints_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCCEVCInterface_TimeSlotConstraints_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.TimeSlotConstraints, error)) *UCCEVCInterface_TimeSlotConstraints_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCCEVCInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCCEVCInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCCEVCInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCCEVCInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCCEVCInterface_UpdateUseCaseAvailability_Call { + return &UCCEVCInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCCEVCInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCCEVCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCCEVCInterface_UpdateUseCaseAvailability_Call) Return() *UCCEVCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCCEVCInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCCEVCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCCEVCInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCCEVCInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCCEVCInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCCEVCInterface_Expecter) UseCaseName() *UCCEVCInterface_UseCaseName_Call { + return &UCCEVCInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCCEVCInterface_UseCaseName_Call) Run(run func()) *UCCEVCInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCCEVCInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCCEVCInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCCEVCInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCCEVCInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// WriteIncentiveTableDescriptions provides a mock function with given fields: entity, data +func (_m *UCCEVCInterface) WriteIncentiveTableDescriptions(entity api.EntityRemoteInterface, data []cemdapi.IncentiveTariffDescription) error { + ret := _m.Called(entity, data) + + if len(ret) == 0 { + panic("no return value specified for WriteIncentiveTableDescriptions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.IncentiveTariffDescription) error); ok { + r0 = rf(entity, data) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCCEVCInterface_WriteIncentiveTableDescriptions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteIncentiveTableDescriptions' +type UCCEVCInterface_WriteIncentiveTableDescriptions_Call struct { + *mock.Call +} + +// WriteIncentiveTableDescriptions is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - data []cemdapi.IncentiveTariffDescription +func (_e *UCCEVCInterface_Expecter) WriteIncentiveTableDescriptions(entity interface{}, data interface{}) *UCCEVCInterface_WriteIncentiveTableDescriptions_Call { + return &UCCEVCInterface_WriteIncentiveTableDescriptions_Call{Call: _e.mock.On("WriteIncentiveTableDescriptions", entity, data)} +} + +func (_c *UCCEVCInterface_WriteIncentiveTableDescriptions_Call) Run(run func(entity api.EntityRemoteInterface, data []cemdapi.IncentiveTariffDescription)) *UCCEVCInterface_WriteIncentiveTableDescriptions_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].([]cemdapi.IncentiveTariffDescription)) + }) + return _c +} + +func (_c *UCCEVCInterface_WriteIncentiveTableDescriptions_Call) Return(_a0 error) *UCCEVCInterface_WriteIncentiveTableDescriptions_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCCEVCInterface_WriteIncentiveTableDescriptions_Call) RunAndReturn(run func(api.EntityRemoteInterface, []cemdapi.IncentiveTariffDescription) error) *UCCEVCInterface_WriteIncentiveTableDescriptions_Call { + _c.Call.Return(run) + return _c +} + +// WriteIncentives provides a mock function with given fields: entity, data +func (_m *UCCEVCInterface) WriteIncentives(entity api.EntityRemoteInterface, data []cemdapi.DurationSlotValue) error { + ret := _m.Called(entity, data) + + if len(ret) == 0 { + panic("no return value specified for WriteIncentives") + } + + var r0 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.DurationSlotValue) error); ok { + r0 = rf(entity, data) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCCEVCInterface_WriteIncentives_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteIncentives' +type UCCEVCInterface_WriteIncentives_Call struct { + *mock.Call +} + +// WriteIncentives is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - data []cemdapi.DurationSlotValue +func (_e *UCCEVCInterface_Expecter) WriteIncentives(entity interface{}, data interface{}) *UCCEVCInterface_WriteIncentives_Call { + return &UCCEVCInterface_WriteIncentives_Call{Call: _e.mock.On("WriteIncentives", entity, data)} +} + +func (_c *UCCEVCInterface_WriteIncentives_Call) Run(run func(entity api.EntityRemoteInterface, data []cemdapi.DurationSlotValue)) *UCCEVCInterface_WriteIncentives_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].([]cemdapi.DurationSlotValue)) + }) + return _c +} + +func (_c *UCCEVCInterface_WriteIncentives_Call) Return(_a0 error) *UCCEVCInterface_WriteIncentives_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCCEVCInterface_WriteIncentives_Call) RunAndReturn(run func(api.EntityRemoteInterface, []cemdapi.DurationSlotValue) error) *UCCEVCInterface_WriteIncentives_Call { + _c.Call.Return(run) + return _c +} + +// WritePowerLimits provides a mock function with given fields: entity, data +func (_m *UCCEVCInterface) WritePowerLimits(entity api.EntityRemoteInterface, data []cemdapi.DurationSlotValue) error { + ret := _m.Called(entity, data) + + if len(ret) == 0 { + panic("no return value specified for WritePowerLimits") + } + + var r0 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.DurationSlotValue) error); ok { + r0 = rf(entity, data) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCCEVCInterface_WritePowerLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WritePowerLimits' +type UCCEVCInterface_WritePowerLimits_Call struct { + *mock.Call +} + +// WritePowerLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - data []cemdapi.DurationSlotValue +func (_e *UCCEVCInterface_Expecter) WritePowerLimits(entity interface{}, data interface{}) *UCCEVCInterface_WritePowerLimits_Call { + return &UCCEVCInterface_WritePowerLimits_Call{Call: _e.mock.On("WritePowerLimits", entity, data)} +} + +func (_c *UCCEVCInterface_WritePowerLimits_Call) Run(run func(entity api.EntityRemoteInterface, data []cemdapi.DurationSlotValue)) *UCCEVCInterface_WritePowerLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].([]cemdapi.DurationSlotValue)) + }) + return _c +} + +func (_c *UCCEVCInterface_WritePowerLimits_Call) Return(_a0 error) *UCCEVCInterface_WritePowerLimits_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCCEVCInterface_WritePowerLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface, []cemdapi.DurationSlotValue) error) *UCCEVCInterface_WritePowerLimits_Call { + _c.Call.Return(run) + return _c +} + +// NewUCCEVCInterface creates a new instance of UCCEVCInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCCEVCInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCCEVCInterface { + mock := &UCCEVCInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCEVCCInterface.go b/mocks/UCEVCCInterface.go new file mode 100644 index 0000000..e00c572 --- /dev/null +++ b/mocks/UCEVCCInterface.go @@ -0,0 +1,704 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCEVCCInterface is an autogenerated mock type for the UCEVCCInterface type +type UCEVCCInterface struct { + mock.Mock +} + +type UCEVCCInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCEVCCInterface) EXPECT() *UCEVCCInterface_Expecter { + return &UCEVCCInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCEVCCInterface) AddFeatures() { + _m.Called() +} + +// UCEVCCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCEVCCInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCEVCCInterface_Expecter) AddFeatures() *UCEVCCInterface_AddFeatures_Call { + return &UCEVCCInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCEVCCInterface_AddFeatures_Call) Run(run func()) *UCEVCCInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVCCInterface_AddFeatures_Call) Return() *UCEVCCInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVCCInterface_AddFeatures_Call) RunAndReturn(run func()) *UCEVCCInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCEVCCInterface) AddUseCase() { + _m.Called() +} + +// UCEVCCInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCEVCCInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCEVCCInterface_Expecter) AddUseCase() *UCEVCCInterface_AddUseCase_Call { + return &UCEVCCInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCEVCCInterface_AddUseCase_Call) Run(run func()) *UCEVCCInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVCCInterface_AddUseCase_Call) Return() *UCEVCCInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVCCInterface_AddUseCase_Call) RunAndReturn(run func()) *UCEVCCInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// AsymmetricChargingSupport provides a mock function with given fields: entity +func (_m *UCEVCCInterface) AsymmetricChargingSupport(entity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for AsymmetricChargingSupport") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCCInterface_AsymmetricChargingSupport_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AsymmetricChargingSupport' +type UCEVCCInterface_AsymmetricChargingSupport_Call struct { + *mock.Call +} + +// AsymmetricChargingSupport is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) AsymmetricChargingSupport(entity interface{}) *UCEVCCInterface_AsymmetricChargingSupport_Call { + return &UCEVCCInterface_AsymmetricChargingSupport_Call{Call: _e.mock.On("AsymmetricChargingSupport", entity)} +} + +func (_c *UCEVCCInterface_AsymmetricChargingSupport_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_AsymmetricChargingSupport_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_AsymmetricChargingSupport_Call) Return(_a0 bool, _a1 error) *UCEVCCInterface_AsymmetricChargingSupport_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCCInterface_AsymmetricChargingSupport_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCEVCCInterface_AsymmetricChargingSupport_Call { + _c.Call.Return(run) + return _c +} + +// ChargeState provides a mock function with given fields: entity +func (_m *UCEVCCInterface) ChargeState(entity api.EntityRemoteInterface) (cemdapi.EVChargeStateType, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ChargeState") + } + + var r0 cemdapi.EVChargeStateType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.EVChargeStateType, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.EVChargeStateType); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.EVChargeStateType) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCCInterface_ChargeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChargeState' +type UCEVCCInterface_ChargeState_Call struct { + *mock.Call +} + +// ChargeState is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) ChargeState(entity interface{}) *UCEVCCInterface_ChargeState_Call { + return &UCEVCCInterface_ChargeState_Call{Call: _e.mock.On("ChargeState", entity)} +} + +func (_c *UCEVCCInterface_ChargeState_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_ChargeState_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_ChargeState_Call) Return(_a0 cemdapi.EVChargeStateType, _a1 error) *UCEVCCInterface_ChargeState_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCCInterface_ChargeState_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.EVChargeStateType, error)) *UCEVCCInterface_ChargeState_Call { + _c.Call.Return(run) + return _c +} + +// CommunicationStandard provides a mock function with given fields: entity +func (_m *UCEVCCInterface) CommunicationStandard(entity api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CommunicationStandard") + } + + var r0 model.DeviceConfigurationKeyValueStringType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) model.DeviceConfigurationKeyValueStringType); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(model.DeviceConfigurationKeyValueStringType) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCCInterface_CommunicationStandard_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommunicationStandard' +type UCEVCCInterface_CommunicationStandard_Call struct { + *mock.Call +} + +// CommunicationStandard is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) CommunicationStandard(entity interface{}) *UCEVCCInterface_CommunicationStandard_Call { + return &UCEVCCInterface_CommunicationStandard_Call{Call: _e.mock.On("CommunicationStandard", entity)} +} + +func (_c *UCEVCCInterface_CommunicationStandard_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_CommunicationStandard_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_CommunicationStandard_Call) Return(_a0 model.DeviceConfigurationKeyValueStringType, _a1 error) *UCEVCCInterface_CommunicationStandard_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCCInterface_CommunicationStandard_Call) RunAndReturn(run func(api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error)) *UCEVCCInterface_CommunicationStandard_Call { + _c.Call.Return(run) + return _c +} + +// CurrentLimits provides a mock function with given fields: entity +func (_m *UCEVCCInterface) CurrentLimits(entity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CurrentLimits") + } + + var r0 []float64 + var r1 []float64 + var r2 []float64 + var r3 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) []float64); ok { + r1 = rf(entity) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]float64) + } + } + + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) []float64); ok { + r2 = rf(entity) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).([]float64) + } + } + + if rf, ok := ret.Get(3).(func(api.EntityRemoteInterface) error); ok { + r3 = rf(entity) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// UCEVCCInterface_CurrentLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentLimits' +type UCEVCCInterface_CurrentLimits_Call struct { + *mock.Call +} + +// CurrentLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) CurrentLimits(entity interface{}) *UCEVCCInterface_CurrentLimits_Call { + return &UCEVCCInterface_CurrentLimits_Call{Call: _e.mock.On("CurrentLimits", entity)} +} + +func (_c *UCEVCCInterface_CurrentLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_CurrentLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_CurrentLimits_Call) Return(_a0 []float64, _a1 []float64, _a2 []float64, _a3 error) *UCEVCCInterface_CurrentLimits_Call { + _c.Call.Return(_a0, _a1, _a2, _a3) + return _c +} + +func (_c *UCEVCCInterface_CurrentLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)) *UCEVCCInterface_CurrentLimits_Call { + _c.Call.Return(run) + return _c +} + +// EVConnected provides a mock function with given fields: entity +func (_m *UCEVCCInterface) EVConnected(entity api.EntityRemoteInterface) bool { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EVConnected") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// UCEVCCInterface_EVConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EVConnected' +type UCEVCCInterface_EVConnected_Call struct { + *mock.Call +} + +// EVConnected is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) EVConnected(entity interface{}) *UCEVCCInterface_EVConnected_Call { + return &UCEVCCInterface_EVConnected_Call{Call: _e.mock.On("EVConnected", entity)} +} + +func (_c *UCEVCCInterface_EVConnected_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_EVConnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_EVConnected_Call) Return(_a0 bool) *UCEVCCInterface_EVConnected_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCEVCCInterface_EVConnected_Call) RunAndReturn(run func(api.EntityRemoteInterface) bool) *UCEVCCInterface_EVConnected_Call { + _c.Call.Return(run) + return _c +} + +// Identifications provides a mock function with given fields: entity +func (_m *UCEVCCInterface) Identifications(entity api.EntityRemoteInterface) ([]cemdapi.IdentificationItem, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Identifications") + } + + var r0 []cemdapi.IdentificationItem + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]cemdapi.IdentificationItem, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []cemdapi.IdentificationItem); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]cemdapi.IdentificationItem) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCCInterface_Identifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Identifications' +type UCEVCCInterface_Identifications_Call struct { + *mock.Call +} + +// Identifications is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) Identifications(entity interface{}) *UCEVCCInterface_Identifications_Call { + return &UCEVCCInterface_Identifications_Call{Call: _e.mock.On("Identifications", entity)} +} + +func (_c *UCEVCCInterface_Identifications_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_Identifications_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_Identifications_Call) Return(_a0 []cemdapi.IdentificationItem, _a1 error) *UCEVCCInterface_Identifications_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCCInterface_Identifications_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]cemdapi.IdentificationItem, error)) *UCEVCCInterface_Identifications_Call { + _c.Call.Return(run) + return _c +} + +// IsInSleepMode provides a mock function with given fields: entity +func (_m *UCEVCCInterface) IsInSleepMode(entity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for IsInSleepMode") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCCInterface_IsInSleepMode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsInSleepMode' +type UCEVCCInterface_IsInSleepMode_Call struct { + *mock.Call +} + +// IsInSleepMode is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) IsInSleepMode(entity interface{}) *UCEVCCInterface_IsInSleepMode_Call { + return &UCEVCCInterface_IsInSleepMode_Call{Call: _e.mock.On("IsInSleepMode", entity)} +} + +func (_c *UCEVCCInterface_IsInSleepMode_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_IsInSleepMode_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_IsInSleepMode_Call) Return(_a0 bool, _a1 error) *UCEVCCInterface_IsInSleepMode_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCCInterface_IsInSleepMode_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCEVCCInterface_IsInSleepMode_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCEVCCInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCCInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCEVCCInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCEVCCInterface_IsUseCaseSupported_Call { + return &UCEVCCInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCEVCCInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCEVCCInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCEVCCInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCEVCCInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// ManufacturerData provides a mock function with given fields: entity +func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (string, string, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ManufacturerData") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (string, string, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) string); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) string); ok { + r1 = rf(entity) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) error); ok { + r2 = rf(entity) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCEVCCInterface_ManufacturerData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ManufacturerData' +type UCEVCCInterface_ManufacturerData_Call struct { + *mock.Call +} + +// ManufacturerData is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCCInterface_Expecter) ManufacturerData(entity interface{}) *UCEVCCInterface_ManufacturerData_Call { + return &UCEVCCInterface_ManufacturerData_Call{Call: _e.mock.On("ManufacturerData", entity)} +} + +func (_c *UCEVCCInterface_ManufacturerData_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_ManufacturerData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 string, _a1 string, _a2 error) *UCEVCCInterface_ManufacturerData_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (string, string, error)) *UCEVCCInterface_ManufacturerData_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCEVCCInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCEVCCInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCEVCCInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCEVCCInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCEVCCInterface_UpdateUseCaseAvailability_Call { + return &UCEVCCInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCEVCCInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCEVCCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCEVCCInterface_UpdateUseCaseAvailability_Call) Return() *UCEVCCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVCCInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCEVCCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCEVCCInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCEVCCInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCEVCCInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCEVCCInterface_Expecter) UseCaseName() *UCEVCCInterface_UseCaseName_Call { + return &UCEVCCInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCEVCCInterface_UseCaseName_Call) Run(run func()) *UCEVCCInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVCCInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCEVCCInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCEVCCInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCEVCCInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCEVCCInterface creates a new instance of UCEVCCInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCEVCCInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCEVCCInterface { + mock := &UCEVCCInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCEVCEMInterface.go b/mocks/UCEVCEMInterface.go new file mode 100644 index 0000000..ecb5ef9 --- /dev/null +++ b/mocks/UCEVCEMInterface.go @@ -0,0 +1,463 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCEVCEMInterface is an autogenerated mock type for the UCEVCEMInterface type +type UCEVCEMInterface struct { + mock.Mock +} + +type UCEVCEMInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCEVCEMInterface) EXPECT() *UCEVCEMInterface_Expecter { + return &UCEVCEMInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCEVCEMInterface) AddFeatures() { + _m.Called() +} + +// UCEVCEMInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCEVCEMInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCEVCEMInterface_Expecter) AddFeatures() *UCEVCEMInterface_AddFeatures_Call { + return &UCEVCEMInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCEVCEMInterface_AddFeatures_Call) Run(run func()) *UCEVCEMInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVCEMInterface_AddFeatures_Call) Return() *UCEVCEMInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVCEMInterface_AddFeatures_Call) RunAndReturn(run func()) *UCEVCEMInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCEVCEMInterface) AddUseCase() { + _m.Called() +} + +// UCEVCEMInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCEVCEMInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCEVCEMInterface_Expecter) AddUseCase() *UCEVCEMInterface_AddUseCase_Call { + return &UCEVCEMInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCEVCEMInterface_AddUseCase_Call) Run(run func()) *UCEVCEMInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVCEMInterface_AddUseCase_Call) Return() *UCEVCEMInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVCEMInterface_AddUseCase_Call) RunAndReturn(run func()) *UCEVCEMInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// CurrentPerPhase provides a mock function with given fields: entity +func (_m *UCEVCEMInterface) CurrentPerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CurrentPerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCEMInterface_CurrentPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentPerPhase' +type UCEVCEMInterface_CurrentPerPhase_Call struct { + *mock.Call +} + +// CurrentPerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCEMInterface_Expecter) CurrentPerPhase(entity interface{}) *UCEVCEMInterface_CurrentPerPhase_Call { + return &UCEVCEMInterface_CurrentPerPhase_Call{Call: _e.mock.On("CurrentPerPhase", entity)} +} + +func (_c *UCEVCEMInterface_CurrentPerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCEMInterface_CurrentPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCEMInterface_CurrentPerPhase_Call) Return(_a0 []float64, _a1 error) *UCEVCEMInterface_CurrentPerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCEMInterface_CurrentPerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCEVCEMInterface_CurrentPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// EnergyCharged provides a mock function with given fields: entity +func (_m *UCEVCEMInterface) EnergyCharged(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyCharged") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCEMInterface_EnergyCharged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyCharged' +type UCEVCEMInterface_EnergyCharged_Call struct { + *mock.Call +} + +// EnergyCharged is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCEMInterface_Expecter) EnergyCharged(entity interface{}) *UCEVCEMInterface_EnergyCharged_Call { + return &UCEVCEMInterface_EnergyCharged_Call{Call: _e.mock.On("EnergyCharged", entity)} +} + +func (_c *UCEVCEMInterface_EnergyCharged_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCEMInterface_EnergyCharged_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCEMInterface_EnergyCharged_Call) Return(_a0 float64, _a1 error) *UCEVCEMInterface_EnergyCharged_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCEMInterface_EnergyCharged_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCEVCEMInterface_EnergyCharged_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCEVCEMInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCEMInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCEVCEMInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCEVCEMInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCEVCEMInterface_IsUseCaseSupported_Call { + return &UCEVCEMInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCEVCEMInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCEVCEMInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCEMInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCEVCEMInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCEMInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCEVCEMInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// PhasesConnected provides a mock function with given fields: entity +func (_m *UCEVCEMInterface) PhasesConnected(entity api.EntityRemoteInterface) (uint, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PhasesConnected") + } + + var r0 uint + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (uint, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) uint); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(uint) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCEMInterface_PhasesConnected_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PhasesConnected' +type UCEVCEMInterface_PhasesConnected_Call struct { + *mock.Call +} + +// PhasesConnected is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCEMInterface_Expecter) PhasesConnected(entity interface{}) *UCEVCEMInterface_PhasesConnected_Call { + return &UCEVCEMInterface_PhasesConnected_Call{Call: _e.mock.On("PhasesConnected", entity)} +} + +func (_c *UCEVCEMInterface_PhasesConnected_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCEMInterface_PhasesConnected_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCEMInterface_PhasesConnected_Call) Return(_a0 uint, _a1 error) *UCEVCEMInterface_PhasesConnected_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCEMInterface_PhasesConnected_Call) RunAndReturn(run func(api.EntityRemoteInterface) (uint, error)) *UCEVCEMInterface_PhasesConnected_Call { + _c.Call.Return(run) + return _c +} + +// PowerPerPhase provides a mock function with given fields: entity +func (_m *UCEVCEMInterface) PowerPerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PowerPerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVCEMInterface_PowerPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerPerPhase' +type UCEVCEMInterface_PowerPerPhase_Call struct { + *mock.Call +} + +// PowerPerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVCEMInterface_Expecter) PowerPerPhase(entity interface{}) *UCEVCEMInterface_PowerPerPhase_Call { + return &UCEVCEMInterface_PowerPerPhase_Call{Call: _e.mock.On("PowerPerPhase", entity)} +} + +func (_c *UCEVCEMInterface_PowerPerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCEMInterface_PowerPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVCEMInterface_PowerPerPhase_Call) Return(_a0 []float64, _a1 error) *UCEVCEMInterface_PowerPerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVCEMInterface_PowerPerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCEVCEMInterface_PowerPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCEVCEMInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCEVCEMInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCEVCEMInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCEVCEMInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCEVCEMInterface_UpdateUseCaseAvailability_Call { + return &UCEVCEMInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCEVCEMInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCEVCEMInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCEVCEMInterface_UpdateUseCaseAvailability_Call) Return() *UCEVCEMInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVCEMInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCEVCEMInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCEVCEMInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCEVCEMInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCEVCEMInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCEVCEMInterface_Expecter) UseCaseName() *UCEVCEMInterface_UseCaseName_Call { + return &UCEVCEMInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCEVCEMInterface_UseCaseName_Call) Run(run func()) *UCEVCEMInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVCEMInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCEVCEMInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCEVCEMInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCEVCEMInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCEVCEMInterface creates a new instance of UCEVCEMInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCEVCEMInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCEVCEMInterface { + mock := &UCEVCEMInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCEVSECCInterface.go b/mocks/UCEVSECCInterface.go new file mode 100644 index 0000000..a5ae5cb --- /dev/null +++ b/mocks/UCEVSECCInterface.go @@ -0,0 +1,361 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCEVSECCInterface is an autogenerated mock type for the UCEVSECCInterface type +type UCEVSECCInterface struct { + mock.Mock +} + +type UCEVSECCInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCEVSECCInterface) EXPECT() *UCEVSECCInterface_Expecter { + return &UCEVSECCInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCEVSECCInterface) AddFeatures() { + _m.Called() +} + +// UCEVSECCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCEVSECCInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCEVSECCInterface_Expecter) AddFeatures() *UCEVSECCInterface_AddFeatures_Call { + return &UCEVSECCInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCEVSECCInterface_AddFeatures_Call) Run(run func()) *UCEVSECCInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVSECCInterface_AddFeatures_Call) Return() *UCEVSECCInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVSECCInterface_AddFeatures_Call) RunAndReturn(run func()) *UCEVSECCInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCEVSECCInterface) AddUseCase() { + _m.Called() +} + +// UCEVSECCInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCEVSECCInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCEVSECCInterface_Expecter) AddUseCase() *UCEVSECCInterface_AddUseCase_Call { + return &UCEVSECCInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCEVSECCInterface_AddUseCase_Call) Run(run func()) *UCEVSECCInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVSECCInterface_AddUseCase_Call) Return() *UCEVSECCInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVSECCInterface_AddUseCase_Call) RunAndReturn(run func()) *UCEVSECCInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCEVSECCInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVSECCInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCEVSECCInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCEVSECCInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCEVSECCInterface_IsUseCaseSupported_Call { + return &UCEVSECCInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCEVSECCInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCEVSECCInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVSECCInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCEVSECCInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVSECCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCEVSECCInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// ManufacturerData provides a mock function with given fields: entity +func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (string, string, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ManufacturerData") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (string, string, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) string); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) string); ok { + r1 = rf(entity) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) error); ok { + r2 = rf(entity) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCEVSECCInterface_ManufacturerData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ManufacturerData' +type UCEVSECCInterface_ManufacturerData_Call struct { + *mock.Call +} + +// ManufacturerData is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVSECCInterface_Expecter) ManufacturerData(entity interface{}) *UCEVSECCInterface_ManufacturerData_Call { + return &UCEVSECCInterface_ManufacturerData_Call{Call: _e.mock.On("ManufacturerData", entity)} +} + +func (_c *UCEVSECCInterface_ManufacturerData_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVSECCInterface_ManufacturerData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 string, _a1 string, _a2 error) *UCEVSECCInterface_ManufacturerData_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (string, string, error)) *UCEVSECCInterface_ManufacturerData_Call { + _c.Call.Return(run) + return _c +} + +// OperatingState provides a mock function with given fields: entity +func (_m *UCEVSECCInterface) OperatingState(entity api.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for OperatingState") + } + + var r0 model.DeviceDiagnosisOperatingStateType + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) model.DeviceDiagnosisOperatingStateType); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(model.DeviceDiagnosisOperatingStateType) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) string); ok { + r1 = rf(entity) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) error); ok { + r2 = rf(entity) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCEVSECCInterface_OperatingState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OperatingState' +type UCEVSECCInterface_OperatingState_Call struct { + *mock.Call +} + +// OperatingState is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVSECCInterface_Expecter) OperatingState(entity interface{}) *UCEVSECCInterface_OperatingState_Call { + return &UCEVSECCInterface_OperatingState_Call{Call: _e.mock.On("OperatingState", entity)} +} + +func (_c *UCEVSECCInterface_OperatingState_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVSECCInterface_OperatingState_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVSECCInterface_OperatingState_Call) Return(_a0 model.DeviceDiagnosisOperatingStateType, _a1 string, _a2 error) *UCEVSECCInterface_OperatingState_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *UCEVSECCInterface_OperatingState_Call) RunAndReturn(run func(api.EntityRemoteInterface) (model.DeviceDiagnosisOperatingStateType, string, error)) *UCEVSECCInterface_OperatingState_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCEVSECCInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCEVSECCInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCEVSECCInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCEVSECCInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCEVSECCInterface_UpdateUseCaseAvailability_Call { + return &UCEVSECCInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCEVSECCInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCEVSECCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCEVSECCInterface_UpdateUseCaseAvailability_Call) Return() *UCEVSECCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVSECCInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCEVSECCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCEVSECCInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCEVSECCInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCEVSECCInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCEVSECCInterface_Expecter) UseCaseName() *UCEVSECCInterface_UseCaseName_Call { + return &UCEVSECCInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCEVSECCInterface_UseCaseName_Call) Run(run func()) *UCEVSECCInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVSECCInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCEVSECCInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCEVSECCInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCEVSECCInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCEVSECCInterface creates a new instance of UCEVSECCInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCEVSECCInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCEVSECCInterface { + mock := &UCEVSECCInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCEVSOCInterface.go b/mocks/UCEVSOCInterface.go new file mode 100644 index 0000000..2a5b041 --- /dev/null +++ b/mocks/UCEVSOCInterface.go @@ -0,0 +1,291 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCEVSOCInterface is an autogenerated mock type for the UCEVSOCInterface type +type UCEVSOCInterface struct { + mock.Mock +} + +type UCEVSOCInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCEVSOCInterface) EXPECT() *UCEVSOCInterface_Expecter { + return &UCEVSOCInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCEVSOCInterface) AddFeatures() { + _m.Called() +} + +// UCEVSOCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCEVSOCInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCEVSOCInterface_Expecter) AddFeatures() *UCEVSOCInterface_AddFeatures_Call { + return &UCEVSOCInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCEVSOCInterface_AddFeatures_Call) Run(run func()) *UCEVSOCInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVSOCInterface_AddFeatures_Call) Return() *UCEVSOCInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVSOCInterface_AddFeatures_Call) RunAndReturn(run func()) *UCEVSOCInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCEVSOCInterface) AddUseCase() { + _m.Called() +} + +// UCEVSOCInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCEVSOCInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCEVSOCInterface_Expecter) AddUseCase() *UCEVSOCInterface_AddUseCase_Call { + return &UCEVSOCInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCEVSOCInterface_AddUseCase_Call) Run(run func()) *UCEVSOCInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVSOCInterface_AddUseCase_Call) Return() *UCEVSOCInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVSOCInterface_AddUseCase_Call) RunAndReturn(run func()) *UCEVSOCInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCEVSOCInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVSOCInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCEVSOCInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCEVSOCInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCEVSOCInterface_IsUseCaseSupported_Call { + return &UCEVSOCInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCEVSOCInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCEVSOCInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVSOCInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCEVSOCInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVSOCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCEVSOCInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// StateOfCharge provides a mock function with given fields: entity +func (_m *UCEVSOCInterface) StateOfCharge(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for StateOfCharge") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCEVSOCInterface_StateOfCharge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateOfCharge' +type UCEVSOCInterface_StateOfCharge_Call struct { + *mock.Call +} + +// StateOfCharge is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCEVSOCInterface_Expecter) StateOfCharge(entity interface{}) *UCEVSOCInterface_StateOfCharge_Call { + return &UCEVSOCInterface_StateOfCharge_Call{Call: _e.mock.On("StateOfCharge", entity)} +} + +func (_c *UCEVSOCInterface_StateOfCharge_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVSOCInterface_StateOfCharge_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCEVSOCInterface_StateOfCharge_Call) Return(_a0 float64, _a1 error) *UCEVSOCInterface_StateOfCharge_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCEVSOCInterface_StateOfCharge_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCEVSOCInterface_StateOfCharge_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCEVSOCInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCEVSOCInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCEVSOCInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCEVSOCInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCEVSOCInterface_UpdateUseCaseAvailability_Call { + return &UCEVSOCInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCEVSOCInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCEVSOCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCEVSOCInterface_UpdateUseCaseAvailability_Call) Return() *UCEVSOCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCEVSOCInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCEVSOCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCEVSOCInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCEVSOCInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCEVSOCInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCEVSOCInterface_Expecter) UseCaseName() *UCEVSOCInterface_UseCaseName_Call { + return &UCEVSOCInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCEVSOCInterface_UseCaseName_Call) Run(run func()) *UCEVSOCInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCEVSOCInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCEVSOCInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCEVSOCInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCEVSOCInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCEVSOCInterface creates a new instance of UCEVSOCInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCEVSOCInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCEVSOCInterface { + mock := &UCEVSOCInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCLCPInterface.go b/mocks/UCLCPInterface.go new file mode 100644 index 0000000..6cf65f7 --- /dev/null +++ b/mocks/UCLCPInterface.go @@ -0,0 +1,696 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" + + time "time" +) + +// UCLCPInterface is an autogenerated mock type for the UCLCPInterface type +type UCLCPInterface struct { + mock.Mock +} + +type UCLCPInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCLCPInterface) EXPECT() *UCLCPInterface_Expecter { + return &UCLCPInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCLCPInterface) AddFeatures() { + _m.Called() +} + +// UCLCPInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCLCPInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCLCPInterface_Expecter) AddFeatures() *UCLCPInterface_AddFeatures_Call { + return &UCLCPInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCLCPInterface_AddFeatures_Call) Run(run func()) *UCLCPInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPInterface_AddFeatures_Call) Return() *UCLCPInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLCPInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLCPInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCLCPInterface) AddUseCase() { + _m.Called() +} + +// UCLCPInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCLCPInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCLCPInterface_Expecter) AddUseCase() *UCLCPInterface_AddUseCase_Call { + return &UCLCPInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCLCPInterface_AddUseCase_Call) Run(run func()) *UCLCPInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPInterface_AddUseCase_Call) Return() *UCLCPInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLCPInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLCPInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// ContractualConsumptionNominalMax provides a mock function with given fields: entity +func (_m *UCLCPInterface) ContractualConsumptionNominalMax(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ContractualConsumptionNominalMax") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_ContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ContractualConsumptionNominalMax' +type UCLCPInterface_ContractualConsumptionNominalMax_Call struct { + *mock.Call +} + +// ContractualConsumptionNominalMax is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLCPInterface_Expecter) ContractualConsumptionNominalMax(entity interface{}) *UCLCPInterface_ContractualConsumptionNominalMax_Call { + return &UCLCPInterface_ContractualConsumptionNominalMax_Call{Call: _e.mock.On("ContractualConsumptionNominalMax", entity)} +} + +func (_c *UCLCPInterface_ContractualConsumptionNominalMax_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_ContractualConsumptionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPInterface_ContractualConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPInterface_ContractualConsumptionNominalMax_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_ContractualConsumptionNominalMax_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLCPInterface_ContractualConsumptionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity +func (_m *UCLCPInterface) FailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for FailsafeConsumptionActivePowerLimit") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeConsumptionActivePowerLimit' +type UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call struct { + *mock.Call +} + +// FailsafeConsumptionActivePowerLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLCPInterface_Expecter) FailsafeConsumptionActivePowerLimit(entity interface{}) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { + return &UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("FailsafeConsumptionActivePowerLimit", entity)} +} + +func (_c *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call) Return(_a0 float64, _a1 error) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeDurationMinimum provides a mock function with given fields: entity +func (_m *UCLCPInterface) FailsafeDurationMinimum(entity api.EntityRemoteInterface) (time.Duration, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for FailsafeDurationMinimum") + } + + var r0 time.Duration + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (time.Duration, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) time.Duration); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(time.Duration) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' +type UCLCPInterface_FailsafeDurationMinimum_Call struct { + *mock.Call +} + +// FailsafeDurationMinimum is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLCPInterface_Expecter) FailsafeDurationMinimum(entity interface{}) *UCLCPInterface_FailsafeDurationMinimum_Call { + return &UCLCPInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum", entity)} +} + +func (_c *UCLCPInterface_FailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_FailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPInterface_FailsafeDurationMinimum_Call) Return(_a0 time.Duration, _a1 error) *UCLCPInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface) (time.Duration, error)) *UCLCPInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCLCPInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCLCPInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCLCPInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLCPInterface_IsUseCaseSupported_Call { + return &UCLCPInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCLCPInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLCPInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLCPInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLCPInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// LoadControlLimit provides a mock function with given fields: entity +func (_m *UCLCPInterface) LoadControlLimit(entity api.EntityRemoteInterface) (cemdapi.LoadLimit, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for LoadControlLimit") + } + + var r0 cemdapi.LoadLimit + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.LoadLimit); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.LoadLimit) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' +type UCLCPInterface_LoadControlLimit_Call struct { + *mock.Call +} + +// LoadControlLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLCPInterface_Expecter) LoadControlLimit(entity interface{}) *UCLCPInterface_LoadControlLimit_Call { + return &UCLCPInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit", entity)} +} + +func (_c *UCLCPInterface_LoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_LoadControlLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPInterface_LoadControlLimit_Call) Return(limit cemdapi.LoadLimit, resultErr error) *UCLCPInterface_LoadControlLimit_Call { + _c.Call.Return(limit, resultErr) + return _c +} + +func (_c *UCLCPInterface_LoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)) *UCLCPInterface_LoadControlLimit_Call { + _c.Call.Return(run) + return _c +} + +// PowerConsumptionNominalMax provides a mock function with given fields: entity +func (_m *UCLCPInterface) PowerConsumptionNominalMax(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PowerConsumptionNominalMax") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_PowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerConsumptionNominalMax' +type UCLCPInterface_PowerConsumptionNominalMax_Call struct { + *mock.Call +} + +// PowerConsumptionNominalMax is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLCPInterface_Expecter) PowerConsumptionNominalMax(entity interface{}) *UCLCPInterface_PowerConsumptionNominalMax_Call { + return &UCLCPInterface_PowerConsumptionNominalMax_Call{Call: _e.mock.On("PowerConsumptionNominalMax", entity)} +} + +func (_c *UCLCPInterface_PowerConsumptionNominalMax_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_PowerConsumptionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPInterface_PowerConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPInterface_PowerConsumptionNominalMax_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_PowerConsumptionNominalMax_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLCPInterface_PowerConsumptionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCLCPInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCLCPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCLCPInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCLCPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLCPInterface_UpdateUseCaseAvailability_Call { + return &UCLCPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCLCPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCLCPInterface_UpdateUseCaseAvailability_Call) Return() *UCLCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLCPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCLCPInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCLCPInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCLCPInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCLCPInterface_Expecter) UseCaseName() *UCLCPInterface_UseCaseName_Call { + return &UCLCPInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCLCPInterface_UseCaseName_Call) Run(run func()) *UCLCPInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLCPInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCLCPInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLCPInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// WriteFailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity, value +func (_m *UCLCPInterface) WriteFailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { + ret := _m.Called(entity, value) + + if len(ret) == 0 { + panic("no return value specified for WriteFailsafeConsumptionActivePowerLimit") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, float64) (*model.MsgCounterType, error)); ok { + return rf(entity, value) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, float64) *model.MsgCounterType); ok { + r0 = rf(entity, value) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, float64) error); ok { + r1 = rf(entity, value) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeConsumptionActivePowerLimit' +type UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call struct { + *mock.Call +} + +// WriteFailsafeConsumptionActivePowerLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - value float64 +func (_e *UCLCPInterface_Expecter) WriteFailsafeConsumptionActivePowerLimit(entity interface{}, value interface{}) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { + return &UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("WriteFailsafeConsumptionActivePowerLimit", entity, value)} +} + +func (_c *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface, value float64)) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(float64)) + }) + return _c +} + +func (_c *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, float64) (*model.MsgCounterType, error)) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// WriteFailsafeDurationMinimum provides a mock function with given fields: entity, duration +func (_m *UCLCPInterface) WriteFailsafeDurationMinimum(entity api.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) { + ret := _m.Called(entity, duration) + + if len(ret) == 0 { + panic("no return value specified for WriteFailsafeDurationMinimum") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, time.Duration) (*model.MsgCounterType, error)); ok { + return rf(entity, duration) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, time.Duration) *model.MsgCounterType); ok { + r0 = rf(entity, duration) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, time.Duration) error); ok { + r1 = rf(entity, duration) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_WriteFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeDurationMinimum' +type UCLCPInterface_WriteFailsafeDurationMinimum_Call struct { + *mock.Call +} + +// WriteFailsafeDurationMinimum is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - duration time.Duration +func (_e *UCLCPInterface_Expecter) WriteFailsafeDurationMinimum(entity interface{}, duration interface{}) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { + return &UCLCPInterface_WriteFailsafeDurationMinimum_Call{Call: _e.mock.On("WriteFailsafeDurationMinimum", entity, duration)} +} + +func (_c *UCLCPInterface_WriteFailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface, duration time.Duration)) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(time.Duration)) + }) + return _c +} + +func (_c *UCLCPInterface_WriteFailsafeDurationMinimum_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_WriteFailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface, time.Duration) (*model.MsgCounterType, error)) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// WriteLoadControlLimit provides a mock function with given fields: entity, limit +func (_m *UCLCPInterface) WriteLoadControlLimit(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit) (*model.MsgCounterType, error) { + ret := _m.Called(entity, limit) + + if len(ret) == 0 { + panic("no return value specified for WriteLoadControlLimit") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)); ok { + return rf(entity, limit) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) *model.MsgCounterType); ok { + r0 = rf(entity, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) error); ok { + r1 = rf(entity, limit) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPInterface_WriteLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteLoadControlLimit' +type UCLCPInterface_WriteLoadControlLimit_Call struct { + *mock.Call +} + +// WriteLoadControlLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - limit cemdapi.LoadLimit +func (_e *UCLCPInterface_Expecter) WriteLoadControlLimit(entity interface{}, limit interface{}) *UCLCPInterface_WriteLoadControlLimit_Call { + return &UCLCPInterface_WriteLoadControlLimit_Call{Call: _e.mock.On("WriteLoadControlLimit", entity, limit)} +} + +func (_c *UCLCPInterface_WriteLoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit)) *UCLCPInterface_WriteLoadControlLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(cemdapi.LoadLimit)) + }) + return _c +} + +func (_c *UCLCPInterface_WriteLoadControlLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLCPInterface_WriteLoadControlLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPInterface_WriteLoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)) *UCLCPInterface_WriteLoadControlLimit_Call { + _c.Call.Return(run) + return _c +} + +// NewUCLCPInterface creates a new instance of UCLCPInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCLCPInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCLCPInterface { + mock := &UCLCPInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCLCPServerInterface.go b/mocks/UCLCPServerInterface.go new file mode 100644 index 0000000..45b72f8 --- /dev/null +++ b/mocks/UCLCPServerInterface.go @@ -0,0 +1,760 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" + + time "time" +) + +// UCLCPServerInterface is an autogenerated mock type for the UCLCPServerInterface type +type UCLCPServerInterface struct { + mock.Mock +} + +type UCLCPServerInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCLCPServerInterface) EXPECT() *UCLCPServerInterface_Expecter { + return &UCLCPServerInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCLCPServerInterface) AddFeatures() { + _m.Called() +} + +// UCLCPServerInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCLCPServerInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) AddFeatures() *UCLCPServerInterface_AddFeatures_Call { + return &UCLCPServerInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCLCPServerInterface_AddFeatures_Call) Run(run func()) *UCLCPServerInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_AddFeatures_Call) Return() *UCLCPServerInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLCPServerInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLCPServerInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCLCPServerInterface) AddUseCase() { + _m.Called() +} + +// UCLCPServerInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCLCPServerInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) AddUseCase() *UCLCPServerInterface_AddUseCase_Call { + return &UCLCPServerInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCLCPServerInterface_AddUseCase_Call) Run(run func()) *UCLCPServerInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_AddUseCase_Call) Return() *UCLCPServerInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLCPServerInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLCPServerInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// ContractualConsumptionNominalMax provides a mock function with given fields: +func (_m *UCLCPServerInterface) ContractualConsumptionNominalMax() (float64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ContractualConsumptionNominalMax") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func() (float64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPServerInterface_ContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ContractualConsumptionNominalMax' +type UCLCPServerInterface_ContractualConsumptionNominalMax_Call struct { + *mock.Call +} + +// ContractualConsumptionNominalMax is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) ContractualConsumptionNominalMax() *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { + return &UCLCPServerInterface_ContractualConsumptionNominalMax_Call{Call: _e.mock.On("ContractualConsumptionNominalMax")} +} + +func (_c *UCLCPServerInterface_ContractualConsumptionNominalMax_Call) Run(run func()) *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_ContractualConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPServerInterface_ContractualConsumptionNominalMax_Call) RunAndReturn(run func() (float64, error)) *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeConsumptionActivePowerLimit provides a mock function with given fields: +func (_m *UCLCPServerInterface) FailsafeConsumptionActivePowerLimit() (float64, bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for FailsafeConsumptionActivePowerLimit") + } + + var r0 float64 + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func() (float64, bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func() bool); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeConsumptionActivePowerLimit' +type UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call struct { + *mock.Call +} + +// FailsafeConsumptionActivePowerLimit is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) FailsafeConsumptionActivePowerLimit() *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { + return &UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("FailsafeConsumptionActivePowerLimit")} +} + +func (_c *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call) Run(run func()) *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call) Return(value float64, isChangeable bool, resultErr error) *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(value, isChangeable, resultErr) + return _c +} + +func (_c *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func() (float64, bool, error)) *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeDurationMinimum provides a mock function with given fields: +func (_m *UCLCPServerInterface) FailsafeDurationMinimum() (time.Duration, bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for FailsafeDurationMinimum") + } + + var r0 time.Duration + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func() (time.Duration, bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() time.Duration); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Duration) + } + + if rf, ok := ret.Get(1).(func() bool); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCLCPServerInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' +type UCLCPServerInterface_FailsafeDurationMinimum_Call struct { + *mock.Call +} + +// FailsafeDurationMinimum is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) FailsafeDurationMinimum() *UCLCPServerInterface_FailsafeDurationMinimum_Call { + return &UCLCPServerInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum")} +} + +func (_c *UCLCPServerInterface_FailsafeDurationMinimum_Call) Run(run func()) *UCLCPServerInterface_FailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_FailsafeDurationMinimum_Call) Return(duration time.Duration, isChangeable bool, resultErr error) *UCLCPServerInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(duration, isChangeable, resultErr) + return _c +} + +func (_c *UCLCPServerInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func() (time.Duration, bool, error)) *UCLCPServerInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCLCPServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPServerInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCLCPServerInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCLCPServerInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLCPServerInterface_IsUseCaseSupported_Call { + return &UCLCPServerInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCLCPServerInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLCPServerInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLCPServerInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLCPServerInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPServerInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLCPServerInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// LoadControlLimit provides a mock function with given fields: +func (_m *UCLCPServerInterface) LoadControlLimit() (cemdapi.LoadLimit, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LoadControlLimit") + } + + var r0 cemdapi.LoadLimit + var r1 error + if rf, ok := ret.Get(0).(func() (cemdapi.LoadLimit, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() cemdapi.LoadLimit); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(cemdapi.LoadLimit) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPServerInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' +type UCLCPServerInterface_LoadControlLimit_Call struct { + *mock.Call +} + +// LoadControlLimit is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) LoadControlLimit() *UCLCPServerInterface_LoadControlLimit_Call { + return &UCLCPServerInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit")} +} + +func (_c *UCLCPServerInterface_LoadControlLimit_Call) Run(run func()) *UCLCPServerInterface_LoadControlLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_LoadControlLimit_Call) Return(_a0 cemdapi.LoadLimit, _a1 error) *UCLCPServerInterface_LoadControlLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPServerInterface_LoadControlLimit_Call) RunAndReturn(run func() (cemdapi.LoadLimit, error)) *UCLCPServerInterface_LoadControlLimit_Call { + _c.Call.Return(run) + return _c +} + +// PowerConsumptionNominalMax provides a mock function with given fields: +func (_m *UCLCPServerInterface) PowerConsumptionNominalMax() (float64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for PowerConsumptionNominalMax") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func() (float64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLCPServerInterface_PowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerConsumptionNominalMax' +type UCLCPServerInterface_PowerConsumptionNominalMax_Call struct { + *mock.Call +} + +// PowerConsumptionNominalMax is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) PowerConsumptionNominalMax() *UCLCPServerInterface_PowerConsumptionNominalMax_Call { + return &UCLCPServerInterface_PowerConsumptionNominalMax_Call{Call: _e.mock.On("PowerConsumptionNominalMax")} +} + +func (_c *UCLCPServerInterface_PowerConsumptionNominalMax_Call) Run(run func()) *UCLCPServerInterface_PowerConsumptionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_PowerConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPServerInterface_PowerConsumptionNominalMax_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLCPServerInterface_PowerConsumptionNominalMax_Call) RunAndReturn(run func() (float64, error)) *UCLCPServerInterface_PowerConsumptionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// SetContractualConsumptionNominalMax provides a mock function with given fields: value +func (_m *UCLCPServerInterface) SetContractualConsumptionNominalMax(value float64) error { + ret := _m.Called(value) + + if len(ret) == 0 { + panic("no return value specified for SetContractualConsumptionNominalMax") + } + + var r0 error + if rf, ok := ret.Get(0).(func(float64) error); ok { + r0 = rf(value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLCPServerInterface_SetContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetContractualConsumptionNominalMax' +type UCLCPServerInterface_SetContractualConsumptionNominalMax_Call struct { + *mock.Call +} + +// SetContractualConsumptionNominalMax is a helper method to define mock.On call +// - value float64 +func (_e *UCLCPServerInterface_Expecter) SetContractualConsumptionNominalMax(value interface{}) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { + return &UCLCPServerInterface_SetContractualConsumptionNominalMax_Call{Call: _e.mock.On("SetContractualConsumptionNominalMax", value)} +} + +func (_c *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call) Run(run func(value float64)) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(float64)) + }) + return _c +} + +func (_c *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call) Return(resultErr error) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call) RunAndReturn(run func(float64) error) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// SetFailsafeConsumptionActivePowerLimit provides a mock function with given fields: value, changeable +func (_m *UCLCPServerInterface) SetFailsafeConsumptionActivePowerLimit(value float64, changeable bool) error { + ret := _m.Called(value, changeable) + + if len(ret) == 0 { + panic("no return value specified for SetFailsafeConsumptionActivePowerLimit") + } + + var r0 error + if rf, ok := ret.Get(0).(func(float64, bool) error); ok { + r0 = rf(value, changeable) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeConsumptionActivePowerLimit' +type UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call struct { + *mock.Call +} + +// SetFailsafeConsumptionActivePowerLimit is a helper method to define mock.On call +// - value float64 +// - changeable bool +func (_e *UCLCPServerInterface_Expecter) SetFailsafeConsumptionActivePowerLimit(value interface{}, changeable interface{}) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { + return &UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("SetFailsafeConsumptionActivePowerLimit", value, changeable)} +} + +func (_c *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) Run(run func(value float64, changeable bool)) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(float64), args[1].(bool)) + }) + return _c +} + +func (_c *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) Return(resultErr error) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(float64, bool) error) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// SetFailsafeDurationMinimum provides a mock function with given fields: duration, changeable +func (_m *UCLCPServerInterface) SetFailsafeDurationMinimum(duration time.Duration, changeable bool) error { + ret := _m.Called(duration, changeable) + + if len(ret) == 0 { + panic("no return value specified for SetFailsafeDurationMinimum") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Duration, bool) error); ok { + r0 = rf(duration, changeable) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLCPServerInterface_SetFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeDurationMinimum' +type UCLCPServerInterface_SetFailsafeDurationMinimum_Call struct { + *mock.Call +} + +// SetFailsafeDurationMinimum is a helper method to define mock.On call +// - duration time.Duration +// - changeable bool +func (_e *UCLCPServerInterface_Expecter) SetFailsafeDurationMinimum(duration interface{}, changeable interface{}) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { + return &UCLCPServerInterface_SetFailsafeDurationMinimum_Call{Call: _e.mock.On("SetFailsafeDurationMinimum", duration, changeable)} +} + +func (_c *UCLCPServerInterface_SetFailsafeDurationMinimum_Call) Run(run func(duration time.Duration, changeable bool)) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Duration), args[1].(bool)) + }) + return _c +} + +func (_c *UCLCPServerInterface_SetFailsafeDurationMinimum_Call) Return(resultErr error) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLCPServerInterface_SetFailsafeDurationMinimum_Call) RunAndReturn(run func(time.Duration, bool) error) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// SetLoadControlLimit provides a mock function with given fields: limit +func (_m *UCLCPServerInterface) SetLoadControlLimit(limit cemdapi.LoadLimit) error { + ret := _m.Called(limit) + + if len(ret) == 0 { + panic("no return value specified for SetLoadControlLimit") + } + + var r0 error + if rf, ok := ret.Get(0).(func(cemdapi.LoadLimit) error); ok { + r0 = rf(limit) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLCPServerInterface_SetLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLoadControlLimit' +type UCLCPServerInterface_SetLoadControlLimit_Call struct { + *mock.Call +} + +// SetLoadControlLimit is a helper method to define mock.On call +// - limit cemdapi.LoadLimit +func (_e *UCLCPServerInterface_Expecter) SetLoadControlLimit(limit interface{}) *UCLCPServerInterface_SetLoadControlLimit_Call { + return &UCLCPServerInterface_SetLoadControlLimit_Call{Call: _e.mock.On("SetLoadControlLimit", limit)} +} + +func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) Run(run func(limit cemdapi.LoadLimit)) *UCLCPServerInterface_SetLoadControlLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(cemdapi.LoadLimit)) + }) + return _c +} + +func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) Return(resultErr error) *UCLCPServerInterface_SetLoadControlLimit_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) RunAndReturn(run func(cemdapi.LoadLimit) error) *UCLCPServerInterface_SetLoadControlLimit_Call { + _c.Call.Return(run) + return _c +} + +// SetPowerConsumptionNominalMax provides a mock function with given fields: value +func (_m *UCLCPServerInterface) SetPowerConsumptionNominalMax(value float64) error { + ret := _m.Called(value) + + if len(ret) == 0 { + panic("no return value specified for SetPowerConsumptionNominalMax") + } + + var r0 error + if rf, ok := ret.Get(0).(func(float64) error); ok { + r0 = rf(value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLCPServerInterface_SetPowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPowerConsumptionNominalMax' +type UCLCPServerInterface_SetPowerConsumptionNominalMax_Call struct { + *mock.Call +} + +// SetPowerConsumptionNominalMax is a helper method to define mock.On call +// - value float64 +func (_e *UCLCPServerInterface_Expecter) SetPowerConsumptionNominalMax(value interface{}) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { + return &UCLCPServerInterface_SetPowerConsumptionNominalMax_Call{Call: _e.mock.On("SetPowerConsumptionNominalMax", value)} +} + +func (_c *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call) Run(run func(value float64)) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(float64)) + }) + return _c +} + +func (_c *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call) Return(resultErr error) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call) RunAndReturn(run func(float64) error) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCLCPServerInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCLCPServerInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCLCPServerInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCLCPServerInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLCPServerInterface_UpdateUseCaseAvailability_Call { + return &UCLCPServerInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCLCPServerInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLCPServerInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCLCPServerInterface_UpdateUseCaseAvailability_Call) Return() *UCLCPServerInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLCPServerInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLCPServerInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCLCPServerInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCLCPServerInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCLCPServerInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCLCPServerInterface_Expecter) UseCaseName() *UCLCPServerInterface_UseCaseName_Call { + return &UCLCPServerInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCLCPServerInterface_UseCaseName_Call) Run(run func()) *UCLCPServerInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLCPServerInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLCPServerInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCLCPServerInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLCPServerInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCLCPServerInterface creates a new instance of UCLCPServerInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCLCPServerInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCLCPServerInterface { + mock := &UCLCPServerInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCMCPInterface.go b/mocks/UCMCPInterface.go new file mode 100644 index 0000000..e8f3b7a --- /dev/null +++ b/mocks/UCMCPInterface.go @@ -0,0 +1,633 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCMCPInterface is an autogenerated mock type for the UCMCPInterface type +type UCMCPInterface struct { + mock.Mock +} + +type UCMCPInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCMCPInterface) EXPECT() *UCMCPInterface_Expecter { + return &UCMCPInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCMCPInterface) AddFeatures() { + _m.Called() +} + +// UCMCPInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCMCPInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCMCPInterface_Expecter) AddFeatures() *UCMCPInterface_AddFeatures_Call { + return &UCMCPInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCMCPInterface_AddFeatures_Call) Run(run func()) *UCMCPInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCMCPInterface_AddFeatures_Call) Return() *UCMCPInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCMCPInterface_AddFeatures_Call) RunAndReturn(run func()) *UCMCPInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCMCPInterface) AddUseCase() { + _m.Called() +} + +// UCMCPInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCMCPInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCMCPInterface_Expecter) AddUseCase() *UCMCPInterface_AddUseCase_Call { + return &UCMCPInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCMCPInterface_AddUseCase_Call) Run(run func()) *UCMCPInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCMCPInterface_AddUseCase_Call) Return() *UCMCPInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCMCPInterface_AddUseCase_Call) RunAndReturn(run func()) *UCMCPInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// CurrentPerPhase provides a mock function with given fields: entity +func (_m *UCMCPInterface) CurrentPerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CurrentPerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_CurrentPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentPerPhase' +type UCMCPInterface_CurrentPerPhase_Call struct { + *mock.Call +} + +// CurrentPerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) CurrentPerPhase(entity interface{}) *UCMCPInterface_CurrentPerPhase_Call { + return &UCMCPInterface_CurrentPerPhase_Call{Call: _e.mock.On("CurrentPerPhase", entity)} +} + +func (_c *UCMCPInterface_CurrentPerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_CurrentPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_CurrentPerPhase_Call) Return(_a0 []float64, _a1 error) *UCMCPInterface_CurrentPerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_CurrentPerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCMCPInterface_CurrentPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// EnergyConsumed provides a mock function with given fields: entity +func (_m *UCMCPInterface) EnergyConsumed(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyConsumed") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_EnergyConsumed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyConsumed' +type UCMCPInterface_EnergyConsumed_Call struct { + *mock.Call +} + +// EnergyConsumed is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) EnergyConsumed(entity interface{}) *UCMCPInterface_EnergyConsumed_Call { + return &UCMCPInterface_EnergyConsumed_Call{Call: _e.mock.On("EnergyConsumed", entity)} +} + +func (_c *UCMCPInterface_EnergyConsumed_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_EnergyConsumed_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_EnergyConsumed_Call) Return(_a0 float64, _a1 error) *UCMCPInterface_EnergyConsumed_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_EnergyConsumed_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMCPInterface_EnergyConsumed_Call { + _c.Call.Return(run) + return _c +} + +// EnergyProduced provides a mock function with given fields: entity +func (_m *UCMCPInterface) EnergyProduced(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyProduced") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_EnergyProduced_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyProduced' +type UCMCPInterface_EnergyProduced_Call struct { + *mock.Call +} + +// EnergyProduced is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) EnergyProduced(entity interface{}) *UCMCPInterface_EnergyProduced_Call { + return &UCMCPInterface_EnergyProduced_Call{Call: _e.mock.On("EnergyProduced", entity)} +} + +func (_c *UCMCPInterface_EnergyProduced_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_EnergyProduced_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_EnergyProduced_Call) Return(_a0 float64, _a1 error) *UCMCPInterface_EnergyProduced_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_EnergyProduced_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMCPInterface_EnergyProduced_Call { + _c.Call.Return(run) + return _c +} + +// Frequency provides a mock function with given fields: entity +func (_m *UCMCPInterface) Frequency(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Frequency") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_Frequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Frequency' +type UCMCPInterface_Frequency_Call struct { + *mock.Call +} + +// Frequency is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) Frequency(entity interface{}) *UCMCPInterface_Frequency_Call { + return &UCMCPInterface_Frequency_Call{Call: _e.mock.On("Frequency", entity)} +} + +func (_c *UCMCPInterface_Frequency_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_Frequency_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_Frequency_Call) Return(_a0 float64, _a1 error) *UCMCPInterface_Frequency_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_Frequency_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMCPInterface_Frequency_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCMCPInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCMCPInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCMCPInterface_IsUseCaseSupported_Call { + return &UCMCPInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCMCPInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCMCPInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCMCPInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCMCPInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// Power provides a mock function with given fields: entity +func (_m *UCMCPInterface) Power(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Power") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_Power_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Power' +type UCMCPInterface_Power_Call struct { + *mock.Call +} + +// Power is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) Power(entity interface{}) *UCMCPInterface_Power_Call { + return &UCMCPInterface_Power_Call{Call: _e.mock.On("Power", entity)} +} + +func (_c *UCMCPInterface_Power_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_Power_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_Power_Call) Return(_a0 float64, _a1 error) *UCMCPInterface_Power_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_Power_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMCPInterface_Power_Call { + _c.Call.Return(run) + return _c +} + +// PowerPerPhase provides a mock function with given fields: entity +func (_m *UCMCPInterface) PowerPerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PowerPerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_PowerPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerPerPhase' +type UCMCPInterface_PowerPerPhase_Call struct { + *mock.Call +} + +// PowerPerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) PowerPerPhase(entity interface{}) *UCMCPInterface_PowerPerPhase_Call { + return &UCMCPInterface_PowerPerPhase_Call{Call: _e.mock.On("PowerPerPhase", entity)} +} + +func (_c *UCMCPInterface_PowerPerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_PowerPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_PowerPerPhase_Call) Return(_a0 []float64, _a1 error) *UCMCPInterface_PowerPerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_PowerPerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCMCPInterface_PowerPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCMCPInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCMCPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCMCPInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCMCPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCMCPInterface_UpdateUseCaseAvailability_Call { + return &UCMCPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCMCPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCMCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCMCPInterface_UpdateUseCaseAvailability_Call) Return() *UCMCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCMCPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCMCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCMCPInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCMCPInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCMCPInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCMCPInterface_Expecter) UseCaseName() *UCMCPInterface_UseCaseName_Call { + return &UCMCPInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCMCPInterface_UseCaseName_Call) Run(run func()) *UCMCPInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCMCPInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCMCPInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCMCPInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCMCPInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// VoltagePerPhase provides a mock function with given fields: entity +func (_m *UCMCPInterface) VoltagePerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for VoltagePerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMCPInterface_VoltagePerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VoltagePerPhase' +type UCMCPInterface_VoltagePerPhase_Call struct { + *mock.Call +} + +// VoltagePerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMCPInterface_Expecter) VoltagePerPhase(entity interface{}) *UCMCPInterface_VoltagePerPhase_Call { + return &UCMCPInterface_VoltagePerPhase_Call{Call: _e.mock.On("VoltagePerPhase", entity)} +} + +func (_c *UCMCPInterface_VoltagePerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMCPInterface_VoltagePerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMCPInterface_VoltagePerPhase_Call) Return(_a0 []float64, _a1 error) *UCMCPInterface_VoltagePerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMCPInterface_VoltagePerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCMCPInterface_VoltagePerPhase_Call { + _c.Call.Return(run) + return _c +} + +// NewUCMCPInterface creates a new instance of UCMCPInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCMCPInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCMCPInterface { + mock := &UCMCPInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCMGCPInterface.go b/mocks/UCMGCPInterface.go new file mode 100644 index 0000000..61f02f7 --- /dev/null +++ b/mocks/UCMGCPInterface.go @@ -0,0 +1,631 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCMGCPInterface is an autogenerated mock type for the UCMGCPInterface type +type UCMGCPInterface struct { + mock.Mock +} + +type UCMGCPInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCMGCPInterface) EXPECT() *UCMGCPInterface_Expecter { + return &UCMGCPInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCMGCPInterface) AddFeatures() { + _m.Called() +} + +// UCMGCPInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCMGCPInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCMGCPInterface_Expecter) AddFeatures() *UCMGCPInterface_AddFeatures_Call { + return &UCMGCPInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCMGCPInterface_AddFeatures_Call) Run(run func()) *UCMGCPInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCMGCPInterface_AddFeatures_Call) Return() *UCMGCPInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCMGCPInterface_AddFeatures_Call) RunAndReturn(run func()) *UCMGCPInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCMGCPInterface) AddUseCase() { + _m.Called() +} + +// UCMGCPInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCMGCPInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCMGCPInterface_Expecter) AddUseCase() *UCMGCPInterface_AddUseCase_Call { + return &UCMGCPInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCMGCPInterface_AddUseCase_Call) Run(run func()) *UCMGCPInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCMGCPInterface_AddUseCase_Call) Return() *UCMGCPInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCMGCPInterface_AddUseCase_Call) RunAndReturn(run func()) *UCMGCPInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// CurrentPerPhase provides a mock function with given fields: entity +func (_m *UCMGCPInterface) CurrentPerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CurrentPerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_CurrentPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentPerPhase' +type UCMGCPInterface_CurrentPerPhase_Call struct { + *mock.Call +} + +// CurrentPerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) CurrentPerPhase(entity interface{}) *UCMGCPInterface_CurrentPerPhase_Call { + return &UCMGCPInterface_CurrentPerPhase_Call{Call: _e.mock.On("CurrentPerPhase", entity)} +} + +func (_c *UCMGCPInterface_CurrentPerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_CurrentPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_CurrentPerPhase_Call) Return(_a0 []float64, _a1 error) *UCMGCPInterface_CurrentPerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_CurrentPerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCMGCPInterface_CurrentPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// EnergyConsumed provides a mock function with given fields: entity +func (_m *UCMGCPInterface) EnergyConsumed(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyConsumed") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_EnergyConsumed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyConsumed' +type UCMGCPInterface_EnergyConsumed_Call struct { + *mock.Call +} + +// EnergyConsumed is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) EnergyConsumed(entity interface{}) *UCMGCPInterface_EnergyConsumed_Call { + return &UCMGCPInterface_EnergyConsumed_Call{Call: _e.mock.On("EnergyConsumed", entity)} +} + +func (_c *UCMGCPInterface_EnergyConsumed_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_EnergyConsumed_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_EnergyConsumed_Call) Return(_a0 float64, _a1 error) *UCMGCPInterface_EnergyConsumed_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_EnergyConsumed_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMGCPInterface_EnergyConsumed_Call { + _c.Call.Return(run) + return _c +} + +// EnergyFeedIn provides a mock function with given fields: entity +func (_m *UCMGCPInterface) EnergyFeedIn(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyFeedIn") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_EnergyFeedIn_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyFeedIn' +type UCMGCPInterface_EnergyFeedIn_Call struct { + *mock.Call +} + +// EnergyFeedIn is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) EnergyFeedIn(entity interface{}) *UCMGCPInterface_EnergyFeedIn_Call { + return &UCMGCPInterface_EnergyFeedIn_Call{Call: _e.mock.On("EnergyFeedIn", entity)} +} + +func (_c *UCMGCPInterface_EnergyFeedIn_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_EnergyFeedIn_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_EnergyFeedIn_Call) Return(_a0 float64, _a1 error) *UCMGCPInterface_EnergyFeedIn_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_EnergyFeedIn_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMGCPInterface_EnergyFeedIn_Call { + _c.Call.Return(run) + return _c +} + +// Frequency provides a mock function with given fields: entity +func (_m *UCMGCPInterface) Frequency(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Frequency") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_Frequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Frequency' +type UCMGCPInterface_Frequency_Call struct { + *mock.Call +} + +// Frequency is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) Frequency(entity interface{}) *UCMGCPInterface_Frequency_Call { + return &UCMGCPInterface_Frequency_Call{Call: _e.mock.On("Frequency", entity)} +} + +func (_c *UCMGCPInterface_Frequency_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_Frequency_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_Frequency_Call) Return(_a0 float64, _a1 error) *UCMGCPInterface_Frequency_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_Frequency_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMGCPInterface_Frequency_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCMGCPInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCMGCPInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCMGCPInterface_IsUseCaseSupported_Call { + return &UCMGCPInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCMGCPInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCMGCPInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCMGCPInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCMGCPInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// Power provides a mock function with given fields: entity +func (_m *UCMGCPInterface) Power(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Power") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_Power_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Power' +type UCMGCPInterface_Power_Call struct { + *mock.Call +} + +// Power is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) Power(entity interface{}) *UCMGCPInterface_Power_Call { + return &UCMGCPInterface_Power_Call{Call: _e.mock.On("Power", entity)} +} + +func (_c *UCMGCPInterface_Power_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_Power_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_Power_Call) Return(_a0 float64, _a1 error) *UCMGCPInterface_Power_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_Power_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMGCPInterface_Power_Call { + _c.Call.Return(run) + return _c +} + +// PowerLimitationFactor provides a mock function with given fields: entity +func (_m *UCMGCPInterface) PowerLimitationFactor(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PowerLimitationFactor") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_PowerLimitationFactor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerLimitationFactor' +type UCMGCPInterface_PowerLimitationFactor_Call struct { + *mock.Call +} + +// PowerLimitationFactor is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) PowerLimitationFactor(entity interface{}) *UCMGCPInterface_PowerLimitationFactor_Call { + return &UCMGCPInterface_PowerLimitationFactor_Call{Call: _e.mock.On("PowerLimitationFactor", entity)} +} + +func (_c *UCMGCPInterface_PowerLimitationFactor_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_PowerLimitationFactor_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_PowerLimitationFactor_Call) Return(_a0 float64, _a1 error) *UCMGCPInterface_PowerLimitationFactor_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_PowerLimitationFactor_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCMGCPInterface_PowerLimitationFactor_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCMGCPInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCMGCPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCMGCPInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCMGCPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCMGCPInterface_UpdateUseCaseAvailability_Call { + return &UCMGCPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCMGCPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCMGCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCMGCPInterface_UpdateUseCaseAvailability_Call) Return() *UCMGCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCMGCPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCMGCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCMGCPInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCMGCPInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCMGCPInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCMGCPInterface_Expecter) UseCaseName() *UCMGCPInterface_UseCaseName_Call { + return &UCMGCPInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCMGCPInterface_UseCaseName_Call) Run(run func()) *UCMGCPInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCMGCPInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCMGCPInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCMGCPInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCMGCPInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// VoltagePerPhase provides a mock function with given fields: entity +func (_m *UCMGCPInterface) VoltagePerPhase(entity api.EntityRemoteInterface) ([]float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for VoltagePerPhase") + } + + var r0 []float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCMGCPInterface_VoltagePerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VoltagePerPhase' +type UCMGCPInterface_VoltagePerPhase_Call struct { + *mock.Call +} + +// VoltagePerPhase is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCMGCPInterface_Expecter) VoltagePerPhase(entity interface{}) *UCMGCPInterface_VoltagePerPhase_Call { + return &UCMGCPInterface_VoltagePerPhase_Call{Call: _e.mock.On("VoltagePerPhase", entity)} +} + +func (_c *UCMGCPInterface_VoltagePerPhase_Call) Run(run func(entity api.EntityRemoteInterface)) *UCMGCPInterface_VoltagePerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCMGCPInterface_VoltagePerPhase_Call) Return(_a0 []float64, _a1 error) *UCMGCPInterface_VoltagePerPhase_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCMGCPInterface_VoltagePerPhase_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, error)) *UCMGCPInterface_VoltagePerPhase_Call { + _c.Call.Return(run) + return _c +} + +// NewUCMGCPInterface creates a new instance of UCMGCPInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCMGCPInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCMGCPInterface { + mock := &UCMGCPInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCOPEVInterface.go b/mocks/UCOPEVInterface.go new file mode 100644 index 0000000..dd5540b --- /dev/null +++ b/mocks/UCOPEVInterface.go @@ -0,0 +1,354 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCOPEVInterface is an autogenerated mock type for the UCOPEVInterface type +type UCOPEVInterface struct { + mock.Mock +} + +type UCOPEVInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCOPEVInterface) EXPECT() *UCOPEVInterface_Expecter { + return &UCOPEVInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCOPEVInterface) AddFeatures() { + _m.Called() +} + +// UCOPEVInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCOPEVInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCOPEVInterface_Expecter) AddFeatures() *UCOPEVInterface_AddFeatures_Call { + return &UCOPEVInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCOPEVInterface_AddFeatures_Call) Run(run func()) *UCOPEVInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCOPEVInterface_AddFeatures_Call) Return() *UCOPEVInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCOPEVInterface_AddFeatures_Call) RunAndReturn(run func()) *UCOPEVInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCOPEVInterface) AddUseCase() { + _m.Called() +} + +// UCOPEVInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCOPEVInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCOPEVInterface_Expecter) AddUseCase() *UCOPEVInterface_AddUseCase_Call { + return &UCOPEVInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCOPEVInterface_AddUseCase_Call) Run(run func()) *UCOPEVInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCOPEVInterface_AddUseCase_Call) Return() *UCOPEVInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCOPEVInterface_AddUseCase_Call) RunAndReturn(run func()) *UCOPEVInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCOPEVInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCOPEVInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCOPEVInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCOPEVInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCOPEVInterface_IsUseCaseSupported_Call { + return &UCOPEVInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCOPEVInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCOPEVInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCOPEVInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCOPEVInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCOPEVInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCOPEVInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// LoadControlLimits provides a mock function with given fields: entity +func (_m *UCOPEVInterface) LoadControlLimits(entity api.EntityRemoteInterface) ([]cemdapi.LoadLimitsPhase, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for LoadControlLimits") + } + + var r0 []cemdapi.LoadLimitsPhase + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]cemdapi.LoadLimitsPhase, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []cemdapi.LoadLimitsPhase); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]cemdapi.LoadLimitsPhase) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCOPEVInterface_LoadControlLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimits' +type UCOPEVInterface_LoadControlLimits_Call struct { + *mock.Call +} + +// LoadControlLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCOPEVInterface_Expecter) LoadControlLimits(entity interface{}) *UCOPEVInterface_LoadControlLimits_Call { + return &UCOPEVInterface_LoadControlLimits_Call{Call: _e.mock.On("LoadControlLimits", entity)} +} + +func (_c *UCOPEVInterface_LoadControlLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCOPEVInterface_LoadControlLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCOPEVInterface_LoadControlLimits_Call) Return(limits []cemdapi.LoadLimitsPhase, resultErr error) *UCOPEVInterface_LoadControlLimits_Call { + _c.Call.Return(limits, resultErr) + return _c +} + +func (_c *UCOPEVInterface_LoadControlLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]cemdapi.LoadLimitsPhase, error)) *UCOPEVInterface_LoadControlLimits_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCOPEVInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCOPEVInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCOPEVInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCOPEVInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCOPEVInterface_UpdateUseCaseAvailability_Call { + return &UCOPEVInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCOPEVInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCOPEVInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCOPEVInterface_UpdateUseCaseAvailability_Call) Return() *UCOPEVInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCOPEVInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCOPEVInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCOPEVInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCOPEVInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCOPEVInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCOPEVInterface_Expecter) UseCaseName() *UCOPEVInterface_UseCaseName_Call { + return &UCOPEVInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCOPEVInterface_UseCaseName_Call) Run(run func()) *UCOPEVInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCOPEVInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCOPEVInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCOPEVInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCOPEVInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// WriteLoadControlLimits provides a mock function with given fields: entity, limits +func (_m *UCOPEVInterface) WriteLoadControlLimits(entity api.EntityRemoteInterface, limits []cemdapi.LoadLimitsPhase) (*model.MsgCounterType, error) { + ret := _m.Called(entity, limits) + + if len(ret) == 0 { + panic("no return value specified for WriteLoadControlLimits") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) (*model.MsgCounterType, error)); ok { + return rf(entity, limits) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) *model.MsgCounterType); ok { + r0 = rf(entity, limits) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) error); ok { + r1 = rf(entity, limits) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCOPEVInterface_WriteLoadControlLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteLoadControlLimits' +type UCOPEVInterface_WriteLoadControlLimits_Call struct { + *mock.Call +} + +// WriteLoadControlLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - limits []cemdapi.LoadLimitsPhase +func (_e *UCOPEVInterface_Expecter) WriteLoadControlLimits(entity interface{}, limits interface{}) *UCOPEVInterface_WriteLoadControlLimits_Call { + return &UCOPEVInterface_WriteLoadControlLimits_Call{Call: _e.mock.On("WriteLoadControlLimits", entity, limits)} +} + +func (_c *UCOPEVInterface_WriteLoadControlLimits_Call) Run(run func(entity api.EntityRemoteInterface, limits []cemdapi.LoadLimitsPhase)) *UCOPEVInterface_WriteLoadControlLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].([]cemdapi.LoadLimitsPhase)) + }) + return _c +} + +func (_c *UCOPEVInterface_WriteLoadControlLimits_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCOPEVInterface_WriteLoadControlLimits_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCOPEVInterface_WriteLoadControlLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) (*model.MsgCounterType, error)) *UCOPEVInterface_WriteLoadControlLimits_Call { + _c.Call.Return(run) + return _c +} + +// NewUCOPEVInterface creates a new instance of UCOPEVInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCOPEVInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCOPEVInterface { + mock := &UCOPEVInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCOSCEVInterface.go b/mocks/UCOSCEVInterface.go new file mode 100644 index 0000000..5e61f60 --- /dev/null +++ b/mocks/UCOSCEVInterface.go @@ -0,0 +1,354 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCOSCEVInterface is an autogenerated mock type for the UCOSCEVInterface type +type UCOSCEVInterface struct { + mock.Mock +} + +type UCOSCEVInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCOSCEVInterface) EXPECT() *UCOSCEVInterface_Expecter { + return &UCOSCEVInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCOSCEVInterface) AddFeatures() { + _m.Called() +} + +// UCOSCEVInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCOSCEVInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCOSCEVInterface_Expecter) AddFeatures() *UCOSCEVInterface_AddFeatures_Call { + return &UCOSCEVInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCOSCEVInterface_AddFeatures_Call) Run(run func()) *UCOSCEVInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCOSCEVInterface_AddFeatures_Call) Return() *UCOSCEVInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCOSCEVInterface_AddFeatures_Call) RunAndReturn(run func()) *UCOSCEVInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCOSCEVInterface) AddUseCase() { + _m.Called() +} + +// UCOSCEVInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCOSCEVInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCOSCEVInterface_Expecter) AddUseCase() *UCOSCEVInterface_AddUseCase_Call { + return &UCOSCEVInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCOSCEVInterface_AddUseCase_Call) Run(run func()) *UCOSCEVInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCOSCEVInterface_AddUseCase_Call) Return() *UCOSCEVInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCOSCEVInterface_AddUseCase_Call) RunAndReturn(run func()) *UCOSCEVInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCOSCEVInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCOSCEVInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCOSCEVInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCOSCEVInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCOSCEVInterface_IsUseCaseSupported_Call { + return &UCOSCEVInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCOSCEVInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCOSCEVInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCOSCEVInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCOSCEVInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCOSCEVInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCOSCEVInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// LoadControlLimits provides a mock function with given fields: entity +func (_m *UCOSCEVInterface) LoadControlLimits(entity api.EntityRemoteInterface) ([]cemdapi.LoadLimitsPhase, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for LoadControlLimits") + } + + var r0 []cemdapi.LoadLimitsPhase + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]cemdapi.LoadLimitsPhase, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []cemdapi.LoadLimitsPhase); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]cemdapi.LoadLimitsPhase) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCOSCEVInterface_LoadControlLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimits' +type UCOSCEVInterface_LoadControlLimits_Call struct { + *mock.Call +} + +// LoadControlLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCOSCEVInterface_Expecter) LoadControlLimits(entity interface{}) *UCOSCEVInterface_LoadControlLimits_Call { + return &UCOSCEVInterface_LoadControlLimits_Call{Call: _e.mock.On("LoadControlLimits", entity)} +} + +func (_c *UCOSCEVInterface_LoadControlLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCOSCEVInterface_LoadControlLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCOSCEVInterface_LoadControlLimits_Call) Return(limits []cemdapi.LoadLimitsPhase, resultErr error) *UCOSCEVInterface_LoadControlLimits_Call { + _c.Call.Return(limits, resultErr) + return _c +} + +func (_c *UCOSCEVInterface_LoadControlLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]cemdapi.LoadLimitsPhase, error)) *UCOSCEVInterface_LoadControlLimits_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCOSCEVInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCOSCEVInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCOSCEVInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCOSCEVInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCOSCEVInterface_UpdateUseCaseAvailability_Call { + return &UCOSCEVInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCOSCEVInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCOSCEVInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCOSCEVInterface_UpdateUseCaseAvailability_Call) Return() *UCOSCEVInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCOSCEVInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCOSCEVInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCOSCEVInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCOSCEVInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCOSCEVInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCOSCEVInterface_Expecter) UseCaseName() *UCOSCEVInterface_UseCaseName_Call { + return &UCOSCEVInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCOSCEVInterface_UseCaseName_Call) Run(run func()) *UCOSCEVInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCOSCEVInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCOSCEVInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCOSCEVInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCOSCEVInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// WriteLoadControlLimits provides a mock function with given fields: entity, limits +func (_m *UCOSCEVInterface) WriteLoadControlLimits(entity api.EntityRemoteInterface, limits []cemdapi.LoadLimitsPhase) (*model.MsgCounterType, error) { + ret := _m.Called(entity, limits) + + if len(ret) == 0 { + panic("no return value specified for WriteLoadControlLimits") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) (*model.MsgCounterType, error)); ok { + return rf(entity, limits) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) *model.MsgCounterType); ok { + r0 = rf(entity, limits) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) error); ok { + r1 = rf(entity, limits) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCOSCEVInterface_WriteLoadControlLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteLoadControlLimits' +type UCOSCEVInterface_WriteLoadControlLimits_Call struct { + *mock.Call +} + +// WriteLoadControlLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - limits []cemdapi.LoadLimitsPhase +func (_e *UCOSCEVInterface_Expecter) WriteLoadControlLimits(entity interface{}, limits interface{}) *UCOSCEVInterface_WriteLoadControlLimits_Call { + return &UCOSCEVInterface_WriteLoadControlLimits_Call{Call: _e.mock.On("WriteLoadControlLimits", entity, limits)} +} + +func (_c *UCOSCEVInterface_WriteLoadControlLimits_Call) Run(run func(entity api.EntityRemoteInterface, limits []cemdapi.LoadLimitsPhase)) *UCOSCEVInterface_WriteLoadControlLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].([]cemdapi.LoadLimitsPhase)) + }) + return _c +} + +func (_c *UCOSCEVInterface_WriteLoadControlLimits_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCOSCEVInterface_WriteLoadControlLimits_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCOSCEVInterface_WriteLoadControlLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface, []cemdapi.LoadLimitsPhase) (*model.MsgCounterType, error)) *UCOSCEVInterface_WriteLoadControlLimits_Call { + _c.Call.Return(run) + return _c +} + +// NewUCOSCEVInterface creates a new instance of UCOSCEVInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCOSCEVInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCOSCEVInterface { + mock := &UCOSCEVInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCVABDInterface.go b/mocks/UCVABDInterface.go new file mode 100644 index 0000000..4fd5be3 --- /dev/null +++ b/mocks/UCVABDInterface.go @@ -0,0 +1,459 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCVABDInterface is an autogenerated mock type for the UCVABDInterface type +type UCVABDInterface struct { + mock.Mock +} + +type UCVABDInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCVABDInterface) EXPECT() *UCVABDInterface_Expecter { + return &UCVABDInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCVABDInterface) AddFeatures() { + _m.Called() +} + +// UCVABDInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCVABDInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCVABDInterface_Expecter) AddFeatures() *UCVABDInterface_AddFeatures_Call { + return &UCVABDInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCVABDInterface_AddFeatures_Call) Run(run func()) *UCVABDInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCVABDInterface_AddFeatures_Call) Return() *UCVABDInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCVABDInterface_AddFeatures_Call) RunAndReturn(run func()) *UCVABDInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCVABDInterface) AddUseCase() { + _m.Called() +} + +// UCVABDInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCVABDInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCVABDInterface_Expecter) AddUseCase() *UCVABDInterface_AddUseCase_Call { + return &UCVABDInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCVABDInterface_AddUseCase_Call) Run(run func()) *UCVABDInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCVABDInterface_AddUseCase_Call) Return() *UCVABDInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCVABDInterface_AddUseCase_Call) RunAndReturn(run func()) *UCVABDInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// EnergyCharged provides a mock function with given fields: entity +func (_m *UCVABDInterface) EnergyCharged(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyCharged") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVABDInterface_EnergyCharged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyCharged' +type UCVABDInterface_EnergyCharged_Call struct { + *mock.Call +} + +// EnergyCharged is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVABDInterface_Expecter) EnergyCharged(entity interface{}) *UCVABDInterface_EnergyCharged_Call { + return &UCVABDInterface_EnergyCharged_Call{Call: _e.mock.On("EnergyCharged", entity)} +} + +func (_c *UCVABDInterface_EnergyCharged_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVABDInterface_EnergyCharged_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVABDInterface_EnergyCharged_Call) Return(_a0 float64, _a1 error) *UCVABDInterface_EnergyCharged_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVABDInterface_EnergyCharged_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVABDInterface_EnergyCharged_Call { + _c.Call.Return(run) + return _c +} + +// EnergyDischarged provides a mock function with given fields: entity +func (_m *UCVABDInterface) EnergyDischarged(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for EnergyDischarged") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVABDInterface_EnergyDischarged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyDischarged' +type UCVABDInterface_EnergyDischarged_Call struct { + *mock.Call +} + +// EnergyDischarged is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVABDInterface_Expecter) EnergyDischarged(entity interface{}) *UCVABDInterface_EnergyDischarged_Call { + return &UCVABDInterface_EnergyDischarged_Call{Call: _e.mock.On("EnergyDischarged", entity)} +} + +func (_c *UCVABDInterface_EnergyDischarged_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVABDInterface_EnergyDischarged_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVABDInterface_EnergyDischarged_Call) Return(_a0 float64, _a1 error) *UCVABDInterface_EnergyDischarged_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVABDInterface_EnergyDischarged_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVABDInterface_EnergyDischarged_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCVABDInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVABDInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCVABDInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCVABDInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCVABDInterface_IsUseCaseSupported_Call { + return &UCVABDInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCVABDInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCVABDInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVABDInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCVABDInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVABDInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCVABDInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// Power provides a mock function with given fields: entity +func (_m *UCVABDInterface) Power(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Power") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVABDInterface_Power_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Power' +type UCVABDInterface_Power_Call struct { + *mock.Call +} + +// Power is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVABDInterface_Expecter) Power(entity interface{}) *UCVABDInterface_Power_Call { + return &UCVABDInterface_Power_Call{Call: _e.mock.On("Power", entity)} +} + +func (_c *UCVABDInterface_Power_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVABDInterface_Power_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVABDInterface_Power_Call) Return(_a0 float64, _a1 error) *UCVABDInterface_Power_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVABDInterface_Power_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVABDInterface_Power_Call { + _c.Call.Return(run) + return _c +} + +// StateOfCharge provides a mock function with given fields: entity +func (_m *UCVABDInterface) StateOfCharge(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for StateOfCharge") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVABDInterface_StateOfCharge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateOfCharge' +type UCVABDInterface_StateOfCharge_Call struct { + *mock.Call +} + +// StateOfCharge is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVABDInterface_Expecter) StateOfCharge(entity interface{}) *UCVABDInterface_StateOfCharge_Call { + return &UCVABDInterface_StateOfCharge_Call{Call: _e.mock.On("StateOfCharge", entity)} +} + +func (_c *UCVABDInterface_StateOfCharge_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVABDInterface_StateOfCharge_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVABDInterface_StateOfCharge_Call) Return(_a0 float64, _a1 error) *UCVABDInterface_StateOfCharge_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVABDInterface_StateOfCharge_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVABDInterface_StateOfCharge_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCVABDInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCVABDInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCVABDInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCVABDInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCVABDInterface_UpdateUseCaseAvailability_Call { + return &UCVABDInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCVABDInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCVABDInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCVABDInterface_UpdateUseCaseAvailability_Call) Return() *UCVABDInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCVABDInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCVABDInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCVABDInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCVABDInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCVABDInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCVABDInterface_Expecter) UseCaseName() *UCVABDInterface_UseCaseName_Call { + return &UCVABDInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCVABDInterface_UseCaseName_Call) Run(run func()) *UCVABDInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCVABDInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCVABDInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCVABDInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCVABDInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCVABDInterface creates a new instance of UCVABDInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCVABDInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCVABDInterface { + mock := &UCVABDInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCVAPDInterface.go b/mocks/UCVAPDInterface.go new file mode 100644 index 0000000..0cbf801 --- /dev/null +++ b/mocks/UCVAPDInterface.go @@ -0,0 +1,403 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" +) + +// UCVAPDInterface is an autogenerated mock type for the UCVAPDInterface type +type UCVAPDInterface struct { + mock.Mock +} + +type UCVAPDInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCVAPDInterface) EXPECT() *UCVAPDInterface_Expecter { + return &UCVAPDInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCVAPDInterface) AddFeatures() { + _m.Called() +} + +// UCVAPDInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCVAPDInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCVAPDInterface_Expecter) AddFeatures() *UCVAPDInterface_AddFeatures_Call { + return &UCVAPDInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCVAPDInterface_AddFeatures_Call) Run(run func()) *UCVAPDInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCVAPDInterface_AddFeatures_Call) Return() *UCVAPDInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCVAPDInterface_AddFeatures_Call) RunAndReturn(run func()) *UCVAPDInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCVAPDInterface) AddUseCase() { + _m.Called() +} + +// UCVAPDInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCVAPDInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCVAPDInterface_Expecter) AddUseCase() *UCVAPDInterface_AddUseCase_Call { + return &UCVAPDInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCVAPDInterface_AddUseCase_Call) Run(run func()) *UCVAPDInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCVAPDInterface_AddUseCase_Call) Return() *UCVAPDInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCVAPDInterface_AddUseCase_Call) RunAndReturn(run func()) *UCVAPDInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCVAPDInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVAPDInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCVAPDInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCVAPDInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCVAPDInterface_IsUseCaseSupported_Call { + return &UCVAPDInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCVAPDInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCVAPDInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVAPDInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCVAPDInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVAPDInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCVAPDInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// PVYieldTotal provides a mock function with given fields: entity +func (_m *UCVAPDInterface) PVYieldTotal(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PVYieldTotal") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVAPDInterface_PVYieldTotal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PVYieldTotal' +type UCVAPDInterface_PVYieldTotal_Call struct { + *mock.Call +} + +// PVYieldTotal is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVAPDInterface_Expecter) PVYieldTotal(entity interface{}) *UCVAPDInterface_PVYieldTotal_Call { + return &UCVAPDInterface_PVYieldTotal_Call{Call: _e.mock.On("PVYieldTotal", entity)} +} + +func (_c *UCVAPDInterface_PVYieldTotal_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVAPDInterface_PVYieldTotal_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVAPDInterface_PVYieldTotal_Call) Return(_a0 float64, _a1 error) *UCVAPDInterface_PVYieldTotal_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVAPDInterface_PVYieldTotal_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVAPDInterface_PVYieldTotal_Call { + _c.Call.Return(run) + return _c +} + +// Power provides a mock function with given fields: entity +func (_m *UCVAPDInterface) Power(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for Power") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVAPDInterface_Power_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Power' +type UCVAPDInterface_Power_Call struct { + *mock.Call +} + +// Power is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVAPDInterface_Expecter) Power(entity interface{}) *UCVAPDInterface_Power_Call { + return &UCVAPDInterface_Power_Call{Call: _e.mock.On("Power", entity)} +} + +func (_c *UCVAPDInterface_Power_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVAPDInterface_Power_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVAPDInterface_Power_Call) Return(_a0 float64, _a1 error) *UCVAPDInterface_Power_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVAPDInterface_Power_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVAPDInterface_Power_Call { + _c.Call.Return(run) + return _c +} + +// PowerNominalPeak provides a mock function with given fields: entity +func (_m *UCVAPDInterface) PowerNominalPeak(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PowerNominalPeak") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCVAPDInterface_PowerNominalPeak_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerNominalPeak' +type UCVAPDInterface_PowerNominalPeak_Call struct { + *mock.Call +} + +// PowerNominalPeak is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCVAPDInterface_Expecter) PowerNominalPeak(entity interface{}) *UCVAPDInterface_PowerNominalPeak_Call { + return &UCVAPDInterface_PowerNominalPeak_Call{Call: _e.mock.On("PowerNominalPeak", entity)} +} + +func (_c *UCVAPDInterface_PowerNominalPeak_Call) Run(run func(entity api.EntityRemoteInterface)) *UCVAPDInterface_PowerNominalPeak_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCVAPDInterface_PowerNominalPeak_Call) Return(_a0 float64, _a1 error) *UCVAPDInterface_PowerNominalPeak_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCVAPDInterface_PowerNominalPeak_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCVAPDInterface_PowerNominalPeak_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCVAPDInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCVAPDInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCVAPDInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCVAPDInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCVAPDInterface_UpdateUseCaseAvailability_Call { + return &UCVAPDInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCVAPDInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCVAPDInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCVAPDInterface_UpdateUseCaseAvailability_Call) Return() *UCVAPDInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCVAPDInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCVAPDInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCVAPDInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCVAPDInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCVAPDInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCVAPDInterface_Expecter) UseCaseName() *UCVAPDInterface_UseCaseName_Call { + return &UCVAPDInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCVAPDInterface_UseCaseName_Call) Run(run func()) *UCVAPDInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCVAPDInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCVAPDInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCVAPDInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCVAPDInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCVAPDInterface creates a new instance of UCVAPDInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCVAPDInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCVAPDInterface { + mock := &UCVAPDInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UseCaseInterface.go b/mocks/UseCaseInterface.go new file mode 100644 index 0000000..ecd84a2 --- /dev/null +++ b/mocks/UseCaseInterface.go @@ -0,0 +1,235 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + model "github.com/enbility/spine-go/model" + mock "github.com/stretchr/testify/mock" + + spine_goapi "github.com/enbility/spine-go/api" +) + +// UseCaseInterface is an autogenerated mock type for the UseCaseInterface type +type UseCaseInterface struct { + mock.Mock +} + +type UseCaseInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UseCaseInterface) EXPECT() *UseCaseInterface_Expecter { + return &UseCaseInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UseCaseInterface) AddFeatures() { + _m.Called() +} + +// UseCaseInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UseCaseInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UseCaseInterface_Expecter) AddFeatures() *UseCaseInterface_AddFeatures_Call { + return &UseCaseInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UseCaseInterface_AddFeatures_Call) Run(run func()) *UseCaseInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UseCaseInterface_AddFeatures_Call) Return() *UseCaseInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UseCaseInterface_AddFeatures_Call) RunAndReturn(run func()) *UseCaseInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UseCaseInterface) AddUseCase() { + _m.Called() +} + +// UseCaseInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UseCaseInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UseCaseInterface_Expecter) AddUseCase() *UseCaseInterface_AddUseCase_Call { + return &UseCaseInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UseCaseInterface_AddUseCase_Call) Run(run func()) *UseCaseInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UseCaseInterface_AddUseCase_Call) Return() *UseCaseInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UseCaseInterface_AddUseCase_Call) RunAndReturn(run func()) *UseCaseInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UseCaseInterface) IsUseCaseSupported(remoteEntity spine_goapi.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(spine_goapi.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(spine_goapi.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(spine_goapi.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UseCaseInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UseCaseInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity spine_goapi.EntityRemoteInterface +func (_e *UseCaseInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UseCaseInterface_IsUseCaseSupported_Call { + return &UseCaseInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UseCaseInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity spine_goapi.EntityRemoteInterface)) *UseCaseInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(spine_goapi.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UseCaseInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UseCaseInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UseCaseInterface_IsUseCaseSupported_Call) RunAndReturn(run func(spine_goapi.EntityRemoteInterface) (bool, error)) *UseCaseInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UseCaseInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UseCaseInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UseCaseInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UseCaseInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UseCaseInterface_UpdateUseCaseAvailability_Call { + return &UseCaseInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UseCaseInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UseCaseInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UseCaseInterface_UpdateUseCaseAvailability_Call) Return() *UseCaseInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UseCaseInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UseCaseInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UseCaseInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UseCaseInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UseCaseInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UseCaseInterface_Expecter) UseCaseName() *UseCaseInterface_UseCaseName_Call { + return &UseCaseInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UseCaseInterface_UseCaseName_Call) Run(run func()) *UseCaseInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UseCaseInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UseCaseInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UseCaseInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UseCaseInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUseCaseInterface creates a new instance of UseCaseInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUseCaseInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UseCaseInterface { + mock := &UseCaseInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 1cb1b95756b2d1939bf9242cc9f72edde97d8b53 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 16:19:06 +0100 Subject: [PATCH 134/227] Fix comments --- uclpc/events.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uclpc/events.go b/uclpc/events.go index 60a216a..398d45c 100644 --- a/uclpc/events.go +++ b/uclpc/events.go @@ -87,7 +87,7 @@ func (e *UCLPC) loadControlLimitDataUpdate(payload spineapi.EventPayload) { } } -// the configuration key description data of an SMGW was updated +// the configuration key description data was updated func (e *UCLPC) configurationDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { // key value descriptions received, now get the data @@ -97,7 +97,7 @@ func (e *UCLPC) configurationDescriptionDataUpdate(entity spineapi.EntityRemoteI } } -// the configuration key data of an SMGW was updated +// the configuration key data was updated func (e *UCLPC) configurationDataUpdate(payload spineapi.EventPayload) { if _, err := e.FailsafeConsumptionActivePowerLimit(payload.Entity); err != nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeConsumptionActivePowerLimit) From 63b004d893a953b6afa25587c8d60eac2baf95fe Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 16:57:20 +0100 Subject: [PATCH 135/227] Fix wrong validEntity --- ucoscev/ucoscev.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 5394a35..55dd156 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -28,7 +28,7 @@ func NewUCOSCEV(service eebusapi.ServiceInterface, eventCB api.EntityEventCallba uc.validEntityTypes = []model.EntityTypeType{ model.EntityTypeTypeCompressor, model.EntityTypeTypeElectricalImmersionHeater, - model.EntityTypeTypeEVSE, + model.EntityTypeTypeEV, model.EntityTypeTypeHeatPumpAppliance, model.EntityTypeTypeInverter, model.EntityTypeTypeSmartEnergyAppliance, From d051bb0a165bf50c01cda8543919f8566ab39663 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 10 Mar 2024 16:57:36 +0100 Subject: [PATCH 136/227] Add more rudimentary tests --- uccevc/events_test.go | 17 +++++++++++++++++ uccevc/testhelper_test.go | 2 ++ ucevcc/events_test.go | 15 +++++++++++++++ ucevcc/testhelper_test.go | 2 ++ ucevcem/events_test.go | 6 ++++++ ucevcem/testhelper_test.go | 2 ++ ucevsecc/testhelper_test.go | 2 ++ ucevsoc/events_test.go | 4 ++++ ucevsoc/testhelper_test.go | 2 ++ uclpc/events_test.go | 6 ++++++ uclpc/testhelper_test.go | 2 ++ uclpcserver/testhelper_test.go | 2 ++ ucmgcp/events_test.go | 8 ++++++++ ucmgcp/testhelper_test.go | 2 ++ ucmpc/events_test.go | 6 ++++++ ucmpc/testhelper_test.go | 2 ++ ucopev/events_test.go | 6 ++++++ ucopev/testhelper_test.go | 2 ++ ucoscev/testhelper_test.go | 2 ++ ucvabd/events_test.go | 6 ++++++ ucvabd/testhelper_test.go | 2 ++ ucvapd/events_test.go | 8 ++++++++ ucvapd/testhelper_test.go | 2 ++ 23 files changed, 108 insertions(+) diff --git a/uccevc/events_test.go b/uccevc/events_test.go index 4293d1f..34fa2a7 100644 --- a/uccevc/events_test.go +++ b/uccevc/events_test.go @@ -41,6 +41,23 @@ func (s *UCCEVCSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCCEVCSuite) Test_Failures() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.evConnected(s.mockRemoteEntity) + + s.sut.evTimeSeriesDescriptionDataUpdate(payload) + + s.sut.evTimeSeriesDataUpdate(payload) + + s.sut.evIncentiveTableDescriptionDataUpdate(payload) + + s.sut.evCheckTimeSeriesDescriptionConstraintsUpdateRequired(s.mockRemoteEntity) + + s.sut.evCheckIncentiveTableDescriptionUpdateRequired(s.mockRemoteEntity) +} + func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/uccevc/testhelper_test.go b/uccevc/testhelper_test.go index 00385a8..cce39bc 100644 --- a/uccevc/testhelper_test.go +++ b/uccevc/testhelper_test.go @@ -65,6 +65,8 @@ func (s *UCCEVCSuite) BeforeTest(suiteName, testName string) { ops := map[model.FunctionType]spineapi.OperationsInterface{} mockRemoteFeature.EXPECT().Operations().Return(ops).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCCEVC(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucevcc/events_test.go b/ucevcc/events_test.go index 4f00e45..0154cbf 100644 --- a/ucevcc/events_test.go +++ b/ucevcc/events_test.go @@ -40,6 +40,10 @@ func (s *UCEVCCSuite) Test_Events() { payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) s.sut.HandleEvent(payload) + var value model.DeviceDiagnosisOperatingStateType + payload.Data = &value + s.sut.HandleEvent(payload) + payload.Data = eebusutil.Ptr(model.DeviceClassificationManufacturerDataType{}) s.sut.HandleEvent(payload) @@ -53,6 +57,17 @@ func (s *UCEVCCSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCEVCCSuite) Test_Failures() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.evConnected(payload) + + s.sut.evConfigurationDescriptionDataUpdate(s.mockRemoteEntity) + + s.sut.evElectricalParamerDescriptionUpdate(s.mockRemoteEntity) +} + func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index 8f84e19..a688be0 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCEVCC(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucevcem/events_test.go b/ucevcem/events_test.go index 6aab04a..a03eca6 100644 --- a/ucevcem/events_test.go +++ b/ucevcem/events_test.go @@ -36,6 +36,12 @@ func (s *UCEVCEMSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCEVCEMSuite) Test_Failures() { + s.sut.evConnected(s.mockRemoteEntity) + + s.sut.evMeasurementDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCEVCEMSuite) Test_evElectricalConnectionDescriptionDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucevcem/testhelper_test.go b/ucevcem/testhelper_test.go index 1b9f52e..6478268 100644 --- a/ucevcem/testhelper_test.go +++ b/ucevcem/testhelper_test.go @@ -60,6 +60,8 @@ func (s *UCEVCEMSuite) BeforeTest(suiteName, testName string) { s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCEVCEM(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucevsecc/testhelper_test.go b/ucevsecc/testhelper_test.go index 951c4f0..f835343 100644 --- a/ucevsecc/testhelper_test.go +++ b/ucevsecc/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCEVSECCSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCEVSECC(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucevsoc/events_test.go b/ucevsoc/events_test.go index 87f9931..81727b2 100644 --- a/ucevsoc/events_test.go +++ b/ucevsoc/events_test.go @@ -30,6 +30,10 @@ func (s *UCEVSOCSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCEVSOCSuite) Test_Failures() { + s.sut.evConnected(s.mockRemoteEntity) +} + func (s *UCEVSOCSuite) Test_evMeasurementDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucevsoc/testhelper_test.go b/ucevsoc/testhelper_test.go index 6918e8c..9c14bf2 100644 --- a/ucevsoc/testhelper_test.go +++ b/ucevsoc/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCEVSOCSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCEVSOC(s.service, s.Event) s.sut.AddFeatures() diff --git a/uclpc/events_test.go b/uclpc/events_test.go index 7599da7..898993c 100644 --- a/uclpc/events_test.go +++ b/uclpc/events_test.go @@ -39,6 +39,12 @@ func (s *UCLPCSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCLPCSuite) Test_Failures() { + s.sut.connected(s.mockRemoteEntity) + + s.sut.configurationDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCLPCSuite) Test_loadControlLimitDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/uclpc/testhelper_test.go b/uclpc/testhelper_test.go index 5889ece..1f58fb9 100644 --- a/uclpc/testhelper_test.go +++ b/uclpc/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCLPCSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCLPC(s.service, s.Event) s.sut.AddFeatures() diff --git a/uclpcserver/testhelper_test.go b/uclpcserver/testhelper_test.go index 6828838..cc08aaa 100644 --- a/uclpcserver/testhelper_test.go +++ b/uclpcserver/testhelper_test.go @@ -65,6 +65,8 @@ func (s *UCLPCServerSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCLPC(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucmgcp/events_test.go b/ucmgcp/events_test.go index bb2f6c8..c7cebb4 100644 --- a/ucmgcp/events_test.go +++ b/ucmgcp/events_test.go @@ -36,6 +36,14 @@ func (s *UCMGCPSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCMGCPSuite) Test_Failures() { + s.sut.gridConnected(s.mockRemoteEntity) + + s.sut.gridConfigurationDescriptionDataUpdate(s.mockRemoteEntity) + + s.sut.gridMeasurementDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCMGCPSuite) Test_gridConfigurationDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucmgcp/testhelper_test.go b/ucmgcp/testhelper_test.go index ccef3e4..338ee77 100644 --- a/ucmgcp/testhelper_test.go +++ b/ucmgcp/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCMGCPSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCMGCP(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucmpc/events_test.go b/ucmpc/events_test.go index ab6ba42..3494d3a 100644 --- a/ucmpc/events_test.go +++ b/ucmpc/events_test.go @@ -33,6 +33,12 @@ func (s *UCMPCSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCMPCSuite) Test_Failures() { + s.sut.deviceConnected(s.mockRemoteEntity) + + s.sut.deviceMeasurementDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCMPCSuite) Test_deviceMeasurementDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucmpc/testhelper_test.go b/ucmpc/testhelper_test.go index 57a1d3f..a26e07e 100644 --- a/ucmpc/testhelper_test.go +++ b/ucmpc/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCMPCSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCMPC(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucopev/events_test.go b/ucopev/events_test.go index fa2f91c..6039a6e 100644 --- a/ucopev/events_test.go +++ b/ucopev/events_test.go @@ -33,6 +33,12 @@ func (s *UCOPEVSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCOPEVSuite) Test_Failures() { + s.sut.evConnected(s.mockRemoteEntity) + + s.sut.evLoadControlLimitDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index 5c73af2..f064c5f 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCOPEVSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCOPEV(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index a89256a..af8ac1a 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCOSCEVSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCOSCEV(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucvabd/events_test.go b/ucvabd/events_test.go index 40c06f3..6482df4 100644 --- a/ucvabd/events_test.go +++ b/ucvabd/events_test.go @@ -39,6 +39,12 @@ func (s *UCVABDSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCVABDSuite) Test_Failures() { + s.sut.inverterConnected(s.mockRemoteEntity) + + s.sut.inverterMeasurementDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCVABDSuite) Test_inverterMeasurementDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucvabd/testhelper_test.go b/ucvabd/testhelper_test.go index f26937b..a8fd708 100644 --- a/ucvabd/testhelper_test.go +++ b/ucvabd/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCVABDSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCVABD(s.service, s.Event) s.sut.AddFeatures() diff --git a/ucvapd/events_test.go b/ucvapd/events_test.go index 5b10b39..0a8e1b3 100644 --- a/ucvapd/events_test.go +++ b/ucvapd/events_test.go @@ -39,6 +39,14 @@ func (s *UCVAPDSuite) Test_Events() { s.sut.HandleEvent(payload) } +func (s *UCVAPDSuite) Test_Failures() { + s.sut.inverterConnected(s.mockRemoteEntity) + + s.sut.inverterConfigurationDescriptionDataUpdate(s.mockRemoteEntity) + + s.sut.inverterMeasurementDescriptionDataUpdate(s.mockRemoteEntity) +} + func (s *UCVAPDSuite) Test_inverterConfigurationDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucvapd/testhelper_test.go b/ucvapd/testhelper_test.go index 7ad9022..f1087d7 100644 --- a/ucvapd/testhelper_test.go +++ b/ucvapd/testhelper_test.go @@ -63,6 +63,8 @@ func (s *UCVAPDSuite) BeforeTest(suiteName, testName string) { entityAddress := &model.EntityAddressType{} s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() s.sut = NewUCVAPD(s.service, s.Event) s.sut.AddFeatures() From cc8278ab0aea4be0d88900bdc60a550c8c12e412 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 11 Mar 2024 16:38:23 +0100 Subject: [PATCH 137/227] Update eebus, spine and adopt API changes --- go.mod | 4 ++-- go.sum | 8 ++++---- uccevc/results.go | 8 -------- uccevc/uccevc.go | 3 +-- ucevcc/results.go | 44 +++++++++++++++++++++++++++--------------- ucevcc/results_test.go | 30 ++++++++++++++++++++-------- ucevcc/ucevcc.go | 2 +- ucevcem/results.go | 8 -------- ucevcem/ucevcem.go | 3 +-- ucevsecc/results.go | 8 -------- ucevsecc/ucevsecc.go | 3 +-- ucevsoc/results.go | 8 -------- ucevsoc/ucevsoc.go | 3 +-- uclpc/results.go | 8 -------- uclpc/uclpc.go | 23 ++++++++++------------ uclpcserver/results.go | 8 -------- uclpcserver/uclpc.go | 9 ++------- ucmgcp/results.go | 8 -------- ucmgcp/ucmgcp.go | 3 +-- ucmpc/results.go | 8 -------- ucmpc/ucmcp.go | 3 +-- ucopev/results.go | 8 -------- ucopev/ucopev.go | 4 +--- ucoscev/results.go | 8 -------- ucoscev/ucoscev.go | 4 +--- ucvabd/results.go | 8 -------- ucvabd/ucvabd.go | 3 +-- ucvapd/results.go | 8 -------- ucvapd/ucvapd.go | 4 +--- 29 files changed, 79 insertions(+), 170 deletions(-) delete mode 100644 uccevc/results.go delete mode 100644 ucevcem/results.go delete mode 100644 ucevsecc/results.go delete mode 100644 ucevsoc/results.go delete mode 100644 uclpc/results.go delete mode 100644 uclpcserver/results.go delete mode 100644 ucmgcp/results.go delete mode 100644 ucmpc/results.go delete mode 100644 ucopev/results.go delete mode 100644 ucoscev/results.go delete mode 100644 ucvabd/results.go delete mode 100644 ucvapd/results.go diff --git a/go.mod b/go.mod index d85ed88..bcc587d 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240309182657-90c4d4f160f0 + github.com/enbility/eebus-go v0.0.0-20240311152814-5a60b22b2f4c github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240310083958-60c482a7d94f + github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 2fe089f..36d910a 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240309182657-90c4d4f160f0 h1:gX45I8v5bel3KyFDsqgtUEZx7ZOh/i4hEI4tkxBefFY= -github.com/enbility/eebus-go v0.0.0-20240309182657-90c4d4f160f0/go.mod h1:+tNdP83/zUufClY2y4r3iCeE+mHpDalbWgo5VmRT3fs= +github.com/enbility/eebus-go v0.0.0-20240311152814-5a60b22b2f4c h1:oZm/DU0q1nFdk3l+H6CuZNIbzbKRfIV7py+q0u+17Wo= +github.com/enbility/eebus-go v0.0.0-20240311152814-5a60b22b2f4c/go.mod h1:nMbsMRf+Sw5M8hpz9XHs+3S7GXtMC9sHsCF3HBBFpvo= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240310083958-60c482a7d94f h1:JEFBR3+G/1TGEBmTLZv9VZBAI3O4UtxrQ44sqIhx418= -github.com/enbility/spine-go v0.0.0-20240310083958-60c482a7d94f/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 h1:5oHCw3s4igUttQwEpf5yRNly3GNSQtMX9V652b9VWlU= +github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/uccevc/results.go b/uccevc/results.go deleted file mode 100644 index 946ad45..0000000 --- a/uccevc/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package uccevc - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCCEVC) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/uccevc/uccevc.go b/uccevc/uccevc.go index c8d3594..8f178f1 100644 --- a/uccevc/uccevc.go +++ b/uccevc/uccevc.go @@ -49,8 +49,7 @@ func (e *UCCEVC) AddFeatures() { model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/ucevcc/results.go b/ucevcc/results.go index 6748b0a..4a6b237 100644 --- a/ucevcc/results.go +++ b/ucevcc/results.go @@ -7,32 +7,44 @@ import ( "github.com/enbility/spine-go/model" ) -func (e *UCEVCC) HandleResult(errorMsg api.ResultMessage) { +func (e *UCEVCC) HandleResponse(responseMsg api.ResponseMessage) { // before SPINE 1.3 the heartbeats are on the EVSE entity - if errorMsg.EntityRemote == nil || - (errorMsg.EntityRemote.EntityType() != model.EntityTypeTypeEV && - errorMsg.EntityRemote.EntityType() != model.EntityTypeTypeEVSE) { + if responseMsg.EntityRemote == nil || + (responseMsg.EntityRemote.EntityType() != model.EntityTypeTypeEV && + responseMsg.EntityRemote.EntityType() != model.EntityTypeTypeEVSE) { return } // handle errors coming from the remote EVSE entity - if errorMsg.FeatureLocal.Type() == model.FeatureTypeTypeDeviceDiagnosis { - e.handleResultDeviceDiagnosis(errorMsg) + if responseMsg.FeatureLocal.Type() == model.FeatureTypeTypeDeviceDiagnosis { + e.handleResultDeviceDiagnosis(responseMsg) } } // Handle DeviceDiagnosis Results -func (e *UCEVCC) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { +func (e *UCEVCC) handleResultDeviceDiagnosis(responseMsg api.ResponseMessage) { // is this an error for a heartbeat message? - if resultMsg.DeviceRemote == nil || - resultMsg.Result == nil || - resultMsg.Result.ErrorNumber == nil || - *resultMsg.Result.ErrorNumber == model.ErrorNumberTypeNoError { + if responseMsg.DeviceRemote == nil || + responseMsg.Data == nil { + return + } + + var result *model.ResultDataType + + switch responseMsg.Data.(type) { + case *model.ResultDataType: + result = responseMsg.Data.(*model.ResultDataType) + default: + return + } + + if result.ErrorNumber == nil || + *result.ErrorNumber == model.ErrorNumberTypeNoError { return } // check if this is for a cached notify message - datagram, err := resultMsg.DeviceRemote.Sender().DatagramForMsgCounter(resultMsg.MsgCounterReference) + datagram, err := responseMsg.DeviceRemote.Sender().DatagramForMsgCounter(responseMsg.MsgCounterReference) if err != nil { return } @@ -40,10 +52,10 @@ func (e *UCEVCC) handleResultDeviceDiagnosis(resultMsg api.ResultMessage) { if len(datagram.Payload.Cmd) > 0 && datagram.Payload.Cmd[0].DeviceDiagnosisHeartbeatData != nil { // something is horribly wrong, disconnect and hope a new connection will fix it - errorText := fmt.Sprintf("Error Code: %d", resultMsg.Result.ErrorNumber) - if resultMsg.Result.Description != nil { - errorText = fmt.Sprintf("%s - %s", errorText, string(*resultMsg.Result.Description)) + errorText := fmt.Sprintf("Error Code: %d", result.ErrorNumber) + if result.Description != nil { + errorText = fmt.Sprintf("%s - %s", errorText, string(*result.Description)) } - e.service.DisconnectSKI(resultMsg.DeviceRemote.Ski(), errorText) + e.service.DisconnectSKI(responseMsg.DeviceRemote.Ski(), errorText) } } diff --git a/ucevcc/results_test.go b/ucevcc/results_test.go index 7f2abd6..11a93bc 100644 --- a/ucevcc/results_test.go +++ b/ucevcc/results_test.go @@ -13,20 +13,35 @@ func (s *UCEVCCSuite) Test_Results() { localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) localFeature := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - errorMsg := spineapi.ResultMessage{ + errorMsg := spineapi.ResponseMessage{ + DeviceRemote: s.remoteDevice, + EntityRemote: s.evEntity, + FeatureLocal: localFeature, + Data: eebusutil.Ptr(model.MsgCounterType(0)), + } + s.sut.HandleResponse(errorMsg) + + errorMsg = spineapi.ResponseMessage{ + EntityRemote: s.evEntity, + FeatureLocal: localFeature, + Data: eebusutil.Ptr(model.MsgCounterType(0)), + } + s.sut.HandleResponse(errorMsg) + + errorMsg = spineapi.ResponseMessage{ DeviceRemote: s.remoteDevice, EntityRemote: s.mockRemoteEntity, FeatureLocal: localFeature, - Result: &model.ResultDataType{ + Data: &model.ResultDataType{ ErrorNumber: eebusutil.Ptr(model.ErrorNumberTypeNoError), }, } - s.sut.HandleResult(errorMsg) + s.sut.HandleResponse(errorMsg) errorMsg.EntityRemote = s.evEntity - s.sut.HandleResult(errorMsg) + s.sut.HandleResponse(errorMsg) - errorMsg.Result = &model.ResultDataType{ + errorMsg.Data = &model.ResultDataType{ ErrorNumber: eebusutil.Ptr(model.ErrorNumberTypeGeneralError), Description: eebusutil.Ptr(model.DescriptionType("test error")), } @@ -37,7 +52,7 @@ func (s *UCEVCCSuite) Test_Results() { DatagramForMsgCounter(errorMsg.MsgCounterReference). Return(model.DatagramType{}, errors.New("test")).Once() - s.sut.HandleResult(errorMsg) + s.sut.HandleResponse(errorMsg) datagram := model.DatagramType{ Payload: model.PayloadType{ @@ -53,6 +68,5 @@ func (s *UCEVCCSuite) Test_Results() { DatagramForMsgCounter(errorMsg.MsgCounterReference). Return(datagram, nil).Once() - s.sut.HandleResult(errorMsg) - + s.sut.HandleResponse(errorMsg) } diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 3899078..1980195 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -51,7 +51,7 @@ func (e *UCEVCC) AddFeatures() { } for _, feature := range clientFeatures { f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + f.AddResultCallback(e.HandleResponse) } } diff --git a/ucevcem/results.go b/ucevcem/results.go deleted file mode 100644 index e60d387..0000000 --- a/ucevcem/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucevcem - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCEVCEM) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index cf98446..a479476 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -47,8 +47,7 @@ func (e *UCEVCEM) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/ucevsecc/results.go b/ucevsecc/results.go deleted file mode 100644 index eca7cac..0000000 --- a/ucevsecc/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucevsecc - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCEVSECC) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 96ef6b8..f69b720 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -48,8 +48,7 @@ func (e *UCEVSECC) AddFeatures() { } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/ucevsoc/results.go b/ucevsoc/results.go deleted file mode 100644 index 480747c..0000000 --- a/ucevsoc/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucevsoc - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCEVSOC) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucevsoc/ucevsoc.go b/ucevsoc/ucevsoc.go index 2280ece..46b80a2 100644 --- a/ucevsoc/ucevsoc.go +++ b/ucevsoc/ucevsoc.go @@ -47,8 +47,7 @@ func (e *UCEVSOC) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/uclpc/results.go b/uclpc/results.go deleted file mode 100644 index d6a459f..0000000 --- a/uclpc/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package uclpc - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCLPC) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/uclpc/uclpc.go b/uclpc/uclpc.go index 13b5a0c..9f30032 100644 --- a/uclpc/uclpc.go +++ b/uclpc/uclpc.go @@ -47,22 +47,19 @@ func (e *UCLPC) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - f.AddResultHandler(e) - - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeClient) - f.AddResultHandler(e) - - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) - f.AddResultHandler(e) - - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) - f.AddResultHandler(e) + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceDiagnosis, + model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + } + for _, feature := range clientFeatures { + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + } // server features - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) - f.AddResultHandler(e) } func (e *UCLPC) AddUseCase() { diff --git a/uclpcserver/results.go b/uclpcserver/results.go deleted file mode 100644 index a7cb991..0000000 --- a/uclpcserver/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package uclpcserver - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCLPCServer) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 61012ae..02c6895 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -43,14 +43,12 @@ func (e *UCLPCServer) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) // client features - f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) // server features - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) - f.AddResultHandler(e) var limitId model.LoadControlLimitIdType = 0 // get the highest limitId @@ -80,7 +78,6 @@ func (e *UCLPCServer) AddFeatures() { f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) - f.AddResultHandler(e) var configId model.DeviceConfigurationKeyIdType = 0 // get the heighest keyId @@ -112,11 +109,9 @@ func (e *UCLPCServer) AddFeatures() { f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, true) - f.AddResultHandler(e) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) - f.AddResultHandler(e) var elCharId model.ElectricalConnectionCharacteristicIdType = 0 // get the heighest CharacteristicId diff --git a/ucmgcp/results.go b/ucmgcp/results.go deleted file mode 100644 index f04162b..0000000 --- a/ucmgcp/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucmgcp - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCMGCP) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index 71ad484..4569b41 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -48,8 +48,7 @@ func (e *UCMGCP) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/ucmpc/results.go b/ucmpc/results.go deleted file mode 100644 index ca18d6a..0000000 --- a/ucmpc/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucmpc - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCMPC) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index a4096b4..24ae903 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -53,8 +53,7 @@ func (e *UCMPC) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/ucopev/results.go b/ucopev/results.go deleted file mode 100644 index ee128d5..0000000 --- a/ucopev/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucopev - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCOPEV) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 5f9bc02..20ddc3b 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -47,14 +47,12 @@ func (e *UCOPEV) AddFeatures() { model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } // server features f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, false, false) - f.AddResultHandler(e) } func (e *UCOPEV) AddUseCase() { diff --git a/ucoscev/results.go b/ucoscev/results.go deleted file mode 100644 index e1462bc..0000000 --- a/ucoscev/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucoscev - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCOSCEV) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 55dd156..470002f 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -53,14 +53,12 @@ func (e *UCOSCEV) AddFeatures() { model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } // server features f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, false, false) - f.AddResultHandler(e) } func (e *UCOSCEV) AddUseCase() { diff --git a/ucvabd/results.go b/ucvabd/results.go deleted file mode 100644 index 4525487..0000000 --- a/ucvabd/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucvabd - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCVABD) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucvabd/ucvabd.go b/ucvabd/ucvabd.go index e861142..7b824f2 100644 --- a/ucvabd/ucvabd.go +++ b/ucvabd/ucvabd.go @@ -48,8 +48,7 @@ func (e *UCVABD) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } } diff --git a/ucvapd/results.go b/ucvapd/results.go deleted file mode 100644 index 5a4b0e4..0000000 --- a/ucvapd/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package ucvapd - -import ( - "github.com/enbility/spine-go/api" -) - -func (e *UCVAPD) HandleResult(errorMsg api.ResultMessage) { -} diff --git a/ucvapd/ucvapd.go b/ucvapd/ucvapd.go index 0ffbc1f..ea768d7 100644 --- a/ucvapd/ucvapd.go +++ b/ucvapd/ucvapd.go @@ -47,10 +47,8 @@ func (e *UCVAPD) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - f := localEntity.GetOrAddFeature(feature, model.RoleTypeClient) - f.AddResultHandler(e) + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) } - } func (e *UCVAPD) AddUseCase() { From 5a72b48eeac90c9e08d5d65f20564c0dad610013 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 13 Mar 2024 13:07:25 +0100 Subject: [PATCH 138/227] Update SHIP, SPINE, EEBUS --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index bcc587d..32c344b 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240311152814-5a60b22b2f4c - github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 - github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 + github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800 + github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da + github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 36d910a..b9c4884 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240311152814-5a60b22b2f4c h1:oZm/DU0q1nFdk3l+H6CuZNIbzbKRfIV7py+q0u+17Wo= -github.com/enbility/eebus-go v0.0.0-20240311152814-5a60b22b2f4c/go.mod h1:nMbsMRf+Sw5M8hpz9XHs+3S7GXtMC9sHsCF3HBBFpvo= -github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711 h1:gne+vZK4BEEzLdh4fHtYqKDlSgIG3nI1jnlRxQLGG0M= -github.com/enbility/ship-go v0.0.0-20240304152211-acb2920b6711/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654 h1:5oHCw3s4igUttQwEpf5yRNly3GNSQtMX9V652b9VWlU= -github.com/enbility/spine-go v0.0.0-20240311152248-880d656d4654/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800 h1:qMapr0u+13DLw5Q6mMHMUjUQHVW7iXyGoDRgMyY6PkY= +github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800/go.mod h1:30YZwH+GzLqK4Q2NxvrreI9Ib4LUSeT7c6Sbpvr9+Bk= +github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da h1:+O2QSawKtKxDYgVip/C/KYcRBA0NrwwuwSIklsLDWnI= +github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= +github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 169b27707fa27cb84581a766ab93a9225f2268d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Wed, 13 Mar 2024 14:49:32 +0100 Subject: [PATCH 139/227] Extend Manufacturer Data with all available data. --- ucevcc/api.go | 17 ++++++++++++++++- ucevcc/public.go | 32 ++++++++++++++++++-------------- ucevcc/public_test.go | 30 +++++++++++++++--------------- ucevsecc/api.go | 16 +++++++++++++++- ucevsecc/public.go | 33 ++++++++++++++++++--------------- ucevsecc/public_test.go | 30 +++++++++++++++--------------- util/helper.go | 7 +++++++ 7 files changed, 104 insertions(+), 61 deletions(-) diff --git a/ucevcc/api.go b/ucevcc/api.go index b41702d..09fc4d0 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -8,6 +8,21 @@ import ( //go:generate mockery +type ManufacturerData struct { + DeviceName string `json:"deviceName,omitempty"` + DeviceCode string `json:"deviceCode,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + SoftwareRevision string `json:"softwareRevision,omitempty"` + HardwareRevision string `json:"hardwareRevision,omitempty"` + VendorName string `json:"vendorName,omitempty"` + VendorCode string `json:"vendorCode,omitempty"` + BrandName string `json:"brandName,omitempty"` + PowerSource string `json:"powerSource,omitempty"` + ManufacturerNodeIdentification string `json:"manufacturerNodeIdentification,omitempty"` + ManufacturerLabel string `json:"manufacturerLabel,omitempty"` + ManufacturerDescription string `json:"manufacturerDescription,omitempty"` +} + // interface for the EV Commissioning and Configuration UseCase type UCEVCCInterface interface { api.UseCaseInterface @@ -58,7 +73,7 @@ type UCEVCCInterface interface { // // parameters: // - entity: the entity of the EV - ManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (*ManufacturerData, error) // Scenario 6 diff --git a/ucevcc/public.go b/ucevcc/public.go index 7d7e93b..c965cd4 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -198,36 +198,40 @@ func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.I func (e *UCEVCC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( - string, - string, + *ManufacturerData, error, ) { - deviceName := "" - serialNumber := "" if !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return deviceName, serialNumber, api.ErrNoCompatibleEntity + return nil, api.ErrNoCompatibleEntity } evDeviceClassification, err := util.DeviceClassification(e.service, entity) if err != nil { - return deviceName, serialNumber, eebusapi.ErrDataNotAvailable + return nil, eebusapi.ErrDataNotAvailable } data, err := evDeviceClassification.GetManufacturerDetails() if err != nil { - return deviceName, serialNumber, err - } - - if data.DeviceName != nil { - deviceName = string(*data.DeviceName) + return nil, err } - if data.SerialNumber != nil { - serialNumber = string(*data.SerialNumber) + ret := &ManufacturerData{ + DeviceName: util.Deref((*string)(data.DeviceName)), + DeviceCode: util.Deref((*string)(data.DeviceCode)), + SerialNumber: util.Deref((*string)(data.SerialNumber)), + SoftwareRevision: util.Deref((*string)(data.SoftwareRevision)), + HardwareRevision: util.Deref((*string)(data.HardwareRevision)), + VendorName: util.Deref((*string)(data.VendorName)), + VendorCode: util.Deref((*string)(data.VendorCode)), + BrandName: util.Deref((*string)(data.BrandName)), + PowerSource: util.Deref((*string)(data.PowerSource)), + ManufacturerNodeIdentification: util.Deref((*string)(data.ManufacturerNodeIdentification)), + ManufacturerLabel: util.Deref((*string)(data.ManufacturerLabel)), + ManufacturerDescription: util.Deref((*string)(data.ManufacturerDescription)), } - return deviceName, serialNumber, nil + return ret, nil } // return the min, max, default limits for each phase of the connected EV diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index 1b3c643..a57b254 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -254,20 +254,17 @@ func (s *UCEVCCSuite) Test_EVIdentification() { } func (s *UCEVCCSuite) Test_EVManufacturerData() { - device, serial, err := s.sut.ManufacturerData(s.mockRemoteEntity) + data, err := s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.Nil(s.T(), data) - device, serial, err = s.sut.ManufacturerData(s.evEntity) + data, err = s.sut.ManufacturerData(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.Nil(s.T(), data) - device, serial, err = s.sut.ManufacturerData(s.evEntity) + data, err = s.sut.ManufacturerData(s.evEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.Nil(s.T(), data) descData := &model.DeviceClassificationManufacturerDataType{} @@ -275,10 +272,11 @@ func (s *UCEVCCSuite) Test_EVManufacturerData() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.ManufacturerData(s.evEntity) + data, err = s.sut.ManufacturerData(s.evEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.NotNil(s.T(), data) + assert.Equal(s.T(), "", data.DeviceName) + assert.Equal(s.T(), "", data.SerialNumber) descData = &model.DeviceClassificationManufacturerDataType{ DeviceName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), @@ -288,10 +286,12 @@ func (s *UCEVCCSuite) Test_EVManufacturerData() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.ManufacturerData(s.evEntity) + data, err = s.sut.ManufacturerData(s.evEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), "test", device) - assert.Equal(s.T(), "12345", serial) + assert.NotNil(s.T(), data) + assert.Equal(s.T(), "test", data.DeviceName) + assert.Equal(s.T(), "12345", data.SerialNumber) + assert.Equal(s.T(), "", data.BrandName) } func (s *UCEVCCSuite) Test_EVCurrentLimits() { diff --git a/ucevsecc/api.go b/ucevsecc/api.go index 9a37cc4..1ebead7 100644 --- a/ucevsecc/api.go +++ b/ucevsecc/api.go @@ -7,6 +7,20 @@ import ( ) //go:generate mockery +type ManufacturerData struct { + DeviceName string `json:"deviceName,omitempty"` + DeviceCode string `json:"deviceCode,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + SoftwareRevision string `json:"softwareRevision,omitempty"` + HardwareRevision string `json:"hardwareRevision,omitempty"` + VendorName string `json:"vendorName,omitempty"` + VendorCode string `json:"vendorCode,omitempty"` + BrandName string `json:"brandName,omitempty"` + PowerSource string `json:"powerSource,omitempty"` + ManufacturerNodeIdentification string `json:"manufacturerNodeIdentification,omitempty"` + ManufacturerLabel string `json:"manufacturerLabel,omitempty"` + ManufacturerDescription string `json:"manufacturerDescription,omitempty"` +} // interface for the EVSE Commissioning and Configuration UseCase type UCEVSECCInterface interface { @@ -18,7 +32,7 @@ type UCEVSECCInterface interface { // - entity: the entity of the EV // // returns deviceName, serialNumber, error - ManufacturerData(entity spineapi.EntityRemoteInterface) (string, string, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (*ManufacturerData, error) // the operating state data of an EVSE // diff --git a/ucevsecc/public.go b/ucevsecc/public.go index de8ec31..e04b2f2 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -12,36 +12,39 @@ import ( func (e *UCEVSECC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( - string, - string, + *ManufacturerData, error, ) { - deviceName := "" - serialNumber := "" - if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return deviceName, serialNumber, api.ErrNoCompatibleEntity + return nil, api.ErrNoCompatibleEntity } evseDeviceClassification, err := util.DeviceClassification(e.service, entity) if err != nil { - return deviceName, serialNumber, err + return nil, err } data, err := evseDeviceClassification.GetManufacturerDetails() if err != nil { - return deviceName, serialNumber, err - } - - if data.DeviceName != nil { - deviceName = string(*data.DeviceName) + return nil, err } - if data.SerialNumber != nil { - serialNumber = string(*data.SerialNumber) + ret := &ManufacturerData{ + DeviceName: util.Deref((*string)(data.DeviceName)), + DeviceCode: util.Deref((*string)(data.DeviceCode)), + SerialNumber: util.Deref((*string)(data.SerialNumber)), + SoftwareRevision: util.Deref((*string)(data.SoftwareRevision)), + HardwareRevision: util.Deref((*string)(data.HardwareRevision)), + VendorName: util.Deref((*string)(data.VendorName)), + VendorCode: util.Deref((*string)(data.VendorCode)), + BrandName: util.Deref((*string)(data.BrandName)), + PowerSource: util.Deref((*string)(data.PowerSource)), + ManufacturerNodeIdentification: util.Deref((*string)(data.ManufacturerNodeIdentification)), + ManufacturerLabel: util.Deref((*string)(data.ManufacturerLabel)), + ManufacturerDescription: util.Deref((*string)(data.ManufacturerDescription)), } - return deviceName, serialNumber, nil + return ret, nil } // the operating state data of an EVSE diff --git a/ucevsecc/public_test.go b/ucevsecc/public_test.go index 3b92c8c..53b70aa 100644 --- a/ucevsecc/public_test.go +++ b/ucevsecc/public_test.go @@ -7,20 +7,17 @@ import ( ) func (s *UCEVSECCSuite) Test_EVSEManufacturerData() { - device, serial, err := s.sut.ManufacturerData(nil) + data, err := s.sut.ManufacturerData(nil) assert.NotNil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.Nil(s.T(), data) - device, serial, err = s.sut.ManufacturerData(s.mockRemoteEntity) + data, err = s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.Nil(s.T(), data) - device, serial, err = s.sut.ManufacturerData(s.evseEntity) + data, err = s.sut.ManufacturerData(s.evseEntity) assert.NotNil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.Nil(s.T(), data) descData := &model.DeviceClassificationManufacturerDataType{} @@ -28,10 +25,11 @@ func (s *UCEVSECCSuite) Test_EVSEManufacturerData() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.ManufacturerData(s.evseEntity) + data, err = s.sut.ManufacturerData(s.evseEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), "", device) - assert.Equal(s.T(), "", serial) + assert.NotNil(s.T(), data) + assert.Equal(s.T(), "", data.DeviceName) + assert.Equal(s.T(), "", data.SerialNumber) descData = &model.DeviceClassificationManufacturerDataType{ DeviceName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), @@ -41,10 +39,12 @@ func (s *UCEVSECCSuite) Test_EVSEManufacturerData() { fErr = rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - device, serial, err = s.sut.ManufacturerData(s.evseEntity) + data, err = s.sut.ManufacturerData(s.evseEntity) assert.Nil(s.T(), err) - assert.Equal(s.T(), "test", device) - assert.Equal(s.T(), "12345", serial) + assert.NotNil(s.T(), data) + assert.Equal(s.T(), "test", data.DeviceName) + assert.Equal(s.T(), "12345", data.SerialNumber) + assert.Equal(s.T(), "", data.BrandName) } func (s *UCEVSECCSuite) Test_EVSEOperatingState() { diff --git a/util/helper.go b/util/helper.go index 64e7283..459a4a6 100644 --- a/util/helper.go +++ b/util/helper.go @@ -48,3 +48,10 @@ func IsEntityDisconnected(payload spineapi.EventPayload) bool { return false } + +func Deref(v *string) string { + if v != nil { + return string(*v) + } + return "" +} From a27348e695c4e535cf3544973942df77f89d572b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Wed, 13 Mar 2024 14:57:41 +0100 Subject: [PATCH 140/227] Update mocks for new Interface --- mocks/UCEVCCInterface.go | 35 ++++++++++++++++------------------- mocks/UCEVSECCInterface.go | 35 ++++++++++++++++------------------- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/mocks/UCEVCCInterface.go b/mocks/UCEVCCInterface.go index e00c572..4acdf09 100644 --- a/mocks/UCEVCCInterface.go +++ b/mocks/UCEVCCInterface.go @@ -9,6 +9,8 @@ import ( mock "github.com/stretchr/testify/mock" model "github.com/enbility/spine-go/model" + + ucevcc "github.com/enbility/cemd/ucevcc" ) // UCEVCCInterface is an autogenerated mock type for the UCEVCCInterface type @@ -549,38 +551,33 @@ func (_c *UCEVCCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.Ent } // ManufacturerData provides a mock function with given fields: entity -func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (string, string, error) { +func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*ucevcc.ManufacturerData, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for ManufacturerData") } - var r0 string - var r1 string - var r2 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (string, string, error)); ok { + var r0 *ucevcc.ManufacturerData + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*ucevcc.ManufacturerData, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) string); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *ucevcc.ManufacturerData); ok { r0 = rf(entity) } else { - r0 = ret.Get(0).(string) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ucevcc.ManufacturerData) + } } - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) string); ok { + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { r1 = rf(entity) } else { - r1 = ret.Get(1).(string) - } - - if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) error); ok { - r2 = rf(entity) - } else { - r2 = ret.Error(2) + r1 = ret.Error(1) } - return r0, r1, r2 + return r0, r1 } // UCEVCCInterface_ManufacturerData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ManufacturerData' @@ -601,12 +598,12 @@ func (_c *UCEVCCInterface_ManufacturerData_Call) Run(run func(entity api.EntityR return _c } -func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 string, _a1 string, _a2 error) *UCEVCCInterface_ManufacturerData_Call { - _c.Call.Return(_a0, _a1, _a2) +func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 *ucevcc.ManufacturerData, _a1 error) *UCEVCCInterface_ManufacturerData_Call { + _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (string, string, error)) *UCEVCCInterface_ManufacturerData_Call { +func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*ucevcc.ManufacturerData, error)) *UCEVCCInterface_ManufacturerData_Call { _c.Call.Return(run) return _c } diff --git a/mocks/UCEVSECCInterface.go b/mocks/UCEVSECCInterface.go index a5ae5cb..3324963 100644 --- a/mocks/UCEVSECCInterface.go +++ b/mocks/UCEVSECCInterface.go @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" model "github.com/enbility/spine-go/model" + + ucevsecc "github.com/enbility/cemd/ucevsecc" ) // UCEVSECCInterface is an autogenerated mock type for the UCEVSECCInterface type @@ -143,38 +145,33 @@ func (_c *UCEVSECCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.E } // ManufacturerData provides a mock function with given fields: entity -func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (string, string, error) { +func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*ucevsecc.ManufacturerData, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for ManufacturerData") } - var r0 string - var r1 string - var r2 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (string, string, error)); ok { + var r0 *ucevsecc.ManufacturerData + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*ucevsecc.ManufacturerData, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) string); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *ucevsecc.ManufacturerData); ok { r0 = rf(entity) } else { - r0 = ret.Get(0).(string) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ucevsecc.ManufacturerData) + } } - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) string); ok { + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { r1 = rf(entity) } else { - r1 = ret.Get(1).(string) - } - - if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) error); ok { - r2 = rf(entity) - } else { - r2 = ret.Error(2) + r1 = ret.Error(1) } - return r0, r1, r2 + return r0, r1 } // UCEVSECCInterface_ManufacturerData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ManufacturerData' @@ -195,12 +192,12 @@ func (_c *UCEVSECCInterface_ManufacturerData_Call) Run(run func(entity api.Entit return _c } -func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 string, _a1 string, _a2 error) *UCEVSECCInterface_ManufacturerData_Call { - _c.Call.Return(_a0, _a1, _a2) +func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 *ucevsecc.ManufacturerData, _a1 error) *UCEVSECCInterface_ManufacturerData_Call { + _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (string, string, error)) *UCEVSECCInterface_ManufacturerData_Call { +func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*ucevsecc.ManufacturerData, error)) *UCEVSECCInterface_ManufacturerData_Call { _c.Call.Return(run) return _c } From 012a92352a59f26c77bf78b56f1a9fb696ae8228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Thu, 14 Mar 2024 09:41:09 +0100 Subject: [PATCH 141/227] Move ManufacturerData struct to /api --- api/api.go | 15 +++++++++++++++ mocks/UCEVCCInterface.go | 16 +++++++--------- mocks/UCEVSECCInterface.go | 18 +++++++++--------- ucevcc/api.go | 17 +---------------- ucevcc/public.go | 4 ++-- ucevsecc/api.go | 16 +--------------- ucevsecc/public.go | 4 ++-- 7 files changed, 37 insertions(+), 53 deletions(-) diff --git a/api/api.go b/api/api.go index 7adb7f9..315dee1 100644 --- a/api/api.go +++ b/api/api.go @@ -53,3 +53,18 @@ type UseCaseInterface interface { // - and others IsUseCaseSupported(remoteEntity spineapi.EntityRemoteInterface) (bool, error) } + +type ManufacturerData struct { + DeviceName string `json:"deviceName,omitempty"` + DeviceCode string `json:"deviceCode,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + SoftwareRevision string `json:"softwareRevision,omitempty"` + HardwareRevision string `json:"hardwareRevision,omitempty"` + VendorName string `json:"vendorName,omitempty"` + VendorCode string `json:"vendorCode,omitempty"` + BrandName string `json:"brandName,omitempty"` + PowerSource string `json:"powerSource,omitempty"` + ManufacturerNodeIdentification string `json:"manufacturerNodeIdentification,omitempty"` + ManufacturerLabel string `json:"manufacturerLabel,omitempty"` + ManufacturerDescription string `json:"manufacturerDescription,omitempty"` +} diff --git a/mocks/UCEVCCInterface.go b/mocks/UCEVCCInterface.go index 4acdf09..d036acf 100644 --- a/mocks/UCEVCCInterface.go +++ b/mocks/UCEVCCInterface.go @@ -9,8 +9,6 @@ import ( mock "github.com/stretchr/testify/mock" model "github.com/enbility/spine-go/model" - - ucevcc "github.com/enbility/cemd/ucevcc" ) // UCEVCCInterface is an autogenerated mock type for the UCEVCCInterface type @@ -551,23 +549,23 @@ func (_c *UCEVCCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.Ent } // ManufacturerData provides a mock function with given fields: entity -func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*ucevcc.ManufacturerData, error) { +func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for ManufacturerData") } - var r0 *ucevcc.ManufacturerData + var r0 *cemdapi.ManufacturerData var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*ucevcc.ManufacturerData, error)); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *ucevcc.ManufacturerData); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *cemdapi.ManufacturerData); ok { r0 = rf(entity) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*ucevcc.ManufacturerData) + r0 = ret.Get(0).(*cemdapi.ManufacturerData) } } @@ -598,12 +596,12 @@ func (_c *UCEVCCInterface_ManufacturerData_Call) Run(run func(entity api.EntityR return _c } -func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 *ucevcc.ManufacturerData, _a1 error) *UCEVCCInterface_ManufacturerData_Call { +func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 *cemdapi.ManufacturerData, _a1 error) *UCEVCCInterface_ManufacturerData_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*ucevcc.ManufacturerData, error)) *UCEVCCInterface_ManufacturerData_Call { +func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)) *UCEVCCInterface_ManufacturerData_Call { _c.Call.Return(run) return _c } diff --git a/mocks/UCEVSECCInterface.go b/mocks/UCEVSECCInterface.go index 3324963..1499ec6 100644 --- a/mocks/UCEVSECCInterface.go +++ b/mocks/UCEVSECCInterface.go @@ -3,12 +3,12 @@ package mocks import ( + cemdapi "github.com/enbility/cemd/api" api "github.com/enbility/spine-go/api" + mock "github.com/stretchr/testify/mock" model "github.com/enbility/spine-go/model" - - ucevsecc "github.com/enbility/cemd/ucevsecc" ) // UCEVSECCInterface is an autogenerated mock type for the UCEVSECCInterface type @@ -145,23 +145,23 @@ func (_c *UCEVSECCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.E } // ManufacturerData provides a mock function with given fields: entity -func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*ucevsecc.ManufacturerData, error) { +func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for ManufacturerData") } - var r0 *ucevsecc.ManufacturerData + var r0 *cemdapi.ManufacturerData var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*ucevsecc.ManufacturerData, error)); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *ucevsecc.ManufacturerData); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *cemdapi.ManufacturerData); ok { r0 = rf(entity) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*ucevsecc.ManufacturerData) + r0 = ret.Get(0).(*cemdapi.ManufacturerData) } } @@ -192,12 +192,12 @@ func (_c *UCEVSECCInterface_ManufacturerData_Call) Run(run func(entity api.Entit return _c } -func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 *ucevsecc.ManufacturerData, _a1 error) *UCEVSECCInterface_ManufacturerData_Call { +func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 *cemdapi.ManufacturerData, _a1 error) *UCEVSECCInterface_ManufacturerData_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*ucevsecc.ManufacturerData, error)) *UCEVSECCInterface_ManufacturerData_Call { +func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)) *UCEVSECCInterface_ManufacturerData_Call { _c.Call.Return(run) return _c } diff --git a/ucevcc/api.go b/ucevcc/api.go index 09fc4d0..846335b 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -8,21 +8,6 @@ import ( //go:generate mockery -type ManufacturerData struct { - DeviceName string `json:"deviceName,omitempty"` - DeviceCode string `json:"deviceCode,omitempty"` - SerialNumber string `json:"serialNumber,omitempty"` - SoftwareRevision string `json:"softwareRevision,omitempty"` - HardwareRevision string `json:"hardwareRevision,omitempty"` - VendorName string `json:"vendorName,omitempty"` - VendorCode string `json:"vendorCode,omitempty"` - BrandName string `json:"brandName,omitempty"` - PowerSource string `json:"powerSource,omitempty"` - ManufacturerNodeIdentification string `json:"manufacturerNodeIdentification,omitempty"` - ManufacturerLabel string `json:"manufacturerLabel,omitempty"` - ManufacturerDescription string `json:"manufacturerDescription,omitempty"` -} - // interface for the EV Commissioning and Configuration UseCase type UCEVCCInterface interface { api.UseCaseInterface @@ -73,7 +58,7 @@ type UCEVCCInterface interface { // // parameters: // - entity: the entity of the EV - ManufacturerData(entity spineapi.EntityRemoteInterface) (*ManufacturerData, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (*api.ManufacturerData, error) // Scenario 6 diff --git a/ucevcc/public.go b/ucevcc/public.go index c965cd4..2362b3d 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -198,7 +198,7 @@ func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.I func (e *UCEVCC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( - *ManufacturerData, + *api.ManufacturerData, error, ) { @@ -216,7 +216,7 @@ func (e *UCEVCC) ManufacturerData( return nil, err } - ret := &ManufacturerData{ + ret := &api.ManufacturerData{ DeviceName: util.Deref((*string)(data.DeviceName)), DeviceCode: util.Deref((*string)(data.DeviceCode)), SerialNumber: util.Deref((*string)(data.SerialNumber)), diff --git a/ucevsecc/api.go b/ucevsecc/api.go index 1ebead7..15c722c 100644 --- a/ucevsecc/api.go +++ b/ucevsecc/api.go @@ -7,20 +7,6 @@ import ( ) //go:generate mockery -type ManufacturerData struct { - DeviceName string `json:"deviceName,omitempty"` - DeviceCode string `json:"deviceCode,omitempty"` - SerialNumber string `json:"serialNumber,omitempty"` - SoftwareRevision string `json:"softwareRevision,omitempty"` - HardwareRevision string `json:"hardwareRevision,omitempty"` - VendorName string `json:"vendorName,omitempty"` - VendorCode string `json:"vendorCode,omitempty"` - BrandName string `json:"brandName,omitempty"` - PowerSource string `json:"powerSource,omitempty"` - ManufacturerNodeIdentification string `json:"manufacturerNodeIdentification,omitempty"` - ManufacturerLabel string `json:"manufacturerLabel,omitempty"` - ManufacturerDescription string `json:"manufacturerDescription,omitempty"` -} // interface for the EVSE Commissioning and Configuration UseCase type UCEVSECCInterface interface { @@ -32,7 +18,7 @@ type UCEVSECCInterface interface { // - entity: the entity of the EV // // returns deviceName, serialNumber, error - ManufacturerData(entity spineapi.EntityRemoteInterface) (*ManufacturerData, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (*api.ManufacturerData, error) // the operating state data of an EVSE // diff --git a/ucevsecc/public.go b/ucevsecc/public.go index e04b2f2..7b055d5 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -12,7 +12,7 @@ import ( func (e *UCEVSECC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( - *ManufacturerData, + *api.ManufacturerData, error, ) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { @@ -29,7 +29,7 @@ func (e *UCEVSECC) ManufacturerData( return nil, err } - ret := &ManufacturerData{ + ret := &api.ManufacturerData{ DeviceName: util.Deref((*string)(data.DeviceName)), DeviceCode: util.Deref((*string)(data.DeviceCode)), SerialNumber: util.Deref((*string)(data.SerialNumber)), From 24a7d6fd49bca1881a691eac39439ff18d80499d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Thu, 14 Mar 2024 14:11:56 +0100 Subject: [PATCH 142/227] Refactor to single util function --- ucevcc/public.go | 31 +-------------------------- ucevsecc/public.go | 31 +-------------------------- util/manufacturerdata.go | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 60 deletions(-) create mode 100644 util/manufacturerdata.go diff --git a/ucevcc/public.go b/ucevcc/public.go index 2362b3d..a9a6a16 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -202,36 +202,7 @@ func (e *UCEVCC) ManufacturerData( error, ) { - if !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return nil, api.ErrNoCompatibleEntity - } - - evDeviceClassification, err := util.DeviceClassification(e.service, entity) - if err != nil { - return nil, eebusapi.ErrDataNotAvailable - } - - data, err := evDeviceClassification.GetManufacturerDetails() - if err != nil { - return nil, err - } - - ret := &api.ManufacturerData{ - DeviceName: util.Deref((*string)(data.DeviceName)), - DeviceCode: util.Deref((*string)(data.DeviceCode)), - SerialNumber: util.Deref((*string)(data.SerialNumber)), - SoftwareRevision: util.Deref((*string)(data.SoftwareRevision)), - HardwareRevision: util.Deref((*string)(data.HardwareRevision)), - VendorName: util.Deref((*string)(data.VendorName)), - VendorCode: util.Deref((*string)(data.VendorCode)), - BrandName: util.Deref((*string)(data.BrandName)), - PowerSource: util.Deref((*string)(data.PowerSource)), - ManufacturerNodeIdentification: util.Deref((*string)(data.ManufacturerNodeIdentification)), - ManufacturerLabel: util.Deref((*string)(data.ManufacturerLabel)), - ManufacturerDescription: util.Deref((*string)(data.ManufacturerDescription)), - } - - return ret, nil + return util.ManufacturerData(e.service, entity, e.validEntityTypes) } // return the min, max, default limits for each phase of the connected EV diff --git a/ucevsecc/public.go b/ucevsecc/public.go index 7b055d5..a26de9e 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -15,36 +15,7 @@ func (e *UCEVSECC) ManufacturerData( *api.ManufacturerData, error, ) { - if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return nil, api.ErrNoCompatibleEntity - } - - evseDeviceClassification, err := util.DeviceClassification(e.service, entity) - if err != nil { - return nil, err - } - - data, err := evseDeviceClassification.GetManufacturerDetails() - if err != nil { - return nil, err - } - - ret := &api.ManufacturerData{ - DeviceName: util.Deref((*string)(data.DeviceName)), - DeviceCode: util.Deref((*string)(data.DeviceCode)), - SerialNumber: util.Deref((*string)(data.SerialNumber)), - SoftwareRevision: util.Deref((*string)(data.SoftwareRevision)), - HardwareRevision: util.Deref((*string)(data.HardwareRevision)), - VendorName: util.Deref((*string)(data.VendorName)), - VendorCode: util.Deref((*string)(data.VendorCode)), - BrandName: util.Deref((*string)(data.BrandName)), - PowerSource: util.Deref((*string)(data.PowerSource)), - ManufacturerNodeIdentification: util.Deref((*string)(data.ManufacturerNodeIdentification)), - ManufacturerLabel: util.Deref((*string)(data.ManufacturerLabel)), - ManufacturerDescription: util.Deref((*string)(data.ManufacturerDescription)), - } - - return ret, nil + return util.ManufacturerData(e.service, entity, e.validEntityTypes) } // the operating state data of an EVSE diff --git a/util/manufacturerdata.go b/util/manufacturerdata.go new file mode 100644 index 0000000..11f14b0 --- /dev/null +++ b/util/manufacturerdata.go @@ -0,0 +1,46 @@ +package util + +import ( + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// return the current manufacturer data for a entity +// +// possible errors: +// - ErrNoCompatibleEntity if entity is not compatible +// - and others +func ManufacturerData(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType) (*api.ManufacturerData, error) { + if entity == nil || !IsCompatibleEntity(entity, entityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + deviceClassification, err := DeviceClassification(service, entity) + if err != nil { + return nil, err + } + + data, err := deviceClassification.GetManufacturerDetails() + if err != nil { + return nil, err + } + + ret := &api.ManufacturerData{ + DeviceName: Deref((*string)(data.DeviceName)), + DeviceCode: Deref((*string)(data.DeviceCode)), + SerialNumber: Deref((*string)(data.SerialNumber)), + SoftwareRevision: Deref((*string)(data.SoftwareRevision)), + HardwareRevision: Deref((*string)(data.HardwareRevision)), + VendorName: Deref((*string)(data.VendorName)), + VendorCode: Deref((*string)(data.VendorCode)), + BrandName: Deref((*string)(data.BrandName)), + PowerSource: Deref((*string)(data.PowerSource)), + ManufacturerNodeIdentification: Deref((*string)(data.ManufacturerNodeIdentification)), + ManufacturerLabel: Deref((*string)(data.ManufacturerLabel)), + ManufacturerDescription: Deref((*string)(data.ManufacturerDescription)), + } + + return ret, nil +} From 96a67dcccd5dada8f3c81d9b34472251247c01a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Thu, 14 Mar 2024 14:12:34 +0100 Subject: [PATCH 143/227] Add DeviceClassification feature to test entity --- util/testhelper_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/util/testhelper_test.go b/util/testhelper_test.go index 5bea0a0..7014e0d 100644 --- a/util/testhelper_test.go +++ b/util/testhelper_test.go @@ -85,6 +85,8 @@ func setupDevices( localEntity.AddFeature(f) f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) localEntity.AddFeature(f) + f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) + localEntity.AddFeature(f) f = spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) @@ -94,10 +96,14 @@ func setupDevices( f.AddFunctionType(model.FunctionTypeElectricalConnectionPermittedValueSetListData, true, false) f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) localEntity.AddFeature(f) - f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + f = spine.NewFeatureLocal(3, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) localEntity.AddFeature(f) + f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceClassificationManufacturerData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceClassificationUserData, true, true) + localEntity.AddFeature(f) writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() @@ -131,6 +137,13 @@ func setupDevices( model.FunctionTypeMeasurementListData, }, }, + {model.FeatureTypeTypeDeviceClassification, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceClassificationManufacturerData, + model.FunctionTypeDeviceClassificationUserData, + }, + }, } remoteDeviceName := "remote" From eeee9c7e5e52f02723cc9888013ff1ca20378a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Thu, 14 Mar 2024 14:12:59 +0100 Subject: [PATCH 144/227] Use another entity as monitoredEntity now supports DeviceClassification --- util/features_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/features_test.go b/util/features_test.go index 3b043f9..f5f72b9 100644 --- a/util/features_test.go +++ b/util/features_test.go @@ -3,7 +3,7 @@ package util import "github.com/stretchr/testify/assert" func (s *UtilSuite) Test_Features() { - feature1, err := DeviceClassification(s.service, s.monitoredEntity) + feature1, err := DeviceClassification(s.service, s.evseEntity) assert.Nil(s.T(), feature1) assert.NotNil(s.T(), err) From d292fe4e62074e0ff7a739e26e943450170b2fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Thu, 14 Mar 2024 14:13:05 +0100 Subject: [PATCH 145/227] Add Tests --- util/manufacturerdata_test.go | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 util/manufacturerdata_test.go diff --git a/util/manufacturerdata_test.go b/util/manufacturerdata_test.go new file mode 100644 index 0000000..6afd634 --- /dev/null +++ b/util/manufacturerdata_test.go @@ -0,0 +1,38 @@ +package util + +import ( + "github.com/enbility/ship-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UtilSuite) Test_ManufacturerData() { + entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} + + data, err := ManufacturerData(s.service, s.mockRemoteEntity, entityTypes) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + data, err = ManufacturerData(s.service, s.monitoredEntity, entityTypes) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + descData := &model.DeviceClassificationManufacturerDataType{ + + DeviceName: util.Ptr(model.DeviceClassificationStringType("deviceName")), + DeviceCode: util.Ptr(model.DeviceClassificationStringType("deviceCode")), + SerialNumber: util.Ptr(model.DeviceClassificationStringType("serialNumber")), + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeServer) + assert.NotNil(s.T(), rFeature) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) + assert.Nil(s.T(), fErr) + data, err = ManufacturerData(s.service, s.monitoredEntity, entityTypes) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + assert.Equal(s.T(), "deviceName", data.DeviceName) + assert.Equal(s.T(), "deviceCode", data.DeviceCode) + assert.Equal(s.T(), "serialNumber", data.SerialNumber) + assert.Equal(s.T(), "", data.SoftwareRevision) +} From 829d702776f962d76ca001dd1dd2b29687a9ee66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Fri, 15 Mar 2024 09:03:51 +0100 Subject: [PATCH 146/227] Return struct by value --- ucevcc/api.go | 2 +- ucevcc/public.go | 2 +- ucevcc/public_test.go | 11 ++++------- ucevsecc/api.go | 2 +- ucevsecc/public.go | 2 +- ucevsecc/public_test.go | 11 ++++------- util/manufacturerdata.go | 10 +++++----- util/manufacturerdata_test.go | 8 +++----- 8 files changed, 20 insertions(+), 28 deletions(-) diff --git a/ucevcc/api.go b/ucevcc/api.go index 846335b..f801add 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -58,7 +58,7 @@ type UCEVCCInterface interface { // // parameters: // - entity: the entity of the EV - ManufacturerData(entity spineapi.EntityRemoteInterface) (*api.ManufacturerData, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (api.ManufacturerData, error) // Scenario 6 diff --git a/ucevcc/public.go b/ucevcc/public.go index a9a6a16..1227bf6 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -198,7 +198,7 @@ func (e *UCEVCC) Identifications(entity spineapi.EntityRemoteInterface) ([]api.I func (e *UCEVCC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( - *api.ManufacturerData, + api.ManufacturerData, error, ) { diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index a57b254..bc3d2cb 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -254,17 +254,14 @@ func (s *UCEVCCSuite) Test_EVIdentification() { } func (s *UCEVCCSuite) Test_EVManufacturerData() { - data, err := s.sut.ManufacturerData(s.mockRemoteEntity) + _, err := s.sut.ManufacturerData(nil) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - data, err = s.sut.ManufacturerData(s.evEntity) + _, err = s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - data, err = s.sut.ManufacturerData(s.evEntity) + _, err = s.sut.ManufacturerData(s.evEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) descData := &model.DeviceClassificationManufacturerDataType{} @@ -272,7 +269,7 @@ func (s *UCEVCCSuite) Test_EVManufacturerData() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.ManufacturerData(s.evEntity) + data, err := s.sut.ManufacturerData(s.evEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), data) assert.Equal(s.T(), "", data.DeviceName) diff --git a/ucevsecc/api.go b/ucevsecc/api.go index 15c722c..675274c 100644 --- a/ucevsecc/api.go +++ b/ucevsecc/api.go @@ -18,7 +18,7 @@ type UCEVSECCInterface interface { // - entity: the entity of the EV // // returns deviceName, serialNumber, error - ManufacturerData(entity spineapi.EntityRemoteInterface) (*api.ManufacturerData, error) + ManufacturerData(entity spineapi.EntityRemoteInterface) (api.ManufacturerData, error) // the operating state data of an EVSE // diff --git a/ucevsecc/public.go b/ucevsecc/public.go index a26de9e..0f48139 100644 --- a/ucevsecc/public.go +++ b/ucevsecc/public.go @@ -12,7 +12,7 @@ import ( func (e *UCEVSECC) ManufacturerData( entity spineapi.EntityRemoteInterface, ) ( - *api.ManufacturerData, + api.ManufacturerData, error, ) { return util.ManufacturerData(e.service, entity, e.validEntityTypes) diff --git a/ucevsecc/public_test.go b/ucevsecc/public_test.go index 53b70aa..2a884b0 100644 --- a/ucevsecc/public_test.go +++ b/ucevsecc/public_test.go @@ -7,17 +7,14 @@ import ( ) func (s *UCEVSECCSuite) Test_EVSEManufacturerData() { - data, err := s.sut.ManufacturerData(nil) + _, err := s.sut.ManufacturerData(nil) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - data, err = s.sut.ManufacturerData(s.mockRemoteEntity) + _, err = s.sut.ManufacturerData(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - data, err = s.sut.ManufacturerData(s.evseEntity) + _, err = s.sut.ManufacturerData(s.evseEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) descData := &model.DeviceClassificationManufacturerDataType{} @@ -25,7 +22,7 @@ func (s *UCEVSECCSuite) Test_EVSEManufacturerData() { fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.ManufacturerData(s.evseEntity) + data, err := s.sut.ManufacturerData(s.evseEntity) assert.Nil(s.T(), err) assert.NotNil(s.T(), data) assert.Equal(s.T(), "", data.DeviceName) diff --git a/util/manufacturerdata.go b/util/manufacturerdata.go index 11f14b0..660dd80 100644 --- a/util/manufacturerdata.go +++ b/util/manufacturerdata.go @@ -12,22 +12,22 @@ import ( // possible errors: // - ErrNoCompatibleEntity if entity is not compatible // - and others -func ManufacturerData(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType) (*api.ManufacturerData, error) { +func ManufacturerData(service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType) (api.ManufacturerData, error) { if entity == nil || !IsCompatibleEntity(entity, entityTypes) { - return nil, api.ErrNoCompatibleEntity + return api.ManufacturerData{}, api.ErrNoCompatibleEntity } deviceClassification, err := DeviceClassification(service, entity) if err != nil { - return nil, err + return api.ManufacturerData{}, err } data, err := deviceClassification.GetManufacturerDetails() if err != nil { - return nil, err + return api.ManufacturerData{}, err } - ret := &api.ManufacturerData{ + ret := api.ManufacturerData{ DeviceName: Deref((*string)(data.DeviceName)), DeviceCode: Deref((*string)(data.DeviceCode)), SerialNumber: Deref((*string)(data.SerialNumber)), diff --git a/util/manufacturerdata_test.go b/util/manufacturerdata_test.go index 6afd634..b5a2734 100644 --- a/util/manufacturerdata_test.go +++ b/util/manufacturerdata_test.go @@ -9,13 +9,11 @@ import ( func (s *UtilSuite) Test_ManufacturerData() { entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} - data, err := ManufacturerData(s.service, s.mockRemoteEntity, entityTypes) + _, err := ManufacturerData(s.service, s.mockRemoteEntity, entityTypes) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) - data, err = ManufacturerData(s.service, s.monitoredEntity, entityTypes) + _, err = ManufacturerData(s.service, s.monitoredEntity, entityTypes) assert.NotNil(s.T(), err) - assert.Nil(s.T(), data) descData := &model.DeviceClassificationManufacturerDataType{ @@ -28,7 +26,7 @@ func (s *UtilSuite) Test_ManufacturerData() { assert.NotNil(s.T(), rFeature) fErr := rFeature.UpdateData(model.FunctionTypeDeviceClassificationManufacturerData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = ManufacturerData(s.service, s.monitoredEntity, entityTypes) + data, err := ManufacturerData(s.service, s.monitoredEntity, entityTypes) assert.Nil(s.T(), err) assert.NotNil(s.T(), data) assert.Equal(s.T(), "deviceName", data.DeviceName) From 469375e37cd073bf1f18252d92d954a212f74bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Krollmann?= Date: Fri, 15 Mar 2024 11:02:15 +0100 Subject: [PATCH 147/227] Update the mocks to the right Interface version --- mocks/UCEVCCInterface.go | 16 +++++++--------- mocks/UCEVSECCInterface.go | 16 +++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/mocks/UCEVCCInterface.go b/mocks/UCEVCCInterface.go index d036acf..94ee954 100644 --- a/mocks/UCEVCCInterface.go +++ b/mocks/UCEVCCInterface.go @@ -549,24 +549,22 @@ func (_c *UCEVCCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.Ent } // ManufacturerData provides a mock function with given fields: entity -func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error) { +func (_m *UCEVCCInterface) ManufacturerData(entity api.EntityRemoteInterface) (cemdapi.ManufacturerData, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for ManufacturerData") } - var r0 *cemdapi.ManufacturerData + var r0 cemdapi.ManufacturerData var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.ManufacturerData, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *cemdapi.ManufacturerData); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.ManufacturerData); ok { r0 = rf(entity) } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*cemdapi.ManufacturerData) - } + r0 = ret.Get(0).(cemdapi.ManufacturerData) } if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { @@ -596,12 +594,12 @@ func (_c *UCEVCCInterface_ManufacturerData_Call) Run(run func(entity api.EntityR return _c } -func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 *cemdapi.ManufacturerData, _a1 error) *UCEVCCInterface_ManufacturerData_Call { +func (_c *UCEVCCInterface_ManufacturerData_Call) Return(_a0 cemdapi.ManufacturerData, _a1 error) *UCEVCCInterface_ManufacturerData_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)) *UCEVCCInterface_ManufacturerData_Call { +func (_c *UCEVCCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.ManufacturerData, error)) *UCEVCCInterface_ManufacturerData_Call { _c.Call.Return(run) return _c } diff --git a/mocks/UCEVSECCInterface.go b/mocks/UCEVSECCInterface.go index 1499ec6..d2aa9a9 100644 --- a/mocks/UCEVSECCInterface.go +++ b/mocks/UCEVSECCInterface.go @@ -145,24 +145,22 @@ func (_c *UCEVSECCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.E } // ManufacturerData provides a mock function with given fields: entity -func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error) { +func (_m *UCEVSECCInterface) ManufacturerData(entity api.EntityRemoteInterface) (cemdapi.ManufacturerData, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for ManufacturerData") } - var r0 *cemdapi.ManufacturerData + var r0 cemdapi.ManufacturerData var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.ManufacturerData, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) *cemdapi.ManufacturerData); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.ManufacturerData); ok { r0 = rf(entity) } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*cemdapi.ManufacturerData) - } + r0 = ret.Get(0).(cemdapi.ManufacturerData) } if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { @@ -192,12 +190,12 @@ func (_c *UCEVSECCInterface_ManufacturerData_Call) Run(run func(entity api.Entit return _c } -func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 *cemdapi.ManufacturerData, _a1 error) *UCEVSECCInterface_ManufacturerData_Call { +func (_c *UCEVSECCInterface_ManufacturerData_Call) Return(_a0 cemdapi.ManufacturerData, _a1 error) *UCEVSECCInterface_ManufacturerData_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (*cemdapi.ManufacturerData, error)) *UCEVSECCInterface_ManufacturerData_Call { +func (_c *UCEVSECCInterface_ManufacturerData_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.ManufacturerData, error)) *UCEVSECCInterface_ManufacturerData_Call { _c.Call.Return(run) return _c } From 2bb3f076b01b606792b7c9f30472b6409663535f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 27 Mar 2024 11:11:13 +0100 Subject: [PATCH 148/227] Update SHIP --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 32c344b..e9b58f5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800 - github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da + github.com/enbility/ship-go v0.0.0-20240326184117-0a9706f277b9 github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index b9c4884..78ab1ed 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800 h1:qMapr0u+13DLw5Q6mMHMUjUQHVW7iXyGoDRgMyY6PkY= github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800/go.mod h1:30YZwH+GzLqK4Q2NxvrreI9Ib4LUSeT7c6Sbpvr9+Bk= -github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da h1:+O2QSawKtKxDYgVip/C/KYcRBA0NrwwuwSIklsLDWnI= -github.com/enbility/ship-go v0.0.0-20240312193628-cce1ca9735da/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/ship-go v0.0.0-20240326184117-0a9706f277b9 h1:jJGc8+toPGQw/lwll/H+CTk8C+QXubjbHjWH9Prn0IA= +github.com/enbility/ship-go v0.0.0-20240326184117-0a9706f277b9/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= From a9f3fa03aca7001a1b04631da2a8c4ced463dc53 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 27 Mar 2024 11:13:06 +0100 Subject: [PATCH 149/227] Add CurrentLimits to UCOPEV and UCOSCEV --- ucopev/api.go | 6 ++ ucopev/public.go | 9 ++ ucopev/public_test.go | 8 +- ucoscev/api.go | 6 ++ ucoscev/public.go | 9 ++ ucoscev/public_test.go | 8 +- util/electricalconnection.go | 44 ++++++++ util/electricalconnection_test.go | 160 ++++++++++++++++++++++++++++++ 8 files changed, 248 insertions(+), 2 deletions(-) diff --git a/ucopev/api.go b/ucopev/api.go index ccfbc46..6c6d81d 100644 --- a/ucopev/api.go +++ b/ucopev/api.go @@ -14,6 +14,12 @@ type UCOPEVInterface interface { // Scenario 1 + // return the min, max, default limits for each phase of the connected EV + // + // parameters: + // - entity: the entity of the EV + CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) + // return the current loadcontrol obligation limits // // parameters: diff --git a/ucopev/public.go b/ucopev/public.go index 2b85ff9..fe31236 100644 --- a/ucopev/public.go +++ b/ucopev/public.go @@ -7,6 +7,15 @@ import ( "github.com/enbility/spine-go/model" ) +// return the min, max, default limits for each phase of the connected EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCOPEV) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + return util.GetPhaseCurrentLimits(e.service, entity, e.validEntityTypes) +} + // return the current loadcontrol obligation limits // // parameters: diff --git a/ucopev/public_test.go b/ucopev/public_test.go index 0751dd8..cedcf8f 100644 --- a/ucopev/public_test.go +++ b/ucopev/public_test.go @@ -8,7 +8,13 @@ import ( func (s *UCOPEVSuite) Test_Public() { // The actual tests of the functionality is located in the util package - _, err := s.sut.LoadControlLimits(s.mockRemoteEntity) + _, _, _, err := s.sut.CurrentLimits(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + + _, _, _, err = s.sut.CurrentLimits(s.evEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.LoadControlLimits(s.mockRemoteEntity) assert.NotNil(s.T(), err) _, err = s.sut.LoadControlLimits(s.evEntity) diff --git a/ucoscev/api.go b/ucoscev/api.go index e64baef..13ac02e 100644 --- a/ucoscev/api.go +++ b/ucoscev/api.go @@ -14,6 +14,12 @@ type UCOSCEVInterface interface { // Scenario 1 + // return the min, max, default limits for each phase of the connected EV + // + // parameters: + // - entity: the entity of the EV + CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) + // return the current loadcontrol recommendation limits // // parameters: diff --git a/ucoscev/public.go b/ucoscev/public.go index 2c1f96c..5df5c74 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -7,6 +7,15 @@ import ( "github.com/enbility/spine-go/model" ) +// return the min, max, default limits for each phase of the connected EV +// +// possible errors: +// - ErrDataNotAvailable if no such measurement is (yet) available +// - and others +func (e *UCOSCEV) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + return util.GetPhaseCurrentLimits(e.service, entity, e.validEntityTypes) +} + // return the current loadcontrol recommendation limits // // parameters: diff --git a/ucoscev/public_test.go b/ucoscev/public_test.go index fd571ea..12511b4 100644 --- a/ucoscev/public_test.go +++ b/ucoscev/public_test.go @@ -8,7 +8,13 @@ import ( func (s *UCOSCEVSuite) Test_Public() { // The actual tests of the functionality is located in the util package - _, err := s.sut.LoadControlLimits(s.mockRemoteEntity) + _, _, _, err := s.sut.CurrentLimits(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + + _, _, _, err = s.sut.CurrentLimits(s.evEntity) + assert.NotNil(s.T(), err) + + _, err = s.sut.LoadControlLimits(s.mockRemoteEntity) assert.NotNil(s.T(), err) _, err = s.sut.LoadControlLimits(s.evEntity) diff --git a/util/electricalconnection.go b/util/electricalconnection.go index 86a9fff..4750da0 100644 --- a/util/electricalconnection.go +++ b/util/electricalconnection.go @@ -1,11 +1,55 @@ package util import ( + "github.com/enbility/cemd/api" eebusapi "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) +func GetPhaseCurrentLimits( + service eebusapi.ServiceInterface, + entity spineapi.EntityRemoteInterface, + entityTypes []model.EntityTypeType) ( + resultMin []float64, resultMax []float64, resultDefault []float64, resultErr error) { + if !IsCompatibleEntity(entity, entityTypes) { + return nil, nil, nil, api.ErrNoCompatibleEntity + } + + evElectricalConnection, err := ElectricalConnection(service, entity) + if err != nil { + return nil, nil, nil, eebusapi.ErrDataNotAvailable + } + + for _, phaseName := range PhaseNameMapping { + // electricalParameterDescription contains the measured phase for each measurementId + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) + if err != nil || elParamDesc.ParameterId == nil { + continue + } + + dataMin, dataMax, dataDefault, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) + if err != nil { + continue + } + + // Min current data should be derived from min power data + // but as this value is only properly provided via VAS the + // currrent min values can not be trusted. + + resultMin = append(resultMin, dataMin) + resultMax = append(resultMax, dataMax) + resultDefault = append(resultDefault, dataDefault) + } + + if len(resultMin) == 0 { + return nil, nil, nil, eebusapi.ErrDataNotAvailable + } + + return resultMin, resultMax, resultDefault, nil +} + func GetLocalElectricalConnectionCharacteristicForContextType( service eebusapi.ServiceInterface, context model.ElectricalConnectionCharacteristicContextType, diff --git a/util/electricalconnection_test.go b/util/electricalconnection_test.go index 32d7be3..4ef48b0 100644 --- a/util/electricalconnection_test.go +++ b/util/electricalconnection_test.go @@ -1,11 +1,171 @@ package util import ( + "testing" + eebusutil "github.com/enbility/eebus-go/util" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) +func (s *UtilSuite) Test_EVCurrentLimits() { + entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} + + minData, maxData, defaultData, err := GetPhaseCurrentLimits(s.service, s.mockRemoteEntity, entityTypes) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + minData, maxData, defaultData, err = GetPhaseCurrentLimits(s.service, s.monitoredEntity, entityTypes) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + minData, maxData, defaultData, err = GetPhaseCurrentLimits(s.service, s.monitoredEntity, entityTypes) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + minData, maxData, defaultData, err = GetPhaseCurrentLimits(s.service, s.monitoredEntity, entityTypes) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), minData) + assert.Nil(s.T(), maxData) + assert.Nil(s.T(), defaultData) + + type permittedStruct struct { + defaultExists bool + defaultValue, expectedDefaultValue float64 + minValue, expectedMinValue float64 + maxValue, expectedMaxValue float64 + } + + tests := []struct { + name string + permitted []permittedStruct + }{ + { + "1 Phase ISO15118", + []permittedStruct{ + {true, 0.1, 0.1, 2, 2, 16, 16}, + }, + }, + { + "1 Phase IEC61851", + []permittedStruct{ + {true, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + { + "1 Phase IEC61851 Elli", + []permittedStruct{ + {false, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + { + "3 Phase ISO15118", + []permittedStruct{ + {true, 0.1, 0.1, 2, 2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, + {true, 0.1, 0.1, 2, 2, 16, 16}, + }, + }, + { + "3 Phase IEC61851", + []permittedStruct{ + {true, 0.0, 0.0, 6, 6, 16, 16}, + {true, 0.0, 0.0, 6, 6, 16, 16}, + {true, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + { + "3 Phase IEC61851 Elli", + []permittedStruct{ + {false, 0.0, 0.0, 6, 6, 16, 16}, + {false, 0.0, 0.0, 6, 6, 16, 16}, + {false, 0.0, 0.0, 6, 6, 16, 16}, + }, + }, + } + + for _, tc := range tests { + s.T().Run(tc.name, func(t *testing.T) { + dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} + permittedData := []model.ScaledNumberSetType{} + for index, data := range tc.permitted { + item := model.ScaledNumberSetType{ + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(data.minValue), + Max: model.NewScaledNumberType(data.maxValue), + }, + }, + } + if data.defaultExists { + item.Value = []model.ScaledNumberType{*model.NewScaledNumberType(data.defaultValue)} + } + permittedData = append(permittedData, item) + + permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(index)), + PermittedValueSet: permittedData, + } + dataSet = append(dataSet, permittedItem) + } + + permData := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: dataSet, + } + + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) + assert.Nil(s.T(), fErr) + + minData, maxData, defaultData, err = GetPhaseCurrentLimits(s.service, s.monitoredEntity, entityTypes) + assert.Nil(s.T(), err) + + assert.Nil(s.T(), err) + assert.Equal(s.T(), len(tc.permitted), len(minData)) + assert.Equal(s.T(), len(tc.permitted), len(maxData)) + assert.Equal(s.T(), len(tc.permitted), len(defaultData)) + for index, item := range tc.permitted { + assert.Equal(s.T(), item.expectedMinValue, minData[index]) + assert.Equal(s.T(), item.expectedMaxValue, maxData[index]) + assert.Equal(s.T(), item.expectedDefaultValue, defaultData[index]) + } + }) + } +} + func (s *UtilSuite) Test_GetLocalElectricalConnectionCharacteristicForContextType() { context := model.ElectricalConnectionCharacteristicContextTypeEntity charType := model.ElectricalConnectionCharacteristicTypeTypeApparentPowerConsumptionNominalMax From a9049a16e69346f64c4d7f23547cf6c09d6fb3da Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 27 Mar 2024 11:13:34 +0100 Subject: [PATCH 150/227] Fix UCEVCC to use ACPowerTotal instead of phase A --- ucevcc/api.go | 4 +- ucevcc/public.go | 52 ++++++++-------- ucevcc/public_test.go | 139 ++++++++++++------------------------------ 3 files changed, 67 insertions(+), 128 deletions(-) diff --git a/ucevcc/api.go b/ucevcc/api.go index f801add..59be93c 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -62,11 +62,11 @@ type UCEVCCInterface interface { // Scenario 6 - // return the min, max, default limits for each phase of the connected EV + // return the minimum, maximum charging and, standby power of the connected EV // // parameters: // - entity: the entity of the EV - CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) + CurrentLimits(entity spineapi.EntityRemoteInterface) (float64, float64, float64, error) // Scenario 7 diff --git a/ucevcc/public.go b/ucevcc/public.go index 1227bf6..ce4dcff 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -205,49 +205,47 @@ func (e *UCEVCC) ManufacturerData( return util.ManufacturerData(e.service, entity, e.validEntityTypes) } -// return the min, max, default limits for each phase of the connected EV +// return the minimum, maximum charging and, standby power of the connected EV // // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64, []float64, []float64, error) { +func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) (float64, float64, float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return nil, nil, nil, api.ErrNoCompatibleEntity + return 0.0, 0.0, 0.0, api.ErrNoCompatibleEntity } evElectricalConnection, err := util.ElectricalConnection(e.service, entity) if err != nil { - return nil, nil, nil, eebusapi.ErrDataNotAvailable + return 0.0, 0.0, 0.0, eebusapi.ErrDataNotAvailable } - var resultMin, resultMax, resultDefault []float64 - - for _, phaseName := range util.PhaseNameMapping { - // electricalParameterDescription contains the measured phase for each measurementId - elParamDesc, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(phaseName) - if err != nil || elParamDesc.ParameterId == nil { - continue - } - - dataMin, dataMax, dataDefault, err := evElectricalConnection.GetLimitsForParameterId(*elParamDesc.ParameterId) - if err != nil { - continue - } - - // Min current data should be derived from min power data - // but as this value is only properly provided via VAS the - // currrent min values can not be trusted. + elParamDesc, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPowerTotal) + if err != nil || elParamDesc.ParameterId == nil { + return 0.0, 0.0, 0.0, eebusapi.ErrDataNotAvailable + } - resultMin = append(resultMin, dataMin) - resultMax = append(resultMax, dataMax) - resultDefault = append(resultDefault, dataDefault) + dataSet, err := evElectricalConnection.GetPermittedValueSetForParameterId(*elParamDesc.ParameterId) + if err != nil || dataSet == nil || + dataSet.PermittedValueSet == nil || + len(dataSet.PermittedValueSet) != 1 || + dataSet.PermittedValueSet[0].Range == nil || + len(dataSet.PermittedValueSet[0].Range) != 1 { + return 0.0, 0.0, 0.0, eebusapi.ErrDataNotAvailable } - if len(resultMin) == 0 { - return nil, nil, nil, eebusapi.ErrDataNotAvailable + var minValue, maxValue, standByValue float64 + if dataSet.PermittedValueSet[0].Range[0].Min != nil { + minValue = dataSet.PermittedValueSet[0].Range[0].Min.GetValue() + } + if dataSet.PermittedValueSet[0].Range[0].Max != nil { + maxValue = dataSet.PermittedValueSet[0].Range[0].Max.GetValue() + } + if dataSet.PermittedValueSet[0].Value != nil && len(dataSet.PermittedValueSet[0].Value) > 0 { + standByValue = dataSet.PermittedValueSet[0].Value[0].GetValue() } - return resultMin, resultMax, resultDefault, nil + return minValue, maxValue, standByValue, nil } // is the EV in sleep mode diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index bc3d2cb..d9242fe 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -292,43 +292,24 @@ func (s *UCEVCCSuite) Test_EVManufacturerData() { } func (s *UCEVCCSuite) Test_EVCurrentLimits() { - minData, maxData, defaultData, err := s.sut.CurrentLimits(s.mockRemoteEntity) + minData, maxData, standByData, err := s.sut.CurrentLimits(s.mockRemoteEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), minData) - assert.Nil(s.T(), maxData) - assert.Nil(s.T(), defaultData) + assert.Equal(s.T(), 0.0, minData) + assert.Equal(s.T(), 0.0, maxData) + assert.Equal(s.T(), 0.0, standByData) - minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) + minData, maxData, standByData, err = s.sut.CurrentLimits(s.evEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), minData) - assert.Nil(s.T(), maxData) - assert.Nil(s.T(), defaultData) - - minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) - assert.NotNil(s.T(), err) - assert.Nil(s.T(), minData) - assert.Nil(s.T(), maxData) - assert.Nil(s.T(), defaultData) + assert.Equal(s.T(), 0.0, minData) + assert.Equal(s.T(), 0.0, maxData) + assert.Equal(s.T(), 0.0, standByData) paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ { ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), - MeasurementId: util.Ptr(model.MeasurementIdType(0)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - }, - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(1)), - MeasurementId: util.Ptr(model.MeasurementIdType(1)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), - }, - { - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(2)), - MeasurementId: util.Ptr(model.MeasurementIdType(2)), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), }, }, } @@ -337,64 +318,33 @@ func (s *UCEVCCSuite) Test_EVCurrentLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) + minData, maxData, standByData, err = s.sut.CurrentLimits(s.evEntity) assert.NotNil(s.T(), err) - assert.Nil(s.T(), minData) - assert.Nil(s.T(), maxData) - assert.Nil(s.T(), defaultData) + assert.Equal(s.T(), 0.0, minData) + assert.Equal(s.T(), 0.0, maxData) + assert.Equal(s.T(), 0.0, standByData) type permittedStruct struct { - defaultExists bool - defaultValue, expectedDefaultValue float64 + standByValue, expectedStandByValue float64 minValue, expectedMinValue float64 maxValue, expectedMaxValue float64 } tests := []struct { name string - permitted []permittedStruct + permitted permittedStruct }{ { - "1 Phase ISO15118", - []permittedStruct{ - {true, 0.1, 0.1, 2, 2, 16, 16}, - }, - }, - { - "1 Phase IEC61851", - []permittedStruct{ - {true, 0.0, 0.0, 6, 6, 16, 16}, - }, - }, - { - "1 Phase IEC61851 Elli", - []permittedStruct{ - {false, 0.0, 0.0, 6, 6, 16, 16}, - }, + "IEC 3 Phase", + permittedStruct{0.1, 0.1, 4287600, 4287600, 11433600, 11433600}, }, { - "3 Phase ISO15118", - []permittedStruct{ - {true, 0.1, 0.1, 2, 2, 16, 16}, - {true, 0.1, 0.1, 2, 2, 16, 16}, - {true, 0.1, 0.1, 2, 2, 16, 16}, - }, + "ISO15118 VW", + permittedStruct{0.1, 0.1, 800, 800, 11433600, 11433600}, }, { - "3 Phase IEC61851", - []permittedStruct{ - {true, 0.0, 0.0, 6, 6, 16, 16}, - {true, 0.0, 0.0, 6, 6, 16, 16}, - {true, 0.0, 0.0, 6, 6, 16, 16}, - }, - }, - { - "3 Phase IEC61851 Elli", - []permittedStruct{ - {false, 0.0, 0.0, 6, 6, 16, 16}, - {false, 0.0, 0.0, 6, 6, 16, 16}, - {false, 0.0, 0.0, 6, 6, 16, 16}, - }, + "ISO15118 Taycan", + permittedStruct{0.1, 0.1, 400, 400, 11433600, 11433600}, }, } @@ -402,27 +352,23 @@ func (s *UCEVCCSuite) Test_EVCurrentLimits() { s.T().Run(tc.name, func(t *testing.T) { dataSet := []model.ElectricalConnectionPermittedValueSetDataType{} permittedData := []model.ScaledNumberSetType{} - for index, data := range tc.permitted { - item := model.ScaledNumberSetType{ - Range: []model.ScaledNumberRangeType{ - { - Min: model.NewScaledNumberType(data.minValue), - Max: model.NewScaledNumberType(data.maxValue), - }, + item := model.ScaledNumberSetType{ + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(tc.permitted.minValue), + Max: model.NewScaledNumberType(tc.permitted.maxValue), }, - } - if data.defaultExists { - item.Value = []model.ScaledNumberType{*model.NewScaledNumberType(data.defaultValue)} - } - permittedData = append(permittedData, item) - - permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ - ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(index)), - PermittedValueSet: permittedData, - } - dataSet = append(dataSet, permittedItem) + }, + Value: []model.ScaledNumberType{*model.NewScaledNumberType(tc.permitted.standByValue)}, + } + permittedData = append(permittedData, item) + + permittedItem := model.ElectricalConnectionPermittedValueSetDataType{ + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: util.Ptr(model.ElectricalConnectionParameterIdType(0)), + PermittedValueSet: permittedData, } + dataSet = append(dataSet, permittedItem) permData := &model.ElectricalConnectionPermittedValueSetListDataType{ ElectricalConnectionPermittedValueSetData: dataSet, @@ -431,18 +377,13 @@ func (s *UCEVCCSuite) Test_EVCurrentLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - minData, maxData, defaultData, err = s.sut.CurrentLimits(s.evEntity) + minData, maxData, standByData, err = s.sut.CurrentLimits(s.evEntity) assert.Nil(s.T(), err) assert.Nil(s.T(), err) - assert.Equal(s.T(), len(tc.permitted), len(minData)) - assert.Equal(s.T(), len(tc.permitted), len(maxData)) - assert.Equal(s.T(), len(tc.permitted), len(defaultData)) - for index, item := range tc.permitted { - assert.Equal(s.T(), item.expectedMinValue, minData[index]) - assert.Equal(s.T(), item.expectedMaxValue, maxData[index]) - assert.Equal(s.T(), item.expectedDefaultValue, defaultData[index]) - } + assert.Equal(s.T(), tc.permitted.expectedMinValue, minData) + assert.Equal(s.T(), tc.permitted.expectedMaxValue, maxData) + assert.Equal(s.T(), tc.permitted.expectedStandByValue, standByData) }) } } From b78714f1e5bbf2d255e0a32cde752ec5f419888b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 27 Mar 2024 11:37:19 +0100 Subject: [PATCH 151/227] Add Data Update events to UCOPEV and UCOSCEV --- ucopev/events.go | 23 +++++++++++ ucopev/events_test.go | 79 ++++++++++++++++++++++++++++++++++++++ ucopev/testhelper_test.go | 6 +++ ucopev/types.go | 7 ++++ ucoscev/events.go | 23 +++++++++++ ucoscev/events_test.go | 78 ++++++++++++++++++++++++++++++++++++- ucoscev/testhelper_test.go | 7 ++++ ucoscev/types.go | 7 ++++ 8 files changed, 229 insertions(+), 1 deletion(-) diff --git a/ucopev/events.go b/ucopev/events.go index 66e584b..cbd19bd 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -26,6 +26,8 @@ func (e *UCOPEV) HandleEvent(payload spineapi.EventPayload) { } switch payload.Data.(type) { + case *model.ElectricalConnectionPermittedValueSetListDataType: + e.evElectricalPermittedValuesUpdate(payload) case *model.LoadControlLimitDescriptionListDataType: e.evLoadControlLimitDescriptionDataUpdate(payload.Entity) case *model.LoadControlLimitListDataType: @@ -90,3 +92,24 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { } } + +// the electrical connection permitted value sets data of an EV was updated +func (e *UCOPEV) evElectricalPermittedValuesUpdate(payload spineapi.EventPayload) { + evElectricalConnection, err := util.ElectricalConnection(e.service, payload.Entity) + if err != nil { + return + } + + data, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(model.ElectricalConnectionPhaseNameTypeA) + if err != nil || data.ParameterId == nil { + return + } + + values, err := evElectricalConnection.GetPermittedValueSetForParameterId(*data.ParameterId) + if err != nil || values == nil { + return + } + + // Scenario 6 + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentLimits) +} diff --git a/ucopev/events_test.go b/ucopev/events_test.go index 6039a6e..e0da71c 100644 --- a/ucopev/events_test.go +++ b/ucopev/events_test.go @@ -26,6 +26,9 @@ func (s *UCOPEVSuite) Test_Events() { payload.EventType = spineapi.EventTypeDataChange payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.ElectricalConnectionPermittedValueSetListDataType{}) + s.sut.HandleEvent(payload) + payload.Data = eebusutil.Ptr(model.LoadControlLimitDescriptionListDataType{}) s.sut.HandleEvent(payload) @@ -39,6 +42,82 @@ func (s *UCOPEVSuite) Test_Failures() { s.sut.evLoadControlLimitDescriptionDataUpdate(s.mockRemoteEntity) } +func (s *UCOPEVSuite) Test_evElectricalPermittedValuesUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evElectricalPermittedValuesUpdate(payload) + + payload.Entity = s.evEntity + s.sut.evElectricalPermittedValuesUpdate(payload) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalPermittedValuesUpdate(payload) + + data := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + PermittedValueSet: []model.ScaledNumberSetType{ + { + Value: []model.ScaledNumberType{ + *model.NewScaledNumberType(0.1), + }, + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(1400), + Max: model.NewScaledNumberType(11000), + }, + }, + }, + }, + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalPermittedValuesUpdate(payload) +} + func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucopev/testhelper_test.go b/ucopev/testhelper_test.go index f064c5f..513ff90 100644 --- a/ucopev/testhelper_test.go +++ b/ucopev/testhelper_test.go @@ -94,6 +94,12 @@ func setupDevices( featureType model.FeatureTypeType supportedFcts []model.FunctionType }{ + {model.FeatureTypeTypeElectricalConnection, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, {model.FeatureTypeTypeLoadControl, []model.FunctionType{ model.FunctionTypeLoadControlLimitDescriptionListData, diff --git a/ucopev/types.go b/ucopev/types.go index 092a1f6..48fbd75 100644 --- a/ucopev/types.go +++ b/ucopev/types.go @@ -3,6 +3,13 @@ package ucopev import "github.com/enbility/cemd/api" const ( + // EV current limits + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" + // EV load control obligation limit data updated // // The callback with this message provides: diff --git a/ucoscev/events.go b/ucoscev/events.go index eeeb905..07b08a7 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -23,6 +23,8 @@ func (e *UCOSCEV) HandleEvent(payload spineapi.EventPayload) { // the codefactor warning is invalid, as .(type) check can not be replaced with if then //revive:disable-next-line switch payload.Data.(type) { + case *model.ElectricalConnectionPermittedValueSetListDataType: + e.evElectricalPermittedValuesUpdate(payload) case *model.LoadControlLimitListDataType: e.evLoadControlLimitDataUpdate(payload) } @@ -54,3 +56,24 @@ func (e *UCOSCEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { return } } + +// the electrical connection permitted value sets data of an EV was updated +func (e *UCOSCEV) evElectricalPermittedValuesUpdate(payload spineapi.EventPayload) { + evElectricalConnection, err := util.ElectricalConnection(e.service, payload.Entity) + if err != nil { + return + } + + data, err := evElectricalConnection.GetParameterDescriptionForMeasuredPhase(model.ElectricalConnectionPhaseNameTypeA) + if err != nil || data.ParameterId == nil { + return + } + + values, err := evElectricalConnection.GetPermittedValueSetForParameterId(*data.ParameterId) + if err != nil || values == nil { + return + } + + // Scenario 6 + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentLimits) +} diff --git a/ucoscev/events_test.go b/ucoscev/events_test.go index 0535794..8981bf4 100644 --- a/ucoscev/events_test.go +++ b/ucoscev/events_test.go @@ -26,13 +26,89 @@ func (s *UCOSCEVSuite) Test_Events() { payload.EventType = spineapi.EventTypeDataChange payload.ChangeType = spineapi.ElementChangeUpdate - payload.Data = eebusutil.Ptr(model.LoadControlLimitDescriptionListDataType{}) + payload.Data = eebusutil.Ptr(model.ElectricalConnectionPermittedValueSetListDataType{}) s.sut.HandleEvent(payload) payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) s.sut.HandleEvent(payload) } +func (s *UCOSCEVSuite) Test_evElectricalPermittedValuesUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + s.sut.evElectricalPermittedValuesUpdate(payload) + + payload.Entity = s.evEntity + s.sut.evElectricalPermittedValuesUpdate(payload) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalPermittedValuesUpdate(payload) + + data := &model.ElectricalConnectionPermittedValueSetListDataType{ + ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + PermittedValueSet: []model.ScaledNumberSetType{ + { + Value: []model.ScaledNumberType{ + *model.NewScaledNumberType(0.1), + }, + Range: []model.ScaledNumberRangeType{ + { + Min: model.NewScaledNumberType(1400), + Max: model.NewScaledNumberType(11000), + }, + }, + }, + }, + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.evElectricalPermittedValuesUpdate(payload) +} + func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { payload := spineapi.EventPayload{ Ski: remoteSki, diff --git a/ucoscev/testhelper_test.go b/ucoscev/testhelper_test.go index af8ac1a..06af093 100644 --- a/ucoscev/testhelper_test.go +++ b/ucoscev/testhelper_test.go @@ -96,6 +96,13 @@ func setupDevices( role model.RoleType supportedFcts []model.FunctionType }{ + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionParameterDescriptionListData, + model.FunctionTypeElectricalConnectionPermittedValueSetListData, + }, + }, {model.FeatureTypeTypeLoadControl, model.RoleTypeServer, []model.FunctionType{ diff --git a/ucoscev/types.go b/ucoscev/types.go index f56df60..5d870d5 100644 --- a/ucoscev/types.go +++ b/ucoscev/types.go @@ -3,6 +3,13 @@ package ucoscev import "github.com/enbility/cemd/api" const ( + // EV current limits + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" + // EV load control recommendation limit data updated // // The callback with this message provides: From 38462114cc768218c7295f793d80a4b8fe320b66 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 27 Mar 2024 13:52:58 +0100 Subject: [PATCH 152/227] Update SHIP. EEBUS --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e9b58f5..0e791b3 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800 - github.com/enbility/ship-go v0.0.0-20240326184117-0a9706f277b9 + github.com/enbility/eebus-go v0.0.0-20240327124756-c2f35e1217be + github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 78ab1ed..42b6cae 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800 h1:qMapr0u+13DLw5Q6mMHMUjUQHVW7iXyGoDRgMyY6PkY= -github.com/enbility/eebus-go v0.0.0-20240313120500-d123aac13800/go.mod h1:30YZwH+GzLqK4Q2NxvrreI9Ib4LUSeT7c6Sbpvr9+Bk= -github.com/enbility/ship-go v0.0.0-20240326184117-0a9706f277b9 h1:jJGc8+toPGQw/lwll/H+CTk8C+QXubjbHjWH9Prn0IA= -github.com/enbility/ship-go v0.0.0-20240326184117-0a9706f277b9/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/eebus-go v0.0.0-20240327124756-c2f35e1217be h1:HD3VqcjLzF1W9zLCA+qEJ2gfPKBm2n29XdBVIPaQ1Es= +github.com/enbility/eebus-go v0.0.0-20240327124756-c2f35e1217be/go.mod h1:f2X3uJaPyP9vhK0jnyYI2VMkq+HUnjp9usyeAeuMSyw= +github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 h1:r71X9nuRTe2bUwWVRBpgaWII/oweFVWeM3DvgPtorAM= +github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= From 4e553cfddabf73424761ef16614dde60cd4a3514 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 29 Mar 2024 12:43:29 +0100 Subject: [PATCH 153/227] Update mocks --- mocks/UCEVCCInterface.go | 32 +++++++---------- mocks/UCOPEVInterface.go | 76 +++++++++++++++++++++++++++++++++++++++ mocks/UCOSCEVInterface.go | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 19 deletions(-) diff --git a/mocks/UCEVCCInterface.go b/mocks/UCEVCCInterface.go index 94ee954..a76a81a 100644 --- a/mocks/UCEVCCInterface.go +++ b/mocks/UCEVCCInterface.go @@ -257,42 +257,36 @@ func (_c *UCEVCCInterface_CommunicationStandard_Call) RunAndReturn(run func(api. } // CurrentLimits provides a mock function with given fields: entity -func (_m *UCEVCCInterface) CurrentLimits(entity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) { +func (_m *UCEVCCInterface) CurrentLimits(entity api.EntityRemoteInterface) (float64, float64, float64, error) { ret := _m.Called(entity) if len(ret) == 0 { panic("no return value specified for CurrentLimits") } - var r0 []float64 - var r1 []float64 - var r2 []float64 + var r0 float64 + var r1 float64 + var r2 float64 var r3 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, float64, float64, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { r0 = rf(entity) } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]float64) - } + r0 = ret.Get(0).(float64) } - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) []float64); ok { + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) float64); ok { r1 = rf(entity) } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]float64) - } + r1 = ret.Get(1).(float64) } - if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) []float64); ok { + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) float64); ok { r2 = rf(entity) } else { - if ret.Get(2) != nil { - r2 = ret.Get(2).([]float64) - } + r2 = ret.Get(2).(float64) } if rf, ok := ret.Get(3).(func(api.EntityRemoteInterface) error); ok { @@ -322,12 +316,12 @@ func (_c *UCEVCCInterface_CurrentLimits_Call) Run(run func(entity api.EntityRemo return _c } -func (_c *UCEVCCInterface_CurrentLimits_Call) Return(_a0 []float64, _a1 []float64, _a2 []float64, _a3 error) *UCEVCCInterface_CurrentLimits_Call { +func (_c *UCEVCCInterface_CurrentLimits_Call) Return(_a0 float64, _a1 float64, _a2 float64, _a3 error) *UCEVCCInterface_CurrentLimits_Call { _c.Call.Return(_a0, _a1, _a2, _a3) return _c } -func (_c *UCEVCCInterface_CurrentLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)) *UCEVCCInterface_CurrentLimits_Call { +func (_c *UCEVCCInterface_CurrentLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, float64, float64, error)) *UCEVCCInterface_CurrentLimits_Call { _c.Call.Return(run) return _c } diff --git a/mocks/UCOPEVInterface.go b/mocks/UCOPEVInterface.go index dd5540b..47bf73d 100644 --- a/mocks/UCOPEVInterface.go +++ b/mocks/UCOPEVInterface.go @@ -88,6 +88,82 @@ func (_c *UCOPEVInterface_AddUseCase_Call) RunAndReturn(run func()) *UCOPEVInter return _c } +// CurrentLimits provides a mock function with given fields: entity +func (_m *UCOPEVInterface) CurrentLimits(entity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CurrentLimits") + } + + var r0 []float64 + var r1 []float64 + var r2 []float64 + var r3 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) []float64); ok { + r1 = rf(entity) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]float64) + } + } + + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) []float64); ok { + r2 = rf(entity) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).([]float64) + } + } + + if rf, ok := ret.Get(3).(func(api.EntityRemoteInterface) error); ok { + r3 = rf(entity) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// UCOPEVInterface_CurrentLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentLimits' +type UCOPEVInterface_CurrentLimits_Call struct { + *mock.Call +} + +// CurrentLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCOPEVInterface_Expecter) CurrentLimits(entity interface{}) *UCOPEVInterface_CurrentLimits_Call { + return &UCOPEVInterface_CurrentLimits_Call{Call: _e.mock.On("CurrentLimits", entity)} +} + +func (_c *UCOPEVInterface_CurrentLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCOPEVInterface_CurrentLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCOPEVInterface_CurrentLimits_Call) Return(_a0 []float64, _a1 []float64, _a2 []float64, _a3 error) *UCOPEVInterface_CurrentLimits_Call { + _c.Call.Return(_a0, _a1, _a2, _a3) + return _c +} + +func (_c *UCOPEVInterface_CurrentLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)) *UCOPEVInterface_CurrentLimits_Call { + _c.Call.Return(run) + return _c +} + // IsUseCaseSupported provides a mock function with given fields: remoteEntity func (_m *UCOPEVInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { ret := _m.Called(remoteEntity) diff --git a/mocks/UCOSCEVInterface.go b/mocks/UCOSCEVInterface.go index 5e61f60..cfaf957 100644 --- a/mocks/UCOSCEVInterface.go +++ b/mocks/UCOSCEVInterface.go @@ -88,6 +88,82 @@ func (_c *UCOSCEVInterface_AddUseCase_Call) RunAndReturn(run func()) *UCOSCEVInt return _c } +// CurrentLimits provides a mock function with given fields: entity +func (_m *UCOSCEVInterface) CurrentLimits(entity api.EntityRemoteInterface) ([]float64, []float64, []float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for CurrentLimits") + } + + var r0 []float64 + var r1 []float64 + var r2 []float64 + var r3 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) []float64); ok { + r0 = rf(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) []float64); ok { + r1 = rf(entity) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]float64) + } + } + + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) []float64); ok { + r2 = rf(entity) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).([]float64) + } + } + + if rf, ok := ret.Get(3).(func(api.EntityRemoteInterface) error); ok { + r3 = rf(entity) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// UCOSCEVInterface_CurrentLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentLimits' +type UCOSCEVInterface_CurrentLimits_Call struct { + *mock.Call +} + +// CurrentLimits is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCOSCEVInterface_Expecter) CurrentLimits(entity interface{}) *UCOSCEVInterface_CurrentLimits_Call { + return &UCOSCEVInterface_CurrentLimits_Call{Call: _e.mock.On("CurrentLimits", entity)} +} + +func (_c *UCOSCEVInterface_CurrentLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCOSCEVInterface_CurrentLimits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCOSCEVInterface_CurrentLimits_Call) Return(_a0 []float64, _a1 []float64, _a2 []float64, _a3 error) *UCOSCEVInterface_CurrentLimits_Call { + _c.Call.Return(_a0, _a1, _a2, _a3) + return _c +} + +func (_c *UCOSCEVInterface_CurrentLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) ([]float64, []float64, []float64, error)) *UCOSCEVInterface_CurrentLimits_Call { + _c.Call.Return(run) + return _c +} + // IsUseCaseSupported provides a mock function with given fields: remoteEntity func (_m *UCOSCEVInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { ret := _m.Called(remoteEntity) From 29d9146b9ee8f0ade79285d40936c482cf9e9a85 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 29 Mar 2024 13:00:49 +0100 Subject: [PATCH 154/227] Add tests for checking event cb being invoked --- ucevcc/events_test.go | 17 +++++++++++++++++ ucevcc/testhelper_test.go | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/ucevcc/events_test.go b/ucevcc/events_test.go index 0154cbf..b9c33c2 100644 --- a/ucevcc/events_test.go +++ b/ucevcc/events_test.go @@ -75,9 +75,11 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { Entity: s.mockRemoteEntity, } s.sut.evConfigurationDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) payload.Entity = s.evEntity s.sut.evConfigurationDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ @@ -97,6 +99,7 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { assert.Nil(s.T(), fErr) s.sut.evConfigurationDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) data := &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ @@ -119,6 +122,7 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { assert.Nil(s.T(), fErr) s.sut.evConfigurationDataUpdate(payload) + assert.True(s.T(), s.eventCBInvoked) } func (s *UCEVCCSuite) Test_evOperatingStateDataUpdate() { @@ -128,9 +132,11 @@ func (s *UCEVCCSuite) Test_evOperatingStateDataUpdate() { Entity: s.mockRemoteEntity, } s.sut.evOperatingStateDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) payload.Entity = s.evEntity s.sut.evOperatingStateDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) data := &model.DeviceDiagnosisStateDataType{ OperatingState: eebusutil.Ptr(model.DeviceDiagnosisOperatingStateTypeNormalOperation), @@ -141,6 +147,7 @@ func (s *UCEVCCSuite) Test_evOperatingStateDataUpdate() { assert.Nil(s.T(), fErr) s.sut.evOperatingStateDataUpdate(payload) + assert.True(s.T(), s.eventCBInvoked) } func (s *UCEVCCSuite) Test_evIdentificationDataUpdate() { @@ -150,9 +157,11 @@ func (s *UCEVCCSuite) Test_evIdentificationDataUpdate() { Entity: s.mockRemoteEntity, } s.sut.evIdentificationDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) payload.Entity = s.evEntity s.sut.evIdentificationDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) data := &model.IdentificationListDataType{ IdentificationData: []model.IdentificationDataType{ @@ -173,6 +182,7 @@ func (s *UCEVCCSuite) Test_evIdentificationDataUpdate() { assert.Nil(s.T(), fErr) s.sut.evIdentificationDataUpdate(payload) + assert.True(s.T(), s.eventCBInvoked) } func (s *UCEVCCSuite) Test_evManufacturerDataUpdate() { @@ -182,9 +192,11 @@ func (s *UCEVCCSuite) Test_evManufacturerDataUpdate() { Entity: s.mockRemoteEntity, } s.sut.evManufacturerDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) payload.Entity = s.evEntity s.sut.evManufacturerDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) data := &model.DeviceClassificationManufacturerDataType{ BrandName: eebusutil.Ptr(model.DeviceClassificationStringType("test")), @@ -195,6 +207,7 @@ func (s *UCEVCCSuite) Test_evManufacturerDataUpdate() { assert.Nil(s.T(), fErr) s.sut.evManufacturerDataUpdate(payload) + assert.True(s.T(), s.eventCBInvoked) } func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { @@ -204,9 +217,11 @@ func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { Entity: s.mockRemoteEntity, } s.sut.evElectricalPermittedValuesUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) payload.Entity = s.evEntity s.sut.evElectricalPermittedValuesUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ @@ -223,6 +238,7 @@ func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { assert.Nil(s.T(), fErr) s.sut.evElectricalPermittedValuesUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) permData := &model.ElectricalConnectionPermittedValueSetListDataType{ ElectricalConnectionPermittedValueSetData: []model.ElectricalConnectionPermittedValueSetDataType{ @@ -237,4 +253,5 @@ func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { assert.Nil(s.T(), fErr) s.sut.evElectricalPermittedValuesUpdate(payload) + assert.True(s.T(), s.eventCBInvoked) } diff --git a/ucevcc/testhelper_test.go b/ucevcc/testhelper_test.go index a688be0..6461b7b 100644 --- a/ucevcc/testhelper_test.go +++ b/ucevcc/testhelper_test.go @@ -34,9 +34,12 @@ type UCEVCCSuite struct { mockSender *mocks.SenderInterface mockRemoteEntity *mocks.EntityRemoteInterface evEntity spineapi.EntityRemoteInterface + + eventCBInvoked bool } func (s *UCEVCCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { + s.eventCBInvoked = true } func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { @@ -66,6 +69,8 @@ func (s *UCEVCCSuite) BeforeTest(suiteName, testName string) { mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() + s.eventCBInvoked = false + s.sut = NewUCEVCC(s.service, s.Event) s.sut.AddFeatures() s.sut.AddUseCase() From e03ba3396d43a5cd55b88df8f6141008210701a9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 2 Apr 2024 11:45:07 +0200 Subject: [PATCH 155/227] Fix used ScopeType in UCEVCC --- ucevcc/events.go | 2 +- ucevcc/events_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ucevcc/events.go b/ucevcc/events.go index 3c00bc3..b611e1c 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -206,7 +206,7 @@ func (e *UCEVCC) evElectricalPermittedValuesUpdate(payload spineapi.EventPayload return } - data, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPower) + data, err := evElectricalConnection.GetParameterDescriptionForScopeType(model.ScopeTypeTypeACPowerTotal) if err != nil || data.ParameterId == nil { return } diff --git a/ucevcc/events_test.go b/ucevcc/events_test.go index b9c33c2..0b1c79e 100644 --- a/ucevcc/events_test.go +++ b/ucevcc/events_test.go @@ -228,7 +228,7 @@ func (s *UCEVCCSuite) Test_evElectricalPermittedValuesUpdate() { { ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), - ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPowerTotal), }, }, } From ba52be15f5fdae2f7f2c5af58ff434ced9d62181 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 2 Apr 2024 17:20:11 +0200 Subject: [PATCH 156/227] Add ElectricalConnection subscription in UCEVCEM --- ucevcem/events.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ucevcem/events.go b/ucevcem/events.go index 79e99a4..86c486b 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -37,6 +37,13 @@ func (e *UCEVCEM) HandleEvent(payload spineapi.EventPayload) { // an EV was connected func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { // initialise features, e.g. subscriptions, descriptions + + if evElectricalConnection, err := util.ElectricalConnection(e.service, entity); err == nil { + if _, err := evElectricalConnection.Subscribe(); err != nil { + logging.Log().Debug(err) + } + } + if evMeasurement, err := util.Measurement(e.service, entity); err == nil { if _, err := evMeasurement.Subscribe(); err != nil { logging.Log().Debug(err) From 631165a45d5adccad9233f6df2a93bc1f52f49df Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 2 Apr 2024 17:21:53 +0200 Subject: [PATCH 157/227] Also read description and parameter descriptions --- ucevcem/events.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ucevcem/events.go b/ucevcem/events.go index 86c486b..05d7939 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -42,6 +42,17 @@ func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { if _, err := evElectricalConnection.Subscribe(); err != nil { logging.Log().Debug(err) } + + // get electrical connection descriptions + if _, err := evElectricalConnection.RequestDescriptions(); err != nil { + logging.Log().Debug(err) + } + + // get electrical connection parameter descriptions + if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { + logging.Log().Debug(err) + } + } if evMeasurement, err := util.Measurement(e.service, entity); err == nil { From b2ffec6d0a6262ee3ff7bd5a37dce91655905cac Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 09:03:01 +0200 Subject: [PATCH 158/227] CEM shall not support this in UCLPCServer The CEM may not support `PowerConsumptionNominalMax`, this is restricted to consumable devices not being a CEM. This is defined in UC LPC 1.0 Spec, chapter 2.6.4.1 --- uclpcserver/api.go | 11 ----------- uclpcserver/public.go | 29 ----------------------------- uclpcserver/public_test.go | 13 ------------- uclpcserver/uclpc.go | 8 -------- 4 files changed, 61 deletions(-) diff --git a/uclpcserver/api.go b/uclpcserver/api.go index e054f9d..99d7aec 100644 --- a/uclpcserver/api.go +++ b/uclpcserver/api.go @@ -67,17 +67,6 @@ type UCLCPServerInterface interface { // Scenario 4 - // return nominal maximum active (real) power the Controllable System is - // able to consume according to the device label or data sheet. - PowerConsumptionNominalMax() (float64, error) - - // set nominal maximum active (real) power the Controllable System is - // able to consume according to the device label or data sheet. - // - // parameters: - // - value: nominal max power consumption in W - SetPowerConsumptionNominalMax(value float64) (resultErr error) - // return nominal maximum active (real) power the Controllable System is // allowed to consume due to the customer's contract. ContractualConsumptionNominalMax() (float64, error) diff --git a/uclpcserver/public.go b/uclpcserver/public.go index 089bc65..c652c83 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -176,35 +176,6 @@ func (e *UCLPCServer) SetFailsafeDurationMinimum(duration time.Duration, changea // Scenario 4 -// return nominal maximum active (real) power the Controllable System is -// able to consume according to the device label or data sheet. -func (e *UCLPCServer) PowerConsumptionNominalMax() (value float64, resultErr error) { - value = 0 - resultErr = eebusapi.ErrDataNotAvailable - - charData := util.GetLocalElectricalConnectionCharacteristicForContextType( - e.service, - model.ElectricalConnectionCharacteristicContextTypeEntity, - model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax, - ) - if charData.CharacteristicId == nil || charData.Value == nil { - return - } - - return charData.Value.GetValue(), nil -} - -// set nominal maximum active (real) power the Controllable System is -// able to consume according to the device label or data sheet. -func (e *UCLPCServer) SetPowerConsumptionNominalMax(value float64) error { - return util.SetLocalElectricalConnectionCharacteristicForContextType( - e.service, - model.ElectricalConnectionCharacteristicContextTypeEntity, - model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax, - value, - ) -} - // return nominal maximum active (real) power the Controllable System is // allowed to consume due to the customer's contract. func (e *UCLPCServer) ContractualConsumptionNominalMax() (value float64, resultErr error) { diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index 3eba46f..36ed3a8 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -60,19 +60,6 @@ func (s *UCLPCServerSuite) Test_FailsafeDurationMinimum() { assert.Nil(s.T(), err) } -func (s *UCLPCServerSuite) Test_PowerConsumptionNominalMax() { - value, err := s.sut.PowerConsumptionNominalMax() - assert.Equal(s.T(), 0.0, value) - assert.NotNil(s.T(), err) - - err = s.sut.SetPowerConsumptionNominalMax(10) - assert.Nil(s.T(), err) - - value, err = s.sut.PowerConsumptionNominalMax() - assert.Equal(s.T(), 10.0, value) - assert.Nil(s.T(), err) -} - func (s *UCLPCServerSuite) Test_ContractualConsumptionNominalMax() { value, err := s.sut.ContractualConsumptionNominalMax() assert.Equal(s.T(), 0.0, value) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 02c6895..ea90fe9 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -133,14 +133,6 @@ func (e *UCLPCServer) AddFeatures() { ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), CharacteristicId: eebusutil.Ptr(elCharId), CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), - CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypePowerConsumptionNominalMax), - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - }, - { - ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), - CharacteristicId: eebusutil.Ptr(elCharId + 1), - CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax), Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), }, From 5b8537285dfb3d0443ad7da171c6052d203a5410 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 09:06:03 +0200 Subject: [PATCH 159/227] ElectricalConnection is not required in UCLPC A consumable device is not required to support scenario 4 of the LPC use case, so we should require it being available. --- uclpc/uclpc.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uclpc/uclpc.go b/uclpc/uclpc.go index 9f30032..59811ae 100644 --- a/uclpc/uclpc.go +++ b/uclpc/uclpc.go @@ -100,7 +100,6 @@ func (e *UCLPC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, model.FeatureTypeTypeDeviceDiagnosis, model.FeatureTypeTypeLoadControl, model.FeatureTypeTypeDeviceConfiguration, - model.FeatureTypeTypeElectricalConnection, }, ) { return false, nil @@ -118,9 +117,5 @@ func (e *UCLPC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, return false, eebusapi.ErrFunctionNotSupported } - if _, err := util.ElectricalConnection(e.service, entity); err != nil { - return false, eebusapi.ErrFunctionNotSupported - } - return true, nil } From 8648f6dd60bcf32e7aacea2c74bcc32315a0b376 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 09:07:52 +0200 Subject: [PATCH 160/227] CEM shall not support this in UCLPC A consumable device not being a CEM may not support `ContractualConsumptionNominalMax `, this is restricted to consumable devices being a CEM. This is defined in UC LPC 1.0 Spec, chapter 2.6.4.1 --- uclpc/api.go | 7 ------- uclpc/public.go | 23 ----------------------- uclpc/public_test.go | 30 ------------------------------ 3 files changed, 60 deletions(-) diff --git a/uclpc/api.go b/uclpc/api.go index 8fbef89..11a2f04 100644 --- a/uclpc/api.go +++ b/uclpc/api.go @@ -84,11 +84,4 @@ type UCLCPInterface interface { // parameters: // - entity: the entity of the e.g. EVSE PowerConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) - - // return nominal maximum active (real) power the Controllable System is - // allowed to consume due to the customer's contract. - // - // parameters: - // - entity: the entity of the e.g. EVSE - ContractualConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) } diff --git a/uclpc/public.go b/uclpc/public.go index 5f4dee5..08370f7 100644 --- a/uclpc/public.go +++ b/uclpc/public.go @@ -302,26 +302,3 @@ func (e *UCLPC) PowerConsumptionNominalMax(entity spineapi.EntityRemoteInterface return data.Value.GetValue(), nil } - -// return nominal maximum active (real) power the Controllable System is -// allowed to consume due to the customer's contract. -func (e *UCLPC) ContractualConsumptionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) { - if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { - return 0, api.ErrNoCompatibleEntity - } - - electricalConnection, err := util.ElectricalConnection(e.service, entity) - if err != nil || electricalConnection == nil { - return 0, err - } - - data, err := electricalConnection.GetCharacteristicForContextType( - model.ElectricalConnectionCharacteristicContextTypeEntity, - model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax, - ) - if err != nil || data.Value == nil { - return 0, err - } - - return data.Value.GetValue(), nil -} diff --git a/uclpc/public_test.go b/uclpc/public_test.go index 08dc723..18f64af 100644 --- a/uclpc/public_test.go +++ b/uclpc/public_test.go @@ -353,33 +353,3 @@ func (s *UCLPCSuite) Test_PowerConsumptionNominalMax() { assert.Nil(s.T(), err) assert.Equal(s.T(), 8000.0, data) } - -func (s *UCLPCSuite) Test_ContractualConsumptionNominalMax() { - data, err := s.sut.ContractualConsumptionNominalMax(s.mockRemoteEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, data) - - data, err = s.sut.ContractualConsumptionNominalMax(s.monitoredEntity) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, data) - - charData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ - { - ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), - CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), - CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), - CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax), - Value: model.NewScaledNumberType(7000), - }, - }, - } - - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) - fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionCharacteristicListData, charData, nil, nil) - assert.Nil(s.T(), fErr) - - data, err = s.sut.ContractualConsumptionNominalMax(s.monitoredEntity) - assert.Nil(s.T(), err) - assert.Equal(s.T(), 7000.0, data) -} From 6697f4dcc2427accc251d21c3c05d1a5eec3ca89 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 16:52:21 +0200 Subject: [PATCH 161/227] Update SPINE, EEBUS - Fixes incorect ElectricalConnectionCharacteristicListDataType model --- go.mod | 4 ++-- go.sum | 8 ++++---- uclpc/public_test.go | 2 +- uclpcserver/uclpc.go | 6 +++--- util/electricalconnection.go | 6 +++--- util/electricalconnection_test.go | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 0e791b3..ff8d862 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240327124756-c2f35e1217be + github.com/enbility/eebus-go v0.0.0-20240403142700-2f6a203b9c99 github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 - github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 + github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 42b6cae..32951ea 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240327124756-c2f35e1217be h1:HD3VqcjLzF1W9zLCA+qEJ2gfPKBm2n29XdBVIPaQ1Es= -github.com/enbility/eebus-go v0.0.0-20240327124756-c2f35e1217be/go.mod h1:f2X3uJaPyP9vhK0jnyYI2VMkq+HUnjp9usyeAeuMSyw= +github.com/enbility/eebus-go v0.0.0-20240403142700-2f6a203b9c99 h1:6uhFCOpzAS1bdxaH/od6B30Xn20PQpfuByxhnemWCGY= +github.com/enbility/eebus-go v0.0.0-20240403142700-2f6a203b9c99/go.mod h1:zgZeurjiQgixRvIMPd1ofN2xQJGXDq4371DyeqHDZi8= github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 h1:r71X9nuRTe2bUwWVRBpgaWII/oweFVWeM3DvgPtorAM= github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534 h1:DPyWFN3+6eAJwdA2nrLzXOXPSoe+mb0ccUFSVB8TI90= -github.com/enbility/spine-go v0.0.0-20240313120235-e9eec2c46534/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a h1:H34tdsbi4MIQYbZxYKdMx5fm37WsXmJbyD2R3vGo4EQ= +github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/uclpc/public_test.go b/uclpc/public_test.go index 18f64af..a449188 100644 --- a/uclpc/public_test.go +++ b/uclpc/public_test.go @@ -334,7 +334,7 @@ func (s *UCLPCSuite) Test_PowerConsumptionNominalMax() { assert.Equal(s.T(), 0.0, data) charData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ { ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index ea90fe9..8133698 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -116,8 +116,8 @@ func (e *UCLPCServer) AddFeatures() { var elCharId model.ElectricalConnectionCharacteristicIdType = 0 // get the heighest CharacteristicId if desc, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( - f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicListData != nil { - for _, desc := range desc.ElectricalConnectionCharacteristicListData { + f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicData != nil { + for _, desc := range desc.ElectricalConnectionCharacteristicData { if desc.CharacteristicId != nil && *desc.CharacteristicId >= elCharId { elCharId++ } @@ -127,7 +127,7 @@ func (e *UCLPCServer) AddFeatures() { // ElectricalConnectionId and ParameterId should be identical to the ones used // in a MPC Server role implementation, which is not done here (yet) elCharData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ { ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), diff --git a/util/electricalconnection.go b/util/electricalconnection.go index 4750da0..b41a055 100644 --- a/util/electricalconnection.go +++ b/util/electricalconnection.go @@ -67,11 +67,11 @@ func GetLocalElectricalConnectionCharacteristicForContextType( function := model.FunctionTypeElectricalConnectionCharacteristicListData data, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( electricalConnection, function) - if err != nil || data == nil || data.ElectricalConnectionCharacteristicListData == nil { + if err != nil || data == nil || data.ElectricalConnectionCharacteristicData == nil { return } - for _, item := range data.ElectricalConnectionCharacteristicListData { + for _, item := range data.ElectricalConnectionCharacteristicData { if item.CharacteristicContext != nil && *item.CharacteristicContext == context && item.CharacteristicType != nil && *item.CharacteristicType == charType { charData = item @@ -105,7 +105,7 @@ func SetLocalElectricalConnectionCharacteristicForContextType( function := model.FunctionTypeElectricalConnectionCharacteristicListData listData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{charData}, + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{charData}, } electricalConnection.SetData(function, listData) diff --git a/util/electricalconnection_test.go b/util/electricalconnection_test.go index 4ef48b0..1ff03b0 100644 --- a/util/electricalconnection_test.go +++ b/util/electricalconnection_test.go @@ -177,7 +177,7 @@ func (s *UtilSuite) Test_GetLocalElectricalConnectionCharacteristicForContextTyp feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) charData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ { ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), @@ -205,7 +205,7 @@ func (s *UtilSuite) Test_SetLocalElectricalConnectionCharacteristicForContextTyp feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) charData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicListData: []model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ { ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), From 44056d98d2a942c9256ccc22cef3048e0e321d56 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 17:32:18 +0200 Subject: [PATCH 162/227] Rename UCEVCC api The name should fit the scenario description and not confuse with currents --- ucevcc/api.go | 2 +- ucevcc/public.go | 2 +- ucevcc/public_test.go | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ucevcc/api.go b/ucevcc/api.go index 59be93c..83b24af 100644 --- a/ucevcc/api.go +++ b/ucevcc/api.go @@ -66,7 +66,7 @@ type UCEVCCInterface interface { // // parameters: // - entity: the entity of the EV - CurrentLimits(entity spineapi.EntityRemoteInterface) (float64, float64, float64, error) + ChargingPowerLimits(entity spineapi.EntityRemoteInterface) (float64, float64, float64, error) // Scenario 7 diff --git a/ucevcc/public.go b/ucevcc/public.go index ce4dcff..3acd71e 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -210,7 +210,7 @@ func (e *UCEVCC) ManufacturerData( // possible errors: // - ErrDataNotAvailable if no such measurement is (yet) available // - and others -func (e *UCEVCC) CurrentLimits(entity spineapi.EntityRemoteInterface) (float64, float64, float64, error) { +func (e *UCEVCC) ChargingPowerLimits(entity spineapi.EntityRemoteInterface) (float64, float64, float64, error) { if !util.IsCompatibleEntity(entity, e.validEntityTypes) { return 0.0, 0.0, 0.0, api.ErrNoCompatibleEntity } diff --git a/ucevcc/public_test.go b/ucevcc/public_test.go index d9242fe..8ff39dd 100644 --- a/ucevcc/public_test.go +++ b/ucevcc/public_test.go @@ -291,14 +291,14 @@ func (s *UCEVCCSuite) Test_EVManufacturerData() { assert.Equal(s.T(), "", data.BrandName) } -func (s *UCEVCCSuite) Test_EVCurrentLimits() { - minData, maxData, standByData, err := s.sut.CurrentLimits(s.mockRemoteEntity) +func (s *UCEVCCSuite) Test_EVChargingPowerLimits() { + minData, maxData, standByData, err := s.sut.ChargingPowerLimits(s.mockRemoteEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, minData) assert.Equal(s.T(), 0.0, maxData) assert.Equal(s.T(), 0.0, standByData) - minData, maxData, standByData, err = s.sut.CurrentLimits(s.evEntity) + minData, maxData, standByData, err = s.sut.ChargingPowerLimits(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, minData) assert.Equal(s.T(), 0.0, maxData) @@ -318,7 +318,7 @@ func (s *UCEVCCSuite) Test_EVCurrentLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - minData, maxData, standByData, err = s.sut.CurrentLimits(s.evEntity) + minData, maxData, standByData, err = s.sut.ChargingPowerLimits(s.evEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, minData) assert.Equal(s.T(), 0.0, maxData) @@ -377,7 +377,7 @@ func (s *UCEVCCSuite) Test_EVCurrentLimits() { fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - minData, maxData, standByData, err = s.sut.CurrentLimits(s.evEntity) + minData, maxData, standByData, err = s.sut.ChargingPowerLimits(s.evEntity) assert.Nil(s.T(), err) assert.Nil(s.T(), err) From 6139a4f8cdf2906942db6b82b07c385b2569ca79 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 3 Apr 2024 17:43:12 +0200 Subject: [PATCH 163/227] Update mocks --- mocks/CemInterface.go | 2 +- mocks/DeviceEventCallback.go | 2 +- mocks/EntityEventCallback.go | 2 +- mocks/UCCEVCInterface.go | 2 +- mocks/UCEVCCInterface.go | 110 +++++++++++++++++----------------- mocks/UCEVCEMInterface.go | 2 +- mocks/UCEVSECCInterface.go | 2 +- mocks/UCEVSOCInterface.go | 2 +- mocks/UCLCPInterface.go | 58 +----------------- mocks/UCLCPServerInterface.go | 103 +------------------------------ mocks/UCMCPInterface.go | 2 +- mocks/UCMGCPInterface.go | 2 +- mocks/UCOPEVInterface.go | 2 +- mocks/UCOSCEVInterface.go | 2 +- mocks/UCVABDInterface.go | 2 +- mocks/UCVAPDInterface.go | 2 +- mocks/UseCaseInterface.go | 2 +- 17 files changed, 71 insertions(+), 228 deletions(-) diff --git a/mocks/CemInterface.go b/mocks/CemInterface.go index f834515..156df30 100644 --- a/mocks/CemInterface.go +++ b/mocks/CemInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/DeviceEventCallback.go b/mocks/DeviceEventCallback.go index 7dc91db..ad11a90 100644 --- a/mocks/DeviceEventCallback.go +++ b/mocks/DeviceEventCallback.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/EntityEventCallback.go b/mocks/EntityEventCallback.go index 6922b4f..530b6ab 100644 --- a/mocks/EntityEventCallback.go +++ b/mocks/EntityEventCallback.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCCEVCInterface.go b/mocks/UCCEVCInterface.go index b66a267..99d022f 100644 --- a/mocks/UCCEVCInterface.go +++ b/mocks/UCCEVCInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCEVCCInterface.go b/mocks/UCEVCCInterface.go index a76a81a..dff64af 100644 --- a/mocks/UCEVCCInterface.go +++ b/mocks/UCEVCCInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks @@ -200,128 +200,128 @@ func (_c *UCEVCCInterface_ChargeState_Call) RunAndReturn(run func(api.EntityRemo return _c } -// CommunicationStandard provides a mock function with given fields: entity -func (_m *UCEVCCInterface) CommunicationStandard(entity api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error) { +// ChargingPowerLimits provides a mock function with given fields: entity +func (_m *UCEVCCInterface) ChargingPowerLimits(entity api.EntityRemoteInterface) (float64, float64, float64, error) { ret := _m.Called(entity) if len(ret) == 0 { - panic("no return value specified for CommunicationStandard") + panic("no return value specified for ChargingPowerLimits") } - var r0 model.DeviceConfigurationKeyValueStringType - var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error)); ok { + var r0 float64 + var r1 float64 + var r2 float64 + var r3 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, float64, float64, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) model.DeviceConfigurationKeyValueStringType); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { r0 = rf(entity) } else { - r0 = ret.Get(0).(model.DeviceConfigurationKeyValueStringType) + r0 = ret.Get(0).(float64) } - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) float64); ok { r1 = rf(entity) } else { - r1 = ret.Error(1) + r1 = ret.Get(1).(float64) } - return r0, r1 + if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) float64); ok { + r2 = rf(entity) + } else { + r2 = ret.Get(2).(float64) + } + + if rf, ok := ret.Get(3).(func(api.EntityRemoteInterface) error); ok { + r3 = rf(entity) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 } -// UCEVCCInterface_CommunicationStandard_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommunicationStandard' -type UCEVCCInterface_CommunicationStandard_Call struct { +// UCEVCCInterface_ChargingPowerLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChargingPowerLimits' +type UCEVCCInterface_ChargingPowerLimits_Call struct { *mock.Call } -// CommunicationStandard is a helper method to define mock.On call +// ChargingPowerLimits is a helper method to define mock.On call // - entity api.EntityRemoteInterface -func (_e *UCEVCCInterface_Expecter) CommunicationStandard(entity interface{}) *UCEVCCInterface_CommunicationStandard_Call { - return &UCEVCCInterface_CommunicationStandard_Call{Call: _e.mock.On("CommunicationStandard", entity)} +func (_e *UCEVCCInterface_Expecter) ChargingPowerLimits(entity interface{}) *UCEVCCInterface_ChargingPowerLimits_Call { + return &UCEVCCInterface_ChargingPowerLimits_Call{Call: _e.mock.On("ChargingPowerLimits", entity)} } -func (_c *UCEVCCInterface_CommunicationStandard_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_CommunicationStandard_Call { +func (_c *UCEVCCInterface_ChargingPowerLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_ChargingPowerLimits_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCEVCCInterface_CommunicationStandard_Call) Return(_a0 model.DeviceConfigurationKeyValueStringType, _a1 error) *UCEVCCInterface_CommunicationStandard_Call { - _c.Call.Return(_a0, _a1) +func (_c *UCEVCCInterface_ChargingPowerLimits_Call) Return(_a0 float64, _a1 float64, _a2 float64, _a3 error) *UCEVCCInterface_ChargingPowerLimits_Call { + _c.Call.Return(_a0, _a1, _a2, _a3) return _c } -func (_c *UCEVCCInterface_CommunicationStandard_Call) RunAndReturn(run func(api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error)) *UCEVCCInterface_CommunicationStandard_Call { +func (_c *UCEVCCInterface_ChargingPowerLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, float64, float64, error)) *UCEVCCInterface_ChargingPowerLimits_Call { _c.Call.Return(run) return _c } -// CurrentLimits provides a mock function with given fields: entity -func (_m *UCEVCCInterface) CurrentLimits(entity api.EntityRemoteInterface) (float64, float64, float64, error) { +// CommunicationStandard provides a mock function with given fields: entity +func (_m *UCEVCCInterface) CommunicationStandard(entity api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error) { ret := _m.Called(entity) if len(ret) == 0 { - panic("no return value specified for CurrentLimits") + panic("no return value specified for CommunicationStandard") } - var r0 float64 - var r1 float64 - var r2 float64 - var r3 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, float64, float64, error)); ok { + var r0 model.DeviceConfigurationKeyValueStringType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error)); ok { return rf(entity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) model.DeviceConfigurationKeyValueStringType); ok { r0 = rf(entity) } else { - r0 = ret.Get(0).(float64) + r0 = ret.Get(0).(model.DeviceConfigurationKeyValueStringType) } - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) float64); ok { + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { r1 = rf(entity) } else { - r1 = ret.Get(1).(float64) - } - - if rf, ok := ret.Get(2).(func(api.EntityRemoteInterface) float64); ok { - r2 = rf(entity) - } else { - r2 = ret.Get(2).(float64) - } - - if rf, ok := ret.Get(3).(func(api.EntityRemoteInterface) error); ok { - r3 = rf(entity) - } else { - r3 = ret.Error(3) + r1 = ret.Error(1) } - return r0, r1, r2, r3 + return r0, r1 } -// UCEVCCInterface_CurrentLimits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentLimits' -type UCEVCCInterface_CurrentLimits_Call struct { +// UCEVCCInterface_CommunicationStandard_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommunicationStandard' +type UCEVCCInterface_CommunicationStandard_Call struct { *mock.Call } -// CurrentLimits is a helper method to define mock.On call +// CommunicationStandard is a helper method to define mock.On call // - entity api.EntityRemoteInterface -func (_e *UCEVCCInterface_Expecter) CurrentLimits(entity interface{}) *UCEVCCInterface_CurrentLimits_Call { - return &UCEVCCInterface_CurrentLimits_Call{Call: _e.mock.On("CurrentLimits", entity)} +func (_e *UCEVCCInterface_Expecter) CommunicationStandard(entity interface{}) *UCEVCCInterface_CommunicationStandard_Call { + return &UCEVCCInterface_CommunicationStandard_Call{Call: _e.mock.On("CommunicationStandard", entity)} } -func (_c *UCEVCCInterface_CurrentLimits_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_CurrentLimits_Call { +func (_c *UCEVCCInterface_CommunicationStandard_Call) Run(run func(entity api.EntityRemoteInterface)) *UCEVCCInterface_CommunicationStandard_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCEVCCInterface_CurrentLimits_Call) Return(_a0 float64, _a1 float64, _a2 float64, _a3 error) *UCEVCCInterface_CurrentLimits_Call { - _c.Call.Return(_a0, _a1, _a2, _a3) +func (_c *UCEVCCInterface_CommunicationStandard_Call) Return(_a0 model.DeviceConfigurationKeyValueStringType, _a1 error) *UCEVCCInterface_CommunicationStandard_Call { + _c.Call.Return(_a0, _a1) return _c } -func (_c *UCEVCCInterface_CurrentLimits_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, float64, float64, error)) *UCEVCCInterface_CurrentLimits_Call { +func (_c *UCEVCCInterface_CommunicationStandard_Call) RunAndReturn(run func(api.EntityRemoteInterface) (model.DeviceConfigurationKeyValueStringType, error)) *UCEVCCInterface_CommunicationStandard_Call { _c.Call.Return(run) return _c } diff --git a/mocks/UCEVCEMInterface.go b/mocks/UCEVCEMInterface.go index ecb5ef9..380180d 100644 --- a/mocks/UCEVCEMInterface.go +++ b/mocks/UCEVCEMInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCEVSECCInterface.go b/mocks/UCEVSECCInterface.go index d2aa9a9..0b91d79 100644 --- a/mocks/UCEVSECCInterface.go +++ b/mocks/UCEVSECCInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCEVSOCInterface.go b/mocks/UCEVSOCInterface.go index 2a5b041..fb86676 100644 --- a/mocks/UCEVSOCInterface.go +++ b/mocks/UCEVSOCInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCLCPInterface.go b/mocks/UCLCPInterface.go index 6cf65f7..f61b5db 100644 --- a/mocks/UCLCPInterface.go +++ b/mocks/UCLCPInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks @@ -90,62 +90,6 @@ func (_c *UCLCPInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLCPInterfa return _c } -// ContractualConsumptionNominalMax provides a mock function with given fields: entity -func (_m *UCLCPInterface) ContractualConsumptionNominalMax(entity api.EntityRemoteInterface) (float64, error) { - ret := _m.Called(entity) - - if len(ret) == 0 { - panic("no return value specified for ContractualConsumptionNominalMax") - } - - var r0 float64 - var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { - return rf(entity) - } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { - r0 = rf(entity) - } else { - r0 = ret.Get(0).(float64) - } - - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { - r1 = rf(entity) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UCLCPInterface_ContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ContractualConsumptionNominalMax' -type UCLCPInterface_ContractualConsumptionNominalMax_Call struct { - *mock.Call -} - -// ContractualConsumptionNominalMax is a helper method to define mock.On call -// - entity api.EntityRemoteInterface -func (_e *UCLCPInterface_Expecter) ContractualConsumptionNominalMax(entity interface{}) *UCLCPInterface_ContractualConsumptionNominalMax_Call { - return &UCLCPInterface_ContractualConsumptionNominalMax_Call{Call: _e.mock.On("ContractualConsumptionNominalMax", entity)} -} - -func (_c *UCLCPInterface_ContractualConsumptionNominalMax_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_ContractualConsumptionNominalMax_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EntityRemoteInterface)) - }) - return _c -} - -func (_c *UCLCPInterface_ContractualConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPInterface_ContractualConsumptionNominalMax_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *UCLCPInterface_ContractualConsumptionNominalMax_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLCPInterface_ContractualConsumptionNominalMax_Call { - _c.Call.Return(run) - return _c -} - // FailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity func (_m *UCLCPInterface) FailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface) (float64, error) { ret := _m.Called(entity) diff --git a/mocks/UCLCPServerInterface.go b/mocks/UCLCPServerInterface.go index 45b72f8..e769bfe 100644 --- a/mocks/UCLCPServerInterface.go +++ b/mocks/UCLCPServerInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks @@ -380,61 +380,6 @@ func (_c *UCLCPServerInterface_LoadControlLimit_Call) RunAndReturn(run func() (c return _c } -// PowerConsumptionNominalMax provides a mock function with given fields: -func (_m *UCLCPServerInterface) PowerConsumptionNominalMax() (float64, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for PowerConsumptionNominalMax") - } - - var r0 float64 - var r1 error - if rf, ok := ret.Get(0).(func() (float64, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() float64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(float64) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UCLCPServerInterface_PowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerConsumptionNominalMax' -type UCLCPServerInterface_PowerConsumptionNominalMax_Call struct { - *mock.Call -} - -// PowerConsumptionNominalMax is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) PowerConsumptionNominalMax() *UCLCPServerInterface_PowerConsumptionNominalMax_Call { - return &UCLCPServerInterface_PowerConsumptionNominalMax_Call{Call: _e.mock.On("PowerConsumptionNominalMax")} -} - -func (_c *UCLCPServerInterface_PowerConsumptionNominalMax_Call) Run(run func()) *UCLCPServerInterface_PowerConsumptionNominalMax_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *UCLCPServerInterface_PowerConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPServerInterface_PowerConsumptionNominalMax_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *UCLCPServerInterface_PowerConsumptionNominalMax_Call) RunAndReturn(run func() (float64, error)) *UCLCPServerInterface_PowerConsumptionNominalMax_Call { - _c.Call.Return(run) - return _c -} - // SetContractualConsumptionNominalMax provides a mock function with given fields: value func (_m *UCLCPServerInterface) SetContractualConsumptionNominalMax(value float64) error { ret := _m.Called(value) @@ -621,52 +566,6 @@ func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) RunAndReturn(run func(c return _c } -// SetPowerConsumptionNominalMax provides a mock function with given fields: value -func (_m *UCLCPServerInterface) SetPowerConsumptionNominalMax(value float64) error { - ret := _m.Called(value) - - if len(ret) == 0 { - panic("no return value specified for SetPowerConsumptionNominalMax") - } - - var r0 error - if rf, ok := ret.Get(0).(func(float64) error); ok { - r0 = rf(value) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UCLCPServerInterface_SetPowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPowerConsumptionNominalMax' -type UCLCPServerInterface_SetPowerConsumptionNominalMax_Call struct { - *mock.Call -} - -// SetPowerConsumptionNominalMax is a helper method to define mock.On call -// - value float64 -func (_e *UCLCPServerInterface_Expecter) SetPowerConsumptionNominalMax(value interface{}) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { - return &UCLCPServerInterface_SetPowerConsumptionNominalMax_Call{Call: _e.mock.On("SetPowerConsumptionNominalMax", value)} -} - -func (_c *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call) Run(run func(value float64)) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(float64)) - }) - return _c -} - -func (_c *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call) Return(resultErr error) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { - _c.Call.Return(resultErr) - return _c -} - -func (_c *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call) RunAndReturn(run func(float64) error) *UCLCPServerInterface_SetPowerConsumptionNominalMax_Call { - _c.Call.Return(run) - return _c -} - // UpdateUseCaseAvailability provides a mock function with given fields: available func (_m *UCLCPServerInterface) UpdateUseCaseAvailability(available bool) { _m.Called(available) diff --git a/mocks/UCMCPInterface.go b/mocks/UCMCPInterface.go index e8f3b7a..4afdefb 100644 --- a/mocks/UCMCPInterface.go +++ b/mocks/UCMCPInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCMGCPInterface.go b/mocks/UCMGCPInterface.go index 61f02f7..5c5d0bf 100644 --- a/mocks/UCMGCPInterface.go +++ b/mocks/UCMGCPInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCOPEVInterface.go b/mocks/UCOPEVInterface.go index 47bf73d..279f84c 100644 --- a/mocks/UCOPEVInterface.go +++ b/mocks/UCOPEVInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCOSCEVInterface.go b/mocks/UCOSCEVInterface.go index cfaf957..3ede470 100644 --- a/mocks/UCOSCEVInterface.go +++ b/mocks/UCOSCEVInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCVABDInterface.go b/mocks/UCVABDInterface.go index 4fd5be3..d761012 100644 --- a/mocks/UCVABDInterface.go +++ b/mocks/UCVABDInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UCVAPDInterface.go b/mocks/UCVAPDInterface.go index 0cbf801..e2ee134 100644 --- a/mocks/UCVAPDInterface.go +++ b/mocks/UCVAPDInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/mocks/UseCaseInterface.go b/mocks/UseCaseInterface.go index ecd84a2..98831d8 100644 --- a/mocks/UseCaseInterface.go +++ b/mocks/UseCaseInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks From 8797d1684cbf11c1c9b8bb790b5d552cbbda79c5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 4 Apr 2024 10:26:12 +0200 Subject: [PATCH 164/227] Add Binding request to UCOPEV --- ucopev/events.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ucopev/events.go b/ucopev/events.go index cbd19bd..c328c6c 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -43,6 +43,10 @@ func (e *UCOPEV) evConnected(entity spineapi.EntityRemoteInterface) { logging.Log().Debug(err) } + if _, err := evLoadControl.Bind(); err != nil { + logging.Log().Debug(err) + } + // get descriptions if _, err := evLoadControl.RequestLimitDescriptions(); err != nil { logging.Log().Debug(err) From 24344bec84a0476a4402bb0201476f58c07a82d7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Apr 2024 13:18:22 +0200 Subject: [PATCH 165/227] Minor cleanup --- cmd/democem/service.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/democem/service.go b/cmd/democem/service.go index e82e8d8..2a0f7b7 100644 --- a/cmd/democem/service.go +++ b/cmd/democem/service.go @@ -6,9 +6,6 @@ import ( ) // report the Ship ID of a newly trusted connection -func (d *DemoCem) RemoteServiceShipIDReported(service eebusapi.ServiceInterface, ski string, shipID string) { -} - func (d *DemoCem) RemoteSKIConnected(service eebusapi.ServiceInterface, ski string) {} func (d *DemoCem) RemoteSKIDisconnected(service eebusapi.ServiceInterface, ski string) {} @@ -19,5 +16,3 @@ func (d *DemoCem) VisibleRemoteServicesUpdated(service eebusapi.ServiceInterface func (h *DemoCem) ServiceShipIDUpdate(ski string, shipdID string) {} func (h *DemoCem) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) {} - -func (h *DemoCem) AllowWaitingForTrust(ski string) bool { return true } From 126daaf416cec0278a8a828d8088fa541bab7d03 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Apr 2024 13:18:54 +0200 Subject: [PATCH 166/227] Add new event type to UCCEVC --- uccevc/events.go | 10 +++++++++- uccevc/events_test.go | 3 +++ uccevc/types.go | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/uccevc/events.go b/uccevc/events.go index 0709918..292066e 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -38,6 +38,9 @@ func (e *UCCEVC) HandleEvent(payload spineapi.EventPayload) { case *model.IncentiveTableDescriptionDataType: e.evIncentiveTableDescriptionDataUpdate(payload) + case *model.IncentiveTableConstraintsDataType: + e.evIncentiveTableConstraintsDataUpdate(payload) + case *model.IncentiveDataType: e.evIncentiveTableDataUpdate(payload) } @@ -156,7 +159,12 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(payload spineapi.EventPay } -// the load control limit data of an EV was updated +// the incentive table constraint data of an EV was updated +func (e *UCCEVC) evIncentiveTableConstraintsDataUpdate(payload spineapi.EventPayload) { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateIncentiveTable) +} + +// the incentive table data of an EV was updated func (e *UCCEVC) evIncentiveTableDataUpdate(payload spineapi.EventPayload) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateIncentiveTable) } diff --git a/uccevc/events_test.go b/uccevc/events_test.go index 34fa2a7..c4a2aa7 100644 --- a/uccevc/events_test.go +++ b/uccevc/events_test.go @@ -37,6 +37,9 @@ func (s *UCCEVCSuite) Test_Events() { payload.Data = eebusutil.Ptr(model.IncentiveTableDescriptionDataType{}) s.sut.HandleEvent(payload) + payload.Data = eebusutil.Ptr(model.IncentiveTableConstraintsDataType{}) + s.sut.HandleEvent(payload) + payload.Data = eebusutil.Ptr(model.IncentiveDataType{}) s.sut.HandleEvent(payload) } diff --git a/uccevc/types.go b/uccevc/types.go index 1ed5e24..4f1cb11 100644 --- a/uccevc/types.go +++ b/uccevc/types.go @@ -30,6 +30,13 @@ const ( // - the entity of the EV DataUpdateIncentiveTable api.EventType = "DataUpdateIncentiveTable" + // EV incentive table data constraints updated + // + // The callback with this message provides: + // - the device of the EVSE the EV is connected to + // - the entity of the EV + DataUpdateIncentiveTableConstraints api.EventType = "DataUpdateIncentiveTableConstraints" + // EV requested an incentive table, call to WriteIncentiveTableDescriptions required // // The callback with this message provides: From 51591bc7d9681936c1019a69471c21b160074cde Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 5 Apr 2024 13:19:10 +0200 Subject: [PATCH 167/227] Update naming of UCEVCC event type --- ucevcc/events.go | 2 +- ucevcc/types.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ucevcc/events.go b/ucevcc/events.go index b611e1c..7d5c161 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -140,7 +140,7 @@ func (e *UCEVCC) evConfigurationDataUpdate(payload spineapi.EventPayload) { // Scenario 3 if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { - e.eventCB(payload.Ski, payload.Device, payload.Entity, AsymmetricChargingSupportDataUpdate) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateAsymmetricChargingSupport) } } diff --git a/ucevcc/types.go b/ucevcc/types.go index 2a5b7f9..b06efe3 100644 --- a/ucevcc/types.go +++ b/ucevcc/types.go @@ -56,7 +56,7 @@ const ( // - the entity of the EV // // Note: the referred data may be updated together with all other configuration items of this use case - AsymmetricChargingSupportDataUpdate api.EventType = "AsymmetricChargingSupportDataUpdate" + DataUpdateAsymmetricChargingSupport api.EventType = "DataUpdateAsymmetricChargingSupport" // EV identificationdata was updated // From 457cab1a439e036434d2671cafe93a11b91ecc04 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Apr 2024 19:01:53 +0200 Subject: [PATCH 168/227] Update SHIP, eebus libs --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ff8d862..9cc5759 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240403142700-2f6a203b9c99 - github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 + github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 + github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 32951ea..eaf3670 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240403142700-2f6a203b9c99 h1:6uhFCOpzAS1bdxaH/od6B30Xn20PQpfuByxhnemWCGY= -github.com/enbility/eebus-go v0.0.0-20240403142700-2f6a203b9c99/go.mod h1:zgZeurjiQgixRvIMPd1ofN2xQJGXDq4371DyeqHDZi8= -github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652 h1:r71X9nuRTe2bUwWVRBpgaWII/oweFVWeM3DvgPtorAM= -github.com/enbility/ship-go v0.0.0-20240327112751-034de9b6e652/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 h1:Tbt5F8hroDxTOXqwJtzVXdpFIg8vby21Vzchm2ftvIk= +github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400/go.mod h1:H0XQ8jb91zqsdWNTOKPOHHs+EAyKzLrNiGyPwjol028= +github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 h1:k89K2PYYVgAYbRZ3Kj+1AZ9Pgme7aOESXEhXSZ3qEHs= +github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a h1:H34tdsbi4MIQYbZxYKdMx5fm37WsXmJbyD2R3vGo4EQ= github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= From 1e808c628975f2434af57565497c8b87c8a7b90c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 8 Apr 2024 19:02:32 +0200 Subject: [PATCH 169/227] Fix heartbeats and remove write access --- uclpcserver/uclpc.go | 2 +- ucopev/ucopev.go | 3 ++- ucoscev/ucoscev.go | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 8133698..7400763 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -108,7 +108,7 @@ func (e *UCLPCServer) AddFeatures() { f.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, deviceConfigDesc) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, true) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 20ddc3b..80fa6c2 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -52,7 +52,8 @@ func (e *UCOPEV) AddFeatures() { // server features f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, false, false) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) } func (e *UCOPEV) AddUseCase() { diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 470002f..0bb940d 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -58,7 +58,8 @@ func (e *UCOSCEV) AddFeatures() { // server features f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) - f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, false, false) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) } func (e *UCOSCEV) AddUseCase() { From 3a20e5afda2b21af972e828b416e5b2f71a28239 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 12:22:37 +0200 Subject: [PATCH 170/227] Update SPINE Includes the local feature a remote device wanted to bind to for this kind of event --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9cc5759..86b6171 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 - github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a + github.com/enbility/spine-go v0.0.0-20240409101612-0cdc3e9a9cfc github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index eaf3670..cec78d4 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 h1:Tbt5F8hroDxTO github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400/go.mod h1:H0XQ8jb91zqsdWNTOKPOHHs+EAyKzLrNiGyPwjol028= github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 h1:k89K2PYYVgAYbRZ3Kj+1AZ9Pgme7aOESXEhXSZ3qEHs= github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a h1:H34tdsbi4MIQYbZxYKdMx5fm37WsXmJbyD2R3vGo4EQ= -github.com/enbility/spine-go v0.0.0-20240403141316-f4ab578e8a1a/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240409101612-0cdc3e9a9cfc h1:67EyKovCzjjf8FYlcwxb6KUR3AuXHwdJ6odtEC+mxlw= +github.com/enbility/spine-go v0.0.0-20240409101612-0cdc3e9a9cfc/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From dc1324efee4d221f03cc0e2dd39570f5dd92e28e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 12:23:19 +0200 Subject: [PATCH 171/227] Allow a CEM entity to be used in an SMGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though this doesn’t make sense to me, but KEO does it --- uclpcserver/uclpc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 7400763..458d9ad 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -28,6 +28,7 @@ func NewUCLPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback uc.validEntityTypes = []model.EntityTypeType{ model.EntityTypeTypeGridGuard, + model.EntityTypeTypeCEM, // KEO uses this entity type for an SMGW whysoever } _ = spine.Events.Subscribe(uc) From 49bfd009612102d2671bc2a4c56cdb2128dc2205 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 12:58:06 +0200 Subject: [PATCH 172/227] Add heartbeat subscription to UCLPCServer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subscribe to a remote entities DeviceDiagnosis Server for heartbeats. Also considers KEO’s implementation using multiple entities for the same functionality and requiring to subscribe to the one that binds to the local loadcontrol server feature --- uclpcserver/events.go | 80 ++++++++++++++++ uclpcserver/events_test.go | 162 +++++++++++++++++++++++++++++++++ uclpcserver/testhelper_test.go | 2 + uclpcserver/uclpc.go | 2 + 4 files changed, 246 insertions(+) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 98817ff..e520577 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -1,7 +1,10 @@ package uclpcserver import ( + "slices" + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" ) @@ -14,6 +17,22 @@ func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + if util.IsDeviceConnected(payload) { + e.deviceConnected(payload) + return + } + + // did we receive a binding to the loadControl server and the + // heartbeatWorkaround is required? + if payload.EventType == spineapi.EventTypeBindingChange && + payload.ChangeType == spineapi.ElementChangeAdd && + payload.LocalFeature != nil && + payload.LocalFeature.Type() == model.FeatureTypeTypeDeviceDiagnosis && + payload.LocalFeature.Role() == model.RoleTypeServer { + e.subscribeHeartbeatWorkaround(payload) + return + } + if localEntity == nil || payload.EventType != spineapi.EventTypeDataChange || payload.ChangeType != spineapi.ElementChangeUpdate || @@ -46,6 +65,67 @@ func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) { } } +// a remote device was connected and we know its entities +func (e *UCLPCServer) deviceConnected(payload spineapi.EventPayload) { + if payload.Device == nil { + return + } + + // check if there is a DeviceDiagnosis server on one or more entities + remoteDevice := payload.Device + + var deviceDiagEntites []spineapi.EntityRemoteInterface + + entites := remoteDevice.Entities() + for _, entity := range entites { + if !slices.Contains(e.validEntityTypes, entity.EntityType()) { + continue + } + + deviceDiagF := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if deviceDiagF == nil { + continue + } + + deviceDiagEntites = append(deviceDiagEntites, entity) + } + + // the remote device does not have a DeviceDiagnosis Server, which it should + if len(deviceDiagEntites) == 0 { + return + } + + // we only found one matching entity, as it should be, subscribe + if len(deviceDiagEntites) == 1 { + if localDeviceDiag, err := util.DeviceDiagnosis(e.service, deviceDiagEntites[0]); err == nil { + if _, err := localDeviceDiag.Subscribe(); err != nil { + logging.Log().Debug(err) + } + } + + return + } + + // we only found more one matching entity, this is not good + // according to KEO the subscription should be done on the entity that requests a binding to + // the local loadControlLimit server feature + e.heartbeatWorkaround = true +} + +// subscribe to the DeviceDiagnosis Server of the entity that created a binding +func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload) { + // the workaround is not needed, exit + if !e.heartbeatWorkaround { + return + } + + if localDeviceDiag, err := util.DeviceDiagnosis(e.service, payload.Entity); err == nil { + if _, err := localDeviceDiag.Subscribe(); err != nil { + logging.Log().Debug(err) + } + } +} + // the load control limit data was updated func (e *UCLPCServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { if _, err := e.LoadControlLimit(); err != nil { diff --git a/uclpcserver/events_test.go b/uclpcserver/events_test.go index 280f2d8..2c55fe1 100644 --- a/uclpcserver/events_test.go +++ b/uclpcserver/events_test.go @@ -1,8 +1,11 @@ package uclpcserver import ( + "fmt" + eebusutil "github.com/enbility/eebus-go/util" spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" "github.com/enbility/spine-go/model" ) @@ -12,9 +15,14 @@ func (s *UCLPCServerSuite) Test_Events() { } s.sut.HandleEvent(payload) + payload.Device = s.monitoredEntity.Device() payload.Entity = s.monitoredEntity s.sut.HandleEvent(payload) + payload.EventType = spineapi.EventTypeDeviceChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + payload.EventType = spineapi.EventTypeEntityChange payload.ChangeType = spineapi.ElementChangeAdd s.sut.HandleEvent(payload) @@ -39,4 +47,158 @@ func (s *UCLPCServerSuite) Test_Events() { payload.LocalFeature = s.deviceConfigurationFeature s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeBindingChange + payload.ChangeType = spineapi.ElementChangeAdd + payload.LocalFeature = s.deviceDiagnosisFeature + s.sut.HandleEvent(payload) +} + +func (s *UCLPCServerSuite) Test_deviceConnected() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + + s.sut.deviceConnected(payload) + + // no entities + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().Entities().Return(nil) + payload.Device = mockRemoteDevice + s.sut.deviceConnected(payload) + + // one entity with one DeviceDiagnosis server + payload.Device = s.remoteDevice + s.sut.deviceConnected(payload) + + s.sut.subscribeHeartbeatWorkaround(payload) +} + +func (s *UCLPCServerSuite) Test_multipleDeviceDiagServer() { + // multiple entities each with DeviceDiagnosis server + + payload := spineapi.EventPayload{ + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisHeartbeatData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeClient, + []model.FunctionType{}, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + // 4 entites + for i := 1; i < 5; i++ { + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{model.AddressEntityType(i)}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{2}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{3}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{4}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + }, + FeatureInformation: featureInformations, + } + + _, err := s.remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + s.remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + s.sut.deviceConnected(payload) + + s.sut.subscribeHeartbeatWorkaround(payload) } diff --git a/uclpcserver/testhelper_test.go b/uclpcserver/testhelper_test.go index cc08aaa..2814c66 100644 --- a/uclpcserver/testhelper_test.go +++ b/uclpcserver/testhelper_test.go @@ -35,6 +35,7 @@ type UCLPCServerSuite struct { mockRemoteEntity *mocks.EntityRemoteInterface monitoredEntity spineapi.EntityRemoteInterface loadControlFeature, + deviceDiagnosisFeature, deviceConfigurationFeature spineapi.FeatureLocalInterface } @@ -74,6 +75,7 @@ func (s *UCLPCServerSuite) BeforeTest(suiteName, testName string) { localEntity := s.sut.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.loadControlFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + s.deviceDiagnosisFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) s.deviceConfigurationFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 458d9ad..b37749b 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -16,6 +16,8 @@ type UCLPCServer struct { eventCB api.EntityEventCallback validEntityTypes []model.EntityTypeType + + heartbeatWorkaround bool } var _ UCLCPServerInterface = (*UCLPCServer)(nil) From 0f7f85e016ecec72fa2f56a1daffa94d01eda21b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 13:00:15 +0200 Subject: [PATCH 173/227] Subscribe to remote DeviceDiag server in UCLPC --- uclpc/events.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/uclpc/events.go b/uclpc/events.go index 398d45c..65c9232 100644 --- a/uclpc/events.go +++ b/uclpc/events.go @@ -35,7 +35,7 @@ func (e *UCLPC) HandleEvent(payload spineapi.EventPayload) { } } -// the remote device was connected +// the remote entity was connected func (e *UCLPC) connected(entity spineapi.EntityRemoteInterface) { // initialise features, e.g. subscriptions, descriptions if loadControl, err := util.LoadControl(e.service, entity); err == nil { @@ -48,6 +48,12 @@ func (e *UCLPC) connected(entity spineapi.EntityRemoteInterface) { logging.Log().Debug(err) } } + + if localDeviceDiag, err := util.DeviceDiagnosis(e.service, entity); err == nil { + if _, err := localDeviceDiag.Subscribe(); err != nil { + logging.Log().Debug(err) + } + } } // the load control limit description data was updated From 37ee84d228be22b4ac791a0bb422bdbcea03bf43 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 14:11:09 +0200 Subject: [PATCH 174/227] Persist generated cert in demo cmd --- cmd/main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmd/main.go b/cmd/main.go index 332fb96..6821be7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,7 +1,10 @@ package main import ( + "crypto/ecdsa" "crypto/tls" + "crypto/x509" + "encoding/pem" "flag" "fmt" "log" @@ -37,6 +40,20 @@ func main() { if err != nil { log.Fatal(err) } + + // persist certificate into default files + pemdata := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certificate.Certificate[0], + }) + os.WriteFile("cert.crt", pemdata, 0666) + + b, err := x509.MarshalECPrivateKey(certificate.PrivateKey.(*ecdsa.PrivateKey)) + if err != nil { + log.Fatal(err) + } + pemdata = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + os.WriteFile("cert.key", pemdata, 0666) } else { fmt.Println("Using certificate file", *crt, "and key file", *key) } From 5f68512873eec0941e9d363e31853e67d2a6c3c9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 14:11:26 +0200 Subject: [PATCH 175/227] Add LPC Server to demo cmd --- cmd/democem/democem.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index c9e7b4d..2c72e66 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -3,6 +3,7 @@ package democem import ( "github.com/enbility/cemd/cem" "github.com/enbility/cemd/ucevsecc" + "github.com/enbility/cemd/uclpcserver" eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/ship-go/logging" ) @@ -25,6 +26,9 @@ func (d *DemoCem) Setup() error { return err } + lpcs := uclpcserver.NewUCLPC(d.cem.Service, d.entityEventCB) + d.cem.AddUseCase(lpcs) + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(evsecc) From 9ac52685c5c721a052f88974a6abfb6f88ac197d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 14:24:49 +0200 Subject: [PATCH 176/227] Fix write --- cmd/main.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 6821be7..224e558 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -46,14 +46,20 @@ func main() { Type: "CERTIFICATE", Bytes: certificate.Certificate[0], }) - os.WriteFile("cert.crt", pemdata, 0666) + err := os.WriteFile("cert.crt", pemdata, 0600) + if err != nil { + log.Fatal(err) + } b, err := x509.MarshalECPrivateKey(certificate.PrivateKey.(*ecdsa.PrivateKey)) if err != nil { log.Fatal(err) } pemdata = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) - os.WriteFile("cert.key", pemdata, 0666) + err = os.WriteFile("cert.key", pemdata, 0600) + if err != nil { + log.Fatal(err) + } } else { fmt.Println("Using certificate file", *crt, "and key file", *key) } From 5ba82fad35d6f2c3d1153167b90765158e8e8abe Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 16:14:52 +0200 Subject: [PATCH 177/227] Fix KEO workaround for UCLPCServer --- uclpcserver/events.go | 8 ++++---- uclpcserver/events_test.go | 2 +- uclpcserver/uclpc.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index e520577..c426e90 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -27,7 +27,7 @@ func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) { if payload.EventType == spineapi.EventTypeBindingChange && payload.ChangeType == spineapi.ElementChangeAdd && payload.LocalFeature != nil && - payload.LocalFeature.Type() == model.FeatureTypeTypeDeviceDiagnosis && + payload.LocalFeature.Type() == model.FeatureTypeTypeLoadControl && payload.LocalFeature.Role() == model.RoleTypeServer { e.subscribeHeartbeatWorkaround(payload) return @@ -106,16 +106,16 @@ func (e *UCLPCServer) deviceConnected(payload spineapi.EventPayload) { return } - // we only found more one matching entity, this is not good + // we found more than one matching entity, this is not good // according to KEO the subscription should be done on the entity that requests a binding to // the local loadControlLimit server feature - e.heartbeatWorkaround = true + e.heartbeatKeoWorkaround = true } // subscribe to the DeviceDiagnosis Server of the entity that created a binding func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload) { // the workaround is not needed, exit - if !e.heartbeatWorkaround { + if !e.heartbeatKeoWorkaround { return } diff --git a/uclpcserver/events_test.go b/uclpcserver/events_test.go index 2c55fe1..77b0763 100644 --- a/uclpcserver/events_test.go +++ b/uclpcserver/events_test.go @@ -50,7 +50,7 @@ func (s *UCLPCServerSuite) Test_Events() { payload.EventType = spineapi.EventTypeBindingChange payload.ChangeType = spineapi.ElementChangeAdd - payload.LocalFeature = s.deviceDiagnosisFeature + payload.LocalFeature = s.loadControlFeature s.sut.HandleEvent(payload) } diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index b37749b..0f9aa92 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -17,7 +17,7 @@ type UCLPCServer struct { validEntityTypes []model.EntityTypeType - heartbeatWorkaround bool + heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use } var _ UCLCPServerInterface = (*UCLPCServer)(nil) From 820273266311567bc88502469b68d6a8773c5057 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 16:57:20 +0200 Subject: [PATCH 178/227] Fix device change detection --- uclpcserver/events.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index c426e90..347d663 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -11,17 +11,17 @@ import ( // handle SPINE events func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) { - if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { + if util.IsDeviceConnected(payload) { + e.deviceConnected(payload) return } - localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - - if util.IsDeviceConnected(payload) { - e.deviceConnected(payload) + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { return } + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + // did we receive a binding to the loadControl server and the // heartbeatWorkaround is required? if payload.EventType == spineapi.EventTypeBindingChange && From 18a17ad9d83ea9bec4c8d2df81d81ae297236da8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 17:16:20 +0200 Subject: [PATCH 179/227] Update SPINE --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 86b6171..e7be832 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 - github.com/enbility/spine-go v0.0.0-20240409101612-0cdc3e9a9cfc + github.com/enbility/spine-go v0.0.0-20240409151434-b2462e8e0e84 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index cec78d4..5ba912e 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 h1:Tbt5F8hroDxTO github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400/go.mod h1:H0XQ8jb91zqsdWNTOKPOHHs+EAyKzLrNiGyPwjol028= github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 h1:k89K2PYYVgAYbRZ3Kj+1AZ9Pgme7aOESXEhXSZ3qEHs= github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240409101612-0cdc3e9a9cfc h1:67EyKovCzjjf8FYlcwxb6KUR3AuXHwdJ6odtEC+mxlw= -github.com/enbility/spine-go v0.0.0-20240409101612-0cdc3e9a9cfc/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240409151434-b2462e8e0e84 h1:8AZZvsXBaA6i7P4pqzA/3lih+CzKfrY32a+cQYZKsdQ= +github.com/enbility/spine-go v0.0.0-20240409151434-b2462e8e0e84/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 21ebed4a050f648205ea74069749bb9d21edbead Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 17:30:10 +0200 Subject: [PATCH 180/227] Also request a heartbeat after subscription --- uclpcserver/events.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 347d663..8705510 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -101,6 +101,10 @@ func (e *UCLPCServer) deviceConnected(payload spineapi.EventPayload) { if _, err := localDeviceDiag.Subscribe(); err != nil { logging.Log().Debug(err) } + + if _, err := localDeviceDiag.RequestHeartbeat(); err != nil { + logging.Log().Debug(err) + } } return @@ -123,6 +127,10 @@ func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload if _, err := localDeviceDiag.Subscribe(); err != nil { logging.Log().Debug(err) } + + if _, err := localDeviceDiag.RequestHeartbeat(); err != nil { + logging.Log().Debug(err) + } } } From c06f0d62fcb908a98691cb6fbe09f3aea795d6df Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 17:40:43 +0200 Subject: [PATCH 181/227] Set initial UCLPCServer values --- uclpcserver/uclpc.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 0f9aa92..c8fe499 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -78,6 +78,17 @@ func (e *UCLPCServer) AddFeatures() { } f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, loadControlDesc) + loadControl := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), + IsLimitChangeable: eebusutil.Ptr(true), + IsLimitActive: eebusutil.Ptr(false), + }, + }, + } + f.SetData(model.FunctionTypeLoadControlLimitListData, loadControl) + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) @@ -110,6 +121,20 @@ func (e *UCLPCServer) AddFeatures() { } f.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, deviceConfigDesc) + deviceConfig := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + IsValueChangeable: eebusutil.Ptr(true), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + IsValueChangeable: eebusutil.Ptr(true), + }, + }, + } + f.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, deviceConfig) + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) From f6469c26a7c8698f5272ea777627414411933f7e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 18:29:57 +0200 Subject: [PATCH 182/227] Update SHIP, SPINE, eebus --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index e7be832..1b638e4 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 - github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 - github.com/enbility/spine-go v0.0.0-20240409151434-b2462e8e0e84 + github.com/enbility/eebus-go v0.0.0-20240409162436-d7be76ca222b + github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 + github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 5ba912e..2b88513 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400 h1:Tbt5F8hroDxTOXqwJtzVXdpFIg8vby21Vzchm2ftvIk= -github.com/enbility/eebus-go v0.0.0-20240408170036-0382e8911400/go.mod h1:H0XQ8jb91zqsdWNTOKPOHHs+EAyKzLrNiGyPwjol028= -github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1 h1:k89K2PYYVgAYbRZ3Kj+1AZ9Pgme7aOESXEhXSZ3qEHs= -github.com/enbility/ship-go v0.0.0-20240408165750-94ea245772a1/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240409151434-b2462e8e0e84 h1:8AZZvsXBaA6i7P4pqzA/3lih+CzKfrY32a+cQYZKsdQ= -github.com/enbility/spine-go v0.0.0-20240409151434-b2462e8e0e84/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/eebus-go v0.0.0-20240409162436-d7be76ca222b h1:uzMUOeW3QiP4L0aevYzujpmqDvoiTR8du30WVTofe14= +github.com/enbility/eebus-go v0.0.0-20240409162436-d7be76ca222b/go.mod h1:SIr4W8XTlS+zIjZreeksebaao40MEkl3r0/alurqcB4= +github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 h1:A4odLKYuqCfiv08Ths5gVkiDjgnHxhbVUhThizMucPA= +github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c h1:Hk48Nz21cfd2wXe1DHK5lB1mmW2PNs/WYzseTno7rTI= +github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 909839c3757fbff5c79f0026598bae97dd29d132 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 9 Apr 2024 18:30:10 +0200 Subject: [PATCH 183/227] Minor linter and typo fixes --- .golangci.yml | 58 +++++++++++++++++++++++++++++++++++++ cem/events.go | 1 - uccevc/events.go | 1 - uccevc/events_test.go | 1 - uccevc/public_scen1.go | 1 - uccevc/public_scen3_test.go | 1 - uccevc/public_scen4.go | 1 - uccevc/public_scen4_test.go | 1 - ucevcc/events.go | 1 - ucevcc/public.go | 3 +- ucevcem/events.go | 1 - ucevsecc/ucevsecc.go | 1 - ucmgcp/events.go | 1 - ucmgcp/public_test.go | 1 - ucmpc/events.go | 1 - ucmpc/public_test.go | 1 - ucopev/events.go | 1 - ucvabd/ucvabd_test.go | 1 - ucvapd/ucvapd_test.go | 1 - util/loadcontrol.go | 1 - util/loadcontrol_test.go | 2 +- util/measurement.go | 2 -- util/measurement_test.go | 1 - 23 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..dd9bcca --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,58 @@ +run: + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 5m + + # include test files or not, default is true + tests: true + + # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + modules-download-mode: readonly + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + formats: + - format: colored-line-number + +linters: + enable: + - bodyclose + - errcheck + - errorlint + - gocheckcompilerdirectives + - gochecknoinits + - gochecksumtype + - goconst + - gofmt + - gosimple + - gosec + - govet + - nilerr + - nilnil + - staticcheck + - typecheck + - unused + - whitespace + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - errcheck + - goconst + - gosec + + # checking for errors in defers seldom makes sense... + - source: "^\\s*defer\\s" + linters: + - errcheck + - staticcheck diff --git a/cem/events.go b/cem/events.go index 6b1a1f7..a2f1ebc 100644 --- a/cem/events.go +++ b/cem/events.go @@ -7,7 +7,6 @@ import ( // handle SPINE events func (h *Cem) HandleEvent(payload spineapi.EventPayload) { - if util.IsDeviceConnected(payload) { h.eventCB(payload.Ski, payload.Device, DeviceConnected) return diff --git a/uccevc/events.go b/uccevc/events.go index 292066e..1e36d6a 100644 --- a/uccevc/events.go +++ b/uccevc/events.go @@ -156,7 +156,6 @@ func (e *UCCEVC) evIncentiveTableDescriptionDataUpdate(payload spineapi.EventPay if e.evCheckIncentiveTableDescriptionUpdateRequired(payload.Entity) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataRequestedIncentiveTableDescription) } - } // the incentive table constraint data of an EV was updated diff --git a/uccevc/events_test.go b/uccevc/events_test.go index c4a2aa7..7e858fe 100644 --- a/uccevc/events_test.go +++ b/uccevc/events_test.go @@ -158,5 +158,4 @@ func (s *UCCEVCSuite) Test_evTimeSeriesDescriptionDataUpdate() { assert.Nil(s.T(), fErr) s.sut.evTimeSeriesDescriptionDataUpdate(payload) - } diff --git a/uccevc/public_scen1.go b/uccevc/public_scen1.go index 88b319c..fa7f23b 100644 --- a/uccevc/public_scen1.go +++ b/uccevc/public_scen1.go @@ -63,7 +63,6 @@ func (e *UCCEVC) ChargeStrategy(entity spineapi.EntityRemoteInterface) api.EVCha return api.EVChargeStrategyTypeNoDemand } - } return api.EVChargeStrategyTypeUnknown diff --git a/uccevc/public_scen3_test.go b/uccevc/public_scen3_test.go index 2c086a9..85dc458 100644 --- a/uccevc/public_scen3_test.go +++ b/uccevc/public_scen3_test.go @@ -203,7 +203,6 @@ func (s *UCCEVCSuite) Test_WriteIncentives() { for _, tc := range tests { s.T().Run(tc.name, func(t *testing.T) { for _, data := range tc.data { - constData = &model.IncentiveTableConstraintsDataType{ IncentiveTableConstraints: []model.IncentiveTableConstraintsType{ { diff --git a/uccevc/public_scen4.go b/uccevc/public_scen4.go index 9b69968..9a99a35 100644 --- a/uccevc/public_scen4.go +++ b/uccevc/public_scen4.go @@ -92,7 +92,6 @@ func (e *UCCEVC) ChargePlan(entity spineapi.EntityRemoteInterface) (api.ChargePl currentStart := time.Now() currentEnd := currentStart if data.TimePeriod != nil && data.TimePeriod.StartTime != nil { - if start, err := data.TimePeriod.StartTime.GetTimeDuration(); err == nil { currentStart = currentStart.Add(start) startAvailable = true diff --git a/uccevc/public_scen4_test.go b/uccevc/public_scen4_test.go index 1e67f9f..9265e4f 100644 --- a/uccevc/public_scen4_test.go +++ b/uccevc/public_scen4_test.go @@ -78,7 +78,6 @@ func (s *UCCEVCSuite) Test_ChargePlanConstaints() { _, err = s.sut.ChargePlanConstraints(s.evEntity) assert.Nil(s.T(), err) - } func (s *UCCEVCSuite) Test_ChargePlan() { diff --git a/ucevcc/events.go b/ucevcc/events.go index 7d5c161..caccd42 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -187,7 +187,6 @@ func (e *UCEVCC) evManufacturerDataUpdate(payload spineapi.EventPayload) { if _, err := evDeviceClassification.GetManufacturerDetails(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateManufacturerData) } - } // the electrical connection parameter description data of an EV was updated diff --git a/ucevcc/public.go b/ucevcc/public.go index 3acd71e..b653dec 100644 --- a/ucevcc/public.go +++ b/ucevcc/public.go @@ -16,7 +16,7 @@ func (e *UCEVCC) ChargeState(entity spineapi.EntityRemoteInterface) (api.EVCharg evDeviceDiagnosis, err := util.DeviceDiagnosis(e.service, entity) if err != nil { - return api.EVChargeStateTypeUnplugged, nil + return api.EVChargeStateTypeUnplugged, err } diagnosisState, err := evDeviceDiagnosis.GetState() @@ -201,7 +201,6 @@ func (e *UCEVCC) ManufacturerData( api.ManufacturerData, error, ) { - return util.ManufacturerData(e.service, entity, e.validEntityTypes) } diff --git a/ucevcem/events.go b/ucevcem/events.go index 05d7939..a76bb84 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -52,7 +52,6 @@ func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { if _, err := evElectricalConnection.RequestParameterDescriptions(); err != nil { logging.Log().Debug(err) } - } if evMeasurement, err := util.Measurement(e.service, entity); err == nil { diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index f69b720..8b7130c 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -98,7 +98,6 @@ func (e *UCEVSECC) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bo ) { return false, nil } - } return true, nil diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 9315a69..3d9419f 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -139,5 +139,4 @@ func (e *UCMGCP) gridMeasurementDataUpdate(payload spineapi.EventPayload) { if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACFrequency); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFrequency) } - } diff --git a/ucmgcp/public_test.go b/ucmgcp/public_test.go index c4e2689..7523fbc 100644 --- a/ucmgcp/public_test.go +++ b/ucmgcp/public_test.go @@ -322,7 +322,6 @@ func (s *UCMGCPSuite) Test_CurrentPerPhase() { data, err = s.sut.CurrentPerPhase(s.smgwEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) - } func (s *UCMGCPSuite) Test_VoltagePerPhase() { diff --git a/ucmpc/events.go b/ucmpc/events.go index 128852e..7ee1a30 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -110,5 +110,4 @@ func (e *UCMPC) deviceMeasurementDataUpdate(payload spineapi.EventPayload) { if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACFrequency); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFrequency) } - } diff --git a/ucmpc/public_test.go b/ucmpc/public_test.go index 5b2c9a2..fc81c93 100644 --- a/ucmpc/public_test.go +++ b/ucmpc/public_test.go @@ -382,7 +382,6 @@ func (s *UCMPCSuite) Test_CurrentPerPhase() { data, err = s.sut.CurrentPerPhase(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) - } func (s *UCMPCSuite) Test_VoltagePerPhase() { diff --git a/ucopev/events.go b/ucopev/events.go index c328c6c..737b936 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -94,7 +94,6 @@ func (e *UCOPEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) return } - } // the electrical connection permitted value sets data of an EV was updated diff --git a/ucvabd/ucvabd_test.go b/ucvabd/ucvabd_test.go index 678fc42..e73e3c5 100644 --- a/ucvabd/ucvabd_test.go +++ b/ucvabd/ucvabd_test.go @@ -94,5 +94,4 @@ func (s *UCVABDSuite) Test_IsUseCaseSupported() { data, err = s.sut.IsUseCaseSupported(s.batteryEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), true, data) - } diff --git a/ucvapd/ucvapd_test.go b/ucvapd/ucvapd_test.go index a9e0c84..ed8e709 100644 --- a/ucvapd/ucvapd_test.go +++ b/ucvapd/ucvapd_test.go @@ -111,5 +111,4 @@ func (s *UCVAPDSuite) Test_IsUseCaseSupported() { data, err = s.sut.IsUseCaseSupported(s.pvEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), true, data) - } diff --git a/util/loadcontrol.go b/util/loadcontrol.go index 8a850de..fce2ef7 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -19,7 +19,6 @@ func LoadControlLimits( entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType, category model.LoadControlCategoryType) (limits []api.LoadLimitsPhase, resultErr error) { - limits = nil resultErr = api.ErrNoCompatibleEntity if entity == nil || !IsCompatibleEntity(entity, entityTypes) { diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index f79f74b..507a941 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -351,8 +351,8 @@ func (s *UtilSuite) Test_WriteLoadControlLimits() { IsActive: true, Value: limit, }) - } + msgCounter, err = WriteLoadControlLimits(s.service, s.monitoredEntity, entityTypes, category, phaseLimitValues) assert.Nil(t, err) assert.NotNil(t, msgCounter) diff --git a/util/measurement.go b/util/measurement.go index f87f82b..a54307a 100644 --- a/util/measurement.go +++ b/util/measurement.go @@ -43,7 +43,6 @@ func MeasurementValuesForTypeCommodityScope( energyDirection model.EnergyDirectionType, validPhaseNameTypes []model.ElectricalConnectionPhaseNameType, ) ([]float64, error) { - measurement := measurementType commodity := commodityType scope := scopeType @@ -99,7 +98,6 @@ func GetValuesForTypeCommodityScope( measurement model.MeasurementTypeType, commodity model.CommodityTypeType, scope model.ScopeTypeType) ([]model.MeasurementDataType, error) { - measurementFeature, err := Measurement(service, entity) if err != nil || measurementFeature == nil { return nil, err diff --git a/util/measurement_test.go b/util/measurement_test.go index 98df790..336d79b 100644 --- a/util/measurement_test.go +++ b/util/measurement_test.go @@ -197,5 +197,4 @@ func (s *UtilSuite) Test_MeasurementValuesForTypeCommodityScope() { ) assert.Nil(s.T(), err) assert.Equal(s.T(), []float64{10, 10, 10}, data) - } From 3de7e828f9d2484b934ceba7c8cb8ba24a6d0f69 Mon Sep 17 00:00:00 2001 From: Andreas Rehn Date: Tue, 9 Apr 2024 22:01:55 +0200 Subject: [PATCH 184/227] uclpcserver: align electricalConnectionCharacteristicListData operations in Limitation of Power Consumption v1.0.0 section 3.2.2.2.1 the ListData is declared only readable Signed-off-by: Andreas Rehn --- uclpcserver/uclpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index c8fe499..c6723c1 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -139,7 +139,7 @@ func (e *UCLPCServer) AddFeatures() { f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) - f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, false) var elCharId model.ElectricalConnectionCharacteristicIdType = 0 // get the heighest CharacteristicId From 405481f267429322dac15bac0895d85cb6ed38be Mon Sep 17 00:00:00 2001 From: Andreas Rehn Date: Tue, 9 Apr 2024 22:02:49 +0200 Subject: [PATCH 185/227] chore: fix typos Signed-off-by: Andreas Rehn --- uclpcserver/uclpc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index c6723c1..08c7de4 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -94,7 +94,7 @@ func (e *UCLPCServer) AddFeatures() { f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) var configId model.DeviceConfigurationKeyIdType = 0 - // get the heighest keyId + // get the highest keyId if desc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData); err == nil && desc.DeviceConfigurationKeyValueDescriptionData != nil { for _, desc := range desc.DeviceConfigurationKeyValueDescriptionData { @@ -142,7 +142,7 @@ func (e *UCLPCServer) AddFeatures() { f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, false) var elCharId model.ElectricalConnectionCharacteristicIdType = 0 - // get the heighest CharacteristicId + // get the highest CharacteristicId if desc, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicData != nil { for _, desc := range desc.ElectricalConnectionCharacteristicData { From 9a08ab14142aa07919578d1563ff676e10e1c74b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 10 Apr 2024 16:16:05 +0200 Subject: [PATCH 186/227] Fix actor for UCLPC and UCLPCServer --- uclpc/uclpc.go | 4 ++-- uclpcserver/uclpc.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uclpc/uclpc.go b/uclpc/uclpc.go index 59811ae..30da99a 100644 --- a/uclpc/uclpc.go +++ b/uclpc/uclpc.go @@ -66,7 +66,7 @@ func (e *UCLPC) AddUseCase() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, + model.UseCaseActorTypeEnergyGuard, e.UseCaseName(), model.SpecificationVersionType("1.0.0"), "release", @@ -77,7 +77,7 @@ func (e *UCLPC) AddUseCase() { func (e *UCLPC) UpdateUseCaseAvailability(available bool) { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeEnergyGuard, e.UseCaseName(), available) } // returns if the entity supports the usecase diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 08c7de4..94e811f 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -173,7 +173,7 @@ func (e *UCLPCServer) AddUseCase() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) localEntity.AddUseCaseSupport( - model.UseCaseActorTypeCEM, + model.UseCaseActorTypeControllableSystem, e.UseCaseName(), model.SpecificationVersionType("1.0.0"), "release", @@ -184,7 +184,7 @@ func (e *UCLPCServer) AddUseCase() { func (e *UCLPCServer) UpdateUseCaseAvailability(available bool) { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeControllableSystem, e.UseCaseName(), available) } // returns if the entity supports the usecase From d956256400ab6b73188d5b6d867e295f86da22ef Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 10 Apr 2024 16:22:02 +0200 Subject: [PATCH 187/227] Fix actor for UCMGCP, UCMCP --- ucmgcp/ucmgcp.go | 2 +- ucmpc/ucmcp.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ucmgcp/ucmgcp.go b/ucmgcp/ucmgcp.go index 4569b41..a4fe9be 100644 --- a/ucmgcp/ucmgcp.go +++ b/ucmgcp/ucmgcp.go @@ -67,7 +67,7 @@ func (e *UCMGCP) AddUseCase() { func (e *UCMGCP) UpdateUseCaseAvailability(available bool) { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeMonitoringAppliance, e.UseCaseName(), available) } // returns if the entity supports the usecase diff --git a/ucmpc/ucmcp.go b/ucmpc/ucmcp.go index 24ae903..e32e3ca 100644 --- a/ucmpc/ucmcp.go +++ b/ucmpc/ucmcp.go @@ -72,7 +72,7 @@ func (e *UCMPC) AddUseCase() { func (e *UCMPC) UpdateUseCaseAvailability(available bool) { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) - localEntity.SetUseCaseAvailability(model.UseCaseActorTypeCEM, e.UseCaseName(), available) + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeMonitoringAppliance, e.UseCaseName(), available) } // returns if the entity supports the usecase From c92c5da9783e692e68ac2f8d53d05cffbc1f04fb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 10 Apr 2024 16:26:20 +0200 Subject: [PATCH 188/227] Update UC subRevisions --- ucevcc/ucevcc.go | 2 +- ucevcem/ucevcem.go | 2 +- ucevsecc/ucevsecc.go | 2 +- ucopev/ucopev.go | 2 +- ucoscev/ucoscev.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ucevcc/ucevcc.go b/ucevcc/ucevcc.go index 1980195..c99a887 100644 --- a/ucevcc/ucevcc.go +++ b/ucevcc/ucevcc.go @@ -62,7 +62,7 @@ func (e *UCEVCC) AddUseCase() { model.UseCaseActorTypeCEM, e.UseCaseName(), model.SpecificationVersionType("1.0.1"), - "", + "release", true, []model.UseCaseScenarioSupportType{1, 2, 3, 4, 5, 6, 7, 8}) } diff --git a/ucevcem/ucevcem.go b/ucevcem/ucevcem.go index a479476..935372a 100644 --- a/ucevcem/ucevcem.go +++ b/ucevcem/ucevcem.go @@ -58,7 +58,7 @@ func (e *UCEVCEM) AddUseCase() { model.UseCaseActorTypeCEM, e.UseCaseName(), model.SpecificationVersionType("1.0.1"), - "", + "release", true, []model.UseCaseScenarioSupportType{1, 2, 3}) } diff --git a/ucevsecc/ucevsecc.go b/ucevsecc/ucevsecc.go index 8b7130c..ddf6c0f 100644 --- a/ucevsecc/ucevsecc.go +++ b/ucevsecc/ucevsecc.go @@ -59,7 +59,7 @@ func (e *UCEVSECC) AddUseCase() { model.UseCaseActorTypeCEM, e.UseCaseName(), model.SpecificationVersionType("1.0.1"), - "", + "release", true, []model.UseCaseScenarioSupportType{1, 2}) } diff --git a/ucopev/ucopev.go b/ucopev/ucopev.go index 80fa6c2..e9c89ce 100644 --- a/ucopev/ucopev.go +++ b/ucopev/ucopev.go @@ -63,7 +63,7 @@ func (e *UCOPEV) AddUseCase() { model.UseCaseActorTypeCEM, e.UseCaseName(), model.SpecificationVersionType("1.0.1"), - "", + "release", true, []model.UseCaseScenarioSupportType{1, 2, 3}) } diff --git a/ucoscev/ucoscev.go b/ucoscev/ucoscev.go index 0bb940d..1efb1c7 100644 --- a/ucoscev/ucoscev.go +++ b/ucoscev/ucoscev.go @@ -69,7 +69,7 @@ func (e *UCOSCEV) AddUseCase() { model.UseCaseActorTypeCEM, e.UseCaseName(), model.SpecificationVersionType("1.0.1"), - "", + "release", true, []model.UseCaseScenarioSupportType{1, 2, 3}) } From 27050317441ae5afac2ff31d28f3af92eb080740 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 10 Apr 2024 17:12:35 +0200 Subject: [PATCH 189/227] UCLPCServer fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Properly update DeviceConfiguration values and don’t overwrite everything with one value - Remove initial LoadControl limit setting, this should be done by the application using the public methods --- uclpcserver/public_test.go | 9 ++++++--- uclpcserver/uclpc.go | 11 ----------- util/deviceconfiguration.go | 21 ++++++++++++++------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index 36ed3a8..87aca1b 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -26,7 +26,7 @@ func (s *UCLPCServerSuite) Test_LoadControlLimit() { assert.Nil(s.T(), err) } -func (s *UCLPCServerSuite) Test_FailsafeConsumptionActivePowerLimit() { +func (s *UCLPCServerSuite) Test_Failsafe() { limit, changeable, err := s.sut.FailsafeConsumptionActivePowerLimit() assert.Equal(s.T(), 0.0, limit) assert.Equal(s.T(), false, changeable) @@ -39,9 +39,7 @@ func (s *UCLPCServerSuite) Test_FailsafeConsumptionActivePowerLimit() { assert.Equal(s.T(), 10.0, limit) assert.Equal(s.T(), true, changeable) assert.Nil(s.T(), err) -} -func (s *UCLPCServerSuite) Test_FailsafeDurationMinimum() { // The actual tests of the functionality is located in the util package duration, changeable, err := s.sut.FailsafeDurationMinimum() assert.Equal(s.T(), time.Duration(0), duration) @@ -54,6 +52,11 @@ func (s *UCLPCServerSuite) Test_FailsafeDurationMinimum() { err = s.sut.SetFailsafeDurationMinimum(time.Duration(time.Hour*2), true) assert.Nil(s.T(), err) + limit, changeable, err = s.sut.FailsafeConsumptionActivePowerLimit() + assert.Equal(s.T(), 10.0, limit) + assert.Equal(s.T(), true, changeable) + assert.Nil(s.T(), err) + duration, changeable, err = s.sut.FailsafeDurationMinimum() assert.Equal(s.T(), time.Duration(time.Hour*2), duration) assert.Equal(s.T(), true, changeable) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 94e811f..764b014 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -78,17 +78,6 @@ func (e *UCLPCServer) AddFeatures() { } f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, loadControlDesc) - loadControl := &model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{ - { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), - IsLimitChangeable: eebusutil.Ptr(true), - IsLimitActive: eebusutil.Ptr(false), - }, - }, - } - f.SetData(model.FunctionTypeLoadControlLimitListData, loadControl) - f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) diff --git a/util/deviceconfiguration.go b/util/deviceconfiguration.go index f1ae01f..05979d0 100644 --- a/util/deviceconfiguration.go +++ b/util/deviceconfiguration.go @@ -90,16 +90,23 @@ func SetLocalDeviceConfigurationKeyValue( return } - keyData := model.DeviceConfigurationKeyValueDataType{ - KeyId: eebusutil.Ptr(*description.KeyId), - IsValueChangeable: eebusutil.Ptr(changeable), - Value: eebusutil.Ptr(value), + data, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueListDataType](deviceConfiguration, model.FunctionTypeDeviceConfigurationKeyValueListData) + if err != nil { + data = &model.DeviceConfigurationKeyValueListDataType{} } - keysData := &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{keyData}, + + for index, item := range data.DeviceConfigurationKeyValueData { + if item.KeyId == nil || *item.KeyId != *description.KeyId { + continue + } + + item.IsValueChangeable = eebusutil.Ptr(changeable) + item.Value = eebusutil.Ptr(value) + + data.DeviceConfigurationKeyValueData[index] = item } - deviceConfiguration.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, keysData) + deviceConfiguration.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, data) return nil } From 6e5928b79442a6f38840be5b66cf5cc2ed799470 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 10 Apr 2024 19:21:38 +0200 Subject: [PATCH 190/227] Add item if keyId was not found --- util/deviceconfiguration.go | 11 +++++++++++ util/deviceconfiguration_test.go | 3 +++ 2 files changed, 14 insertions(+) diff --git a/util/deviceconfiguration.go b/util/deviceconfiguration.go index 05979d0..6d4f9a1 100644 --- a/util/deviceconfiguration.go +++ b/util/deviceconfiguration.go @@ -95,6 +95,7 @@ func SetLocalDeviceConfigurationKeyValue( data = &model.DeviceConfigurationKeyValueListDataType{} } + found := false for index, item := range data.DeviceConfigurationKeyValueData { if item.KeyId == nil || *item.KeyId != *description.KeyId { continue @@ -104,6 +105,16 @@ func SetLocalDeviceConfigurationKeyValue( item.Value = eebusutil.Ptr(value) data.DeviceConfigurationKeyValueData[index] = item + found = true + } + + if !found { + item := model.DeviceConfigurationKeyValueDataType{ + KeyId: eebusutil.Ptr(*description.KeyId), + IsValueChangeable: eebusutil.Ptr(changeable), + Value: eebusutil.Ptr(value), + } + data.DeviceConfigurationKeyValueData = append(data.DeviceConfigurationKeyValueData, item) } deviceConfiguration.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, data) diff --git a/util/deviceconfiguration_test.go b/util/deviceconfiguration_test.go index 1604484..4625dd2 100644 --- a/util/deviceconfiguration_test.go +++ b/util/deviceconfiguration_test.go @@ -97,4 +97,7 @@ func (s *UtilSuite) Test_SetLocalDeviceConfigurationKeyValue() { assert.NotNil(s.T(), data.Value) assert.NotNil(s.T(), data.Value.ScaledNumber) assert.Equal(s.T(), 10.0, data.Value.ScaledNumber.GetValue()) + + err = SetLocalDeviceConfigurationKeyValue(s.service, keyName, true, value) + assert.Nil(s.T(), err) } From 0bdd6a12a5835c81d6534a49276e20b14032731e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Apr 2024 12:37:27 +0200 Subject: [PATCH 191/227] Update DemoCEM - Set default values for LPCServer - Properly register RemoteSKI - Add console logging --- cmd/democem/democem.go | 82 +++++++++++++++++++++++++++++++++++++++--- cmd/main.go | 8 +++-- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index 2c72e66..542dd63 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -1,6 +1,10 @@ package democem import ( + "fmt" + "time" + + "github.com/enbility/cemd/api" "github.com/enbility/cemd/cem" "github.com/enbility/cemd/ucevsecc" "github.com/enbility/cemd/uclpcserver" @@ -10,13 +14,16 @@ import ( type DemoCem struct { cem *cem.Cem + + remoteSki string } -func NewDemoCem(configuration *eebusapi.Configuration) *DemoCem { - demo := &DemoCem{} +func NewDemoCem(configuration *eebusapi.Configuration, remoteSki string) *DemoCem { + demo := &DemoCem{ + remoteSki: remoteSki, + } - noLogging := &logging.NoLogging{} - demo.cem = cem.NewCEM(configuration, demo, demo.deviceEventCB, noLogging) + demo.cem = cem.NewCEM(configuration, demo, demo.deviceEventCB, demo) return demo } @@ -29,10 +36,77 @@ func (d *DemoCem) Setup() error { lpcs := uclpcserver.NewUCLPC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(lpcs) + if err := lpcs.SetLoadControlLimit(api.LoadLimit{ + IsChangeable: true, + IsActive: false, + Value: 0, + }); err != nil { + logging.Log().Debug(err) + } + if err := lpcs.SetContractualConsumptionNominalMax(22000); err != nil { + logging.Log().Debug(err) + } + if err := lpcs.SetFailsafeConsumptionActivePowerLimit(4300, true); err != nil { + logging.Log().Debug(err) + } + if err := lpcs.SetFailsafeDurationMinimum(time.Hour*2, true); err != nil { + logging.Log().Debug(err) + } + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(evsecc) + d.cem.Service.RegisterRemoteSKI(d.remoteSki, true) + d.cem.Start() return nil } + +// Logging interface + +func (d *DemoCem) Trace(args ...interface{}) { + d.print("TRACE", args...) +} + +func (d *DemoCem) Tracef(format string, args ...interface{}) { + d.printFormat("TRACE", format, args...) +} + +func (d *DemoCem) Debug(args ...interface{}) { + d.print("DEBUG", args...) +} + +func (d *DemoCem) Debugf(format string, args ...interface{}) { + d.printFormat("DEBUG", format, args...) +} + +func (d *DemoCem) Info(args ...interface{}) { + d.print("INFO ", args...) +} + +func (d *DemoCem) Infof(format string, args ...interface{}) { + d.printFormat("INFO ", format, args...) +} + +func (d *DemoCem) Error(args ...interface{}) { + d.print("ERROR", args...) +} + +func (d *DemoCem) Errorf(format string, args ...interface{}) { + d.printFormat("ERROR", format, args...) +} + +func (d *DemoCem) currentTimestamp() string { + return time.Now().Format("2006-01-02 15:04:05") +} + +func (d *DemoCem) print(msgType string, args ...interface{}) { + value := fmt.Sprintln(args...) + fmt.Printf("%s %s %s", d.currentTimestamp(), msgType, value) +} + +func (d *DemoCem) printFormat(msgType, format string, args ...interface{}) { + value := fmt.Sprintf(format, args...) + fmt.Println(d.currentTimestamp(), msgType, value) +} diff --git a/cmd/main.go b/cmd/main.go index 224e558..c6d6550 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,6 +16,7 @@ import ( "github.com/enbility/cemd/cmd/democem" eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/ship-go/cert" + "github.com/enbility/ship-go/mdns" "github.com/enbility/spine-go/model" ) @@ -67,7 +68,7 @@ func main() { configuration, err := eebusapi.NewConfiguration( "Demo", "Demo", - "HEMS", + "Device", "123456789", model.DeviceTypeTypeEnergyManagementSystem, []model.EntityTypeType{model.EntityTypeTypeCEM}, @@ -80,13 +81,16 @@ func main() { return } + configuration.SetMdnsProviderSelection(mdns.MdnsProviderSelectionGoZeroConfOnly) + if iface != nil && *iface != "" { ifaces := []string{*iface} configuration.SetInterfaces(ifaces) } - demo := democem.NewDemoCem(configuration) + demo := democem.NewDemoCem(configuration, *remoteSki) + if err := demo.Setup(); err != nil { fmt.Println("Error setting up cem: ", err) return From fb482ef409ddbfcdc15763c15382e00f5a46233f Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 15 Apr 2024 17:27:46 +0200 Subject: [PATCH 192/227] Set fake measurementId for UCLPCServer This is required for the KEO Stack to actually send limits. But it does not make sense when the CEM does not have a ElectricalConnection server which it can only have when having a MPC server. --- uclpcserver/uclpc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 764b014..f1afc80 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -71,6 +71,7 @@ func (e *UCLPCServer) AddFeatures() { LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), // This is a fake Measurement ID, as there is no Electrical Connection server defined, it can't provide any meaningful. But KEO requires this to be set :( Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), }, From b2c3837176605dedd7981fa57347d33a4f23a63d Mon Sep 17 00:00:00 2001 From: Andreas Rehn Date: Mon, 15 Apr 2024 23:29:14 +0200 Subject: [PATCH 193/227] DemoCem: log errors as Errors --- cmd/democem/democem.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index 542dd63..dd5e3eb 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -41,16 +41,16 @@ func (d *DemoCem) Setup() error { IsActive: false, Value: 0, }); err != nil { - logging.Log().Debug(err) + logging.Log().Error(err) } if err := lpcs.SetContractualConsumptionNominalMax(22000); err != nil { - logging.Log().Debug(err) + logging.Log().Error(err) } if err := lpcs.SetFailsafeConsumptionActivePowerLimit(4300, true); err != nil { - logging.Log().Debug(err) + logging.Log().Error(err) } if err := lpcs.SetFailsafeDurationMinimum(time.Hour*2, true); err != nil { - logging.Log().Debug(err) + logging.Log().Error(err) } evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.entityEventCB) From 38f4c945141157820f1dfca978ef437b0bba0fac Mon Sep 17 00:00:00 2001 From: Andreas Rehn Date: Mon, 15 Apr 2024 23:30:32 +0200 Subject: [PATCH 194/227] chore: fix lpc typo --- .../{UCLCPInterface.go => UCLPCInterface.go} | 210 ++++++++-------- ...erInterface.go => UCLPCServerInterface.go} | 226 +++++++++--------- uclpc/api.go | 2 +- uclpc/uclpc.go | 2 +- uclpcserver/api.go | 2 +- uclpcserver/uclpc.go | 2 +- 6 files changed, 222 insertions(+), 222 deletions(-) rename mocks/{UCLCPInterface.go => UCLPCInterface.go} (60%) rename mocks/{UCLCPServerInterface.go => UCLPCServerInterface.go} (55%) diff --git a/mocks/UCLCPInterface.go b/mocks/UCLPCInterface.go similarity index 60% rename from mocks/UCLCPInterface.go rename to mocks/UCLPCInterface.go index f61b5db..d59148a 100644 --- a/mocks/UCLCPInterface.go +++ b/mocks/UCLPCInterface.go @@ -13,85 +13,85 @@ import ( time "time" ) -// UCLCPInterface is an autogenerated mock type for the UCLCPInterface type -type UCLCPInterface struct { +// UCLPCInterface is an autogenerated mock type for the UCLPCInterface type +type UCLPCInterface struct { mock.Mock } -type UCLCPInterface_Expecter struct { +type UCLPCInterface_Expecter struct { mock *mock.Mock } -func (_m *UCLCPInterface) EXPECT() *UCLCPInterface_Expecter { - return &UCLCPInterface_Expecter{mock: &_m.Mock} +func (_m *UCLPCInterface) EXPECT() *UCLPCInterface_Expecter { + return &UCLPCInterface_Expecter{mock: &_m.Mock} } // AddFeatures provides a mock function with given fields: -func (_m *UCLCPInterface) AddFeatures() { +func (_m *UCLPCInterface) AddFeatures() { _m.Called() } -// UCLCPInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' -type UCLCPInterface_AddFeatures_Call struct { +// UCLPCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCLPCInterface_AddFeatures_Call struct { *mock.Call } // AddFeatures is a helper method to define mock.On call -func (_e *UCLCPInterface_Expecter) AddFeatures() *UCLCPInterface_AddFeatures_Call { - return &UCLCPInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +func (_e *UCLPCInterface_Expecter) AddFeatures() *UCLPCInterface_AddFeatures_Call { + return &UCLPCInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} } -func (_c *UCLCPInterface_AddFeatures_Call) Run(run func()) *UCLCPInterface_AddFeatures_Call { +func (_c *UCLPCInterface_AddFeatures_Call) Run(run func()) *UCLPCInterface_AddFeatures_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPInterface_AddFeatures_Call) Return() *UCLCPInterface_AddFeatures_Call { +func (_c *UCLPCInterface_AddFeatures_Call) Return() *UCLPCInterface_AddFeatures_Call { _c.Call.Return() return _c } -func (_c *UCLCPInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLCPInterface_AddFeatures_Call { +func (_c *UCLPCInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLPCInterface_AddFeatures_Call { _c.Call.Return(run) return _c } // AddUseCase provides a mock function with given fields: -func (_m *UCLCPInterface) AddUseCase() { +func (_m *UCLPCInterface) AddUseCase() { _m.Called() } -// UCLCPInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' -type UCLCPInterface_AddUseCase_Call struct { +// UCLPCInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCLPCInterface_AddUseCase_Call struct { *mock.Call } // AddUseCase is a helper method to define mock.On call -func (_e *UCLCPInterface_Expecter) AddUseCase() *UCLCPInterface_AddUseCase_Call { - return &UCLCPInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +func (_e *UCLPCInterface_Expecter) AddUseCase() *UCLPCInterface_AddUseCase_Call { + return &UCLPCInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} } -func (_c *UCLCPInterface_AddUseCase_Call) Run(run func()) *UCLCPInterface_AddUseCase_Call { +func (_c *UCLPCInterface_AddUseCase_Call) Run(run func()) *UCLPCInterface_AddUseCase_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPInterface_AddUseCase_Call) Return() *UCLCPInterface_AddUseCase_Call { +func (_c *UCLPCInterface_AddUseCase_Call) Return() *UCLPCInterface_AddUseCase_Call { _c.Call.Return() return _c } -func (_c *UCLCPInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLCPInterface_AddUseCase_Call { +func (_c *UCLPCInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLPCInterface_AddUseCase_Call { _c.Call.Return(run) return _c } // FailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity -func (_m *UCLCPInterface) FailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface) (float64, error) { +func (_m *UCLPCInterface) FailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface) (float64, error) { ret := _m.Called(entity) if len(ret) == 0 { @@ -118,36 +118,36 @@ func (_m *UCLCPInterface) FailsafeConsumptionActivePowerLimit(entity api.EntityR return r0, r1 } -// UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeConsumptionActivePowerLimit' -type UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call struct { +// UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeConsumptionActivePowerLimit' +type UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call struct { *mock.Call } // FailsafeConsumptionActivePowerLimit is a helper method to define mock.On call // - entity api.EntityRemoteInterface -func (_e *UCLCPInterface_Expecter) FailsafeConsumptionActivePowerLimit(entity interface{}) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { - return &UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("FailsafeConsumptionActivePowerLimit", entity)} +func (_e *UCLPCInterface_Expecter) FailsafeConsumptionActivePowerLimit(entity interface{}) *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call { + return &UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("FailsafeConsumptionActivePowerLimit", entity)} } -func (_c *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call) Return(_a0 float64, _a1 error) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call) Return(_a0 float64, _a1 error) *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLCPInterface_FailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLPCInterface_FailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(run) return _c } // FailsafeDurationMinimum provides a mock function with given fields: entity -func (_m *UCLCPInterface) FailsafeDurationMinimum(entity api.EntityRemoteInterface) (time.Duration, error) { +func (_m *UCLPCInterface) FailsafeDurationMinimum(entity api.EntityRemoteInterface) (time.Duration, error) { ret := _m.Called(entity) if len(ret) == 0 { @@ -174,36 +174,36 @@ func (_m *UCLCPInterface) FailsafeDurationMinimum(entity api.EntityRemoteInterfa return r0, r1 } -// UCLCPInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' -type UCLCPInterface_FailsafeDurationMinimum_Call struct { +// UCLPCInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' +type UCLPCInterface_FailsafeDurationMinimum_Call struct { *mock.Call } // FailsafeDurationMinimum is a helper method to define mock.On call // - entity api.EntityRemoteInterface -func (_e *UCLCPInterface_Expecter) FailsafeDurationMinimum(entity interface{}) *UCLCPInterface_FailsafeDurationMinimum_Call { - return &UCLCPInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum", entity)} +func (_e *UCLPCInterface_Expecter) FailsafeDurationMinimum(entity interface{}) *UCLPCInterface_FailsafeDurationMinimum_Call { + return &UCLPCInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum", entity)} } -func (_c *UCLCPInterface_FailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_FailsafeDurationMinimum_Call { +func (_c *UCLPCInterface_FailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPCInterface_FailsafeDurationMinimum_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCLCPInterface_FailsafeDurationMinimum_Call) Return(_a0 time.Duration, _a1 error) *UCLCPInterface_FailsafeDurationMinimum_Call { +func (_c *UCLPCInterface_FailsafeDurationMinimum_Call) Return(_a0 time.Duration, _a1 error) *UCLPCInterface_FailsafeDurationMinimum_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface) (time.Duration, error)) *UCLCPInterface_FailsafeDurationMinimum_Call { +func (_c *UCLPCInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface) (time.Duration, error)) *UCLPCInterface_FailsafeDurationMinimum_Call { _c.Call.Return(run) return _c } // IsUseCaseSupported provides a mock function with given fields: remoteEntity -func (_m *UCLCPInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { +func (_m *UCLPCInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { ret := _m.Called(remoteEntity) if len(ret) == 0 { @@ -230,36 +230,36 @@ func (_m *UCLCPInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterf return r0, r1 } -// UCLCPInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' -type UCLCPInterface_IsUseCaseSupported_Call struct { +// UCLPCInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCLPCInterface_IsUseCaseSupported_Call struct { *mock.Call } // IsUseCaseSupported is a helper method to define mock.On call // - remoteEntity api.EntityRemoteInterface -func (_e *UCLCPInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLCPInterface_IsUseCaseSupported_Call { - return &UCLCPInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +func (_e *UCLPCInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLPCInterface_IsUseCaseSupported_Call { + return &UCLPCInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} } -func (_c *UCLCPInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLCPInterface_IsUseCaseSupported_Call { +func (_c *UCLPCInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLPCInterface_IsUseCaseSupported_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCLCPInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLCPInterface_IsUseCaseSupported_Call { +func (_c *UCLPCInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLPCInterface_IsUseCaseSupported_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLCPInterface_IsUseCaseSupported_Call { +func (_c *UCLPCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLPCInterface_IsUseCaseSupported_Call { _c.Call.Return(run) return _c } // LoadControlLimit provides a mock function with given fields: entity -func (_m *UCLCPInterface) LoadControlLimit(entity api.EntityRemoteInterface) (cemdapi.LoadLimit, error) { +func (_m *UCLPCInterface) LoadControlLimit(entity api.EntityRemoteInterface) (cemdapi.LoadLimit, error) { ret := _m.Called(entity) if len(ret) == 0 { @@ -286,36 +286,36 @@ func (_m *UCLCPInterface) LoadControlLimit(entity api.EntityRemoteInterface) (ce return r0, r1 } -// UCLCPInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' -type UCLCPInterface_LoadControlLimit_Call struct { +// UCLPCInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' +type UCLPCInterface_LoadControlLimit_Call struct { *mock.Call } // LoadControlLimit is a helper method to define mock.On call // - entity api.EntityRemoteInterface -func (_e *UCLCPInterface_Expecter) LoadControlLimit(entity interface{}) *UCLCPInterface_LoadControlLimit_Call { - return &UCLCPInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit", entity)} +func (_e *UCLPCInterface_Expecter) LoadControlLimit(entity interface{}) *UCLPCInterface_LoadControlLimit_Call { + return &UCLPCInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit", entity)} } -func (_c *UCLCPInterface_LoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_LoadControlLimit_Call { +func (_c *UCLPCInterface_LoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPCInterface_LoadControlLimit_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCLCPInterface_LoadControlLimit_Call) Return(limit cemdapi.LoadLimit, resultErr error) *UCLCPInterface_LoadControlLimit_Call { +func (_c *UCLPCInterface_LoadControlLimit_Call) Return(limit cemdapi.LoadLimit, resultErr error) *UCLPCInterface_LoadControlLimit_Call { _c.Call.Return(limit, resultErr) return _c } -func (_c *UCLCPInterface_LoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)) *UCLCPInterface_LoadControlLimit_Call { +func (_c *UCLPCInterface_LoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)) *UCLPCInterface_LoadControlLimit_Call { _c.Call.Return(run) return _c } // PowerConsumptionNominalMax provides a mock function with given fields: entity -func (_m *UCLCPInterface) PowerConsumptionNominalMax(entity api.EntityRemoteInterface) (float64, error) { +func (_m *UCLPCInterface) PowerConsumptionNominalMax(entity api.EntityRemoteInterface) (float64, error) { ret := _m.Called(entity) if len(ret) == 0 { @@ -342,69 +342,69 @@ func (_m *UCLCPInterface) PowerConsumptionNominalMax(entity api.EntityRemoteInte return r0, r1 } -// UCLCPInterface_PowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerConsumptionNominalMax' -type UCLCPInterface_PowerConsumptionNominalMax_Call struct { +// UCLPCInterface_PowerConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerConsumptionNominalMax' +type UCLPCInterface_PowerConsumptionNominalMax_Call struct { *mock.Call } // PowerConsumptionNominalMax is a helper method to define mock.On call // - entity api.EntityRemoteInterface -func (_e *UCLCPInterface_Expecter) PowerConsumptionNominalMax(entity interface{}) *UCLCPInterface_PowerConsumptionNominalMax_Call { - return &UCLCPInterface_PowerConsumptionNominalMax_Call{Call: _e.mock.On("PowerConsumptionNominalMax", entity)} +func (_e *UCLPCInterface_Expecter) PowerConsumptionNominalMax(entity interface{}) *UCLPCInterface_PowerConsumptionNominalMax_Call { + return &UCLPCInterface_PowerConsumptionNominalMax_Call{Call: _e.mock.On("PowerConsumptionNominalMax", entity)} } -func (_c *UCLCPInterface_PowerConsumptionNominalMax_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLCPInterface_PowerConsumptionNominalMax_Call { +func (_c *UCLPCInterface_PowerConsumptionNominalMax_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPCInterface_PowerConsumptionNominalMax_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCLCPInterface_PowerConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPInterface_PowerConsumptionNominalMax_Call { +func (_c *UCLPCInterface_PowerConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLPCInterface_PowerConsumptionNominalMax_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_PowerConsumptionNominalMax_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLCPInterface_PowerConsumptionNominalMax_Call { +func (_c *UCLPCInterface_PowerConsumptionNominalMax_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLPCInterface_PowerConsumptionNominalMax_Call { _c.Call.Return(run) return _c } // UpdateUseCaseAvailability provides a mock function with given fields: available -func (_m *UCLCPInterface) UpdateUseCaseAvailability(available bool) { +func (_m *UCLPCInterface) UpdateUseCaseAvailability(available bool) { _m.Called(available) } -// UCLCPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' -type UCLCPInterface_UpdateUseCaseAvailability_Call struct { +// UCLPCInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCLPCInterface_UpdateUseCaseAvailability_Call struct { *mock.Call } // UpdateUseCaseAvailability is a helper method to define mock.On call // - available bool -func (_e *UCLCPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLCPInterface_UpdateUseCaseAvailability_Call { - return &UCLCPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +func (_e *UCLPCInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLPCInterface_UpdateUseCaseAvailability_Call { + return &UCLPCInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} } -func (_c *UCLCPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLCPInterface_UpdateUseCaseAvailability_Call { +func (_c *UCLPCInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLPCInterface_UpdateUseCaseAvailability_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(bool)) }) return _c } -func (_c *UCLCPInterface_UpdateUseCaseAvailability_Call) Return() *UCLCPInterface_UpdateUseCaseAvailability_Call { +func (_c *UCLPCInterface_UpdateUseCaseAvailability_Call) Return() *UCLPCInterface_UpdateUseCaseAvailability_Call { _c.Call.Return() return _c } -func (_c *UCLCPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLCPInterface_UpdateUseCaseAvailability_Call { +func (_c *UCLPCInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLPCInterface_UpdateUseCaseAvailability_Call { _c.Call.Return(run) return _c } // UseCaseName provides a mock function with given fields: -func (_m *UCLCPInterface) UseCaseName() model.UseCaseNameType { +func (_m *UCLPCInterface) UseCaseName() model.UseCaseNameType { ret := _m.Called() if len(ret) == 0 { @@ -421,35 +421,35 @@ func (_m *UCLCPInterface) UseCaseName() model.UseCaseNameType { return r0 } -// UCLCPInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' -type UCLCPInterface_UseCaseName_Call struct { +// UCLPCInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCLPCInterface_UseCaseName_Call struct { *mock.Call } // UseCaseName is a helper method to define mock.On call -func (_e *UCLCPInterface_Expecter) UseCaseName() *UCLCPInterface_UseCaseName_Call { - return &UCLCPInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +func (_e *UCLPCInterface_Expecter) UseCaseName() *UCLPCInterface_UseCaseName_Call { + return &UCLPCInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} } -func (_c *UCLCPInterface_UseCaseName_Call) Run(run func()) *UCLCPInterface_UseCaseName_Call { +func (_c *UCLPCInterface_UseCaseName_Call) Run(run func()) *UCLPCInterface_UseCaseName_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLCPInterface_UseCaseName_Call { +func (_c *UCLPCInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLPCInterface_UseCaseName_Call { _c.Call.Return(_a0) return _c } -func (_c *UCLCPInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLCPInterface_UseCaseName_Call { +func (_c *UCLPCInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLPCInterface_UseCaseName_Call { _c.Call.Return(run) return _c } // WriteFailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity, value -func (_m *UCLCPInterface) WriteFailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { +func (_m *UCLPCInterface) WriteFailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { ret := _m.Called(entity, value) if len(ret) == 0 { @@ -478,37 +478,37 @@ func (_m *UCLCPInterface) WriteFailsafeConsumptionActivePowerLimit(entity api.En return r0, r1 } -// UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeConsumptionActivePowerLimit' -type UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call struct { +// UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeConsumptionActivePowerLimit' +type UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call struct { *mock.Call } // WriteFailsafeConsumptionActivePowerLimit is a helper method to define mock.On call // - entity api.EntityRemoteInterface // - value float64 -func (_e *UCLCPInterface_Expecter) WriteFailsafeConsumptionActivePowerLimit(entity interface{}, value interface{}) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { - return &UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("WriteFailsafeConsumptionActivePowerLimit", entity, value)} +func (_e *UCLPCInterface_Expecter) WriteFailsafeConsumptionActivePowerLimit(entity interface{}, value interface{}) *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call { + return &UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("WriteFailsafeConsumptionActivePowerLimit", entity, value)} } -func (_c *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface, value float64)) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface, value float64)) *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface), args[1].(float64)) }) return _c } -func (_c *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, float64) (*model.MsgCounterType, error)) *UCLCPInterface_WriteFailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, float64) (*model.MsgCounterType, error)) *UCLPCInterface_WriteFailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(run) return _c } // WriteFailsafeDurationMinimum provides a mock function with given fields: entity, duration -func (_m *UCLCPInterface) WriteFailsafeDurationMinimum(entity api.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) { +func (_m *UCLPCInterface) WriteFailsafeDurationMinimum(entity api.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) { ret := _m.Called(entity, duration) if len(ret) == 0 { @@ -537,37 +537,37 @@ func (_m *UCLCPInterface) WriteFailsafeDurationMinimum(entity api.EntityRemoteIn return r0, r1 } -// UCLCPInterface_WriteFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeDurationMinimum' -type UCLCPInterface_WriteFailsafeDurationMinimum_Call struct { +// UCLPCInterface_WriteFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeDurationMinimum' +type UCLPCInterface_WriteFailsafeDurationMinimum_Call struct { *mock.Call } // WriteFailsafeDurationMinimum is a helper method to define mock.On call // - entity api.EntityRemoteInterface // - duration time.Duration -func (_e *UCLCPInterface_Expecter) WriteFailsafeDurationMinimum(entity interface{}, duration interface{}) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { - return &UCLCPInterface_WriteFailsafeDurationMinimum_Call{Call: _e.mock.On("WriteFailsafeDurationMinimum", entity, duration)} +func (_e *UCLPCInterface_Expecter) WriteFailsafeDurationMinimum(entity interface{}, duration interface{}) *UCLPCInterface_WriteFailsafeDurationMinimum_Call { + return &UCLPCInterface_WriteFailsafeDurationMinimum_Call{Call: _e.mock.On("WriteFailsafeDurationMinimum", entity, duration)} } -func (_c *UCLCPInterface_WriteFailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface, duration time.Duration)) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { +func (_c *UCLPCInterface_WriteFailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface, duration time.Duration)) *UCLPCInterface_WriteFailsafeDurationMinimum_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface), args[1].(time.Duration)) }) return _c } -func (_c *UCLCPInterface_WriteFailsafeDurationMinimum_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { +func (_c *UCLPCInterface_WriteFailsafeDurationMinimum_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPCInterface_WriteFailsafeDurationMinimum_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_WriteFailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface, time.Duration) (*model.MsgCounterType, error)) *UCLCPInterface_WriteFailsafeDurationMinimum_Call { +func (_c *UCLPCInterface_WriteFailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface, time.Duration) (*model.MsgCounterType, error)) *UCLPCInterface_WriteFailsafeDurationMinimum_Call { _c.Call.Return(run) return _c } // WriteLoadControlLimit provides a mock function with given fields: entity, limit -func (_m *UCLCPInterface) WriteLoadControlLimit(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit) (*model.MsgCounterType, error) { +func (_m *UCLPCInterface) WriteLoadControlLimit(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit) (*model.MsgCounterType, error) { ret := _m.Called(entity, limit) if len(ret) == 0 { @@ -596,42 +596,42 @@ func (_m *UCLCPInterface) WriteLoadControlLimit(entity api.EntityRemoteInterface return r0, r1 } -// UCLCPInterface_WriteLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteLoadControlLimit' -type UCLCPInterface_WriteLoadControlLimit_Call struct { +// UCLPCInterface_WriteLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteLoadControlLimit' +type UCLPCInterface_WriteLoadControlLimit_Call struct { *mock.Call } // WriteLoadControlLimit is a helper method to define mock.On call // - entity api.EntityRemoteInterface // - limit cemdapi.LoadLimit -func (_e *UCLCPInterface_Expecter) WriteLoadControlLimit(entity interface{}, limit interface{}) *UCLCPInterface_WriteLoadControlLimit_Call { - return &UCLCPInterface_WriteLoadControlLimit_Call{Call: _e.mock.On("WriteLoadControlLimit", entity, limit)} +func (_e *UCLPCInterface_Expecter) WriteLoadControlLimit(entity interface{}, limit interface{}) *UCLPCInterface_WriteLoadControlLimit_Call { + return &UCLPCInterface_WriteLoadControlLimit_Call{Call: _e.mock.On("WriteLoadControlLimit", entity, limit)} } -func (_c *UCLCPInterface_WriteLoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit)) *UCLCPInterface_WriteLoadControlLimit_Call { +func (_c *UCLPCInterface_WriteLoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit)) *UCLPCInterface_WriteLoadControlLimit_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface), args[1].(cemdapi.LoadLimit)) }) return _c } -func (_c *UCLCPInterface_WriteLoadControlLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLCPInterface_WriteLoadControlLimit_Call { +func (_c *UCLPCInterface_WriteLoadControlLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPCInterface_WriteLoadControlLimit_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPInterface_WriteLoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)) *UCLCPInterface_WriteLoadControlLimit_Call { +func (_c *UCLPCInterface_WriteLoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)) *UCLPCInterface_WriteLoadControlLimit_Call { _c.Call.Return(run) return _c } -// NewUCLCPInterface creates a new instance of UCLCPInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewUCLPCInterface creates a new instance of UCLPCInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewUCLCPInterface(t interface { +func NewUCLPCInterface(t interface { mock.TestingT Cleanup(func()) -}) *UCLCPInterface { - mock := &UCLCPInterface{} +}) *UCLPCInterface { + mock := &UCLPCInterface{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/mocks/UCLCPServerInterface.go b/mocks/UCLPCServerInterface.go similarity index 55% rename from mocks/UCLCPServerInterface.go rename to mocks/UCLPCServerInterface.go index e769bfe..1f695be 100644 --- a/mocks/UCLCPServerInterface.go +++ b/mocks/UCLPCServerInterface.go @@ -13,85 +13,85 @@ import ( time "time" ) -// UCLCPServerInterface is an autogenerated mock type for the UCLCPServerInterface type -type UCLCPServerInterface struct { +// UCLPCServerInterface is an autogenerated mock type for the UCLPCServerInterface type +type UCLPCServerInterface struct { mock.Mock } -type UCLCPServerInterface_Expecter struct { +type UCLPCServerInterface_Expecter struct { mock *mock.Mock } -func (_m *UCLCPServerInterface) EXPECT() *UCLCPServerInterface_Expecter { - return &UCLCPServerInterface_Expecter{mock: &_m.Mock} +func (_m *UCLPCServerInterface) EXPECT() *UCLPCServerInterface_Expecter { + return &UCLPCServerInterface_Expecter{mock: &_m.Mock} } // AddFeatures provides a mock function with given fields: -func (_m *UCLCPServerInterface) AddFeatures() { +func (_m *UCLPCServerInterface) AddFeatures() { _m.Called() } -// UCLCPServerInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' -type UCLCPServerInterface_AddFeatures_Call struct { +// UCLPCServerInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCLPCServerInterface_AddFeatures_Call struct { *mock.Call } // AddFeatures is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) AddFeatures() *UCLCPServerInterface_AddFeatures_Call { - return &UCLCPServerInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +func (_e *UCLPCServerInterface_Expecter) AddFeatures() *UCLPCServerInterface_AddFeatures_Call { + return &UCLPCServerInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} } -func (_c *UCLCPServerInterface_AddFeatures_Call) Run(run func()) *UCLCPServerInterface_AddFeatures_Call { +func (_c *UCLPCServerInterface_AddFeatures_Call) Run(run func()) *UCLPCServerInterface_AddFeatures_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_AddFeatures_Call) Return() *UCLCPServerInterface_AddFeatures_Call { +func (_c *UCLPCServerInterface_AddFeatures_Call) Return() *UCLPCServerInterface_AddFeatures_Call { _c.Call.Return() return _c } -func (_c *UCLCPServerInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLCPServerInterface_AddFeatures_Call { +func (_c *UCLPCServerInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLPCServerInterface_AddFeatures_Call { _c.Call.Return(run) return _c } // AddUseCase provides a mock function with given fields: -func (_m *UCLCPServerInterface) AddUseCase() { +func (_m *UCLPCServerInterface) AddUseCase() { _m.Called() } -// UCLCPServerInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' -type UCLCPServerInterface_AddUseCase_Call struct { +// UCLPCServerInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCLPCServerInterface_AddUseCase_Call struct { *mock.Call } // AddUseCase is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) AddUseCase() *UCLCPServerInterface_AddUseCase_Call { - return &UCLCPServerInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +func (_e *UCLPCServerInterface_Expecter) AddUseCase() *UCLPCServerInterface_AddUseCase_Call { + return &UCLPCServerInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} } -func (_c *UCLCPServerInterface_AddUseCase_Call) Run(run func()) *UCLCPServerInterface_AddUseCase_Call { +func (_c *UCLPCServerInterface_AddUseCase_Call) Run(run func()) *UCLPCServerInterface_AddUseCase_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_AddUseCase_Call) Return() *UCLCPServerInterface_AddUseCase_Call { +func (_c *UCLPCServerInterface_AddUseCase_Call) Return() *UCLPCServerInterface_AddUseCase_Call { _c.Call.Return() return _c } -func (_c *UCLCPServerInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLCPServerInterface_AddUseCase_Call { +func (_c *UCLPCServerInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLPCServerInterface_AddUseCase_Call { _c.Call.Return(run) return _c } // ContractualConsumptionNominalMax provides a mock function with given fields: -func (_m *UCLCPServerInterface) ContractualConsumptionNominalMax() (float64, error) { +func (_m *UCLPCServerInterface) ContractualConsumptionNominalMax() (float64, error) { ret := _m.Called() if len(ret) == 0 { @@ -118,35 +118,35 @@ func (_m *UCLCPServerInterface) ContractualConsumptionNominalMax() (float64, err return r0, r1 } -// UCLCPServerInterface_ContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ContractualConsumptionNominalMax' -type UCLCPServerInterface_ContractualConsumptionNominalMax_Call struct { +// UCLPCServerInterface_ContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ContractualConsumptionNominalMax' +type UCLPCServerInterface_ContractualConsumptionNominalMax_Call struct { *mock.Call } // ContractualConsumptionNominalMax is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) ContractualConsumptionNominalMax() *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { - return &UCLCPServerInterface_ContractualConsumptionNominalMax_Call{Call: _e.mock.On("ContractualConsumptionNominalMax")} +func (_e *UCLPCServerInterface_Expecter) ContractualConsumptionNominalMax() *UCLPCServerInterface_ContractualConsumptionNominalMax_Call { + return &UCLPCServerInterface_ContractualConsumptionNominalMax_Call{Call: _e.mock.On("ContractualConsumptionNominalMax")} } -func (_c *UCLCPServerInterface_ContractualConsumptionNominalMax_Call) Run(run func()) *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { +func (_c *UCLPCServerInterface_ContractualConsumptionNominalMax_Call) Run(run func()) *UCLPCServerInterface_ContractualConsumptionNominalMax_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_ContractualConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { +func (_c *UCLPCServerInterface_ContractualConsumptionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLPCServerInterface_ContractualConsumptionNominalMax_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPServerInterface_ContractualConsumptionNominalMax_Call) RunAndReturn(run func() (float64, error)) *UCLCPServerInterface_ContractualConsumptionNominalMax_Call { +func (_c *UCLPCServerInterface_ContractualConsumptionNominalMax_Call) RunAndReturn(run func() (float64, error)) *UCLPCServerInterface_ContractualConsumptionNominalMax_Call { _c.Call.Return(run) return _c } // FailsafeConsumptionActivePowerLimit provides a mock function with given fields: -func (_m *UCLCPServerInterface) FailsafeConsumptionActivePowerLimit() (float64, bool, error) { +func (_m *UCLPCServerInterface) FailsafeConsumptionActivePowerLimit() (float64, bool, error) { ret := _m.Called() if len(ret) == 0 { @@ -180,35 +180,35 @@ func (_m *UCLCPServerInterface) FailsafeConsumptionActivePowerLimit() (float64, return r0, r1, r2 } -// UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeConsumptionActivePowerLimit' -type UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call struct { +// UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeConsumptionActivePowerLimit' +type UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call struct { *mock.Call } // FailsafeConsumptionActivePowerLimit is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) FailsafeConsumptionActivePowerLimit() *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { - return &UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("FailsafeConsumptionActivePowerLimit")} +func (_e *UCLPCServerInterface_Expecter) FailsafeConsumptionActivePowerLimit() *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call { + return &UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("FailsafeConsumptionActivePowerLimit")} } -func (_c *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call) Run(run func()) *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call) Run(run func()) *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call) Return(value float64, isChangeable bool, resultErr error) *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call) Return(value float64, isChangeable bool, resultErr error) *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(value, isChangeable, resultErr) return _c } -func (_c *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func() (float64, bool, error)) *UCLCPServerInterface_FailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func() (float64, bool, error)) *UCLPCServerInterface_FailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(run) return _c } // FailsafeDurationMinimum provides a mock function with given fields: -func (_m *UCLCPServerInterface) FailsafeDurationMinimum() (time.Duration, bool, error) { +func (_m *UCLPCServerInterface) FailsafeDurationMinimum() (time.Duration, bool, error) { ret := _m.Called() if len(ret) == 0 { @@ -242,35 +242,35 @@ func (_m *UCLCPServerInterface) FailsafeDurationMinimum() (time.Duration, bool, return r0, r1, r2 } -// UCLCPServerInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' -type UCLCPServerInterface_FailsafeDurationMinimum_Call struct { +// UCLPCServerInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' +type UCLPCServerInterface_FailsafeDurationMinimum_Call struct { *mock.Call } // FailsafeDurationMinimum is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) FailsafeDurationMinimum() *UCLCPServerInterface_FailsafeDurationMinimum_Call { - return &UCLCPServerInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum")} +func (_e *UCLPCServerInterface_Expecter) FailsafeDurationMinimum() *UCLPCServerInterface_FailsafeDurationMinimum_Call { + return &UCLPCServerInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum")} } -func (_c *UCLCPServerInterface_FailsafeDurationMinimum_Call) Run(run func()) *UCLCPServerInterface_FailsafeDurationMinimum_Call { +func (_c *UCLPCServerInterface_FailsafeDurationMinimum_Call) Run(run func()) *UCLPCServerInterface_FailsafeDurationMinimum_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_FailsafeDurationMinimum_Call) Return(duration time.Duration, isChangeable bool, resultErr error) *UCLCPServerInterface_FailsafeDurationMinimum_Call { +func (_c *UCLPCServerInterface_FailsafeDurationMinimum_Call) Return(duration time.Duration, isChangeable bool, resultErr error) *UCLPCServerInterface_FailsafeDurationMinimum_Call { _c.Call.Return(duration, isChangeable, resultErr) return _c } -func (_c *UCLCPServerInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func() (time.Duration, bool, error)) *UCLCPServerInterface_FailsafeDurationMinimum_Call { +func (_c *UCLPCServerInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func() (time.Duration, bool, error)) *UCLPCServerInterface_FailsafeDurationMinimum_Call { _c.Call.Return(run) return _c } // IsUseCaseSupported provides a mock function with given fields: remoteEntity -func (_m *UCLCPServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { +func (_m *UCLPCServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { ret := _m.Called(remoteEntity) if len(ret) == 0 { @@ -297,36 +297,36 @@ func (_m *UCLCPServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemote return r0, r1 } -// UCLCPServerInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' -type UCLCPServerInterface_IsUseCaseSupported_Call struct { +// UCLPCServerInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCLPCServerInterface_IsUseCaseSupported_Call struct { *mock.Call } // IsUseCaseSupported is a helper method to define mock.On call // - remoteEntity api.EntityRemoteInterface -func (_e *UCLCPServerInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLCPServerInterface_IsUseCaseSupported_Call { - return &UCLCPServerInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +func (_e *UCLPCServerInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLPCServerInterface_IsUseCaseSupported_Call { + return &UCLPCServerInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} } -func (_c *UCLCPServerInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLCPServerInterface_IsUseCaseSupported_Call { +func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLPCServerInterface_IsUseCaseSupported_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(api.EntityRemoteInterface)) }) return _c } -func (_c *UCLCPServerInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLCPServerInterface_IsUseCaseSupported_Call { +func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLPCServerInterface_IsUseCaseSupported_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPServerInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLCPServerInterface_IsUseCaseSupported_Call { +func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLPCServerInterface_IsUseCaseSupported_Call { _c.Call.Return(run) return _c } // LoadControlLimit provides a mock function with given fields: -func (_m *UCLCPServerInterface) LoadControlLimit() (cemdapi.LoadLimit, error) { +func (_m *UCLPCServerInterface) LoadControlLimit() (cemdapi.LoadLimit, error) { ret := _m.Called() if len(ret) == 0 { @@ -353,35 +353,35 @@ func (_m *UCLCPServerInterface) LoadControlLimit() (cemdapi.LoadLimit, error) { return r0, r1 } -// UCLCPServerInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' -type UCLCPServerInterface_LoadControlLimit_Call struct { +// UCLPCServerInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' +type UCLPCServerInterface_LoadControlLimit_Call struct { *mock.Call } // LoadControlLimit is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) LoadControlLimit() *UCLCPServerInterface_LoadControlLimit_Call { - return &UCLCPServerInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit")} +func (_e *UCLPCServerInterface_Expecter) LoadControlLimit() *UCLPCServerInterface_LoadControlLimit_Call { + return &UCLPCServerInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit")} } -func (_c *UCLCPServerInterface_LoadControlLimit_Call) Run(run func()) *UCLCPServerInterface_LoadControlLimit_Call { +func (_c *UCLPCServerInterface_LoadControlLimit_Call) Run(run func()) *UCLPCServerInterface_LoadControlLimit_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_LoadControlLimit_Call) Return(_a0 cemdapi.LoadLimit, _a1 error) *UCLCPServerInterface_LoadControlLimit_Call { +func (_c *UCLPCServerInterface_LoadControlLimit_Call) Return(_a0 cemdapi.LoadLimit, _a1 error) *UCLPCServerInterface_LoadControlLimit_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *UCLCPServerInterface_LoadControlLimit_Call) RunAndReturn(run func() (cemdapi.LoadLimit, error)) *UCLCPServerInterface_LoadControlLimit_Call { +func (_c *UCLPCServerInterface_LoadControlLimit_Call) RunAndReturn(run func() (cemdapi.LoadLimit, error)) *UCLPCServerInterface_LoadControlLimit_Call { _c.Call.Return(run) return _c } // SetContractualConsumptionNominalMax provides a mock function with given fields: value -func (_m *UCLCPServerInterface) SetContractualConsumptionNominalMax(value float64) error { +func (_m *UCLPCServerInterface) SetContractualConsumptionNominalMax(value float64) error { ret := _m.Called(value) if len(ret) == 0 { @@ -398,36 +398,36 @@ func (_m *UCLCPServerInterface) SetContractualConsumptionNominalMax(value float6 return r0 } -// UCLCPServerInterface_SetContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetContractualConsumptionNominalMax' -type UCLCPServerInterface_SetContractualConsumptionNominalMax_Call struct { +// UCLPCServerInterface_SetContractualConsumptionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetContractualConsumptionNominalMax' +type UCLPCServerInterface_SetContractualConsumptionNominalMax_Call struct { *mock.Call } // SetContractualConsumptionNominalMax is a helper method to define mock.On call // - value float64 -func (_e *UCLCPServerInterface_Expecter) SetContractualConsumptionNominalMax(value interface{}) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { - return &UCLCPServerInterface_SetContractualConsumptionNominalMax_Call{Call: _e.mock.On("SetContractualConsumptionNominalMax", value)} +func (_e *UCLPCServerInterface_Expecter) SetContractualConsumptionNominalMax(value interface{}) *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call { + return &UCLPCServerInterface_SetContractualConsumptionNominalMax_Call{Call: _e.mock.On("SetContractualConsumptionNominalMax", value)} } -func (_c *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call) Run(run func(value float64)) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { +func (_c *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call) Run(run func(value float64)) *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(float64)) }) return _c } -func (_c *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call) Return(resultErr error) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { +func (_c *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call) Return(resultErr error) *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call { _c.Call.Return(resultErr) return _c } -func (_c *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call) RunAndReturn(run func(float64) error) *UCLCPServerInterface_SetContractualConsumptionNominalMax_Call { +func (_c *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call) RunAndReturn(run func(float64) error) *UCLPCServerInterface_SetContractualConsumptionNominalMax_Call { _c.Call.Return(run) return _c } // SetFailsafeConsumptionActivePowerLimit provides a mock function with given fields: value, changeable -func (_m *UCLCPServerInterface) SetFailsafeConsumptionActivePowerLimit(value float64, changeable bool) error { +func (_m *UCLPCServerInterface) SetFailsafeConsumptionActivePowerLimit(value float64, changeable bool) error { ret := _m.Called(value, changeable) if len(ret) == 0 { @@ -444,37 +444,37 @@ func (_m *UCLCPServerInterface) SetFailsafeConsumptionActivePowerLimit(value flo return r0 } -// UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeConsumptionActivePowerLimit' -type UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call struct { +// UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeConsumptionActivePowerLimit' +type UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call struct { *mock.Call } // SetFailsafeConsumptionActivePowerLimit is a helper method to define mock.On call // - value float64 // - changeable bool -func (_e *UCLCPServerInterface_Expecter) SetFailsafeConsumptionActivePowerLimit(value interface{}, changeable interface{}) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { - return &UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("SetFailsafeConsumptionActivePowerLimit", value, changeable)} +func (_e *UCLPCServerInterface_Expecter) SetFailsafeConsumptionActivePowerLimit(value interface{}, changeable interface{}) *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { + return &UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call{Call: _e.mock.On("SetFailsafeConsumptionActivePowerLimit", value, changeable)} } -func (_c *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) Run(run func(value float64, changeable bool)) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) Run(run func(value float64, changeable bool)) *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(float64), args[1].(bool)) }) return _c } -func (_c *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) Return(resultErr error) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) Return(resultErr error) *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(resultErr) return _c } -func (_c *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(float64, bool) error) *UCLCPServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { +func (_c *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call) RunAndReturn(run func(float64, bool) error) *UCLPCServerInterface_SetFailsafeConsumptionActivePowerLimit_Call { _c.Call.Return(run) return _c } // SetFailsafeDurationMinimum provides a mock function with given fields: duration, changeable -func (_m *UCLCPServerInterface) SetFailsafeDurationMinimum(duration time.Duration, changeable bool) error { +func (_m *UCLPCServerInterface) SetFailsafeDurationMinimum(duration time.Duration, changeable bool) error { ret := _m.Called(duration, changeable) if len(ret) == 0 { @@ -491,37 +491,37 @@ func (_m *UCLCPServerInterface) SetFailsafeDurationMinimum(duration time.Duratio return r0 } -// UCLCPServerInterface_SetFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeDurationMinimum' -type UCLCPServerInterface_SetFailsafeDurationMinimum_Call struct { +// UCLPCServerInterface_SetFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeDurationMinimum' +type UCLPCServerInterface_SetFailsafeDurationMinimum_Call struct { *mock.Call } // SetFailsafeDurationMinimum is a helper method to define mock.On call // - duration time.Duration // - changeable bool -func (_e *UCLCPServerInterface_Expecter) SetFailsafeDurationMinimum(duration interface{}, changeable interface{}) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { - return &UCLCPServerInterface_SetFailsafeDurationMinimum_Call{Call: _e.mock.On("SetFailsafeDurationMinimum", duration, changeable)} +func (_e *UCLPCServerInterface_Expecter) SetFailsafeDurationMinimum(duration interface{}, changeable interface{}) *UCLPCServerInterface_SetFailsafeDurationMinimum_Call { + return &UCLPCServerInterface_SetFailsafeDurationMinimum_Call{Call: _e.mock.On("SetFailsafeDurationMinimum", duration, changeable)} } -func (_c *UCLCPServerInterface_SetFailsafeDurationMinimum_Call) Run(run func(duration time.Duration, changeable bool)) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { +func (_c *UCLPCServerInterface_SetFailsafeDurationMinimum_Call) Run(run func(duration time.Duration, changeable bool)) *UCLPCServerInterface_SetFailsafeDurationMinimum_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(time.Duration), args[1].(bool)) }) return _c } -func (_c *UCLCPServerInterface_SetFailsafeDurationMinimum_Call) Return(resultErr error) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { +func (_c *UCLPCServerInterface_SetFailsafeDurationMinimum_Call) Return(resultErr error) *UCLPCServerInterface_SetFailsafeDurationMinimum_Call { _c.Call.Return(resultErr) return _c } -func (_c *UCLCPServerInterface_SetFailsafeDurationMinimum_Call) RunAndReturn(run func(time.Duration, bool) error) *UCLCPServerInterface_SetFailsafeDurationMinimum_Call { +func (_c *UCLPCServerInterface_SetFailsafeDurationMinimum_Call) RunAndReturn(run func(time.Duration, bool) error) *UCLPCServerInterface_SetFailsafeDurationMinimum_Call { _c.Call.Return(run) return _c } // SetLoadControlLimit provides a mock function with given fields: limit -func (_m *UCLCPServerInterface) SetLoadControlLimit(limit cemdapi.LoadLimit) error { +func (_m *UCLPCServerInterface) SetLoadControlLimit(limit cemdapi.LoadLimit) error { ret := _m.Called(limit) if len(ret) == 0 { @@ -538,69 +538,69 @@ func (_m *UCLCPServerInterface) SetLoadControlLimit(limit cemdapi.LoadLimit) err return r0 } -// UCLCPServerInterface_SetLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLoadControlLimit' -type UCLCPServerInterface_SetLoadControlLimit_Call struct { +// UCLPCServerInterface_SetLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLoadControlLimit' +type UCLPCServerInterface_SetLoadControlLimit_Call struct { *mock.Call } // SetLoadControlLimit is a helper method to define mock.On call // - limit cemdapi.LoadLimit -func (_e *UCLCPServerInterface_Expecter) SetLoadControlLimit(limit interface{}) *UCLCPServerInterface_SetLoadControlLimit_Call { - return &UCLCPServerInterface_SetLoadControlLimit_Call{Call: _e.mock.On("SetLoadControlLimit", limit)} +func (_e *UCLPCServerInterface_Expecter) SetLoadControlLimit(limit interface{}) *UCLPCServerInterface_SetLoadControlLimit_Call { + return &UCLPCServerInterface_SetLoadControlLimit_Call{Call: _e.mock.On("SetLoadControlLimit", limit)} } -func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) Run(run func(limit cemdapi.LoadLimit)) *UCLCPServerInterface_SetLoadControlLimit_Call { +func (_c *UCLPCServerInterface_SetLoadControlLimit_Call) Run(run func(limit cemdapi.LoadLimit)) *UCLPCServerInterface_SetLoadControlLimit_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(cemdapi.LoadLimit)) }) return _c } -func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) Return(resultErr error) *UCLCPServerInterface_SetLoadControlLimit_Call { +func (_c *UCLPCServerInterface_SetLoadControlLimit_Call) Return(resultErr error) *UCLPCServerInterface_SetLoadControlLimit_Call { _c.Call.Return(resultErr) return _c } -func (_c *UCLCPServerInterface_SetLoadControlLimit_Call) RunAndReturn(run func(cemdapi.LoadLimit) error) *UCLCPServerInterface_SetLoadControlLimit_Call { +func (_c *UCLPCServerInterface_SetLoadControlLimit_Call) RunAndReturn(run func(cemdapi.LoadLimit) error) *UCLPCServerInterface_SetLoadControlLimit_Call { _c.Call.Return(run) return _c } // UpdateUseCaseAvailability provides a mock function with given fields: available -func (_m *UCLCPServerInterface) UpdateUseCaseAvailability(available bool) { +func (_m *UCLPCServerInterface) UpdateUseCaseAvailability(available bool) { _m.Called(available) } -// UCLCPServerInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' -type UCLCPServerInterface_UpdateUseCaseAvailability_Call struct { +// UCLPCServerInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCLPCServerInterface_UpdateUseCaseAvailability_Call struct { *mock.Call } // UpdateUseCaseAvailability is a helper method to define mock.On call // - available bool -func (_e *UCLCPServerInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLCPServerInterface_UpdateUseCaseAvailability_Call { - return &UCLCPServerInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +func (_e *UCLPCServerInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLPCServerInterface_UpdateUseCaseAvailability_Call { + return &UCLPCServerInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} } -func (_c *UCLCPServerInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLCPServerInterface_UpdateUseCaseAvailability_Call { +func (_c *UCLPCServerInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLPCServerInterface_UpdateUseCaseAvailability_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(bool)) }) return _c } -func (_c *UCLCPServerInterface_UpdateUseCaseAvailability_Call) Return() *UCLCPServerInterface_UpdateUseCaseAvailability_Call { +func (_c *UCLPCServerInterface_UpdateUseCaseAvailability_Call) Return() *UCLPCServerInterface_UpdateUseCaseAvailability_Call { _c.Call.Return() return _c } -func (_c *UCLCPServerInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLCPServerInterface_UpdateUseCaseAvailability_Call { +func (_c *UCLPCServerInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLPCServerInterface_UpdateUseCaseAvailability_Call { _c.Call.Return(run) return _c } // UseCaseName provides a mock function with given fields: -func (_m *UCLCPServerInterface) UseCaseName() model.UseCaseNameType { +func (_m *UCLPCServerInterface) UseCaseName() model.UseCaseNameType { ret := _m.Called() if len(ret) == 0 { @@ -617,40 +617,40 @@ func (_m *UCLCPServerInterface) UseCaseName() model.UseCaseNameType { return r0 } -// UCLCPServerInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' -type UCLCPServerInterface_UseCaseName_Call struct { +// UCLPCServerInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCLPCServerInterface_UseCaseName_Call struct { *mock.Call } // UseCaseName is a helper method to define mock.On call -func (_e *UCLCPServerInterface_Expecter) UseCaseName() *UCLCPServerInterface_UseCaseName_Call { - return &UCLCPServerInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +func (_e *UCLPCServerInterface_Expecter) UseCaseName() *UCLPCServerInterface_UseCaseName_Call { + return &UCLPCServerInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} } -func (_c *UCLCPServerInterface_UseCaseName_Call) Run(run func()) *UCLCPServerInterface_UseCaseName_Call { +func (_c *UCLPCServerInterface_UseCaseName_Call) Run(run func()) *UCLPCServerInterface_UseCaseName_Call { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *UCLCPServerInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLCPServerInterface_UseCaseName_Call { +func (_c *UCLPCServerInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLPCServerInterface_UseCaseName_Call { _c.Call.Return(_a0) return _c } -func (_c *UCLCPServerInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLCPServerInterface_UseCaseName_Call { +func (_c *UCLPCServerInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLPCServerInterface_UseCaseName_Call { _c.Call.Return(run) return _c } -// NewUCLCPServerInterface creates a new instance of UCLCPServerInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewUCLPCServerInterface creates a new instance of UCLPCServerInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewUCLCPServerInterface(t interface { +func NewUCLPCServerInterface(t interface { mock.TestingT Cleanup(func()) -}) *UCLCPServerInterface { - mock := &UCLCPServerInterface{} +}) *UCLPCServerInterface { + mock := &UCLPCServerInterface{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/uclpc/api.go b/uclpc/api.go index 11a2f04..291c997 100644 --- a/uclpc/api.go +++ b/uclpc/api.go @@ -11,7 +11,7 @@ import ( //go:generate mockery // interface for the Limitation of Power Consumption UseCase as a server -type UCLCPInterface interface { +type UCLPCInterface interface { api.UseCaseInterface // Scenario 1 diff --git a/uclpc/uclpc.go b/uclpc/uclpc.go index 30da99a..6311ba0 100644 --- a/uclpc/uclpc.go +++ b/uclpc/uclpc.go @@ -17,7 +17,7 @@ type UCLPC struct { validEntityTypes []model.EntityTypeType } -var _ UCLCPInterface = (*UCLPC)(nil) +var _ UCLPCInterface = (*UCLPC)(nil) func NewUCLPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPC { uc := &UCLPC{ diff --git a/uclpcserver/api.go b/uclpcserver/api.go index 99d7aec..96fa870 100644 --- a/uclpcserver/api.go +++ b/uclpcserver/api.go @@ -9,7 +9,7 @@ import ( //go:generate mockery // interface for the Monitoring of Power Consumption UseCase -type UCLCPServerInterface interface { +type UCLPCServerInterface interface { api.UseCaseInterface // Scenario 1 diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index f1afc80..8d86939 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -20,7 +20,7 @@ type UCLPCServer struct { heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use } -var _ UCLCPServerInterface = (*UCLPCServer)(nil) +var _ UCLPCServerInterface = (*UCLPCServer)(nil) func NewUCLPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPCServer { uc := &UCLPCServer{ From 307488e8bd6dcd247f690116587a4775eeeb29b3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 16 Apr 2024 12:53:52 +0200 Subject: [PATCH 195/227] Fix comment for UCLPC and UCLPCServer --- uclpc/api.go | 2 +- uclpcserver/api.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uclpc/api.go b/uclpc/api.go index 291c997..0391b96 100644 --- a/uclpc/api.go +++ b/uclpc/api.go @@ -10,7 +10,7 @@ import ( //go:generate mockery -// interface for the Limitation of Power Consumption UseCase as a server +// interface for the Limitation of Power Consumption UseCase type UCLPCInterface interface { api.UseCaseInterface diff --git a/uclpcserver/api.go b/uclpcserver/api.go index 96fa870..1f4531c 100644 --- a/uclpcserver/api.go +++ b/uclpcserver/api.go @@ -8,7 +8,7 @@ import ( //go:generate mockery -// interface for the Monitoring of Power Consumption UseCase +// interface for the Limitation of Power Consumption UseCase as a server type UCLPCServerInterface interface { api.UseCaseInterface From e14b3947520a3896f14adb3557e2f1a187001217 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 16 Apr 2024 13:05:43 +0200 Subject: [PATCH 196/227] Update UCLPC api namings --- cmd/democem/democem.go | 2 +- mocks/UCLPCInterface.go | 230 +++++++++++++++++----------------- mocks/UCLPCServerInterface.go | 172 ++++++++++++------------- uclpc/api.go | 6 +- uclpc/public.go | 4 +- uclpc/public_test.go | 18 +-- uclpcserver/api.go | 6 +- uclpcserver/events.go | 2 +- uclpcserver/public.go | 4 +- uclpcserver/public_test.go | 8 +- 10 files changed, 226 insertions(+), 226 deletions(-) diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index dd5e3eb..1817aa8 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -36,7 +36,7 @@ func (d *DemoCem) Setup() error { lpcs := uclpcserver.NewUCLPC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(lpcs) - if err := lpcs.SetLoadControlLimit(api.LoadLimit{ + if err := lpcs.SetConsumptionLimit(api.LoadLimit{ IsChangeable: true, IsActive: false, Value: 0, diff --git a/mocks/UCLPCInterface.go b/mocks/UCLPCInterface.go index d59148a..d9ff2e8 100644 --- a/mocks/UCLPCInterface.go +++ b/mocks/UCLPCInterface.go @@ -90,6 +90,62 @@ func (_c *UCLPCInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLPCInterfa return _c } +// ConsumptionLimit provides a mock function with given fields: entity +func (_m *UCLPCInterface) ConsumptionLimit(entity api.EntityRemoteInterface) (cemdapi.LoadLimit, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ConsumptionLimit") + } + + var r0 cemdapi.LoadLimit + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.LoadLimit); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.LoadLimit) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPCInterface_ConsumptionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConsumptionLimit' +type UCLPCInterface_ConsumptionLimit_Call struct { + *mock.Call +} + +// ConsumptionLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLPCInterface_Expecter) ConsumptionLimit(entity interface{}) *UCLPCInterface_ConsumptionLimit_Call { + return &UCLPCInterface_ConsumptionLimit_Call{Call: _e.mock.On("ConsumptionLimit", entity)} +} + +func (_c *UCLPCInterface_ConsumptionLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPCInterface_ConsumptionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPCInterface_ConsumptionLimit_Call) Return(limit cemdapi.LoadLimit, resultErr error) *UCLPCInterface_ConsumptionLimit_Call { + _c.Call.Return(limit, resultErr) + return _c +} + +func (_c *UCLPCInterface_ConsumptionLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)) *UCLPCInterface_ConsumptionLimit_Call { + _c.Call.Return(run) + return _c +} + // FailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity func (_m *UCLPCInterface) FailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface) (float64, error) { ret := _m.Called(entity) @@ -258,62 +314,6 @@ func (_c *UCLPCInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.Enti return _c } -// LoadControlLimit provides a mock function with given fields: entity -func (_m *UCLPCInterface) LoadControlLimit(entity api.EntityRemoteInterface) (cemdapi.LoadLimit, error) { - ret := _m.Called(entity) - - if len(ret) == 0 { - panic("no return value specified for LoadControlLimit") - } - - var r0 cemdapi.LoadLimit - var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)); ok { - return rf(entity) - } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.LoadLimit); ok { - r0 = rf(entity) - } else { - r0 = ret.Get(0).(cemdapi.LoadLimit) - } - - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { - r1 = rf(entity) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UCLPCInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' -type UCLPCInterface_LoadControlLimit_Call struct { - *mock.Call -} - -// LoadControlLimit is a helper method to define mock.On call -// - entity api.EntityRemoteInterface -func (_e *UCLPCInterface_Expecter) LoadControlLimit(entity interface{}) *UCLPCInterface_LoadControlLimit_Call { - return &UCLPCInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit", entity)} -} - -func (_c *UCLPCInterface_LoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPCInterface_LoadControlLimit_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EntityRemoteInterface)) - }) - return _c -} - -func (_c *UCLPCInterface_LoadControlLimit_Call) Return(limit cemdapi.LoadLimit, resultErr error) *UCLPCInterface_LoadControlLimit_Call { - _c.Call.Return(limit, resultErr) - return _c -} - -func (_c *UCLPCInterface_LoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)) *UCLPCInterface_LoadControlLimit_Call { - _c.Call.Return(run) - return _c -} - // PowerConsumptionNominalMax provides a mock function with given fields: entity func (_m *UCLPCInterface) PowerConsumptionNominalMax(entity api.EntityRemoteInterface) (float64, error) { ret := _m.Called(entity) @@ -448,6 +448,65 @@ func (_c *UCLPCInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCase return _c } +// WriteConsumptionLimit provides a mock function with given fields: entity, limit +func (_m *UCLPCInterface) WriteConsumptionLimit(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit) (*model.MsgCounterType, error) { + ret := _m.Called(entity, limit) + + if len(ret) == 0 { + panic("no return value specified for WriteConsumptionLimit") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)); ok { + return rf(entity, limit) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) *model.MsgCounterType); ok { + r0 = rf(entity, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) error); ok { + r1 = rf(entity, limit) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPCInterface_WriteConsumptionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteConsumptionLimit' +type UCLPCInterface_WriteConsumptionLimit_Call struct { + *mock.Call +} + +// WriteConsumptionLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - limit cemdapi.LoadLimit +func (_e *UCLPCInterface_Expecter) WriteConsumptionLimit(entity interface{}, limit interface{}) *UCLPCInterface_WriteConsumptionLimit_Call { + return &UCLPCInterface_WriteConsumptionLimit_Call{Call: _e.mock.On("WriteConsumptionLimit", entity, limit)} +} + +func (_c *UCLPCInterface_WriteConsumptionLimit_Call) Run(run func(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit)) *UCLPCInterface_WriteConsumptionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(cemdapi.LoadLimit)) + }) + return _c +} + +func (_c *UCLPCInterface_WriteConsumptionLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPCInterface_WriteConsumptionLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPCInterface_WriteConsumptionLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)) *UCLPCInterface_WriteConsumptionLimit_Call { + _c.Call.Return(run) + return _c +} + // WriteFailsafeConsumptionActivePowerLimit provides a mock function with given fields: entity, value func (_m *UCLPCInterface) WriteFailsafeConsumptionActivePowerLimit(entity api.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { ret := _m.Called(entity, value) @@ -566,65 +625,6 @@ func (_c *UCLPCInterface_WriteFailsafeDurationMinimum_Call) RunAndReturn(run fun return _c } -// WriteLoadControlLimit provides a mock function with given fields: entity, limit -func (_m *UCLPCInterface) WriteLoadControlLimit(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit) (*model.MsgCounterType, error) { - ret := _m.Called(entity, limit) - - if len(ret) == 0 { - panic("no return value specified for WriteLoadControlLimit") - } - - var r0 *model.MsgCounterType - var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)); ok { - return rf(entity, limit) - } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) *model.MsgCounterType); ok { - r0 = rf(entity, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.MsgCounterType) - } - } - - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) error); ok { - r1 = rf(entity, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UCLPCInterface_WriteLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteLoadControlLimit' -type UCLPCInterface_WriteLoadControlLimit_Call struct { - *mock.Call -} - -// WriteLoadControlLimit is a helper method to define mock.On call -// - entity api.EntityRemoteInterface -// - limit cemdapi.LoadLimit -func (_e *UCLPCInterface_Expecter) WriteLoadControlLimit(entity interface{}, limit interface{}) *UCLPCInterface_WriteLoadControlLimit_Call { - return &UCLPCInterface_WriteLoadControlLimit_Call{Call: _e.mock.On("WriteLoadControlLimit", entity, limit)} -} - -func (_c *UCLPCInterface_WriteLoadControlLimit_Call) Run(run func(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit)) *UCLPCInterface_WriteLoadControlLimit_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EntityRemoteInterface), args[1].(cemdapi.LoadLimit)) - }) - return _c -} - -func (_c *UCLPCInterface_WriteLoadControlLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPCInterface_WriteLoadControlLimit_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *UCLPCInterface_WriteLoadControlLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)) *UCLPCInterface_WriteLoadControlLimit_Call { - _c.Call.Return(run) - return _c -} - // NewUCLPCInterface creates a new instance of UCLPCInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewUCLPCInterface(t interface { diff --git a/mocks/UCLPCServerInterface.go b/mocks/UCLPCServerInterface.go index 1f695be..b06e28d 100644 --- a/mocks/UCLPCServerInterface.go +++ b/mocks/UCLPCServerInterface.go @@ -3,13 +3,13 @@ package mocks import ( - cemdapi "github.com/enbility/cemd/api" - api "github.com/enbility/spine-go/api" - + api "github.com/enbility/cemd/api" mock "github.com/stretchr/testify/mock" model "github.com/enbility/spine-go/model" + spine_goapi "github.com/enbility/spine-go/api" + time "time" ) @@ -90,6 +90,61 @@ func (_c *UCLPCServerInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLPCS return _c } +// ConsumptionLimit provides a mock function with given fields: +func (_m *UCLPCServerInterface) ConsumptionLimit() (api.LoadLimit, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ConsumptionLimit") + } + + var r0 api.LoadLimit + var r1 error + if rf, ok := ret.Get(0).(func() (api.LoadLimit, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() api.LoadLimit); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(api.LoadLimit) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPCServerInterface_ConsumptionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConsumptionLimit' +type UCLPCServerInterface_ConsumptionLimit_Call struct { + *mock.Call +} + +// ConsumptionLimit is a helper method to define mock.On call +func (_e *UCLPCServerInterface_Expecter) ConsumptionLimit() *UCLPCServerInterface_ConsumptionLimit_Call { + return &UCLPCServerInterface_ConsumptionLimit_Call{Call: _e.mock.On("ConsumptionLimit")} +} + +func (_c *UCLPCServerInterface_ConsumptionLimit_Call) Run(run func()) *UCLPCServerInterface_ConsumptionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPCServerInterface_ConsumptionLimit_Call) Return(_a0 api.LoadLimit, _a1 error) *UCLPCServerInterface_ConsumptionLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPCServerInterface_ConsumptionLimit_Call) RunAndReturn(run func() (api.LoadLimit, error)) *UCLPCServerInterface_ConsumptionLimit_Call { + _c.Call.Return(run) + return _c +} + // ContractualConsumptionNominalMax provides a mock function with given fields: func (_m *UCLPCServerInterface) ContractualConsumptionNominalMax() (float64, error) { ret := _m.Called() @@ -270,7 +325,7 @@ func (_c *UCLPCServerInterface_FailsafeDurationMinimum_Call) RunAndReturn(run fu } // IsUseCaseSupported provides a mock function with given fields: remoteEntity -func (_m *UCLPCServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { +func (_m *UCLPCServerInterface) IsUseCaseSupported(remoteEntity spine_goapi.EntityRemoteInterface) (bool, error) { ret := _m.Called(remoteEntity) if len(ret) == 0 { @@ -279,16 +334,16 @@ func (_m *UCLPCServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemote var r0 bool var r1 error - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + if rf, ok := ret.Get(0).(func(spine_goapi.EntityRemoteInterface) (bool, error)); ok { return rf(remoteEntity) } - if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + if rf, ok := ret.Get(0).(func(spine_goapi.EntityRemoteInterface) bool); ok { r0 = rf(remoteEntity) } else { r0 = ret.Get(0).(bool) } - if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + if rf, ok := ret.Get(1).(func(spine_goapi.EntityRemoteInterface) error); ok { r1 = rf(remoteEntity) } else { r1 = ret.Error(1) @@ -303,14 +358,14 @@ type UCLPCServerInterface_IsUseCaseSupported_Call struct { } // IsUseCaseSupported is a helper method to define mock.On call -// - remoteEntity api.EntityRemoteInterface +// - remoteEntity spine_goapi.EntityRemoteInterface func (_e *UCLPCServerInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLPCServerInterface_IsUseCaseSupported_Call { return &UCLPCServerInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} } -func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLPCServerInterface_IsUseCaseSupported_Call { +func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity spine_goapi.EntityRemoteInterface)) *UCLPCServerInterface_IsUseCaseSupported_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(api.EntityRemoteInterface)) + run(args[0].(spine_goapi.EntityRemoteInterface)) }) return _c } @@ -320,62 +375,53 @@ func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 err return _c } -func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLPCServerInterface_IsUseCaseSupported_Call { +func (_c *UCLPCServerInterface_IsUseCaseSupported_Call) RunAndReturn(run func(spine_goapi.EntityRemoteInterface) (bool, error)) *UCLPCServerInterface_IsUseCaseSupported_Call { _c.Call.Return(run) return _c } -// LoadControlLimit provides a mock function with given fields: -func (_m *UCLPCServerInterface) LoadControlLimit() (cemdapi.LoadLimit, error) { - ret := _m.Called() +// SetConsumptionLimit provides a mock function with given fields: limit +func (_m *UCLPCServerInterface) SetConsumptionLimit(limit api.LoadLimit) error { + ret := _m.Called(limit) if len(ret) == 0 { - panic("no return value specified for LoadControlLimit") - } - - var r0 cemdapi.LoadLimit - var r1 error - if rf, ok := ret.Get(0).(func() (cemdapi.LoadLimit, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() cemdapi.LoadLimit); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(cemdapi.LoadLimit) + panic("no return value specified for SetConsumptionLimit") } - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + var r0 error + if rf, ok := ret.Get(0).(func(api.LoadLimit) error); ok { + r0 = rf(limit) } else { - r1 = ret.Error(1) + r0 = ret.Error(0) } - return r0, r1 + return r0 } -// UCLPCServerInterface_LoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoadControlLimit' -type UCLPCServerInterface_LoadControlLimit_Call struct { +// UCLPCServerInterface_SetConsumptionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetConsumptionLimit' +type UCLPCServerInterface_SetConsumptionLimit_Call struct { *mock.Call } -// LoadControlLimit is a helper method to define mock.On call -func (_e *UCLPCServerInterface_Expecter) LoadControlLimit() *UCLPCServerInterface_LoadControlLimit_Call { - return &UCLPCServerInterface_LoadControlLimit_Call{Call: _e.mock.On("LoadControlLimit")} +// SetConsumptionLimit is a helper method to define mock.On call +// - limit api.LoadLimit +func (_e *UCLPCServerInterface_Expecter) SetConsumptionLimit(limit interface{}) *UCLPCServerInterface_SetConsumptionLimit_Call { + return &UCLPCServerInterface_SetConsumptionLimit_Call{Call: _e.mock.On("SetConsumptionLimit", limit)} } -func (_c *UCLPCServerInterface_LoadControlLimit_Call) Run(run func()) *UCLPCServerInterface_LoadControlLimit_Call { +func (_c *UCLPCServerInterface_SetConsumptionLimit_Call) Run(run func(limit api.LoadLimit)) *UCLPCServerInterface_SetConsumptionLimit_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(api.LoadLimit)) }) return _c } -func (_c *UCLPCServerInterface_LoadControlLimit_Call) Return(_a0 cemdapi.LoadLimit, _a1 error) *UCLPCServerInterface_LoadControlLimit_Call { - _c.Call.Return(_a0, _a1) +func (_c *UCLPCServerInterface_SetConsumptionLimit_Call) Return(resultErr error) *UCLPCServerInterface_SetConsumptionLimit_Call { + _c.Call.Return(resultErr) return _c } -func (_c *UCLPCServerInterface_LoadControlLimit_Call) RunAndReturn(run func() (cemdapi.LoadLimit, error)) *UCLPCServerInterface_LoadControlLimit_Call { +func (_c *UCLPCServerInterface_SetConsumptionLimit_Call) RunAndReturn(run func(api.LoadLimit) error) *UCLPCServerInterface_SetConsumptionLimit_Call { _c.Call.Return(run) return _c } @@ -520,52 +566,6 @@ func (_c *UCLPCServerInterface_SetFailsafeDurationMinimum_Call) RunAndReturn(run return _c } -// SetLoadControlLimit provides a mock function with given fields: limit -func (_m *UCLPCServerInterface) SetLoadControlLimit(limit cemdapi.LoadLimit) error { - ret := _m.Called(limit) - - if len(ret) == 0 { - panic("no return value specified for SetLoadControlLimit") - } - - var r0 error - if rf, ok := ret.Get(0).(func(cemdapi.LoadLimit) error); ok { - r0 = rf(limit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UCLPCServerInterface_SetLoadControlLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLoadControlLimit' -type UCLPCServerInterface_SetLoadControlLimit_Call struct { - *mock.Call -} - -// SetLoadControlLimit is a helper method to define mock.On call -// - limit cemdapi.LoadLimit -func (_e *UCLPCServerInterface_Expecter) SetLoadControlLimit(limit interface{}) *UCLPCServerInterface_SetLoadControlLimit_Call { - return &UCLPCServerInterface_SetLoadControlLimit_Call{Call: _e.mock.On("SetLoadControlLimit", limit)} -} - -func (_c *UCLPCServerInterface_SetLoadControlLimit_Call) Run(run func(limit cemdapi.LoadLimit)) *UCLPCServerInterface_SetLoadControlLimit_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(cemdapi.LoadLimit)) - }) - return _c -} - -func (_c *UCLPCServerInterface_SetLoadControlLimit_Call) Return(resultErr error) *UCLPCServerInterface_SetLoadControlLimit_Call { - _c.Call.Return(resultErr) - return _c -} - -func (_c *UCLPCServerInterface_SetLoadControlLimit_Call) RunAndReturn(run func(cemdapi.LoadLimit) error) *UCLPCServerInterface_SetLoadControlLimit_Call { - _c.Call.Return(run) - return _c -} - // UpdateUseCaseAvailability provides a mock function with given fields: available func (_m *UCLPCServerInterface) UpdateUseCaseAvailability(available bool) { _m.Called(available) diff --git a/uclpc/api.go b/uclpc/api.go index 0391b96..ceeb0f0 100644 --- a/uclpc/api.go +++ b/uclpc/api.go @@ -16,7 +16,7 @@ type UCLPCInterface interface { // Scenario 1 - // return the current loadcontrol limit data + // return the current consumption limit data // // parameters: // - entity: the entity of the e.g. EVSE @@ -27,14 +27,14 @@ type UCLPCInterface interface { // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - LoadControlLimit(entity spineapi.EntityRemoteInterface) (limit api.LoadLimit, resultErr error) + ConsumptionLimit(entity spineapi.EntityRemoteInterface) (limit api.LoadLimit, resultErr error) // send new LoadControlLimits // // parameters: // - entity: the entity of the e.g. EVSE // - limit: load limit data - WriteLoadControlLimit(entity spineapi.EntityRemoteInterface, limit api.LoadLimit) (*model.MsgCounterType, error) + WriteConsumptionLimit(entity spineapi.EntityRemoteInterface, limit api.LoadLimit) (*model.MsgCounterType, error) // Scenario 2 diff --git a/uclpc/public.go b/uclpc/public.go index 08370f7..267b19d 100644 --- a/uclpc/public.go +++ b/uclpc/public.go @@ -25,7 +25,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others -func (e *UCLPC) LoadControlLimit(entity spineapi.EntityRemoteInterface) ( +func (e *UCLPC) ConsumptionLimit(entity spineapi.EntityRemoteInterface) ( limit api.LoadLimit, resultErr error) { limit = api.LoadLimit{ Value: 0.0, @@ -77,7 +77,7 @@ func (e *UCLPC) LoadControlLimit(entity spineapi.EntityRemoteInterface) ( // parameters: // - entity: the entity of the e.g. EVSE // - limit: load limit data -func (e *UCLPC) WriteLoadControlLimit( +func (e *UCLPC) WriteConsumptionLimit( entity spineapi.EntityRemoteInterface, limit api.LoadLimit) (*model.MsgCounterType, error) { if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { diff --git a/uclpc/public_test.go b/uclpc/public_test.go index a449188..2416942 100644 --- a/uclpc/public_test.go +++ b/uclpc/public_test.go @@ -10,13 +10,13 @@ import ( ) func (s *UCLPCSuite) Test_LoadControlLimit() { - data, err := s.sut.LoadControlLimit(s.monitoredEntity) + data, err := s.sut.ConsumptionLimit(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data.Value) assert.Equal(s.T(), false, data.IsChangeable) assert.Equal(s.T(), false, data.IsActive) - data, err = s.sut.LoadControlLimit(s.monitoredEntity) + data, err = s.sut.ConsumptionLimit(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data.Value) assert.Equal(s.T(), false, data.IsChangeable) @@ -38,7 +38,7 @@ func (s *UCLPCSuite) Test_LoadControlLimit() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.LoadControlLimit(s.monitoredEntity) + data, err = s.sut.ConsumptionLimit(s.monitoredEntity) assert.NotNil(s.T(), err) assert.Equal(s.T(), 0.0, data.Value) assert.Equal(s.T(), false, data.IsChangeable) @@ -61,7 +61,7 @@ func (s *UCLPCSuite) Test_LoadControlLimit() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) assert.Nil(s.T(), fErr) - data, err = s.sut.LoadControlLimit(s.monitoredEntity) + data, err = s.sut.ConsumptionLimit(s.monitoredEntity) assert.Nil(s.T(), err) assert.Equal(s.T(), 6000.0, data.Value) assert.Equal(s.T(), true, data.IsChangeable) @@ -74,10 +74,10 @@ func (s *UCLPCSuite) Test_WriteLoadControlLimit() { IsActive: true, Duration: 0, } - _, err := s.sut.WriteLoadControlLimit(s.mockRemoteEntity, limit) + _, err := s.sut.WriteConsumptionLimit(s.mockRemoteEntity, limit) assert.NotNil(s.T(), err) - _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + _, err = s.sut.WriteConsumptionLimit(s.monitoredEntity, limit) assert.NotNil(s.T(), err) descData := &model.LoadControlLimitDescriptionListDataType{ @@ -96,7 +96,7 @@ func (s *UCLPCSuite) Test_WriteLoadControlLimit() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + _, err = s.sut.WriteConsumptionLimit(s.monitoredEntity, limit) assert.NotNil(s.T(), err) limitData := &model.LoadControlLimitListDataType{ @@ -113,11 +113,11 @@ func (s *UCLPCSuite) Test_WriteLoadControlLimit() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) assert.Nil(s.T(), fErr) - _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + _, err = s.sut.WriteConsumptionLimit(s.monitoredEntity, limit) assert.NotNil(s.T(), err) limit.Duration = time.Duration(time.Hour * 2) - _, err = s.sut.WriteLoadControlLimit(s.monitoredEntity, limit) + _, err = s.sut.WriteConsumptionLimit(s.monitoredEntity, limit) assert.NotNil(s.T(), err) } diff --git a/uclpcserver/api.go b/uclpcserver/api.go index 1f4531c..7c24bb5 100644 --- a/uclpcserver/api.go +++ b/uclpcserver/api.go @@ -14,7 +14,7 @@ type UCLPCServerInterface interface { // Scenario 1 - // return the current loadcontrol limit data + // return the current consumption limit data // // return values: // - limit: load limit data @@ -22,10 +22,10 @@ type UCLPCServerInterface interface { // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others - LoadControlLimit() (api.LoadLimit, error) + ConsumptionLimit() (api.LoadLimit, error) // set the current loadcontrol limit data - SetLoadControlLimit(limit api.LoadLimit) (resultErr error) + SetConsumptionLimit(limit api.LoadLimit) (resultErr error) // Scenario 2 diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 8705510..70430f9 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -136,7 +136,7 @@ func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload // the load control limit data was updated func (e *UCLPCServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - if _, err := e.LoadControlLimit(); err != nil { + if _, err := e.ConsumptionLimit(); err != nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) } } diff --git a/uclpcserver/public.go b/uclpcserver/public.go index c652c83..f9ebf40 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -21,7 +21,7 @@ import ( // possible errors: // - ErrDataNotAvailable if no such limit is (yet) available // - and others -func (e *UCLPCServer) LoadControlLimit() (limit api.LoadLimit, resultErr error) { +func (e *UCLPCServer) ConsumptionLimit() (limit api.LoadLimit, resultErr error) { limit = api.LoadLimit{ Value: 0.0, IsChangeable: false, @@ -59,7 +59,7 @@ func (e *UCLPCServer) LoadControlLimit() (limit api.LoadLimit, resultErr error) } // set the current loadcontrol limit data -func (e *UCLPCServer) SetLoadControlLimit(limit api.LoadLimit) (resultErr error) { +func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) { resultErr = eebusapi.ErrDataNotAvailable description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index 87aca1b..d4384b2 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" ) -func (s *UCLPCServerSuite) Test_LoadControlLimit() { - limit, err := s.sut.LoadControlLimit() +func (s *UCLPCServerSuite) Test_ConsumptionLimit() { + limit, err := s.sut.ConsumptionLimit() assert.Equal(s.T(), 0.0, limit.Value) assert.NotNil(s.T(), err) @@ -18,10 +18,10 @@ func (s *UCLPCServerSuite) Test_LoadControlLimit() { IsChangeable: true, Value: 16, } - err = s.sut.SetLoadControlLimit(newLimit) + err = s.sut.SetConsumptionLimit(newLimit) assert.Nil(s.T(), err) - limit, err = s.sut.LoadControlLimit() + limit, err = s.sut.ConsumptionLimit() assert.Equal(s.T(), 16.0, limit.Value) assert.Nil(s.T(), err) } From 02b8f694d48caac691a2f4b4c5672179ecdc012b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 16 Apr 2024 14:29:39 +0200 Subject: [PATCH 197/227] Add UCLPP and UCLPPServer Add implementations for the use case Limitation of Power Production (LPP) as client and server --- .mockery.yaml | 2 + mocks/UCLPPInterface.go | 640 ++++++++++++++++++++++++++++++++ mocks/UCLPPServerInterface.go | 659 +++++++++++++++++++++++++++++++++ uclpp/api.go | 87 +++++ uclpp/events.go | 114 ++++++ uclpp/events_test.go | 84 +++++ uclpp/public.go | 304 +++++++++++++++ uclpp/public_test.go | 355 ++++++++++++++++++ uclpp/testhelper_test.go | 182 +++++++++ uclpp/types.go | 38 ++ uclpp/uclpp.go | 119 ++++++ uclpp/uclpp_test.go | 45 +++ uclppserver/api.go | 80 ++++ uclppserver/events.go | 152 ++++++++ uclppserver/events_test.go | 204 ++++++++++ uclppserver/public.go | 206 +++++++++++ uclppserver/public_test.go | 77 ++++ uclppserver/testhelper_test.go | 197 ++++++++++ uclppserver/types.go | 38 ++ uclppserver/uclpp.go | 208 +++++++++++ uclppserver/uclpp_test.go | 45 +++ 21 files changed, 3836 insertions(+) create mode 100644 mocks/UCLPPInterface.go create mode 100644 mocks/UCLPPServerInterface.go create mode 100644 uclpp/api.go create mode 100644 uclpp/events.go create mode 100644 uclpp/events_test.go create mode 100644 uclpp/public.go create mode 100644 uclpp/public_test.go create mode 100644 uclpp/testhelper_test.go create mode 100644 uclpp/types.go create mode 100644 uclpp/uclpp.go create mode 100644 uclpp/uclpp_test.go create mode 100644 uclppserver/api.go create mode 100644 uclppserver/events.go create mode 100644 uclppserver/events_test.go create mode 100644 uclppserver/public.go create mode 100644 uclppserver/public_test.go create mode 100644 uclppserver/testhelper_test.go create mode 100644 uclppserver/types.go create mode 100644 uclppserver/uclpp.go create mode 100644 uclppserver/uclpp_test.go diff --git a/.mockery.yaml b/.mockery.yaml index 33fe715..29eacfc 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -15,6 +15,8 @@ packages: github.com/enbility/cemd/ucevsoc: github.com/enbility/cemd/uclpc: github.com/enbility/cemd/uclpcserver: + github.com/enbility/cemd/uclpp: + github.com/enbility/cemd/uclppserver: github.com/enbility/cemd/ucmgcp: github.com/enbility/cemd/ucmpc: github.com/enbility/cemd/ucopev: diff --git a/mocks/UCLPPInterface.go b/mocks/UCLPPInterface.go new file mode 100644 index 0000000..fa99ced --- /dev/null +++ b/mocks/UCLPPInterface.go @@ -0,0 +1,640 @@ +// Code generated by mockery v2.42.1. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" + + time "time" +) + +// UCLPPInterface is an autogenerated mock type for the UCLPPInterface type +type UCLPPInterface struct { + mock.Mock +} + +type UCLPPInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCLPPInterface) EXPECT() *UCLPPInterface_Expecter { + return &UCLPPInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCLPPInterface) AddFeatures() { + _m.Called() +} + +// UCLPPInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCLPPInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCLPPInterface_Expecter) AddFeatures() *UCLPPInterface_AddFeatures_Call { + return &UCLPPInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCLPPInterface_AddFeatures_Call) Run(run func()) *UCLPPInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPInterface_AddFeatures_Call) Return() *UCLPPInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLPPInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLPPInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCLPPInterface) AddUseCase() { + _m.Called() +} + +// UCLPPInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCLPPInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCLPPInterface_Expecter) AddUseCase() *UCLPPInterface_AddUseCase_Call { + return &UCLPPInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCLPPInterface_AddUseCase_Call) Run(run func()) *UCLPPInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPInterface_AddUseCase_Call) Return() *UCLPPInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLPPInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLPPInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeDurationMinimum provides a mock function with given fields: entity +func (_m *UCLPPInterface) FailsafeDurationMinimum(entity api.EntityRemoteInterface) (time.Duration, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for FailsafeDurationMinimum") + } + + var r0 time.Duration + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (time.Duration, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) time.Duration); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(time.Duration) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' +type UCLPPInterface_FailsafeDurationMinimum_Call struct { + *mock.Call +} + +// FailsafeDurationMinimum is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLPPInterface_Expecter) FailsafeDurationMinimum(entity interface{}) *UCLPPInterface_FailsafeDurationMinimum_Call { + return &UCLPPInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum", entity)} +} + +func (_c *UCLPPInterface_FailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPPInterface_FailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPPInterface_FailsafeDurationMinimum_Call) Return(_a0 time.Duration, _a1 error) *UCLPPInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface) (time.Duration, error)) *UCLPPInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeProductionActivePowerLimit provides a mock function with given fields: entity +func (_m *UCLPPInterface) FailsafeProductionActivePowerLimit(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for FailsafeProductionActivePowerLimit") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_FailsafeProductionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeProductionActivePowerLimit' +type UCLPPInterface_FailsafeProductionActivePowerLimit_Call struct { + *mock.Call +} + +// FailsafeProductionActivePowerLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLPPInterface_Expecter) FailsafeProductionActivePowerLimit(entity interface{}) *UCLPPInterface_FailsafeProductionActivePowerLimit_Call { + return &UCLPPInterface_FailsafeProductionActivePowerLimit_Call{Call: _e.mock.On("FailsafeProductionActivePowerLimit", entity)} +} + +func (_c *UCLPPInterface_FailsafeProductionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPPInterface_FailsafeProductionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPPInterface_FailsafeProductionActivePowerLimit_Call) Return(_a0 float64, _a1 error) *UCLPPInterface_FailsafeProductionActivePowerLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_FailsafeProductionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLPPInterface_FailsafeProductionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCLPPInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCLPPInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCLPPInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLPPInterface_IsUseCaseSupported_Call { + return &UCLPPInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCLPPInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLPPInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPPInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLPPInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLPPInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// PowerProductionNominalMax provides a mock function with given fields: entity +func (_m *UCLPPInterface) PowerProductionNominalMax(entity api.EntityRemoteInterface) (float64, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for PowerProductionNominalMax") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (float64, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) float64); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_PowerProductionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerProductionNominalMax' +type UCLPPInterface_PowerProductionNominalMax_Call struct { + *mock.Call +} + +// PowerProductionNominalMax is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLPPInterface_Expecter) PowerProductionNominalMax(entity interface{}) *UCLPPInterface_PowerProductionNominalMax_Call { + return &UCLPPInterface_PowerProductionNominalMax_Call{Call: _e.mock.On("PowerProductionNominalMax", entity)} +} + +func (_c *UCLPPInterface_PowerProductionNominalMax_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPPInterface_PowerProductionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPPInterface_PowerProductionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLPPInterface_PowerProductionNominalMax_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_PowerProductionNominalMax_Call) RunAndReturn(run func(api.EntityRemoteInterface) (float64, error)) *UCLPPInterface_PowerProductionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// ProductionLimit provides a mock function with given fields: entity +func (_m *UCLPPInterface) ProductionLimit(entity api.EntityRemoteInterface) (cemdapi.LoadLimit, error) { + ret := _m.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for ProductionLimit") + } + + var r0 cemdapi.LoadLimit + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)); ok { + return rf(entity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) cemdapi.LoadLimit); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(cemdapi.LoadLimit) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_ProductionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ProductionLimit' +type UCLPPInterface_ProductionLimit_Call struct { + *mock.Call +} + +// ProductionLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *UCLPPInterface_Expecter) ProductionLimit(entity interface{}) *UCLPPInterface_ProductionLimit_Call { + return &UCLPPInterface_ProductionLimit_Call{Call: _e.mock.On("ProductionLimit", entity)} +} + +func (_c *UCLPPInterface_ProductionLimit_Call) Run(run func(entity api.EntityRemoteInterface)) *UCLPPInterface_ProductionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPPInterface_ProductionLimit_Call) Return(limit cemdapi.LoadLimit, resultErr error) *UCLPPInterface_ProductionLimit_Call { + _c.Call.Return(limit, resultErr) + return _c +} + +func (_c *UCLPPInterface_ProductionLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface) (cemdapi.LoadLimit, error)) *UCLPPInterface_ProductionLimit_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCLPPInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCLPPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCLPPInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCLPPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLPPInterface_UpdateUseCaseAvailability_Call { + return &UCLPPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCLPPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLPPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCLPPInterface_UpdateUseCaseAvailability_Call) Return() *UCLPPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLPPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLPPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCLPPInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCLPPInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCLPPInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCLPPInterface_Expecter) UseCaseName() *UCLPPInterface_UseCaseName_Call { + return &UCLPPInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCLPPInterface_UseCaseName_Call) Run(run func()) *UCLPPInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLPPInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCLPPInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLPPInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// WriteFailsafeDurationMinimum provides a mock function with given fields: entity, duration +func (_m *UCLPPInterface) WriteFailsafeDurationMinimum(entity api.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) { + ret := _m.Called(entity, duration) + + if len(ret) == 0 { + panic("no return value specified for WriteFailsafeDurationMinimum") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, time.Duration) (*model.MsgCounterType, error)); ok { + return rf(entity, duration) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, time.Duration) *model.MsgCounterType); ok { + r0 = rf(entity, duration) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, time.Duration) error); ok { + r1 = rf(entity, duration) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_WriteFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeDurationMinimum' +type UCLPPInterface_WriteFailsafeDurationMinimum_Call struct { + *mock.Call +} + +// WriteFailsafeDurationMinimum is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - duration time.Duration +func (_e *UCLPPInterface_Expecter) WriteFailsafeDurationMinimum(entity interface{}, duration interface{}) *UCLPPInterface_WriteFailsafeDurationMinimum_Call { + return &UCLPPInterface_WriteFailsafeDurationMinimum_Call{Call: _e.mock.On("WriteFailsafeDurationMinimum", entity, duration)} +} + +func (_c *UCLPPInterface_WriteFailsafeDurationMinimum_Call) Run(run func(entity api.EntityRemoteInterface, duration time.Duration)) *UCLPPInterface_WriteFailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(time.Duration)) + }) + return _c +} + +func (_c *UCLPPInterface_WriteFailsafeDurationMinimum_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPPInterface_WriteFailsafeDurationMinimum_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_WriteFailsafeDurationMinimum_Call) RunAndReturn(run func(api.EntityRemoteInterface, time.Duration) (*model.MsgCounterType, error)) *UCLPPInterface_WriteFailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// WriteFailsafeProductionActivePowerLimit provides a mock function with given fields: entity, value +func (_m *UCLPPInterface) WriteFailsafeProductionActivePowerLimit(entity api.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { + ret := _m.Called(entity, value) + + if len(ret) == 0 { + panic("no return value specified for WriteFailsafeProductionActivePowerLimit") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, float64) (*model.MsgCounterType, error)); ok { + return rf(entity, value) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, float64) *model.MsgCounterType); ok { + r0 = rf(entity, value) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, float64) error); ok { + r1 = rf(entity, value) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteFailsafeProductionActivePowerLimit' +type UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call struct { + *mock.Call +} + +// WriteFailsafeProductionActivePowerLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - value float64 +func (_e *UCLPPInterface_Expecter) WriteFailsafeProductionActivePowerLimit(entity interface{}, value interface{}) *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call { + return &UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call{Call: _e.mock.On("WriteFailsafeProductionActivePowerLimit", entity, value)} +} + +func (_c *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call) Run(run func(entity api.EntityRemoteInterface, value float64)) *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(float64)) + }) + return _c +} + +func (_c *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, float64) (*model.MsgCounterType, error)) *UCLPPInterface_WriteFailsafeProductionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// WriteProductionLimit provides a mock function with given fields: entity, limit +func (_m *UCLPPInterface) WriteProductionLimit(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit) (*model.MsgCounterType, error) { + ret := _m.Called(entity, limit) + + if len(ret) == 0 { + panic("no return value specified for WriteProductionLimit") + } + + var r0 *model.MsgCounterType + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)); ok { + return rf(entity, limit) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) *model.MsgCounterType); ok { + r0 = rf(entity, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.MsgCounterType) + } + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface, cemdapi.LoadLimit) error); ok { + r1 = rf(entity, limit) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPInterface_WriteProductionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteProductionLimit' +type UCLPPInterface_WriteProductionLimit_Call struct { + *mock.Call +} + +// WriteProductionLimit is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - limit cemdapi.LoadLimit +func (_e *UCLPPInterface_Expecter) WriteProductionLimit(entity interface{}, limit interface{}) *UCLPPInterface_WriteProductionLimit_Call { + return &UCLPPInterface_WriteProductionLimit_Call{Call: _e.mock.On("WriteProductionLimit", entity, limit)} +} + +func (_c *UCLPPInterface_WriteProductionLimit_Call) Run(run func(entity api.EntityRemoteInterface, limit cemdapi.LoadLimit)) *UCLPPInterface_WriteProductionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface), args[1].(cemdapi.LoadLimit)) + }) + return _c +} + +func (_c *UCLPPInterface_WriteProductionLimit_Call) Return(_a0 *model.MsgCounterType, _a1 error) *UCLPPInterface_WriteProductionLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPInterface_WriteProductionLimit_Call) RunAndReturn(run func(api.EntityRemoteInterface, cemdapi.LoadLimit) (*model.MsgCounterType, error)) *UCLPPInterface_WriteProductionLimit_Call { + _c.Call.Return(run) + return _c +} + +// NewUCLPPInterface creates a new instance of UCLPPInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCLPPInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCLPPInterface { + mock := &UCLPPInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/UCLPPServerInterface.go b/mocks/UCLPPServerInterface.go new file mode 100644 index 0000000..1d6273f --- /dev/null +++ b/mocks/UCLPPServerInterface.go @@ -0,0 +1,659 @@ +// Code generated by mockery v2.42.1. DO NOT EDIT. + +package mocks + +import ( + cemdapi "github.com/enbility/cemd/api" + api "github.com/enbility/spine-go/api" + + mock "github.com/stretchr/testify/mock" + + model "github.com/enbility/spine-go/model" + + time "time" +) + +// UCLPPServerInterface is an autogenerated mock type for the UCLPPServerInterface type +type UCLPPServerInterface struct { + mock.Mock +} + +type UCLPPServerInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *UCLPPServerInterface) EXPECT() *UCLPPServerInterface_Expecter { + return &UCLPPServerInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function with given fields: +func (_m *UCLPPServerInterface) AddFeatures() { + _m.Called() +} + +// UCLPPServerInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type UCLPPServerInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) AddFeatures() *UCLPPServerInterface_AddFeatures_Call { + return &UCLPPServerInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *UCLPPServerInterface_AddFeatures_Call) Run(run func()) *UCLPPServerInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_AddFeatures_Call) Return() *UCLPPServerInterface_AddFeatures_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLPPServerInterface_AddFeatures_Call) RunAndReturn(run func()) *UCLPPServerInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function with given fields: +func (_m *UCLPPServerInterface) AddUseCase() { + _m.Called() +} + +// UCLPPServerInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type UCLPPServerInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) AddUseCase() *UCLPPServerInterface_AddUseCase_Call { + return &UCLPPServerInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *UCLPPServerInterface_AddUseCase_Call) Run(run func()) *UCLPPServerInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_AddUseCase_Call) Return() *UCLPPServerInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLPPServerInterface_AddUseCase_Call) RunAndReturn(run func()) *UCLPPServerInterface_AddUseCase_Call { + _c.Call.Return(run) + return _c +} + +// ContractualProductionNominalMax provides a mock function with given fields: +func (_m *UCLPPServerInterface) ContractualProductionNominalMax() (float64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ContractualProductionNominalMax") + } + + var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func() (float64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPServerInterface_ContractualProductionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ContractualProductionNominalMax' +type UCLPPServerInterface_ContractualProductionNominalMax_Call struct { + *mock.Call +} + +// ContractualProductionNominalMax is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) ContractualProductionNominalMax() *UCLPPServerInterface_ContractualProductionNominalMax_Call { + return &UCLPPServerInterface_ContractualProductionNominalMax_Call{Call: _e.mock.On("ContractualProductionNominalMax")} +} + +func (_c *UCLPPServerInterface_ContractualProductionNominalMax_Call) Run(run func()) *UCLPPServerInterface_ContractualProductionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_ContractualProductionNominalMax_Call) Return(_a0 float64, _a1 error) *UCLPPServerInterface_ContractualProductionNominalMax_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPServerInterface_ContractualProductionNominalMax_Call) RunAndReturn(run func() (float64, error)) *UCLPPServerInterface_ContractualProductionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeDurationMinimum provides a mock function with given fields: +func (_m *UCLPPServerInterface) FailsafeDurationMinimum() (time.Duration, bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for FailsafeDurationMinimum") + } + + var r0 time.Duration + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func() (time.Duration, bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() time.Duration); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Duration) + } + + if rf, ok := ret.Get(1).(func() bool); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCLPPServerInterface_FailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeDurationMinimum' +type UCLPPServerInterface_FailsafeDurationMinimum_Call struct { + *mock.Call +} + +// FailsafeDurationMinimum is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) FailsafeDurationMinimum() *UCLPPServerInterface_FailsafeDurationMinimum_Call { + return &UCLPPServerInterface_FailsafeDurationMinimum_Call{Call: _e.mock.On("FailsafeDurationMinimum")} +} + +func (_c *UCLPPServerInterface_FailsafeDurationMinimum_Call) Run(run func()) *UCLPPServerInterface_FailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_FailsafeDurationMinimum_Call) Return(duration time.Duration, isChangeable bool, resultErr error) *UCLPPServerInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(duration, isChangeable, resultErr) + return _c +} + +func (_c *UCLPPServerInterface_FailsafeDurationMinimum_Call) RunAndReturn(run func() (time.Duration, bool, error)) *UCLPPServerInterface_FailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// FailsafeProductionActivePowerLimit provides a mock function with given fields: +func (_m *UCLPPServerInterface) FailsafeProductionActivePowerLimit() (float64, bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for FailsafeProductionActivePowerLimit") + } + + var r0 float64 + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func() (float64, bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + if rf, ok := ret.Get(1).(func() bool); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FailsafeProductionActivePowerLimit' +type UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call struct { + *mock.Call +} + +// FailsafeProductionActivePowerLimit is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) FailsafeProductionActivePowerLimit() *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call { + return &UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call{Call: _e.mock.On("FailsafeProductionActivePowerLimit")} +} + +func (_c *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call) Run(run func()) *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call) Return(value float64, isChangeable bool, resultErr error) *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call { + _c.Call.Return(value, isChangeable, resultErr) + return _c +} + +func (_c *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call) RunAndReturn(run func() (float64, bool, error)) *UCLPPServerInterface_FailsafeProductionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// IsUseCaseSupported provides a mock function with given fields: remoteEntity +func (_m *UCLPPServerInterface) IsUseCaseSupported(remoteEntity api.EntityRemoteInterface) (bool, error) { + ret := _m.Called(remoteEntity) + + if len(ret) == 0 { + panic("no return value specified for IsUseCaseSupported") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) (bool, error)); ok { + return rf(remoteEntity) + } + if rf, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = rf(remoteEntity) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(api.EntityRemoteInterface) error); ok { + r1 = rf(remoteEntity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPServerInterface_IsUseCaseSupported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUseCaseSupported' +type UCLPPServerInterface_IsUseCaseSupported_Call struct { + *mock.Call +} + +// IsUseCaseSupported is a helper method to define mock.On call +// - remoteEntity api.EntityRemoteInterface +func (_e *UCLPPServerInterface_Expecter) IsUseCaseSupported(remoteEntity interface{}) *UCLPPServerInterface_IsUseCaseSupported_Call { + return &UCLPPServerInterface_IsUseCaseSupported_Call{Call: _e.mock.On("IsUseCaseSupported", remoteEntity)} +} + +func (_c *UCLPPServerInterface_IsUseCaseSupported_Call) Run(run func(remoteEntity api.EntityRemoteInterface)) *UCLPPServerInterface_IsUseCaseSupported_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(api.EntityRemoteInterface)) + }) + return _c +} + +func (_c *UCLPPServerInterface_IsUseCaseSupported_Call) Return(_a0 bool, _a1 error) *UCLPPServerInterface_IsUseCaseSupported_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPServerInterface_IsUseCaseSupported_Call) RunAndReturn(run func(api.EntityRemoteInterface) (bool, error)) *UCLPPServerInterface_IsUseCaseSupported_Call { + _c.Call.Return(run) + return _c +} + +// ProductionLimit provides a mock function with given fields: +func (_m *UCLPPServerInterface) ProductionLimit() (cemdapi.LoadLimit, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ProductionLimit") + } + + var r0 cemdapi.LoadLimit + var r1 error + if rf, ok := ret.Get(0).(func() (cemdapi.LoadLimit, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() cemdapi.LoadLimit); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(cemdapi.LoadLimit) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UCLPPServerInterface_ProductionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ProductionLimit' +type UCLPPServerInterface_ProductionLimit_Call struct { + *mock.Call +} + +// ProductionLimit is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) ProductionLimit() *UCLPPServerInterface_ProductionLimit_Call { + return &UCLPPServerInterface_ProductionLimit_Call{Call: _e.mock.On("ProductionLimit")} +} + +func (_c *UCLPPServerInterface_ProductionLimit_Call) Run(run func()) *UCLPPServerInterface_ProductionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_ProductionLimit_Call) Return(_a0 cemdapi.LoadLimit, _a1 error) *UCLPPServerInterface_ProductionLimit_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UCLPPServerInterface_ProductionLimit_Call) RunAndReturn(run func() (cemdapi.LoadLimit, error)) *UCLPPServerInterface_ProductionLimit_Call { + _c.Call.Return(run) + return _c +} + +// SetContractualProductionNominalMax provides a mock function with given fields: value +func (_m *UCLPPServerInterface) SetContractualProductionNominalMax(value float64) error { + ret := _m.Called(value) + + if len(ret) == 0 { + panic("no return value specified for SetContractualProductionNominalMax") + } + + var r0 error + if rf, ok := ret.Get(0).(func(float64) error); ok { + r0 = rf(value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLPPServerInterface_SetContractualProductionNominalMax_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetContractualProductionNominalMax' +type UCLPPServerInterface_SetContractualProductionNominalMax_Call struct { + *mock.Call +} + +// SetContractualProductionNominalMax is a helper method to define mock.On call +// - value float64 +func (_e *UCLPPServerInterface_Expecter) SetContractualProductionNominalMax(value interface{}) *UCLPPServerInterface_SetContractualProductionNominalMax_Call { + return &UCLPPServerInterface_SetContractualProductionNominalMax_Call{Call: _e.mock.On("SetContractualProductionNominalMax", value)} +} + +func (_c *UCLPPServerInterface_SetContractualProductionNominalMax_Call) Run(run func(value float64)) *UCLPPServerInterface_SetContractualProductionNominalMax_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(float64)) + }) + return _c +} + +func (_c *UCLPPServerInterface_SetContractualProductionNominalMax_Call) Return(resultErr error) *UCLPPServerInterface_SetContractualProductionNominalMax_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLPPServerInterface_SetContractualProductionNominalMax_Call) RunAndReturn(run func(float64) error) *UCLPPServerInterface_SetContractualProductionNominalMax_Call { + _c.Call.Return(run) + return _c +} + +// SetFailsafeDurationMinimum provides a mock function with given fields: duration, changeable +func (_m *UCLPPServerInterface) SetFailsafeDurationMinimum(duration time.Duration, changeable bool) error { + ret := _m.Called(duration, changeable) + + if len(ret) == 0 { + panic("no return value specified for SetFailsafeDurationMinimum") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Duration, bool) error); ok { + r0 = rf(duration, changeable) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLPPServerInterface_SetFailsafeDurationMinimum_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeDurationMinimum' +type UCLPPServerInterface_SetFailsafeDurationMinimum_Call struct { + *mock.Call +} + +// SetFailsafeDurationMinimum is a helper method to define mock.On call +// - duration time.Duration +// - changeable bool +func (_e *UCLPPServerInterface_Expecter) SetFailsafeDurationMinimum(duration interface{}, changeable interface{}) *UCLPPServerInterface_SetFailsafeDurationMinimum_Call { + return &UCLPPServerInterface_SetFailsafeDurationMinimum_Call{Call: _e.mock.On("SetFailsafeDurationMinimum", duration, changeable)} +} + +func (_c *UCLPPServerInterface_SetFailsafeDurationMinimum_Call) Run(run func(duration time.Duration, changeable bool)) *UCLPPServerInterface_SetFailsafeDurationMinimum_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Duration), args[1].(bool)) + }) + return _c +} + +func (_c *UCLPPServerInterface_SetFailsafeDurationMinimum_Call) Return(resultErr error) *UCLPPServerInterface_SetFailsafeDurationMinimum_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLPPServerInterface_SetFailsafeDurationMinimum_Call) RunAndReturn(run func(time.Duration, bool) error) *UCLPPServerInterface_SetFailsafeDurationMinimum_Call { + _c.Call.Return(run) + return _c +} + +// SetFailsafeProductionActivePowerLimit provides a mock function with given fields: value, changeable +func (_m *UCLPPServerInterface) SetFailsafeProductionActivePowerLimit(value float64, changeable bool) error { + ret := _m.Called(value, changeable) + + if len(ret) == 0 { + panic("no return value specified for SetFailsafeProductionActivePowerLimit") + } + + var r0 error + if rf, ok := ret.Get(0).(func(float64, bool) error); ok { + r0 = rf(value, changeable) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFailsafeProductionActivePowerLimit' +type UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call struct { + *mock.Call +} + +// SetFailsafeProductionActivePowerLimit is a helper method to define mock.On call +// - value float64 +// - changeable bool +func (_e *UCLPPServerInterface_Expecter) SetFailsafeProductionActivePowerLimit(value interface{}, changeable interface{}) *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call { + return &UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call{Call: _e.mock.On("SetFailsafeProductionActivePowerLimit", value, changeable)} +} + +func (_c *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call) Run(run func(value float64, changeable bool)) *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(float64), args[1].(bool)) + }) + return _c +} + +func (_c *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call) Return(resultErr error) *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call) RunAndReturn(run func(float64, bool) error) *UCLPPServerInterface_SetFailsafeProductionActivePowerLimit_Call { + _c.Call.Return(run) + return _c +} + +// SetProductionLimit provides a mock function with given fields: limit +func (_m *UCLPPServerInterface) SetProductionLimit(limit cemdapi.LoadLimit) error { + ret := _m.Called(limit) + + if len(ret) == 0 { + panic("no return value specified for SetProductionLimit") + } + + var r0 error + if rf, ok := ret.Get(0).(func(cemdapi.LoadLimit) error); ok { + r0 = rf(limit) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UCLPPServerInterface_SetProductionLimit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetProductionLimit' +type UCLPPServerInterface_SetProductionLimit_Call struct { + *mock.Call +} + +// SetProductionLimit is a helper method to define mock.On call +// - limit cemdapi.LoadLimit +func (_e *UCLPPServerInterface_Expecter) SetProductionLimit(limit interface{}) *UCLPPServerInterface_SetProductionLimit_Call { + return &UCLPPServerInterface_SetProductionLimit_Call{Call: _e.mock.On("SetProductionLimit", limit)} +} + +func (_c *UCLPPServerInterface_SetProductionLimit_Call) Run(run func(limit cemdapi.LoadLimit)) *UCLPPServerInterface_SetProductionLimit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(cemdapi.LoadLimit)) + }) + return _c +} + +func (_c *UCLPPServerInterface_SetProductionLimit_Call) Return(resultErr error) *UCLPPServerInterface_SetProductionLimit_Call { + _c.Call.Return(resultErr) + return _c +} + +func (_c *UCLPPServerInterface_SetProductionLimit_Call) RunAndReturn(run func(cemdapi.LoadLimit) error) *UCLPPServerInterface_SetProductionLimit_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function with given fields: available +func (_m *UCLPPServerInterface) UpdateUseCaseAvailability(available bool) { + _m.Called(available) +} + +// UCLPPServerInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type UCLPPServerInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *UCLPPServerInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *UCLPPServerInterface_UpdateUseCaseAvailability_Call { + return &UCLPPServerInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *UCLPPServerInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *UCLPPServerInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool)) + }) + return _c +} + +func (_c *UCLPPServerInterface_UpdateUseCaseAvailability_Call) Return() *UCLPPServerInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *UCLPPServerInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *UCLPPServerInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return(run) + return _c +} + +// UseCaseName provides a mock function with given fields: +func (_m *UCLPPServerInterface) UseCaseName() model.UseCaseNameType { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UseCaseName") + } + + var r0 model.UseCaseNameType + if rf, ok := ret.Get(0).(func() model.UseCaseNameType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(model.UseCaseNameType) + } + + return r0 +} + +// UCLPPServerInterface_UseCaseName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UseCaseName' +type UCLPPServerInterface_UseCaseName_Call struct { + *mock.Call +} + +// UseCaseName is a helper method to define mock.On call +func (_e *UCLPPServerInterface_Expecter) UseCaseName() *UCLPPServerInterface_UseCaseName_Call { + return &UCLPPServerInterface_UseCaseName_Call{Call: _e.mock.On("UseCaseName")} +} + +func (_c *UCLPPServerInterface_UseCaseName_Call) Run(run func()) *UCLPPServerInterface_UseCaseName_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UCLPPServerInterface_UseCaseName_Call) Return(_a0 model.UseCaseNameType) *UCLPPServerInterface_UseCaseName_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *UCLPPServerInterface_UseCaseName_Call) RunAndReturn(run func() model.UseCaseNameType) *UCLPPServerInterface_UseCaseName_Call { + _c.Call.Return(run) + return _c +} + +// NewUCLPPServerInterface creates a new instance of UCLPPServerInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUCLPPServerInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *UCLPPServerInterface { + mock := &UCLPPServerInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/uclpp/api.go b/uclpp/api.go new file mode 100644 index 0000000..53c64e9 --- /dev/null +++ b/uclpp/api.go @@ -0,0 +1,87 @@ +package uclpp + +import ( + "time" + + "github.com/enbility/cemd/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +//go:generate mockery + +// interface for the Limitation of Power Production UseCase +type UCLPPInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current production limit data + // + // parameters: + // - entity: the entity of the e.g. EVSE + // + // return values: + // - limit: load limit data + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + ProductionLimit(entity spineapi.EntityRemoteInterface) (limit api.LoadLimit, resultErr error) + + // send new LoadControlLimits + // + // parameters: + // - entity: the entity of the e.g. EVSE + // - limit: load limit data + WriteProductionLimit(entity spineapi.EntityRemoteInterface, limit api.LoadLimit) (*model.MsgCounterType, error) + + // Scenario 2 + + // return Failsafe limit for the produced active (real) power of the + // Controllable System. This limit becomes activated in "init" state or "failsafe state". + // + // parameters: + // - entity: the entity of the e.g. EVSE + // + // return values: + // - positive values are used for production + FailsafeProductionActivePowerLimit(entity spineapi.EntityRemoteInterface) (float64, error) + + // send new Failsafe Production Active Power Limit + // + // parameters: + // - entity: the entity of the e.g. EVSE + // - value: the new limit in W + WriteFailsafeProductionActivePowerLimit(entity spineapi.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) + + // return minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" + // + // parameters: + // - entity: the entity of the e.g. EVSE + // + // return values: + // - negative values are used for production + FailsafeDurationMinimum(entity spineapi.EntityRemoteInterface) (time.Duration, error) + + // send new Failsafe Duration Minimum + // + // parameters: + // - entity: the entity of the e.g. EVSE + // - duration: the duration, between 2h and 24h + WriteFailsafeDurationMinimum(entity spineapi.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) + + // Scenario 3 + + // this is automatically covered by the SPINE implementation + + // Scenario 4 + + // return nominal maximum active (real) power the Controllable System is + // able to produce according to the device label or data sheet. + // + // parameters: + // - entity: the entity of the e.g. EVSE + PowerProductionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) +} diff --git a/uclpp/events.go b/uclpp/events.go new file mode 100644 index 0000000..fd81811 --- /dev/null +++ b/uclpp/events.go @@ -0,0 +1,114 @@ +package uclpp + +import ( + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCLPP) HandleEvent(payload spineapi.EventPayload) { + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { + return + } + + if util.IsEntityConnected(payload) { + e.connected(payload.Entity) + return + } + + if payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate { + return + } + + switch payload.Data.(type) { + case *model.LoadControlLimitDescriptionListDataType: + e.loadControlLimitDescriptionDataUpdate(payload.Entity) + case *model.LoadControlLimitListDataType: + e.loadControlLimitDataUpdate(payload) + case *model.DeviceConfigurationKeyValueDescriptionListDataType: + e.configurationDescriptionDataUpdate(payload.Entity) + case *model.DeviceConfigurationKeyValueListDataType: + e.configurationDataUpdate(payload) + } +} + +// the remote entity was connected +func (e *UCLPP) connected(entity spineapi.EntityRemoteInterface) { + // initialise features, e.g. subscriptions, descriptions + if loadControl, err := util.LoadControl(e.service, entity); err == nil { + if _, err := loadControl.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + // get descriptions + if _, err := loadControl.RequestLimitDescriptions(); err != nil { + logging.Log().Debug(err) + } + } + + if localDeviceDiag, err := util.DeviceDiagnosis(e.service, entity); err == nil { + if _, err := localDeviceDiag.Subscribe(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit description data was updated +func (e *UCLPP) loadControlLimitDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if loadControl, err := util.LoadControl(e.service, entity); err == nil { + // get values + if _, err := loadControl.RequestLimitValues(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit data was updated +func (e *UCLPP) loadControlLimitDataUpdate(payload spineapi.EventPayload) { + loadControl, err := util.LoadControl(e.service, payload.Entity) + if err != nil { + return + } + + data, err := loadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) + if err != nil { + return + } + + for _, item := range data { + if item.LimitId == nil { + continue + } + + _, err := loadControl.GetLimitValueForLimitId(*item.LimitId) + if err != nil { + continue + } + + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) + return + } +} + +// the configuration key description data was updated +func (e *UCLPP) configurationDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { + if deviceConfiguration, err := util.DeviceConfiguration(e.service, entity); err == nil { + // key value descriptions received, now get the data + if _, err := deviceConfiguration.RequestKeyValues(); err != nil { + logging.Log().Error("Error getting configuration key values:", err) + } + } +} + +// the configuration key data was updated +func (e *UCLPP) configurationDataUpdate(payload spineapi.EventPayload) { + if _, err := e.FailsafeProductionActivePowerLimit(payload.Entity); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeProductionActivePowerLimit) + } + if _, err := e.FailsafeDurationMinimum(payload.Entity); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) + } +} diff --git a/uclpp/events_test.go b/uclpp/events_test.go new file mode 100644 index 0000000..b51014a --- /dev/null +++ b/uclpp/events_test.go @@ -0,0 +1,84 @@ +package uclpp + +import ( + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPPSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Entity = s.monitoredEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Data = eebusutil.Ptr(model.LoadControlLimitDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueDescriptionListDataType{}) + s.sut.HandleEvent(payload) + + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) +} + +func (s *UCLPPSuite) Test_Failures() { + s.sut.connected(s.mockRemoteEntity) + + s.sut.configurationDescriptionDataUpdate(s.mockRemoteEntity) +} + +func (s *UCLPPSuite) Test_loadControlLimitDataUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.loadControlLimitDataUpdate(payload) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.loadControlLimitDataUpdate(payload) + + data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.loadControlLimitDataUpdate(payload) +} diff --git a/uclpp/public.go b/uclpp/public.go new file mode 100644 index 0000000..49e43c1 --- /dev/null +++ b/uclpp/public.go @@ -0,0 +1,304 @@ +package uclpp + +import ( + "errors" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// Scenario 1 + +// return the current loadcontrol limit data +// +// parameters: +// - entity: the entity of the e.g. EVSE +// +// return values: +// - limit: load limit data +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCLPP) ProductionLimit(entity spineapi.EntityRemoteInterface) ( + limit api.LoadLimit, resultErr error) { + limit = api.LoadLimit{ + Value: 0.0, + IsChangeable: false, + IsActive: false, + } + + resultErr = api.ErrNoCompatibleEntity + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return + } + + resultErr = eebusapi.ErrDataNotAvailable + loadControl, err := util.LoadControl(e.service, entity) + if err != nil || loadControl == nil { + return + } + + limitDescriptions, err := loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit) + if err != nil || len(limitDescriptions) != 1 { + return + } + + value, err := loadControl.GetLimitValueForLimitId(*limitDescriptions[0].LimitId) + if err != nil || value.Value == nil { + return + } + + limit.Value = value.Value.GetValue() + limit.IsChangeable = (value.IsLimitChangeable != nil && *value.IsLimitChangeable) + limit.IsActive = (value.IsLimitActive != nil && *value.IsLimitActive) + if value.TimePeriod != nil && value.TimePeriod.EndTime != nil { + if duration, err := value.TimePeriod.EndTime.GetTimeDuration(); err == nil { + limit.Duration = duration + } + } + + resultErr = nil + + return +} + +// send new LoadControlLimits +// +// parameters: +// - entity: the entity of the e.g. EVSE +// - limit: load limit data +func (e *UCLPP) WriteProductionLimit( + entity spineapi.EntityRemoteInterface, + limit api.LoadLimit) (*model.MsgCounterType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + loadControl, err := util.LoadControl(e.service, entity) + if err != nil { + return nil, api.ErrNoCompatibleEntity + } + + var limitData []model.LoadControlLimitDataType + + limitDescriptions, err := loadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit, + ) + if err != nil || + len(limitDescriptions) != 1 || + limitDescriptions[0].LimitId == nil { + return nil, eebusapi.ErrMetadataNotAvailable + } + + limitDesc := limitDescriptions[0] + + if _, err := loadControl.GetLimitValueForLimitId(*limitDesc.LimitId); err != nil { + return nil, eebusapi.ErrDataNotAvailable + } + + currentLimits, err := loadControl.GetLimitValues() + if err != nil { + return nil, eebusapi.ErrDataNotAvailable + } + + for index, item := range currentLimits { + if item.LimitId == nil || + *item.LimitId != *limitDesc.LimitId { + continue + } + + // EEBus_UC_TS_LimitationOfPowerProduction V1.0.0 3.2.2.2.2.2 + // If set to "true", the timePeriod, value and isLimitActive Elements SHALL be writeable by a client. + if item.IsLimitChangeable != nil && !*item.IsLimitChangeable { + return nil, eebusapi.ErrNotSupported + } + + newLimit := model.LoadControlLimitDataType{ + LimitId: limitDesc.LimitId, + IsLimitActive: eebusutil.Ptr(limit.IsActive), + Value: model.NewScaledNumberType(limit.Value), + } + if limit.Duration > 0 { + newLimit.TimePeriod = &model.TimePeriodType{ + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(limit.Duration), + } + } + + currentLimits[index] = newLimit + break + } + + msgCounter, err := loadControl.WriteLimitValues(limitData) + + return msgCounter, err +} + +// Scenario 2 + +// return Failsafe limit for the produced active (real) power of the +// Controllable System. This limit becomes activated in "init" state or "failsafe state". +func (e *UCLPP) FailsafeProductionActivePowerLimit(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetKeyValueForKeyName(keyname, model.DeviceConfigurationKeyValueTypeTypeScaledNumber) + if err != nil || data == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + value, ok := data.(*model.ScaledNumberType) + if !ok || value == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + return value.GetValue(), nil +} + +// send new Failsafe Production Active Power Limit +// +// parameters: +// - entity: the entity of the e.g. EVSE +// - value: the new limit in W +func (e *UCLPP) WriteFailsafeProductionActivePowerLimit(entity spineapi.EntityRemoteInterface, value float64) (*model.MsgCounterType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetDescriptionForKeyName(keyname) + if err != nil || data == nil || data.KeyId == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + keyData := []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: data.KeyId, + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(value), + }, + }, + } + + msgCounter, err := deviceConfiguration.WriteKeyValues(keyData) + + return msgCounter, err +} + +// return minimum time the Controllable System remains in "failsafe state" unless conditions +// specified in this Use Case permit leaving the "failsafe state" +func (e *UCLPP) FailsafeDurationMinimum(entity spineapi.EntityRemoteInterface) (time.Duration, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetKeyValueForKeyName(keyname, model.DeviceConfigurationKeyValueTypeTypeDuration) + if err != nil || data == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + value, ok := data.(*model.DurationType) + if !ok || value == nil { + return 0, eebusapi.ErrDataNotAvailable + } + + return value.GetTimeDuration() +} + +// send new Failsafe Duration Minimum +// +// parameters: +// - entity: the entity of the e.g. EVSE +// - duration: the duration, between 2h and 24h +func (e *UCLPP) WriteFailsafeDurationMinimum(entity spineapi.EntityRemoteInterface, duration time.Duration) (*model.MsgCounterType, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return nil, api.ErrNoCompatibleEntity + } + + if duration < time.Duration(time.Hour*2) || duration > time.Duration(time.Hour*24) { + return nil, errors.New("duration outside of allowed range") + } + + keyname := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + + deviceConfiguration, err := util.DeviceConfiguration(e.service, entity) + if err != nil || deviceConfiguration == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + data, err := deviceConfiguration.GetDescriptionForKeyName(keyname) + if err != nil || data == nil || data.KeyId == nil { + return nil, eebusapi.ErrDataNotAvailable + } + + keyData := []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: data.KeyId, + Value: &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(duration), + }, + }, + } + + msgCounter, err := deviceConfiguration.WriteKeyValues(keyData) + + return msgCounter, err +} + +// Scenario 4 + +// return nominal maximum active (real) power the Controllable System is +// able to produce according to the device label or data sheet. +func (e *UCLPP) PowerProductionNominalMax(entity spineapi.EntityRemoteInterface) (float64, error) { + if entity == nil || !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return 0, api.ErrNoCompatibleEntity + } + + electricalConnection, err := util.ElectricalConnection(e.service, entity) + if err != nil || electricalConnection == nil { + return 0, err + } + + data, err := electricalConnection.GetCharacteristicForContextType( + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypePowerProductionNominalMax, + ) + if err != nil || data.Value == nil { + return 0, err + } + + return data.Value.GetValue(), nil +} diff --git a/uclpp/public_test.go b/uclpp/public_test.go new file mode 100644 index 0000000..9b31d19 --- /dev/null +++ b/uclpp/public_test.go @@ -0,0 +1,355 @@ +package uclpp + +import ( + "time" + + "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPPSuite) Test_LoadControlLimit() { + data, err := s.sut.ProductionLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data.Value) + assert.Equal(s.T(), false, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) + + data, err = s.sut.ProductionLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data.Value) + assert.Equal(s.T(), false, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.ProductionLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data.Value) + assert.Equal(s.T(), false, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitChangeable: eebusutil.Ptr(true), + IsLimitActive: eebusutil.Ptr(false), + Value: model.NewScaledNumberType(6000), + TimePeriod: &model.TimePeriodType{ + EndTime: model.NewAbsoluteOrRelativeTimeType("PT2H"), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.ProductionLimit(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 6000.0, data.Value) + assert.Equal(s.T(), true, data.IsChangeable) + assert.Equal(s.T(), false, data.IsActive) +} + +func (s *UCLPPSuite) Test_WriteLoadControlLimit() { + limit := api.LoadLimit{ + Value: 6000, + IsActive: true, + Duration: 0, + } + _, err := s.sut.WriteProductionLimit(s.mockRemoteEntity, limit) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteProductionLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteProductionLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitChangeable: eebusutil.Ptr(true), + IsLimitActive: eebusutil.Ptr(false), + Value: model.NewScaledNumberType(6000), + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteProductionLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) + + limit.Duration = time.Duration(time.Hour * 2) + _, err = s.sut.WriteProductionLimit(s.monitoredEntity, limit) + assert.NotNil(s.T(), err) +} + +func (s *UCLPPSuite) Test_FailsafeProductionActivePowerLimit() { + data, err := s.sut.FailsafeProductionActivePowerLimit(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.FailsafeProductionActivePowerLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeProductionActivePowerLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeProductionActivePowerLimit(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + keyData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(4000), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeProductionActivePowerLimit(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 4000.0, data) +} + +func (s *UCLPPSuite) Test_WriteFailsafeProductionActivePowerLimit() { + _, err := s.sut.WriteFailsafeProductionActivePowerLimit(s.mockRemoteEntity, 6000) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteFailsafeProductionActivePowerLimit(s.monitoredEntity, 6000) + assert.NotNil(s.T(), err) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeProductionActivePowerLimit(s.monitoredEntity, 6000) + assert.Nil(s.T(), err) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeProductionActivePowerLimit(s.monitoredEntity, 6000) + assert.Nil(s.T(), err) +} + +func (s *UCLPPSuite) Test_FailsafeDurationMinimum() { + data, err := s.sut.FailsafeDurationMinimum(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), time.Duration(0), data) + + keyData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(time.Hour * 2), + }, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.FailsafeDurationMinimum(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), time.Duration(time.Hour*2), data) +} + +func (s *UCLPPSuite) Test_WriteFailsafeDurationMinimum() { + _, err := s.sut.WriteFailsafeDurationMinimum(s.mockRemoteEntity, time.Duration(time.Hour*2)) + assert.NotNil(s.T(), err) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*2)) + assert.NotNil(s.T(), err) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*2)) + assert.Nil(s.T(), err) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, keyData, nil, nil) + assert.Nil(s.T(), fErr) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*2)) + assert.Nil(s.T(), err) + + _, err = s.sut.WriteFailsafeDurationMinimum(s.monitoredEntity, time.Duration(time.Hour*1)) + assert.NotNil(s.T(), err) +} + +func (s *UCLPPSuite) Test_PowerProductionNominalMax() { + data, err := s.sut.PowerProductionNominalMax(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + data, err = s.sut.PowerProductionNominalMax(s.monitoredEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), 0.0, data) + + charData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + CharacteristicId: eebusutil.Ptr(model.ElectricalConnectionCharacteristicIdType(0)), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypePowerProductionNominalMax), + Value: model.NewScaledNumberType(8000), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionCharacteristicListData, charData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.PowerProductionNominalMax(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 8000.0, data) +} diff --git a/uclpp/testhelper_test.go b/uclpp/testhelper_test.go new file mode 100644 index 0000000..dd0a80c --- /dev/null +++ b/uclpp/testhelper_test.go @@ -0,0 +1,182 @@ +package uclpp + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestLPPSuite(t *testing.T) { + suite.Run(t, new(UCLPPSuite)) +} + +type UCLPPSuite struct { + suite.Suite + + sut *UCLPP + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface +} + +func (s *UCLPPSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} + +func (s *UCLPPSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() + + s.sut = NewUCLPP(s.service, s.Event) + s.sut.AddFeatures() + s.sut.AddUseCase() + + s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeLoadControlLimitDescriptionListData, + model.FunctionTypeLoadControlLimitListData, + }, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisHeartbeatData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeElectricalConnectionCharacteristicListData, + }, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeEVSE), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/uclpp/types.go b/uclpp/types.go new file mode 100644 index 0000000..f54c515 --- /dev/null +++ b/uclpp/types.go @@ -0,0 +1,38 @@ +package uclpp + +import "github.com/enbility/cemd/api" + +const ( + // Load control obligation limit data updated + // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // + // Use Case LPC, Scenario 1 + DataUpdateLimit api.EventType = "DataUpdateLimit" + + // Failsafe limit for the produced active (real) power of the + // Controllable System data updated + // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeProductionActivePowerLimit api.EventType = "DataUpdateFailsafeProductionActivePowerLimit" + + // Minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" data updated + // + // The callback with this message provides: + // - the device of the e.g. EVSE + // - the entity of the e.g. EVSE + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" +) diff --git a/uclpp/uclpp.go b/uclpp/uclpp.go new file mode 100644 index 0000000..798faf5 --- /dev/null +++ b/uclpp/uclpp.go @@ -0,0 +1,119 @@ +package uclpp + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCLPP struct { + service eebusapi.ServiceInterface + + eventCB api.EntityEventCallback + + validEntityTypes []model.EntityTypeType +} + +var _ UCLPPInterface = (*UCLPP)(nil) + +func NewUCLPP(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPP { + uc := &UCLPP{ + service: service, + eventCB: eventCB, + } + + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeEVSE, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCLPP) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeLimitationOfPowerProduction +} + +func (e *UCLPP) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + var clientFeatures = []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceDiagnosis, + model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeDeviceConfiguration, + model.FeatureTypeTypeElectricalConnection, + } + for _, feature := range clientFeatures { + _ = localEntity.GetOrAddFeature(feature, model.RoleTypeClient) + } + + // server features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) +} + +func (e *UCLPP) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeEnergyGuard, + e.UseCaseName(), + model.SpecificationVersionType("1.0.0"), + "release", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4}) +} + +func (e *UCLPP) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeEnergyGuard, e.UseCaseName(), available) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCLPP) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEnergyGuard, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceDiagnosis, + model.FeatureTypeTypeLoadControl, + model.FeatureTypeTypeDeviceConfiguration, + }, + ) { + return false, nil + } + + if _, err := util.DeviceDiagnosis(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + if _, err := util.LoadControl(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + if _, err := util.DeviceConfiguration(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + return true, nil +} diff --git a/uclpp/uclpp_test.go b/uclpp/uclpp_test.go new file mode 100644 index 0000000..0c90cec --- /dev/null +++ b/uclpp/uclpp_test.go @@ -0,0 +1,45 @@ +package uclpp + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPPSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + +func (s *UCLPPSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEnergyGuard), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeLimitationOfPowerProduction), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} diff --git a/uclppserver/api.go b/uclppserver/api.go new file mode 100644 index 0000000..8df3440 --- /dev/null +++ b/uclppserver/api.go @@ -0,0 +1,80 @@ +package uclppserver + +import ( + "time" + + "github.com/enbility/cemd/api" +) + +//go:generate mockery + +// interface for the Limitation of Power Production UseCase +type UCLPPServerInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the current loadcontrol limit data + // + // return values: + // - limit: load limit data + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + ProductionLimit() (api.LoadLimit, error) + + // set the current loadcontrol limit data + SetProductionLimit(limit api.LoadLimit) (resultErr error) + + // Scenario 2 + + // return Failsafe limit for the produced active (real) power of the + // Controllable System. This limit becomes activated in "init" state or "failsafe state". + // + // return values: + // - value: the power limit in W + // - changeable: boolean if the client service can change the limit + FailsafeProductionActivePowerLimit() (value float64, isChangeable bool, resultErr error) + + // set Failsafe limit for the produced active (real) power of the + // Controllable System. This limit becomes activated in "init" state or "failsafe state". + // + // parameters: + // - value: the power limit in W + // - changeable: boolean if the client service can change the limit + SetFailsafeProductionActivePowerLimit(value float64, changeable bool) (resultErr error) + + // return minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" + // + // return values: + // - value: the power limit in W + // - changeable: boolean if the client service can change the limit + FailsafeDurationMinimum() (duration time.Duration, isChangeable bool, resultErr error) + + // set minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" + // + // parameters: + // - duration: has to be >= 2h and <= 24h + // - changeable: boolean if the client service can change this value + SetFailsafeDurationMinimum(duration time.Duration, changeable bool) (resultErr error) + + // Scenario 3 + + // this is automatically covered by the SPINE implementation + + // Scenario 4 + + // return nominal maximum active (real) power the Controllable System is + // allowed to produce due to the customer's contract. + ContractualProductionNominalMax() (float64, error) + + // set nominal maximum active (real) power the Controllable System is + // allowed to produce due to the customer's contract. + // + // parameters: + // - value: contractual nominal max power production in W + SetContractualProductionNominalMax(value float64) (resultErr error) +} diff --git a/uclppserver/events.go b/uclppserver/events.go new file mode 100644 index 0000000..49ecba5 --- /dev/null +++ b/uclppserver/events.go @@ -0,0 +1,152 @@ +package uclppserver + +import ( + "slices" + + "github.com/enbility/cemd/util" + "github.com/enbility/ship-go/logging" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +// handle SPINE events +func (e *UCLPPServer) HandleEvent(payload spineapi.EventPayload) { + if util.IsDeviceConnected(payload) { + e.deviceConnected(payload) + return + } + + if !util.IsCompatibleEntity(payload.Entity, e.validEntityTypes) { + return + } + + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // did we receive a binding to the loadControl server and the + // heartbeatWorkaround is required? + if payload.EventType == spineapi.EventTypeBindingChange && + payload.ChangeType == spineapi.ElementChangeAdd && + payload.LocalFeature != nil && + payload.LocalFeature.Type() == model.FeatureTypeTypeLoadControl && + payload.LocalFeature.Role() == model.RoleTypeServer { + e.subscribeHeartbeatWorkaround(payload) + return + } + + if localEntity == nil || + payload.EventType != spineapi.EventTypeDataChange || + payload.ChangeType != spineapi.ElementChangeUpdate || + payload.CmdClassifier == nil || + *payload.CmdClassifier != model.CmdClassifierTypeWrite { + return + } + + // the codefactor warning is invalid, as .(type) check can not be replaced with if then + //revive:disable-next-line + switch payload.Data.(type) { + case *model.LoadControlLimitListDataType: + serverF := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + + if payload.Function != model.FunctionTypeLoadControlLimitListData || + payload.LocalFeature != serverF { + return + } + + e.loadControlLimitDataUpdate(payload) + case *model.DeviceConfigurationKeyValueListDataType: + serverF := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + if payload.Function != model.FunctionTypeDeviceConfigurationKeyValueListData || + payload.LocalFeature != serverF { + return + } + + e.configurationDataUpdate(payload) + } +} + +// a remote device was connected and we know its entities +func (e *UCLPPServer) deviceConnected(payload spineapi.EventPayload) { + if payload.Device == nil { + return + } + + // check if there is a DeviceDiagnosis server on one or more entities + remoteDevice := payload.Device + + var deviceDiagEntites []spineapi.EntityRemoteInterface + + entites := remoteDevice.Entities() + for _, entity := range entites { + if !slices.Contains(e.validEntityTypes, entity.EntityType()) { + continue + } + + deviceDiagF := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if deviceDiagF == nil { + continue + } + + deviceDiagEntites = append(deviceDiagEntites, entity) + } + + // the remote device does not have a DeviceDiagnosis Server, which it should + if len(deviceDiagEntites) == 0 { + return + } + + // we only found one matching entity, as it should be, subscribe + if len(deviceDiagEntites) == 1 { + if localDeviceDiag, err := util.DeviceDiagnosis(e.service, deviceDiagEntites[0]); err == nil { + if _, err := localDeviceDiag.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + if _, err := localDeviceDiag.RequestHeartbeat(); err != nil { + logging.Log().Debug(err) + } + } + + return + } + + // we found more than one matching entity, this is not good + // according to KEO the subscription should be done on the entity that requests a binding to + // the local loadControlLimit server feature + e.heartbeatKeoWorkaround = true +} + +// subscribe to the DeviceDiagnosis Server of the entity that created a binding +func (e *UCLPPServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload) { + // the workaround is not needed, exit + if !e.heartbeatKeoWorkaround { + return + } + + if localDeviceDiag, err := util.DeviceDiagnosis(e.service, payload.Entity); err == nil { + if _, err := localDeviceDiag.Subscribe(); err != nil { + logging.Log().Debug(err) + } + + if _, err := localDeviceDiag.RequestHeartbeat(); err != nil { + logging.Log().Debug(err) + } + } +} + +// the load control limit data was updated +func (e *UCLPPServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { + if _, err := e.ProductionLimit(); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) + } +} + +// the configuration key data of an SMGW was updated +func (e *UCLPPServer) configurationDataUpdate(payload spineapi.EventPayload) { + if _, _, err := e.FailsafeProductionActivePowerLimit(); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeProductionActivePowerLimit) + } + if _, _, err := e.FailsafeDurationMinimum(); err != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) + } +} diff --git a/uclppserver/events_test.go b/uclppserver/events_test.go new file mode 100644 index 0000000..3307de5 --- /dev/null +++ b/uclppserver/events_test.go @@ -0,0 +1,204 @@ +package uclppserver + +import ( + "fmt" + + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" +) + +func (s *UCLPPServerSuite) Test_Events() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + s.sut.HandleEvent(payload) + + payload.Device = s.monitoredEntity.Device() + payload.Entity = s.monitoredEntity + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDeviceChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeEntityChange + payload.ChangeType = spineapi.ElementChangeAdd + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.CmdClassifier = eebusutil.Ptr(model.CmdClassifierTypeWrite) + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeDataChange + payload.ChangeType = spineapi.ElementChangeUpdate + payload.Function = model.FunctionTypeLoadControlLimitListData + payload.Data = eebusutil.Ptr(model.LoadControlLimitListDataType{}) + s.sut.HandleEvent(payload) + + payload.LocalFeature = s.loadControlFeature + s.sut.HandleEvent(payload) + + payload.Function = model.FunctionTypeDeviceConfigurationKeyValueListData + payload.Data = eebusutil.Ptr(model.DeviceConfigurationKeyValueListDataType{}) + s.sut.HandleEvent(payload) + + payload.LocalFeature = s.deviceConfigurationFeature + s.sut.HandleEvent(payload) + + payload.EventType = spineapi.EventTypeBindingChange + payload.ChangeType = spineapi.ElementChangeAdd + payload.LocalFeature = s.loadControlFeature + s.sut.HandleEvent(payload) +} + +func (s *UCLPPServerSuite) Test_deviceConnected() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + + s.sut.deviceConnected(payload) + + // no entities + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().Entities().Return(nil) + payload.Device = mockRemoteDevice + s.sut.deviceConnected(payload) + + // one entity with one DeviceDiagnosis server + payload.Device = s.remoteDevice + s.sut.deviceConnected(payload) + + s.sut.subscribeHeartbeatWorkaround(payload) +} + +func (s *UCLPPServerSuite) Test_multipleDeviceDiagServer() { + // multiple entities each with DeviceDiagnosis server + + payload := spineapi.EventPayload{ + Device: s.remoteDevice, + Entity: s.mockRemoteEntity, + } + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisHeartbeatData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeClient, + []model.FunctionType{}, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + // 4 entites + for i := 1; i < 5; i++ { + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{model.AddressEntityType(i)}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{2}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{3}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{4}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeCEM), + }, + }, + }, + FeatureInformation: featureInformations, + } + + _, err := s.remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + s.remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + s.sut.deviceConnected(payload) + + s.sut.subscribeHeartbeatWorkaround(payload) +} diff --git a/uclppserver/public.go b/uclppserver/public.go new file mode 100644 index 0000000..5cbab72 --- /dev/null +++ b/uclppserver/public.go @@ -0,0 +1,206 @@ +package uclppserver + +import ( + "errors" + "time" + + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" +) + +// Scenario 1 + +// return the current production limit data +// +// return values: +// - limit: load limit data +// +// possible errors: +// - ErrDataNotAvailable if no such limit is (yet) available +// - and others +func (e *UCLPPServer) ProductionLimit() (limit api.LoadLimit, resultErr error) { + limit = api.LoadLimit{ + Value: 0.0, + IsChangeable: false, + IsActive: false, + Duration: 0, + } + resultErr = eebusapi.ErrDataNotAvailable + + description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit, + ) + if description.LimitId == nil { + return + } + + value := util.GetLocalLimitValueForLimitId(e.service, *description.LimitId) + if value.LimitId == nil || value.Value == nil { + return + } + + limit.Value = value.Value.GetValue() + limit.IsChangeable = (value.IsLimitChangeable != nil && *value.IsLimitChangeable) + limit.IsActive = (value.IsLimitActive != nil && *value.IsLimitActive) + if value.TimePeriod != nil && value.TimePeriod.EndTime != nil { + if duration, err := value.TimePeriod.EndTime.GetTimeDuration(); err == nil { + limit.Duration = duration + } + } + + return limit, nil +} + +// set the current production limit data +func (e *UCLPPServer) SetProductionLimit(limit api.LoadLimit) (resultErr error) { + resultErr = eebusapi.ErrDataNotAvailable + + description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit, + ) + if description.LimitId == nil { + return + } + + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + loadControl := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + if loadControl == nil { + return + } + + limitData := model.LoadControlLimitDataType{ + LimitId: description.LimitId, + IsLimitChangeable: eebusutil.Ptr(limit.IsChangeable), + IsLimitActive: eebusutil.Ptr(limit.IsActive), + Value: model.NewScaledNumberType(limit.Value), + } + if limit.Duration > 0 { + limitData.TimePeriod = &model.TimePeriodType{ + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(limit.Duration), + } + } + limits := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{limitData}, + } + + loadControl.SetData(model.FunctionTypeLoadControlLimitListData, limits) + + return nil +} + +// Scenario 2 + +// return Failsafe limit for the produced active (real) power of the +// Controllable System. This limit becomes activated in "init" state or "failsafe state". +func (e *UCLPPServer) FailsafeProductionActivePowerLimit() (limit float64, isChangeable bool, resultErr error) { + limit = 0 + isChangeable = false + resultErr = eebusapi.ErrDataNotAvailable + + keyName := model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit + keyData := util.GetLocalDeviceConfigurationKeyValueForKeyName(e.service, keyName) + if keyData.KeyId == nil || keyData.Value == nil || keyData.Value.ScaledNumber == nil { + return + } + + limit = keyData.Value.ScaledNumber.GetValue() + isChangeable = (keyData.IsValueChangeable != nil && *keyData.IsValueChangeable) + resultErr = nil + return +} + +// set Failsafe limit for the produced active (real) power of the +// Controllable System. This limit becomes activated in "init" state or "failsafe state". +func (e *UCLPPServer) SetFailsafeProductionActivePowerLimit(value float64, changeable bool) error { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit + keyValue := model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(value), + } + + return util.SetLocalDeviceConfigurationKeyValue(e.service, keyName, changeable, keyValue) +} + +// return minimum time the Controllable System remains in "failsafe state" unless conditions +// specified in this Use Case permit leaving the "failsafe state" +func (e *UCLPPServer) FailsafeDurationMinimum() (duration time.Duration, isChangeable bool, resultErr error) { + duration = 0 + isChangeable = false + resultErr = eebusapi.ErrDataNotAvailable + + keyName := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + keyData := util.GetLocalDeviceConfigurationKeyValueForKeyName(e.service, keyName) + if keyData.KeyId == nil || keyData.Value == nil || keyData.Value.Duration == nil { + return + } + + durationValue, err := keyData.Value.Duration.GetTimeDuration() + if err != nil { + return + } + + duration = durationValue + isChangeable = (keyData.IsValueChangeable != nil && *keyData.IsValueChangeable) + resultErr = nil + return +} + +// set minimum time the Controllable System remains in "failsafe state" unless conditions +// specified in this Use Case permit leaving the "failsafe state" +// +// parameters: +// - duration: has to be >= 2h and <= 24h +// - changeable: boolean if the client service can change this value +func (e *UCLPPServer) SetFailsafeDurationMinimum(duration time.Duration, changeable bool) error { + if duration < time.Duration(time.Hour*2) || duration > time.Duration(time.Hour*24) { + return errors.New("duration outside of allowed range") + } + keyName := model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum + keyValue := model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(duration), + } + + return util.SetLocalDeviceConfigurationKeyValue(e.service, keyName, changeable, keyValue) +} + +// Scenario 4 + +// return nominal maximum active (real) power the Controllable System is +// allowed to produce due to the customer's contract. +func (e *UCLPPServer) ContractualProductionNominalMax() (value float64, resultErr error) { + value = 0 + resultErr = eebusapi.ErrDataNotAvailable + + charData := util.GetLocalElectricalConnectionCharacteristicForContextType( + e.service, + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeContractualProductionNominalMax, + ) + if charData.CharacteristicId == nil || charData.Value == nil { + return + } + + return charData.Value.GetValue(), nil +} + +// set nominal maximum active (real) power the Controllable System is +// allowed to produce due to the customer's contract. +func (e *UCLPPServer) SetContractualProductionNominalMax(value float64) error { + return util.SetLocalElectricalConnectionCharacteristicForContextType( + e.service, + model.ElectricalConnectionCharacteristicContextTypeEntity, + model.ElectricalConnectionCharacteristicTypeTypeContractualProductionNominalMax, + value, + ) +} diff --git a/uclppserver/public_test.go b/uclppserver/public_test.go new file mode 100644 index 0000000..fa903a1 --- /dev/null +++ b/uclppserver/public_test.go @@ -0,0 +1,77 @@ +package uclppserver + +import ( + "time" + + "github.com/enbility/cemd/api" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPPServerSuite) Test_LoadControlLimit() { + limit, err := s.sut.ProductionLimit() + assert.Equal(s.T(), 0.0, limit.Value) + assert.NotNil(s.T(), err) + + newLimit := api.LoadLimit{ + Duration: time.Duration(time.Hour * 2), + IsActive: true, + IsChangeable: true, + Value: 16, + } + err = s.sut.SetProductionLimit(newLimit) + assert.Nil(s.T(), err) + + limit, err = s.sut.ProductionLimit() + assert.Equal(s.T(), 16.0, limit.Value) + assert.Nil(s.T(), err) +} + +func (s *UCLPPServerSuite) Test_Failsafe() { + limit, changeable, err := s.sut.FailsafeProductionActivePowerLimit() + assert.Equal(s.T(), 0.0, limit) + assert.Equal(s.T(), false, changeable) + assert.NotNil(s.T(), err) + + err = s.sut.SetFailsafeProductionActivePowerLimit(10, true) + assert.Nil(s.T(), err) + + limit, changeable, err = s.sut.FailsafeProductionActivePowerLimit() + assert.Equal(s.T(), 10.0, limit) + assert.Equal(s.T(), true, changeable) + assert.Nil(s.T(), err) + + // The actual tests of the functionality is located in the util package + duration, changeable, err := s.sut.FailsafeDurationMinimum() + assert.Equal(s.T(), time.Duration(0), duration) + assert.Equal(s.T(), false, changeable) + assert.NotNil(s.T(), err) + + err = s.sut.SetFailsafeDurationMinimum(time.Duration(time.Hour*1), true) + assert.NotNil(s.T(), err) + + err = s.sut.SetFailsafeDurationMinimum(time.Duration(time.Hour*2), true) + assert.Nil(s.T(), err) + + limit, changeable, err = s.sut.FailsafeProductionActivePowerLimit() + assert.Equal(s.T(), 10.0, limit) + assert.Equal(s.T(), true, changeable) + assert.Nil(s.T(), err) + + duration, changeable, err = s.sut.FailsafeDurationMinimum() + assert.Equal(s.T(), time.Duration(time.Hour*2), duration) + assert.Equal(s.T(), true, changeable) + assert.Nil(s.T(), err) +} + +func (s *UCLPPServerSuite) Test_ContractualProductionNominalMax() { + value, err := s.sut.ContractualProductionNominalMax() + assert.Equal(s.T(), 0.0, value) + assert.NotNil(s.T(), err) + + err = s.sut.SetContractualProductionNominalMax(10) + assert.Nil(s.T(), err) + + value, err = s.sut.ContractualProductionNominalMax() + assert.Equal(s.T(), 10.0, value) + assert.Nil(s.T(), err) +} diff --git a/uclppserver/testhelper_test.go b/uclppserver/testhelper_test.go new file mode 100644 index 0000000..fd9e33d --- /dev/null +++ b/uclppserver/testhelper_test.go @@ -0,0 +1,197 @@ +package uclppserver + +import ( + "fmt" + "testing" + "time" + + "github.com/enbility/cemd/api" + eebusapi "github.com/enbility/eebus-go/api" + eebusmocks "github.com/enbility/eebus-go/mocks" + "github.com/enbility/eebus-go/service" + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/ship-go/cert" + shipmocks "github.com/enbility/ship-go/mocks" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestLPPServerSuite(t *testing.T) { + suite.Run(t, new(UCLPPServerSuite)) +} + +type UCLPPServerSuite struct { + suite.Suite + + sut *UCLPPServer + + service eebusapi.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *mocks.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface + loadControlFeature, + deviceDiagnosisFeature, + deviceConfigurationFeature spineapi.FeatureLocalInterface +} + +func (s *UCLPPServerSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} + +func (s *UCLPPServerSuite) BeforeTest(suiteName, testName string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := eebusapi.NewConfiguration( + "test", "test", "test", "test", + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeCEM}, + 9999, cert, 230.0, time.Second*4) + + serviceHandler := eebusmocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + mockRemoteDevice := mocks.NewDeviceRemoteInterface(s.T()) + s.mockRemoteEntity = mocks.NewEntityRemoteInterface(s.T()) + mockRemoteFeature := mocks.NewFeatureRemoteInterface(s.T()) + mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe() + mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe() + s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() + + s.sut = NewUCLPP(s.service, s.Event) + s.sut.AddFeatures() + s.sut.AddUseCase() + + localEntity := s.sut.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + s.loadControlFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + s.deviceDiagnosisFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + s.deviceConfigurationFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + + s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) +} + +const remoteSki string = "testremoteski" + +func setupDevices( + eebusService eebusapi.ServiceInterface, t *testing.T) ( + spineapi.DeviceRemoteInterface, + spineapi.EntityRemoteInterface) { + localDevice := eebusService.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + f := spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + localEntity.AddFeature(f) + f = spine.NewFeatureLocal(2, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionPermittedValueSetListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, true) + localEntity.AddFeature(f) + + writeHandler := shipmocks.NewShipConnectionDataWriterInterface(t) + writeHandler.EXPECT().WriteShipMessageWithPayload(mock.Anything).Return().Maybe() + sender := spine.NewSender(writeHandler) + remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) + + remoteDeviceName := "remote" + + var remoteFeatures = []struct { + featureType model.FeatureTypeType + role model.RoleType + supportedFcts []model.FunctionType + }{ + {model.FeatureTypeTypeLoadControl, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeClient, + []model.FunctionType{}, + }, + {model.FeatureTypeTypeDeviceDiagnosis, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceDiagnosisHeartbeatData, + }, + }, + {model.FeatureTypeTypeElectricalConnection, + model.RoleTypeClient, + []model.FunctionType{}, + }, + } + var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType + for index, feature := range remoteFeatures { + supportedFcts := []model.FunctionPropertyType{} + for _, fct := range feature.supportedFcts { + supportedFct := model.FunctionPropertyType{ + Function: eebusutil.Ptr(fct), + PossibleOperations: &model.PossibleOperationsType{ + Read: &model.PossibleOperationsReadType{}, + }, + } + supportedFcts = append(supportedFcts, supportedFct) + } + + featureInformation := model.NodeManagementDetailedDiscoveryFeatureInformationType{ + Description: &model.NetworkManagementFeatureDescriptionDataType{ + FeatureAddress: &model.FeatureAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + Feature: eebusutil.Ptr(model.AddressFeatureType(index)), + }, + FeatureType: eebusutil.Ptr(feature.featureType), + Role: eebusutil.Ptr(feature.role), + SupportedFunction: supportedFcts, + }, + } + featureInformations = append(featureInformations, featureInformation) + } + + detailedData := &model.NodeManagementDetailedDiscoveryDataType{ + DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ + Description: &model.NetworkManagementDeviceDescriptionDataType{ + DeviceAddress: &model.DeviceAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + }, + }, + }, + EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ + { + Description: &model.NetworkManagementEntityDescriptionDataType{ + EntityAddress: &model.EntityAddressType{ + Device: eebusutil.Ptr(model.AddressDeviceType(remoteDeviceName)), + Entity: []model.AddressEntityType{1}, + }, + EntityType: eebusutil.Ptr(model.EntityTypeTypeGridGuard), + }, + }, + }, + FeatureInformation: featureInformations, + } + + entities, err := remoteDevice.AddEntityAndFeatures(true, detailedData) + if err != nil { + fmt.Println(err) + } + remoteDevice.UpdateDevice(detailedData.DeviceInformation.Description) + + localDevice.AddRemoteDeviceForSki(remoteSki, remoteDevice) + + return remoteDevice, entities[0] +} diff --git a/uclppserver/types.go b/uclppserver/types.go new file mode 100644 index 0000000..e780589 --- /dev/null +++ b/uclppserver/types.go @@ -0,0 +1,38 @@ +package uclppserver + +import "github.com/enbility/cemd/api" + +const ( + // Load control obligation limit data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // + // Use Case LPC, Scenario 1 + DataUpdateLimit api.EventType = "DataUpdateLimit" + + // Failsafe limit for the produced active (real) power of the + // Controllable System data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeProductionActivePowerLimit api.EventType = "DataUpdateFailsafeProductionActivePowerLimit" + + // Minimum time the Controllable System remains in "failsafe state" unless conditions + // specified in this Use Case permit leaving the "failsafe state" data updated + // + // The callback with this message provides: + // - the device of the e.g. SMGW + // - the entity of the e.g. SMGW + // + // Use Case LPC, Scenario 2 + // + // Note: the referred data may be updated together with all other configuration items of this use case + DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" +) diff --git a/uclppserver/uclpp.go b/uclppserver/uclpp.go new file mode 100644 index 0000000..3c0ad5b --- /dev/null +++ b/uclppserver/uclpp.go @@ -0,0 +1,208 @@ +package uclppserver + +import ( + "github.com/enbility/cemd/api" + "github.com/enbility/cemd/util" + eebusapi "github.com/enbility/eebus-go/api" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" +) + +type UCLPPServer struct { + service eebusapi.ServiceInterface + + eventCB api.EntityEventCallback + + validEntityTypes []model.EntityTypeType + + heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use +} + +var _ UCLPPServerInterface = (*UCLPPServer)(nil) + +func NewUCLPP(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPPServer { + uc := &UCLPPServer{ + service: service, + eventCB: eventCB, + } + + uc.validEntityTypes = []model.EntityTypeType{ + model.EntityTypeTypeGridGuard, + model.EntityTypeTypeCEM, // KEO uses this entity type for an SMGW whysoever + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (c *UCLPPServer) UseCaseName() model.UseCaseNameType { + return model.UseCaseNameTypeLimitationOfPowerProduction +} + +func (e *UCLPPServer) AddFeatures() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + // client features + _ = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + + // server features + f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + + var limitId model.LoadControlLimitIdType = 0 + // get the highest limitId + if desc, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( + f, model.FunctionTypeLoadControlLimitDescriptionListData); err == nil && desc.LoadControlLimitDescriptionData != nil { + for _, desc := range desc.LoadControlLimitDescriptionData { + if desc.LimitId != nil && *desc.LimitId >= limitId { + limitId++ + } + } + } + + loadControlDesc := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), // This is a fake Measurement ID, as there is no Electrical Connection server defined, it can't provide any meaningful. But KEO requires this to be set :( + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, loadControlDesc) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) + + var configId model.DeviceConfigurationKeyIdType = 0 + // get the highest keyId + if desc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( + f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData); err == nil && desc.DeviceConfigurationKeyValueDescriptionData != nil { + for _, desc := range desc.DeviceConfigurationKeyValueDescriptionData { + if desc.KeyId != nil && *desc.KeyId >= configId { + configId++ + } + } + } + + deviceConfigDesc := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), + }, + }, + } + f.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, deviceConfigDesc) + + deviceConfig := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + IsValueChangeable: eebusutil.Ptr(true), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + IsValueChangeable: eebusutil.Ptr(true), + }, + }, + } + f.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, deviceConfig) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + + f = localEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, false) + + var elCharId model.ElectricalConnectionCharacteristicIdType = 0 + // get the highest CharacteristicId + if desc, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( + f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicData != nil { + for _, desc := range desc.ElectricalConnectionCharacteristicData { + if desc.CharacteristicId != nil && *desc.CharacteristicId >= elCharId { + elCharId++ + } + } + } + + // ElectricalConnectionId and ParameterId should be identical to the ones used + // in a MPC Server role implementation, which is not done here (yet) + elCharData := &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(elCharId), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualProductionNominalMax), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + }, + } + f.SetData(model.FunctionTypeElectricalConnectionCharacteristicListData, elCharData) +} + +func (e *UCLPPServer) AddUseCase() { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.AddUseCaseSupport( + model.UseCaseActorTypeControllableSystem, + e.UseCaseName(), + model.SpecificationVersionType("1.0.0"), + "release", + true, + []model.UseCaseScenarioSupportType{1, 2, 3, 4}) +} + +func (e *UCLPPServer) UpdateUseCaseAvailability(available bool) { + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + localEntity.SetUseCaseAvailability(model.UseCaseActorTypeControllableSystem, e.UseCaseName(), available) +} + +// returns if the entity supports the usecase +// +// possible errors: +// - ErrDataNotAvailable if that information is not (yet) available +// - and others +func (e *UCLPPServer) IsUseCaseSupported(entity spineapi.EntityRemoteInterface) (bool, error) { + if !util.IsCompatibleEntity(entity, e.validEntityTypes) { + return false, api.ErrNoCompatibleEntity + } + + // check if the usecase and mandatory scenarios are supported and + // if the required server features are available + if !entity.Device().VerifyUseCaseScenariosAndFeaturesSupport( + model.UseCaseActorTypeEnergyGuard, + e.UseCaseName(), + []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + []model.FeatureTypeType{ + model.FeatureTypeTypeDeviceDiagnosis, + }, + ) { + return false, nil + } + + if _, err := util.DeviceDiagnosis(e.service, entity); err != nil { + return false, eebusapi.ErrFunctionNotSupported + } + + return true, nil +} diff --git a/uclppserver/uclpp_test.go b/uclppserver/uclpp_test.go new file mode 100644 index 0000000..1d5ba50 --- /dev/null +++ b/uclppserver/uclpp_test.go @@ -0,0 +1,45 @@ +package uclppserver + +import ( + eebusutil "github.com/enbility/eebus-go/util" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *UCLPPServerSuite) Test_UpdateUseCaseAvailability() { + s.sut.UpdateUseCaseAvailability(true) +} + +func (s *UCLPPServerSuite) Test_IsUseCaseSupported() { + data, err := s.sut.IsUseCaseSupported(s.mockRemoteEntity) + assert.NotNil(s.T(), err) + assert.Equal(s.T(), false, data) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), false, data) + + ucData := &model.NodeManagementUseCaseDataType{ + UseCaseInformation: []model.UseCaseInformationDataType{ + { + Actor: eebusutil.Ptr(model.UseCaseActorTypeEnergyGuard), + UseCaseSupport: []model.UseCaseSupportType{ + { + UseCaseName: eebusutil.Ptr(model.UseCaseNameTypeLimitationOfPowerProduction), + UseCaseAvailable: eebusutil.Ptr(true), + ScenarioSupport: []model.UseCaseScenarioSupportType{1, 2, 3, 4}, + }, + }, + }, + }, + } + + nodemgmtEntity := s.remoteDevice.Entity([]model.AddressEntityType{0}) + nodeFeature := s.remoteDevice.FeatureByEntityTypeAndRole(nodemgmtEntity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) + fErr := nodeFeature.UpdateData(model.FunctionTypeNodeManagementUseCaseData, ucData, nil, nil) + assert.Nil(s.T(), fErr) + + data, err = s.sut.IsUseCaseSupported(s.monitoredEntity) + assert.Nil(s.T(), err) + assert.Equal(s.T(), true, data) +} From 072fc37a370056e5ac264ce1a21c994a188768f0 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 16 Apr 2024 17:20:46 +0200 Subject: [PATCH 198/227] Support UCLPC and UCLPP server running together --- cmd/democem/democem.go | 21 +++++++ uclpcserver/uclpc.go | 133 ++++++++++++++++++++++++----------------- uclppserver/uclpp.go | 133 ++++++++++++++++++++++++----------------- 3 files changed, 175 insertions(+), 112 deletions(-) diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index 1817aa8..d4e2306 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -8,6 +8,7 @@ import ( "github.com/enbility/cemd/cem" "github.com/enbility/cemd/ucevsecc" "github.com/enbility/cemd/uclpcserver" + "github.com/enbility/cemd/uclppserver" eebusapi "github.com/enbility/eebus-go/api" "github.com/enbility/ship-go/logging" ) @@ -53,6 +54,26 @@ func (d *DemoCem) Setup() error { logging.Log().Error(err) } + lpps := uclppserver.NewUCLPP(d.cem.Service, d.entityEventCB) + d.cem.AddUseCase(lpps) + + if err := lpps.SetProductionLimit(api.LoadLimit{ + IsChangeable: true, + IsActive: false, + Value: 0, + }); err != nil { + logging.Log().Error(err) + } + if err := lpps.SetContractualProductionNominalMax(-7000); err != nil { + logging.Log().Error(err) + } + if err := lpps.SetFailsafeProductionActivePowerLimit(0, true); err != nil { + logging.Log().Error(err) + } + if err := lpps.SetFailsafeDurationMinimum(time.Hour*2, true); err != nil { + logging.Log().Error(err) + } + evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(evsecc) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 8d86939..4dff596 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -55,8 +55,9 @@ func (e *UCLPCServer) AddFeatures() { var limitId model.LoadControlLimitIdType = 0 // get the highest limitId - if desc, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( - f, model.FunctionTypeLoadControlLimitDescriptionListData); err == nil && desc.LoadControlLimitDescriptionData != nil { + desc, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( + f, model.FunctionTypeLoadControlLimitDescriptionListData) + if err == nil && desc.LoadControlLimitDescriptionData != nil { for _, desc := range desc.LoadControlLimitDescriptionData { if desc.LimitId != nil && *desc.LimitId >= limitId { limitId++ @@ -64,20 +65,23 @@ func (e *UCLPCServer) AddFeatures() { } } - loadControlDesc := &model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ - { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), - LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), - LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), - LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), - MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), // This is a fake Measurement ID, as there is no Electrical Connection server defined, it can't provide any meaningful. But KEO requires this to be set :( - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), - }, - }, + if desc == nil || len(desc.LoadControlLimitDescriptionData) == 0 { + desc = &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{}, + } } - f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, loadControlDesc) + + newLimitDesc := model.LoadControlLimitDescriptionDataType{ + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), // This is a fake Measurement ID, as there is no Electrical Connection server defined, it can't provide any meaningful. But KEO requires this to be set :( + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + } + desc.LoadControlLimitDescriptionData = append(desc.LoadControlLimitDescriptionData, newLimitDesc) + f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, desc) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) @@ -85,45 +89,58 @@ func (e *UCLPCServer) AddFeatures() { var configId model.DeviceConfigurationKeyIdType = 0 // get the highest keyId - if desc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( - f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData); err == nil && desc.DeviceConfigurationKeyValueDescriptionData != nil { - for _, desc := range desc.DeviceConfigurationKeyValueDescriptionData { + deviceConfigDesc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( + f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) + if err == nil && deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData != nil { + for _, desc := range deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData { if desc.KeyId != nil && *desc.KeyId >= configId { configId++ } } } - deviceConfigDesc := &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), - KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), - ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - }, - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), - KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), - }, + if err != nil || deviceConfigDesc == nil || len(deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData) == 0 { + deviceConfigDesc = &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{}, + } + } + + newConfigs := []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), }, } + deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData = append(deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData, newConfigs...) f.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, deviceConfigDesc) - deviceConfig := &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), - IsValueChangeable: eebusutil.Ptr(true), - }, - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), - IsValueChangeable: eebusutil.Ptr(true), - }, + configData, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueListDataType](f, model.FunctionTypeDeviceConfigurationKeyValueListData) + if err != nil || configData == nil || len(configData.DeviceConfigurationKeyValueData) == 0 { + configData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + } + + newConfigData := []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + IsValueChangeable: eebusutil.Ptr(true), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + IsValueChangeable: eebusutil.Ptr(true), }, } - f.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, deviceConfig) + + configData.DeviceConfigurationKeyValueData = append(configData.DeviceConfigurationKeyValueData, newConfigData...) + f.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, configData) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) @@ -133,29 +150,33 @@ func (e *UCLPCServer) AddFeatures() { var elCharId model.ElectricalConnectionCharacteristicIdType = 0 // get the highest CharacteristicId - if desc, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( - f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicData != nil { - for _, desc := range desc.ElectricalConnectionCharacteristicData { + elCharData, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( + f, model.FunctionTypeElectricalConnectionCharacteristicListData) + if err == nil && elCharData.ElectricalConnectionCharacteristicData != nil { + for _, desc := range elCharData.ElectricalConnectionCharacteristicData { if desc.CharacteristicId != nil && *desc.CharacteristicId >= elCharId { elCharId++ } } } + if err != nil || configData == nil || len(configData.DeviceConfigurationKeyValueData) == 0 { + elCharData = &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{}, + } + } + // ElectricalConnectionId and ParameterId should be identical to the ones used // in a MPC Server role implementation, which is not done here (yet) - elCharData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ - { - ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), - CharacteristicId: eebusutil.Ptr(elCharId), - CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), - CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax), - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - }, - }, + newCharData := model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(elCharId), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualConsumptionNominalMax), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), } + elCharData.ElectricalConnectionCharacteristicData = append(elCharData.ElectricalConnectionCharacteristicData, newCharData) f.SetData(model.FunctionTypeElectricalConnectionCharacteristicListData, elCharData) } diff --git a/uclppserver/uclpp.go b/uclppserver/uclpp.go index 3c0ad5b..e5a615f 100644 --- a/uclppserver/uclpp.go +++ b/uclppserver/uclpp.go @@ -55,28 +55,32 @@ func (e *UCLPPServer) AddFeatures() { var limitId model.LoadControlLimitIdType = 0 // get the highest limitId - if desc, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( - f, model.FunctionTypeLoadControlLimitDescriptionListData); err == nil && desc.LoadControlLimitDescriptionData != nil { - for _, desc := range desc.LoadControlLimitDescriptionData { + loadControlDesc, err := spine.LocalFeatureDataCopyOfType[*model.LoadControlLimitDescriptionListDataType]( + f, model.FunctionTypeLoadControlLimitDescriptionListData) + if err == nil && loadControlDesc.LoadControlLimitDescriptionData != nil { + for _, desc := range loadControlDesc.LoadControlLimitDescriptionData { if desc.LimitId != nil && *desc.LimitId >= limitId { limitId++ } } } - loadControlDesc := &model.LoadControlLimitDescriptionListDataType{ - LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ - { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), - LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), - LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), - LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), - MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), // This is a fake Measurement ID, as there is no Electrical Connection server defined, it can't provide any meaningful. But KEO requires this to be set :( - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), - }, - }, + if loadControlDesc == nil || len(loadControlDesc.LoadControlLimitDescriptionData) == 0 { + loadControlDesc = &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{}, + } } + + newLimitDesc := model.LoadControlLimitDescriptionDataType{ + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(limitId)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), // This is a fake Measurement ID, as there is no Electrical Connection server defined, it can't provide any meaningful. But KEO requires this to be set :( + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + } + loadControlDesc.LoadControlLimitDescriptionData = append(loadControlDesc.LoadControlLimitDescriptionData, newLimitDesc) f.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, loadControlDesc) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) @@ -85,45 +89,58 @@ func (e *UCLPPServer) AddFeatures() { var configId model.DeviceConfigurationKeyIdType = 0 // get the highest keyId - if desc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( - f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData); err == nil && desc.DeviceConfigurationKeyValueDescriptionData != nil { - for _, desc := range desc.DeviceConfigurationKeyValueDescriptionData { + deviceConfigDesc, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueDescriptionListDataType]( + f, model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData) + if err == nil && deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData != nil { + for _, desc := range deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData { if desc.KeyId != nil && *desc.KeyId >= configId { configId++ } } } - deviceConfigDesc := &model.DeviceConfigurationKeyValueDescriptionListDataType{ - DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), - KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), - ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - }, - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), - KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), - }, + if deviceConfigDesc == nil || len(deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData) == 0 { + deviceConfigDesc = &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{}, + } + } + + newConfigs := []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + ValueType: eebusutil.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), }, } + deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData = append(deviceConfigDesc.DeviceConfigurationKeyValueDescriptionData, newConfigs...) f.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, deviceConfigDesc) - deviceConfig := &model.DeviceConfigurationKeyValueListDataType{ - DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), - IsValueChangeable: eebusutil.Ptr(true), - }, - { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), - IsValueChangeable: eebusutil.Ptr(true), - }, + configData, err := spine.LocalFeatureDataCopyOfType[*model.DeviceConfigurationKeyValueListDataType](f, model.FunctionTypeDeviceConfigurationKeyValueListData) + if err != nil || configData == nil || len(configData.DeviceConfigurationKeyValueData) == 0 { + configData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + } + + newConfigData := []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId)), + IsValueChangeable: eebusutil.Ptr(true), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(configId + 1)), + IsValueChangeable: eebusutil.Ptr(true), }, } - f.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, deviceConfig) + + configData.DeviceConfigurationKeyValueData = append(configData.DeviceConfigurationKeyValueData, newConfigData...) + f.SetData(model.FunctionTypeDeviceConfigurationKeyValueListData, configData) f = localEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) @@ -133,29 +150,33 @@ func (e *UCLPPServer) AddFeatures() { var elCharId model.ElectricalConnectionCharacteristicIdType = 0 // get the highest CharacteristicId - if desc, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( - f, model.FunctionTypeElectricalConnectionCharacteristicListData); err == nil && desc.ElectricalConnectionCharacteristicData != nil { - for _, desc := range desc.ElectricalConnectionCharacteristicData { + elCharData, err := spine.LocalFeatureDataCopyOfType[*model.ElectricalConnectionCharacteristicListDataType]( + f, model.FunctionTypeElectricalConnectionCharacteristicListData) + if err == nil && elCharData.ElectricalConnectionCharacteristicData != nil { + for _, desc := range elCharData.ElectricalConnectionCharacteristicData { if desc.CharacteristicId != nil && *desc.CharacteristicId >= elCharId { elCharId++ } } } + if err != nil || configData == nil || len(configData.DeviceConfigurationKeyValueData) == 0 { + elCharData = &model.ElectricalConnectionCharacteristicListDataType{ + ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{}, + } + } + // ElectricalConnectionId and ParameterId should be identical to the ones used // in a MPC Server role implementation, which is not done here (yet) - elCharData := &model.ElectricalConnectionCharacteristicListDataType{ - ElectricalConnectionCharacteristicData: []model.ElectricalConnectionCharacteristicDataType{ - { - ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), - ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), - CharacteristicId: eebusutil.Ptr(elCharId), - CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), - CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualProductionNominalMax), - Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), - }, - }, + newCharData := model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + CharacteristicId: eebusutil.Ptr(elCharId), + CharacteristicContext: eebusutil.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: eebusutil.Ptr(model.ElectricalConnectionCharacteristicTypeTypeContractualProductionNominalMax), + Unit: eebusutil.Ptr(model.UnitOfMeasurementTypeW), } + elCharData.ElectricalConnectionCharacteristicData = append(elCharData.ElectricalConnectionCharacteristicData, newCharData) f.SetData(model.FunctionTypeElectricalConnectionCharacteristicListData, elCharData) } From 1d7d79ed7911bb66a2b776623cdaf4634eab118c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 18 Apr 2024 21:32:28 +0200 Subject: [PATCH 199/227] Update SPINE, EEBUS --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1b638e4..6ff9fd7 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240409162436-d7be76ca222b + github.com/enbility/eebus-go v0.0.0-20240418193007-689039cfe6f9 github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 - github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c + github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 2b88513..8e2069e 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240409162436-d7be76ca222b h1:uzMUOeW3QiP4L0aevYzujpmqDvoiTR8du30WVTofe14= -github.com/enbility/eebus-go v0.0.0-20240409162436-d7be76ca222b/go.mod h1:SIr4W8XTlS+zIjZreeksebaao40MEkl3r0/alurqcB4= +github.com/enbility/eebus-go v0.0.0-20240418193007-689039cfe6f9 h1:AD6VFdCqOpaF+Dn4ddT6Qt62P5kvVzYCrAUpD9n2AeI= +github.com/enbility/eebus-go v0.0.0-20240418193007-689039cfe6f9/go.mod h1:WXTRCcoza+DYVUZp3e7KBHglsXG839Z1QVOBuQD5jhM= github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 h1:A4odLKYuqCfiv08Ths5gVkiDjgnHxhbVUhThizMucPA= github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c h1:Hk48Nz21cfd2wXe1DHK5lB1mmW2PNs/WYzseTno7rTI= -github.com/enbility/spine-go v0.0.0-20240409160227-cdb210396e4c/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 h1:zyksXO307BwB1t1U5b6vgxw4Pnd0TlbYVouwZQQvrP8= +github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 5dd2ea6aba9390b19eb0a2f1783a6daa65200803 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 18 Apr 2024 21:36:27 +0200 Subject: [PATCH 200/227] Improve getting proper loadlimit values --- ucopev/public.go | 8 +++++++- ucoscev/public.go | 8 +++++++- util/loadcontrol.go | 7 +++++-- util/loadcontrol_test.go | 20 ++++++++++++++------ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/ucopev/public.go b/ucopev/public.go index fe31236..84f6750 100644 --- a/ucopev/public.go +++ b/ucopev/public.go @@ -29,7 +29,13 @@ func (e *UCOPEV) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float64 // - and others func (e *UCOPEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) ( limits []api.LoadLimitsPhase, resultErr error) { - return util.LoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeObligation) + return util.LoadControlLimits( + e.service, + entity, + e.validEntityTypes, + model.LoadControlLimitTypeTypeMaxValueLimit, + model.LoadControlCategoryTypeObligation, + model.ScopeTypeTypeOverloadProtection) } // send new LoadControlLimits to the remote EV diff --git a/ucoscev/public.go b/ucoscev/public.go index 5df5c74..6da8be8 100644 --- a/ucoscev/public.go +++ b/ucoscev/public.go @@ -28,7 +28,13 @@ func (e *UCOSCEV) CurrentLimits(entity spineapi.EntityRemoteInterface) ([]float6 // - ErrDataNotAvailable if no such limit is (yet) available // - and others func (e *UCOSCEV) LoadControlLimits(entity spineapi.EntityRemoteInterface) (limits []api.LoadLimitsPhase, resultErr error) { - return util.LoadControlLimits(e.service, entity, e.validEntityTypes, model.LoadControlCategoryTypeRecommendation) + return util.LoadControlLimits( + e.service, + entity, + e.validEntityTypes, + model.LoadControlLimitTypeTypeMaxValueLimit, + model.LoadControlCategoryTypeRecommendation, + model.ScopeTypeTypeSelfConsumption) } // send new LoadControlLimits to the remote EV diff --git a/util/loadcontrol.go b/util/loadcontrol.go index fce2ef7..5a64446 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -18,7 +18,9 @@ func LoadControlLimits( service eebusapi.ServiceInterface, entity spineapi.EntityRemoteInterface, entityTypes []model.EntityTypeType, - category model.LoadControlCategoryType) (limits []api.LoadLimitsPhase, resultErr error) { + limitType model.LoadControlLimitTypeType, + limitCategory model.LoadControlCategoryType, + scopeType model.ScopeTypeType) (limits []api.LoadLimitsPhase, resultErr error) { limits = nil resultErr = api.ErrNoCompatibleEntity if entity == nil || !IsCompatibleEntity(entity, entityTypes) { @@ -34,7 +36,8 @@ func LoadControlLimits( resultErr = eebusapi.ErrDataNotAvailable // find out the appropriate limitId for each phase value // limitDescription contains the measurementId for each limitId - limitDescriptions, err := evLoadControl.GetLimitDescriptionsForCategory(category) + limitDescriptions, err := evLoadControl.GetLimitDescriptionsForTypeCategoryDirectionScope( + limitType, limitCategory, "", scopeType) if err != nil { return } diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 507a941..df1919c 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -12,14 +12,16 @@ import ( func (s *UtilSuite) Test_LoadControlLimits() { var data []api.LoadLimitsPhase var err error + limitType := model.LoadControlLimitTypeTypeMaxValueLimit + scope := model.ScopeTypeTypeSelfConsumption category := model.LoadControlCategoryTypeObligation entityTypes := []model.EntityTypeType{model.EntityTypeTypeEV} - data, err = LoadControlLimits(s.service, s.mockRemoteEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.mockRemoteEntity, entityTypes, limitType, category, scope) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) - data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, limitType, category, scope) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -29,16 +31,22 @@ func (s *UtilSuite) Test_LoadControlLimits() { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), LimitCategory: eebusutil.Ptr(category), MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), }, { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), LimitCategory: eebusutil.Ptr(category), MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), }, { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), LimitCategory: eebusutil.Ptr(category), MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), }, }, } @@ -47,7 +55,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, limitType, category, scope) assert.Nil(s.T(), err) assert.Equal(s.T(), 3, len(data)) assert.Equal(s.T(), 0.0, data[0].Value) @@ -79,7 +87,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, limitType, category, scope) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -102,7 +110,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, limitData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, limitType, category, scope) assert.NotNil(s.T(), err) assert.Nil(s.T(), data) @@ -131,7 +139,7 @@ func (s *UtilSuite) Test_LoadControlLimits() { fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionPermittedValueSetListData, permData, nil, nil) assert.Nil(s.T(), fErr) - data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, category) + data, err = LoadControlLimits(s.service, s.monitoredEntity, entityTypes, limitType, category, scope) assert.Nil(s.T(), err) assert.Equal(s.T(), 3, len(data)) assert.Equal(s.T(), 16.0, data[0].Value) From 0fc0da4165e8344fb181337f078807b456c53cbd Mon Sep 17 00:00:00 2001 From: Andreas Rehn Date: Wed, 24 Apr 2024 14:06:17 +0200 Subject: [PATCH 201/227] uclppserver: fix loadControlLimitDataUpdate event not raised --- uclppserver/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uclppserver/events.go b/uclppserver/events.go index 49ecba5..eac39a7 100644 --- a/uclppserver/events.go +++ b/uclppserver/events.go @@ -136,7 +136,7 @@ func (e *UCLPPServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload // the load control limit data was updated func (e *UCLPPServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - if _, err := e.ProductionLimit(); err != nil { + if _, err := e.ProductionLimit(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) } } From 7b81919e4198da85498d7de359f8b0b0b5dc58b3 Mon Sep 17 00:00:00 2001 From: Andreas Rehn Date: Wed, 24 Apr 2024 14:06:28 +0200 Subject: [PATCH 202/227] uclpcserver: fix loadControlLimitDataUpdate event not raised --- uclpcserver/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 70430f9..0f640ec 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -136,7 +136,7 @@ func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload // the load control limit data was updated func (e *UCLPCServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - if _, err := e.ConsumptionLimit(); err != nil { + if _, err := e.ConsumptionLimit(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) } } From b5217dc42d2709d53bf3750b67449831d4e8ed18 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 24 Apr 2024 19:28:01 +0200 Subject: [PATCH 203/227] Additional event fixes --- uclpcserver/events.go | 4 ++-- uclppserver/events.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 0f640ec..56c8cd3 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -143,10 +143,10 @@ func (e *UCLPCServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) // the configuration key data of an SMGW was updated func (e *UCLPCServer) configurationDataUpdate(payload spineapi.EventPayload) { - if _, _, err := e.FailsafeConsumptionActivePowerLimit(); err != nil { + if _, _, err := e.FailsafeConsumptionActivePowerLimit(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeConsumptionActivePowerLimit) } - if _, _, err := e.FailsafeDurationMinimum(); err != nil { + if _, _, err := e.FailsafeDurationMinimum(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) } } diff --git a/uclppserver/events.go b/uclppserver/events.go index eac39a7..0e34692 100644 --- a/uclppserver/events.go +++ b/uclppserver/events.go @@ -143,10 +143,10 @@ func (e *UCLPPServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) // the configuration key data of an SMGW was updated func (e *UCLPPServer) configurationDataUpdate(payload spineapi.EventPayload) { - if _, _, err := e.FailsafeProductionActivePowerLimit(); err != nil { + if _, _, err := e.FailsafeProductionActivePowerLimit(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeProductionActivePowerLimit) } - if _, _, err := e.FailsafeDurationMinimum(); err != nil { + if _, _, err := e.FailsafeDurationMinimum(); err == nil { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) } } From 2f8813fbfc6364b1745ea1f12a82d1aa17d8acf9 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 4 May 2024 15:31:19 +0200 Subject: [PATCH 204/227] Update SHIP, SPINE, EEBUS --- cmd/democem/democem.go | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/democem/democem.go b/cmd/democem/democem.go index d4e2306..1e23ddb 100644 --- a/cmd/democem/democem.go +++ b/cmd/democem/democem.go @@ -77,7 +77,7 @@ func (d *DemoCem) Setup() error { evsecc := ucevsecc.NewUCEVSECC(d.cem.Service, d.entityEventCB) d.cem.AddUseCase(evsecc) - d.cem.Service.RegisterRemoteSKI(d.remoteSki, true) + d.cem.Service.RegisterRemoteSKI(d.remoteSki) d.cem.Start() diff --git a/go.mod b/go.mod index 6ff9fd7..8dcd114 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240418193007-689039cfe6f9 - github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 - github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 + github.com/enbility/eebus-go v0.0.0-20240504132411-033cd23978d4 + github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 + github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 8e2069e..8033320 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240418193007-689039cfe6f9 h1:AD6VFdCqOpaF+Dn4ddT6Qt62P5kvVzYCrAUpD9n2AeI= -github.com/enbility/eebus-go v0.0.0-20240418193007-689039cfe6f9/go.mod h1:WXTRCcoza+DYVUZp3e7KBHglsXG839Z1QVOBuQD5jhM= -github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77 h1:A4odLKYuqCfiv08Ths5gVkiDjgnHxhbVUhThizMucPA= -github.com/enbility/ship-go v0.0.0-20240409162155-d5c47f397f77/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44 h1:zyksXO307BwB1t1U5b6vgxw4Pnd0TlbYVouwZQQvrP8= -github.com/enbility/spine-go v0.0.0-20240418192402-4df49a779e44/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/eebus-go v0.0.0-20240504132411-033cd23978d4 h1:ZlaF9PuK0opVUlBu/oznXcARMPbcPHRP0XZO7EQlxbM= +github.com/enbility/eebus-go v0.0.0-20240504132411-033cd23978d4/go.mod h1:D2RB5Tkd9GMOa+fJkXLbtRhEoD5lO6mmd0AVWbJ0Vn8= +github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 h1:9z6MITNW3lQigNEVFzwX/WeX1dyjRdsArCrK8jCzDRk= +github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe h1:u2AIEbiW1jR+XD2QwIjfwO4tE+f63U2VDL9PVA4/mD4= +github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From e1f62a97c26d5caacd2957a9d305e883f0430add Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 4 May 2024 15:31:48 +0200 Subject: [PATCH 205/227] Add TODOs to UCLPCServer and UCLPPServer --- uclpcserver/public.go | 1 + uclppserver/public.go | 1 + 2 files changed, 2 insertions(+) diff --git a/uclpcserver/public.go b/uclpcserver/public.go index f9ebf40..ad46c93 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -91,6 +91,7 @@ func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(limit.Duration), } } + // TODO: this overwrites LPP data as well limits := &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{limitData}, } diff --git a/uclppserver/public.go b/uclppserver/public.go index 5cbab72..3a82819 100644 --- a/uclppserver/public.go +++ b/uclppserver/public.go @@ -91,6 +91,7 @@ func (e *UCLPPServer) SetProductionLimit(limit api.LoadLimit) (resultErr error) EndTime: model.NewAbsoluteOrRelativeTimeTypeFromDuration(limit.Duration), } } + // TODO: this overwrites LPC data as well limits := &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{limitData}, } From d84105b69d3cec9efdea4ca34aefb996c4f1a0b6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sun, 12 May 2024 19:11:47 +0200 Subject: [PATCH 206/227] Update SHIP, SPINE, EEBUS --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8dcd114..b3d9d20 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240504132411-033cd23978d4 - github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 - github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe + github.com/enbility/eebus-go v0.0.0-20240512154912-a94167bca521 + github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 + github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 8033320..486c666 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240504132411-033cd23978d4 h1:ZlaF9PuK0opVUlBu/oznXcARMPbcPHRP0XZO7EQlxbM= -github.com/enbility/eebus-go v0.0.0-20240504132411-033cd23978d4/go.mod h1:D2RB5Tkd9GMOa+fJkXLbtRhEoD5lO6mmd0AVWbJ0Vn8= -github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371 h1:9z6MITNW3lQigNEVFzwX/WeX1dyjRdsArCrK8jCzDRk= -github.com/enbility/ship-go v0.0.0-20240504130140-26a90b4a6371/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe h1:u2AIEbiW1jR+XD2QwIjfwO4tE+f63U2VDL9PVA4/mD4= -github.com/enbility/spine-go v0.0.0-20240504105240-1f18cd7937fe/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/eebus-go v0.0.0-20240512154912-a94167bca521 h1:IabEKRx+OXHx9kKkLB24MstnpOf/JYXB0o9+ml/8RNQ= +github.com/enbility/eebus-go v0.0.0-20240512154912-a94167bca521/go.mod h1:CYr+oO68lMVQns6nZ/EcXWtnV6ei8viPjlrG3Ti1YAg= +github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= +github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 h1:dNB7YGajeWQbSAgOg0NXqlq5SdmLh0AVUv+wHWuoWa4= +github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 74909ddb2fda024cd74fb6a15d31e814d189c239 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 13 May 2024 13:36:00 +0200 Subject: [PATCH 207/227] Update events to use actual provided data Before data update events where triggered, whenever the data type was sent and the actual data item existed in the data storage. Which means that only updating a current measurement also triggered an update event for SoC if that was present in the store from an earlier update. This is now changed and only considers the data elements that are actually provided in the payload. Note: This does NOT consider if a datapoint has been deleted and not updated! --- ucevcc/events.go | 9 +-- ucevcc/events_test.go | 22 ++++-- ucevcc/types.go | 3 - ucevcem/events.go | 17 +++-- ucevcem/events_test.go | 16 +++-- ucevcem/types.go | 8 --- ucevsoc/events.go | 2 +- ucevsoc/events_test.go | 3 +- ucevsoc/types.go | 2 - uclpc/events.go | 31 +++------ uclpc/events_test.go | 71 +++++++++++++++++-- uclpc/types.go | 4 -- uclpcserver/events.go | 11 ++- uclpcserver/events_test.go | 105 ++++++++++++++++++++++++++++ uclpcserver/public.go | 10 +-- uclpcserver/types.go | 10 +-- uclpp/events.go | 31 +++------ uclpp/events_test.go | 71 +++++++++++++++++-- uclpp/types.go | 4 -- uclppserver/events.go | 11 ++- uclppserver/events_test.go | 105 ++++++++++++++++++++++++++++ uclppserver/public.go | 10 +-- uclppserver/types.go | 10 +-- ucmgcp/events.go | 12 ++-- ucmgcp/events_test.go | 3 +- ucmgcp/types.go | 14 ---- ucmpc/events.go | 14 ++-- ucmpc/events_test.go | 3 +- ucmpc/types.go | 14 ---- ucopev/events.go | 24 +------ ucopev/events_test.go | 13 +++- ucoscev/events.go | 25 ++----- ucoscev/events_test.go | 13 +++- ucvabd/events.go | 8 +-- ucvabd/events_test.go | 3 +- ucvabd/types.go | 8 --- ucvapd/events.go | 4 +- ucvapd/events_test.go | 3 +- ucvapd/types.go | 4 -- util/deviceconfiguration.go | 44 ++++++++++-- util/deviceconfiguration_test.go | 58 ++++++++++++++++ util/features_test.go | 10 ++- util/loadcontrol.go | 66 +++++++++++++++--- util/loadcontrol_test.go | 116 ++++++++++++++++++++++++++++++- util/measurement.go | 25 ++++--- util/measurement_test.go | 52 +++++++++----- util/testhelper_test.go | 13 +++- 47 files changed, 827 insertions(+), 288 deletions(-) diff --git a/ucevcc/events.go b/ucevcc/events.go index caccd42..fb0b783 100644 --- a/ucevcc/events.go +++ b/ucevcc/events.go @@ -128,18 +128,13 @@ func (e *UCEVCC) evConfigurationDescriptionDataUpdate(entity spineapi.EntityRemo // the configuration key data of an EV was updated func (e *UCEVCC) evConfigurationDataUpdate(payload spineapi.EventPayload) { - evDeviceConfiguration, err := util.DeviceConfiguration(e.service, payload.Entity) - if err != nil { - return - } - // Scenario 2 - if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeCommunicationsStandard, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(false, e.service, payload, model.DeviceConfigurationKeyNameTypeCommunicationsStandard) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCommunicationStandard) } // Scenario 3 - if _, err := evDeviceConfiguration.GetKeyValueForKeyName(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported, model.DeviceConfigurationKeyValueTypeTypeString); err == nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(false, e.service, payload, model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateAsymmetricChargingSupport) } } diff --git a/ucevcc/events_test.go b/ucevcc/events_test.go index 0b1c79e..d569c40 100644 --- a/ucevcc/events_test.go +++ b/ucevcc/events_test.go @@ -84,11 +84,11 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeCommunicationsStandard), }, { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeAsymmetricChargingSupported), }, }, @@ -102,15 +102,28 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { assert.False(s.T(), s.eventCBInvoked) data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = data + + s.sut.evConfigurationDataUpdate(payload) + assert.False(s.T(), s.eventCBInvoked) + + data = &model.DeviceConfigurationKeyValueListDataType{ DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ { KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{}), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{ String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeISO151182ED2), }), }, { - KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{ Boolean: eebusutil.Ptr(false), }), @@ -118,8 +131,7 @@ func (s *UCEVCCSuite) Test_evConfigurationDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.evConfigurationDataUpdate(payload) assert.True(s.T(), s.eventCBInvoked) diff --git a/ucevcc/types.go b/ucevcc/types.go index b06efe3..7ed9191 100644 --- a/ucevcc/types.go +++ b/ucevcc/types.go @@ -46,7 +46,6 @@ const ( // - the entity of the EV // // Use Case EVCC, Scenario 2 - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateCommunicationStandard api.EventType = "DataUpdateCommunicationStandard" // EV asymmetric charging data was updated @@ -54,8 +53,6 @@ const ( // The callback with this message provides: // - the device of the EVSE the EV is connected to // - the entity of the EV - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateAsymmetricChargingSupport api.EventType = "DataUpdateAsymmetricChargingSupport" // EV identificationdata was updated diff --git a/ucevcem/events.go b/ucevcem/events.go index a76bb84..cd32d22 100644 --- a/ucevcem/events.go +++ b/ucevcem/events.go @@ -73,11 +73,18 @@ func (e *UCEVCEM) evConnected(entity spineapi.EntityRemoteInterface) { // the electrical connection description data of an EV was updated func (e *UCEVCEM) evElectricalConnectionDescriptionDataUpdate(payload spineapi.EventPayload) { - if _, err := e.PhasesConnected(payload.Entity); err != nil { + if payload.Data == nil { return } - e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePhasesConnected) + data := payload.Data.(*model.ElectricalConnectionDescriptionListDataType) + + for _, item := range data.ElectricalConnectionDescriptionData { + if item.ElectricalConnectionId != nil && item.AcConnectedPhases != nil { + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePhasesConnected) + return + } + } } // the measurement description data of an EV was updated @@ -93,17 +100,17 @@ func (e *UCEVCEM) evMeasurementDescriptionDataUpdate(entity spineapi.EntityRemot // the measurement data of an EV was updated func (e *UCEVCEM) evMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACCurrent); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACCurrent) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentPerPhase) } // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPower); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACPower) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePowerPerPhase) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeCharge); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeCharge) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyCharged) } } diff --git a/ucevcem/events_test.go b/ucevcem/events_test.go index a03eca6..3bfbfd7 100644 --- a/ucevcem/events_test.go +++ b/ucevcem/events_test.go @@ -54,6 +54,14 @@ func (s *UCEVCEMSuite) Test_evElectricalConnectionDescriptionDataUpdate() { s.sut.evElectricalConnectionDescriptionDataUpdate(payload) descData := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{}, + } + + payload.Data = descData + + s.sut.evElectricalConnectionDescriptionDataUpdate(payload) + + descData = &model.ElectricalConnectionDescriptionListDataType{ ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ { ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), @@ -62,9 +70,7 @@ func (s *UCEVCEMSuite) Test_evElectricalConnectionDescriptionDataUpdate() { }, } - rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.evEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) - fErr := rFeature.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, descData, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = descData s.sut.evElectricalConnectionDescriptionDataUpdate(payload) } @@ -119,9 +125,7 @@ func (s *UCEVCEMSuite) Test_evMeasurementDataUpdate() { }, }, } - - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.evMeasurementDataUpdate(payload) } diff --git a/ucevcem/types.go b/ucevcem/types.go index c99be79..e3e4e83 100644 --- a/ucevcem/types.go +++ b/ucevcem/types.go @@ -10,8 +10,6 @@ const ( // - the entity of the EV // // Use Case EVCEM, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePhasesConnected api.EventType = "DataUpdatePhasesConnected" // EV current measurement data updated @@ -21,8 +19,6 @@ const ( // - the entity of the EV // // Use Case EVCEM, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateCurrentPerPhase api.EventType = "DataUpdateCurrentPerPhase" // EV power measurement data updated @@ -32,8 +28,6 @@ const ( // - the entity of the EV // // Use Case EVCEM, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePowerPerPhase api.EventType = "DataUpdatePowerPerPhase" // EV charging energy measurement data updated @@ -43,7 +37,5 @@ const ( // - the entity of the EV // // Use Case EVCEM, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyCharged api.EventType = "DataUpdateEnergyCharged" ) diff --git a/ucevsoc/events.go b/ucevsoc/events.go index d88f39a..5b46c27 100644 --- a/ucevsoc/events.go +++ b/ucevsoc/events.go @@ -56,7 +56,7 @@ func (e *UCEVSOC) evConnected(entity spineapi.EntityRemoteInterface) { // the measurement data of an EV was updated func (e *UCEVSOC) evMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeStateOfCharge); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeStateOfCharge) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateStateOfCharge) } } diff --git a/ucevsoc/events_test.go b/ucevsoc/events_test.go index 81727b2..bc8d74a 100644 --- a/ucevsoc/events_test.go +++ b/ucevsoc/events_test.go @@ -85,8 +85,7 @@ func (s *UCEVSOCSuite) Test_evMeasurementDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.evMeasurementDataUpdate(payload) } diff --git a/ucevsoc/types.go b/ucevsoc/types.go index 388b959..eedb5ad 100644 --- a/ucevsoc/types.go +++ b/ucevsoc/types.go @@ -10,7 +10,5 @@ const ( // - the entity of the EV // // Use Case EVSOC, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateStateOfCharge api.EventType = "DataUpdateStateOfCharge" ) diff --git a/uclpc/events.go b/uclpc/events.go index 65c9232..218f1fb 100644 --- a/uclpc/events.go +++ b/uclpc/events.go @@ -68,28 +68,13 @@ func (e *UCLPC) loadControlLimitDescriptionDataUpdate(entity spineapi.EntityRemo // the load control limit data was updated func (e *UCLPC) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - loadControl, err := util.LoadControl(e.service, payload.Entity) - if err != nil { - return - } - - data, err := loadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) - if err != nil { - return - } - - for _, item := range data { - if item.LimitId == nil { - continue - } - - _, err := loadControl.GetLimitValueForLimitId(*item.LimitId) - if err != nil { - continue - } - + if util.LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope( + false, e.service, payload, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) - return } } @@ -105,10 +90,10 @@ func (e *UCLPC) configurationDescriptionDataUpdate(entity spineapi.EntityRemoteI // the configuration key data was updated func (e *UCLPC) configurationDataUpdate(payload spineapi.EventPayload) { - if _, err := e.FailsafeConsumptionActivePowerLimit(payload.Entity); err != nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(false, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeConsumptionActivePowerLimit) } - if _, err := e.FailsafeDurationMinimum(payload.Entity); err != nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(false, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) } } diff --git a/uclpc/events_test.go b/uclpc/events_test.go index 898993c..6098d62 100644 --- a/uclpc/events_test.go +++ b/uclpc/events_test.go @@ -56,8 +56,11 @@ func (s *UCLPCSuite) Test_loadControlLimitDataUpdate() { descData := &model.LoadControlLimitDescriptionListDataType{ LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), - LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), }, }, } @@ -69,6 +72,14 @@ func (s *UCLPCSuite) Test_loadControlLimitDataUpdate() { s.sut.loadControlLimitDataUpdate(payload) data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = data + + s.sut.loadControlLimitDataUpdate(payload) + + data = &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), @@ -77,8 +88,60 @@ func (s *UCLPCSuite) Test_loadControlLimitDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.loadControlLimitDataUpdate(payload) } + +func (s *UCLPCSuite) Test_configurationDataUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.configurationDataUpdate(payload) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.configurationDataUpdate(payload) + + data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) + + data = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) +} diff --git a/uclpc/types.go b/uclpc/types.go index a059175..4649102 100644 --- a/uclpc/types.go +++ b/uclpc/types.go @@ -20,8 +20,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeConsumptionActivePowerLimit api.EventType = "DataUpdateFailsafeConsumptionActivePowerLimit" // Minimum time the Controllable System remains in "failsafe state" unless conditions @@ -32,7 +30,5 @@ const ( // - the entity of the e.g. EVSE // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" ) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 56c8cd3..1619a77 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -136,17 +136,22 @@ func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload // the load control limit data was updated func (e *UCLPCServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - if _, err := e.ConsumptionLimit(); err == nil { + if util.LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope( + true, e.service, payload, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) } } // the configuration key data of an SMGW was updated func (e *UCLPCServer) configurationDataUpdate(payload spineapi.EventPayload) { - if _, _, err := e.FailsafeConsumptionActivePowerLimit(); err == nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(true, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeConsumptionActivePowerLimit) } - if _, _, err := e.FailsafeDurationMinimum(); err == nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(true, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) } } diff --git a/uclpcserver/events_test.go b/uclpcserver/events_test.go index 77b0763..b3706bc 100644 --- a/uclpcserver/events_test.go +++ b/uclpcserver/events_test.go @@ -202,3 +202,108 @@ func (s *UCLPCServerSuite) Test_multipleDeviceDiagServer() { s.sut.subscribeHeartbeatWorkaround(payload) } + +func (s *UCLPCServerSuite) Test_loadControlLimitDataUpdate() { + localDevice := s.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.loadControlLimitDataUpdate(payload) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeConsume), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + + lFeature := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + lFeature.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, descData) + + s.sut.loadControlLimitDataUpdate(payload) + + data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = data + + s.sut.loadControlLimitDataUpdate(payload) + + data = &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + }, + } + + payload.Data = data + + s.sut.loadControlLimitDataUpdate(payload) +} + +func (s *UCLPCServerSuite) Test_configurationDataUpdate() { + localDevice := s.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.configurationDataUpdate(payload) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + lFeature := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + lFeature.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData) + + s.sut.configurationDataUpdate(payload) + + data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) + + data = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{}), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{}), + }, + }, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) +} diff --git a/uclpcserver/public.go b/uclpcserver/public.go index ad46c93..8e58b2f 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -30,16 +30,17 @@ func (e *UCLPCServer) ConsumptionLimit() (limit api.LoadLimit, resultErr error) } resultErr = eebusapi.ErrDataNotAvailable - description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( e.service, model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeConsume, model.ScopeTypeTypeActivePowerLimit, ) - if description.LimitId == nil { + if len(descriptions) != 1 || descriptions[0].LimitId == nil { return } + description := descriptions[0] value := util.GetLocalLimitValueForLimitId(e.service, *description.LimitId) if value.LimitId == nil || value.Value == nil { @@ -62,16 +63,17 @@ func (e *UCLPCServer) ConsumptionLimit() (limit api.LoadLimit, resultErr error) func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) { resultErr = eebusapi.ErrDataNotAvailable - description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( e.service, model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeConsume, model.ScopeTypeTypeActivePowerLimit, ) - if description.LimitId == nil { + if len(descriptions) != 1 || descriptions[0].LimitId == nil { return } + description := descriptions[0] localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/uclpcserver/types.go b/uclpcserver/types.go index 418f176..bca31aa 100644 --- a/uclpcserver/types.go +++ b/uclpcserver/types.go @@ -3,7 +3,7 @@ package uclpcserver import "github.com/enbility/cemd/api" const ( - // Load control obligation limit data updated + // Load control obligation limit data update received // // The callback with this message provides: // - the device of the e.g. SMGW @@ -13,26 +13,22 @@ const ( DataUpdateLimit api.EventType = "DataUpdateLimit" // Failsafe limit for the consumed active (real) power of the - // Controllable System data updated + // Controllable System data update received // // The callback with this message provides: // - the device of the e.g. SMGW // - the entity of the e.g. SMGW // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeConsumptionActivePowerLimit api.EventType = "DataUpdateFailsafeConsumptionActivePowerLimit" // Minimum time the Controllable System remains in "failsafe state" unless conditions - // specified in this Use Case permit leaving the "failsafe state" data updated + // specified in this Use Case permit leaving the "failsafe state" data update received // // The callback with this message provides: // - the device of the e.g. SMGW // - the entity of the e.g. SMGW // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" ) diff --git a/uclpp/events.go b/uclpp/events.go index fd81811..61ad08b 100644 --- a/uclpp/events.go +++ b/uclpp/events.go @@ -68,28 +68,13 @@ func (e *UCLPP) loadControlLimitDescriptionDataUpdate(entity spineapi.EntityRemo // the load control limit data was updated func (e *UCLPP) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - loadControl, err := util.LoadControl(e.service, payload.Entity) - if err != nil { - return - } - - data, err := loadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) - if err != nil { - return - } - - for _, item := range data { - if item.LimitId == nil { - continue - } - - _, err := loadControl.GetLimitValueForLimitId(*item.LimitId) - if err != nil { - continue - } - + if util.LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope( + false, e.service, payload, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) - return } } @@ -105,10 +90,10 @@ func (e *UCLPP) configurationDescriptionDataUpdate(entity spineapi.EntityRemoteI // the configuration key data was updated func (e *UCLPP) configurationDataUpdate(payload spineapi.EventPayload) { - if _, err := e.FailsafeProductionActivePowerLimit(payload.Entity); err != nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(false, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeProductionActivePowerLimit) } - if _, err := e.FailsafeDurationMinimum(payload.Entity); err != nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(false, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) } } diff --git a/uclpp/events_test.go b/uclpp/events_test.go index b51014a..8790725 100644 --- a/uclpp/events_test.go +++ b/uclpp/events_test.go @@ -56,8 +56,11 @@ func (s *UCLPPSuite) Test_loadControlLimitDataUpdate() { descData := &model.LoadControlLimitDescriptionListDataType{ LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), - LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), }, }, } @@ -69,6 +72,14 @@ func (s *UCLPPSuite) Test_loadControlLimitDataUpdate() { s.sut.loadControlLimitDataUpdate(payload) data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = data + + s.sut.loadControlLimitDataUpdate(payload) + + data = &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), @@ -77,8 +88,60 @@ func (s *UCLPPSuite) Test_loadControlLimitDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.loadControlLimitDataUpdate(payload) } + +func (s *UCLPPSuite) Test_configurationDataUpdate() { + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.configurationDataUpdate(payload) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + s.sut.configurationDataUpdate(payload) + + data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) + + data = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + Value: &model.DeviceConfigurationKeyValueValueType{}, + }, + }, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) +} diff --git a/uclpp/types.go b/uclpp/types.go index f54c515..1fcc382 100644 --- a/uclpp/types.go +++ b/uclpp/types.go @@ -20,8 +20,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeProductionActivePowerLimit api.EventType = "DataUpdateFailsafeProductionActivePowerLimit" // Minimum time the Controllable System remains in "failsafe state" unless conditions @@ -32,7 +30,5 @@ const ( // - the entity of the e.g. EVSE // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" ) diff --git a/uclppserver/events.go b/uclppserver/events.go index 0e34692..409fbb3 100644 --- a/uclppserver/events.go +++ b/uclppserver/events.go @@ -136,17 +136,22 @@ func (e *UCLPPServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload // the load control limit data was updated func (e *UCLPPServer) loadControlLimitDataUpdate(payload spineapi.EventPayload) { - if _, err := e.ProductionLimit(); err == nil { + if util.LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope( + true, e.service, payload, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) } } // the configuration key data of an SMGW was updated func (e *UCLPPServer) configurationDataUpdate(payload spineapi.EventPayload) { - if _, _, err := e.FailsafeProductionActivePowerLimit(); err == nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(true, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeProductionActivePowerLimit) } - if _, _, err := e.FailsafeDurationMinimum(); err == nil { + if util.DeviceConfigurationCheckDataPayloadForKeyName(true, e.service, payload, model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFailsafeDurationMinimum) } } diff --git a/uclppserver/events_test.go b/uclppserver/events_test.go index 3307de5..5146c2f 100644 --- a/uclppserver/events_test.go +++ b/uclppserver/events_test.go @@ -202,3 +202,108 @@ func (s *UCLPPServerSuite) Test_multipleDeviceDiagServer() { s.sut.subscribeHeartbeatWorkaround(payload) } + +func (s *UCLPPServerSuite) Test_loadControlLimitDataUpdate() { + localDevice := s.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.loadControlLimitDataUpdate(payload) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeSignDependentAbsValueLimit), + LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + LimitDirection: eebusutil.Ptr(model.EnergyDirectionTypeProduce), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeActivePowerLimit), + }, + }, + } + + lFeature := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + lFeature.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, descData) + + s.sut.loadControlLimitDataUpdate(payload) + + data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = data + + s.sut.loadControlLimitDataUpdate(payload) + + data = &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + }, + } + + payload.Data = data + + s.sut.loadControlLimitDataUpdate(payload) +} + +func (s *UCLPPServerSuite) Test_configurationDataUpdate() { + localDevice := s.service.LocalDevice() + localEntity := localDevice.EntityForType(model.EntityTypeTypeCEM) + + payload := spineapi.EventPayload{ + Ski: remoteSki, + Device: s.remoteDevice, + Entity: s.monitoredEntity, + } + s.sut.configurationDataUpdate(payload) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + KeyName: eebusutil.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + }, + } + + lFeature := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + lFeature.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData) + + s.sut.configurationDataUpdate(payload) + + data := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) + + data = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(1)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{}), + }, + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(2)), + Value: eebusutil.Ptr(model.DeviceConfigurationKeyValueValueType{}), + }, + }, + } + + payload.Data = data + + s.sut.configurationDataUpdate(payload) +} diff --git a/uclppserver/public.go b/uclppserver/public.go index 3a82819..f6bf54c 100644 --- a/uclppserver/public.go +++ b/uclppserver/public.go @@ -30,16 +30,17 @@ func (e *UCLPPServer) ProductionLimit() (limit api.LoadLimit, resultErr error) { } resultErr = eebusapi.ErrDataNotAvailable - description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( e.service, model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeProduce, model.ScopeTypeTypeActivePowerLimit, ) - if description.LimitId == nil { + if len(descriptions) != 1 || descriptions[0].LimitId == nil { return } + description := descriptions[0] value := util.GetLocalLimitValueForLimitId(e.service, *description.LimitId) if value.LimitId == nil || value.Value == nil { @@ -62,16 +63,17 @@ func (e *UCLPPServer) ProductionLimit() (limit api.LoadLimit, resultErr error) { func (e *UCLPPServer) SetProductionLimit(limit api.LoadLimit) (resultErr error) { resultErr = eebusapi.ErrDataNotAvailable - description := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( e.service, model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, model.LoadControlCategoryTypeObligation, model.EnergyDirectionTypeProduce, model.ScopeTypeTypeActivePowerLimit, ) - if description.LimitId == nil { + if len(descriptions) != 1 || descriptions[0].LimitId == nil { return } + description := descriptions[0] localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) diff --git a/uclppserver/types.go b/uclppserver/types.go index e780589..0303d57 100644 --- a/uclppserver/types.go +++ b/uclppserver/types.go @@ -3,7 +3,7 @@ package uclppserver import "github.com/enbility/cemd/api" const ( - // Load control obligation limit data updated + // Load control obligation limit data update received // // The callback with this message provides: // - the device of the e.g. SMGW @@ -13,26 +13,22 @@ const ( DataUpdateLimit api.EventType = "DataUpdateLimit" // Failsafe limit for the produced active (real) power of the - // Controllable System data updated + // Controllable System data update received // // The callback with this message provides: // - the device of the e.g. SMGW // - the entity of the e.g. SMGW // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeProductionActivePowerLimit api.EventType = "DataUpdateFailsafeProductionActivePowerLimit" // Minimum time the Controllable System remains in "failsafe state" unless conditions - // specified in this Use Case permit leaving the "failsafe state" data updated + // specified in this Use Case permit leaving the "failsafe state" data update received // // The callback with this message provides: // - the device of the e.g. SMGW // - the entity of the e.g. SMGW // // Use Case LPC, Scenario 2 - // - // Note: the referred data may be updated together with all other configuration items of this use case DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" ) diff --git a/ucmgcp/events.go b/ucmgcp/events.go index 3d9419f..272b35c 100644 --- a/ucmgcp/events.go +++ b/ucmgcp/events.go @@ -111,32 +111,32 @@ func (e *UCMGCP) gridMeasurementDescriptionDataUpdate(entity spineapi.EntityRemo // the measurement data of an SMGW was updated func (e *UCMGCP) gridMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACPowerTotal) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeGridFeedIn); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeGridFeedIn) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyFeedIn) } // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeGridConsumption); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeGridConsumption) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyConsumed) } // Scenario 5 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACCurrent); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACCurrent) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentPerPhase) } // Scenario 6 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACVoltage); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACVoltage) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateVoltagePerPhase) } // Scenario 7 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACFrequency); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACFrequency) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFrequency) } } diff --git a/ucmgcp/events_test.go b/ucmgcp/events_test.go index c7cebb4..3587b77 100644 --- a/ucmgcp/events_test.go +++ b/ucmgcp/events_test.go @@ -154,8 +154,7 @@ func (s *UCMGCPSuite) Test_gridMeasurementDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.gridMeasurementDataUpdate(payload) } diff --git a/ucmgcp/types.go b/ucmgcp/types.go index c6df7b7..3b50296 100644 --- a/ucmgcp/types.go +++ b/ucmgcp/types.go @@ -11,8 +11,6 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePowerLimitationFactor api.EventType = "DataUpdatePowerLimitationFactor" // Grid momentary power consumption/production data updated @@ -22,8 +20,6 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePower api.EventType = "DataUpdatePower" // Total grid feed in energy data updated @@ -33,8 +29,6 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyFeedIn api.EventType = "DataUpdateEnergyFeedIn" // Total grid consumed energy data updated @@ -44,8 +38,6 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 4 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyConsumed api.EventType = "DataUpdateEnergyConsumed" // Phase specific momentary current consumption/production phase detail data updated @@ -55,8 +47,6 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 5 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateCurrentPerPhase api.EventType = "DataUpdateCurrentPerPhase" // Phase specific voltage at the grid connection point @@ -66,8 +56,6 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 6 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateVoltagePerPhase api.EventType = "DataUpdateVoltagePerPhase" // Grid frequency data updated @@ -77,7 +65,5 @@ const ( // - the entity of the e.g. SMGW // // Use Case MGCP, Scenario 7 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateFrequency api.EventType = "DataUpdateFrequency" ) diff --git a/ucmpc/events.go b/ucmpc/events.go index 7ee1a30..36ffa9a 100644 --- a/ucmpc/events.go +++ b/ucmpc/events.go @@ -79,35 +79,35 @@ func (e *UCMPC) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRem // the measurement data of a device was updated func (e *UCMPC) deviceMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACPowerTotal) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPower); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACPower) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePowerPerPhase) } // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACEnergyConsumed); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACEnergyConsumed) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyConsumed) } - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACEnergyProduced); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACEnergyProduced) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyProduced) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACCurrent); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACCurrent) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateCurrentsPerPhase) } // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACVoltage); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACVoltage) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateVoltagePerPhase) } // Scenario 5 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACFrequency); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACFrequency) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateFrequency) } } diff --git a/ucmpc/events_test.go b/ucmpc/events_test.go index 3494d3a..9a98f78 100644 --- a/ucmpc/events_test.go +++ b/ucmpc/events_test.go @@ -119,8 +119,7 @@ func (s *UCMPCSuite) Test_deviceMeasurementDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.deviceMeasurementDataUpdate(payload) } diff --git a/ucmpc/types.go b/ucmpc/types.go index a3cfcc0..5c0bd6d 100644 --- a/ucmpc/types.go +++ b/ucmpc/types.go @@ -10,8 +10,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePower api.EventType = "DataUpdatePower" // Phase specific momentary active power consumption or production @@ -21,8 +19,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePowerPerPhase api.EventType = "DataUpdatePowerPerPhase" // Total energy consumed @@ -32,8 +28,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyConsumed api.EventType = "DataUpdateEnergyConsumed" // Total energy produced @@ -43,8 +37,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyProduced api.EventType = "DataUpdateEnergyProduced" // Phase specific momentary current consumption or production @@ -54,8 +46,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateCurrentsPerPhase api.EventType = "DataUpdateCurrentsPerPhase" // Phase specific voltage @@ -65,8 +55,6 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateVoltagePerPhase api.EventType = "DataUpdateVoltagePerPhase" // Power network frequency data updated @@ -76,7 +64,5 @@ const ( // - the entity of the e.g. EVSE // // Use Case MCP, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateFrequency api.EventType = "DataUpdateFrequency" ) diff --git a/ucopev/events.go b/ucopev/events.go index 737b936..369a02f 100644 --- a/ucopev/events.go +++ b/ucopev/events.go @@ -71,28 +71,10 @@ func (e *UCOPEV) evLoadControlLimitDescriptionDataUpdate(entity spineapi.EntityR // the load control limit data of an EV was updated func (e *UCOPEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { - evLoadControl, err := util.LoadControl(e.service, payload.Entity) - if err != nil { - return - } - - data, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeObligation) - if err != nil { - return - } - - for _, item := range data { - if item.LimitId == nil { - continue - } - - _, err := evLoadControl.GetLimitValueForLimitId(*item.LimitId) - if err != nil { - continue - } - + if util.LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, + e.service, payload, model.LoadControlLimitTypeTypeMaxValueLimit, + model.LoadControlCategoryTypeObligation, "", model.ScopeTypeTypeOverloadProtection) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) - return } } diff --git a/ucopev/events_test.go b/ucopev/events_test.go index e0da71c..534b7c4 100644 --- a/ucopev/events_test.go +++ b/ucopev/events_test.go @@ -133,7 +133,9 @@ func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeMaxValueLimit), LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeObligation), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeOverloadProtection), }, }, } @@ -145,6 +147,14 @@ func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { s.sut.evLoadControlLimitDataUpdate(payload) data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = data + + s.sut.evLoadControlLimitDataUpdate(payload) + + data = &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), @@ -153,8 +163,7 @@ func (s *UCOPEVSuite) Test_evLoadControlLimitDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.evLoadControlLimitDataUpdate(payload) } diff --git a/ucoscev/events.go b/ucoscev/events.go index 07b08a7..aff48d0 100644 --- a/ucoscev/events.go +++ b/ucoscev/events.go @@ -32,28 +32,11 @@ func (e *UCOSCEV) HandleEvent(payload spineapi.EventPayload) { // the load control limit data of an EV was updated func (e *UCOSCEV) evLoadControlLimitDataUpdate(payload spineapi.EventPayload) { - evLoadControl, err := util.LoadControl(e.service, payload.Entity) - if err != nil { - return - } - - data, err := evLoadControl.GetLimitDescriptionsForCategory(model.LoadControlCategoryTypeRecommendation) - if err != nil { - return - } - - for _, item := range data { - if item.LimitId == nil { - continue - } - - _, err := evLoadControl.GetLimitValueForLimitId(*item.LimitId) - if err != nil { - continue - } - + if util.LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, + e.service, payload, model.LoadControlLimitTypeTypeMaxValueLimit, + model.LoadControlCategoryTypeRecommendation, "", + model.ScopeTypeTypeSelfConsumption) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateLimit) - return } } diff --git a/ucoscev/events_test.go b/ucoscev/events_test.go index 8981bf4..582e8ca 100644 --- a/ucoscev/events_test.go +++ b/ucoscev/events_test.go @@ -125,6 +125,8 @@ func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), LimitCategory: eebusutil.Ptr(model.LoadControlCategoryTypeRecommendation), + LimitType: eebusutil.Ptr(model.LoadControlLimitTypeTypeMaxValueLimit), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeSelfConsumption), }, }, } @@ -136,6 +138,14 @@ func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { s.sut.evLoadControlLimitDataUpdate(payload) data := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = data + + s.sut.evLoadControlLimitDataUpdate(payload) + + data = &model.LoadControlLimitListDataType{ LoadControlLimitData: []model.LoadControlLimitDataType{ { LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), @@ -144,8 +154,7 @@ func (s *UCOSCEVSuite) Test_evLoadControlLimitDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeLoadControlLimitListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.evLoadControlLimitDataUpdate(payload) } diff --git a/ucvabd/events.go b/ucvabd/events.go index 54ddcde..e4aa10c 100644 --- a/ucvabd/events.go +++ b/ucvabd/events.go @@ -79,22 +79,22 @@ func (e *UCVABD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity // the measurement data of an SMGW was updated func (e *UCVABD) inverterMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 1 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACPowerTotal) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeCharge); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeCharge) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyCharged) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeDischarge); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeDischarge) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateEnergyDischarged) } // Scenario 4 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeStateOfCharge); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeStateOfCharge) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateStateOfCharge) } } diff --git a/ucvabd/events_test.go b/ucvabd/events_test.go index 6482df4..aed4b5f 100644 --- a/ucvabd/events_test.go +++ b/ucvabd/events_test.go @@ -101,8 +101,7 @@ func (s *UCVABDSuite) Test_inverterMeasurementDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.inverterMeasurementDataUpdate(payload) } diff --git a/ucvabd/types.go b/ucvabd/types.go index 858a81d..c9a9bf9 100644 --- a/ucvabd/types.go +++ b/ucvabd/types.go @@ -10,8 +10,6 @@ const ( // - the entity of the inverter // // Use Case VABD, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePower api.EventType = "d" // Battery System cumulated charge energy data updated @@ -21,8 +19,6 @@ const ( // - the entity of the inverter // // Use Case VABD, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyCharged api.EventType = "DataUpdateEnergyCharged" // Battery System cumulated discharge energy data updated @@ -32,8 +28,6 @@ const ( // - the entity of the inverter // // Use Case VABD, Scenario 2 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateEnergyDischarged api.EventType = "DataUpdateEnergyDischarged" // Battery System state of charge data updated @@ -43,7 +37,5 @@ const ( // - the entity of the inverter // // Use Case VABD, Scenario 4 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdateStateOfCharge api.EventType = "DataUpdateStateOfCharge" ) diff --git a/ucvapd/events.go b/ucvapd/events.go index a97af4d..3210dfb 100644 --- a/ucvapd/events.go +++ b/ucvapd/events.go @@ -116,12 +116,12 @@ func (e *UCVAPD) inverterMeasurementDescriptionDataUpdate(entity spineapi.Entity // the measurement data of an SMGW was updated func (e *UCVAPD) inverterMeasurementDataUpdate(payload spineapi.EventPayload) { // Scenario 2 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACPowerTotal); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACPowerTotal) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePower) } // Scenario 3 - if _, err := util.MeasurementValueForScope(e.service, payload.Entity, model.ScopeTypeTypeACYieldTotal); err == nil { + if util.MeasurementCheckPayloadDataForScope(e.service, payload, model.ScopeTypeTypeACYieldTotal) { e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdatePVYieldTotal) } } diff --git a/ucvapd/events_test.go b/ucvapd/events_test.go index 0a8e1b3..493bc94 100644 --- a/ucvapd/events_test.go +++ b/ucvapd/events_test.go @@ -130,8 +130,7 @@ func (s *UCVAPDSuite) Test_inverterMeasurementDataUpdate() { }, } - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data s.sut.inverterMeasurementDataUpdate(payload) } diff --git a/ucvapd/types.go b/ucvapd/types.go index 4488008..2255cd8 100644 --- a/ucvapd/types.go +++ b/ucvapd/types.go @@ -10,8 +10,6 @@ const ( // - the entity of the inverter // // Use Case VAPD, Scenario 1 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePower api.EventType = "DataUpdatePower" // PV System nominal peak power data updated @@ -30,7 +28,5 @@ const ( // - the entity of the inverter // // Use Case VAPD, Scenario 3 - // - // Note: the referred data may be updated together with all other measurement items of this use case DataUpdatePVYieldTotal api.EventType = "DataUpdatePVYieldTotal" ) diff --git a/util/deviceconfiguration.go b/util/deviceconfiguration.go index 6d4f9a1..f3ff268 100644 --- a/util/deviceconfiguration.go +++ b/util/deviceconfiguration.go @@ -3,15 +3,52 @@ package util import ( eebusapi "github.com/enbility/eebus-go/api" eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" ) +func DeviceConfigurationCheckDataPayloadForKeyName(localServer bool, service eebusapi.ServiceInterface, + payload spineapi.EventPayload, keyName model.DeviceConfigurationKeyNameType) bool { + var desc *model.DeviceConfigurationKeyValueDescriptionDataType + var data *model.DeviceConfigurationKeyValueListDataType + + if payload.Data == nil { + return false + } + data = payload.Data.(*model.DeviceConfigurationKeyValueListDataType) + + if localServer { + desc = GetLocalDeviceConfigurationDescriptionForKeyName(service, keyName) + } else { + deviceconfigF, err := DeviceConfiguration(service, payload.Entity) + if err != nil || payload.Data == nil { + return false + } + + desc, err = deviceconfigF.GetDescriptionForKeyName(keyName) + if err != nil { + return false + } + } + + for _, item := range data.DeviceConfigurationKeyValueData { + if item.KeyId == nil || *item.KeyId != *desc.KeyId || + item.Value == nil { + continue + } + + return true + } + + return false +} + func GetLocalDeviceConfigurationDescriptionForKeyName( service eebusapi.ServiceInterface, keyName model.DeviceConfigurationKeyNameType, -) (description model.DeviceConfigurationKeyValueDescriptionDataType) { - description = model.DeviceConfigurationKeyValueDescriptionDataType{} +) (description *model.DeviceConfigurationKeyValueDescriptionDataType) { + description = &model.DeviceConfigurationKeyValueDescriptionDataType{} localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) @@ -28,8 +65,7 @@ func GetLocalDeviceConfigurationDescriptionForKeyName( for _, desc := range data.DeviceConfigurationKeyValueDescriptionData { if desc.KeyName != nil && *desc.KeyName == keyName { - description = desc - break + return &desc } } diff --git a/util/deviceconfiguration_test.go b/util/deviceconfiguration_test.go index 4625dd2..4ecce04 100644 --- a/util/deviceconfiguration_test.go +++ b/util/deviceconfiguration_test.go @@ -2,10 +2,68 @@ package util import ( eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) +func (s *UtilSuite) Test_DeviceConfigurationCheckPayloadForKeyName() { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + + exists := DeviceConfigurationCheckDataPayloadForKeyName(false, s.service, payload, keyName) + assert.False(s.T(), exists) + + payload.Entity = s.monitoredEntity + + exists = DeviceConfigurationCheckDataPayloadForKeyName(false, s.service, payload, keyName) + assert.False(s.T(), exists) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(keyName), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + exists = DeviceConfigurationCheckDataPayloadForKeyName(false, s.service, payload, keyName) + assert.False(s.T(), exists) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = keyData + + exists = DeviceConfigurationCheckDataPayloadForKeyName(false, s.service, payload, keyName) + assert.False(s.T(), exists) + + keyData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeIEC61851), + }, + }, + }, + } + + payload.Data = keyData + + exists = DeviceConfigurationCheckDataPayloadForKeyName(false, s.service, payload, keyName) + assert.True(s.T(), exists) +} + func (s *UtilSuite) Test_GetLocalDeviceConfigurationDescriptionForKeyName() { keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit diff --git a/util/features_test.go b/util/features_test.go index f5f72b9..f7072b1 100644 --- a/util/features_test.go +++ b/util/features_test.go @@ -7,10 +7,18 @@ func (s *UtilSuite) Test_Features() { assert.Nil(s.T(), feature1) assert.NotNil(s.T(), err) - feature2, err := DeviceConfiguration(s.service, s.monitoredEntity) + feature11, err := DeviceClassification(s.service, s.monitoredEntity) + assert.NotNil(s.T(), feature11) + assert.Nil(s.T(), err) + + feature2, err := DeviceConfiguration(s.service, s.evseEntity) assert.Nil(s.T(), feature2) assert.NotNil(s.T(), err) + feature21, err := DeviceConfiguration(s.service, s.monitoredEntity) + assert.NotNil(s.T(), feature21) + assert.Nil(s.T(), err) + feature3, err := DeviceDiagnosis(s.service, s.monitoredEntity) assert.Nil(s.T(), feature3) assert.NotNil(s.T(), err) diff --git a/util/loadcontrol.go b/util/loadcontrol.go index 5a64446..c0a6b13 100644 --- a/util/loadcontrol.go +++ b/util/loadcontrol.go @@ -9,6 +9,54 @@ import ( "github.com/enbility/spine-go/spine" ) +// Check the payload data if it contains measurementId values for a given scope +func LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope( + localServer bool, + service eebusapi.ServiceInterface, + payload spineapi.EventPayload, + limitType model.LoadControlLimitTypeType, + limitCategory model.LoadControlCategoryType, + direction model.EnergyDirectionType, + scope model.ScopeTypeType) bool { + var descs []model.LoadControlLimitDescriptionDataType + + if payload.Data == nil { + return false + } + + limits := payload.Data.(*model.LoadControlLimitListDataType) + + if localServer { + descs = GetLocalLimitDescriptionsForTypeCategoryDirectionScope(service, limitType, limitCategory, direction, scope) + } else { + loadcontrolF, err := LoadControl(service, payload.Entity) + if err != nil { + return false + } + + descs, err = loadcontrolF.GetLimitDescriptionsForTypeCategoryDirectionScope(limitType, limitCategory, direction, scope) + if err != nil { + return false + } + } + + for _, item := range descs { + if item.LimitId == nil { + continue + } + + for _, limit := range limits.LoadControlLimitData { + if limit.LimitId != nil && + *limit.LimitId == *item.LimitId && + limit.Value != nil { + return true + } + } + } + + return false +} + // return the current loadcontrol limits for a categoriy // // possible errors: @@ -207,8 +255,8 @@ func GetLocalLimitDescriptionsForTypeCategoryDirectionScope( limitCategory model.LoadControlCategoryType, limitDirection model.EnergyDirectionType, scopeType model.ScopeTypeType, -) (description model.LoadControlLimitDescriptionDataType) { - description = model.LoadControlLimitDescriptionDataType{} +) (descriptions []model.LoadControlLimitDescriptionDataType) { + descriptions = []model.LoadControlLimitDescriptionDataType{} localEntity := service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) @@ -224,16 +272,16 @@ func GetLocalLimitDescriptionsForTypeCategoryDirectionScope( } for _, desc := range data.LoadControlLimitDescriptionData { - if desc.LimitType != nil && *desc.LimitType == limitType && - desc.LimitCategory != nil && *desc.LimitCategory == limitCategory && - desc.LimitDirection != nil && *desc.LimitDirection == limitDirection && - desc.ScopeType != nil && *desc.ScopeType == scopeType { - description = desc - break + if desc.LimitId != nil && + (limitType == "" || (desc.LimitType != nil && *desc.LimitType == limitType)) && + (limitCategory == "" || (desc.LimitCategory != nil && *desc.LimitCategory == limitCategory)) && + (limitDirection == "" || (desc.LimitDirection != nil && *desc.LimitDirection == limitDirection)) && + (scopeType == "" || (desc.ScopeType != nil && *desc.ScopeType == scopeType)) { + descriptions = append(descriptions, desc) } } - return description + return descriptions } func GetLocalLimitValueForLimitId( diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index df1919c..15ccb67 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -5,10 +5,121 @@ import ( "github.com/enbility/cemd/api" eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) +func (s *UtilSuite) Test_LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope() { + limitType := model.LoadControlLimitTypeTypeMaxValueLimit + scope := model.ScopeTypeTypeSelfConsumption + category := model.LoadControlCategoryTypeObligation + direction := model.EnergyDirectionType("") + + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + + exists := LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + payload.Entity = s.monitoredEntity + + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), + }, + }, + } + + rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + fErr := rFeature.UpdateData(model.FunctionTypeLoadControlLimitDescriptionListData, descData, nil, nil) + assert.Nil(s.T(), fErr) + + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + fErr = rElFeature.UpdateData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData, nil, nil) + assert.Nil(s.T(), fErr) + + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = limitData + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + limitData = &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), + Value: model.NewScaledNumberType(16), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), + }, + }, + } + + payload.Data = limitData + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(false, s.service, payload, limitType, category, direction, scope) + assert.True(s.T(), exists) +} + func (s *UtilSuite) Test_LoadControlLimits() { var data []api.LoadLimitsPhase var err error @@ -380,7 +491,7 @@ func (s *UtilSuite) Test_GetLocalLimitDescriptionsForTypeCategoryDirectionScope( limitScopeType := model.ScopeTypeTypeActivePowerLimit data := GetLocalLimitDescriptionsForTypeCategoryDirectionScope(s.service, limitType, limitCategory, limitDirection, limitScopeType) - assert.Nil(s.T(), data.LimitId) + assert.Equal(s.T(), 0, len(data)) entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) @@ -399,7 +510,8 @@ func (s *UtilSuite) Test_GetLocalLimitDescriptionsForTypeCategoryDirectionScope( feature.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, desc) data = GetLocalLimitDescriptionsForTypeCategoryDirectionScope(s.service, limitType, limitCategory, limitDirection, limitScopeType) - assert.NotNil(s.T(), data.LimitId) + assert.Equal(s.T(), 1, len(data)) + assert.NotNil(s.T(), data[0].LimitId) } func (s *UtilSuite) Test_GetLocalLimitValueForLimitId() { diff --git a/util/measurement.go b/util/measurement.go index a54307a..eb0b40c 100644 --- a/util/measurement.go +++ b/util/measurement.go @@ -8,29 +8,32 @@ import ( "github.com/enbility/spine-go/model" ) -// return the measurement value for a scope or an error -func MeasurementValueForScope( - service eebusapi.ServiceInterface, - entity spineapi.EntityRemoteInterface, - scope model.ScopeTypeType) (float64, error) { - measurementF, err := Measurement(service, entity) - if err != nil { - return 0, eebusapi.ErrFunctionNotSupported +// Check the payload data if it contains measurementId values for a given scope +func MeasurementCheckPayloadDataForScope(service eebusapi.ServiceInterface, payload spineapi.EventPayload, scope model.ScopeTypeType) bool { + measurementF, err := Measurement(service, payload.Entity) + if err != nil || payload.Data == nil { + return false } if data, err := measurementF.GetDescriptionsForScope(scope); err == nil { + measurements := payload.Data.(*model.MeasurementListDataType) + for _, item := range data { if item.MeasurementId == nil { continue } - if value, err := measurementF.GetValueForMeasurementId(*item.MeasurementId); err == nil { - return value, nil + for _, measurement := range measurements.MeasurementData { + if measurement.MeasurementId != nil && + *measurement.MeasurementId == *item.MeasurementId && + measurement.Value != nil { + return true + } } } } - return 0, eebusapi.ErrDataNotAvailable + return false } // return the phase specific voltage details diff --git a/util/measurement_test.go b/util/measurement_test.go index 336d79b..4a96889 100644 --- a/util/measurement_test.go +++ b/util/measurement_test.go @@ -2,23 +2,31 @@ package util import ( eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) -func (s *UtilSuite) Test_MeasurementValueForScope() { - value, err := MeasurementValueForScope(s.service, s.mockRemoteEntity, model.ScopeTypeTypeACPower) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, value) +func (s *UtilSuite) Test_MeasurementCheckPayloadDataForScope() { + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } - value, err = MeasurementValueForScope(s.service, s.monitoredEntity, model.ScopeTypeTypeACPower) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, value) + exists := MeasurementCheckPayloadDataForScope(s.service, payload, model.ScopeTypeTypeACPower) + assert.False(s.T(), exists) + + payload.Entity = s.monitoredEntity + + exists = MeasurementCheckPayloadDataForScope(s.service, payload, model.ScopeTypeTypeACPower) + assert.False(s.T(), exists) descData := &model.MeasurementDescriptionListDataType{ MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ { - MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), ScopeType: eebusutil.Ptr(model.ScopeTypeTypeACPower), }, }, @@ -28,25 +36,35 @@ func (s *UtilSuite) Test_MeasurementValueForScope() { fErr := rFeature.UpdateData(model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) assert.Nil(s.T(), fErr) - value, err = MeasurementValueForScope(s.service, s.monitoredEntity, model.ScopeTypeTypeACPower) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), 0.0, value) + exists = MeasurementCheckPayloadDataForScope(s.service, payload, model.ScopeTypeTypeACPower) + assert.False(s.T(), exists) data := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + {}, + }, + } + payload.Data = data + + exists = MeasurementCheckPayloadDataForScope(s.service, payload, model.ScopeTypeTypeACPower) + assert.False(s.T(), exists) + + data = &model.MeasurementListDataType{ MeasurementData: []model.MeasurementDataType{ { - MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(80), + }, + { + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), Value: model.NewScaledNumberType(80), }, }, } - fErr = rFeature.UpdateData(model.FunctionTypeMeasurementListData, data, nil, nil) - assert.Nil(s.T(), fErr) + payload.Data = data - value, err = MeasurementValueForScope(s.service, s.monitoredEntity, model.ScopeTypeTypeACPower) - assert.Nil(s.T(), err) - assert.Equal(s.T(), 80.0, value) + exists = MeasurementCheckPayloadDataForScope(s.service, payload, model.ScopeTypeTypeACPower) + assert.True(s.T(), exists) } func (s *UtilSuite) Test_MeasurementValuesForTypeCommodityScope() { diff --git a/util/testhelper_test.go b/util/testhelper_test.go index 7014e0d..53cd607 100644 --- a/util/testhelper_test.go +++ b/util/testhelper_test.go @@ -87,6 +87,8 @@ func setupDevices( localEntity.AddFeature(f) f = spine.NewFeatureLocal(4, localEntity, model.FeatureTypeTypeDeviceClassification, model.RoleTypeClient) localEntity.AddFeature(f) + f = spine.NewFeatureLocal(5, localEntity, model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeClient) + localEntity.AddFeature(f) f = spine.NewFeatureLocal(1, localEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) @@ -110,7 +112,7 @@ func setupDevices( sender := spine.NewSender(writeHandler) remoteDevice := spine.NewDeviceRemote(localDevice, remoteSki, sender) - var clientRemoteFeatures = []struct { + var remoteFeatures = []struct { featureType model.FeatureTypeType role model.RoleType supportedFcts []model.FunctionType @@ -144,12 +146,19 @@ func setupDevices( model.FunctionTypeDeviceClassificationUserData, }, }, + {model.FeatureTypeTypeDeviceConfiguration, + model.RoleTypeServer, + []model.FunctionType{ + model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, + model.FunctionTypeDeviceConfigurationKeyValueListData, + }, + }, } remoteDeviceName := "remote" var featureInformations []model.NodeManagementDetailedDiscoveryFeatureInformationType - for index, feature := range clientRemoteFeatures { + for index, feature := range remoteFeatures { supportedFcts := []model.FunctionPropertyType{} for _, fct := range feature.supportedFcts { supportedFct := model.FunctionPropertyType{ From b9a01300f6607ae3394848d66562046b011754e3 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 13 May 2024 13:46:19 +0200 Subject: [PATCH 208/227] Add tests --- util/deviceconfiguration.go | 2 +- util/deviceconfiguration_test.go | 57 ++++++++++++++++ util/loadcontrol_test.go | 109 +++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) diff --git a/util/deviceconfiguration.go b/util/deviceconfiguration.go index f3ff268..928231c 100644 --- a/util/deviceconfiguration.go +++ b/util/deviceconfiguration.go @@ -22,7 +22,7 @@ func DeviceConfigurationCheckDataPayloadForKeyName(localServer bool, service eeb desc = GetLocalDeviceConfigurationDescriptionForKeyName(service, keyName) } else { deviceconfigF, err := DeviceConfiguration(service, payload.Entity) - if err != nil || payload.Data == nil { + if err != nil { return false } diff --git a/util/deviceconfiguration_test.go b/util/deviceconfiguration_test.go index 4ecce04..c950765 100644 --- a/util/deviceconfiguration_test.go +++ b/util/deviceconfiguration_test.go @@ -7,6 +7,63 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UtilSuite) Test_DeviceConfigurationCheckPayloadForKeyNameLocal() { + keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit + + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + + exists := DeviceConfigurationCheckDataPayloadForKeyName(true, s.service, payload, keyName) + assert.False(s.T(), exists) + + payload.Entity = s.monitoredEntity + + exists = DeviceConfigurationCheckDataPayloadForKeyName(true, s.service, payload, keyName) + assert.False(s.T(), exists) + + descData := &model.DeviceConfigurationKeyValueDescriptionListDataType{ + DeviceConfigurationKeyValueDescriptionData: []model.DeviceConfigurationKeyValueDescriptionDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + KeyName: eebusutil.Ptr(keyName), + }, + }, + } + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + feature.SetData(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, descData) + + exists = DeviceConfigurationCheckDataPayloadForKeyName(true, s.service, payload, keyName) + assert.False(s.T(), exists) + + keyData := &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{}, + } + + payload.Data = keyData + + exists = DeviceConfigurationCheckDataPayloadForKeyName(true, s.service, payload, keyName) + assert.False(s.T(), exists) + + keyData = &model.DeviceConfigurationKeyValueListDataType{ + DeviceConfigurationKeyValueData: []model.DeviceConfigurationKeyValueDataType{ + { + KeyId: eebusutil.Ptr(model.DeviceConfigurationKeyIdType(0)), + Value: &model.DeviceConfigurationKeyValueValueType{ + String: eebusutil.Ptr(model.DeviceConfigurationKeyValueStringTypeIEC61851), + }, + }, + }, + } + + payload.Data = keyData + + exists = DeviceConfigurationCheckDataPayloadForKeyName(true, s.service, payload, keyName) + assert.True(s.T(), exists) +} + func (s *UtilSuite) Test_DeviceConfigurationCheckPayloadForKeyName() { keyName := model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit diff --git a/util/loadcontrol_test.go b/util/loadcontrol_test.go index 15ccb67..1b8bc1c 100644 --- a/util/loadcontrol_test.go +++ b/util/loadcontrol_test.go @@ -10,6 +10,115 @@ import ( "github.com/stretchr/testify/assert" ) +func (s *UtilSuite) Test_LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScopeLocal() { + limitType := model.LoadControlLimitTypeTypeMaxValueLimit + scope := model.ScopeTypeTypeSelfConsumption + category := model.LoadControlCategoryTypeObligation + direction := model.EnergyDirectionType("") + + payload := spineapi.EventPayload{ + Entity: s.mockRemoteEntity, + } + + exists := LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(true, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + payload.Entity = s.monitoredEntity + + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(true, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + descData := &model.LoadControlLimitDescriptionListDataType{ + LoadControlLimitDescriptionData: []model.LoadControlLimitDescriptionDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), + LimitCategory: eebusutil.Ptr(category), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + LimitType: eebusutil.Ptr(limitType), + ScopeType: eebusutil.Ptr(scope), + }, + }, + } + + entity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + feature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + feature.SetData(model.FunctionTypeLoadControlLimitDescriptionListData, descData) + + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(true, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + paramData := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(0)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(0)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(1)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(1)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: eebusutil.Ptr(model.ElectricalConnectionIdType(0)), + ParameterId: eebusutil.Ptr(model.ElectricalConnectionParameterIdType(2)), + MeasurementId: eebusutil.Ptr(model.MeasurementIdType(2)), + AcMeasuredPhases: eebusutil.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + }, + } + + elFeature := entity.FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + elFeature.SetData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramData) + + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(true, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + limitData := &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + } + + payload.Data = limitData + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(true, s.service, payload, limitType, category, direction, scope) + assert.False(s.T(), exists) + + limitData = &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + Value: model.NewScaledNumberType(16), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(1)), + Value: model.NewScaledNumberType(16), + }, + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(2)), + }, + }, + } + + payload.Data = limitData + exists = LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope(true, s.service, payload, limitType, category, direction, scope) + assert.True(s.T(), exists) +} + func (s *UtilSuite) Test_LoadControlLimitsCheckPayloadDataForTypeCategoryDirectionScope() { limitType := model.LoadControlLimitTypeTypeMaxValueLimit scope := model.ScopeTypeTypeSelfConsumption From db87fb3b2fba0f9d2a66ae759922df19d56c6eaa Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 19:34:50 +0200 Subject: [PATCH 209/227] Update SPINE, EEBUS --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b3d9d20..b499bae 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240512154912-a94167bca521 + github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 - github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 + github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 486c666..28d494e 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240512154912-a94167bca521 h1:IabEKRx+OXHx9kKkLB24MstnpOf/JYXB0o9+ml/8RNQ= -github.com/enbility/eebus-go v0.0.0-20240512154912-a94167bca521/go.mod h1:CYr+oO68lMVQns6nZ/EcXWtnV6ei8viPjlrG3Ti1YAg= +github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b h1:8WdMCIKnB289riC3Fcni965IoPXci7LKGbGARPi3AAI= +github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b/go.mod h1:1jzTHrSftixngak1cpaLIwjMJT62qJcKYDvnUS0wB94= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7 h1:dNB7YGajeWQbSAgOg0NXqlq5SdmLh0AVUv+wHWuoWa4= -github.com/enbility/spine-go v0.0.0-20240509174030-97d6413152e7/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 h1:ra+Eom9EQD9YN85WqZ9rmSTxWDi6j1R4SzHx+gHQCqs= +github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From 27b9b6216c61db76b0b69669cd0b86be68f616e7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 21:45:35 +0200 Subject: [PATCH 210/227] Add write approval API to UCLPCServer --- uclpcserver/api.go | 12 +++++++ uclpcserver/public.go | 71 ++++++++++++++++++++++++++++++++++++++ uclpcserver/public_test.go | 70 +++++++++++++++++++++++++++++++++++++ uclpcserver/types.go | 5 +++ uclpcserver/uclpc.go | 24 +++++++++++-- uclpcserver/uclpc_test.go | 15 ++++++++ 6 files changed, 195 insertions(+), 2 deletions(-) diff --git a/uclpcserver/api.go b/uclpcserver/api.go index 7c24bb5..84ba0da 100644 --- a/uclpcserver/api.go +++ b/uclpcserver/api.go @@ -4,6 +4,7 @@ import ( "time" "github.com/enbility/cemd/api" + "github.com/enbility/spine-go/model" ) //go:generate mockery @@ -27,6 +28,17 @@ type UCLPCServerInterface interface { // set the current loadcontrol limit data SetConsumptionLimit(limit api.LoadLimit) (resultErr error) + // return the currently pending incoming consumption write limits + PendingConsumptionLimits() map[model.MsgCounterType]api.LoadLimit + + // accept or deny an incoming consumption write limit + // + // parameters: + // - msg: the incoming write message + // - approve: if the write limit for msg should be approved or not + // - reason: the reason why the approval is denied, otherwise an empty string + ApproveOrDenyConsumptionLimit(msgCounter model.MsgCounterType, approve bool, reason string) + // Scenario 2 // return Failsafe limit for the consumed active (real) power of the diff --git a/uclpcserver/public.go b/uclpcserver/public.go index 8e58b2f..c3482f0 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -103,6 +103,77 @@ func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) return nil } +// return the currently pending incoming consumption write limits +func (e *UCLPCServer) PendingConsumptionLimits() map[model.MsgCounterType]api.LoadLimit { + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + result := make(map[model.MsgCounterType]api.LoadLimit) + + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit, + ) + if len(descriptions) != 1 || descriptions[0].LimitId == nil { + return result + } + description := descriptions[0] + + for key, msg := range e.pendingLimits { + data := msg.Cmd.LoadControlLimitListData + + if data == nil || data.LoadControlLimitData == nil || len(data.LoadControlLimitData) == 0 { + continue + } + + // we assume there is always only one limit + element := data.LoadControlLimitData[0] + + if description.LimitId == nil || element.LimitId == nil || *description.LimitId != *element.LimitId { + continue + } + + limit := api.LoadLimit{} + + if element.TimePeriod != nil { + if duration, err := element.TimePeriod.GetDuration(); err == nil { + limit.Duration = duration + } + } + + if element.IsLimitActive != nil { + limit.IsActive = *element.IsLimitActive + } + + if element.Value != nil { + limit.Value = element.Value.GetValue() + } + + result[key] = limit + } + + return result +} + +// accept or deny an incoming consumption write limit +func (e *UCLPCServer) ApproveOrDenyConsumptionLimit(msgCounter model.MsgCounterType, approve bool, reason string) { + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + msg, ok := e.pendingLimits[msgCounter] + if !ok { + return + } + + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + f := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + f.ApproveOrDenyWrite(msg, approve, reason) +} + // Scenario 2 // return Failsafe limit for the consumed active (real) power of the diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index d4384b2..eeab4b3 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -4,6 +4,9 @@ import ( "time" "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) @@ -26,6 +29,73 @@ func (s *UCLPCServerSuite) Test_ConsumptionLimit() { assert.Nil(s.T(), err) } +func (s *UCLPCServerSuite) Test_PendingConsumptionLimits() { + data := s.sut.PendingConsumptionLimits() + assert.Equal(s.T(), 0, len(data)) + + msgCounter := model.MsgCounterType(500) + + msg := &spineapi.Message{ + RequestHeader: &model.HeaderType{ + MsgCounter: eebusutil.Ptr(msgCounter), + }, + Cmd: model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{}, + }, + } + + s.sut.loadControlWriteCB(msg) + + data = s.sut.PendingConsumptionLimits() + assert.Equal(s.T(), 0, len(data)) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + }, + } + + s.sut.loadControlWriteCB(msg) + + data = s.sut.PendingConsumptionLimits() + assert.Equal(s.T(), 0, len(data)) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + {}, + }, + }, + } + + s.sut.loadControlWriteCB(msg) + + data = s.sut.PendingConsumptionLimits() + assert.Equal(s.T(), 0, len(data)) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitActive: eebusutil.Ptr(true), + Value: model.NewScaledNumberType(1000), + TimePeriod: model.NewTimePeriodTypeWithRelativeEndTime(time.Minute * 2), + }, + }, + }, + } + + s.sut.loadControlWriteCB(msg) + + data = s.sut.PendingConsumptionLimits() + assert.Equal(s.T(), 1, len(data)) + + s.sut.ApproveOrDenyConsumptionLimit(model.MsgCounterType(499), true, "") + + s.sut.ApproveOrDenyConsumptionLimit(msgCounter, true, "") +} + func (s *UCLPCServerSuite) Test_Failsafe() { limit, changeable, err := s.sut.FailsafeConsumptionActivePowerLimit() assert.Equal(s.T(), 0.0, limit) diff --git a/uclpcserver/types.go b/uclpcserver/types.go index bca31aa..de9c224 100644 --- a/uclpcserver/types.go +++ b/uclpcserver/types.go @@ -12,6 +12,11 @@ const ( // Use Case LPC, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" + // An incoming load control obligation limit needs to be approved or denied + // + // Use Case LPC, Scenario 1 + WriteApprovalRequired api.EventType = "WriteApprovalRequired" + // Failsafe limit for the consumed active (real) power of the // Controllable System data update received // diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 4dff596..f74f101 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -1,6 +1,8 @@ package uclpcserver import ( + "sync" + "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" eebusapi "github.com/enbility/eebus-go/api" @@ -17,6 +19,9 @@ type UCLPCServer struct { validEntityTypes []model.EntityTypeType + pendingMux sync.Mutex + pendingLimits map[model.MsgCounterType]*spineapi.Message + heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use } @@ -24,8 +29,9 @@ var _ UCLPCServerInterface = (*UCLPCServer)(nil) func NewUCLPC(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPCServer { uc := &UCLPCServer{ - service: service, - eventCB: eventCB, + service: service, + eventCB: eventCB, + pendingLimits: make(map[model.MsgCounterType]*spineapi.Message), } uc.validEntityTypes = []model.EntityTypeType{ @@ -42,6 +48,19 @@ func (c *UCLPCServer) UseCaseName() model.UseCaseNameType { return model.UseCaseNameTypeLimitationOfPowerConsumption } +func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + if msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil { + return + } + + if _, ok := e.pendingLimits[*msg.RequestHeader.MsgCounter]; !ok { + e.pendingLimits[*msg.RequestHeader.MsgCounter] = msg + } +} + func (e *UCLPCServer) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) @@ -52,6 +71,7 @@ func (e *UCLPCServer) AddFeatures() { f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + _ = f.AddWriteApprovalCallback(e.loadControlWriteCB) var limitId model.LoadControlLimitIdType = 0 // get the highest limitId diff --git a/uclpcserver/uclpc_test.go b/uclpcserver/uclpc_test.go index e4c8414..9b4d68f 100644 --- a/uclpcserver/uclpc_test.go +++ b/uclpcserver/uclpc_test.go @@ -2,10 +2,25 @@ package uclpcserver import ( eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) +func (s *UCLPCServerSuite) Test_loadControlWriteCB() { + msg := &spineapi.Message{} + + s.sut.loadControlWriteCB(msg) + + msg = &spineapi.Message{ + RequestHeader: &model.HeaderType{ + MsgCounter: eebusutil.Ptr(model.MsgCounterType(500)), + }, + } + + s.sut.loadControlWriteCB(msg) +} + func (s *UCLPCServerSuite) Test_UpdateUseCaseAvailability() { s.sut.UpdateUseCaseAvailability(true) } From 487c1de1b4378be78151647a522e171006d523d1 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 22:06:15 +0200 Subject: [PATCH 211/227] Filter usecase specific limitIds --- uclpcserver/uclpc.go | 28 +++++++++++++++++++++++++++- uclpcserver/uclpc_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index f74f101..98317bb 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -52,7 +52,33 @@ func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { e.pendingMux.Lock() defer e.pendingMux.Unlock() - if msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil { + if msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil || + msg.Cmd.LoadControlLimitListData == nil { + return + } + + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeConsume, + model.ScopeTypeTypeActivePowerLimit, + ) + if len(descriptions) != 1 || descriptions[0].LimitId == nil { + return + } + description := descriptions[0] + + data := msg.Cmd.LoadControlLimitListData + + if data == nil || data.LoadControlLimitData == nil || len(data.LoadControlLimitData) == 0 { + return + } + + // we assume there is always only one limit + element := data.LoadControlLimitData[0] + + if description.LimitId == nil || element.LimitId == nil || *description.LimitId != *element.LimitId { return } diff --git a/uclpcserver/uclpc_test.go b/uclpcserver/uclpc_test.go index 9b4d68f..dbddaf9 100644 --- a/uclpcserver/uclpc_test.go +++ b/uclpcserver/uclpc_test.go @@ -1,6 +1,8 @@ package uclpcserver import ( + "time" + eebusutil "github.com/enbility/eebus-go/util" spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" @@ -16,6 +18,42 @@ func (s *UCLPCServerSuite) Test_loadControlWriteCB() { RequestHeader: &model.HeaderType{ MsgCounter: eebusutil.Ptr(model.MsgCounterType(500)), }, + Cmd: model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{}, + }, + } + + s.sut.loadControlWriteCB(msg) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + }, + } + + s.sut.loadControlWriteCB(msg) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + {}, + }, + }, + } + + s.sut.loadControlWriteCB(msg) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitActive: eebusutil.Ptr(true), + Value: model.NewScaledNumberType(1000), + TimePeriod: model.NewTimePeriodTypeWithRelativeEndTime(time.Minute * 2), + }, + }, + }, } s.sut.loadControlWriteCB(msg) From 8e2da41e994a30c4beae80d0bc533a41dee9736b Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 22:15:41 +0200 Subject: [PATCH 212/227] Simplify --- uclpcserver/public.go | 21 ++-------------- uclpcserver/public_test.go | 49 +++++++------------------------------- uclpcserver/uclpc.go | 40 +++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 73 deletions(-) diff --git a/uclpcserver/public.go b/uclpcserver/public.go index c3482f0..1a3558d 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -110,32 +110,15 @@ func (e *UCLPCServer) PendingConsumptionLimits() map[model.MsgCounterType]api.Lo result := make(map[model.MsgCounterType]api.LoadLimit) - descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( - e.service, - model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, - model.LoadControlCategoryTypeObligation, - model.EnergyDirectionTypeConsume, - model.ScopeTypeTypeActivePowerLimit, - ) - if len(descriptions) != 1 || descriptions[0].LimitId == nil { - return result - } - description := descriptions[0] - for key, msg := range e.pendingLimits { data := msg.Cmd.LoadControlLimitListData - if data == nil || data.LoadControlLimitData == nil || len(data.LoadControlLimitData) == 0 { - continue - } + // elements are only added to the map if all required fields exist + // therefor not check for these are needed here // we assume there is always only one limit element := data.LoadControlLimitData[0] - if description.LimitId == nil || element.LimitId == nil || *description.LimitId != *element.LimitId { - continue - } - limit := api.LoadLimit{} if element.TimePeriod != nil { diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index eeab4b3..ef865c0 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -40,47 +40,14 @@ func (s *UCLPCServerSuite) Test_PendingConsumptionLimits() { MsgCounter: eebusutil.Ptr(msgCounter), }, Cmd: model.CmdType{ - LoadControlLimitListData: &model.LoadControlLimitListDataType{}, - }, - } - - s.sut.loadControlWriteCB(msg) - - data = s.sut.PendingConsumptionLimits() - assert.Equal(s.T(), 0, len(data)) - - msg.Cmd = model.CmdType{ - LoadControlLimitListData: &model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{}, - }, - } - - s.sut.loadControlWriteCB(msg) - - data = s.sut.PendingConsumptionLimits() - assert.Equal(s.T(), 0, len(data)) - - msg.Cmd = model.CmdType{ - LoadControlLimitListData: &model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{ - {}, - }, - }, - } - - s.sut.loadControlWriteCB(msg) - - data = s.sut.PendingConsumptionLimits() - assert.Equal(s.T(), 0, len(data)) - - msg.Cmd = model.CmdType{ - LoadControlLimitListData: &model.LoadControlLimitListDataType{ - LoadControlLimitData: []model.LoadControlLimitDataType{ - { - LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), - IsLimitActive: eebusutil.Ptr(true), - Value: model.NewScaledNumberType(1000), - TimePeriod: model.NewTimePeriodTypeWithRelativeEndTime(time.Minute * 2), + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitActive: eebusutil.Ptr(true), + Value: model.NewScaledNumberType(1000), + TimePeriod: model.NewTimePeriodTypeWithRelativeEndTime(time.Minute * 2), + }, }, }, }, diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 98317bb..ff193bb 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -1,6 +1,7 @@ package uclpcserver import ( + "errors" "sync" "github.com/enbility/cemd/api" @@ -48,14 +49,9 @@ func (c *UCLPCServer) UseCaseName() model.UseCaseNameType { return model.UseCaseNameTypeLimitationOfPowerConsumption } -func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { - e.pendingMux.Lock() - defer e.pendingMux.Unlock() - - if msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil || - msg.Cmd.LoadControlLimitListData == nil { - return - } +func (e *UCLPCServer) loadControlLimitId() (limitid model.LoadControlLimitIdType, err error) { + limitid = model.LoadControlLimitIdType(0) + err = errors.New("not found") descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( e.service, @@ -69,16 +65,34 @@ func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { } description := descriptions[0] - data := msg.Cmd.LoadControlLimitListData + if description.LimitId == nil { + return + } + + return *description.LimitId, nil +} - if data == nil || data.LoadControlLimitData == nil || len(data.LoadControlLimitData) == 0 { +func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + if msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil || + msg.Cmd.LoadControlLimitListData == nil { return } - // we assume there is always only one limit - element := data.LoadControlLimitData[0] + limitId, err := e.loadControlLimitId() + if err != nil { + return + } - if description.LimitId == nil || element.LimitId == nil || *description.LimitId != *element.LimitId { + data := msg.Cmd.LoadControlLimitListData + + // we assume there is always only one limit + if data == nil || data.LoadControlLimitData == nil || + len(data.LoadControlLimitData) == 0 || + data.LoadControlLimitData[0].LimitId == nil || + limitId != *data.LoadControlLimitData[0].LimitId { return } From e594ef186fdb03f3a39cd6d24939c1473ddead02 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 22:19:09 +0200 Subject: [PATCH 213/227] Add comment --- uclpcserver/uclpc.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index ff193bb..5f9d5ba 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -72,6 +72,9 @@ func (e *UCLPCServer) loadControlLimitId() (limitid model.LoadControlLimitIdType return *description.LimitId, nil } +// callback invoked on incoming write messages to this +// loadcontrol server feature. +// the implementation only considers write messages for this use case func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { e.pendingMux.Lock() defer e.pendingMux.Unlock() From ce4fd70d4e47ad383da5455fbf9687e4fc22ae74 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 16 May 2024 22:36:51 +0200 Subject: [PATCH 214/227] Use updated write approve api This allows the result to use the defined error number on rejection of the write message --- go.mod | 2 +- go.sum | 4 ++-- uclpcserver/public.go | 10 +++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index b499bae..7032291 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 - github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 + github.com/enbility/spine-go v0.0.0-20240516203331-df8b5981ac96 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 28d494e..114fa1f 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b h1:8WdMCIKnB289r github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b/go.mod h1:1jzTHrSftixngak1cpaLIwjMJT62qJcKYDvnUS0wB94= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93 h1:ra+Eom9EQD9YN85WqZ9rmSTxWDi6j1R4SzHx+gHQCqs= -github.com/enbility/spine-go v0.0.0-20240516174755-b3c1a8a73d93/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240516203331-df8b5981ac96 h1:zHvbCsoZlbgXB1mPY3oViVXUiArhtO5OoFqtMNE/Hos= +github.com/enbility/spine-go v0.0.0-20240516203331-df8b5981ac96/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= diff --git a/uclpcserver/public.go b/uclpcserver/public.go index 1a3558d..62692bb 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -154,7 +154,15 @@ func (e *UCLPCServer) ApproveOrDenyConsumptionLimit(msgCounter model.MsgCounterT localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) f := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) - f.ApproveOrDenyWrite(msg, approve, reason) + + result := model.ErrorType{ + ErrorNumber: model.ErrorNumberType(0), + } + if !approve { + result.ErrorNumber = model.ErrorNumberType(7) + result.Description = eebusutil.Ptr(model.DescriptionType(reason)) + } + f.ApproveOrDenyWrite(msg, result) } // Scenario 2 From 31c3243a5b941662c961276d5a4213457359efae Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 12:15:23 +0200 Subject: [PATCH 215/227] Update SPINE --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7032291..ecfc813 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.1 require ( github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 - github.com/enbility/spine-go v0.0.0-20240516203331-df8b5981ac96 + github.com/enbility/spine-go v0.0.0-20240518100904-8a8dfc01cb3c github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 114fa1f..cab1f27 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b h1:8WdMCIKnB289r github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b/go.mod h1:1jzTHrSftixngak1cpaLIwjMJT62qJcKYDvnUS0wB94= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240516203331-df8b5981ac96 h1:zHvbCsoZlbgXB1mPY3oViVXUiArhtO5OoFqtMNE/Hos= -github.com/enbility/spine-go v0.0.0-20240516203331-df8b5981ac96/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/spine-go v0.0.0-20240518100904-8a8dfc01cb3c h1:bX0Ohq5ldOMNjOoi+/fCqE6vVDpoZsr3oW6fFsHp1t4= +github.com/enbility/spine-go v0.0.0-20240518100904-8a8dfc01cb3c/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= From a09802c27bb7c0dc58c0980f43e2daa25a56e696 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 12:26:07 +0200 Subject: [PATCH 216/227] Find the matching limit in the dataset --- uclpcserver/public.go | 44 ++++++++++++++++++++++++-------------- uclpcserver/public_test.go | 2 +- uclpcserver/types.go | 3 +++ uclpcserver/uclpc.go | 16 +++++++++----- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/uclpcserver/public.go b/uclpcserver/public.go index 62692bb..129078a 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -105,43 +105,55 @@ func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) // return the currently pending incoming consumption write limits func (e *UCLPCServer) PendingConsumptionLimits() map[model.MsgCounterType]api.LoadLimit { + result := make(map[model.MsgCounterType]api.LoadLimit) + + limitId, err := e.loadControlLimitId() + if err != nil { + return result + } + e.pendingMux.Lock() defer e.pendingMux.Unlock() - result := make(map[model.MsgCounterType]api.LoadLimit) - for key, msg := range e.pendingLimits { data := msg.Cmd.LoadControlLimitListData // elements are only added to the map if all required fields exist // therefor not check for these are needed here - // we assume there is always only one limit - element := data.LoadControlLimitData[0] + // find the item which contains the limit for this usecase + for _, item := range data.LoadControlLimitData { + if item.LimitId == nil || + limitId != *item.LimitId { + continue + } - limit := api.LoadLimit{} + limit := api.LoadLimit{} - if element.TimePeriod != nil { - if duration, err := element.TimePeriod.GetDuration(); err == nil { - limit.Duration = duration + if item.TimePeriod != nil { + if duration, err := item.TimePeriod.GetDuration(); err == nil { + limit.Duration = duration + } } - } - if element.IsLimitActive != nil { - limit.IsActive = *element.IsLimitActive - } + if item.IsLimitActive != nil { + limit.IsActive = *item.IsLimitActive + } - if element.Value != nil { - limit.Value = element.Value.GetValue() - } + if item.Value != nil { + limit.Value = item.Value.GetValue() + } - result[key] = limit + result[key] = limit + } } return result } // accept or deny an incoming consumption write limit +// +// use PendingConsumptionLimits to get the list of currently pending requests func (e *UCLPCServer) ApproveOrDenyConsumptionLimit(msgCounter model.MsgCounterType, approve bool, reason string) { e.pendingMux.Lock() defer e.pendingMux.Unlock() diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index ef865c0..eb5ef0f 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -60,7 +60,7 @@ func (s *UCLPCServerSuite) Test_PendingConsumptionLimits() { s.sut.ApproveOrDenyConsumptionLimit(model.MsgCounterType(499), true, "") - s.sut.ApproveOrDenyConsumptionLimit(msgCounter, true, "") + s.sut.ApproveOrDenyConsumptionLimit(msgCounter, false, "leave me alone") } func (s *UCLPCServerSuite) Test_Failsafe() { diff --git a/uclpcserver/types.go b/uclpcserver/types.go index de9c224..f0b8290 100644 --- a/uclpcserver/types.go +++ b/uclpcserver/types.go @@ -14,6 +14,9 @@ const ( // An incoming load control obligation limit needs to be approved or denied // + // Use `PendingConsumptionLimits` to get the currently pending write approval requests + // and invoke `ApproveOrDenyConsumptionLimit` for each + // // Use Case LPC, Scenario 1 WriteApprovalRequired api.EventType = "WriteApprovalRequired" diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 5f9d5ba..99c1a0c 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -93,14 +93,20 @@ func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { // we assume there is always only one limit if data == nil || data.LoadControlLimitData == nil || - len(data.LoadControlLimitData) == 0 || - data.LoadControlLimitData[0].LimitId == nil || - limitId != *data.LoadControlLimitData[0].LimitId { + len(data.LoadControlLimitData) == 0 { return } - if _, ok := e.pendingLimits[*msg.RequestHeader.MsgCounter]; !ok { - e.pendingLimits[*msg.RequestHeader.MsgCounter] = msg + // check if there is a matching limitId in the data + for _, item := range data.LoadControlLimitData { + if item.LimitId == nil || + limitId != *item.LimitId { + continue + } + + if _, ok := e.pendingLimits[*msg.RequestHeader.MsgCounter]; !ok { + e.pendingLimits[*msg.RequestHeader.MsgCounter] = msg + } } } From 264644d407a55b21e593ce09736bb47d28dee0d5 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 12:33:41 +0200 Subject: [PATCH 217/227] Remove duplicate code --- uclpcserver/public.go | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/uclpcserver/public.go b/uclpcserver/public.go index 129078a..e95e07b 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -30,19 +30,12 @@ func (e *UCLPCServer) ConsumptionLimit() (limit api.LoadLimit, resultErr error) } resultErr = eebusapi.ErrDataNotAvailable - descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( - e.service, - model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, - model.LoadControlCategoryTypeObligation, - model.EnergyDirectionTypeConsume, - model.ScopeTypeTypeActivePowerLimit, - ) - if len(descriptions) != 1 || descriptions[0].LimitId == nil { + limidId, err := e.loadControlLimitId() + if err != nil { return } - description := descriptions[0] - value := util.GetLocalLimitValueForLimitId(e.service, *description.LimitId) + value := util.GetLocalLimitValueForLimitId(e.service, limidId) if value.LimitId == nil || value.Value == nil { return } @@ -63,17 +56,10 @@ func (e *UCLPCServer) ConsumptionLimit() (limit api.LoadLimit, resultErr error) func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) { resultErr = eebusapi.ErrDataNotAvailable - descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( - e.service, - model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, - model.LoadControlCategoryTypeObligation, - model.EnergyDirectionTypeConsume, - model.ScopeTypeTypeActivePowerLimit, - ) - if len(descriptions) != 1 || descriptions[0].LimitId == nil { + limidId, err := e.loadControlLimitId() + if err != nil { return } - description := descriptions[0] localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) @@ -83,7 +69,7 @@ func (e *UCLPCServer) SetConsumptionLimit(limit api.LoadLimit) (resultErr error) } limitData := model.LoadControlLimitDataType{ - LimitId: description.LimitId, + LimitId: eebusutil.Ptr(limidId), IsLimitChangeable: eebusutil.Ptr(limit.IsChangeable), IsLimitActive: eebusutil.Ptr(limit.IsActive), Value: model.NewScaledNumberType(limit.Value), From b0d24319965efc47accba87b20b5c5a9d8615638 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 13:12:01 +0200 Subject: [PATCH 218/227] Send the write request event and approve others --- uclpcserver/uclpc.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 99c1a0c..8aa0493 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -74,7 +74,8 @@ func (e *UCLPCServer) loadControlLimitId() (limitid model.LoadControlLimitIdType // callback invoked on incoming write messages to this // loadcontrol server feature. -// the implementation only considers write messages for this use case +// the implementation only considers write messages for this use case and +// approves all others func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { e.pendingMux.Lock() defer e.pendingMux.Unlock() @@ -106,8 +107,13 @@ func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { if _, ok := e.pendingLimits[*msg.RequestHeader.MsgCounter]; !ok { e.pendingLimits[*msg.RequestHeader.MsgCounter] = msg + e.eventCB(msg.DeviceRemote.Ski(), msg.DeviceRemote, msg.EntityRemote, WriteApprovalRequired) + return } } + + // approve, because this is no request for this usecase + e.ApproveOrDenyConsumptionLimit(*msg.RequestHeader.MsgCounter, true, "") } func (e *UCLPCServer) AddFeatures() { From 7a43be4f9a786145979f3232dea3fda00b1c6098 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 13:12:15 +0200 Subject: [PATCH 219/227] Implement the write API for UCLPPServer --- uclppserver/api.go | 12 +++++ uclppserver/public.go | 100 +++++++++++++++++++++++++++++++++--------- uclppserver/types.go | 8 ++++ uclppserver/uclpp.go | 74 +++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 20 deletions(-) diff --git a/uclppserver/api.go b/uclppserver/api.go index 8df3440..c710a89 100644 --- a/uclppserver/api.go +++ b/uclppserver/api.go @@ -4,6 +4,7 @@ import ( "time" "github.com/enbility/cemd/api" + "github.com/enbility/spine-go/model" ) //go:generate mockery @@ -27,6 +28,17 @@ type UCLPPServerInterface interface { // set the current loadcontrol limit data SetProductionLimit(limit api.LoadLimit) (resultErr error) + // return the currently pending incoming consumption write limits + PendingProductionLimits() map[model.MsgCounterType]api.LoadLimit + + // accept or deny an incoming consumption write limit + // + // parameters: + // - msg: the incoming write message + // - approve: if the write limit for msg should be approved or not + // - reason: the reason why the approval is denied, otherwise an empty string + ApproveOrDenyProductionLimit(msgCounter model.MsgCounterType, approve bool, reason string) + // Scenario 2 // return Failsafe limit for the produced active (real) power of the diff --git a/uclppserver/public.go b/uclppserver/public.go index f6bf54c..d11477d 100644 --- a/uclppserver/public.go +++ b/uclppserver/public.go @@ -30,19 +30,12 @@ func (e *UCLPPServer) ProductionLimit() (limit api.LoadLimit, resultErr error) { } resultErr = eebusapi.ErrDataNotAvailable - descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( - e.service, - model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, - model.LoadControlCategoryTypeObligation, - model.EnergyDirectionTypeProduce, - model.ScopeTypeTypeActivePowerLimit, - ) - if len(descriptions) != 1 || descriptions[0].LimitId == nil { + limidId, err := e.loadControlLimitId() + if err != nil { return } - description := descriptions[0] - value := util.GetLocalLimitValueForLimitId(e.service, *description.LimitId) + value := util.GetLocalLimitValueForLimitId(e.service, limidId) if value.LimitId == nil || value.Value == nil { return } @@ -63,17 +56,10 @@ func (e *UCLPPServer) ProductionLimit() (limit api.LoadLimit, resultErr error) { func (e *UCLPPServer) SetProductionLimit(limit api.LoadLimit) (resultErr error) { resultErr = eebusapi.ErrDataNotAvailable - descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( - e.service, - model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, - model.LoadControlCategoryTypeObligation, - model.EnergyDirectionTypeProduce, - model.ScopeTypeTypeActivePowerLimit, - ) - if len(descriptions) != 1 || descriptions[0].LimitId == nil { + limidId, err := e.loadControlLimitId() + if err != nil { return } - description := descriptions[0] localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) @@ -83,7 +69,7 @@ func (e *UCLPPServer) SetProductionLimit(limit api.LoadLimit) (resultErr error) } limitData := model.LoadControlLimitDataType{ - LimitId: description.LimitId, + LimitId: eebusutil.Ptr(limidId), IsLimitChangeable: eebusutil.Ptr(limit.IsChangeable), IsLimitActive: eebusutil.Ptr(limit.IsActive), Value: model.NewScaledNumberType(limit.Value), @@ -103,6 +89,80 @@ func (e *UCLPPServer) SetProductionLimit(limit api.LoadLimit) (resultErr error) return nil } +// return the currently pending incoming consumption write limits +func (e *UCLPPServer) PendingProductionLimits() map[model.MsgCounterType]api.LoadLimit { + result := make(map[model.MsgCounterType]api.LoadLimit) + + limitId, err := e.loadControlLimitId() + if err != nil { + return result + } + + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + for key, msg := range e.pendingLimits { + data := msg.Cmd.LoadControlLimitListData + + // elements are only added to the map if all required fields exist + // therefor not check for these are needed here + + // find the item which contains the limit for this usecase + for _, item := range data.LoadControlLimitData { + if item.LimitId == nil || + limitId != *item.LimitId { + continue + } + + limit := api.LoadLimit{} + + if item.TimePeriod != nil { + if duration, err := item.TimePeriod.GetDuration(); err == nil { + limit.Duration = duration + } + } + + if item.IsLimitActive != nil { + limit.IsActive = *item.IsLimitActive + } + + if item.Value != nil { + limit.Value = item.Value.GetValue() + } + + result[key] = limit + } + } + + return result +} + +// accept or deny an incoming consumption write limit +// +// use PendingProductionLimits to get the list of currently pending requests +func (e *UCLPPServer) ApproveOrDenyProductionLimit(msgCounter model.MsgCounterType, approve bool, reason string) { + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + msg, ok := e.pendingLimits[msgCounter] + if !ok { + return + } + + localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) + + f := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + + result := model.ErrorType{ + ErrorNumber: model.ErrorNumberType(0), + } + if !approve { + result.ErrorNumber = model.ErrorNumberType(7) + result.Description = eebusutil.Ptr(model.DescriptionType(reason)) + } + f.ApproveOrDenyWrite(msg, result) +} + // Scenario 2 // return Failsafe limit for the produced active (real) power of the diff --git a/uclppserver/types.go b/uclppserver/types.go index 0303d57..0766b68 100644 --- a/uclppserver/types.go +++ b/uclppserver/types.go @@ -12,6 +12,14 @@ const ( // Use Case LPC, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" + // An incoming load control obligation limit needs to be approved or denied + // + // Use `PendingProductionLimits` to get the currently pending write approval requests + // and invoke `ApproveOrDenyProductionLimit` for each + // + // Use Case LPC, Scenario 1 + WriteApprovalRequired api.EventType = "WriteApprovalRequired" + // Failsafe limit for the produced active (real) power of the // Controllable System data update received // diff --git a/uclppserver/uclpp.go b/uclppserver/uclpp.go index e5a615f..228ef74 100644 --- a/uclppserver/uclpp.go +++ b/uclppserver/uclpp.go @@ -1,6 +1,9 @@ package uclppserver import ( + "errors" + "sync" + "github.com/enbility/cemd/api" "github.com/enbility/cemd/util" eebusapi "github.com/enbility/eebus-go/api" @@ -17,6 +20,9 @@ type UCLPPServer struct { validEntityTypes []model.EntityTypeType + pendingMux sync.Mutex + pendingLimits map[model.MsgCounterType]*spineapi.Message + heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use } @@ -42,6 +48,73 @@ func (c *UCLPPServer) UseCaseName() model.UseCaseNameType { return model.UseCaseNameTypeLimitationOfPowerProduction } +func (e *UCLPPServer) loadControlLimitId() (limitid model.LoadControlLimitIdType, err error) { + limitid = model.LoadControlLimitIdType(0) + err = errors.New("not found") + + descriptions := util.GetLocalLimitDescriptionsForTypeCategoryDirectionScope( + e.service, + model.LoadControlLimitTypeTypeSignDependentAbsValueLimit, + model.LoadControlCategoryTypeObligation, + model.EnergyDirectionTypeProduce, + model.ScopeTypeTypeActivePowerLimit, + ) + if len(descriptions) != 1 || descriptions[0].LimitId == nil { + return + } + description := descriptions[0] + + if description.LimitId == nil { + return + } + + return *description.LimitId, nil +} + +// callback invoked on incoming write messages to this +// loadcontrol server feature. +// the implementation only considers write messages for this use case and +// approves all others +func (e *UCLPPServer) loadControlWriteCB(msg *spineapi.Message) { + e.pendingMux.Lock() + defer e.pendingMux.Unlock() + + if msg.RequestHeader == nil || msg.RequestHeader.MsgCounter == nil || + msg.Cmd.LoadControlLimitListData == nil { + return + } + + limitId, err := e.loadControlLimitId() + if err != nil { + return + } + + data := msg.Cmd.LoadControlLimitListData + + // we assume there is always only one limit + if data == nil || data.LoadControlLimitData == nil || + len(data.LoadControlLimitData) == 0 { + return + } + + // check if there is a matching limitId in the data + for _, item := range data.LoadControlLimitData { + if item.LimitId == nil || + limitId != *item.LimitId { + continue + } + + if _, ok := e.pendingLimits[*msg.RequestHeader.MsgCounter]; !ok { + e.pendingLimits[*msg.RequestHeader.MsgCounter] = msg + e.eventCB(msg.DeviceRemote.Ski(), msg.DeviceRemote, msg.EntityRemote, WriteApprovalRequired) + return + } + } + + // approve, because this is no request for this usecase + e.ApproveOrDenyProductionLimit(*msg.RequestHeader.MsgCounter, true, "") +} + func (e *UCLPPServer) AddFeatures() { localEntity := e.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) @@ -52,6 +125,7 @@ func (e *UCLPPServer) AddFeatures() { f := localEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) + _ = f.AddWriteApprovalCallback(e.loadControlWriteCB) var limitId model.LoadControlLimitIdType = 0 // get the highest limitId From 8fe8f2b53440d1781482bb11629051543bf5ada4 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 13:15:48 +0200 Subject: [PATCH 220/227] Fix mutex lock --- uclpcserver/uclpc.go | 2 +- uclppserver/uclpp.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uclpcserver/uclpc.go b/uclpcserver/uclpc.go index 8aa0493..f0bc76a 100644 --- a/uclpcserver/uclpc.go +++ b/uclpcserver/uclpc.go @@ -113,7 +113,7 @@ func (e *UCLPCServer) loadControlWriteCB(msg *spineapi.Message) { } // approve, because this is no request for this usecase - e.ApproveOrDenyConsumptionLimit(*msg.RequestHeader.MsgCounter, true, "") + go e.ApproveOrDenyConsumptionLimit(*msg.RequestHeader.MsgCounter, true, "") } func (e *UCLPCServer) AddFeatures() { diff --git a/uclppserver/uclpp.go b/uclppserver/uclpp.go index 228ef74..8354758 100644 --- a/uclppserver/uclpp.go +++ b/uclppserver/uclpp.go @@ -112,7 +112,7 @@ func (e *UCLPPServer) loadControlWriteCB(msg *spineapi.Message) { } // approve, because this is no request for this usecase - e.ApproveOrDenyProductionLimit(*msg.RequestHeader.MsgCounter, true, "") + go e.ApproveOrDenyProductionLimit(*msg.RequestHeader.MsgCounter, true, "") } func (e *UCLPPServer) AddFeatures() { From fe2ff2fbea165e0ac8c901e5e736028e580f4682 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 13:20:54 +0200 Subject: [PATCH 221/227] Fix missing initializer --- uclppserver/uclpp.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/uclppserver/uclpp.go b/uclppserver/uclpp.go index 8354758..9543d94 100644 --- a/uclppserver/uclpp.go +++ b/uclppserver/uclpp.go @@ -30,8 +30,9 @@ var _ UCLPPServerInterface = (*UCLPPServer)(nil) func NewUCLPP(service eebusapi.ServiceInterface, eventCB api.EntityEventCallback) *UCLPPServer { uc := &UCLPPServer{ - service: service, - eventCB: eventCB, + service: service, + eventCB: eventCB, + pendingLimits: make(map[model.MsgCounterType]*spineapi.Message), } uc.validEntityTypes = []model.EntityTypeType{ From 2bafe21b48efb3f911ad4764eada7932f95c3651 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 13:21:01 +0200 Subject: [PATCH 222/227] Fix tests --- uclpcserver/public_test.go | 2 ++ uclpcserver/uclpc_test.go | 2 ++ uclppserver/public_test.go | 39 +++++++++++++++++++++++++++ uclppserver/uclpp_test.go | 55 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index eb5ef0f..54fa1d6 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -51,6 +51,8 @@ func (s *UCLPCServerSuite) Test_PendingConsumptionLimits() { }, }, }, + DeviceRemote: s.remoteDevice, + EntityRemote: s.monitoredEntity, } s.sut.loadControlWriteCB(msg) diff --git a/uclpcserver/uclpc_test.go b/uclpcserver/uclpc_test.go index dbddaf9..ee3ee5f 100644 --- a/uclpcserver/uclpc_test.go +++ b/uclpcserver/uclpc_test.go @@ -21,6 +21,8 @@ func (s *UCLPCServerSuite) Test_loadControlWriteCB() { Cmd: model.CmdType{ LoadControlLimitListData: &model.LoadControlLimitListDataType{}, }, + DeviceRemote: s.remoteDevice, + EntityRemote: s.monitoredEntity, } s.sut.loadControlWriteCB(msg) diff --git a/uclppserver/public_test.go b/uclppserver/public_test.go index fa903a1..c022d84 100644 --- a/uclppserver/public_test.go +++ b/uclppserver/public_test.go @@ -4,6 +4,9 @@ import ( "time" "github.com/enbility/cemd/api" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) @@ -26,6 +29,42 @@ func (s *UCLPPServerSuite) Test_LoadControlLimit() { assert.Nil(s.T(), err) } +func (s *UCLPPServerSuite) Test_PendingProductionLimits() { + data := s.sut.PendingProductionLimits() + assert.Equal(s.T(), 0, len(data)) + + msgCounter := model.MsgCounterType(500) + + msg := &spineapi.Message{ + RequestHeader: &model.HeaderType{ + MsgCounter: eebusutil.Ptr(msgCounter), + }, + Cmd: model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitActive: eebusutil.Ptr(true), + Value: model.NewScaledNumberType(1000), + TimePeriod: model.NewTimePeriodTypeWithRelativeEndTime(time.Minute * 2), + }, + }, + }, + }, + DeviceRemote: s.remoteDevice, + EntityRemote: s.monitoredEntity, + } + + s.sut.loadControlWriteCB(msg) + + data = s.sut.PendingProductionLimits() + assert.Equal(s.T(), 1, len(data)) + + s.sut.ApproveOrDenyProductionLimit(model.MsgCounterType(499), true, "") + + s.sut.ApproveOrDenyProductionLimit(msgCounter, false, "leave me alone") +} + func (s *UCLPPServerSuite) Test_Failsafe() { limit, changeable, err := s.sut.FailsafeProductionActivePowerLimit() assert.Equal(s.T(), 0.0, limit) diff --git a/uclppserver/uclpp_test.go b/uclppserver/uclpp_test.go index 1d5ba50..e5974f4 100644 --- a/uclppserver/uclpp_test.go +++ b/uclppserver/uclpp_test.go @@ -1,11 +1,66 @@ package uclppserver import ( + "time" + eebusutil "github.com/enbility/eebus-go/util" + spineapi "github.com/enbility/spine-go/api" "github.com/enbility/spine-go/model" "github.com/stretchr/testify/assert" ) +func (s *UCLPPServerSuite) Test_loadControlWriteCB() { + msg := &spineapi.Message{} + + s.sut.loadControlWriteCB(msg) + + msg = &spineapi.Message{ + RequestHeader: &model.HeaderType{ + MsgCounter: eebusutil.Ptr(model.MsgCounterType(500)), + }, + Cmd: model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{}, + }, + DeviceRemote: s.remoteDevice, + EntityRemote: s.monitoredEntity, + } + + s.sut.loadControlWriteCB(msg) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{}, + }, + } + + s.sut.loadControlWriteCB(msg) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + {}, + }, + }, + } + + s.sut.loadControlWriteCB(msg) + + msg.Cmd = model.CmdType{ + LoadControlLimitListData: &model.LoadControlLimitListDataType{ + LoadControlLimitData: []model.LoadControlLimitDataType{ + { + LimitId: eebusutil.Ptr(model.LoadControlLimitIdType(0)), + IsLimitActive: eebusutil.Ptr(true), + Value: model.NewScaledNumberType(1000), + TimePeriod: model.NewTimePeriodTypeWithRelativeEndTime(time.Minute * 2), + }, + }, + }, + } + + s.sut.loadControlWriteCB(msg) +} + func (s *UCLPPServerSuite) Test_UpdateUseCaseAvailability() { s.sut.UpdateUseCaseAvailability(true) } From 692f2a8460a995d3f4ee27cd4f9aa8eff57259be Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 14:06:06 +0200 Subject: [PATCH 223/227] Update event types documentation --- uccevc/types.go | 35 +++++------------------------------ ucevcc/types.go | 36 +++++++----------------------------- ucevcem/types.go | 16 ++++------------ ucevsecc/types.go | 16 ++-------------- ucevsoc/types.go | 4 +--- uclpc/types.go | 12 +++--------- uclpcserver/types.go | 10 ++++------ uclpp/types.go | 12 +++--------- uclppserver/types.go | 12 +++--------- ucmgcp/types.go | 28 +++++++--------------------- ucmpc/types.go | 28 +++++++--------------------- ucopev/types.go | 8 ++------ ucoscev/types.go | 8 ++------ ucvabd/types.go | 16 ++++------------ ucvapd/types.go | 12 +++--------- 15 files changed, 57 insertions(+), 196 deletions(-) diff --git a/uccevc/types.go b/uccevc/types.go index 4f1cb11..bc5c7f4 100644 --- a/uccevc/types.go +++ b/uccevc/types.go @@ -7,65 +7,40 @@ const ( // EV provided an energy demand // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `EnergyDemand` to get the current data DataUpdateEnergyDemand api.EventType = "DataUpdateEnergyDemand" // Scenario 2 // EV provided a charge plan constraints // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `TimeSlotConstraints` to get the current data DataUpdateTimeSlotConstraints api.EventType = "DataUpdateTimeSlotConstraints" // Scenario 3 // EV incentive table data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `IncentiveConstraints` to get the current data DataUpdateIncentiveTable api.EventType = "DataUpdateIncentiveTable" - // EV incentive table data constraints updated - // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV - DataUpdateIncentiveTableConstraints api.EventType = "DataUpdateIncentiveTableConstraints" - // EV requested an incentive table, call to WriteIncentiveTableDescriptions required - // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV DataRequestedIncentiveTableDescription api.EventType = "DataRequestedIncentiveTableDescription" // Scenario 2 & 3 // EV requested power limits, call to WritePowerLimits and WriteIncentives required - // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV DataRequestedPowerLimitsAndIncentives api.EventType = "DataRequestedPowerLimitsAndIncentives" // Scenario 4 // EV provided a charge plan // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `ChargePlanConstraints` to get the current data DataUpdateChargePlanConstraints api.EventType = "DataUpdateChargePlanConstraints" // EV provided a charge plan // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `ChargePlan` to get the current data DataUpdateChargePlan api.EventType = "DataUpdateChargePlan" ) diff --git a/ucevcc/types.go b/ucevcc/types.go index 7ed9191..bc89a83 100644 --- a/ucevcc/types.go +++ b/ucevcc/types.go @@ -14,19 +14,11 @@ const ( // An EV was connected // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV - // // Use Case EVCC, Scenario 1 EvConnected api.EventType = "EvConnected" // An EV was disconnected // - // The callback with this message provides: - // - the device of the EVSE the EV was connected to - // - the entity of the EV - // // Note: The ev entity is no longer connected to the device! // // Use Case EVCC, Scenario 8 @@ -34,59 +26,45 @@ const ( // EV charge state data was updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `ChargeState` to get the current data DataUpdateChargeState api.EventType = "DataUpdateChargeState" // EV communication standard data was updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `CommunicationStandard` to get the current data // // Use Case EVCC, Scenario 2 DataUpdateCommunicationStandard api.EventType = "DataUpdateCommunicationStandard" // EV asymmetric charging data was updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `AsymmetricChargingSupport` to get the current data DataUpdateAsymmetricChargingSupport api.EventType = "DataUpdateAsymmetricChargingSupport" // EV identificationdata was updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `Identifications` to get the current data // // Use Case EVCC, Scenario 4 DataUpdateIdentifications api.EventType = "DataUpdateIdentifications" // EV manufacturer data was updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `ManufacturerData` to get the current data // // Use Case EVCC, Scenario 5 DataUpdateManufacturerData api.EventType = "DataUpdateManufacturerData" // EV charging power limits // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `ChargingPowerLimits` to get the current data // // Use Case EVCC, Scenario 6 DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" // EV permitted power limits updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `IsInSleepMode` to get the current data // // Use Case EVCC, Scenario 7 DataUpdateIsInSleepMode api.EventType = "DataUpdateIsInSleepMode" diff --git a/ucevcem/types.go b/ucevcem/types.go index e3e4e83..e834202 100644 --- a/ucevcem/types.go +++ b/ucevcem/types.go @@ -5,36 +5,28 @@ import "github.com/enbility/cemd/api" const ( // EV number of connected phases data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `PhasesConnected` to get the current data // // Use Case EVCEM, Scenario 1 DataUpdatePhasesConnected api.EventType = "DataUpdatePhasesConnected" // EV current measurement data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `CurrentPerPhase` to get the current data // // Use Case EVCEM, Scenario 1 DataUpdateCurrentPerPhase api.EventType = "DataUpdateCurrentPerPhase" // EV power measurement data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `PowerPerPhase` to get the current data // // Use Case EVCEM, Scenario 2 DataUpdatePowerPerPhase api.EventType = "DataUpdatePowerPerPhase" // EV charging energy measurement data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `EnergyCharged` to get the current data // // Use Case EVCEM, Scenario 3 DataUpdateEnergyCharged api.EventType = "DataUpdateEnergyCharged" diff --git a/ucevsecc/types.go b/ucevsecc/types.go index 33e76ac..7a463f8 100644 --- a/ucevsecc/types.go +++ b/ucevsecc/types.go @@ -4,24 +4,14 @@ import "github.com/enbility/cemd/api" const ( // An EVSE was connected - // - // The callback with this message provides: - // - the device of the EVSE - // - the entity of the EVSE EvseConnected api.EventType = "EvseConnected" // An EVSE was disconnected - // - // The callback with this message provides: - // - the device of the EVSE - // - the entity of the EVSE EvseDisconnected api.EventType = "EvseDisconnected" // EVSE manufacturer data was updated // - // The callback with this message provides: - // - the device of the EVSE - // - the entity of the EVSE + // Use `ManufacturerData` to get the current data // // Use Case EVSECC, Scenario 1 // @@ -30,9 +20,7 @@ const ( // EVSE operation state was updated // - // The callback with this message provides: - // - the device of the EVSE - // - the entity of the EVSE + // Use `OperatingState` to get the current data // // Use Case EVSECC, Scenario 2 // diff --git a/ucevsoc/types.go b/ucevsoc/types.go index eedb5ad..b5f2b0a 100644 --- a/ucevsoc/types.go +++ b/ucevsoc/types.go @@ -5,9 +5,7 @@ import "github.com/enbility/cemd/api" const ( // EV state of charge data was updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `StateOfCharge` to get the current data // // Use Case EVSOC, Scenario 1 DataUpdateStateOfCharge api.EventType = "DataUpdateStateOfCharge" diff --git a/uclpc/types.go b/uclpc/types.go index 4649102..82550e8 100644 --- a/uclpc/types.go +++ b/uclpc/types.go @@ -5,9 +5,7 @@ import "github.com/enbility/cemd/api" const ( // Load control obligation limit data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `ConsumptionLimit` to get the current data // // Use Case LPC, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" @@ -15,9 +13,7 @@ const ( // Failsafe limit for the consumed active (real) power of the // Controllable System data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `FailsafeConsumptionActivePowerLimit` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeConsumptionActivePowerLimit api.EventType = "DataUpdateFailsafeConsumptionActivePowerLimit" @@ -25,9 +21,7 @@ const ( // Minimum time the Controllable System remains in "failsafe state" unless conditions // specified in this Use Case permit leaving the "failsafe state" data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `FailsafeDurationMinimum` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" diff --git a/uclpcserver/types.go b/uclpcserver/types.go index f0b8290..736ae68 100644 --- a/uclpcserver/types.go +++ b/uclpcserver/types.go @@ -9,6 +9,8 @@ const ( // - the device of the e.g. SMGW // - the entity of the e.g. SMGW // + // Use `ConsumptionLimit` to get the current data + // // Use Case LPC, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" @@ -23,9 +25,7 @@ const ( // Failsafe limit for the consumed active (real) power of the // Controllable System data update received // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `FailsafeConsumptionActivePowerLimit` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeConsumptionActivePowerLimit api.EventType = "DataUpdateFailsafeConsumptionActivePowerLimit" @@ -33,9 +33,7 @@ const ( // Minimum time the Controllable System remains in "failsafe state" unless conditions // specified in this Use Case permit leaving the "failsafe state" data update received // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `FailsafeDurationMinimum` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" diff --git a/uclpp/types.go b/uclpp/types.go index 1fcc382..cbac18a 100644 --- a/uclpp/types.go +++ b/uclpp/types.go @@ -5,9 +5,7 @@ import "github.com/enbility/cemd/api" const ( // Load control obligation limit data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `ProductionLimit` to get the current data // // Use Case LPC, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" @@ -15,9 +13,7 @@ const ( // Failsafe limit for the produced active (real) power of the // Controllable System data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `FailsafeProductionActivePowerLimit` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeProductionActivePowerLimit api.EventType = "DataUpdateFailsafeProductionActivePowerLimit" @@ -25,9 +21,7 @@ const ( // Minimum time the Controllable System remains in "failsafe state" unless conditions // specified in this Use Case permit leaving the "failsafe state" data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `FailsafeDurationMinimum` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" diff --git a/uclppserver/types.go b/uclppserver/types.go index 0766b68..e5df6d8 100644 --- a/uclppserver/types.go +++ b/uclppserver/types.go @@ -5,9 +5,7 @@ import "github.com/enbility/cemd/api" const ( // Load control obligation limit data update received // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `ProductionLimit` to get the current data // // Use Case LPC, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" @@ -23,9 +21,7 @@ const ( // Failsafe limit for the produced active (real) power of the // Controllable System data update received // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `FailsafeProductionActivePowerLimit` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeProductionActivePowerLimit api.EventType = "DataUpdateFailsafeProductionActivePowerLimit" @@ -33,9 +29,7 @@ const ( // Minimum time the Controllable System remains in "failsafe state" unless conditions // specified in this Use Case permit leaving the "failsafe state" data update received // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `FailsafeDurationMinimum` to get the current data // // Use Case LPC, Scenario 2 DataUpdateFailsafeDurationMinimum api.EventType = "DataUpdateFailsafeDurationMinimum" diff --git a/ucmgcp/types.go b/ucmgcp/types.go index 3b50296..3d453a3 100644 --- a/ucmgcp/types.go +++ b/ucmgcp/types.go @@ -6,63 +6,49 @@ const ( // Grid maximum allowed feed-in power as percentage value of the cumulated // nominal peak power of all electricity producting PV systems was updated // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `PowerLimitationFactor` to get the current data // // Use Case MGCP, Scenario 2 DataUpdatePowerLimitationFactor api.EventType = "DataUpdatePowerLimitationFactor" // Grid momentary power consumption/production data updated // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `Power` to get the current data // // Use Case MGCP, Scenario 2 DataUpdatePower api.EventType = "DataUpdatePower" // Total grid feed in energy data updated // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `EnergyFeedIn` to get the current data // // Use Case MGCP, Scenario 3 DataUpdateEnergyFeedIn api.EventType = "DataUpdateEnergyFeedIn" // Total grid consumed energy data updated // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `EnergyConsumed` to get the current data // // Use Case MGCP, Scenario 4 DataUpdateEnergyConsumed api.EventType = "DataUpdateEnergyConsumed" // Phase specific momentary current consumption/production phase detail data updated // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `CurrentPerPhase` to get the current data // // Use Case MGCP, Scenario 5 DataUpdateCurrentPerPhase api.EventType = "DataUpdateCurrentPerPhase" // Phase specific voltage at the grid connection point // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `VoltagePerPhase` to get the current data // // Use Case MGCP, Scenario 6 DataUpdateVoltagePerPhase api.EventType = "DataUpdateVoltagePerPhase" // Grid frequency data updated // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW + // Use `Frequency` to get the current data // // Use Case MGCP, Scenario 7 DataUpdateFrequency api.EventType = "DataUpdateFrequency" diff --git a/ucmpc/types.go b/ucmpc/types.go index 5c0bd6d..1c5e01a 100644 --- a/ucmpc/types.go +++ b/ucmpc/types.go @@ -5,63 +5,49 @@ import "github.com/enbility/cemd/api" const ( // Total momentary active power consumption or production // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `Power` to get the current data // // Use Case MCP, Scenario 1 DataUpdatePower api.EventType = "DataUpdatePower" // Phase specific momentary active power consumption or production // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `PowerPerPhase` to get the current data // // Use Case MCP, Scenario 1 DataUpdatePowerPerPhase api.EventType = "DataUpdatePowerPerPhase" // Total energy consumed // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `EnergyConsumed` to get the current data // // Use Case MCP, Scenario 2 DataUpdateEnergyConsumed api.EventType = "DataUpdateEnergyConsumed" // Total energy produced // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `EnergyProduced` to get the current data // // Use Case MCP, Scenario 2 DataUpdateEnergyProduced api.EventType = "DataUpdateEnergyProduced" // Phase specific momentary current consumption or production // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `CurrentPerPhase` to get the current data // // Use Case MCP, Scenario 3 DataUpdateCurrentsPerPhase api.EventType = "DataUpdateCurrentsPerPhase" // Phase specific voltage // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `VoltagePerPhase` to get the current data // // Use Case MCP, Scenario 3 DataUpdateVoltagePerPhase api.EventType = "DataUpdateVoltagePerPhase" // Power network frequency data updated // - // The callback with this message provides: - // - the device of the e.g. EVSE - // - the entity of the e.g. EVSE + // Use `Frequency` to get the current data // // Use Case MCP, Scenario 3 DataUpdateFrequency api.EventType = "DataUpdateFrequency" diff --git a/ucopev/types.go b/ucopev/types.go index 48fbd75..32eebe6 100644 --- a/ucopev/types.go +++ b/ucopev/types.go @@ -5,15 +5,11 @@ import "github.com/enbility/cemd/api" const ( // EV current limits // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `CurrentLimits` to get the current data DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" // EV load control obligation limit data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `LoadControlLimits` to get the current data DataUpdateLimit api.EventType = "DataUpdateLimit" ) diff --git a/ucoscev/types.go b/ucoscev/types.go index 5d870d5..fe79145 100644 --- a/ucoscev/types.go +++ b/ucoscev/types.go @@ -5,16 +5,12 @@ import "github.com/enbility/cemd/api" const ( // EV current limits // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `CurrentLimits` to get the current data DataUpdateCurrentLimits api.EventType = "DataUpdateCurrentLimits" // EV load control recommendation limit data updated // - // The callback with this message provides: - // - the device of the EVSE the EV is connected to - // - the entity of the EV + // Use `LoadControlLimits` to get the current data // // Use Case OSCEV, Scenario 1 DataUpdateLimit api.EventType = "DataUpdateLimit" diff --git a/ucvabd/types.go b/ucvabd/types.go index c9a9bf9..c93ddce 100644 --- a/ucvabd/types.go +++ b/ucvabd/types.go @@ -5,36 +5,28 @@ import "github.com/enbility/cemd/api" const ( // Battery System (dis)charge power data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `Power` to get the current data // // Use Case VABD, Scenario 1 DataUpdatePower api.EventType = "d" // Battery System cumulated charge energy data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `EnergyCharged` to get the current data // // Use Case VABD, Scenario 2 DataUpdateEnergyCharged api.EventType = "DataUpdateEnergyCharged" // Battery System cumulated discharge energy data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `EnergyDischarged` to get the current data // // Use Case VABD, Scenario 2 DataUpdateEnergyDischarged api.EventType = "DataUpdateEnergyDischarged" // Battery System state of charge data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `StateOfCharge` to get the current data // // Use Case VABD, Scenario 4 DataUpdateStateOfCharge api.EventType = "DataUpdateStateOfCharge" diff --git a/ucvapd/types.go b/ucvapd/types.go index 2255cd8..52b01c0 100644 --- a/ucvapd/types.go +++ b/ucvapd/types.go @@ -5,27 +5,21 @@ import "github.com/enbility/cemd/api" const ( // PV System total power data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `Power` to get the current data // // Use Case VAPD, Scenario 1 DataUpdatePower api.EventType = "DataUpdatePower" // PV System nominal peak power data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `PowerNominalPeak` to get the current data // // Use Case VAPD, Scenario 2 DataUpdatePowerNominalPeak api.EventType = "DataUpdatePowerNominalPeak" // PV System total yield data updated // - // The callback with this message provides: - // - the device of the inverter - // - the entity of the inverter + // Use `PVYieldTotal` to get the current data // // Use Case VAPD, Scenario 3 DataUpdatePVYieldTotal api.EventType = "DataUpdatePVYieldTotal" From 90db1c86de025fbb3be04181e8b937c5711f5db7 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 14:07:06 +0200 Subject: [PATCH 224/227] Add missing change --- uclpcserver/types.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/uclpcserver/types.go b/uclpcserver/types.go index 736ae68..f555694 100644 --- a/uclpcserver/types.go +++ b/uclpcserver/types.go @@ -5,10 +5,6 @@ import "github.com/enbility/cemd/api" const ( // Load control obligation limit data update received // - // The callback with this message provides: - // - the device of the e.g. SMGW - // - the entity of the e.g. SMGW - // // Use `ConsumptionLimit` to get the current data // // Use Case LPC, Scenario 1 From 8d8719097e6accea2409c1d4ff89e2e7ff6067a8 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 14:09:49 +0200 Subject: [PATCH 225/227] Updare license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 5453335..beae98e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Andreas Linde +Copyright (c) 2022-2024 Andreas Linde Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c07e1103147280b1478567a85e41cf4dad623517 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 15:54:03 +0200 Subject: [PATCH 226/227] Update README badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c0e62c..6a8c814 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # cemd -[![Build Status](https://github.com/enbility/cemd/actions/workflows/default.yml/badge.svg?branch=dev)](https://github.com/enbility/cemd/actions/workflows/default.yml/badge.svg?branch=dev) +[![Build Status](https://github.com/enbility/cemd/actions/workflows/default.yml/badge.svg?branch=main)](https://github.com/enbility/cemd/actions/workflows/default.yml/badge.svg?branch=main) [![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4)](https://godoc.org/github.com/enbility/cemd) -[![Coverage Status](https://coveralls.io/repos/github/enbility/cemd/badge.svg?branch=dev)](https://coveralls.io/github/enbility/cemd?branch=dev) +[![Coverage Status](https://coveralls.io/repos/github/enbility/cemd/badge.svg?branch=main)](https://coveralls.io/github/enbility/cemd?branch=main) [![Go report](https://goreportcard.com/badge/github.com/enbility/cemd)](https://goreportcard.com/report/github.com/enbility/cemd) [![CodeFactor](https://www.codefactor.io/repository/github/enbility/cemd/badge)](https://www.codefactor.io/repository/github/enbility/cemd) From 9365043f165c065b12222fe9ae7b86fc6ca91449 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Sat, 18 May 2024 15:54:42 +0200 Subject: [PATCH 227/227] Update SHIP, SPINE, EEBUS --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index ecfc813..524bb35 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/enbility/cemd go 1.21.1 require ( - github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b - github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 - github.com/enbility/spine-go v0.0.0-20240518100904-8a8dfc01cb3c + github.com/enbility/eebus-go v0.5.0 + github.com/enbility/ship-go v0.5.0 + github.com/enbility/spine-go v0.5.0 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index cab1f27..64ecafa 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b h1:8WdMCIKnB289riC3Fcni965IoPXci7LKGbGARPi3AAI= -github.com/enbility/eebus-go v0.0.0-20240516175253-e0841966938b/go.mod h1:1jzTHrSftixngak1cpaLIwjMJT62qJcKYDvnUS0wB94= -github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0 h1:iPs4/u/N5qf3oiRHPK0tBOAr0N2vQzHV28lPe1U9iHE= -github.com/enbility/ship-go v0.0.0-20240512152836-f8ae5a3899f0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= -github.com/enbility/spine-go v0.0.0-20240518100904-8a8dfc01cb3c h1:bX0Ohq5ldOMNjOoi+/fCqE6vVDpoZsr3oW6fFsHp1t4= -github.com/enbility/spine-go v0.0.0-20240518100904-8a8dfc01cb3c/go.mod h1:2SXeC20kPX23mTnsudvPq9qprgo7GKDiNiVdX0ebovw= +github.com/enbility/eebus-go v0.5.0 h1:iC+CSc7eVGqls0GT4d4eWA0vrm1m9eMroG9rvEia06Y= +github.com/enbility/eebus-go v0.5.0/go.mod h1:JhLSoVxGiKSgOtxoGkA81vs+JRB4QHTa8P8LzHsq5WQ= +github.com/enbility/ship-go v0.5.0 h1:Uqol2XjzDOcvT8HUAE4B/59yqd3mxhpJJ/Q2eDHNGqc= +github.com/enbility/ship-go v0.5.0/go.mod h1:ovyrJE3oPnGT5+eQnOqWut80gFDQ0XHn3ZWU2fHV9xQ= +github.com/enbility/spine-go v0.5.0 h1:3OQBl8gQPW/iuWmwcabmCIXDcFCP0RsDw7uP8BYUmaY= +github.com/enbility/spine-go v0.5.0/go.mod h1:8rXOJ7nTa4qrSRK0PpfavBXMztxi6l+h/IFpIVmHviM= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b h1:sg3c6LJ4eWffwtt9SW0lgcIX4Oh274vwdJnNFNNrDco= github.com/enbility/zeroconf/v2 v2.0.0-20240210101930-d0004078577b/go.mod h1:BjzRRiYX6mWdOgku1xxDE+NsV8PijTby7Q7BkYVdfDU= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=