diff --git a/api/api.go b/api/api.go index 1dc14a7c..d429e592 100644 --- a/api/api.go +++ b/api/api.go @@ -32,7 +32,7 @@ type ServiceInterface interface { IsRunning() bool // add a use case to the service - AddUseCase(useCase UseCaseInterface) + AddUseCase(useCase UseCaseInterface) error // set logging interface SetLogging(logger logging.LoggingInterface) diff --git a/api/featuresserver.go b/api/featuresserver.go index 23e79fd8..edc57f0f 100644 --- a/api/featuresserver.go +++ b/api/featuresserver.go @@ -109,6 +109,13 @@ type ElectricalConnectionServerInterface interface { deleteSelector *model.ElectricalConnectionPermittedValueSetListDataSelectorsType, deleteElements *model.ElectricalConnectionPermittedValueSetDataElementsType, ) error + + // either returns the given description id or creates a new one for the given description + // + // will return error if could not add the new description + GetOrAddIdForDescription( + electricalConnectionDescription model.ElectricalConnectionDescriptionDataType, + ) (*model.ElectricalConnectionIdType, error) } type LoadControlLimitDataForID struct { diff --git a/api/usecases.go b/api/usecases.go index bb120ee7..37203270 100644 --- a/api/usecases.go +++ b/api/usecases.go @@ -56,6 +56,15 @@ type UseCaseBaseInterface interface { type UseCaseInterface interface { UseCaseBaseInterface - // add the features - AddFeatures() + // add the features described by the Use Case + // + // returns an error if any Feature could not be added + // - errors should not occur during normal usage of eebus-go, and should + // generally be considered fatal implementation errors + // - if an error occurs while adding features to a new Entity, that Entity + // will be in an incomplete state and should not be added to the service + // + // No cleanup occurs on error, some features may end up partially + // configured and unused + AddFeatures() error } diff --git a/examples/ced/main.go b/examples/ced/main.go index 1d2e1014..4aecdde4 100644 --- a/examples/ced/main.go +++ b/examples/ced/main.go @@ -126,12 +126,18 @@ func (h *controlbox) run() { localEntity := h.myService.LocalDevice().EntityForType(model.EntityTypeTypeHeatPumpAppliance) h.uclpc = lpc.NewLPC(localEntity, h.OnLPCEvent) - h.myService.AddUseCase(h.uclpc) + err = h.myService.AddUseCase(h.uclpc) + if err != nil { + log.Fatal(err) + } // h.uclpp = lpp.NewLPP(localEntity, h.OnLPPEvent) // h.myService.AddUseCase(h.uclpp) h.ucmpc = mpc.NewMPC(localEntity, h.OnMPCEvent) - h.myService.AddUseCase(h.ucmpc) + err = h.myService.AddUseCase(h.ucmpc) + if err != nil { + log.Fatal(err) + } if len(config.remoteSKI) == 0 && pairingConfig == nil { os.Exit(0) diff --git a/examples/controlbox/main.go b/examples/controlbox/main.go index 53344a3b..cfc8c304 100644 --- a/examples/controlbox/main.go +++ b/examples/controlbox/main.go @@ -189,10 +189,16 @@ func (h *controlbox) run() { localEntity := h.myService.LocalDevice().EntityForType(model.EntityTypeTypeGridGuard) h.uclpc = lpc.NewLPC(localEntity, h.OnLPCEvent) - h.myService.AddUseCase(h.uclpc) + err = h.myService.AddUseCase(h.uclpc) + if err != nil { + log.Fatal(err) + } h.uclpp = lpp.NewLPP(localEntity, h.OnLPPEvent) - h.myService.AddUseCase(h.uclpp) + err = h.myService.AddUseCase(h.uclpp) + if err != nil { + log.Fatal(err) + } for _, remoteSki := range config.remoteSKIs { h.myService.RegisterRemoteService(shipapi.NewServiceIdentity(remoteSki, "", "")) diff --git a/examples/evse/main.go b/examples/evse/main.go index e2278628..718c09f2 100644 --- a/examples/evse/main.go +++ b/examples/evse/main.go @@ -119,7 +119,10 @@ func (h *evse) run() { localEntity := h.myService.LocalDevice().EntityForType(model.EntityTypeTypeEVSE) h.uclpc = lpc.NewLPC(localEntity, h.OnLPCEvent) - h.myService.AddUseCase(h.uclpc) + err = h.myService.AddUseCase(h.uclpc) + if err != nil { + log.Fatal(err) + } // Initialize local server data _ = h.uclpc.SetConsumptionNominalMax(32000) diff --git a/examples/hems/main.go b/examples/hems/main.go index cfabe83f..4063aa59 100644 --- a/examples/hems/main.go +++ b/examples/hems/main.go @@ -129,19 +129,46 @@ func (h *hems) run() { localEntity := h.myService.LocalDevice().EntityForType(model.EntityTypeTypeCEM) h.uccslpc = cslpc.NewLPC(localEntity, h.OnLPCEvent) - h.myService.AddUseCase(h.uccslpc) + err = h.myService.AddUseCase(h.uccslpc) + if err != nil { + log.Fatal(err) + } + h.uccslpp = cslpp.NewLPP(localEntity, h.OnLPPEvent) - h.myService.AddUseCase(h.uccslpp) + err = h.myService.AddUseCase(h.uccslpp) + if err != nil { + log.Fatal(err) + } + h.uceglpc = eglpc.NewLPC(localEntity, nil) - h.myService.AddUseCase(h.uceglpc) + err = h.myService.AddUseCase(h.uceglpc) + if err != nil { + log.Fatal(err) + } + h.uceglpp = eglpp.NewLPP(localEntity, nil) - h.myService.AddUseCase(h.uceglpp) + err = h.myService.AddUseCase(h.uceglpp) + if err != nil { + log.Fatal(err) + } + h.ucmamgcp = mgcp.NewMGCP(localEntity, h.OnMGCPEvent) - h.myService.AddUseCase(h.ucmamgcp) + err = h.myService.AddUseCase(h.ucmamgcp) + if err != nil { + log.Fatal(err) + } + h.uccemvabd = vabd.NewVABD(localEntity, h.OnVABDEvent) - h.myService.AddUseCase(h.uccemvabd) + err = h.myService.AddUseCase(h.uccemvabd) + if err != nil { + log.Fatal(err) + } + h.uccemvapd = vapd.NewVAPD(localEntity, h.OnVAPDEvent) - h.myService.AddUseCase(h.uccemvapd) + err = h.myService.AddUseCase(h.uccemvapd) + if err != nil { + log.Fatal(err) + } // Initialize local server data _ = h.uccslpc.SetConsumptionNominalMax(32000) diff --git a/examples/remote/ucs.go b/examples/remote/ucs.go index 457963a2..1adb2b66 100644 --- a/examples/remote/ucs.go +++ b/examples/remote/ucs.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "log" "github.com/enbility/eebus-go/api" spineapi "github.com/enbility/spine-go/api" @@ -31,7 +32,10 @@ func (r *Remote) RegisterUseCase(entityType model.EntityTypeType, usecaseId stri ) { r.PropagateEvent(identifier, ski, device, entity, event) }) - r.service.AddUseCase(uc) + err := r.service.AddUseCase(uc) + if err != nil { + log.Fatal(err) + } return r.registerStaticReceiverProxy(usecaseId, uc) } diff --git a/features/server/electricalconnection.go b/features/server/electricalconnection.go index 39bef0a7..3a37e5de 100644 --- a/features/server/electricalconnection.go +++ b/features/server/electricalconnection.go @@ -31,6 +31,51 @@ func NewElectricalConnection(localEntity spineapi.EntityLocalInterface) (*Electr return ec, nil } +// Get or add the id for a electrical connection with a given electricalConnectionDescription +// +// NOTE: This can be used instead of AddDescription to be sure it exists only one id for the same description +// +// will return the id for the electrical connection with the given description +func (e *ElectricalConnection) GetOrAddIdForDescription( + electricalConnectionDescription model.ElectricalConnectionDescriptionDataType, +) (*model.ElectricalConnectionIdType, error) { + electricalConnectionId := (*model.ElectricalConnectionIdType)(nil) + highestExistingElectricalConnectionId := model.ElectricalConnectionIdType(0) + + descriptionData := e.featureLocal.DataCopy(model.FunctionTypeElectricalConnectionDescriptionListData).(*model.ElectricalConnectionDescriptionListDataType) + + if descriptionData != nil && descriptionData.ElectricalConnectionDescriptionData != nil { + for _, description := range descriptionData.ElectricalConnectionDescriptionData { + if description.ElectricalConnectionId != nil && + description.PowerSupplyType == electricalConnectionDescription.PowerSupplyType && + description.AcConnectedPhases == electricalConnectionDescription.AcConnectedPhases && + description.AcRmsPeriodDuration == electricalConnectionDescription.AcRmsPeriodDuration && + description.PositiveEnergyDirection == electricalConnectionDescription.PositiveEnergyDirection && + description.ScopeType == electricalConnectionDescription.ScopeType && + description.Label == electricalConnectionDescription.Label && + description.Description == electricalConnectionDescription.Description { + electricalConnectionId = description.ElectricalConnectionId + return electricalConnectionId, nil + } else if description.ElectricalConnectionId != nil { + if *description.ElectricalConnectionId > highestExistingElectricalConnectionId { + highestExistingElectricalConnectionId = *description.ElectricalConnectionId + } + } + } + } + + electricalConnectionId = util.Ptr(highestExistingElectricalConnectionId + 1) + description := electricalConnectionDescription + description.ElectricalConnectionId = electricalConnectionId + if errType := e.featureLocal.UpdateData(model.FunctionTypeElectricalConnectionDescriptionListData, &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{description}, + }, model.NewFilterTypePartial(), nil); errType != nil { + return nil, errors.New("could not add description data") + } + + return electricalConnectionId, nil +} + // Add a new description data set // // NOTE: the electricalConnectionId has to be provided diff --git a/features/server/electricalconnection_test.go b/features/server/electricalconnection_test.go index bb8187cc..8f6fd1df 100644 --- a/features/server/electricalconnection_test.go +++ b/features/server/electricalconnection_test.go @@ -119,6 +119,40 @@ func (s *ElectricalConnectionSuite) Test_Description() { assert.NotNil(s.T(), data) } +func (s *ElectricalConnectionSuite) Test_GetOrAddIdForDescription() { + filter := model.ElectricalConnectionDescriptionDataType{} + + data, err := s.sut.GetDescriptionsForFilter(filter) + assert.NotNil(s.T(), err) + assert.Nil(s.T(), data) + + desc1 := model.ElectricalConnectionDescriptionDataType{ + PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeDc), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + } + + eConnectionId, err := s.sut.GetOrAddIdForDescription(desc1) + assert.Nil(s.T(), err) + assert.Equal(s.T(), model.ElectricalConnectionIdType(1), *eConnectionId) + + eConnectionId, err = s.sut.GetOrAddIdForDescription(desc1) + assert.Nil(s.T(), err) + assert.Equal(s.T(), model.ElectricalConnectionIdType(1), *eConnectionId) + + desc2 := model.ElectricalConnectionDescriptionDataType{ + PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + } + + eConnectionId2, err := s.sut.GetOrAddIdForDescription(desc2) + assert.Nil(s.T(), err) + assert.Equal(s.T(), model.ElectricalConnectionIdType(2), *eConnectionId2) + + eConnectionId, err = s.sut.GetOrAddIdForDescription(desc1) + assert.Nil(s.T(), err) + assert.Equal(s.T(), model.ElectricalConnectionIdType(1), *eConnectionId) +} + func (s *ElectricalConnectionSuite) Test_ParameterDescription() { filter := model.ElectricalConnectionParameterDescriptionDataType{} diff --git a/mocks/ElectricalConnectionServerInterface.go b/mocks/ElectricalConnectionServerInterface.go index 101b49b3..95420edd 100644 --- a/mocks/ElectricalConnectionServerInterface.go +++ b/mocks/ElectricalConnectionServerInterface.go @@ -503,6 +503,68 @@ func (_c *ElectricalConnectionServerInterface_GetDescriptionsForFilter_Call) Run return _c } +// GetOrAddIdForDescription provides a mock function for the type ElectricalConnectionServerInterface +func (_mock *ElectricalConnectionServerInterface) GetOrAddIdForDescription(electricalConnectionDescription model.ElectricalConnectionDescriptionDataType) (*model.ElectricalConnectionIdType, error) { + ret := _mock.Called(electricalConnectionDescription) + + if len(ret) == 0 { + panic("no return value specified for GetOrAddIdForDescription") + } + + var r0 *model.ElectricalConnectionIdType + var r1 error + if returnFunc, ok := ret.Get(0).(func(model.ElectricalConnectionDescriptionDataType) (*model.ElectricalConnectionIdType, error)); ok { + return returnFunc(electricalConnectionDescription) + } + if returnFunc, ok := ret.Get(0).(func(model.ElectricalConnectionDescriptionDataType) *model.ElectricalConnectionIdType); ok { + r0 = returnFunc(electricalConnectionDescription) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.ElectricalConnectionIdType) + } + } + if returnFunc, ok := ret.Get(1).(func(model.ElectricalConnectionDescriptionDataType) error); ok { + r1 = returnFunc(electricalConnectionDescription) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetOrAddIdForDescription' +type ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call struct { + *mock.Call +} + +// GetOrAddIdForDescription is a helper method to define mock.On call +// - electricalConnectionDescription model.ElectricalConnectionDescriptionDataType +func (_e *ElectricalConnectionServerInterface_Expecter) GetOrAddIdForDescription(electricalConnectionDescription interface{}) *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call { + return &ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call{Call: _e.mock.On("GetOrAddIdForDescription", electricalConnectionDescription)} +} + +func (_c *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call) Run(run func(electricalConnectionDescription model.ElectricalConnectionDescriptionDataType)) *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 model.ElectricalConnectionDescriptionDataType + if args[0] != nil { + arg0 = args[0].(model.ElectricalConnectionDescriptionDataType) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call) Return(electricalConnectionIdType *model.ElectricalConnectionIdType, err error) *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call { + _c.Call.Return(electricalConnectionIdType, err) + return _c +} + +func (_c *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call) RunAndReturn(run func(electricalConnectionDescription model.ElectricalConnectionDescriptionDataType) (*model.ElectricalConnectionIdType, error)) *ElectricalConnectionServerInterface_GetOrAddIdForDescription_Call { + _c.Call.Return(run) + return _c +} + // GetParameterDescriptionsForFilter provides a mock function for the type ElectricalConnectionServerInterface func (_mock *ElectricalConnectionServerInterface) GetParameterDescriptionsForFilter(filter model.ElectricalConnectionParameterDescriptionDataType) ([]model.ElectricalConnectionParameterDescriptionDataType, error) { ret := _mock.Called(filter) diff --git a/mocks/EntityEventCallback.go b/mocks/EntityEventCallback.go index 042ca199..b0dd293f 100644 --- a/mocks/EntityEventCallback.go +++ b/mocks/EntityEventCallback.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. +// Code generated by mockery v2.53.4. DO NOT EDIT. package mocks @@ -54,7 +54,7 @@ func (_c *EntityEventCallback_Execute_Call) Return() *EntityEventCallback_Execut } func (_c *EntityEventCallback_Execute_Call) RunAndReturn(run func(string, api.DeviceRemoteInterface, api.EntityRemoteInterface, eebus_goapi.EventType)) *EntityEventCallback_Execute_Call { - _c.Call.Return(run) + _c.Run(run) return _c } diff --git a/mocks/ServiceInterface.go b/mocks/ServiceInterface.go index 05dd00e5..3b0f3a7e 100644 --- a/mocks/ServiceInterface.go +++ b/mocks/ServiceInterface.go @@ -40,9 +40,20 @@ func (_m *ServiceInterface) EXPECT() *ServiceInterface_Expecter { } // AddUseCase provides a mock function for the type ServiceInterface -func (_mock *ServiceInterface) AddUseCase(useCase api.UseCaseInterface) { - _mock.Called(useCase) - return +func (_mock *ServiceInterface) AddUseCase(useCase api.UseCaseInterface) error { + ret := _mock.Called(useCase) + + if len(ret) == 0 { + panic("no return value specified for AddUseCase") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(api.UseCaseInterface) error); ok { + r0 = returnFunc(useCase) + } else { + r0 = ret.Error(0) + } + return r0 } // ServiceInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' @@ -69,13 +80,13 @@ func (_c *ServiceInterface_AddUseCase_Call) Run(run func(useCase api.UseCaseInte return _c } -func (_c *ServiceInterface_AddUseCase_Call) Return() *ServiceInterface_AddUseCase_Call { - _c.Call.Return() +func (_c *ServiceInterface_AddUseCase_Call) Return(err error) *ServiceInterface_AddUseCase_Call { + _c.Call.Return(err) return _c } -func (_c *ServiceInterface_AddUseCase_Call) RunAndReturn(run func(useCase api.UseCaseInterface)) *ServiceInterface_AddUseCase_Call { - _c.Run(run) +func (_c *ServiceInterface_AddUseCase_Call) RunAndReturn(run func(useCase api.UseCaseInterface) error) *ServiceInterface_AddUseCase_Call { + _c.Call.Return(run) return _c } diff --git a/mocks/UseCaseInterface.go b/mocks/UseCaseInterface.go index 31081cf3..308ef381 100644 --- a/mocks/UseCaseInterface.go +++ b/mocks/UseCaseInterface.go @@ -38,9 +38,20 @@ func (_m *UseCaseInterface) EXPECT() *UseCaseInterface_Expecter { } // AddFeatures provides a mock function for the type UseCaseInterface -func (_mock *UseCaseInterface) AddFeatures() { - _mock.Called() - return +func (_mock *UseCaseInterface) AddFeatures() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for AddFeatures") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 } // UseCaseInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' @@ -60,13 +71,13 @@ func (_c *UseCaseInterface_AddFeatures_Call) Run(run func()) *UseCaseInterface_A return _c } -func (_c *UseCaseInterface_AddFeatures_Call) Return() *UseCaseInterface_AddFeatures_Call { - _c.Call.Return() +func (_c *UseCaseInterface_AddFeatures_Call) Return(err error) *UseCaseInterface_AddFeatures_Call { + _c.Call.Return(err) return _c } -func (_c *UseCaseInterface_AddFeatures_Call) RunAndReturn(run func()) *UseCaseInterface_AddFeatures_Call { - _c.Run(run) +func (_c *UseCaseInterface_AddFeatures_Call) RunAndReturn(run func() error) *UseCaseInterface_AddFeatures_Call { + _c.Call.Return(run) return _c } diff --git a/service/service.go b/service/service.go index 27904413..e41a1eed 100644 --- a/service/service.go +++ b/service/service.go @@ -205,11 +205,22 @@ func (s *Service) IsRunning() bool { } // add a use case to the service -func (s *Service) AddUseCase(useCase api.UseCaseInterface) { +// +// returns an error when adding features to the entity fails +// +// errors should not occur during normal usage of eebus-go, and should +// generally be considered fatal implementation errors +// +// see usecase.AddFeatures() for more information +func (s *Service) AddUseCase(useCase api.UseCaseInterface) error { s.usecases = append(s.usecases, useCase) - useCase.AddFeatures() + if err := useCase.AddFeatures(); err != nil { + return err + } useCase.AddUseCase() + + return nil } func (s *Service) Configuration() *api.Configuration { diff --git a/service/service_test.go b/service/service_test.go index 122f5a43..08546503 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -64,12 +64,20 @@ func (s *ServiceSuite) BeforeTest(suiteName, testName string) { func (s *ServiceSuite) Test_AddUseCase() { ucMock := mocks.NewUseCaseInterface(s.T()) - ucMock.EXPECT().AddFeatures().Return().Once() + ucMock.EXPECT().AddFeatures().Return(nil).Once() ucMock.EXPECT().AddUseCase().Return().Once() s.sut.AddUseCase(ucMock) } +func (s *ServiceSuite) Test_AddUseCase_Error() { + ucMock := mocks.NewUseCaseInterface(s.T()) + ucMock.EXPECT().AddFeatures().Return(assert.AnError).Once() + + err := s.sut.AddUseCase(ucMock) + assert.Equal(s.T(), assert.AnError, err) +} + func (s *ServiceSuite) Test_EEBUSHandler() { testSki := "test" diff --git a/usecases/README.md b/usecases/README.md index 47a977b1..665eb4b2 100644 --- a/usecases/README.md +++ b/usecases/README.md @@ -29,8 +29,18 @@ Actors: - `lpc`: Limitation of Power Consumption - `lpp`: Limitation of Power Production +- `gcp`: Grid Connection Point + + Use Cases: + - `mgcp`: Monitoring of Grid Connection Point + - `ma`: Monitoring Appliance Use Cases: - `mpc`: Monitoring of Power Consumption - `mgcp`: Monitoring of Grid Connection Point + +- `mu`: Monitored Unit + + Use Cases: + - `mpc`: Monitoring of Power Consumption diff --git a/usecases/api/api.go b/usecases/api/api.go index 898b38b1..3452e50a 100644 --- a/usecases/api/api.go +++ b/usecases/api/api.go @@ -1,7 +1,44 @@ package api -import "github.com/enbility/spine-go/model" +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" +) //go:generate mockery var PhaseNameMapping = []model.ElectricalConnectionPhaseNameType{model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC} + +// used to enable batch data updates for certain usecases +// +// a usecase that wants to provide batch update capabilities using this interface should +// +// 1. provide methods that return a type implementing this interface +// 2. provide an Update method that accepts a list of this interface +// +// The Update method can then iterate over the provided UpdateData, ensure all +// data points are supported, and then create a batched spine update request +type UpdateData interface { + Supported() bool + NotSupportedError() error +} + +// used to enable batch data updates for MeaserumentData +// +// usecases can use this interface to provide batch update capabilities by +// implementing a method that takes a list of this interface and passes a list +// of MeasurementData to Measurement.UpdateDataForIds +type UpdateMeasurementData interface { + UpdateData + MeasurementData() api.MeasurementDataForID +} + +// used to enable batch data updates for ConfigurationData +// +// usecases can use this interface to provide batch update capabilities by +// implementing a method that takes a list of this interface and updates the +// configuration data +type UpdateConfigurationData interface { + UpdateData + ConfigurationData() model.DeviceConfigurationKeyValueDataType +} diff --git a/usecases/api/gcp_mgcp.go b/usecases/api/gcp_mgcp.go new file mode 100644 index 00000000..e471cea3 --- /dev/null +++ b/usecases/api/gcp_mgcp.go @@ -0,0 +1,238 @@ +package api + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" + "time" +) + +type GcpMGCPInterface interface { + api.UseCaseInterface + + // ------------------------- Getters ------------------------- // + + // Scenario 1 + + // get the current power limitation factor + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + PowerLimitationFactor() (float64, error) + + // Scenario 2 + + // get the momentary power consumption or production at the grid connection point + // + // return values: + // - positive values are used for consumption + // - negative values are used for production + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + PowerTotal() (float64, error) + + // Scenario 3 + + // get the total feed in energy at the grid connection point + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + EnergyFeedIn() (float64, error) + + // Scenario 4 + + // get the total consumption energy at the grid connection point + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + EnergyConsumed() (float64, error) + + // Scenario 5 + + // get the momentary phase specific current consumption or production + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + CurrentPerPhase() ([]float64, error) + + // Scenario 6 + + // get the momentary phase specific voltage consumption or production + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + VoltagePerPhase() ([]float64, error) + + // Scenario 7 + + // get frequency + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + Frequency() (float64, error) + + // ------------------------- Setters ------------------------- // + + // Update the data + + // use Update to update the data of the MGCP Usecase + // use it like this: + // + // mgcp.Update( + // mgcp.MeasuredAcPowerTotal(1000, nil, nil), + // mgcp.MeasuredAcPowerPhaseA(500, nil, nil), + // ... + // ) + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + Update(updateValueTypes ...UpdateData) error + + // Scenario 1 + + // Use UpdateDataPowerLimitationFactor in Update to set the current power limitation factor + UpdateDataPowerLimitationFactor(value float64) UpdateData + + // Scenario 2 + + // Use UpdateDataPowerTotal in Update to set the momentary power consumption or production at the grid connection point + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataPowerTotal( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Scenario 3 + + // Use UpdateDataEnergyFeedIn in Update to set the total feed in energy at the grid connection point + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + // The evaluationPeriodStart and evaluationPeriodEnd are optional and can be nil (both must be set to be used) + UpdateDataEnergyFeedIn( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationPeriodStart *time.Time, + evaluationPeriodEnd *time.Time, + ) UpdateData + + // Scenario 4 + + // Use UpdateDataEnergyConsumed in Update to set the total consumption energy at the grid connection point + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + // The evaluationPeriodStart and evaluationPeriodEnd are optional and can be nil (both must be set to be used) + UpdateDataEnergyConsumed( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationPeriodStart *time.Time, + evaluationPeriodEnd *time.Time, + ) UpdateData + + // Scenario 5 + + // Use UpdateDataCurrentPhaseA in Update to set the momentary phase specific current consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataCurrentPhaseA( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataCurrentPhaseB in Update to set the momentary phase specific current consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataCurrentPhaseB( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataCurrentPhaseC in Update to set the momentary phase specific current consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataCurrentPhaseC( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Scenario 6 + + // Use UpdateDataVoltagePhaseA in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseA( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataVoltagePhaseB in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseB( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataVoltagePhaseC in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseC( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataVoltagePhaseAToB in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseAToB( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataVoltagePhaseBToC in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseBToC( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Use UpdateDataVoltagePhaseCToA in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseCToA( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData + + // Scenario 7 + + // Use UpdateDataFrequency in Update to set the frequency + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataFrequency( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + ) UpdateData +} diff --git a/usecases/api/mu_mpc.go b/usecases/api/mu_mpc.go new file mode 100644 index 00000000..26baa548 --- /dev/null +++ b/usecases/api/mu_mpc.go @@ -0,0 +1,201 @@ +package api + +import ( + "time" + + "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" +) + +// Actor: Monitoring Unit +// UseCase: Monitoring of Power Consumption +type MuMPCInterface interface { + api.UseCaseInterface + // ------------------------- Getters ------------------------- // + + // Scenario 1 + + // get the momentary active power consumption or production + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + Power() (float64, error) + + // get the momentary active power consumption or production per phase + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + PowerPerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) + + // Scenario 2 + + // get the total feed in energy + // + // - negative values are used for production + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + EnergyProduced() (float64, error) + + // get the total feed in energy + // + // - negative values are used for production + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + EnergyConsumed() (float64, error) + + // Scenario 3 + + // get the momentary phase specific current consumption or production + // + // - positive values are used for consumption + // - negative values are used for production + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + CurrentPerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) + + // Scenario 4 + + // get the phase specific voltage details + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + VoltagePerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) + + // Scenario 5 + + // get frequency + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + Frequency() (float64, error) + + // ------------------------- Setters ------------------------- // + + // use Update to update the measurement data + // use it like this: + // + // mpc.Update( + // mpc.UpdateDataPowerTotal(1000, nil, nil), + // mpc.UpdateDataPowerPhaseA(500, nil, nil), + // ... + // ) + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + Update(data ...UpdateMeasurementData) error + + // Scenario 1 + + // use UpdateDataPowerTotal in Update to set the momentary active power consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataPowerTotal(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataPowerPhaseA in Update to set the momentary active power consumption or production per phase + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataPowerPhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataPowerPhaseB in Update to set the momentary active power consumption or production per phase + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataPowerPhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataPowerPhaseC in Update to set the momentary active power consumption or production per phase + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataPowerPhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // Scenario 2 + + // use UpdateDataEnergyConsumed in Update to set the total feed in energy + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + // The evaluationStart and End are optional and can be nil (both must be set to be used) + UpdateDataEnergyConsumed( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationStart *time.Time, + evaluationEnd *time.Time, + ) UpdateMeasurementData + + // use UpdateDataEnergyProduced in Update to set the total feed in energy + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + // The evaluationStart and End are optional and can be nil (both must be set to be used) + UpdateDataEnergyProduced( + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationStart *time.Time, + evaluationEnd *time.Time, + ) UpdateMeasurementData + + // Scenario 3 + + // use UpdateDataCurrentPhaseA in Update to set the momentary phase specific current consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataCurrentPhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataCurrentPhaseB in Update to set the momentary phase specific current consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataCurrentPhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataCurrentPhaseC in Update to set the momentary phase specific current consumption or production + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataCurrentPhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // Scenario 4 + + // use UpdateDataVoltagePhaseA in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataVoltagePhaseB in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataVoltagePhaseC in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataVoltagePhaseAToB in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseAToB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataVoltagePhaseBToC in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseBToC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // use UpdateDataVoltagePhaseAToC in Update to set the phase specific voltage details + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataVoltagePhaseAToC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData + + // Scenario 5 + + // use AcFrequency in Update to set the frequency + // The timestamp is optional and can be nil + // The valueState shall be set if it differs from the normal valueState otherwise it can be nil + UpdateDataFrequency(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) UpdateMeasurementData +} diff --git a/usecases/cem/cevc/testhelper_test.go b/usecases/cem/cevc/testhelper_test.go index 2ff55e74..52c365c3 100644 --- a/usecases/cem/cevc/testhelper_test.go +++ b/usecases/cem/cevc/testhelper_test.go @@ -75,7 +75,7 @@ func (s *CemCEVCSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewCEVC(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() var entities []spineapi.EntityRemoteInterface diff --git a/usecases/cem/cevc/usecase.go b/usecases/cem/cevc/usecase.go index 72186c32..685212b8 100644 --- a/usecases/cem/cevc/usecase.go +++ b/usecases/cem/cevc/usecase.go @@ -1,6 +1,7 @@ package cevc import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -69,6 +70,7 @@ func NewCEVC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &CEVC{ @@ -80,7 +82,7 @@ func NewCEVC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC return uc } -func (e *CEVC) AddFeatures() { +func (e *CEVC) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceConfiguration, @@ -89,11 +91,15 @@ func (e *CEVC) AddFeatures() { model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("failed to add feature: " + string(feature)) + } } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, true, false) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + + return nil } diff --git a/usecases/cem/evcc/usecase.go b/usecases/cem/evcc/usecase.go index de647580..8f8461c7 100644 --- a/usecases/cem/evcc/usecase.go +++ b/usecases/cem/evcc/usecase.go @@ -1,6 +1,7 @@ package evcc import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -82,6 +83,7 @@ func NewEVCC( UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &EVCC{ @@ -94,7 +96,7 @@ func NewEVCC( return uc } -func (e *EVCC) AddFeatures() { +func (e *EVCC) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceConfiguration, @@ -105,6 +107,11 @@ func (e *EVCC) AddFeatures() { } for _, feature := range clientFeatures { f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f == nil { + return errors.New("could not add feature: " + string(feature)) + } f.AddResultCallback(e.HandleResponse) } + + return nil } diff --git a/usecases/cem/evcem/testhelper_test.go b/usecases/cem/evcem/testhelper_test.go index c1d43c55..86043980 100644 --- a/usecases/cem/evcem/testhelper_test.go +++ b/usecases/cem/evcem/testhelper_test.go @@ -70,7 +70,7 @@ func (s *CemEVCEMSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewEVCEM(s.service, localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() var entities []spineapi.EntityRemoteInterface diff --git a/usecases/cem/evcem/usecase.go b/usecases/cem/evcem/usecase.go index bb76eb83..696230e2 100644 --- a/usecases/cem/evcem/usecase.go +++ b/usecases/cem/evcem/usecase.go @@ -1,6 +1,7 @@ package evcem import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" usecase "github.com/enbility/eebus-go/usecases/usecase" @@ -68,7 +69,9 @@ func NewEVCEM( eventCB, UseCaseSupportUpdate, validActorTypes, - validEntityTypes) + validEntityTypes, + false, + ) uc := &EVCEM{ UseCaseBase: usecase, @@ -80,13 +83,17 @@ func NewEVCEM( return uc } -func (e *EVCEM) AddFeatures() { +func (e *EVCEM) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeElectricalConnection, model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("failed to add feature: " + string(feature)) + } } + + return nil } diff --git a/usecases/cem/evsecc/testhelper_test.go b/usecases/cem/evsecc/testhelper_test.go index 4ab305a5..4927b9f1 100644 --- a/usecases/cem/evsecc/testhelper_test.go +++ b/usecases/cem/evsecc/testhelper_test.go @@ -73,7 +73,7 @@ func (s *CemEVSECCSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewEVSECC(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() var entities []spineapi.EntityRemoteInterface diff --git a/usecases/cem/evsecc/usecase.go b/usecases/cem/evsecc/usecase.go index 7f41137a..1df087af 100644 --- a/usecases/cem/evsecc/usecase.go +++ b/usecases/cem/evsecc/usecase.go @@ -1,6 +1,7 @@ package evsecc import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -50,7 +51,9 @@ func NewEVSECC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEven eventCB, UseCaseSupportUpdate, validActorTypes, - validEntityTypes) + validEntityTypes, + false, + ) uc := &EVSECC{ UseCaseBase: usecase, @@ -61,7 +64,7 @@ func NewEVSECC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEven return uc } -func (e *EVSECC) AddFeatures() { +func (e *EVSECC) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceClassification, @@ -69,6 +72,10 @@ func (e *EVSECC) AddFeatures() { } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } + + return nil } diff --git a/usecases/cem/evsoc/testhelper_test.go b/usecases/cem/evsoc/testhelper_test.go index 8ce937d7..495de203 100644 --- a/usecases/cem/evsoc/testhelper_test.go +++ b/usecases/cem/evsoc/testhelper_test.go @@ -73,7 +73,7 @@ func (s *CemEVSOCSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewEVSOC(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() var entities []spineapi.EntityRemoteInterface diff --git a/usecases/cem/evsoc/usecase.go b/usecases/cem/evsoc/usecase.go index 975a7dbc..55c241d9 100644 --- a/usecases/cem/evsoc/usecase.go +++ b/usecases/cem/evsoc/usecase.go @@ -1,6 +1,7 @@ package evsoc import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" usecase "github.com/enbility/eebus-go/usecases/usecase" @@ -46,6 +47,7 @@ func NewEVSOC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEvent UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &EVSOC{ @@ -57,15 +59,19 @@ func NewEVSOC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEvent return uc } -func (e *EVSOC) AddFeatures() { +func (e *EVSOC) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeElectricalConnection, model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } + + return nil } func (e *EVSOC) UpdateUseCaseAvailability(available bool) { diff --git a/usecases/cem/opev/testhelper_test.go b/usecases/cem/opev/testhelper_test.go index 67c19a79..3efa29ff 100644 --- a/usecases/cem/opev/testhelper_test.go +++ b/usecases/cem/opev/testhelper_test.go @@ -73,7 +73,7 @@ func (s *CemOPEVSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewOPEV(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() var clientFeatures = []model.FeatureTypeType{ diff --git a/usecases/cem/opev/usecase.go b/usecases/cem/opev/usecase.go index 272fdea3..185fa5a4 100644 --- a/usecases/cem/opev/usecase.go +++ b/usecases/cem/opev/usecase.go @@ -1,6 +1,7 @@ package opev import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -57,6 +58,7 @@ func NewOPEV(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &OPEV{ @@ -68,18 +70,25 @@ func NewOPEV(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC return uc } -func (e *OPEV) AddFeatures() { +func (e *OPEV) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeLoadControl, model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if f == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeDeviceDiagnosis)) + } f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, true, false) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + + return nil } diff --git a/usecases/cem/oscev/testhelper_test.go b/usecases/cem/oscev/testhelper_test.go index a771abc8..b2924b1f 100644 --- a/usecases/cem/oscev/testhelper_test.go +++ b/usecases/cem/oscev/testhelper_test.go @@ -72,7 +72,7 @@ func (s *CemOSCEVSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewOSCEV(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() var clientFeatures = []model.FeatureTypeType{ diff --git a/usecases/cem/oscev/usecase.go b/usecases/cem/oscev/usecase.go index 91411664..8befa8da 100644 --- a/usecases/cem/oscev/usecase.go +++ b/usecases/cem/oscev/usecase.go @@ -1,6 +1,7 @@ package oscev import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -57,6 +58,7 @@ func NewOSCEV(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEvent UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &OSCEV{ @@ -68,18 +70,26 @@ func NewOSCEV(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEvent return uc } -func (e *OSCEV) AddFeatures() { +func (e *OSCEV) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeLoadControl, model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if f == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeDeviceDiagnosis)) + } + f.AddFunctionType(model.FunctionTypeDeviceDiagnosisStateData, true, false) f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + + return nil } diff --git a/usecases/cem/vabd/testhelper_test.go b/usecases/cem/vabd/testhelper_test.go index 7f39eba1..ff0a6eb9 100644 --- a/usecases/cem/vabd/testhelper_test.go +++ b/usecases/cem/vabd/testhelper_test.go @@ -73,7 +73,7 @@ func (s *CemVABDSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewVABD(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() s.remoteDevice, s.batteryEntity = setupDevices(s.service, s.T()) diff --git a/usecases/cem/vabd/usecase.go b/usecases/cem/vabd/usecase.go index a4ccd22e..6ae5dc33 100644 --- a/usecases/cem/vabd/usecase.go +++ b/usecases/cem/vabd/usecase.go @@ -1,6 +1,7 @@ package vabd import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -71,6 +72,7 @@ func NewVABD(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &VABD{ @@ -82,7 +84,7 @@ func NewVABD(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC return uc } -func (e *VABD) AddFeatures() { +func (e *VABD) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceConfiguration, @@ -90,6 +92,10 @@ func (e *VABD) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } + + return nil } diff --git a/usecases/cem/vapd/testhelper_test.go b/usecases/cem/vapd/testhelper_test.go index cc05e217..380612ec 100644 --- a/usecases/cem/vapd/testhelper_test.go +++ b/usecases/cem/vapd/testhelper_test.go @@ -73,7 +73,7 @@ func (s *CemVAPDSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewVAPD(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() s.remoteDevice, s.pvEntity = setupDevices(s.service, s.T()) diff --git a/usecases/cem/vapd/usecase.go b/usecases/cem/vapd/usecase.go index 10b27f8d..da3cd3a3 100644 --- a/usecases/cem/vapd/usecase.go +++ b/usecases/cem/vapd/usecase.go @@ -1,6 +1,7 @@ package vapd import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -62,6 +63,7 @@ func NewVAPD(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &VAPD{ @@ -73,7 +75,7 @@ func NewVAPD(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventC return uc } -func (e *VAPD) AddFeatures() { +func (e *VAPD) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceConfiguration, @@ -81,6 +83,10 @@ func (e *VAPD) AddFeatures() { model.FeatureTypeTypeMeasurement, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } + + return nil } diff --git a/usecases/cs/lpc/testhelper_test.go b/usecases/cs/lpc/testhelper_test.go index c155de57..3f759138 100644 --- a/usecases/cs/lpc/testhelper_test.go +++ b/usecases/cs/lpc/testhelper_test.go @@ -16,6 +16,7 @@ import ( "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -76,7 +77,7 @@ func (s *CsLPCSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewLPC(localEntity, s.Event) - s.sut.AddFeatures() + assert.Nil(s.T(), s.sut.AddFeatures()) s.sut.AddUseCase() s.loadControlFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) diff --git a/usecases/cs/lpc/usecase.go b/usecases/cs/lpc/usecase.go index f897ad54..eaa26c0b 100644 --- a/usecases/cs/lpc/usecase.go +++ b/usecases/cs/lpc/usecase.go @@ -1,6 +1,7 @@ package lpc import ( + "errors" "sync" "github.com/enbility/eebus-go/api" @@ -42,8 +43,13 @@ var _ ucapi.CsLPCInterface = (*LPC)(nil) func NewLPC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCallback) *LPC { validActorTypes := []model.UseCaseActorType{model.UseCaseActorTypeEnergyGuard} validEntityTypes := []model.EntityTypeType{ - model.EntityTypeTypeGridGuard, - model.EntityTypeTypeCEM, // KEO uses this entity type for an SMGW whysoever + model.EntityTypeTypeCEM, + model.EntityTypeTypeCompressor, + model.EntityTypeTypeEVSE, + model.EntityTypeTypeHeatPumpAppliance, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, } useCaseScenarios := []api.UseCaseScenario{ { @@ -76,6 +82,7 @@ func NewLPC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCa UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &LPC{ @@ -225,15 +232,23 @@ func (e *LPC) deviceConfigurationWriteCB(msg *spineapi.Message) { go e.approveOrDenyDeviceConfiguration(msg, true, "") } -func (e *LPC) AddFeatures() { +func (e *LPC) AddFeatures() error { // client features - _ = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient); f == nil { + return errors.New("feature not found: DeviceDiagnosis") + } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + if f == nil { + return errors.New("feature not found: LoadControl") + } + f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) - _ = f.AddWriteApprovalCallback(e.loadControlWriteCB) + if err := f.AddWriteApprovalCallback(e.loadControlWriteCB); err != nil { + return err + } measurementId := internal.GetPowerTotalMeasurementId(e.LocalEntity) newLimitDesc := model.LoadControlLimitDescriptionDataType{ @@ -244,20 +259,26 @@ func (e *LPC) AddFeatures() { Unit: util.Ptr(model.UnitOfMeasurementTypeW), ScopeType: util.Ptr(model.ScopeTypeTypeActivePowerLimit), } - if lc, err := server.NewLoadControl(e.LocalEntity); err == nil { - limitId := lc.AddLimitDescription(newLimitDesc) - - newLimiData := []api.LoadControlLimitDataForID{ - { - Data: model.LoadControlLimitDataType{ - Value: model.NewScaledNumberType(0), - IsLimitChangeable: util.Ptr(true), - IsLimitActive: util.Ptr(false), - }, - Id: *limitId, + + lc, err := server.NewLoadControl(e.LocalEntity) + if err != nil { + return err + } + + limitId := lc.AddLimitDescription(newLimitDesc) + + newLimiData := []api.LoadControlLimitDataForID{ + { + Data: model.LoadControlLimitDataType{ + Value: model.NewScaledNumberType(0), + IsLimitChangeable: util.Ptr(true), + IsLimitActive: util.Ptr(false), }, - } - _ = lc.UpdateLimitDataForIds(newLimiData) + Id: *limitId, + }, + } + if err := lc.UpdateLimitDataForIds(newLimiData); err != nil { + return err } f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) @@ -265,55 +286,62 @@ func (e *LPC) AddFeatures() { f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) _ = f.AddWriteApprovalCallback(e.deviceConfigurationWriteCB) - if dcs, err := server.NewDeviceConfiguration(e.LocalEntity); err == nil { - dcs.AddKeyValueDescription( - model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - ) + dcs, err := server.NewDeviceConfiguration(e.LocalEntity) + if err != nil { + return err + } - // only add if it doesn't exist yet - filter := model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - } - if data, err := dcs.GetKeyValueDescriptionsForFilter(filter); err == nil && len(data) == 0 { - dcs.AddKeyValueDescription( - model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), - }, - ) - } + dcs.AddKeyValueDescription( + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + }, + ) - value := &model.DeviceConfigurationKeyValueValueType{ - ScaledNumber: model.NewScaledNumberType(0), - } - _ = dcs.UpdateKeyValueDataForFilter( - model.DeviceConfigurationKeyValueDataType{ - Value: value, - IsValueChangeable: util.Ptr(true), - }, - nil, + // only add if it doesn't exist yet + filter := model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + } + if data, err := dcs.GetKeyValueDescriptionsForFilter(filter); err == nil && len(data) == 0 { + dcs.AddKeyValueDescription( model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), }, ) + } - value = &model.DeviceConfigurationKeyValueValueType{ - Duration: model.NewDurationType(0), - } - _ = dcs.UpdateKeyValueDataForFilter( - model.DeviceConfigurationKeyValueDataType{ - Value: value, - IsValueChangeable: util.Ptr(true), - }, - nil, - model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - }, - ) + value := &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(0), + } + if err := dcs.UpdateKeyValueDataForFilter( + model.DeviceConfigurationKeyValueDataType{ + Value: value, + IsValueChangeable: util.Ptr(true), + }, + nil, + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeConsumptionActivePowerLimit), + }, + ); err != nil { + return err + } + + value = &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(0), + } + if err := dcs.UpdateKeyValueDataForFilter( + model.DeviceConfigurationKeyValueDataType{ + Value: value, + IsValueChangeable: util.Ptr(true), + }, + nil, + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + ); err != nil { + return err } f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) @@ -322,16 +350,22 @@ func (e *LPC) AddFeatures() { f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, false) - if ec, err := server.NewElectricalConnection(e.LocalEntity); err == nil { - electricalConnectionId := internal.GetElectricalConnectionId(e.LocalEntity) - parameterId := internal.GetParameterIdForACPowerTotalMeasurement(e.LocalEntity, electricalConnectionId, measurementId) - newCharData := model.ElectricalConnectionCharacteristicDataType{ - ElectricalConnectionId: util.Ptr(electricalConnectionId), - ParameterId: util.Ptr(parameterId), - CharacteristicContext: util.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), - CharacteristicType: util.Ptr(e.characteristicType()), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - } - _, _ = ec.AddCharacteristic(newCharData) + ec, err := server.NewElectricalConnection(e.LocalEntity) + if err != nil { + return err } + electricalConnectionId := internal.GetElectricalConnectionId(e.LocalEntity) + parameterId := internal.GetParameterIdForACPowerTotalMeasurement(e.LocalEntity, electricalConnectionId, measurementId) + newCharData := model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionId: util.Ptr(electricalConnectionId), + ParameterId: util.Ptr(parameterId), + CharacteristicContext: util.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: util.Ptr(e.characteristicType()), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + } + if _, err := ec.AddCharacteristic(newCharData); err != nil { + return err + } + + return nil } diff --git a/usecases/cs/lpp/testhelper_test.go b/usecases/cs/lpp/testhelper_test.go index 539c5c9a..f6679d4e 100644 --- a/usecases/cs/lpp/testhelper_test.go +++ b/usecases/cs/lpp/testhelper_test.go @@ -16,6 +16,7 @@ import ( "github.com/enbility/spine-go/model" "github.com/enbility/spine-go/spine" "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -76,7 +77,7 @@ func (s *CsLPPSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewLPP(localEntity, s.Event) - s.sut.AddFeatures() + assert.Nil(s.T(), s.sut.AddFeatures()) s.sut.AddUseCase() s.loadControlFeature = localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) diff --git a/usecases/cs/lpp/usecase.go b/usecases/cs/lpp/usecase.go index 1087bc4d..6997eca5 100644 --- a/usecases/cs/lpp/usecase.go +++ b/usecases/cs/lpp/usecase.go @@ -1,6 +1,7 @@ package lpp import ( + "errors" "sync" "github.com/enbility/eebus-go/api" @@ -42,8 +43,11 @@ var _ ucapi.CsLPPInterface = (*LPP)(nil) func NewLPP(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCallback) *LPP { validActorTypes := []model.UseCaseActorType{model.UseCaseActorTypeEnergyGuard} validEntityTypes := []model.EntityTypeType{ - model.EntityTypeTypeGridGuard, - model.EntityTypeTypeCEM, // KEO uses this entity type for an SMGW whysoever + model.EntityTypeTypeCEM, + model.EntityTypeTypeEVSE, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, } useCaseScenarios := []api.UseCaseScenario{ { @@ -75,6 +79,7 @@ func NewLPP(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCa UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &LPP{ @@ -225,15 +230,22 @@ func (e *LPP) deviceConfigurationWriteCB(msg *spineapi.Message) { go e.approveOrDenyDeviceConfiguration(msg, true, "") } -func (e *LPP) AddFeatures() { +func (e *LPP) AddFeatures() error { // client features - _ = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeDeviceDiagnosis)) + } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeLoadControl, model.RoleTypeServer) + if f == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeLoadControl)) + } f.AddFunctionType(model.FunctionTypeLoadControlLimitDescriptionListData, true, false) f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) - _ = f.AddWriteApprovalCallback(e.loadControlWriteCB) + if err := f.AddWriteApprovalCallback(e.loadControlWriteCB); err != nil { + return err + } measurementId := internal.GetPowerTotalMeasurementId(e.LocalEntity) newLimitDesc := model.LoadControlLimitDescriptionDataType{ @@ -244,20 +256,26 @@ func (e *LPP) AddFeatures() { Unit: util.Ptr(model.UnitOfMeasurementTypeW), ScopeType: util.Ptr(model.ScopeTypeTypeActivePowerLimit), } - if lc, err := server.NewLoadControl(e.LocalEntity); err == nil { - limitId := lc.AddLimitDescription(newLimitDesc) - - newLimiData := []api.LoadControlLimitDataForID{ - { - Data: model.LoadControlLimitDataType{ - Value: model.NewScaledNumberType(0), - IsLimitChangeable: util.Ptr(true), - IsLimitActive: util.Ptr(false), - }, - Id: *limitId, + + lc, err := server.NewLoadControl(e.LocalEntity) + if err != nil { + return err + } + + limitId := lc.AddLimitDescription(newLimitDesc) + + newLimiData := []api.LoadControlLimitDataForID{ + { + Data: model.LoadControlLimitDataType{ + Value: model.NewScaledNumberType(0), + IsLimitChangeable: util.Ptr(true), + IsLimitActive: util.Ptr(false), }, - } - _ = lc.UpdateLimitDataForIds(newLimiData) + Id: *limitId, + }, + } + if err = lc.UpdateLimitDataForIds(newLimiData); err != nil { + return err } f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) @@ -265,55 +283,62 @@ func (e *LPP) AddFeatures() { f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) _ = f.AddWriteApprovalCallback(e.deviceConfigurationWriteCB) - if dcs, err := server.NewDeviceConfiguration(e.LocalEntity); err == nil { - dcs.AddKeyValueDescription( - model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - }, - ) + dcs, err := server.NewDeviceConfiguration(e.LocalEntity) + if err != nil { + return err + } - // only add if it doesn't exist yet - filter := model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - } - if data, err := dcs.GetKeyValueDescriptionsForFilter(filter); err == nil && len(data) == 0 { - dcs.AddKeyValueDescription( - model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), - }, - ) - } + dcs.AddKeyValueDescription( + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + }, + ) - value := &model.DeviceConfigurationKeyValueValueType{ - ScaledNumber: model.NewScaledNumberType(0), - } - _ = dcs.UpdateKeyValueDataForFilter( - model.DeviceConfigurationKeyValueDataType{ - Value: value, - IsValueChangeable: util.Ptr(true), - }, - nil, + // only add if it doesn't exist yet + filter := model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + } + if data, err := dcs.GetKeyValueDescriptionsForFilter(filter); err == nil && len(data) == 0 { + dcs.AddKeyValueDescription( model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeDuration), }, ) + } - value = &model.DeviceConfigurationKeyValueValueType{ - Duration: model.NewDurationType(0), - } - _ = dcs.UpdateKeyValueDataForFilter( - model.DeviceConfigurationKeyValueDataType{ - Value: value, - IsValueChangeable: util.Ptr(true), - }, - nil, - model.DeviceConfigurationKeyValueDescriptionDataType{ - KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), - }, - ) + value := &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(0), + } + if err := dcs.UpdateKeyValueDataForFilter( + model.DeviceConfigurationKeyValueDataType{ + Value: value, + IsValueChangeable: util.Ptr(true), + }, + nil, + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeProductionActivePowerLimit), + }, + ); err != nil { + return err + } + + value = &model.DeviceConfigurationKeyValueValueType{ + Duration: model.NewDurationType(0), + } + if err := dcs.UpdateKeyValueDataForFilter( + model.DeviceConfigurationKeyValueDataType{ + Value: value, + IsValueChangeable: util.Ptr(true), + }, + nil, + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypeFailsafeDurationMinimum), + }, + ); err != nil { + return err } f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) @@ -322,16 +347,23 @@ func (e *LPP) AddFeatures() { f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, false) - if ec, err := server.NewElectricalConnection(e.LocalEntity); err == nil { - electricalConnectionId := internal.GetElectricalConnectionId(e.LocalEntity) - parameterId := internal.GetParameterIdForACPowerTotalMeasurement(e.LocalEntity, electricalConnectionId, measurementId) - newCharData := model.ElectricalConnectionCharacteristicDataType{ - ElectricalConnectionId: util.Ptr(electricalConnectionId), - ParameterId: util.Ptr(parameterId), - CharacteristicContext: util.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), - CharacteristicType: util.Ptr(e.characteristicType()), - Unit: util.Ptr(model.UnitOfMeasurementTypeW), - } - _, _ = ec.AddCharacteristic(newCharData) + ec, err := server.NewElectricalConnection(e.LocalEntity) + if err != nil { + return err + } + + electricalConnectionId := internal.GetElectricalConnectionId(e.LocalEntity) + parameterId := internal.GetParameterIdForACPowerTotalMeasurement(e.LocalEntity, electricalConnectionId, measurementId) + newCharData := model.ElectricalConnectionCharacteristicDataType{ + ElectricalConnectionId: util.Ptr(electricalConnectionId), + ParameterId: util.Ptr(parameterId), + CharacteristicContext: util.Ptr(model.ElectricalConnectionCharacteristicContextTypeEntity), + CharacteristicType: util.Ptr(e.characteristicType()), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + } + if _, err := ec.AddCharacteristic(newCharData); err != nil { + return err } + + return nil } diff --git a/usecases/eg/lpc/usecase.go b/usecases/eg/lpc/usecase.go index 20081961..2a19f484 100644 --- a/usecases/eg/lpc/usecase.go +++ b/usecases/eg/lpc/usecase.go @@ -1,6 +1,7 @@ package lpc import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" usecase "github.com/enbility/eebus-go/usecases/usecase" @@ -65,6 +66,7 @@ func NewLPC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCa UseCaseSupportUpdate, validActorTypes, validEntityTypes, + false, ) uc := &LPC{ @@ -76,7 +78,7 @@ func NewLPC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCa return uc } -func (e *LPC) AddFeatures() { +func (e *LPC) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceDiagnosis, @@ -85,10 +87,17 @@ func (e *LPC) AddFeatures() { model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if f == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeDeviceDiagnosis)) + } f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + + return nil } diff --git a/usecases/eg/lpp/testhelper_test.go b/usecases/eg/lpp/testhelper_test.go index 0b083652..e7fda5d2 100644 --- a/usecases/eg/lpp/testhelper_test.go +++ b/usecases/eg/lpp/testhelper_test.go @@ -73,7 +73,7 @@ func (s *EgLPPSuite) BeforeTest(suiteName, testName string) { localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeCEM) s.sut = NewLPP(localEntity, s.Event) - s.sut.AddFeatures() + _ = s.sut.AddFeatures() s.sut.AddUseCase() s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T()) diff --git a/usecases/eg/lpp/usecase.go b/usecases/eg/lpp/usecase.go index 192c78ac..007c0be1 100644 --- a/usecases/eg/lpp/usecase.go +++ b/usecases/eg/lpp/usecase.go @@ -1,6 +1,7 @@ package lpp import ( + "errors" "github.com/enbility/eebus-go/api" ucapi "github.com/enbility/eebus-go/usecases/api" "github.com/enbility/eebus-go/usecases/usecase" @@ -62,7 +63,9 @@ func NewLPP(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCa eventCB, UseCaseSupportUpdate, validActorTypes, - validEntityTypes) + validEntityTypes, + false, + ) uc := &LPP{ UseCaseBase: usecase, @@ -73,7 +76,7 @@ func NewLPP(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCa return uc } -func (e *LPP) AddFeatures() { +func (e *LPP) AddFeatures() error { // client features var clientFeatures = []model.FeatureTypeType{ model.FeatureTypeTypeDeviceDiagnosis, @@ -82,12 +85,19 @@ func (e *LPP) AddFeatures() { model.FeatureTypeTypeElectricalConnection, } for _, feature := range clientFeatures { - _ = e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient) + if f := e.LocalEntity.GetOrAddFeature(feature, model.RoleTypeClient); f == nil { + return errors.New("could not add feature: " + string(feature)) + } } // server features f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer) + if f == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeDeviceDiagnosis)) + } f.AddFunctionType(model.FunctionTypeDeviceDiagnosisHeartbeatData, true, false) + + return nil } func (e *LPP) UpdateUseCaseAvailability(available bool) { diff --git a/usecases/gcp/mgcp/config.go b/usecases/gcp/mgcp/config.go new file mode 100644 index 00000000..a542b51f --- /dev/null +++ b/usecases/gcp/mgcp/config.go @@ -0,0 +1,64 @@ +package mgcp + +import "github.com/enbility/spine-go/model" + +// MonitorPvFeedInPowerLimitationFactorConfig is the configuration for the power limitation factor monitoring use case in the MGCP +// If this config is passed via NewMGCP, the MGCP use case will support power limitation factor monitoring +type MonitorPvFeedInPowerLimitationFactorConfig struct { +} + +// MonitorPowerConfig is the configuration for the power monitoring use case in the MGCP +// This config is required by the MGCP use case and must be used in NewMGCP +type MonitorPowerConfig struct { + ValueSource *model.MeasurementValueSourceType // The source of the values from the acPowerTotal (not optional) + ValueConstraints *model.MeasurementConstraintsDataType // The constraints for the acPowerTotal (optional, can be nil) +} + +// MonitorEnergyConfig is the configuration for the energy monitoring use case in the MGCP +// This config is required by the MGCP use case and must be used in NewMGCP +type MonitorEnergyConfig struct { + ValueSourceProduction *model.MeasurementValueSourceType // The source of the production values (not optional) + ValueSourceConsumption *model.MeasurementValueSourceType // The source of the consumption values (not optional) + + ValueConstraintsProduction *model.MeasurementConstraintsDataType // The constraints for the production values (optional, can be nil) + ValueConstraintsConsumption *model.MeasurementConstraintsDataType // The constraints for the consumption values (optional, can be nil) +} + +// MonitorCurrentConfig is the configuration for the current monitoring use case in the MGCP +// If this config is passed via NewMGCP, the MGCP use case will support current monitoring +type MonitorCurrentConfig struct { + ValueSourcePhaseA *model.MeasurementValueSourceType // The source of the values for phase A (not optional) + ValueSourcePhaseB *model.MeasurementValueSourceType // The source of the values for phase B (not optional) + ValueSourcePhaseC *model.MeasurementValueSourceType // The source of the values for phase C (not optional) + + ValueConstraintsPhaseA *model.MeasurementConstraintsDataType // The constraints for the current for phase A (optional, can be nil) + ValueConstraintsPhaseB *model.MeasurementConstraintsDataType // The constraints for the current for phase B (optional, can be nil) + ValueConstraintsPhaseC *model.MeasurementConstraintsDataType // The constraints for the current for phase C (optional, can be nil) +} + +// MonitorVoltageConfig is the configuration for the voltage monitoring use case in the MGCP +// If this config is passed via NewMGCP, the MGCP use case will support voltage monitoring +type MonitorVoltageConfig struct { + // If the value source is not nil, the use case will support the voltage monitoring for the respective phase + // If the value source is nil, the use case will not support the voltage monitoring for the respective phase + ValueSourcePhaseA *model.MeasurementValueSourceType // The source of the values for phase A (optional, can be nil) + ValueSourcePhaseB *model.MeasurementValueSourceType // The source of the values for phase B (optional, can be nil) + ValueSourcePhaseC *model.MeasurementValueSourceType // The source of the values for phase C (optional, can be nil) + ValueSourcePhaseAToB *model.MeasurementValueSourceType // The source of the values for phase A to B (optional, can be nil) + ValueSourcePhaseBToC *model.MeasurementValueSourceType // The source of the values for phase B to C (optional, can be nil) + ValueSourcePhaseCToA *model.MeasurementValueSourceType // The source of the values for phase C to A (optional, can be nil) + + ValueConstraintsPhaseA *model.MeasurementConstraintsDataType // The constraints for the voltage for phase A (optional, can be nil) (needs ValueSourcePhaseA to be set) + ValueConstraintsPhaseB *model.MeasurementConstraintsDataType // The constraints for the voltage for phase B (optional, can be nil) (needs ValueSourcePhaseB to be set) + ValueConstraintsPhaseC *model.MeasurementConstraintsDataType // The constraints for the voltage for phase C (optional, can be nil) (needs ValueSourcePhaseC to be set) + ValueConstraintsPhaseAToB *model.MeasurementConstraintsDataType // The constraints for the voltage for phase A to B (optional, can be nil) (needs ValueSourcePhaseAToB to be set) + ValueConstraintsPhaseBToC *model.MeasurementConstraintsDataType // The constraints for the voltage for phase B to C (optional, can be nil) (needs ValueSourcePhaseBToC to be set) + ValueConstraintsPhaseCToA *model.MeasurementConstraintsDataType // The constraints for the voltage for phase C to A (optional, can be nil) (needs ValueSourcePhaseCToA to be set) +} + +// MonitorFrequencyConfig is the configuration for the frequency monitoring use case in the MGCP +// If this config is passed via NewMGCP, the MGCP use case will support frequency monitoring +type MonitorFrequencyConfig struct { + ValueSource *model.MeasurementValueSourceType // The source of the values (not optional) + ValueConstraints *model.MeasurementConstraintsDataType // The constraints for the frequency values (optional can be nil) +} diff --git a/usecases/gcp/mgcp/events.go b/usecases/gcp/mgcp/events.go new file mode 100644 index 00000000..b792131e --- /dev/null +++ b/usecases/gcp/mgcp/events.go @@ -0,0 +1,10 @@ +package mgcp + +import ( + spineapi "github.com/enbility/spine-go/api" +) + +// handle SPINE events +func (m *MGCP) HandleEvent(payload spineapi.EventPayload) { + // No event handling for MGCP +} diff --git a/usecases/gcp/mgcp/public.go b/usecases/gcp/mgcp/public.go new file mode 100644 index 00000000..75ceb992 --- /dev/null +++ b/usecases/gcp/mgcp/public.go @@ -0,0 +1,496 @@ +package mgcp + +import ( + "errors" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + usecaseapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/model" + "time" +) + +// -------- Getters -------- // + +// Scenario 1 + +// get the current power limitation factor +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) PowerLimitationFactor() (float64, error) { + _configurations, err := server.NewDeviceConfiguration(m.LocalEntity) + if err != nil { + return 0, err + } + + value, err := _configurations.GetKeyValueDataForKeyId(*m.pvFeedInLimitationFactor) + + if err != nil { + return 0, err + } + + return value.Value.ScaledNumber.GetValue(), nil +} + +// Scenario 2 + +// get the momentary active power consumption or production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) PowerTotal() (float64, error) { + return m.getMeasurementDataForId(m.acPowerTotal) +} + +// Scenario 3 + +// get the total produced energy +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) EnergyFeedIn() (float64, error) { + return m.getMeasurementDataForId(m.gridFeedIn) +} + +// Scenario 4 + +// get the total consumed energy +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) EnergyConsumed() (float64, error) { + return m.getMeasurementDataForId(m.gridConsumption) +} + +// Scenario 5 + +// get the momentary phase specific current consumption or production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) CurrentPerPhase() ([]float64, error) { + acCurrent := make([]float64, 0) + + for _, id := range m.acCurrent { + if id != nil { + value, err := m.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + acCurrent = append(acCurrent, value) + } + } + + if len(acCurrent) == 0 { + return nil, api.ErrDataNotAvailable + } + + return acCurrent, nil +} + +// Scenario 6 + +// get the momentary phase specific voltage consumption or production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) VoltagePerPhase() ([]float64, error) { + acVoltage := make([]float64, 0) + + for _, id := range m.acVoltage { + if id != nil { + value, err := m.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + acVoltage = append(acVoltage, value) + } + } + + if len(acVoltage) == 0 { + return nil, api.ErrDataNotAvailable + } + + return acVoltage, nil +} + +// Scenario 7 + +// get frequency +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) Frequency() (float64, error) { + return m.getMeasurementDataForId(m.acFrequency) +} + +// -------- Setters -------- // + +// Update the data + +// use MPC.Update to update the data of the MGCP Usecase +// use it like this: +// +// mgcp.Update( +// mgcp.MeasuredAcPowerTotal(1000), +// mgcp.MeasuredAcPowerPhaseA(500), +// ... +// ) +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (m *MGCP) Update(updateValueType ...usecaseapi.UpdateData) error { + measurements := make([]api.MeasurementDataForID, 0) + configurations := make([]model.DeviceConfigurationKeyValueDataType, 0) + + for _, update := range updateValueType { + switch update.(type) { + case usecaseapi.UpdateMeasurementData: + measurements = append(measurements, update.(usecaseapi.UpdateMeasurementData).MeasurementData()) + case usecaseapi.UpdateConfigurationData: + configurations = append(configurations, update.(usecaseapi.UpdateConfigurationData).ConfigurationData()) + default: + return errors.New("unsupported updateValueType") + } + } + + if len(measurements) > 0 { + _measurements, err := server.NewMeasurement(m.LocalEntity) + if err != nil { + return err + } + + err = _measurements.UpdateDataForIds(measurements) + if err != nil { + return err + } + } + + if len(configurations) == 1 { + _configurations, err := server.NewDeviceConfiguration(m.LocalEntity) + if err != nil { + return err + } + + err = _configurations.UpdateKeyValueDataForKeyId(configurations[0], nil, *configurations[0].KeyId) + if err != nil { + return err + } + } else { + if len(configurations) > 1 { + return errors.New("only one PowerLimitationFactor update is supported at a time") + } + } + + return nil +} + +// Scenario 1 + +// Use MGCP.UpdateDataPowerLimitationFactor in MGCP.Update to set the current power limitation factor +func (m *MGCP) UpdateDataPowerLimitationFactor(pvFeedInLimitationFactor float64) usecaseapi.UpdateData { + if m.pvFeedInLimitationFactor == nil { + return &UpdateData{ + supported: false, + notSupportedError: errors.New("id is nil: UpdateDataPowerLimitationFactor, please check the mgcp configuration"), + } + } + + return &UpdateConfigurationData{ + UpdateData: UpdateData{ + supported: true, + }, + configurationData: model.DeviceConfigurationKeyValueDataType{ + KeyId: m.pvFeedInLimitationFactor, + Value: &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(pvFeedInLimitationFactor), + }, + }, + } +} + +// Scenario 2 + +// Use MGCP.UpdateDataPowerTotal in MGCP.Update to set the current total power +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataPowerTotal( + acPowerTotal float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataPowerTotal", + m.acPowerTotal, + m.powerConfig.ValueSource, + acPowerTotal, + timestamp, + valueState, + nil, + nil, + ) +} + +// Scenario 3 + +// Use MGCP.UpdateDataEnergyFeedIn in MGCP.Update to set the total feed in energy +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +// The evaluationPeriodStart and evaluationPeriodEnd are optional and can be nil (both must be set to be used) +func (m *MGCP) UpdateDataEnergyFeedIn( + energy float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationPeriodStart *time.Time, + evaluationPeriodEnd *time.Time, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataEnergyFeedIn", + m.gridFeedIn, + m.energyConfig.ValueSourceProduction, + energy, + timestamp, + valueState, + evaluationPeriodStart, + evaluationPeriodEnd, + ) +} + +// Scenario 4 + +// Use MGCP.UpdateDataEnergyConsumed in MGCP.Update to set the total feed in energy +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +// The evaluationPeriodStart and evaluationPeriodEnd are optional and can be nil (both must be set to be used) +func (m *MGCP) UpdateDataEnergyConsumed( + energy float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationPeriodStart *time.Time, + evaluationPeriodEnd *time.Time, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataEnergyConsumed", + m.gridConsumption, + m.energyConfig.ValueSourceConsumption, + energy, + timestamp, + valueState, + evaluationPeriodStart, + evaluationPeriodEnd, + ) +} + +// Scenario 5 + +// Use MGCP.UpdateDataCurrentPhaseA in MGCP.Update to set the current of phase A +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataCurrentPhaseA( + current float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataCurrentPhaseA", + m.acCurrent[0], + m.currentConfig.ValueSourcePhaseA, + current, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataCurrentPhaseB in MGCP.Update to set the current of phase B +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataCurrentPhaseB( + current float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataCurrentPhaseB", + m.acCurrent[1], + m.currentConfig.ValueSourcePhaseB, + current, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataCurrentPhaseC in MGCP.Update to set the current of phase C +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataCurrentPhaseC( + current float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataCurrentPhaseC", + m.acCurrent[2], + m.currentConfig.ValueSourcePhaseC, + current, + timestamp, + valueState, + nil, + nil, + ) +} + +// Scenario 6 + +// Use MGCP.UpdateDataVoltagePhaseA in MGCP.Update to set the voltage of phase A +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataVoltagePhaseA( + voltage float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataVoltagePhaseA", + m.acVoltage[0], + m.voltageConfig.ValueSourcePhaseA, + voltage, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataVoltagePhaseB in MGCP.Update to set the voltage of phase B +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataVoltagePhaseB( + voltage float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataVoltagePhaseB", + m.acVoltage[1], + m.voltageConfig.ValueSourcePhaseB, + voltage, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataVoltagePhaseC in MGCP.Update to set the voltage of phase C +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataVoltagePhaseC( + voltage float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataVoltagePhaseC", + m.acVoltage[2], + m.voltageConfig.ValueSourcePhaseC, + voltage, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataVoltagePhaseAToB in MGCP.Update to set the voltage between phase A and B +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataVoltagePhaseAToB( + voltage float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataVoltagePhaseAToB", + m.acVoltage[3], + m.voltageConfig.ValueSourcePhaseAToB, + voltage, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataVoltagePhaseBToC in MGCP.Update to set the voltage between phase B and C +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataVoltagePhaseBToC( + voltage float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataVoltagePhaseBToC", + m.acVoltage[4], + m.voltageConfig.ValueSourcePhaseBToC, + voltage, + timestamp, + valueState, + nil, + nil, + ) +} + +// Use MGCP.UpdateDataVoltagePhaseCToA in MGCP.Update to set the voltage between phase C and A +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataVoltagePhaseCToA( + voltage float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataVoltagePhaseCToA", + m.acVoltage[5], + m.voltageConfig.ValueSourcePhaseCToA, + voltage, + timestamp, + valueState, + nil, + nil, + ) +} + +// Scenario 7 + +// Use MGCP.UpdateDataFrequency in MGCP.Update to set the frequency +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (m *MGCP) UpdateDataFrequency( + frequency float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateData { + return updateMeasurementData( + "UpdateDataFrequency", + m.acFrequency, + m.frequencyConfig.ValueSource, + frequency, + timestamp, + valueState, + nil, + nil, + ) +} diff --git a/usecases/gcp/mgcp/public_test.go b/usecases/gcp/mgcp/public_test.go new file mode 100644 index 00000000..bdc37645 --- /dev/null +++ b/usecases/gcp/mgcp/public_test.go @@ -0,0 +1,196 @@ +package mgcp + +import ( + "github.com/enbility/eebus-go/features/server" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" +) + +func (s *GcpMpcgSuite) Test_PowerLimitationFactor() { + err := s.sut.Update( + s.sut.UpdateDataPowerLimitationFactor(0.5), + ) + + assert.Nil(s.T(), err) + + powerLimitationFactor, err := s.sut.PowerLimitationFactor() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 0.5, powerLimitationFactor) + + // Test client getter + keyname := model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor + + deviceConfiguration, err := server.NewDeviceConfiguration(s.localEntity) + assert.Nil(s.T(), err) + + filter := model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: &keyname, + } + + _, err = deviceConfiguration.GetKeyValueDescriptionsForFilter(filter) + assert.Nil(s.T(), err) + + filter.ValueType = util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber) + data, err := deviceConfiguration.GetKeyValueDataForFilter(filter) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), data) + + assert.Equal(s.T(), 0.5, data.Value.ScaledNumber.GetValue()) +} + +func (s *GcpMpcgSuite) Test_PowerTotal() { + err := s.sut.Update( + s.sut.UpdateDataPowerTotal(5.0, nil, nil), + ) + + assert.Nil(s.T(), err) + + totalPower, err := s.sut.PowerTotal() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, totalPower) + + // Test client getter + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + } + + data, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, nil) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, data[0]) +} + +func (s *GcpMpcgSuite) Test_EnergyConsumed() { + err := s.sut.Update( + s.sut.UpdateDataEnergyConsumed(5.0, nil, nil, nil, nil), + ) + + assert.Nil(s.T(), err) + + energyConsumed, err := s.sut.EnergyConsumed() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, energyConsumed) + + // Test client getter + measurement, err := server.NewMeasurement(s.localEntity) + assert.Nil(s.T(), err) + + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeGridConsumption), + } + + result, err := measurement.GetDataForFilter(filter) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), 5.0, result[0].Value.GetValue()) +} + +func (s *GcpMpcgSuite) Test_EnergyFeedIn() { + err := s.sut.Update( + s.sut.UpdateDataEnergyFeedIn(6.0, nil, nil, nil, nil), + ) + + assert.Nil(s.T(), err) + + energyProduced, err := s.sut.EnergyFeedIn() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 6.0, energyProduced) + + // Test client getter + measurement, err := server.NewMeasurement(s.localEntity) + assert.Nil(s.T(), err) + + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeGridFeedIn), + } + + result, err := measurement.GetDataForFilter(filter) + assert.Nil(s.T(), err) + + assert.Equal(s.T(), 6.0, result[0].Value.GetValue()) +} + +func (s *GcpMpcgSuite) Test_CurrentPerPhase() { + err := s.sut.Update( + s.sut.UpdateDataCurrentPhaseA(5.0, nil, nil), + s.sut.UpdateDataCurrentPhaseB(6.0, nil, nil), + s.sut.UpdateDataCurrentPhaseC(7.0, nil, nil), + ) + + assert.Nil(s.T(), err) + + currentPerPhases, err := s.sut.CurrentPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.0, 6.0, 7.0}, currentPerPhases) + + // Test client getter + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + } + + data, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, nil) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.0, 6.0, 7.0}, data) +} + +func (s *GcpMpcgSuite) Test_VoltagePerPhase() { + err := s.sut.Update( + s.sut.UpdateDataVoltagePhaseA(5.0, nil, nil), + s.sut.UpdateDataVoltagePhaseB(6.0, nil, nil), + s.sut.UpdateDataVoltagePhaseC(7.0, nil, nil), + s.sut.UpdateDataVoltagePhaseAToB(8.0, nil, nil), + s.sut.UpdateDataVoltagePhaseBToC(9.0, nil, nil), + s.sut.UpdateDataVoltagePhaseCToA(10.0, nil, nil), + ) + + assert.Nil(s.T(), err) + + voltagePerPhases, err := s.sut.VoltagePerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, voltagePerPhases) + + // Test client getter + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + } + + data, err := s.measurementPhaseSpecificDataForFilter(filter, "", nil) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, data) +} + +func (s *GcpMpcgSuite) Test_Frequency() { + err := s.sut.Update( + s.sut.UpdateDataFrequency(50.0, nil, nil), + ) + + assert.Nil(s.T(), err) + + frequency, err := s.sut.Frequency() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 50.0, frequency) + + // Test client getter + measurement, err := server.NewMeasurement(s.localEntity) + assert.Nil(s.T(), err) + + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + } + + data, err := measurement.GetDataForFilter(filter) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 50.0, data[0].Value.GetValue()) +} diff --git a/usecases/gcp/mgcp/testhelper_test.go b/usecases/gcp/mgcp/testhelper_test.go new file mode 100644 index 00000000..36439a4d --- /dev/null +++ b/usecases/gcp/mgcp/testhelper_test.go @@ -0,0 +1,164 @@ +package mgcp + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + "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" + spineapi "github.com/enbility/spine-go/api" + spinemocks "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "slices" + "testing" + "time" +) + +const remoteSki string = "testremoteski" + +func TestMuMPCPublicSuite(t *testing.T) { + suite.Run(t, new(GcpMpcgSuite)) +} + +type GcpMpcgSuite struct { + suite.Suite + + sut *MGCP + + service api.ServiceInterface + + remoteDevice spineapi.DeviceRemoteInterface + mockRemoteEntity *spinemocks.EntityRemoteInterface + monitoredEntity spineapi.EntityRemoteInterface + loadControlFeature, + deviceDiagnosisFeature, + deviceConfigurationFeature spineapi.FeatureLocalInterface + + eventCalled bool + localEntity spineapi.EntityLocalInterface +} + +func (s *GcpMpcgSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { + s.eventCalled = true +} + +func (s *GcpMpcgSuite) BeforeTest(suiteName, testName string) { + s.eventCalled = false + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := api.NewConfiguration( + "test", "test", "test", "test", + []shipapi.DeviceCategoryType{shipapi.DeviceCategoryTypeGridConnectionHub}, + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeInverter}, + 9999, cert, time.Second*4, nil, nil) + + serviceHandler := mocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + s.localEntity = s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) + s.sut, _ = NewMGCP( + s.localEntity, + s.Event, + &MonitorPvFeedInPowerLimitationFactorConfig{}, + &MonitorPowerConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeCalculatedValue), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(100), + ValueStepSize: model.NewScaledNumberType(1), + }), + }, + &MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeCalculatedValue), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorCurrentConfig{ + ValueSourcePhaseA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorVoltageConfig{ + ValueSourcePhaseA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseAToB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseBToC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseCToA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorFrequencyConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + ) + + _ = s.sut.AddFeatures() + s.sut.AddUseCase() +} + +func (s *GcpMpcgSuite) measurementPhaseSpecificDataForFilter( + measurementFilter model.MeasurementDescriptionDataType, + energyDirection model.EnergyDirectionType, + validPhaseNameTypes []model.ElectricalConnectionPhaseNameType, +) ([]float64, error) { + measurements, err := server.NewMeasurement(s.sut.LocalEntity) + if err != nil { + return nil, err + } + + electricalConnection, err := server.NewElectricalConnection(s.sut.LocalEntity) + if err != nil { + return nil, err + } + + data, err := measurements.GetDataForFilter(measurementFilter) + if err != nil || len(data) == 0 { + return nil, api.ErrDataNotAvailable + } + + var result []float64 + + for _, item := range data { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + if validPhaseNameTypes != nil { + filter := model.ElectricalConnectionParameterDescriptionDataType{ + MeasurementId: item.MeasurementId, + } + param, err := electricalConnection.GetParameterDescriptionsForFilter(filter) + if err != nil || len(param) == 0 || + param[0].AcMeasuredPhases == nil || + !slices.Contains(validPhaseNameTypes, *param[0].AcMeasuredPhases) { + continue + } + } + + if energyDirection != "" { + filter := model.ElectricalConnectionParameterDescriptionDataType{ + MeasurementId: item.MeasurementId, + } + desc, err := electricalConnection.GetDescriptionForParameterDescriptionFilter(filter) + if err != nil || desc == 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 +} diff --git a/usecases/gcp/mgcp/types.go b/usecases/gcp/mgcp/types.go new file mode 100644 index 00000000..31a023cf --- /dev/null +++ b/usecases/gcp/mgcp/types.go @@ -0,0 +1,10 @@ +package mgcp + +import "github.com/enbility/eebus-go/api" + +const ( + // Update of the list of remote entites supporting the Use Case + // + // Use `RemoteEntities` to get the current data + UseCaseSupportUpdate api.EventType = "gcp-mgcp-UseCaseSupportUpdate" +) diff --git a/usecases/gcp/mgcp/update_helper.go b/usecases/gcp/mgcp/update_helper.go new file mode 100644 index 00000000..8f6a77af --- /dev/null +++ b/usecases/gcp/mgcp/update_helper.go @@ -0,0 +1,92 @@ +package mgcp + +import ( + "fmt" + "github.com/enbility/eebus-go/api" + usecaseapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "time" +) + +type UpdateData struct { + supported bool + notSupportedError error +} + +type UpdateMeasurementData struct { + UpdateData + + measurementData api.MeasurementDataForID +} + +type UpdateConfigurationData struct { + UpdateData + + configurationData model.DeviceConfigurationKeyValueDataType +} + +func (u *UpdateData) Supported() bool { + return u.supported +} + +func (u *UpdateData) NotSupportedError() error { + return u.notSupportedError +} + +func (u *UpdateMeasurementData) MeasurementData() api.MeasurementDataForID { + return u.measurementData +} + +func (u UpdateConfigurationData) ConfigurationData() model.DeviceConfigurationKeyValueDataType { + return u.configurationData +} + +func updateMeasurementData( + errorName string, + id *model.MeasurementIdType, + valueSource *model.MeasurementValueSourceType, + value float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationStart *time.Time, + evaluationEnd *time.Time, +) usecaseapi.UpdateData { + if id == nil { + return &UpdateData{ + supported: false, + notSupportedError: fmt.Errorf("id is nil: %s, please check the mgcp configuration", errorName), + } + } + + updateValueType := UpdateMeasurementData{ + UpdateData: UpdateData{ + supported: true, + }, + measurementData: api.MeasurementDataForID{ + Id: *id, + Data: model.MeasurementDataType{ + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: valueSource, + Value: model.NewScaledNumberType(value), + }, + }, + } + + if timestamp != nil { + updateValueType.measurementData.Data.Timestamp = model.NewAbsoluteOrRelativeTimeTypeFromTime(*timestamp) + } + + if valueState != nil { + updateValueType.measurementData.Data.ValueState = valueState + } + + if evaluationStart != nil && evaluationEnd != nil { + updateValueType.measurementData.Data.EvaluationPeriod = &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeTypeFromTime(*evaluationStart), + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromTime(*evaluationEnd), + } + } + + return &updateValueType +} diff --git a/usecases/gcp/mgcp/usecase.go b/usecases/gcp/mgcp/usecase.go new file mode 100644 index 00000000..9e1f0740 --- /dev/null +++ b/usecases/gcp/mgcp/usecase.go @@ -0,0 +1,574 @@ +package mgcp + +import ( + "errors" + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + "github.com/enbility/eebus-go/usecases/usecase" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" +) + +type MGCP struct { + *usecase.UseCaseBase + + limitationConfig *MonitorPvFeedInPowerLimitationFactorConfig + powerConfig *MonitorPowerConfig + energyConfig *MonitorEnergyConfig + currentConfig *MonitorCurrentConfig + voltageConfig *MonitorVoltageConfig + frequencyConfig *MonitorFrequencyConfig + + pvFeedInLimitationFactor *model.DeviceConfigurationKeyIdType + acPowerTotal *model.MeasurementIdType + gridFeedIn *model.MeasurementIdType + gridConsumption *model.MeasurementIdType + acCurrent [3]*model.MeasurementIdType + acVoltage [6]*model.MeasurementIdType + acFrequency *model.MeasurementIdType +} + +// creates a new MGCP usecase instance for a MonitoredUnit entity +// +// parameters: +// - localEntity: the local entity for which the use case is created +// - eventCB: the callback function to be called when an event is triggered +// - monitorFeedInLimitationConfig: (optional) configuration parameters for MGCP scenario 1 +// - monitorPowerConfig: (required) configuration parameters for MGCP scenario 2 +// - monitorEnergyConfig: (required) configuration parameters for MGCP scenario 3 +// - monitorCurrentConfig: (optional) configuration parameters for MGCP scenario 4 +// - monitorVoltageConfig: (optional) configuration parameters for MGCP scenario 5 +// - monitorFrequencyConfig: (optional) configuration parameters for MGCP scenario 6 +// +// possible errors: +// - configuration error +// - and more... + +func NewMGCP( + localEntity spineapi.EntityLocalInterface, + eventCB api.EntityEventCallback, + monitorFeedInLimitationConfig *MonitorPvFeedInPowerLimitationFactorConfig, + monitorPowerConfig *MonitorPowerConfig, + monitorEnergyConfig *MonitorEnergyConfig, + monitorCurrentConfig *MonitorCurrentConfig, + monitorVoltageConfig *MonitorVoltageConfig, + monitorFrequencyConfig *MonitorFrequencyConfig, +) (*MGCP, error) { + if monitorPowerConfig == nil { + return nil, errors.New("monitorPowerConfig must be set") + } + + if monitorEnergyConfig == nil { + return nil, errors.New("monitorEnergyConfig must be set") + } + + validActorTypes := []model.UseCaseActorType{model.UseCaseActorTypeGridConnectionPoint} + useCaseScenarios := make([]api.UseCaseScenario, 0) + + if monitorFeedInLimitationConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(1), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{model.FeatureTypeTypeDeviceConfiguration}, + }) + } + + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(2), + Mandatory: true, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeElectricalConnection, + }, + }) + + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(3), + Mandatory: true, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeElectricalConnection, + }, + }) + + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(4), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeElectricalConnection, + }, + }) + + if monitorCurrentConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(5), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeElectricalConnection, + }, + }) + } + + if monitorVoltageConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(6), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeElectricalConnection, + }, + }) + } + + if monitorFrequencyConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(7), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeMeasurement, + model.FeatureTypeTypeElectricalConnection, + }, + }) + } + + usecase := usecase.NewUseCaseBase( + localEntity, + model.UseCaseActorTypeMonitoringAppliance, + model.UseCaseNameTypeMonitoringOfGridConnectionPoint, + "1.0.0", + "release", + useCaseScenarios, + eventCB, + UseCaseSupportUpdate, + validActorTypes, + nil, + true, + ) + + uc := &MGCP{ + UseCaseBase: usecase, + + limitationConfig: monitorFeedInLimitationConfig, + powerConfig: monitorPowerConfig, + energyConfig: monitorEnergyConfig, + currentConfig: monitorCurrentConfig, + voltageConfig: monitorVoltageConfig, + frequencyConfig: monitorFrequencyConfig, + } + + _ = localEntity.Device().Events().Subscribe(uc) + + return uc, nil +} + +func (m *MGCP) AddFeatures() error { + // server features + deviceConfigurationFeature := m.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + measurementFeature := m.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + electricalConnectionFeature := m.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + + deviceConfigurationFeature.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) + deviceConfigurationFeature.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, false) + + measurementFeature.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, true, false) + measurementFeature.AddFunctionType(model.FunctionTypeMeasurementConstraintsListData, true, false) + measurementFeature.AddFunctionType(model.FunctionTypeMeasurementListData, true, false) + + electricalConnectionFeature.AddFunctionType(model.FunctionTypeElectricalConnectionDescriptionListData, true, false) + electricalConnectionFeature.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + + configuration, err := server.NewDeviceConfiguration(m.LocalEntity) + if err != nil { + return err + } + + err = m.configurePvFeedInLimitationFactor(configuration) + if err != nil { + return err + } + + measurement, err := server.NewMeasurement(m.LocalEntity) + if err != nil { + return err + } + + electricalConnection, err := server.NewElectricalConnection(m.LocalEntity) + if err != nil { + return err + } + + electricalConnectionId, err := electricalConnection.GetOrAddIdForDescription(model.ElectricalConnectionDescriptionDataType{ + PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + }) + if err != nil { + return err + } + + constraints := make([]model.MeasurementConstraintsDataType, 0) + + configMethods := []func( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, + ) error{ + m.configureMonitorPower, + m.configureGridFeedIn, + m.configureGridConsumption, + m.configureMonitorCurrent, + m.configureMonitorVoltage, + m.configureMonitorFrequency, + } + + for _, configMethod := range configMethods { + if err := configMethod(measurement, electricalConnection, electricalConnectionId, &constraints); err != nil { + return err + } + } + + if len(constraints) > 0 { + measurementFeature.UpdateData( + model.FunctionTypeMeasurementConstraintsListData, + &model.MeasurementConstraintsListDataType{ + MeasurementConstraintsData: constraints, + }, nil, nil, + ) + } + + return nil +} + +func (m *MGCP) configurePvFeedInLimitationFactor( + configurations api.DeviceConfigurationServerInterface, +) error { + if m.limitationConfig == nil { + return nil + } + + m.pvFeedInLimitationFactor = configurations.AddKeyValueDescription(model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: util.Ptr(model.UnitOfMeasurementTypepct), + }) + + if m.pvFeedInLimitationFactor == nil { + return errors.New("failed to add key description") + } + + return nil +} + +func (m *MGCP) configureMonitorPower( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if m.powerConfig == nil { + return errors.New("mgcp power config must be configured") + } + + if m.powerConfig.ValueSource == nil { + return errors.New("mgcp power config value source must be configured") + } + + m.acPowerTotal = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + }) + + parameterDescription := electricalConnection.AddParameterDescription(model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: m.acPowerTotal, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }) + + if parameterDescription == nil { + return errors.New("failed to add parameter description") + } + + if m.powerConfig.ValueConstraints != nil { + m.powerConfig.ValueConstraints.MeasurementId = m.acPowerTotal + *measurementsConstraintData = append(*measurementsConstraintData, *m.powerConfig.ValueConstraints) + } + + return nil +} + +func (m *MGCP) configureGridFeedIn( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if m.energyConfig == nil { + return errors.New("mgcp energy config must be configured") + } + + if m.energyConfig.ValueSourceProduction == nil { + return errors.New("mgcp energy config production value source must be configured") + } + + m.gridFeedIn = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeGridFeedIn), + }) + + parameterDescription := electricalConnection.AddParameterDescription(model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: m.gridFeedIn, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + }) + if parameterDescription == nil { + return errors.New("failed to add parameter description") + } + + if m.energyConfig.ValueConstraintsProduction != nil { + m.energyConfig.ValueConstraintsProduction.MeasurementId = m.gridFeedIn + *measurementsConstraintData = append(*measurementsConstraintData, *m.energyConfig.ValueConstraintsProduction) + } + + return nil +} + +func (m *MGCP) configureGridConsumption( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if m.energyConfig == nil { + return errors.New("mgcp energy config must be configured") + } + + if m.energyConfig.ValueSourceConsumption == nil { + return errors.New("value source consumption must be set") + } + + m.gridConsumption = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeGridConsumption), + }) + + parameterDescription := electricalConnection.AddParameterDescription(model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: m.gridConsumption, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + }) + if parameterDescription == nil { + return errors.New("failed to add parameter description") + } + + if m.energyConfig.ValueConstraintsConsumption != nil { + m.energyConfig.ValueConstraintsConsumption.MeasurementId = m.gridConsumption + *measurementsConstraintData = append(*measurementsConstraintData, *m.energyConfig.ValueConstraintsConsumption) + } + + return nil +} + +func (m *MGCP) configureMonitorCurrent( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if m.currentConfig == nil { + return nil + } + + valueSourcesOfPhases := []*model.MeasurementValueSourceType{ + m.currentConfig.ValueSourcePhaseA, + m.currentConfig.ValueSourcePhaseB, + m.currentConfig.ValueSourcePhaseC, + } + + valueConstraints := []*model.MeasurementConstraintsDataType{ + m.currentConfig.ValueConstraintsPhaseA, + m.currentConfig.ValueConstraintsPhaseB, + m.currentConfig.ValueConstraintsPhaseC, + } + + electricalConnectedPhases := []model.ElectricalConnectionPhaseNameType{ + model.ElectricalConnectionPhaseNameTypeA, + model.ElectricalConnectionPhaseNameTypeB, + model.ElectricalConnectionPhaseNameTypeC, + } + + for i := 0; i < len(m.acCurrent); i++ { + if valueSourcesOfPhases[i] == nil { + continue + } + + m.acCurrent[i] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }) + + parameterDescription := electricalConnection.AddParameterDescription(model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: m.acCurrent[i], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(electricalConnectedPhases[i]), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }) + + if parameterDescription == nil { + return errors.New("failed to add parameter description") + } + + if valueConstraints[i] != nil { + valueConstraints[i].MeasurementId = m.acCurrent[i] + *measurementsConstraintData = append(*measurementsConstraintData, *valueConstraints[i]) + } + } + + return nil +} + +func (m *MGCP) configureMonitorVoltage( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if m.voltageConfig == nil { + return nil + } + + electricalConnectionPhaseToPhase := [][]model.ElectricalConnectionPhaseNameType{ + {model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeNeutral}, + {model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeNeutral}, + {model.ElectricalConnectionPhaseNameTypeC, model.ElectricalConnectionPhaseNameTypeNeutral}, + {model.ElectricalConnectionPhaseNameTypeA, model.ElectricalConnectionPhaseNameTypeB}, + {model.ElectricalConnectionPhaseNameTypeB, model.ElectricalConnectionPhaseNameTypeC}, + {model.ElectricalConnectionPhaseNameTypeC, model.ElectricalConnectionPhaseNameTypeA}, + } + + valueSourcesOfPhases := []*model.MeasurementValueSourceType{ + m.voltageConfig.ValueSourcePhaseA, + m.voltageConfig.ValueSourcePhaseB, + m.voltageConfig.ValueSourcePhaseC, + m.voltageConfig.ValueSourcePhaseAToB, + m.voltageConfig.ValueSourcePhaseBToC, + m.voltageConfig.ValueSourcePhaseCToA, + } + + valueConstraintsOfPhases := []*model.MeasurementConstraintsDataType{ + m.voltageConfig.ValueConstraintsPhaseA, + m.voltageConfig.ValueConstraintsPhaseB, + m.voltageConfig.ValueConstraintsPhaseC, + m.voltageConfig.ValueConstraintsPhaseAToB, + m.voltageConfig.ValueConstraintsPhaseBToC, + m.voltageConfig.ValueConstraintsPhaseCToA, + } + + for i := 0; i < len(m.acVoltage); i++ { + if valueSourcesOfPhases[i] == nil { + continue + } + + m.acVoltage[i] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }) + + parameterDescription := electricalConnection.AddParameterDescription(model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: m.acVoltage[i], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(electricalConnectionPhaseToPhase[i][0]), + AcMeasuredInReferenceTo: util.Ptr(electricalConnectionPhaseToPhase[i][1]), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }) + + if parameterDescription == nil { + return errors.New("failed to add parameter description") + } + + if valueConstraintsOfPhases[i] != nil { + valueConstraintsOfPhases[i].MeasurementId = m.acVoltage[i] + *measurementsConstraintData = append(*measurementsConstraintData, *valueConstraintsOfPhases[i]) + } + } + + return nil +} + +func (m *MGCP) configureMonitorFrequency( + measurements api.MeasurementServerInterface, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if m.frequencyConfig == nil { + return nil + } + + m.acFrequency = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeHz), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + }) + + parameterDescription := electricalConnection.AddParameterDescription(model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: m.acFrequency, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + }) + if parameterDescription == nil { + return errors.New("failed to add parameter description") + } + + if m.frequencyConfig.ValueConstraints != nil { + m.frequencyConfig.ValueConstraints.MeasurementId = m.acFrequency + *measurementsConstraintData = append(*measurementsConstraintData, *m.frequencyConfig.ValueConstraints) + } + + return nil +} + +func (m *MGCP) getMeasurementDataForId(id *model.MeasurementIdType) (float64, error) { + if id == nil { + return 0, api.ErrMissingData + } + + measurements, err := server.NewMeasurement(m.LocalEntity) + if err != nil { + return 0, err + } + + data, err := measurements.GetDataForId(*id) + if err != nil { + return 0, err + } + + if data == nil { + return 0, api.ErrDataNotAvailable + } + + return data.Value.GetValue(), nil +} diff --git a/usecases/gcp/mgcp/usecase_test.go b/usecases/gcp/mgcp/usecase_test.go new file mode 100644 index 00000000..b245b76a --- /dev/null +++ b/usecases/gcp/mgcp/usecase_test.go @@ -0,0 +1,685 @@ +package mgcp + +import ( + "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" + spineapi "github.com/enbility/spine-go/api" + spinemocks "github.com/enbility/spine-go/mocks" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "testing" + "time" +) + +func TestMGCPUsecaseSuite(t *testing.T) { + suite.Run(t, new(MgcpUsecaseSuite)) +} + +type MgcpUsecaseSuite struct { + suite.Suite + + service api.ServiceInterface +} + +func (s *MgcpUsecaseSuite) BeforeTest(_, _ string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := api.NewConfiguration( + "test", "test", "test", "test", + []shipapi.DeviceCategoryType{shipapi.DeviceCategoryTypeEnergyManagementSystem}, + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeGridGuard}, + 9999, cert, time.Second*4, nil, nil) + + serviceHandler := mocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() +} + +func (s *MgcpUsecaseSuite) Event(_ string, _ spineapi.DeviceRemoteInterface, _ spineapi.EntityRemoteInterface, _ api.EventType) { +} + +func (s *MgcpUsecaseSuite) Test_RequiredParameters() { + localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeGridGuard) + + var monitorPowerConfig = MonitorPowerConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + + var monitorEnergyConfig = MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + + numRequiredParams := 2 + + // iterate over all possible combinations of the required parameters that should not work + for i := 0; i < (1< 0 { + tmpRet = _mock.Called(updateValueTypes) + } else { + tmpRet = _mock.Called() + } + ret := tmpRet + + if len(ret) == 0 { + panic("no return value specified for Update") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(...api1.UpdateData) error); ok { + r0 = returnFunc(updateValueTypes...) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// GcpMGCPInterface_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' +type GcpMGCPInterface_Update_Call struct { + *mock.Call +} + +// Update is a helper method to define mock.On call +// - updateValueTypes ...api1.UpdateData +func (_e *GcpMGCPInterface_Expecter) Update(updateValueTypes ...interface{}) *GcpMGCPInterface_Update_Call { + return &GcpMGCPInterface_Update_Call{Call: _e.mock.On("Update", + append([]interface{}{}, updateValueTypes...)...)} +} + +func (_c *GcpMGCPInterface_Update_Call) Run(run func(updateValueTypes ...api1.UpdateData)) *GcpMGCPInterface_Update_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 []api1.UpdateData + var variadicArgs []api1.UpdateData + if len(args) > 0 { + variadicArgs = args[0].([]api1.UpdateData) + } + arg0 = variadicArgs + run( + arg0..., + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_Update_Call) Return(err error) *GcpMGCPInterface_Update_Call { + _c.Call.Return(err) + return _c +} + +func (_c *GcpMGCPInterface_Update_Call) RunAndReturn(run func(updateValueTypes ...api1.UpdateData) error) *GcpMGCPInterface_Update_Call { _c.Call.Return(run) return _c } -// UpdateUseCaseAvailability provides a mock function with given fields: available -func (_m *GcpMGCPInterface) UpdateUseCaseAvailability(available bool) { - _m.Called(available) +// UpdateDataCurrentPhaseA provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataCurrentPhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataCurrentPhaseA") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 } -// GcpMGCPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' -type GcpMGCPInterface_UpdateUseCaseAvailability_Call struct { +// GcpMGCPInterface_UpdateDataCurrentPhaseA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataCurrentPhaseA' +type GcpMGCPInterface_UpdateDataCurrentPhaseA_Call struct { *mock.Call } -// UpdateUseCaseAvailability is a helper method to define mock.On call -// - available bool -func (_e *GcpMGCPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *GcpMGCPInterface_UpdateUseCaseAvailability_Call { - return &GcpMGCPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +// UpdateDataCurrentPhaseA is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataCurrentPhaseA(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call { + return &GcpMGCPInterface_UpdateDataCurrentPhaseA_Call{Call: _e.mock.On("UpdateDataCurrentPhaseA", value, timestamp, valueState)} } -func (_c *GcpMGCPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *GcpMGCPInterface_UpdateUseCaseAvailability_Call { +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) }) return _c } -func (_c *GcpMGCPInterface_UpdateUseCaseAvailability_Call) Return() *GcpMGCPInterface_UpdateUseCaseAvailability_Call { - _c.Call.Return() +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call { + _c.Call.Return(updateData) return _c } -func (_c *GcpMGCPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(bool)) *GcpMGCPInterface_UpdateUseCaseAvailability_Call { +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataCurrentPhaseA_Call { _c.Call.Return(run) return _c } -// VoltagePerPhase provides a mock function with given fields: entity -func (_m *GcpMGCPInterface) VoltagePerPhase(entity spine_goapi.EntityRemoteInterface) ([]float64, error) { - ret := _m.Called(entity) +// UpdateDataCurrentPhaseB provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataCurrentPhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) if len(ret) == 0 { - panic("no return value specified for VoltagePerPhase") + panic("no return value specified for UpdateDataCurrentPhaseB") } - var r0 []float64 - var r1 error - if rf, ok := ret.Get(0).(func(spine_goapi.EntityRemoteInterface) ([]float64, error)); ok { - return rf(entity) + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataCurrentPhaseB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataCurrentPhaseB' +type GcpMGCPInterface_UpdateDataCurrentPhaseB_Call struct { + *mock.Call +} + +// UpdateDataCurrentPhaseB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataCurrentPhaseB(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call { + return &GcpMGCPInterface_UpdateDataCurrentPhaseB_Call{Call: _e.mock.On("UpdateDataCurrentPhaseB", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataCurrentPhaseB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataCurrentPhaseC provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataCurrentPhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataCurrentPhaseC") } - if rf, ok := ret.Get(0).(func(spine_goapi.EntityRemoteInterface) []float64); ok { - r0 = rf(entity) + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]float64) + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataCurrentPhaseC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataCurrentPhaseC' +type GcpMGCPInterface_UpdateDataCurrentPhaseC_Call struct { + *mock.Call +} + +// UpdateDataCurrentPhaseC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataCurrentPhaseC(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call { + return &GcpMGCPInterface_UpdateDataCurrentPhaseC_Call{Call: _e.mock.On("UpdateDataCurrentPhaseC", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataCurrentPhaseC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataEnergyConsumed provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataEnergyConsumed(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationPeriodStart *time.Time, evaluationPeriodEnd *time.Time) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState, evaluationPeriodStart, evaluationPeriodEnd) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataEnergyConsumed") } - if rf, ok := ret.Get(1).(func(spine_goapi.EntityRemoteInterface) error); ok { - r1 = rf(entity) + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType, *time.Time, *time.Time) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState, evaluationPeriodStart, evaluationPeriodEnd) } else { - r1 = ret.Error(1) + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } } + return r0 +} - return r0, r1 +// GcpMGCPInterface_UpdateDataEnergyConsumed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataEnergyConsumed' +type GcpMGCPInterface_UpdateDataEnergyConsumed_Call struct { + *mock.Call } -// GcpMGCPInterface_VoltagePerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VoltagePerPhase' -type GcpMGCPInterface_VoltagePerPhase_Call struct { +// UpdateDataEnergyConsumed is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +// - evaluationPeriodStart *time.Time +// - evaluationPeriodEnd *time.Time +func (_e *GcpMGCPInterface_Expecter) UpdateDataEnergyConsumed(value interface{}, timestamp interface{}, valueState interface{}, evaluationPeriodStart interface{}, evaluationPeriodEnd interface{}) *GcpMGCPInterface_UpdateDataEnergyConsumed_Call { + return &GcpMGCPInterface_UpdateDataEnergyConsumed_Call{Call: _e.mock.On("UpdateDataEnergyConsumed", value, timestamp, valueState, evaluationPeriodStart, evaluationPeriodEnd)} +} + +func (_c *GcpMGCPInterface_UpdateDataEnergyConsumed_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationPeriodStart *time.Time, evaluationPeriodEnd *time.Time)) *GcpMGCPInterface_UpdateDataEnergyConsumed_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + var arg3 *time.Time + if args[3] != nil { + arg3 = args[3].(*time.Time) + } + var arg4 *time.Time + if args[4] != nil { + arg4 = args[4].(*time.Time) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataEnergyConsumed_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataEnergyConsumed_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataEnergyConsumed_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationPeriodStart *time.Time, evaluationPeriodEnd *time.Time) api1.UpdateData) *GcpMGCPInterface_UpdateDataEnergyConsumed_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataEnergyFeedIn provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataEnergyFeedIn(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationPeriodStart *time.Time, evaluationPeriodEnd *time.Time) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState, evaluationPeriodStart, evaluationPeriodEnd) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataEnergyFeedIn") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType, *time.Time, *time.Time) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState, evaluationPeriodStart, evaluationPeriodEnd) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataEnergyFeedIn_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataEnergyFeedIn' +type GcpMGCPInterface_UpdateDataEnergyFeedIn_Call struct { *mock.Call } -// VoltagePerPhase is a helper method to define mock.On call -// - entity spine_goapi.EntityRemoteInterface -func (_e *GcpMGCPInterface_Expecter) VoltagePerPhase(entity interface{}) *GcpMGCPInterface_VoltagePerPhase_Call { - return &GcpMGCPInterface_VoltagePerPhase_Call{Call: _e.mock.On("VoltagePerPhase", entity)} +// UpdateDataEnergyFeedIn is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +// - evaluationPeriodStart *time.Time +// - evaluationPeriodEnd *time.Time +func (_e *GcpMGCPInterface_Expecter) UpdateDataEnergyFeedIn(value interface{}, timestamp interface{}, valueState interface{}, evaluationPeriodStart interface{}, evaluationPeriodEnd interface{}) *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call { + return &GcpMGCPInterface_UpdateDataEnergyFeedIn_Call{Call: _e.mock.On("UpdateDataEnergyFeedIn", value, timestamp, valueState, evaluationPeriodStart, evaluationPeriodEnd)} } -func (_c *GcpMGCPInterface_VoltagePerPhase_Call) Run(run func(entity spine_goapi.EntityRemoteInterface)) *GcpMGCPInterface_VoltagePerPhase_Call { +func (_c *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationPeriodStart *time.Time, evaluationPeriodEnd *time.Time)) *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(spine_goapi.EntityRemoteInterface)) + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + var arg3 *time.Time + if args[3] != nil { + arg3 = args[3].(*time.Time) + } + var arg4 *time.Time + if args[4] != nil { + arg4 = args[4].(*time.Time) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) }) return _c } -func (_c *GcpMGCPInterface_VoltagePerPhase_Call) Return(_a0 []float64, _a1 error) *GcpMGCPInterface_VoltagePerPhase_Call { - _c.Call.Return(_a0, _a1) +func (_c *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call { + _c.Call.Return(updateData) return _c } -func (_c *GcpMGCPInterface_VoltagePerPhase_Call) RunAndReturn(run func(spine_goapi.EntityRemoteInterface) ([]float64, error)) *GcpMGCPInterface_VoltagePerPhase_Call { +func (_c *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationPeriodStart *time.Time, evaluationPeriodEnd *time.Time) api1.UpdateData) *GcpMGCPInterface_UpdateDataEnergyFeedIn_Call { _c.Call.Return(run) return _c } -// NewGcpMGCPInterface creates a new instance of GcpMGCPInterface. 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 NewGcpMGCPInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *GcpMGCPInterface { - mock := &GcpMGCPInterface{} - mock.Mock.Test(t) +// UpdateDataFrequency provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataFrequency(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) - t.Cleanup(func() { mock.AssertExpectations(t) }) + if len(ret) == 0 { + panic("no return value specified for UpdateDataFrequency") + } - return mock + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataFrequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataFrequency' +type GcpMGCPInterface_UpdateDataFrequency_Call struct { + *mock.Call +} + +// UpdateDataFrequency is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataFrequency(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataFrequency_Call { + return &GcpMGCPInterface_UpdateDataFrequency_Call{Call: _e.mock.On("UpdateDataFrequency", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataFrequency_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataFrequency_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataFrequency_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataFrequency_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataFrequency_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataFrequency_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataPowerLimitationFactor provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataPowerLimitationFactor(value float64) api1.UpdateData { + ret := _mock.Called(value) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataPowerLimitationFactor") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64) api1.UpdateData); ok { + r0 = returnFunc(value) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataPowerLimitationFactor' +type GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call struct { + *mock.Call +} + +// UpdateDataPowerLimitationFactor is a helper method to define mock.On call +// - value float64 +func (_e *GcpMGCPInterface_Expecter) UpdateDataPowerLimitationFactor(value interface{}) *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call { + return &GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call{Call: _e.mock.On("UpdateDataPowerLimitationFactor", value)} +} + +func (_c *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call) Run(run func(value float64)) *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call) RunAndReturn(run func(value float64) api1.UpdateData) *GcpMGCPInterface_UpdateDataPowerLimitationFactor_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataPowerTotal provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataPowerTotal(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataPowerTotal") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataPowerTotal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataPowerTotal' +type GcpMGCPInterface_UpdateDataPowerTotal_Call struct { + *mock.Call +} + +// UpdateDataPowerTotal is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataPowerTotal(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataPowerTotal_Call { + return &GcpMGCPInterface_UpdateDataPowerTotal_Call{Call: _e.mock.On("UpdateDataPowerTotal", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataPowerTotal_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataPowerTotal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataPowerTotal_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataPowerTotal_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataPowerTotal_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataPowerTotal_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseA provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataVoltagePhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseA") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataVoltagePhaseA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseA' +type GcpMGCPInterface_UpdateDataVoltagePhaseA_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseA is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataVoltagePhaseA(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call { + return &GcpMGCPInterface_UpdateDataVoltagePhaseA_Call{Call: _e.mock.On("UpdateDataVoltagePhaseA", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseA_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseAToB provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataVoltagePhaseAToB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseAToB") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseAToB' +type GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseAToB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataVoltagePhaseAToB(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call { + return &GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call{Call: _e.mock.On("UpdateDataVoltagePhaseAToB", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseAToB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseB provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataVoltagePhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseB") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataVoltagePhaseB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseB' +type GcpMGCPInterface_UpdateDataVoltagePhaseB_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataVoltagePhaseB(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call { + return &GcpMGCPInterface_UpdateDataVoltagePhaseB_Call{Call: _e.mock.On("UpdateDataVoltagePhaseB", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseBToC provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataVoltagePhaseBToC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseBToC") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseBToC' +type GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseBToC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataVoltagePhaseBToC(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call { + return &GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call{Call: _e.mock.On("UpdateDataVoltagePhaseBToC", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseBToC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseC provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataVoltagePhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseC") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataVoltagePhaseC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseC' +type GcpMGCPInterface_UpdateDataVoltagePhaseC_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataVoltagePhaseC(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call { + return &GcpMGCPInterface_UpdateDataVoltagePhaseC_Call{Call: _e.mock.On("UpdateDataVoltagePhaseC", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseCToA provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateDataVoltagePhaseCToA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseCToA") + } + + var r0 api1.UpdateData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateData) + } + } + return r0 +} + +// GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseCToA' +type GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseCToA is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *GcpMGCPInterface_Expecter) UpdateDataVoltagePhaseCToA(value interface{}, timestamp interface{}, valueState interface{}) *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call { + return &GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call{Call: _e.mock.On("UpdateDataVoltagePhaseCToA", value, timestamp, valueState)} +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call) Return(updateData api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call { + _c.Call.Return(updateData) + return _c +} + +func (_c *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateData) *GcpMGCPInterface_UpdateDataVoltagePhaseCToA_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) UpdateUseCaseAvailability(available bool) { + _mock.Called(available) + return +} + +// GcpMGCPInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type GcpMGCPInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *GcpMGCPInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *GcpMGCPInterface_UpdateUseCaseAvailability_Call { + return &GcpMGCPInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *GcpMGCPInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *GcpMGCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 bool + if args[0] != nil { + arg0 = args[0].(bool) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *GcpMGCPInterface_UpdateUseCaseAvailability_Call) Return() *GcpMGCPInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *GcpMGCPInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(available bool)) *GcpMGCPInterface_UpdateUseCaseAvailability_Call { + _c.Run(run) + return _c +} + +// VoltagePerPhase provides a mock function for the type GcpMGCPInterface +func (_mock *GcpMGCPInterface) VoltagePerPhase() ([]float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for VoltagePerPhase") + } + + var r0 []float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() ([]float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() []float64); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]float64) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// GcpMGCPInterface_VoltagePerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VoltagePerPhase' +type GcpMGCPInterface_VoltagePerPhase_Call struct { + *mock.Call +} + +// VoltagePerPhase is a helper method to define mock.On call +func (_e *GcpMGCPInterface_Expecter) VoltagePerPhase() *GcpMGCPInterface_VoltagePerPhase_Call { + return &GcpMGCPInterface_VoltagePerPhase_Call{Call: _e.mock.On("VoltagePerPhase")} +} + +func (_c *GcpMGCPInterface_VoltagePerPhase_Call) Run(run func()) *GcpMGCPInterface_VoltagePerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GcpMGCPInterface_VoltagePerPhase_Call) Return(float64s []float64, err error) *GcpMGCPInterface_VoltagePerPhase_Call { + _c.Call.Return(float64s, err) + return _c +} + +func (_c *GcpMGCPInterface_VoltagePerPhase_Call) RunAndReturn(run func() ([]float64, error)) *GcpMGCPInterface_VoltagePerPhase_Call { + _c.Call.Return(run) + return _c } diff --git a/usecases/mocks/MaMGCPInterface.go b/usecases/mocks/MaMGCPInterface.go index 9287260e..e2e93db1 100644 --- a/usecases/mocks/MaMGCPInterface.go +++ b/usecases/mocks/MaMGCPInterface.go @@ -38,9 +38,20 @@ func (_m *MaMGCPInterface) EXPECT() *MaMGCPInterface_Expecter { } // AddFeatures provides a mock function for the type MaMGCPInterface -func (_mock *MaMGCPInterface) AddFeatures() { - _mock.Called() - return +func (_mock *MaMGCPInterface) AddFeatures() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for AddFeatures") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 } // MaMGCPInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' @@ -60,13 +71,13 @@ func (_c *MaMGCPInterface_AddFeatures_Call) Run(run func()) *MaMGCPInterface_Add return _c } -func (_c *MaMGCPInterface_AddFeatures_Call) Return() *MaMGCPInterface_AddFeatures_Call { - _c.Call.Return() +func (_c *MaMGCPInterface_AddFeatures_Call) Return(err error) *MaMGCPInterface_AddFeatures_Call { + _c.Call.Return(err) return _c } -func (_c *MaMGCPInterface_AddFeatures_Call) RunAndReturn(run func()) *MaMGCPInterface_AddFeatures_Call { - _c.Run(run) +func (_c *MaMGCPInterface_AddFeatures_Call) RunAndReturn(run func() error) *MaMGCPInterface_AddFeatures_Call { + _c.Call.Return(run) return _c } diff --git a/usecases/mocks/MaMPCInterface.go b/usecases/mocks/MaMPCInterface.go index 02b55cc3..840ae2f4 100644 --- a/usecases/mocks/MaMPCInterface.go +++ b/usecases/mocks/MaMPCInterface.go @@ -38,9 +38,20 @@ func (_m *MaMPCInterface) EXPECT() *MaMPCInterface_Expecter { } // AddFeatures provides a mock function for the type MaMPCInterface -func (_mock *MaMPCInterface) AddFeatures() { - _mock.Called() - return +func (_mock *MaMPCInterface) AddFeatures() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for AddFeatures") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 } // MaMPCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' @@ -60,13 +71,13 @@ func (_c *MaMPCInterface_AddFeatures_Call) Run(run func()) *MaMPCInterface_AddFe return _c } -func (_c *MaMPCInterface_AddFeatures_Call) Return() *MaMPCInterface_AddFeatures_Call { - _c.Call.Return() +func (_c *MaMPCInterface_AddFeatures_Call) Return(err error) *MaMPCInterface_AddFeatures_Call { + _c.Call.Return(err) return _c } -func (_c *MaMPCInterface_AddFeatures_Call) RunAndReturn(run func()) *MaMPCInterface_AddFeatures_Call { - _c.Run(run) +func (_c *MaMPCInterface_AddFeatures_Call) RunAndReturn(run func() error) *MaMPCInterface_AddFeatures_Call { + _c.Call.Return(run) return _c } diff --git a/usecases/mocks/MuMPCInterface.go b/usecases/mocks/MuMPCInterface.go new file mode 100644 index 00000000..9633281a --- /dev/null +++ b/usecases/mocks/MuMPCInterface.go @@ -0,0 +1,1900 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "time" + + api0 "github.com/enbility/eebus-go/api" + api1 "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + mock "github.com/stretchr/testify/mock" +) + +// NewMuMPCInterface creates a new instance of MuMPCInterface. 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 NewMuMPCInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *MuMPCInterface { + mock := &MuMPCInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MuMPCInterface is an autogenerated mock type for the MuMPCInterface type +type MuMPCInterface struct { + mock.Mock +} + +type MuMPCInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *MuMPCInterface) EXPECT() *MuMPCInterface_Expecter { + return &MuMPCInterface_Expecter{mock: &_m.Mock} +} + +// AddFeatures provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) AddFeatures() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for AddFeatures") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MuMPCInterface_AddFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFeatures' +type MuMPCInterface_AddFeatures_Call struct { + *mock.Call +} + +// AddFeatures is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) AddFeatures() *MuMPCInterface_AddFeatures_Call { + return &MuMPCInterface_AddFeatures_Call{Call: _e.mock.On("AddFeatures")} +} + +func (_c *MuMPCInterface_AddFeatures_Call) Run(run func()) *MuMPCInterface_AddFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_AddFeatures_Call) Return(err error) *MuMPCInterface_AddFeatures_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MuMPCInterface_AddFeatures_Call) RunAndReturn(run func() error) *MuMPCInterface_AddFeatures_Call { + _c.Call.Return(run) + return _c +} + +// AddUseCase provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) AddUseCase() { + _mock.Called() + return +} + +// MuMPCInterface_AddUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUseCase' +type MuMPCInterface_AddUseCase_Call struct { + *mock.Call +} + +// AddUseCase is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) AddUseCase() *MuMPCInterface_AddUseCase_Call { + return &MuMPCInterface_AddUseCase_Call{Call: _e.mock.On("AddUseCase")} +} + +func (_c *MuMPCInterface_AddUseCase_Call) Run(run func()) *MuMPCInterface_AddUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_AddUseCase_Call) Return() *MuMPCInterface_AddUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *MuMPCInterface_AddUseCase_Call) RunAndReturn(run func()) *MuMPCInterface_AddUseCase_Call { + _c.Run(run) + return _c +} + +// AvailableScenariosForEntity provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) AvailableScenariosForEntity(entity api.EntityRemoteInterface) []uint { + ret := _mock.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for AvailableScenariosForEntity") + } + + var r0 []uint + if returnFunc, ok := ret.Get(0).(func(api.EntityRemoteInterface) []uint); ok { + r0 = returnFunc(entity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]uint) + } + } + return r0 +} + +// MuMPCInterface_AvailableScenariosForEntity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AvailableScenariosForEntity' +type MuMPCInterface_AvailableScenariosForEntity_Call struct { + *mock.Call +} + +// AvailableScenariosForEntity is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *MuMPCInterface_Expecter) AvailableScenariosForEntity(entity interface{}) *MuMPCInterface_AvailableScenariosForEntity_Call { + return &MuMPCInterface_AvailableScenariosForEntity_Call{Call: _e.mock.On("AvailableScenariosForEntity", entity)} +} + +func (_c *MuMPCInterface_AvailableScenariosForEntity_Call) Run(run func(entity api.EntityRemoteInterface)) *MuMPCInterface_AvailableScenariosForEntity_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 api.EntityRemoteInterface + if args[0] != nil { + arg0 = args[0].(api.EntityRemoteInterface) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MuMPCInterface_AvailableScenariosForEntity_Call) Return(uints []uint) *MuMPCInterface_AvailableScenariosForEntity_Call { + _c.Call.Return(uints) + return _c +} + +func (_c *MuMPCInterface_AvailableScenariosForEntity_Call) RunAndReturn(run func(entity api.EntityRemoteInterface) []uint) *MuMPCInterface_AvailableScenariosForEntity_Call { + _c.Call.Return(run) + return _c +} + +// CurrentPerPhase provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) CurrentPerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for CurrentPerPhase") + } + + var r0 map[model.ElectricalConnectionPhaseNameType]float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (map[model.ElectricalConnectionPhaseNameType]float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() map[model.ElectricalConnectionPhaseNameType]float64); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[model.ElectricalConnectionPhaseNameType]float64) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_CurrentPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CurrentPerPhase' +type MuMPCInterface_CurrentPerPhase_Call struct { + *mock.Call +} + +// CurrentPerPhase is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) CurrentPerPhase() *MuMPCInterface_CurrentPerPhase_Call { + return &MuMPCInterface_CurrentPerPhase_Call{Call: _e.mock.On("CurrentPerPhase")} +} + +func (_c *MuMPCInterface_CurrentPerPhase_Call) Run(run func()) *MuMPCInterface_CurrentPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_CurrentPerPhase_Call) Return(electricalConnectionPhaseNameTypeToFloat64 map[model.ElectricalConnectionPhaseNameType]float64, err error) *MuMPCInterface_CurrentPerPhase_Call { + _c.Call.Return(electricalConnectionPhaseNameTypeToFloat64, err) + return _c +} + +func (_c *MuMPCInterface_CurrentPerPhase_Call) RunAndReturn(run func() (map[model.ElectricalConnectionPhaseNameType]float64, error)) *MuMPCInterface_CurrentPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// EnergyConsumed provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) EnergyConsumed() (float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for EnergyConsumed") + } + + var r0 float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() float64); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(float64) + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_EnergyConsumed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyConsumed' +type MuMPCInterface_EnergyConsumed_Call struct { + *mock.Call +} + +// EnergyConsumed is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) EnergyConsumed() *MuMPCInterface_EnergyConsumed_Call { + return &MuMPCInterface_EnergyConsumed_Call{Call: _e.mock.On("EnergyConsumed")} +} + +func (_c *MuMPCInterface_EnergyConsumed_Call) Run(run func()) *MuMPCInterface_EnergyConsumed_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_EnergyConsumed_Call) Return(f float64, err error) *MuMPCInterface_EnergyConsumed_Call { + _c.Call.Return(f, err) + return _c +} + +func (_c *MuMPCInterface_EnergyConsumed_Call) RunAndReturn(run func() (float64, error)) *MuMPCInterface_EnergyConsumed_Call { + _c.Call.Return(run) + return _c +} + +// EnergyProduced provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) EnergyProduced() (float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for EnergyProduced") + } + + var r0 float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() float64); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(float64) + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_EnergyProduced_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnergyProduced' +type MuMPCInterface_EnergyProduced_Call struct { + *mock.Call +} + +// EnergyProduced is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) EnergyProduced() *MuMPCInterface_EnergyProduced_Call { + return &MuMPCInterface_EnergyProduced_Call{Call: _e.mock.On("EnergyProduced")} +} + +func (_c *MuMPCInterface_EnergyProduced_Call) Run(run func()) *MuMPCInterface_EnergyProduced_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_EnergyProduced_Call) Return(f float64, err error) *MuMPCInterface_EnergyProduced_Call { + _c.Call.Return(f, err) + return _c +} + +func (_c *MuMPCInterface_EnergyProduced_Call) RunAndReturn(run func() (float64, error)) *MuMPCInterface_EnergyProduced_Call { + _c.Call.Return(run) + return _c +} + +// Frequency provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) Frequency() (float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Frequency") + } + + var r0 float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() float64); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(float64) + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_Frequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Frequency' +type MuMPCInterface_Frequency_Call struct { + *mock.Call +} + +// Frequency is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) Frequency() *MuMPCInterface_Frequency_Call { + return &MuMPCInterface_Frequency_Call{Call: _e.mock.On("Frequency")} +} + +func (_c *MuMPCInterface_Frequency_Call) Run(run func()) *MuMPCInterface_Frequency_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_Frequency_Call) Return(f float64, err error) *MuMPCInterface_Frequency_Call { + _c.Call.Return(f, err) + return _c +} + +func (_c *MuMPCInterface_Frequency_Call) RunAndReturn(run func() (float64, error)) *MuMPCInterface_Frequency_Call { + _c.Call.Return(run) + return _c +} + +// IsCompatibleEntityType provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) IsCompatibleEntityType(entity api.EntityRemoteInterface) bool { + ret := _mock.Called(entity) + + if len(ret) == 0 { + panic("no return value specified for IsCompatibleEntityType") + } + + var r0 bool + if returnFunc, ok := ret.Get(0).(func(api.EntityRemoteInterface) bool); ok { + r0 = returnFunc(entity) + } else { + r0 = ret.Get(0).(bool) + } + return r0 +} + +// MuMPCInterface_IsCompatibleEntityType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsCompatibleEntityType' +type MuMPCInterface_IsCompatibleEntityType_Call struct { + *mock.Call +} + +// IsCompatibleEntityType is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +func (_e *MuMPCInterface_Expecter) IsCompatibleEntityType(entity interface{}) *MuMPCInterface_IsCompatibleEntityType_Call { + return &MuMPCInterface_IsCompatibleEntityType_Call{Call: _e.mock.On("IsCompatibleEntityType", entity)} +} + +func (_c *MuMPCInterface_IsCompatibleEntityType_Call) Run(run func(entity api.EntityRemoteInterface)) *MuMPCInterface_IsCompatibleEntityType_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 api.EntityRemoteInterface + if args[0] != nil { + arg0 = args[0].(api.EntityRemoteInterface) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MuMPCInterface_IsCompatibleEntityType_Call) Return(b bool) *MuMPCInterface_IsCompatibleEntityType_Call { + _c.Call.Return(b) + return _c +} + +func (_c *MuMPCInterface_IsCompatibleEntityType_Call) RunAndReturn(run func(entity api.EntityRemoteInterface) bool) *MuMPCInterface_IsCompatibleEntityType_Call { + _c.Call.Return(run) + return _c +} + +// IsScenarioAvailableAtEntity provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) IsScenarioAvailableAtEntity(entity api.EntityRemoteInterface, scenario uint) bool { + ret := _mock.Called(entity, scenario) + + if len(ret) == 0 { + panic("no return value specified for IsScenarioAvailableAtEntity") + } + + var r0 bool + if returnFunc, ok := ret.Get(0).(func(api.EntityRemoteInterface, uint) bool); ok { + r0 = returnFunc(entity, scenario) + } else { + r0 = ret.Get(0).(bool) + } + return r0 +} + +// MuMPCInterface_IsScenarioAvailableAtEntity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsScenarioAvailableAtEntity' +type MuMPCInterface_IsScenarioAvailableAtEntity_Call struct { + *mock.Call +} + +// IsScenarioAvailableAtEntity is a helper method to define mock.On call +// - entity api.EntityRemoteInterface +// - scenario uint +func (_e *MuMPCInterface_Expecter) IsScenarioAvailableAtEntity(entity interface{}, scenario interface{}) *MuMPCInterface_IsScenarioAvailableAtEntity_Call { + return &MuMPCInterface_IsScenarioAvailableAtEntity_Call{Call: _e.mock.On("IsScenarioAvailableAtEntity", entity, scenario)} +} + +func (_c *MuMPCInterface_IsScenarioAvailableAtEntity_Call) Run(run func(entity api.EntityRemoteInterface, scenario uint)) *MuMPCInterface_IsScenarioAvailableAtEntity_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 api.EntityRemoteInterface + if args[0] != nil { + arg0 = args[0].(api.EntityRemoteInterface) + } + var arg1 uint + if args[1] != nil { + arg1 = args[1].(uint) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MuMPCInterface_IsScenarioAvailableAtEntity_Call) Return(b bool) *MuMPCInterface_IsScenarioAvailableAtEntity_Call { + _c.Call.Return(b) + return _c +} + +func (_c *MuMPCInterface_IsScenarioAvailableAtEntity_Call) RunAndReturn(run func(entity api.EntityRemoteInterface, scenario uint) bool) *MuMPCInterface_IsScenarioAvailableAtEntity_Call { + _c.Call.Return(run) + return _c +} + +// Power provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) Power() (float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Power") + } + + var r0 float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() float64); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(float64) + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_Power_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Power' +type MuMPCInterface_Power_Call struct { + *mock.Call +} + +// Power is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) Power() *MuMPCInterface_Power_Call { + return &MuMPCInterface_Power_Call{Call: _e.mock.On("Power")} +} + +func (_c *MuMPCInterface_Power_Call) Run(run func()) *MuMPCInterface_Power_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_Power_Call) Return(f float64, err error) *MuMPCInterface_Power_Call { + _c.Call.Return(f, err) + return _c +} + +func (_c *MuMPCInterface_Power_Call) RunAndReturn(run func() (float64, error)) *MuMPCInterface_Power_Call { + _c.Call.Return(run) + return _c +} + +// PowerPerPhase provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) PowerPerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for PowerPerPhase") + } + + var r0 map[model.ElectricalConnectionPhaseNameType]float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (map[model.ElectricalConnectionPhaseNameType]float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() map[model.ElectricalConnectionPhaseNameType]float64); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[model.ElectricalConnectionPhaseNameType]float64) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_PowerPerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerPerPhase' +type MuMPCInterface_PowerPerPhase_Call struct { + *mock.Call +} + +// PowerPerPhase is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) PowerPerPhase() *MuMPCInterface_PowerPerPhase_Call { + return &MuMPCInterface_PowerPerPhase_Call{Call: _e.mock.On("PowerPerPhase")} +} + +func (_c *MuMPCInterface_PowerPerPhase_Call) Run(run func()) *MuMPCInterface_PowerPerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_PowerPerPhase_Call) Return(electricalConnectionPhaseNameTypeToFloat64 map[model.ElectricalConnectionPhaseNameType]float64, err error) *MuMPCInterface_PowerPerPhase_Call { + _c.Call.Return(electricalConnectionPhaseNameTypeToFloat64, err) + return _c +} + +func (_c *MuMPCInterface_PowerPerPhase_Call) RunAndReturn(run func() (map[model.ElectricalConnectionPhaseNameType]float64, error)) *MuMPCInterface_PowerPerPhase_Call { + _c.Call.Return(run) + return _c +} + +// RemoteEntitiesScenarios provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) RemoteEntitiesScenarios() []api0.RemoteEntityScenarios { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for RemoteEntitiesScenarios") + } + + var r0 []api0.RemoteEntityScenarios + if returnFunc, ok := ret.Get(0).(func() []api0.RemoteEntityScenarios); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]api0.RemoteEntityScenarios) + } + } + return r0 +} + +// MuMPCInterface_RemoteEntitiesScenarios_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteEntitiesScenarios' +type MuMPCInterface_RemoteEntitiesScenarios_Call struct { + *mock.Call +} + +// RemoteEntitiesScenarios is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) RemoteEntitiesScenarios() *MuMPCInterface_RemoteEntitiesScenarios_Call { + return &MuMPCInterface_RemoteEntitiesScenarios_Call{Call: _e.mock.On("RemoteEntitiesScenarios")} +} + +func (_c *MuMPCInterface_RemoteEntitiesScenarios_Call) Run(run func()) *MuMPCInterface_RemoteEntitiesScenarios_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_RemoteEntitiesScenarios_Call) Return(remoteEntityScenarioss []api0.RemoteEntityScenarios) *MuMPCInterface_RemoteEntitiesScenarios_Call { + _c.Call.Return(remoteEntityScenarioss) + return _c +} + +func (_c *MuMPCInterface_RemoteEntitiesScenarios_Call) RunAndReturn(run func() []api0.RemoteEntityScenarios) *MuMPCInterface_RemoteEntitiesScenarios_Call { + _c.Call.Return(run) + return _c +} + +// RemoveUseCase provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) RemoveUseCase() { + _mock.Called() + return +} + +// MuMPCInterface_RemoveUseCase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveUseCase' +type MuMPCInterface_RemoveUseCase_Call struct { + *mock.Call +} + +// RemoveUseCase is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) RemoveUseCase() *MuMPCInterface_RemoveUseCase_Call { + return &MuMPCInterface_RemoveUseCase_Call{Call: _e.mock.On("RemoveUseCase")} +} + +func (_c *MuMPCInterface_RemoveUseCase_Call) Run(run func()) *MuMPCInterface_RemoveUseCase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_RemoveUseCase_Call) Return() *MuMPCInterface_RemoveUseCase_Call { + _c.Call.Return() + return _c +} + +func (_c *MuMPCInterface_RemoveUseCase_Call) RunAndReturn(run func()) *MuMPCInterface_RemoveUseCase_Call { + _c.Run(run) + return _c +} + +// Update provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) Update(data ...api1.UpdateMeasurementData) error { + var tmpRet mock.Arguments + if len(data) > 0 { + tmpRet = _mock.Called(data) + } else { + tmpRet = _mock.Called() + } + ret := tmpRet + + if len(ret) == 0 { + panic("no return value specified for Update") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(...api1.UpdateMeasurementData) error); ok { + r0 = returnFunc(data...) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MuMPCInterface_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' +type MuMPCInterface_Update_Call struct { + *mock.Call +} + +// Update is a helper method to define mock.On call +// - data ...api1.UpdateMeasurementData +func (_e *MuMPCInterface_Expecter) Update(data ...interface{}) *MuMPCInterface_Update_Call { + return &MuMPCInterface_Update_Call{Call: _e.mock.On("Update", + append([]interface{}{}, data...)...)} +} + +func (_c *MuMPCInterface_Update_Call) Run(run func(data ...api1.UpdateMeasurementData)) *MuMPCInterface_Update_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 []api1.UpdateMeasurementData + var variadicArgs []api1.UpdateMeasurementData + if len(args) > 0 { + variadicArgs = args[0].([]api1.UpdateMeasurementData) + } + arg0 = variadicArgs + run( + arg0..., + ) + }) + return _c +} + +func (_c *MuMPCInterface_Update_Call) Return(err error) *MuMPCInterface_Update_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MuMPCInterface_Update_Call) RunAndReturn(run func(data ...api1.UpdateMeasurementData) error) *MuMPCInterface_Update_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataCurrentPhaseA provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataCurrentPhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataCurrentPhaseA") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataCurrentPhaseA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataCurrentPhaseA' +type MuMPCInterface_UpdateDataCurrentPhaseA_Call struct { + *mock.Call +} + +// UpdateDataCurrentPhaseA is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataCurrentPhaseA(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataCurrentPhaseA_Call { + return &MuMPCInterface_UpdateDataCurrentPhaseA_Call{Call: _e.mock.On("UpdateDataCurrentPhaseA", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseA_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataCurrentPhaseA_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseA_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataCurrentPhaseA_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseA_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataCurrentPhaseA_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataCurrentPhaseB provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataCurrentPhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataCurrentPhaseB") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataCurrentPhaseB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataCurrentPhaseB' +type MuMPCInterface_UpdateDataCurrentPhaseB_Call struct { + *mock.Call +} + +// UpdateDataCurrentPhaseB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataCurrentPhaseB(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataCurrentPhaseB_Call { + return &MuMPCInterface_UpdateDataCurrentPhaseB_Call{Call: _e.mock.On("UpdateDataCurrentPhaseB", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataCurrentPhaseB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseB_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataCurrentPhaseB_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataCurrentPhaseB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataCurrentPhaseC provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataCurrentPhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataCurrentPhaseC") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataCurrentPhaseC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataCurrentPhaseC' +type MuMPCInterface_UpdateDataCurrentPhaseC_Call struct { + *mock.Call +} + +// UpdateDataCurrentPhaseC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataCurrentPhaseC(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataCurrentPhaseC_Call { + return &MuMPCInterface_UpdateDataCurrentPhaseC_Call{Call: _e.mock.On("UpdateDataCurrentPhaseC", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataCurrentPhaseC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseC_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataCurrentPhaseC_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataCurrentPhaseC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataCurrentPhaseC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataEnergyConsumed provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataEnergyConsumed(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationStart *time.Time, evaluationEnd *time.Time) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState, evaluationStart, evaluationEnd) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataEnergyConsumed") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType, *time.Time, *time.Time) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState, evaluationStart, evaluationEnd) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataEnergyConsumed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataEnergyConsumed' +type MuMPCInterface_UpdateDataEnergyConsumed_Call struct { + *mock.Call +} + +// UpdateDataEnergyConsumed is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +// - evaluationStart *time.Time +// - evaluationEnd *time.Time +func (_e *MuMPCInterface_Expecter) UpdateDataEnergyConsumed(value interface{}, timestamp interface{}, valueState interface{}, evaluationStart interface{}, evaluationEnd interface{}) *MuMPCInterface_UpdateDataEnergyConsumed_Call { + return &MuMPCInterface_UpdateDataEnergyConsumed_Call{Call: _e.mock.On("UpdateDataEnergyConsumed", value, timestamp, valueState, evaluationStart, evaluationEnd)} +} + +func (_c *MuMPCInterface_UpdateDataEnergyConsumed_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationStart *time.Time, evaluationEnd *time.Time)) *MuMPCInterface_UpdateDataEnergyConsumed_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + var arg3 *time.Time + if args[3] != nil { + arg3 = args[3].(*time.Time) + } + var arg4 *time.Time + if args[4] != nil { + arg4 = args[4].(*time.Time) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataEnergyConsumed_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataEnergyConsumed_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataEnergyConsumed_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationStart *time.Time, evaluationEnd *time.Time) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataEnergyConsumed_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataEnergyProduced provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataEnergyProduced(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationStart *time.Time, evaluationEnd *time.Time) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState, evaluationStart, evaluationEnd) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataEnergyProduced") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType, *time.Time, *time.Time) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState, evaluationStart, evaluationEnd) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataEnergyProduced_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataEnergyProduced' +type MuMPCInterface_UpdateDataEnergyProduced_Call struct { + *mock.Call +} + +// UpdateDataEnergyProduced is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +// - evaluationStart *time.Time +// - evaluationEnd *time.Time +func (_e *MuMPCInterface_Expecter) UpdateDataEnergyProduced(value interface{}, timestamp interface{}, valueState interface{}, evaluationStart interface{}, evaluationEnd interface{}) *MuMPCInterface_UpdateDataEnergyProduced_Call { + return &MuMPCInterface_UpdateDataEnergyProduced_Call{Call: _e.mock.On("UpdateDataEnergyProduced", value, timestamp, valueState, evaluationStart, evaluationEnd)} +} + +func (_c *MuMPCInterface_UpdateDataEnergyProduced_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationStart *time.Time, evaluationEnd *time.Time)) *MuMPCInterface_UpdateDataEnergyProduced_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + var arg3 *time.Time + if args[3] != nil { + arg3 = args[3].(*time.Time) + } + var arg4 *time.Time + if args[4] != nil { + arg4 = args[4].(*time.Time) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataEnergyProduced_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataEnergyProduced_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataEnergyProduced_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType, evaluationStart *time.Time, evaluationEnd *time.Time) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataEnergyProduced_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataFrequency provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataFrequency(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataFrequency") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataFrequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataFrequency' +type MuMPCInterface_UpdateDataFrequency_Call struct { + *mock.Call +} + +// UpdateDataFrequency is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataFrequency(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataFrequency_Call { + return &MuMPCInterface_UpdateDataFrequency_Call{Call: _e.mock.On("UpdateDataFrequency", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataFrequency_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataFrequency_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataFrequency_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataFrequency_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataFrequency_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataFrequency_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataPowerPhaseA provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataPowerPhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataPowerPhaseA") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataPowerPhaseA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataPowerPhaseA' +type MuMPCInterface_UpdateDataPowerPhaseA_Call struct { + *mock.Call +} + +// UpdateDataPowerPhaseA is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataPowerPhaseA(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataPowerPhaseA_Call { + return &MuMPCInterface_UpdateDataPowerPhaseA_Call{Call: _e.mock.On("UpdateDataPowerPhaseA", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseA_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataPowerPhaseA_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseA_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerPhaseA_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseA_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerPhaseA_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataPowerPhaseB provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataPowerPhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataPowerPhaseB") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataPowerPhaseB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataPowerPhaseB' +type MuMPCInterface_UpdateDataPowerPhaseB_Call struct { + *mock.Call +} + +// UpdateDataPowerPhaseB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataPowerPhaseB(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataPowerPhaseB_Call { + return &MuMPCInterface_UpdateDataPowerPhaseB_Call{Call: _e.mock.On("UpdateDataPowerPhaseB", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataPowerPhaseB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseB_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerPhaseB_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerPhaseB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataPowerPhaseC provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataPowerPhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataPowerPhaseC") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataPowerPhaseC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataPowerPhaseC' +type MuMPCInterface_UpdateDataPowerPhaseC_Call struct { + *mock.Call +} + +// UpdateDataPowerPhaseC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataPowerPhaseC(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataPowerPhaseC_Call { + return &MuMPCInterface_UpdateDataPowerPhaseC_Call{Call: _e.mock.On("UpdateDataPowerPhaseC", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataPowerPhaseC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseC_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerPhaseC_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerPhaseC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerPhaseC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataPowerTotal provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataPowerTotal(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataPowerTotal") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataPowerTotal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataPowerTotal' +type MuMPCInterface_UpdateDataPowerTotal_Call struct { + *mock.Call +} + +// UpdateDataPowerTotal is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataPowerTotal(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataPowerTotal_Call { + return &MuMPCInterface_UpdateDataPowerTotal_Call{Call: _e.mock.On("UpdateDataPowerTotal", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataPowerTotal_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataPowerTotal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerTotal_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerTotal_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataPowerTotal_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataPowerTotal_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseA provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataVoltagePhaseA(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseA") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataVoltagePhaseA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseA' +type MuMPCInterface_UpdateDataVoltagePhaseA_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseA is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataVoltagePhaseA(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataVoltagePhaseA_Call { + return &MuMPCInterface_UpdateDataVoltagePhaseA_Call{Call: _e.mock.On("UpdateDataVoltagePhaseA", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseA_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataVoltagePhaseA_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseA_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseA_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseA_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseA_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseAToB provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataVoltagePhaseAToB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseAToB") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataVoltagePhaseAToB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseAToB' +type MuMPCInterface_UpdateDataVoltagePhaseAToB_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseAToB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataVoltagePhaseAToB(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call { + return &MuMPCInterface_UpdateDataVoltagePhaseAToB_Call{Call: _e.mock.On("UpdateDataVoltagePhaseAToB", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseAToB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseAToC provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataVoltagePhaseAToC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseAToC") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataVoltagePhaseAToC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseAToC' +type MuMPCInterface_UpdateDataVoltagePhaseAToC_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseAToC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataVoltagePhaseAToC(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call { + return &MuMPCInterface_UpdateDataVoltagePhaseAToC_Call{Call: _e.mock.On("UpdateDataVoltagePhaseAToC", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseAToC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseB provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataVoltagePhaseB(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseB") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataVoltagePhaseB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseB' +type MuMPCInterface_UpdateDataVoltagePhaseB_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseB is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataVoltagePhaseB(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataVoltagePhaseB_Call { + return &MuMPCInterface_UpdateDataVoltagePhaseB_Call{Call: _e.mock.On("UpdateDataVoltagePhaseB", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseB_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataVoltagePhaseB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseB_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseB_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseB_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseB_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseBToC provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataVoltagePhaseBToC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseBToC") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataVoltagePhaseBToC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseBToC' +type MuMPCInterface_UpdateDataVoltagePhaseBToC_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseBToC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataVoltagePhaseBToC(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call { + return &MuMPCInterface_UpdateDataVoltagePhaseBToC_Call{Call: _e.mock.On("UpdateDataVoltagePhaseBToC", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseBToC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateDataVoltagePhaseC provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateDataVoltagePhaseC(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData { + ret := _mock.Called(value, timestamp, valueState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDataVoltagePhaseC") + } + + var r0 api1.UpdateMeasurementData + if returnFunc, ok := ret.Get(0).(func(float64, *time.Time, *model.MeasurementValueStateType) api1.UpdateMeasurementData); ok { + r0 = returnFunc(value, timestamp, valueState) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(api1.UpdateMeasurementData) + } + } + return r0 +} + +// MuMPCInterface_UpdateDataVoltagePhaseC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDataVoltagePhaseC' +type MuMPCInterface_UpdateDataVoltagePhaseC_Call struct { + *mock.Call +} + +// UpdateDataVoltagePhaseC is a helper method to define mock.On call +// - value float64 +// - timestamp *time.Time +// - valueState *model.MeasurementValueStateType +func (_e *MuMPCInterface_Expecter) UpdateDataVoltagePhaseC(value interface{}, timestamp interface{}, valueState interface{}) *MuMPCInterface_UpdateDataVoltagePhaseC_Call { + return &MuMPCInterface_UpdateDataVoltagePhaseC_Call{Call: _e.mock.On("UpdateDataVoltagePhaseC", value, timestamp, valueState)} +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseC_Call) Run(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType)) *MuMPCInterface_UpdateDataVoltagePhaseC_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 float64 + if args[0] != nil { + arg0 = args[0].(float64) + } + var arg1 *time.Time + if args[1] != nil { + arg1 = args[1].(*time.Time) + } + var arg2 *model.MeasurementValueStateType + if args[2] != nil { + arg2 = args[2].(*model.MeasurementValueStateType) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseC_Call) Return(updateMeasurementData api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseC_Call { + _c.Call.Return(updateMeasurementData) + return _c +} + +func (_c *MuMPCInterface_UpdateDataVoltagePhaseC_Call) RunAndReturn(run func(value float64, timestamp *time.Time, valueState *model.MeasurementValueStateType) api1.UpdateMeasurementData) *MuMPCInterface_UpdateDataVoltagePhaseC_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUseCaseAvailability provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) UpdateUseCaseAvailability(available bool) { + _mock.Called(available) + return +} + +// MuMPCInterface_UpdateUseCaseAvailability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUseCaseAvailability' +type MuMPCInterface_UpdateUseCaseAvailability_Call struct { + *mock.Call +} + +// UpdateUseCaseAvailability is a helper method to define mock.On call +// - available bool +func (_e *MuMPCInterface_Expecter) UpdateUseCaseAvailability(available interface{}) *MuMPCInterface_UpdateUseCaseAvailability_Call { + return &MuMPCInterface_UpdateUseCaseAvailability_Call{Call: _e.mock.On("UpdateUseCaseAvailability", available)} +} + +func (_c *MuMPCInterface_UpdateUseCaseAvailability_Call) Run(run func(available bool)) *MuMPCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 bool + if args[0] != nil { + arg0 = args[0].(bool) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MuMPCInterface_UpdateUseCaseAvailability_Call) Return() *MuMPCInterface_UpdateUseCaseAvailability_Call { + _c.Call.Return() + return _c +} + +func (_c *MuMPCInterface_UpdateUseCaseAvailability_Call) RunAndReturn(run func(available bool)) *MuMPCInterface_UpdateUseCaseAvailability_Call { + _c.Run(run) + return _c +} + +// VoltagePerPhase provides a mock function for the type MuMPCInterface +func (_mock *MuMPCInterface) VoltagePerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for VoltagePerPhase") + } + + var r0 map[model.ElectricalConnectionPhaseNameType]float64 + var r1 error + if returnFunc, ok := ret.Get(0).(func() (map[model.ElectricalConnectionPhaseNameType]float64, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() map[model.ElectricalConnectionPhaseNameType]float64); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[model.ElectricalConnectionPhaseNameType]float64) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MuMPCInterface_VoltagePerPhase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VoltagePerPhase' +type MuMPCInterface_VoltagePerPhase_Call struct { + *mock.Call +} + +// VoltagePerPhase is a helper method to define mock.On call +func (_e *MuMPCInterface_Expecter) VoltagePerPhase() *MuMPCInterface_VoltagePerPhase_Call { + return &MuMPCInterface_VoltagePerPhase_Call{Call: _e.mock.On("VoltagePerPhase")} +} + +func (_c *MuMPCInterface_VoltagePerPhase_Call) Run(run func()) *MuMPCInterface_VoltagePerPhase_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MuMPCInterface_VoltagePerPhase_Call) Return(electricalConnectionPhaseNameTypeToFloat64 map[model.ElectricalConnectionPhaseNameType]float64, err error) *MuMPCInterface_VoltagePerPhase_Call { + _c.Call.Return(electricalConnectionPhaseNameTypeToFloat64, err) + return _c +} + +func (_c *MuMPCInterface_VoltagePerPhase_Call) RunAndReturn(run func() (map[model.ElectricalConnectionPhaseNameType]float64, error)) *MuMPCInterface_VoltagePerPhase_Call { + _c.Call.Return(run) + return _c +} diff --git a/usecases/mocks/UpdateConfigurationData.go b/usecases/mocks/UpdateConfigurationData.go new file mode 100644 index 00000000..f3ec8472 --- /dev/null +++ b/usecases/mocks/UpdateConfigurationData.go @@ -0,0 +1,169 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "github.com/enbility/spine-go/model" + mock "github.com/stretchr/testify/mock" +) + +// NewUpdateConfigurationData creates a new instance of UpdateConfigurationData. 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 NewUpdateConfigurationData(t interface { + mock.TestingT + Cleanup(func()) +}) *UpdateConfigurationData { + mock := &UpdateConfigurationData{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// UpdateConfigurationData is an autogenerated mock type for the UpdateConfigurationData type +type UpdateConfigurationData struct { + mock.Mock +} + +type UpdateConfigurationData_Expecter struct { + mock *mock.Mock +} + +func (_m *UpdateConfigurationData) EXPECT() *UpdateConfigurationData_Expecter { + return &UpdateConfigurationData_Expecter{mock: &_m.Mock} +} + +// ConfigurationData provides a mock function for the type UpdateConfigurationData +func (_mock *UpdateConfigurationData) ConfigurationData() model.DeviceConfigurationKeyValueDataType { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for ConfigurationData") + } + + var r0 model.DeviceConfigurationKeyValueDataType + if returnFunc, ok := ret.Get(0).(func() model.DeviceConfigurationKeyValueDataType); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(model.DeviceConfigurationKeyValueDataType) + } + return r0 +} + +// UpdateConfigurationData_ConfigurationData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfigurationData' +type UpdateConfigurationData_ConfigurationData_Call struct { + *mock.Call +} + +// ConfigurationData is a helper method to define mock.On call +func (_e *UpdateConfigurationData_Expecter) ConfigurationData() *UpdateConfigurationData_ConfigurationData_Call { + return &UpdateConfigurationData_ConfigurationData_Call{Call: _e.mock.On("ConfigurationData")} +} + +func (_c *UpdateConfigurationData_ConfigurationData_Call) Run(run func()) *UpdateConfigurationData_ConfigurationData_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateConfigurationData_ConfigurationData_Call) Return(deviceConfigurationKeyValueDataType model.DeviceConfigurationKeyValueDataType) *UpdateConfigurationData_ConfigurationData_Call { + _c.Call.Return(deviceConfigurationKeyValueDataType) + return _c +} + +func (_c *UpdateConfigurationData_ConfigurationData_Call) RunAndReturn(run func() model.DeviceConfigurationKeyValueDataType) *UpdateConfigurationData_ConfigurationData_Call { + _c.Call.Return(run) + return _c +} + +// NotSupportedError provides a mock function for the type UpdateConfigurationData +func (_mock *UpdateConfigurationData) NotSupportedError() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for NotSupportedError") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 +} + +// UpdateConfigurationData_NotSupportedError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NotSupportedError' +type UpdateConfigurationData_NotSupportedError_Call struct { + *mock.Call +} + +// NotSupportedError is a helper method to define mock.On call +func (_e *UpdateConfigurationData_Expecter) NotSupportedError() *UpdateConfigurationData_NotSupportedError_Call { + return &UpdateConfigurationData_NotSupportedError_Call{Call: _e.mock.On("NotSupportedError")} +} + +func (_c *UpdateConfigurationData_NotSupportedError_Call) Run(run func()) *UpdateConfigurationData_NotSupportedError_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateConfigurationData_NotSupportedError_Call) Return(err error) *UpdateConfigurationData_NotSupportedError_Call { + _c.Call.Return(err) + return _c +} + +func (_c *UpdateConfigurationData_NotSupportedError_Call) RunAndReturn(run func() error) *UpdateConfigurationData_NotSupportedError_Call { + _c.Call.Return(run) + return _c +} + +// Supported provides a mock function for the type UpdateConfigurationData +func (_mock *UpdateConfigurationData) Supported() bool { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Supported") + } + + var r0 bool + if returnFunc, ok := ret.Get(0).(func() bool); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(bool) + } + return r0 +} + +// UpdateConfigurationData_Supported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Supported' +type UpdateConfigurationData_Supported_Call struct { + *mock.Call +} + +// Supported is a helper method to define mock.On call +func (_e *UpdateConfigurationData_Expecter) Supported() *UpdateConfigurationData_Supported_Call { + return &UpdateConfigurationData_Supported_Call{Call: _e.mock.On("Supported")} +} + +func (_c *UpdateConfigurationData_Supported_Call) Run(run func()) *UpdateConfigurationData_Supported_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateConfigurationData_Supported_Call) Return(b bool) *UpdateConfigurationData_Supported_Call { + _c.Call.Return(b) + return _c +} + +func (_c *UpdateConfigurationData_Supported_Call) RunAndReturn(run func() bool) *UpdateConfigurationData_Supported_Call { + _c.Call.Return(run) + return _c +} diff --git a/usecases/mocks/UpdateData.go b/usecases/mocks/UpdateData.go new file mode 100644 index 00000000..126bcac6 --- /dev/null +++ b/usecases/mocks/UpdateData.go @@ -0,0 +1,124 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" +) + +// NewUpdateData creates a new instance of UpdateData. 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 NewUpdateData(t interface { + mock.TestingT + Cleanup(func()) +}) *UpdateData { + mock := &UpdateData{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// UpdateData is an autogenerated mock type for the UpdateData type +type UpdateData struct { + mock.Mock +} + +type UpdateData_Expecter struct { + mock *mock.Mock +} + +func (_m *UpdateData) EXPECT() *UpdateData_Expecter { + return &UpdateData_Expecter{mock: &_m.Mock} +} + +// NotSupportedError provides a mock function for the type UpdateData +func (_mock *UpdateData) NotSupportedError() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for NotSupportedError") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 +} + +// UpdateData_NotSupportedError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NotSupportedError' +type UpdateData_NotSupportedError_Call struct { + *mock.Call +} + +// NotSupportedError is a helper method to define mock.On call +func (_e *UpdateData_Expecter) NotSupportedError() *UpdateData_NotSupportedError_Call { + return &UpdateData_NotSupportedError_Call{Call: _e.mock.On("NotSupportedError")} +} + +func (_c *UpdateData_NotSupportedError_Call) Run(run func()) *UpdateData_NotSupportedError_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateData_NotSupportedError_Call) Return(err error) *UpdateData_NotSupportedError_Call { + _c.Call.Return(err) + return _c +} + +func (_c *UpdateData_NotSupportedError_Call) RunAndReturn(run func() error) *UpdateData_NotSupportedError_Call { + _c.Call.Return(run) + return _c +} + +// Supported provides a mock function for the type UpdateData +func (_mock *UpdateData) Supported() bool { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Supported") + } + + var r0 bool + if returnFunc, ok := ret.Get(0).(func() bool); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(bool) + } + return r0 +} + +// UpdateData_Supported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Supported' +type UpdateData_Supported_Call struct { + *mock.Call +} + +// Supported is a helper method to define mock.On call +func (_e *UpdateData_Expecter) Supported() *UpdateData_Supported_Call { + return &UpdateData_Supported_Call{Call: _e.mock.On("Supported")} +} + +func (_c *UpdateData_Supported_Call) Run(run func()) *UpdateData_Supported_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateData_Supported_Call) Return(b bool) *UpdateData_Supported_Call { + _c.Call.Return(b) + return _c +} + +func (_c *UpdateData_Supported_Call) RunAndReturn(run func() bool) *UpdateData_Supported_Call { + _c.Call.Return(run) + return _c +} diff --git a/usecases/mocks/UpdateMeasurementData.go b/usecases/mocks/UpdateMeasurementData.go new file mode 100644 index 00000000..cd750857 --- /dev/null +++ b/usecases/mocks/UpdateMeasurementData.go @@ -0,0 +1,169 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "github.com/enbility/eebus-go/api" + mock "github.com/stretchr/testify/mock" +) + +// NewUpdateMeasurementData creates a new instance of UpdateMeasurementData. 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 NewUpdateMeasurementData(t interface { + mock.TestingT + Cleanup(func()) +}) *UpdateMeasurementData { + mock := &UpdateMeasurementData{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// UpdateMeasurementData is an autogenerated mock type for the UpdateMeasurementData type +type UpdateMeasurementData struct { + mock.Mock +} + +type UpdateMeasurementData_Expecter struct { + mock *mock.Mock +} + +func (_m *UpdateMeasurementData) EXPECT() *UpdateMeasurementData_Expecter { + return &UpdateMeasurementData_Expecter{mock: &_m.Mock} +} + +// MeasurementData provides a mock function for the type UpdateMeasurementData +func (_mock *UpdateMeasurementData) MeasurementData() api.MeasurementDataForID { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for MeasurementData") + } + + var r0 api.MeasurementDataForID + if returnFunc, ok := ret.Get(0).(func() api.MeasurementDataForID); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(api.MeasurementDataForID) + } + return r0 +} + +// UpdateMeasurementData_MeasurementData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MeasurementData' +type UpdateMeasurementData_MeasurementData_Call struct { + *mock.Call +} + +// MeasurementData is a helper method to define mock.On call +func (_e *UpdateMeasurementData_Expecter) MeasurementData() *UpdateMeasurementData_MeasurementData_Call { + return &UpdateMeasurementData_MeasurementData_Call{Call: _e.mock.On("MeasurementData")} +} + +func (_c *UpdateMeasurementData_MeasurementData_Call) Run(run func()) *UpdateMeasurementData_MeasurementData_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateMeasurementData_MeasurementData_Call) Return(measurementDataForID api.MeasurementDataForID) *UpdateMeasurementData_MeasurementData_Call { + _c.Call.Return(measurementDataForID) + return _c +} + +func (_c *UpdateMeasurementData_MeasurementData_Call) RunAndReturn(run func() api.MeasurementDataForID) *UpdateMeasurementData_MeasurementData_Call { + _c.Call.Return(run) + return _c +} + +// NotSupportedError provides a mock function for the type UpdateMeasurementData +func (_mock *UpdateMeasurementData) NotSupportedError() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for NotSupportedError") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 +} + +// UpdateMeasurementData_NotSupportedError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NotSupportedError' +type UpdateMeasurementData_NotSupportedError_Call struct { + *mock.Call +} + +// NotSupportedError is a helper method to define mock.On call +func (_e *UpdateMeasurementData_Expecter) NotSupportedError() *UpdateMeasurementData_NotSupportedError_Call { + return &UpdateMeasurementData_NotSupportedError_Call{Call: _e.mock.On("NotSupportedError")} +} + +func (_c *UpdateMeasurementData_NotSupportedError_Call) Run(run func()) *UpdateMeasurementData_NotSupportedError_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateMeasurementData_NotSupportedError_Call) Return(err error) *UpdateMeasurementData_NotSupportedError_Call { + _c.Call.Return(err) + return _c +} + +func (_c *UpdateMeasurementData_NotSupportedError_Call) RunAndReturn(run func() error) *UpdateMeasurementData_NotSupportedError_Call { + _c.Call.Return(run) + return _c +} + +// Supported provides a mock function for the type UpdateMeasurementData +func (_mock *UpdateMeasurementData) Supported() bool { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Supported") + } + + var r0 bool + if returnFunc, ok := ret.Get(0).(func() bool); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(bool) + } + return r0 +} + +// UpdateMeasurementData_Supported_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Supported' +type UpdateMeasurementData_Supported_Call struct { + *mock.Call +} + +// Supported is a helper method to define mock.On call +func (_e *UpdateMeasurementData_Expecter) Supported() *UpdateMeasurementData_Supported_Call { + return &UpdateMeasurementData_Supported_Call{Call: _e.mock.On("Supported")} +} + +func (_c *UpdateMeasurementData_Supported_Call) Run(run func()) *UpdateMeasurementData_Supported_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UpdateMeasurementData_Supported_Call) Return(b bool) *UpdateMeasurementData_Supported_Call { + _c.Call.Return(b) + return _c +} + +func (_c *UpdateMeasurementData_Supported_Call) RunAndReturn(run func() bool) *UpdateMeasurementData_Supported_Call { + _c.Call.Return(run) + return _c +} diff --git a/usecases/mu/mpc/config.go b/usecases/mu/mpc/config.go new file mode 100644 index 00000000..52a1bbec --- /dev/null +++ b/usecases/mu/mpc/config.go @@ -0,0 +1,68 @@ +package mpc + +import ( + "strings" + + "github.com/enbility/spine-go/model" +) + +type PhaseMeasurementSourceMap map[model.ElectricalConnectionPhaseNameType]*model.MeasurementValueSourceType +type PhaseMeasurementConstraintsMap map[model.ElectricalConnectionPhaseNameType]*model.MeasurementConstraintsDataType + +// MonitorPowerConfig is the configuration for the monitor use case +// This config is required by the mpc use case and must be used in mpc.NewMPC +type MonitorPowerConfig struct { + ConnectedPhases model.ElectricalConnectionPhaseNameType // The phases that are measured + + ValueSourceTotal *model.MeasurementValueSourceType // The source of the values from the acPowerTotal (required) + ValueSourcePerPhase PhaseMeasurementSourceMap // The source of the values from the acPower per phase (required if the phase is supported) + + ValueConstraintsTotal *model.MeasurementConstraintsDataType // The constraints for the acPowerTotal (optional can be nil) + ValueConstraintsPerPhase PhaseMeasurementConstraintsMap // The constraints for the acPower per phase (optional can be nil) +} + +// MonitorEnergyConfig is the configuration for the monitor use case +// If this config is passed via NewMPC, the use case will support energy monitoring as specified +type MonitorEnergyConfig struct { + ValueSourceProduction *model.MeasurementValueSourceType // The source of the production values (if this is set, the use case will support production) (optional can be nil) + ValueConstraintsProduction *model.MeasurementConstraintsDataType // The constraints for the production values (optional can be nil) (requires ProductionValueSource to be set) + + ValueSourceConsumption *model.MeasurementValueSourceType // The source of the consumption values (if this is set, the use case will support consumption) (optional can be nil) + ValueConstraintsConsumption *model.MeasurementConstraintsDataType // The constraints for the consumption values (optional can be nil) (requires ConsumptionValueSource to be set) +} + +// MonitorCurrentConfig is the configuration for the monitor use case +// If this config is passed via NewMPC, the use case will support current monitoring +// The current phases will be the same as specified in MonitorPowerConfig +type MonitorCurrentConfig struct { + ValueSourcePerPhase PhaseMeasurementSourceMap // The source of the values per phase (required if the phase is supported) + ValueConstraintsPerPhase PhaseMeasurementConstraintsMap // The constraints for the current per phase (optional can be nil) (requires ValueSourcePerPhase to be set) +} + +// MonitorVoltageConfig is the configuration for the monitor use case +// If this config is passed via NewMPC, the use case will support voltage monitoring +// The voltage phases will be the same as specified in MonitorPowerConfig +type MonitorVoltageConfig struct { + ValueSourcePerPhase PhaseMeasurementSourceMap // The source of the values per phase (required if the phase is supported) + ValueConstraintsPerPhase PhaseMeasurementConstraintsMap // The constraints for the voltage per phase (optional can be nil) (requires ValueSourcePerPhase to be set) +} + +// MonitorFrequencyConfig is the configuration for the monitor use case +type MonitorFrequencyConfig struct { + ValueSource *model.MeasurementValueSourceType // The source of the values (required) + ValueConstraints *model.MeasurementConstraintsDataType // The constraints for the frequency values (optional can be nil) +} + +// SupportsPhases checks if the config supports the given phases +// e.g. SupportsPhases([]string{"a", "B"}) will return true if the config has ConnectedPhases set to "ab" or "abc" +func (c *MonitorPowerConfig) SupportsPhases(phase []string) bool { + phasesString := string(c.ConnectedPhases) + supports := true + for _, p := range phase { + if !strings.Contains(strings.ToLower(phasesString), strings.ToLower(p)) { + supports = false + break + } + } + return supports +} diff --git a/usecases/mu/mpc/config_test.go b/usecases/mu/mpc/config_test.go new file mode 100644 index 00000000..8df2279f --- /dev/null +++ b/usecases/mu/mpc/config_test.go @@ -0,0 +1,47 @@ +package mpc + +import ( + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" +) + +func (s *MuMPCSuite) Test_SupportsPhases() { + allowedConstellations := map[model.ElectricalConnectionPhaseNameType][][]string{ + model.ElectricalConnectionPhaseNameTypeA: {{"a"}}, + model.ElectricalConnectionPhaseNameTypeB: {{"b"}}, + model.ElectricalConnectionPhaseNameTypeC: {{"C"}}, + model.ElectricalConnectionPhaseNameTypeAb: {{"a"}, {"b"}, {"a", "b"}}, + model.ElectricalConnectionPhaseNameTypeBc: {{"b"}, {"c"}, {"B", "c"}}, + model.ElectricalConnectionPhaseNameTypeAc: {{"a"}, {"c"}, {"A", "C"}}, + model.ElectricalConnectionPhaseNameTypeAbc: {{"a"}, {"b"}, {"c"}, {"a", "b"}, {"b", "c"}, {"a", "c"}, {"A", "b", "c"}}, + } + + for constellation, phases := range allowedConstellations { + config := MonitorPowerConfig{ + ConnectedPhases: constellation, + } + + for _, phase := range phases { + assert.True(s.T(), config.SupportsPhases(phase)) + } + } + + notAllowedConstellations := map[model.ElectricalConnectionPhaseNameType][]string{ + model.ElectricalConnectionPhaseNameTypeA: {"b", "c", "ab", "bc", "ac", "abc"}, + model.ElectricalConnectionPhaseNameTypeB: {"a", "c", "ab", "bc", "ac", "abc"}, + model.ElectricalConnectionPhaseNameTypeC: {"a", "b", "ab", "bc", "ac", "abc"}, + model.ElectricalConnectionPhaseNameTypeAb: {"c", "ac", "abc"}, + model.ElectricalConnectionPhaseNameTypeBc: {"a", "ab", "abc"}, + model.ElectricalConnectionPhaseNameTypeAc: {"b", "bc", "abc"}, + } + + for constellation, notSupportedPhases := range notAllowedConstellations { + config := MonitorPowerConfig{ + ConnectedPhases: constellation, + } + + for _, phase := range notSupportedPhases { + assert.False(s.T(), config.SupportsPhases([]string{phase})) + } + } +} diff --git a/usecases/mu/mpc/events.go b/usecases/mu/mpc/events.go new file mode 100644 index 00000000..6208ae10 --- /dev/null +++ b/usecases/mu/mpc/events.go @@ -0,0 +1,10 @@ +package mpc + +import ( + spineapi "github.com/enbility/spine-go/api" +) + +// handle SPINE events +func (e *MPC) HandleEvent(payload spineapi.EventPayload) { + // No events expected as remote Monitoring Appliance has no server data, and writes are not supported by the Monitored Unit +} diff --git a/usecases/mu/mpc/public.go b/usecases/mu/mpc/public.go new file mode 100644 index 00000000..01c2415a --- /dev/null +++ b/usecases/mu/mpc/public.go @@ -0,0 +1,639 @@ +package mpc + +import ( + "time" + + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + usecaseapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/model" +) + +// ------------------------- Getters ------------------------- // + +// Scenario 1 + +// get the momentary active power consumption or production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) Power() (float64, error) { + if e.acPowerTotal == nil { + return 0, api.ErrMissingData + } + + return e.getMeasurementDataForId(e.acPowerTotal) +} + +// get the momentary active power consumption or production per phase +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) PowerPerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) { + powerPerPhase := make(map[model.ElectricalConnectionPhaseNameType]float64) + + for phase, id := range e.acPowerPerPhase { + if id != nil { + power, err := e.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + powerPerPhase[phase] = power + } + } + + return powerPerPhase, nil +} + +// Scenario 2 + +// get the total feed in energy +// +// - negative values are used for production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) EnergyConsumed() (float64, error) { + if e.acEnergyConsumed == nil { + return 0, api.ErrMissingData + } + + return e.getMeasurementDataForId(e.acEnergyConsumed) +} + +// get the total feed in energy +// +// - negative values are used for production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) EnergyProduced() (float64, error) { + if e.acEnergyProduced == nil { + return 0, api.ErrMissingData + } + + return e.getMeasurementDataForId(e.acEnergyProduced) +} + +// Scenario 3 + +// get the momentary phase specific current consumption or production +// +// - positive values are used for consumption +// - negative values are used for production +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) CurrentPerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) { + currentPerPhase := make(map[model.ElectricalConnectionPhaseNameType]float64) + + for phase, id := range e.acCurrentPerPhase { + if id != nil { + current, err := e.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + currentPerPhase[phase] = current + } + } + + return currentPerPhase, nil +} + +// Scenario 4 + +// get the phase specific voltage details +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) VoltagePerPhase() (map[model.ElectricalConnectionPhaseNameType]float64, error) { + voltagePerPhase := make(map[model.ElectricalConnectionPhaseNameType]float64) + + for phase, id := range e.acVoltagePerPhase { + if id != nil { + voltage, err := e.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + voltagePerPhase[phase] = voltage + } + } + + return voltagePerPhase, nil +} + +// Scenario 5 + +// get frequency +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) Frequency() (float64, error) { + if e.acFrequency == nil { + return 0, api.ErrMissingData + } + + return e.getMeasurementDataForId(e.acFrequency) +} + +// ------------------------- Setters ------------------------- // + +// use MPC.Update to update the measurement data +// use it like this: +// +// mpc.Update( +// mpc.UpdateDataPowerTotal(1000, nil, nil), +// mpc.UpdateDataPowerPhaseA(500, nil, nil), +// ... +// ) +// +// possible errors: +// - ErrMissingData if the id is not available +// - and others +func (e *MPC) Update(updateData ...usecaseapi.UpdateMeasurementData) error { + measurements, err := server.NewMeasurement(e.LocalEntity) + if err != nil { + return err + } + + measurementDataForIds := make([]api.MeasurementDataForID, 0) + + for _, measurementDataForId := range updateData { + if !measurementDataForId.Supported() { + return measurementDataForId.NotSupportedError() + } + + measurementDataForIds = append(measurementDataForIds, measurementDataForId.MeasurementData()) + } + + return measurements.UpdateDataForIds(measurementDataForIds) +} + +// Scenario 1 + +// use MPC.UpdateDataPowerTotal in MPC.Update to set the momentary active power consumption or production +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataPowerTotal( + acPowerTotal float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + return newUpdateData( + "acPowerTotal is not supported, please check the configuration", + e.acPowerTotal, + measurementData( + acPowerTotal, + timestamp, + e.powerConfig.ValueSourceTotal, + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataPowerPhaseA in MPC.Update to set the momentary active power consumption or production per phase +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataPowerPhaseA( + acPowerPhaseA float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + return newUpdateData( + "acPowerPhaseA is not supported, please check the configuration", + e.acPowerPerPhase[model.ElectricalConnectionPhaseNameTypeA], + measurementData( + acPowerPhaseA, + timestamp, + e.powerConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeA], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataPowerPhaseB in MPC.Update to set the momentary active power consumption or production per phase +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataPowerPhaseB( + acPowerPhaseB float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + return newUpdateData( + "acPowerPhaseB is not supported, please check the configuration", + e.acPowerPerPhase[model.ElectricalConnectionPhaseNameTypeB], + measurementData( + acPowerPhaseB, + timestamp, + e.powerConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeB], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataPowerPhaseC in MPC.Update to set the momentary active power consumption or production per phase +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataPowerPhaseC( + acPowerPhaseC float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + return newUpdateData( + "acPowerPhaseC is not supported, please check the configuration", + e.acPowerPerPhase[model.ElectricalConnectionPhaseNameTypeC], + measurementData( + acPowerPhaseC, + timestamp, + e.powerConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeC], + valueState, + nil, + nil, + ), + ) +} + +// Scenario 2 + +// use MPC.UpdateDataEnergyConsumed in MPC.Update to set the total feed in energy +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +// The evaluationStart and End are optional and can be nil (both must be set to be used) +func (e *MPC) UpdateDataEnergyConsumed( + energyConsumed float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationStart *time.Time, + evaluationEnd *time.Time, +) usecaseapi.UpdateMeasurementData { + if e.acEnergyConsumed == nil { + return newUpdateData( + "acEnergyConsumed is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acEnergyConsumed is not supported, please check the configuration", + e.acEnergyConsumed, + measurementData( + energyConsumed, + timestamp, + e.energyConfig.ValueSourceConsumption, + valueState, + evaluationStart, + evaluationEnd, + ), + ) +} + +// use MPC.MeasuredUpdateDataEnergyProduced in MPC.Update to set the total feed in energy +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +// The evaluationStart and End are optional and can be nil (both must be set to be used) +func (e *MPC) UpdateDataEnergyProduced( + energyProduced float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, + evaluationStart *time.Time, + evaluationEnd *time.Time, +) usecaseapi.UpdateMeasurementData { + if e.acEnergyProduced == nil { + return newUpdateData( + "acEnergyProduced is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acEnergyProduced is not supported, please check the configuration", + e.acEnergyProduced, + measurementData( + energyProduced, + timestamp, + e.energyConfig.ValueSourceProduction, + valueState, + evaluationStart, + evaluationEnd, + ), + ) +} + +// Scenario 3 + +// use MPC.UpdateDataCurrentPhaseA in MPC.Update to set the momentary phase specific current consumption or production +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataCurrentPhaseA( + acCurrentPhaseA float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if current is supported + if e.currentConfig == nil { + return newUpdateData( + "acCurrent is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acCurrentPhaseA is not supported, please check the configuration", + e.acCurrentPerPhase[model.ElectricalConnectionPhaseNameTypeA], + measurementData( + acCurrentPhaseA, + timestamp, + e.currentConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeA], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataCurrentPhaseB in MPC.Update to set the momentary phase specific current consumption or production +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataCurrentPhaseB( + acCurrentPhaseB float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if current is supported + if e.currentConfig == nil { + return newUpdateData( + "acCurrent is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acCurrentPhaseB is not supported, please check the configuration", + e.acCurrentPerPhase[model.ElectricalConnectionPhaseNameTypeB], + measurementData( + acCurrentPhaseB, + timestamp, + e.currentConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeB], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataCurrentPhaseC in MPC.Update to set the momentary phase specific current consumption or production +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataCurrentPhaseC( + acCurrentPhaseC float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if current is supported + if e.currentConfig == nil { + return newUpdateData( + "acCurrent is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acCurrentPhaseC is not supported, please check the configuration", + e.acCurrentPerPhase[model.ElectricalConnectionPhaseNameTypeC], + measurementData( + acCurrentPhaseC, + timestamp, + e.currentConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeC], + valueState, + nil, + nil, + ), + ) +} + +// Scenario 4 + +// use MPC.UpdateDataVoltagePhaseA in MPC.Update to set the phase specific voltage details +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataVoltagePhaseA( + voltagePhaseA float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if voltage is supported + if e.voltageConfig == nil { + return newUpdateData( + "acVoltage is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acVoltagePhaseA is not supported, please check the configuration", + e.acVoltagePerPhase[model.ElectricalConnectionPhaseNameTypeA], + measurementData( + voltagePhaseA, + timestamp, + e.voltageConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeA], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataVoltagePhaseB in MPC.Update to set the phase specific voltage details +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataVoltagePhaseB( + voltagePhaseB float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if voltage is supported + if e.voltageConfig == nil { + return newUpdateData( + "acVoltage is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acVoltagePhaseB is not supported, please check the configuration", + e.acVoltagePerPhase[model.ElectricalConnectionPhaseNameTypeB], + measurementData( + voltagePhaseB, + timestamp, + e.voltageConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeB], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataVoltagePhaseC in MPC.Update to set the phase specific voltage details +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataVoltagePhaseC( + voltagePhaseC float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if voltage is supported + if e.voltageConfig == nil { + return newUpdateData( + "acVoltage is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acVoltagePhaseC is not supported, please check the configuration", + e.acVoltagePerPhase[model.ElectricalConnectionPhaseNameTypeC], + measurementData( + voltagePhaseC, + timestamp, + e.voltageConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeC], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataVoltagePhaseAToB in MPC.Update to set the phase specific voltage details +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataVoltagePhaseAToB( + voltagePhaseAToB float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if voltage is supported + if e.voltageConfig == nil { + return newUpdateData( + "acVoltage is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acVoltagePhaseAToB is not supported, please check the configuration", + e.acVoltagePerPhase[model.ElectricalConnectionPhaseNameTypeAb], + measurementData( + voltagePhaseAToB, + timestamp, + e.voltageConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeAb], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataVoltagePhaseBToC in MPC.Update to set the phase specific voltage details +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataVoltagePhaseBToC( + voltagePhaseBToC float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if voltage is supported + if e.voltageConfig == nil { + return newUpdateData( + "acVoltage is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acVoltagePhaseBToC is not supported, please check the configuration", + e.acVoltagePerPhase[model.ElectricalConnectionPhaseNameTypeBc], + measurementData( + voltagePhaseBToC, + timestamp, + e.voltageConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeBc], + valueState, + nil, + nil, + ), + ) +} + +// use MPC.UpdateDataVoltagePhaseCToA in MPC.Update to set the phase specific voltage details +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataVoltagePhaseAToC( + voltagePhaseCToA float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // validate first if voltage is supported + if e.voltageConfig == nil { + return newUpdateData( + "acVoltage is not supported, please check the configuration", + nil, + nil, + ) + } + return newUpdateData( + "acVoltagePhaseCToA is not supported, please check the configuration", + e.acVoltagePerPhase[model.ElectricalConnectionPhaseNameTypeAc], + measurementData( + voltagePhaseCToA, + timestamp, + e.voltageConfig.ValueSourcePerPhase[model.ElectricalConnectionPhaseNameTypeAc], + valueState, + nil, + nil, + ), + ) +} + +// Scenario 5 + +// use MPC.UpdateDataFrequency in MPC.Update to set the frequency +// The timestamp is optional and can be nil +// The valueState shall be set if it differs from the normal valueState otherwise it can be nil +func (e *MPC) UpdateDataFrequency( + frequency float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) usecaseapi.UpdateMeasurementData { + // Validate first if frequency is supported + if e.frequencyConfig == nil { + return newUpdateData( + "acFrequency is not supported, please check the configuration", + e.acFrequency, + nil, + ) + } + return newUpdateData( + "acFrequency is not supported, please check the configuration", + e.acFrequency, + measurementData( + frequency, + timestamp, + e.frequencyConfig.ValueSource, + valueState, + nil, + nil, + ), + ) +} diff --git a/usecases/mu/mpc/public_abc_test.go b/usecases/mu/mpc/public_abc_test.go new file mode 100644 index 00000000..14a8ed30 --- /dev/null +++ b/usecases/mu/mpc/public_abc_test.go @@ -0,0 +1,255 @@ +package mpc + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/features/server" + ucapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type MuMpcAbcSuite struct { + suite.Suite + *MuMPCSuite +} + +// Test suite testing an MPC MonitoredUnit that supports 3-phase metering (phases ABC) +func TestMuMpcAbcSuite(t *testing.T) { + suite.Run(t, new(MuMpcAbcSuite)) +} + +func (s *MuMpcAbcSuite) BeforeTest(suiteName, testName string) { + s.MuMPCSuite = NewMuMPCSuite( + &s.Suite, + &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + &MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraintsProduction: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueStepSize: model.NewScaledNumberType(0.001), + }), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorCurrentConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + &MonitorVoltageConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeAb: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeBc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeAc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + &MonitorFrequencyConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(100), + ValueStepSize: model.NewScaledNumberType(1), + }), + }, + ) + s.MuMPCSuite.BeforeTest(suiteName, testName) +} + +func (s *MuMpcAbcSuite) Test_Power() { + err := s.sut.Update( + s.sut.UpdateDataPowerTotal(5.0, util.Ptr(time.Now()), nil), + ) + assert.Nil(s.T(), err) + + power, err := s.sut.Power() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, power) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, nil) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.0}, values) +} + +func (s *MuMpcAbcSuite) Test_PowerPerPhase() { + err := s.sut.Update( + s.sut.UpdateDataPowerPhaseA(5.0, util.Ptr(time.Now()), nil), + s.sut.UpdateDataPowerPhaseB(6.0, util.Ptr(time.Now()), nil), + s.sut.UpdateDataPowerPhaseC(7.0, util.Ptr(time.Now()), util.Ptr(model.MeasurementValueStateTypeError)), + ) + assert.Nil(s.T(), err) + expectedPowerPerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeA: 5.0, + model.ElectricalConnectionPhaseNameTypeB: 6.0, + model.ElectricalConnectionPhaseNameTypeC: 7.0, + } + + powerPerPhases, err := s.sut.PowerPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedPowerPerPhases, powerPerPhases) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.ElementsMatch(s.T(), []float64{5.0, 6.0, 7.0}, values) +} + +func (s *MuMpcAbcSuite) Test_EnergyConsumed() { + err := s.sut.Update( + s.sut.UpdateDataEnergyConsumed(5.0, util.Ptr(time.Now()), nil, util.Ptr(time.Now()), util.Ptr(time.Now())), + ) + assert.Nil(s.T(), err) + + energyConsumed, err := s.sut.EnergyConsumed() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, energyConsumed) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyConsumed), + } + measurement, err := server.NewMeasurement(s.sut.LocalEntity) + assert.Nil(s.T(), err) + values, err := measurement.GetDataForFilter(filter) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1, len(values)) + assert.Equal(s.T(), 5.0, (*values[0].Value).GetValue()) +} + +func (s *MuMpcAbcSuite) Test_EnergyProduced() { + err := s.sut.Update( + s.sut.UpdateDataEnergyProduced(5.0, nil, nil, nil, nil), + ) + assert.Nil(s.T(), err) + + energyProduced, err := s.sut.EnergyProduced() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, energyProduced) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyProduced), + } + measurement, err := server.NewMeasurement(s.sut.LocalEntity) + assert.Nil(s.T(), err) + values, err := measurement.GetDataForFilter(filter) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1, len(values)) + assert.Equal(s.T(), 5.0, (*values[0].Value).GetValue()) +} + +func (s *MuMpcAbcSuite) Test_CurrentPerPhase() { + err := s.sut.Update( + s.sut.UpdateDataCurrentPhaseA(5.0, nil, nil), + s.sut.UpdateDataCurrentPhaseB(3.0, nil, nil), + s.sut.UpdateDataCurrentPhaseC(1.0, nil, nil), + ) + assert.Nil(s.T(), err) + expectedCurrentPerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeA: 5.0, + model.ElectricalConnectionPhaseNameTypeB: 3.0, + model.ElectricalConnectionPhaseNameTypeC: 1.0, + } + + currentPerPhases, err := s.sut.CurrentPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedCurrentPerPhases, currentPerPhases) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.ElementsMatch(s.T(), []float64{5.0, 3.0, 1.0}, values) +} + +func (s *MuMpcAbcSuite) Test_VoltagePerPhase() { + err := s.sut.Update( + s.sut.UpdateDataVoltagePhaseA(5.0, nil, nil), + s.sut.UpdateDataVoltagePhaseB(6.0, nil, nil), + s.sut.UpdateDataVoltagePhaseC(7.0, nil, nil), + s.sut.UpdateDataVoltagePhaseAToB(8.0, nil, nil), + s.sut.UpdateDataVoltagePhaseBToC(9.0, nil, nil), + s.sut.UpdateDataVoltagePhaseAToC(10.0, nil, nil), + ) + assert.Nil(s.T(), err) + expectedVoltagePerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeA: 5.0, + model.ElectricalConnectionPhaseNameTypeB: 6.0, + model.ElectricalConnectionPhaseNameTypeC: 7.0, + model.ElectricalConnectionPhaseNameTypeAb: 8.0, + model.ElectricalConnectionPhaseNameTypeBc: 9.0, + model.ElectricalConnectionPhaseNameTypeAc: 10.0, + } + + voltagePerPhases, err := s.sut.VoltagePerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedVoltagePerPhases, voltagePerPhases) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, "", ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.ElementsMatch(s.T(), []float64{5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, values) +} + +func (s *MuMpcAbcSuite) Test_Frequency() { + err := s.sut.Update( + s.sut.UpdateDataFrequency(5.0, nil, nil), + ) + assert.Nil(s.T(), err) + + frequency, err := s.sut.Frequency() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, frequency) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + } + measurements, err := server.NewMeasurement(s.sut.LocalEntity) + assert.Nil(s.T(), err) + values, err := measurements.GetDataForFilter(filter) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1, len(values)) + assert.Equal(s.T(), 5.0, (*values[0].Value).GetValue()) +} diff --git a/usecases/mu/mpc/public_bc_test.go b/usecases/mu/mpc/public_bc_test.go new file mode 100644 index 00000000..6965aae7 --- /dev/null +++ b/usecases/mu/mpc/public_bc_test.go @@ -0,0 +1,201 @@ +package mpc + +import ( + "testing" + "time" + + ucapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type MuMpcBcSuite struct { + suite.Suite + *MuMPCSuite +} + +// Test suite testing an MPC MonitoredUnit that supports metering for 2 phases (phases AB) +func TestMuMpcAbSuite(t *testing.T) { + suite.Run(t, new(MuMpcBcSuite)) +} + +func (s *MuMpcBcSuite) BeforeTest(suiteName, testName string) { + s.MuMPCSuite = NewMuMPCSuite( + &s.Suite, + &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeBc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + &MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorCurrentConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + &MonitorVoltageConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeBc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + &MonitorFrequencyConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(100), + ValueStepSize: model.NewScaledNumberType(1), + }), + }, + ) + s.MuMPCSuite.BeforeTest(suiteName, testName) +} + +func (s *MuMpcBcSuite) Test_Power() { + err := s.sut.Update( + s.sut.UpdateDataPowerTotal(5.0, util.Ptr(time.Now()), nil), + ) + assert.Nil(s.T(), err) + + power, err := s.sut.Power() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, power) +} + +func (s *MuMpcBcSuite) Test_PowerPerPhase() { + err := s.sut.Update( + s.sut.UpdateDataPowerPhaseB(6.0, util.Ptr(time.Now()), nil), + s.sut.UpdateDataPowerPhaseC(7.0, util.Ptr(time.Now()), util.Ptr(model.MeasurementValueStateTypeError)), + ) + assert.Nil(s.T(), err) + expectedPowerPerPhase := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeB: 6.0, + model.ElectricalConnectionPhaseNameTypeC: 7.0, + } + + powerPerPhases, err := s.sut.PowerPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedPowerPerPhase, powerPerPhases) + + err = s.sut.Update( + s.sut.UpdateDataPowerPhaseA(5.0, util.Ptr(time.Now()), nil), + ) + assert.NotNil(s.T(), err) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.ElementsMatch(s.T(), []float64{6.0, 7.0}, values) +} + +func (s *MuMpcBcSuite) Test_EnergyConsumed() { + err := s.sut.Update( + s.sut.UpdateDataEnergyConsumed(5.0, util.Ptr(time.Now()), nil, util.Ptr(time.Now()), util.Ptr(time.Now())), + ) + assert.Nil(s.T(), err) + + energyConsumed, err := s.sut.EnergyConsumed() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, energyConsumed) +} + +func (s *MuMpcBcSuite) Test_EnergyProduced() { + err := s.sut.Update( + s.sut.UpdateDataEnergyProduced(5.0, nil, nil, nil, nil), + ) + assert.Nil(s.T(), err) + + energyProduced, err := s.sut.EnergyProduced() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, energyProduced) +} + +func (s *MuMpcBcSuite) Test_CurrentPerPhase() { + err := s.sut.Update( + s.sut.UpdateDataCurrentPhaseB(3.0, nil, nil), + s.sut.UpdateDataCurrentPhaseC(1.0, nil, nil), + ) + assert.Nil(s.T(), err) + expectedCurrentPerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeB: 3.0, + model.ElectricalConnectionPhaseNameTypeC: 1.0, + } + + currentPerPhases, err := s.sut.CurrentPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedCurrentPerPhases, currentPerPhases) + + err = s.sut.Update( + s.sut.UpdateDataCurrentPhaseA(5.0, nil, nil), + ) + assert.NotNil(s.T(), err) +} + +func (s *MuMpcBcSuite) Test_VoltagePerPhase() { + err := s.sut.Update( + s.sut.UpdateDataVoltagePhaseB(6.0, nil, nil), + s.sut.UpdateDataVoltagePhaseC(7.0, nil, nil), + s.sut.UpdateDataVoltagePhaseBToC(9.0, nil, nil), + ) + assert.Nil(s.T(), err) + expectedVoltagePerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeB: 6.0, + model.ElectricalConnectionPhaseNameTypeC: 7.0, + model.ElectricalConnectionPhaseNameTypeBc: 9.0, + } + + voltagePerPhases, err := s.sut.VoltagePerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedVoltagePerPhases, voltagePerPhases) + + err = s.sut.Update( + s.sut.UpdateDataVoltagePhaseA(5.0, nil, nil), + ) + assert.NotNil(s.T(), err) + + err = s.sut.Update( + s.sut.UpdateDataVoltagePhaseAToB(5.0, nil, nil), + ) + assert.NotNil(s.T(), err) + + err = s.sut.Update( + s.sut.UpdateDataVoltagePhaseAToC(5.0, nil, nil), + ) + assert.NotNil(s.T(), err) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, "", ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.ElementsMatch(s.T(), []float64{6.0, 7.0, 9.0}, values) +} + +func (s *MuMpcBcSuite) Test_Frequency() { + err := s.sut.Update( + s.sut.UpdateDataFrequency(5.0, nil, nil), + ) + assert.Nil(s.T(), err) + + frequency, err := s.sut.Frequency() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 5.0, frequency) +} diff --git a/usecases/mu/mpc/public_constraint_test.go b/usecases/mu/mpc/public_constraint_test.go new file mode 100644 index 00000000..16e286dd --- /dev/null +++ b/usecases/mu/mpc/public_constraint_test.go @@ -0,0 +1,428 @@ +package mpc + +import ( + "testing" + "time" + + "github.com/enbility/eebus-go/features/server" + ucapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type MuMpcConstraintSuite struct { + suite.Suite + *MuMPCSuite +} + +// Test suite testing an MPC MonitoredUnit that uses ValueConstraints on its measurements +func TestMuMpcConstraintSuite(t *testing.T) { + suite.Run(t, new(MuMpcConstraintSuite)) +} + +func (s *MuMpcConstraintSuite) BeforeTest(suiteName, testName string) { + s.MuMPCSuite = NewMuMPCSuite( + &s.Suite, + &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeA, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + ValueConstraintsTotal: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueStepSize: model.NewScaledNumberType(0.1), + }), + ValueConstraintsPerPhase: PhaseMeasurementConstraintsMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueStepSize: model.NewScaledNumberType(0.1), + }), + }, + }, + &MonitorEnergyConfig{ + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraintsConsumption: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueStepSize: model.NewScaledNumberType(100), + }), + }, + &MonitorCurrentConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + ValueConstraintsPerPhase: PhaseMeasurementConstraintsMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(32), + ValueStepSize: model.NewScaledNumberType(0.1), + }), + }, + }, + &MonitorVoltageConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + ValueConstraintsPerPhase: PhaseMeasurementConstraintsMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementConstraintsDataType{ + ValueStepSize: model.NewScaledNumberType(0.1), + }), + }, + }, + &MonitorFrequencyConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(40), + ValueRangeMax: model.NewScaledNumberType(60), + ValueStepSize: model.NewScaledNumberType(0.01), + }), + }, + ) + s.MuMPCSuite.BeforeTest(suiteName, testName) +} + +func (s *MuMpcConstraintSuite) Test_Power() { + // Test when getMeasurementId returns error + { + _, err := s.sut.Power() + assert.Error(s.T(), err) + } + + // Testing updating the power total and read this update + { + err := s.sut.Update( + s.sut.UpdateDataPowerTotal(5.7, util.Ptr(time.Now()), nil), + ) + assert.Nil(s.T(), err) + + power, err := s.sut.Power() + expectedPowerValue := 5.7 + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedPowerValue, power) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, nil) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.7}, values) + } +} + +func (s *MuMpcConstraintSuite) Test_PowerPerPhase() { + // Test when getMeasurementId returns error + { + _, err := s.sut.PowerPerPhase() + assert.Error(s.T(), err) + } + + // Test when updating power power per phase and read this update + { + err := s.sut.Update( + s.sut.UpdateDataPowerPhaseA(5.7, util.Ptr(time.Now()), nil), + ) + expectedPowerPerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeA: 5.7, + } + assert.Nil(s.T(), err) + + powerPerPhases, err := s.sut.PowerPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedPowerPerPhases, powerPerPhases) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{5.7}, values) + } + + // Test updating the energy consumed when it is not supported + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, nil, s.currentConfig, s.voltageConfig, s.frequencyConfig) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + err = mpcInstance.Update( + mpcInstance.UpdateDataEnergyConsumed(100, nil, nil, nil, nil), + ) + assert.Error(s.T(), err) + } +} + +func (s *MuMpcConstraintSuite) Test_EnergyConsumed() { + // Test when getMeasurementId returns error + { + _, err := s.sut.EnergyConsumed() + assert.Error(s.T(), err) + } + + // Test when acEnergyConsumed has no measurement id + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, s.energyConfig, s.currentConfig, s.voltageConfig, s.frequencyConfig) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + mpcInstance.acEnergyConsumed = nil + _, err = mpcInstance.EnergyConsumed() + assert.Error(s.T(), err) + } + + // Test when updating the energy consumed and get this update + { + err := s.sut.Update( + s.sut.UpdateDataEnergyConsumed(570, util.Ptr(time.Now()), nil, util.Ptr(time.Now()), util.Ptr(time.Now())), + ) + assert.Nil(s.T(), err) + + energyConsumed, err := s.sut.EnergyConsumed() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 570.0, energyConsumed) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyConsumed), + } + measurement, err := server.NewMeasurement(s.sut.LocalEntity) + assert.Nil(s.T(), err) + values, err := measurement.GetDataForFilter(filter) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1, len(values)) + assert.Equal(s.T(), 570.0, (*values[0].Value).GetValue()) + } +} + +func (s *MuMpcConstraintSuite) Test_EnergyProduced() { + // Test when getMeasurementId returns error + { + _, err := s.sut.EnergyProduced() + assert.Error(s.T(), err) + } + + // Test when updating the energy produced and read this update + { + err := s.sut.Update( + s.sut.UpdateDataEnergyProduced(5.0, nil, nil, nil, nil), + ) + assert.NotNil(s.T(), err) + + _, err = s.sut.EnergyProduced() + assert.NotNil(s.T(), err) + + // Check if the client filter works (it shouldn't) + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyProduced), + } + measurement, err := server.NewMeasurement(s.sut.LocalEntity) + assert.Nil(s.T(), err) + _, err = measurement.GetDataForFilter(filter) + assert.NotNil(s.T(), err) + } + + // Test updating the energy produced when it is not supported + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, nil, s.currentConfig, s.voltageConfig, s.frequencyConfig) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + err = mpcInstance.Update( + mpcInstance.UpdateDataEnergyProduced(100, nil, nil, nil, nil), + ) + assert.Error(s.T(), err) + } +} + +func (s *MuMpcConstraintSuite) Test_CurrentPerPhase() { + // Test when getMeasurementId returns error + { + _, err := s.sut.CurrentPerPhase() + assert.Error(s.T(), err) + } + + // Test when updating the current per phase and read this update + { + err := s.sut.Update( + s.sut.UpdateDataCurrentPhaseA(0.1, nil, nil), + ) + assert.Nil(s.T(), err) + expectedCurrentPerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeA: 0.1, + } + + currentPerPhases, err := s.sut.CurrentPerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedCurrentPerPhases, currentPerPhases) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, model.EnergyDirectionTypeConsume, ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{0.1}, values) + } + + // Test updating the current per phase when it is not supported + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, s.energyConfig, nil, s.voltageConfig, s.frequencyConfig) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + err = mpcInstance.Update( + mpcInstance.UpdateDataCurrentPhaseA(10, nil, nil), + mpcInstance.UpdateDataCurrentPhaseB(10, nil, nil), + mpcInstance.UpdateDataCurrentPhaseC(10, nil, nil), + ) + assert.Error(s.T(), err) + } +} + +func (s *MuMpcConstraintSuite) Test_VoltagePerPhase() { + // Test when getMeasurementId returns error + { + _, err := s.sut.VoltagePerPhase() + assert.Error(s.T(), err) + } + + // Test when updating the voltage per phase and read this update + { + err := s.sut.Update( + s.sut.UpdateDataVoltagePhaseA(230, nil, nil), + ) + assert.Nil(s.T(), err) + + expectedVoltagePerPhases := map[model.ElectricalConnectionPhaseNameType]float64{ + model.ElectricalConnectionPhaseNameTypeA: 230, + } + + voltagePerPhases, err := s.sut.VoltagePerPhase() + assert.Nil(s.T(), err) + assert.Equal(s.T(), expectedVoltagePerPhases, voltagePerPhases) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + } + values, err := s.measurementPhaseSpecificDataForFilter(filter, "", ucapi.PhaseNameMapping) + assert.Nil(s.T(), err) + assert.Equal(s.T(), []float64{230}, values) + } + + // Test updating the voltage per phase when it is not supported + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, s.energyConfig, s.currentConfig, nil, s.frequencyConfig) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + err = mpcInstance.Update( + mpcInstance.UpdateDataVoltagePhaseA(230, nil, nil), + mpcInstance.UpdateDataVoltagePhaseB(230, nil, nil), + mpcInstance.UpdateDataVoltagePhaseC(230, nil, nil), + mpcInstance.UpdateDataVoltagePhaseAToB(0, nil, nil), + mpcInstance.UpdateDataVoltagePhaseBToC(0, nil, nil), + mpcInstance.UpdateDataVoltagePhaseAToC(0, nil, nil), + ) + assert.Error(s.T(), err) + } +} + +func (s *MuMpcConstraintSuite) Test_Frequency() { + // Test when getMeasurementId returns error + { + _, err := s.sut.Frequency() + assert.Error(s.T(), err) + } + + // Test when acFrequency has no measurement id + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, s.energyConfig, s.currentConfig, s.voltageConfig, s.frequencyConfig) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + mpcInstance.acFrequency = nil + _, err = mpcInstance.Frequency() + assert.Error(s.T(), err) + } + + // Test when updating the frequency and read this update + { + err := s.sut.Update( + s.sut.UpdateDataFrequency(50, nil, nil), + ) + assert.Nil(s.T(), err) + + frequency, err := s.sut.Frequency() + assert.Nil(s.T(), err) + assert.Equal(s.T(), 50.0, frequency) + + // Check if the client filter works + filter := model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + } + measurements, err := server.NewMeasurement(s.sut.LocalEntity) + assert.Nil(s.T(), err) + values, err := measurements.GetDataForFilter(filter) + assert.Nil(s.T(), err) + assert.Equal(s.T(), 1, len(values)) + assert.Equal(s.T(), 50.0, (*values[0].Value).GetValue()) + } + + // Test covering updating frequency when it is not supported + { + mpcInstance, err := NewMPC(s.sut.LocalEntity, s.Event, s.powerConfig, s.energyConfig, s.currentConfig, s.voltageConfig, nil) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.Nil(s.T(), err) + mpcInstance.AddUseCase() + + err = mpcInstance.Update( + mpcInstance.UpdateDataFrequency(50, nil, nil), + ) + assert.Error(s.T(), err) + } +} diff --git a/usecases/mu/mpc/testhelper_test.go b/usecases/mu/mpc/testhelper_test.go new file mode 100644 index 00000000..a4aaa522 --- /dev/null +++ b/usecases/mu/mpc/testhelper_test.go @@ -0,0 +1,147 @@ +package mpc + +import ( + "slices" + "time" + + "github.com/enbility/eebus-go/features/server" + + "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" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +const remoteSki string = "testremoteski" + +type MuMPCSuite struct { + *suite.Suite + + powerConfig *MonitorPowerConfig + energyConfig *MonitorEnergyConfig + currentConfig *MonitorCurrentConfig + voltageConfig *MonitorVoltageConfig + frequencyConfig *MonitorFrequencyConfig + sut *MPC + + service api.ServiceInterface +} + +func NewMuMPCSuite( + suite *suite.Suite, + powerConfig *MonitorPowerConfig, + energyConfig *MonitorEnergyConfig, + currentConfig *MonitorCurrentConfig, + voltageConfig *MonitorVoltageConfig, + frequencyConfig *MonitorFrequencyConfig, +) *MuMPCSuite { + return &MuMPCSuite{ + Suite: suite, + powerConfig: powerConfig, + energyConfig: energyConfig, + currentConfig: currentConfig, + voltageConfig: voltageConfig, + frequencyConfig: frequencyConfig, + } +} + +func (s *MuMPCSuite) Event(_ string, _ spineapi.DeviceRemoteInterface, _ spineapi.EntityRemoteInterface, _ api.EventType) { +} + +func (s *MuMPCSuite) BeforeTest(_, _ string) { + cert, _ := cert.CreateCertificate("test", "test", "DE", "test") + configuration, _ := api.NewConfiguration( + "test", "test", "test", "test", + []shipapi.DeviceCategoryType{shipapi.DeviceCategoryTypeEnergyManagementSystem}, + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeInverter}, + 9999, cert, time.Second*4, nil, nil) + + serviceHandler := mocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + _ = s.service.Setup() + + localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) + s.sut, _ = NewMPC( + localEntity, + s.Event, + s.powerConfig, + s.energyConfig, + s.currentConfig, + s.voltageConfig, + s.frequencyConfig, + ) + + assert.Nil(s.T(), s.sut.AddFeatures()) + s.sut.AddUseCase() +} + +func (s *MuMPCSuite) measurementPhaseSpecificDataForFilter( + measurementFilter model.MeasurementDescriptionDataType, + energyDirection model.EnergyDirectionType, + validPhaseNameTypes []model.ElectricalConnectionPhaseNameType, +) ([]float64, error) { + measurements, err := server.NewMeasurement(s.sut.LocalEntity) + if err != nil { + return nil, err + } + + electricalConnection, err := server.NewElectricalConnection(s.sut.LocalEntity) + if err != nil { + return nil, err + } + + data, err := measurements.GetDataForFilter(measurementFilter) + if err != nil || len(data) == 0 { + return nil, api.ErrDataNotAvailable + } + + var result []float64 + + for _, item := range data { + if item.Value == nil || item.MeasurementId == nil { + continue + } + + if validPhaseNameTypes != nil { + filter := model.ElectricalConnectionParameterDescriptionDataType{ + MeasurementId: item.MeasurementId, + } + param, err := electricalConnection.GetParameterDescriptionsForFilter(filter) + if err != nil || len(param) == 0 || + param[0].AcMeasuredPhases == nil || + !slices.Contains(validPhaseNameTypes, *param[0].AcMeasuredPhases) { + continue + } + } + + if energyDirection != "" { + filter := model.ElectricalConnectionParameterDescriptionDataType{ + MeasurementId: item.MeasurementId, + } + desc, err := electricalConnection.GetDescriptionForParameterDescriptionFilter(filter) + if err != nil || desc == 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 +} diff --git a/usecases/mu/mpc/types.go b/usecases/mu/mpc/types.go new file mode 100644 index 00000000..67f2d3f0 --- /dev/null +++ b/usecases/mu/mpc/types.go @@ -0,0 +1,10 @@ +package mpc + +import "github.com/enbility/eebus-go/api" + +const ( + // Update of the list of remote entities supporting the Use Case + // + // Use `RemoteEntities` to get the current data + UseCaseSupportUpdate api.EventType = "mu-mpc-UseCaseSupportUpdate" +) diff --git a/usecases/mu/mpc/update_helper.go b/usecases/mu/mpc/update_helper.go new file mode 100644 index 00000000..fba4bb98 --- /dev/null +++ b/usecases/mu/mpc/update_helper.go @@ -0,0 +1,113 @@ +package mpc + +import ( + "encoding/json" + "errors" + "time" + + "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" +) + +type UpdateData struct { + supported bool + notSupportedError error + measurementData api.MeasurementDataForID +} + +type serUpdateData struct { + Supported bool + NotSupportedError string + MeasurementData api.MeasurementDataForID +} + +func (r *UpdateData) UnmarshalJSON(data []byte) error { + aux := serUpdateData{} + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + r.supported = aux.Supported + if aux.NotSupportedError != "" { + r.notSupportedError = errors.New(aux.NotSupportedError) + } + r.measurementData = aux.MeasurementData + + return nil +} + +func (r *UpdateData) MarshalJSON() ([]byte, error) { + aux := serUpdateData{ + Supported: r.supported, + MeasurementData: r.measurementData, + } + if r.notSupportedError != nil { + aux.NotSupportedError = r.notSupportedError.Error() + } + + return json.Marshal(aux) +} + +func (u *UpdateData) Supported() bool { + return u.supported +} + +func (u *UpdateData) NotSupportedError() error { + return u.notSupportedError +} + +func (u *UpdateData) MeasurementData() api.MeasurementDataForID { + return u.measurementData +} + +func newUpdateData( + errorString string, + id *model.MeasurementIdType, + data *model.MeasurementDataType, +) *UpdateData { + if id == nil || data == nil { + return &UpdateData{ + supported: false, + notSupportedError: errors.New(errorString), + } + } else { + return &UpdateData{ + supported: true, + measurementData: api.MeasurementDataForID{ + Id: *id, + Data: *data, + }, + } + } +} + +func measurementData( + value float64, + timestamp *time.Time, + valueSource *model.MeasurementValueSourceType, + valueState *model.MeasurementValueStateType, + evaluationStart *time.Time, + evaluationEnd *time.Time, +) *model.MeasurementDataType { + measurement := model.MeasurementDataType{ + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + Value: model.NewScaledNumberType(value), + ValueSource: valueSource, + ValueState: valueState, + } + + if timestamp != nil { + measurement.Timestamp = model.NewAbsoluteOrRelativeTimeTypeFromTime(*timestamp) + } + + if evaluationStart != nil && evaluationEnd != nil { + measurement.EvaluationPeriod = &model.TimePeriodType{ + StartTime: model.NewAbsoluteOrRelativeTimeTypeFromTime(*evaluationStart), + EndTime: model.NewAbsoluteOrRelativeTimeTypeFromTime(*evaluationEnd), + } + } + + return &measurement +} diff --git a/usecases/mu/mpc/usecase.go b/usecases/mu/mpc/usecase.go new file mode 100644 index 00000000..e52937a0 --- /dev/null +++ b/usecases/mu/mpc/usecase.go @@ -0,0 +1,529 @@ +package mpc + +import ( + "errors" + "fmt" + + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + "github.com/enbility/eebus-go/usecases/usecase" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" +) + +type PhaseMeasurementIdMap map[model.ElectricalConnectionPhaseNameType]*model.MeasurementIdType + +type MPC struct { + *usecase.UseCaseBase + + powerConfig *MonitorPowerConfig + energyConfig *MonitorEnergyConfig + currentConfig *MonitorCurrentConfig + voltageConfig *MonitorVoltageConfig + frequencyConfig *MonitorFrequencyConfig + + acPowerTotal *model.MeasurementIdType + acPowerPerPhase PhaseMeasurementIdMap + acEnergyConsumed *model.MeasurementIdType + acEnergyProduced *model.MeasurementIdType + acCurrentPerPhase PhaseMeasurementIdMap + acVoltagePerPhase PhaseMeasurementIdMap + acFrequency *model.MeasurementIdType +} + +// creates a new MPC usecase instance for a MonitoredUnit entity +// +// parameters: +// - localEntity: the local entity for which to construct an MPC instance +// - eventCB: the callback to notify about events for this usecase +// - monitorPowerConfig: (required) configuration parameters for MPC scenario 1 +// - monitorEnergyConfig: (optional) configuration parameters for MPC scenario 2, nil if not supported +// - monitorCurrentConfig: (optional) configuration parameters for MPC scenario 3, nil if not supported +// - monitorVoltageConfig: (optional) configuration parameters for MPC scenario 4, nil if not supported +// - monitorFrequencyConfig: (optional) configuration parameters for MPC scenario, nil if not supported +// +// possible errors: +// - if required fields in parameters are unset +func NewMPC( + localEntity spineapi.EntityLocalInterface, + eventCB api.EntityEventCallback, + monitorPowerConfig *MonitorPowerConfig, + monitorEnergyConfig *MonitorEnergyConfig, + monitorCurrentConfig *MonitorCurrentConfig, + monitorVoltageConfig *MonitorVoltageConfig, + monitorFrequencyConfig *MonitorFrequencyConfig, +) (*MPC, error) { + if monitorPowerConfig == nil { + return nil, errors.New("the monitor power config for the MPC-Use-Case must not be nil") + } + + validActorTypes := []model.UseCaseActorType{model.UseCaseActorTypeMonitoringAppliance} + useCaseScenarios := []api.UseCaseScenario{ + { + Scenario: model.UseCaseScenarioSupportType(1), + Mandatory: true, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + }, + } + + if monitorEnergyConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(2), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + }) + } + + if monitorCurrentConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(3), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + }) + } + + if monitorVoltageConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(4), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + }) + } + + if monitorFrequencyConfig != nil { + useCaseScenarios = append(useCaseScenarios, api.UseCaseScenario{ + Scenario: model.UseCaseScenarioSupportType(5), + Mandatory: false, + ServerFeatures: []model.FeatureTypeType{ + model.FeatureTypeTypeElectricalConnection, + model.FeatureTypeTypeMeasurement, + }, + }) + } + + u := usecase.NewUseCaseBase( + localEntity, + model.UseCaseActorTypeMonitoredUnit, + model.UseCaseNameTypeMonitoringOfPowerConsumption, + "1.0.0", + "release", + useCaseScenarios, + eventCB, + UseCaseSupportUpdate, + validActorTypes, + nil, + true, + ) + + uc := &MPC{ + UseCaseBase: u, + powerConfig: monitorPowerConfig, + energyConfig: monitorEnergyConfig, + currentConfig: monitorCurrentConfig, + voltageConfig: monitorVoltageConfig, + frequencyConfig: monitorFrequencyConfig, + } + uc.acPowerPerPhase = PhaseMeasurementIdMap{} + uc.acCurrentPerPhase = PhaseMeasurementIdMap{} + uc.acVoltagePerPhase = PhaseMeasurementIdMap{} + + _ = localEntity.Device().Events().Subscribe(uc) + + return uc, nil +} + +func (e *MPC) AddFeatures() error { + // server features + electricalConnectionFeature := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + if electricalConnectionFeature == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeElectricalConnection)) + } + electricalConnectionFeature.AddFunctionType(model.FunctionTypeElectricalConnectionDescriptionListData, true, false) + electricalConnectionFeature.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + + measurementFeature := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + if measurementFeature == nil { + return errors.New("could not add feature: " + string(model.FeatureTypeTypeMeasurement)) + } + measurementFeature.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, true, false) + measurementFeature.AddFunctionType(model.FunctionTypeMeasurementConstraintsListData, true, false) + measurementFeature.AddFunctionType(model.FunctionTypeMeasurementListData, true, false) + + measurements, err := server.NewMeasurement(e.LocalEntity) + if err != nil { + return err + } + + electricalConnection, err := server.NewElectricalConnection(e.LocalEntity) + if err != nil { + return err + } + + electricalConnectionId, err := electricalConnection.GetOrAddIdForDescription(model.ElectricalConnectionDescriptionDataType{ + PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + }) + if err != nil { + return err + } + + constraints := make([]model.MeasurementConstraintsDataType, 0) + + configMethods := []func( + measurements *server.Measurement, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, + ) error{ + e.configureMonitorPower, + e.configureMonitorEnergy, + e.configureMonitorCurrent, + e.configureMonitorVoltage, + e.configureMonitorFrequency, + } + + for _, configMethod := range configMethods { + if err := configMethod(measurements, electricalConnection, electricalConnectionId, &constraints); err != nil { + return err + } + } + + // if any of the configured measurements set constraints, update the + // measurementFeature with those accumulated constraints + if len(constraints) > 0 { + measurementFeature.UpdateData( + model.FunctionTypeMeasurementConstraintsListData, + &model.MeasurementConstraintsListDataType{ + MeasurementConstraintsData: constraints, + }, nil, nil, + ) + } + + return nil +} + +func (e *MPC) configureMonitorPower( + measurements *server.Measurement, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if e.powerConfig == nil { + return errors.New("mpc monitoring power must be configured") + } + + e.acPowerTotal = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + }) + + // if constraints are configured for acPowerTotal, set the + // constraint id and update measurementsConstraintData + if e.powerConfig.ValueConstraintsTotal != nil { + e.powerConfig.ValueConstraintsTotal.MeasurementId = e.acPowerTotal + *measurementsConstraintData = append(*measurementsConstraintData, *e.powerConfig.ValueConstraintsTotal) + } + + parameterDescription := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acPowerTotal, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameType(e.powerConfig.ConnectedPhases)), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + + parameterDescriptionId := electricalConnection.AddParameterDescription(parameterDescription) + if parameterDescriptionId == nil { + return errors.New("could not add parameter description") + } + + for phase := range e.powerConfig.ValueSourcePerPhase { + if !e.powerConfig.SupportsPhases([]string{string(phase)}) { + errStr := fmt.Sprintf("power configuration for phase %s is not supported, please check the configuration", phase) + return errors.New(errStr) + } + e.acPowerPerPhase[phase] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + }) + + parameterDescription := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acPowerPerPhase[phase], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(phase), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + parameterDescriptionId := electricalConnection.AddParameterDescription(parameterDescription) + if parameterDescriptionId == nil { + return errors.New("could not add parameter description") + } + + if e.powerConfig.ValueConstraintsPerPhase[phase] == nil { + continue + } + e.powerConfig.ValueConstraintsPerPhase[phase].MeasurementId = e.acPowerPerPhase[phase] + *measurementsConstraintData = append(*measurementsConstraintData, *e.powerConfig.ValueConstraintsPerPhase[phase]) + + } + + return nil +} + +func (e *MPC) configureMonitorEnergy( + measurements *server.Measurement, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if e.energyConfig == nil { + return nil + } + + if e.energyConfig.ValueSourceConsumption != nil { + e.acEnergyConsumed = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyConsumed), + }) + + // if constraints are configured for acEnergyConsumed, set the + // constraint id and update measurementsConstraintData + if e.energyConfig.ValueConstraintsConsumption != nil { + e.energyConfig.ValueConstraintsConsumption.MeasurementId = e.acEnergyConsumed + *measurementsConstraintData = append(*measurementsConstraintData, *e.energyConfig.ValueConstraintsConsumption) + } + + parameterDescription := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acEnergyConsumed, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + } + + parameterDescriptionId := electricalConnection.AddParameterDescription(parameterDescription) + if parameterDescriptionId == nil { + return errors.New("could not add parameter description") + } + } + + if e.energyConfig.ValueSourceProduction != nil { + e.acEnergyProduced = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyProduced), + }) + + // if constraints are configured for acEnergyProduced, set the + // constraint id and update measurementsConstraintData + if e.energyConfig.ValueConstraintsProduction != nil { + e.energyConfig.ValueConstraintsProduction.MeasurementId = e.acEnergyProduced + *measurementsConstraintData = append(*measurementsConstraintData, *e.energyConfig.ValueConstraintsProduction) + } + + p4 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acEnergyProduced, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + } + idP4 := electricalConnection.AddParameterDescription(p4) + if idP4 == nil { + return errors.New("could not add parameter description") + } + } + + return nil +} + +func (e *MPC) configureMonitorCurrent( + measurements *server.Measurement, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if e.currentConfig == nil { + return nil + } + + for phase := range e.currentConfig.ValueSourcePerPhase { + if !e.powerConfig.SupportsPhases([]string{string(phase)}) { + errStr := fmt.Sprintf("power configuration for phase %s is not supported, please check the configuration", phase) + return errors.New(errStr) + } + e.acCurrentPerPhase[phase] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }) + + parameterDescription := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acCurrentPerPhase[phase], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(phase), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + + parameterDescriptionId := electricalConnection.AddParameterDescription(parameterDescription) + if parameterDescriptionId == nil { + return errors.New("could not add parameter description") + } + + if e.currentConfig.ValueConstraintsPerPhase[phase] == nil { + continue + } + e.currentConfig.ValueConstraintsPerPhase[phase].MeasurementId = e.acCurrentPerPhase[phase] + *measurementsConstraintData = append(*measurementsConstraintData, *e.currentConfig.ValueConstraintsPerPhase[phase]) + + } + + return nil +} + +func (e *MPC) configureMonitorVoltage( + measurements *server.Measurement, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if e.voltageConfig == nil { + return nil + } + + for phase := range e.voltageConfig.ValueSourcePerPhase { + switch len(string(phase)) { + case 1: + if !e.powerConfig.SupportsPhases([]string{string(phase)}) { + errStr := fmt.Sprintf("power configuration for phase %s is not supported, please check the configuration", phase) + return errors.New(errStr) + } + case 2: + fromPhase := string(string(phase)[0]) + toPhase := string(string(phase)[1]) + if !e.powerConfig.SupportsPhases([]string{fromPhase, toPhase}) { + errStr := fmt.Sprintf("power configuration for phase %s is not supported, please check the configuration", phase) + return errors.New(errStr) + } + } + e.acVoltagePerPhase[phase] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }) + parameterDescription := model.ElectricalConnectionParameterDescriptionDataType{} + + switch len(string(phase)) { + case 1: + parameterDescription = model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acVoltagePerPhase[phase], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(phase), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + case 2: + fromPhase := string(string(phase)[0]) + toPhase := string(string(phase)[1]) + parameterDescription = model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acVoltagePerPhase[phase], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameType(fromPhase)), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameType(toPhase)), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + } + parameterDescriptionId := electricalConnection.AddParameterDescription(parameterDescription) + if parameterDescriptionId == nil { + return errors.New("could not add parameter description") + } + } + + return nil +} + +func (e *MPC) configureMonitorFrequency( + measurements *server.Measurement, + electricalConnection api.ElectricalConnectionServerInterface, + electricalConnectionId *model.ElectricalConnectionIdType, + measurementsConstraintData *[]model.MeasurementConstraintsDataType, +) error { + if e.frequencyConfig == nil { + return nil + } + + e.acFrequency = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeHz), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + }) + + // if constraints are configured for acFrequency, set the + // constraint id and update measurementsConstraintData + if e.frequencyConfig.ValueConstraints != nil { + e.frequencyConfig.ValueConstraints.MeasurementId = e.acFrequency + *measurementsConstraintData = append(*measurementsConstraintData, *e.frequencyConfig.ValueConstraints) + } + + parameterDescription := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: electricalConnectionId, + MeasurementId: e.acFrequency, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + } + + parameterDescriptionId := electricalConnection.AddParameterDescription(parameterDescription) + if parameterDescriptionId == nil { + return errors.New("could not add parameter description") + } + + return nil +} + +func (e *MPC) getMeasurementDataForId(id *model.MeasurementIdType) (float64, error) { + measurements, err := server.NewMeasurement(e.LocalEntity) + if err != nil { + return 0, err + } + + data, err := measurements.GetDataForId(*id) + if err != nil { + return 0, err + } + + if data == nil { + return 0, api.ErrDataNotAvailable + } + + return data.Value.GetValue(), nil +} diff --git a/usecases/mu/mpc/usecase_test.go b/usecases/mu/mpc/usecase_test.go new file mode 100644 index 00000000..1123c3cb --- /dev/null +++ b/usecases/mu/mpc/usecase_test.go @@ -0,0 +1,744 @@ +package mpc + +import ( + "errors" + "testing" + "time" + + "github.com/enbility/eebus-go/features/server" + spineMocks "github.com/enbility/spine-go/mocks" + + "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" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestBasicSuite(t *testing.T) { + suite.Run(t, new(MuMpcUsecaseSuite)) +} + +type MuMpcUsecaseSuite struct { + suite.Suite + + service api.ServiceInterface + mockedService *mocks.ServiceInterface + + localEntity spineapi.EntityLocalInterface + mockedLocalEntity *spineMocks.EntityLocalInterface + mockedLocalDevice *spineMocks.DeviceLocalInterface + mockedLocalFeature *spineMocks.FeatureLocalInterface + + mockedRemoteEntity *spineMocks.EntityRemoteInterface + mockedRemoteDevice *spineMocks.DeviceRemoteInterface + mockedRemoteFeature *spineMocks.FeatureRemoteInterface + + mockedElectricalConnectionFeature *mocks.ElectricalConnectionServerInterface +} + +func (s *MuMpcUsecaseSuite) Event(_ string, _ spineapi.DeviceRemoteInterface, _ spineapi.EntityRemoteInterface, _ api.EventType) { +} + +func (s *MuMpcUsecaseSuite) BeforeTest(_, _ string) { + cert, err := cert.CreateCertificate("test", "test", "DE", "test") + assert.Nil(s.T(), err) + configuration, _ := api.NewConfiguration( + "test", "test", "test", "test", + []shipapi.DeviceCategoryType{shipapi.DeviceCategoryTypeEnergyManagementSystem}, + model.DeviceTypeTypeEnergyManagementSystem, + []model.EntityTypeType{model.EntityTypeTypeInverter}, + 9999, cert, time.Second*4, nil, nil) + + serviceHandler := mocks.NewServiceReaderInterface(s.T()) + serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe() + + s.service = service.NewService(configuration, serviceHandler) + err = s.service.Setup() + assert.Nil(s.T(), err) + + s.mockedRemoteDevice = spineMocks.NewDeviceRemoteInterface(s.T()) + s.mockedRemoteEntity = spineMocks.NewEntityRemoteInterface(s.T()) + s.mockedRemoteFeature = spineMocks.NewFeatureRemoteInterface(s.T()) + s.mockedRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(s.mockedRemoteFeature).Maybe() + s.mockedRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe() + s.mockedRemoteEntity.EXPECT().Device().Return(s.mockedRemoteDevice).Maybe() + s.mockedRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe() + entityAddress := &model.EntityAddressType{} + s.mockedRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe() + s.mockedRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe() + s.mockedRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe() + s.mockedRemoteFeature.EXPECT().Operations().Return(nil).Maybe() + + s.localEntity = s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) +} + +func (s *MuMpcUsecaseSuite) Test_MpcOptionalParameters() { + localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) + + // required + var monitorPowerConfig = &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + // the following 4 parameters are optional and can be nil + var monitorEnergyConfig = MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + var monitorCurrentConfig = MonitorCurrentConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + var monitorVoltageConfig = MonitorVoltageConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeAb: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeBc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeAc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + var monitorFrequencyConfig = MonitorFrequencyConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(100), + ValueStepSize: model.NewScaledNumberType(1), + }), + } + + numOptionalParams := 4 + + // iterate over all permutations of nil/set + for i := 0; i < (1 << numOptionalParams); i++ { + // Determine which parameters to set + var optEnergyConfig *MonitorEnergyConfig + var optCurrentConfig *MonitorCurrentConfig + var optVoltageConfig *MonitorVoltageConfig + var optFrequencyConfig *MonitorFrequencyConfig + if i&1 != 0 { + optEnergyConfig = &monitorEnergyConfig + } + if i&2 != 0 { + optCurrentConfig = &monitorCurrentConfig + } + if i&4 != 0 { + optVoltageConfig = &monitorVoltageConfig + } + if i&8 != 0 { + optFrequencyConfig = &monitorFrequencyConfig + } + + mpc, err := NewMPC( + localEntity, + s.Event, + monitorPowerConfig, + optEnergyConfig, + optCurrentConfig, + optVoltageConfig, + optFrequencyConfig, + ) + + assert.Nil(s.T(), err) + + err = mpc.AddFeatures() + assert.Nil(s.T(), err) + mpc.AddUseCase() + } + + // test creating new mpc instance without power configuration + { + mpcInstance, err := NewMPC(s.localEntity, s.Event, nil, nil, nil, nil, nil) + expectedError := "the monitor power config for the MPC-Use-Case must not be nil" + assert.ErrorContains(s.T(), err, expectedError) + assert.Nil(s.T(), mpcInstance) + } +} + +func (s *MuMpcUsecaseSuite) Test_MpcRequredParametersError() { + localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) + + _, err := NewMPC( + localEntity, + s.Event, + nil, + nil, + nil, + nil, + nil, + ) + + assert.NotNil(s.T(), err) +} + +func (s *MuMpcUsecaseSuite) Test_getMeasurementDataForId() { + localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) + + monitorPowerConfig := MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + + mpc, err := NewMPC( + localEntity, + s.Event, + &monitorPowerConfig, + nil, + nil, + nil, + nil, + ) + assert.Nil(s.T(), err) + + _, err = mpc.getMeasurementDataForId(mpc.acPowerTotal) + assert.NotNil(s.T(), err) + + err = mpc.AddFeatures() + assert.Nil(s.T(), err) + mpc.AddUseCase() + + _, err = mpc.getMeasurementDataForId(mpc.acPowerTotal) + assert.NotNil(s.T(), err) + + err = mpc.Update( + mpc.UpdateDataPowerTotal(5.0, util.Ptr(time.Now()), nil), + ) + assert.Nil(s.T(), err) + + measurementData, err := mpc.getMeasurementDataForId(mpc.acPowerTotal) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), measurementData) +} + +func (s *MuMpcAbcSuite) Test_AddFeatures_ElectricalFeatureNilError() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + s.sut.LocalEntity = localEntity + + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(nil) + err := s.sut.AddFeatures() + assert.NotNil(s.T(), err) +} + +func (s *MuMpcAbcSuite) Test_AddFeatures_MeasurementFeatureNilError() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + s.sut.LocalEntity = localEntity + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().AddFunctionType(mock.Anything, mock.Anything, mock.Anything).Return() + + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(anyFeature) + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(nil) + + err := s.sut.AddFeatures() + assert.NotNil(s.T(), err) +} + +func (s *MuMpcAbcSuite) Test_AddFeatures_NewMeasurementsError() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + s.sut.LocalEntity = localEntity + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().AddFunctionType(mock.Anything, mock.Anything, mock.Anything).Return() + + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(anyFeature) + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(anyFeature) + + localEntity.EXPECT().Device().Return(nil) + localEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(nil) + + err := s.sut.AddFeatures() + assert.NotNil(s.T(), err) +} + +func (s *MuMpcAbcSuite) Test_AddFeatures_NewElectricalConnectionError() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + s.sut.LocalEntity = localEntity + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().AddFunctionType(mock.Anything, mock.Anything, mock.Anything).Return() + + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(anyFeature) + localEntity.EXPECT().GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(anyFeature) + + localEntity.EXPECT().Device().Return(nil) + localEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(anyFeature) + localEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(nil) + + err := s.sut.AddFeatures() + assert.NotNil(s.T(), err) +} + +func (s *MuMpcUsecaseSuite) Test_configureMonitorPower() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + mockedDevice := spineMocks.NewDeviceLocalInterface(s.T()) + mockedEvents := spineMocks.NewEventsManagerInterface(s.T()) + mockedEvents.EXPECT().Subscribe(mock.Anything).Return(nil).Maybe() + mockedDevice.EXPECT().Events().Return(mockedEvents).Maybe() + localEntity.EXPECT().Device().Return(mockedDevice) + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().DataCopy(mock.Anything).Return(nil) + anyFeature.EXPECT().UpdateData(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + localEntity.EXPECT().FeatureOfTypeAndRole(mock.Anything, mock.Anything).Return(anyFeature) + + monitorPowerConfig := MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + mpc, err := NewMPC( + localEntity, + s.Event, + &monitorPowerConfig, + nil, + nil, + nil, + nil, + ) + assert.Nil(s.T(), err) + + measurements, err := server.NewMeasurement(localEntity) + assert.Nil(s.T(), err) + + var electricalConnection api.ElectricalConnectionServerInterface + electricalConnection, err = server.NewElectricalConnection(localEntity) + assert.Nil(s.T(), err) + + electricalConnectionId := model.ElectricalConnectionIdType(111) + constraints := make([]model.MeasurementConstraintsDataType, 0) + + mpc.powerConfig = nil + err = mpc.configureMonitorPower( + measurements, + electricalConnection, + &electricalConnectionId, + &constraints, + ) + assert.NotNil(s.T(), err) // no monitorPowerConfig + + mpc.powerConfig = &monitorPowerConfig + electricalConnection = mocks.NewElectricalConnectionServerInterface(s.T()) + electricalConnection.(*mocks.ElectricalConnectionServerInterface).EXPECT().AddParameterDescription(mock.Anything).Return(nil) + + constellationsToCheck := []model.ElectricalConnectionPhaseNameType{ + model.ElectricalConnectionPhaseNameTypeA, + model.ElectricalConnectionPhaseNameTypeB, + model.ElectricalConnectionPhaseNameTypeC, + } + + for _, phaseConstellation := range constellationsToCheck { + mpc.powerConfig.ConnectedPhases = phaseConstellation + + err = mpc.configureMonitorPower( + measurements, + electricalConnection, + &electricalConnectionId, + nil, + ) + + assert.NotNil(s.T(), err) // could not add parameter description + } +} + +func (s *MuMpcUsecaseSuite) Test_configureMonitorEnergy() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + mockedDevice := spineMocks.NewDeviceLocalInterface(s.T()) + mockedEvents := spineMocks.NewEventsManagerInterface(s.T()) + mockedEvents.EXPECT().Subscribe(mock.Anything).Return(nil).Maybe() + mockedDevice.EXPECT().Events().Return(mockedEvents).Maybe() + localEntity.EXPECT().Device().Return(mockedDevice) + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().DataCopy(mock.Anything).Return(nil) + anyFeature.EXPECT().UpdateData(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + localEntity.EXPECT().FeatureOfTypeAndRole(mock.Anything, mock.Anything).Return(anyFeature) + + monitorPowerConfig := MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + monitorEnergyConfig := MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + + mpc, err := NewMPC( + localEntity, + s.Event, + &monitorPowerConfig, + &monitorEnergyConfig, + nil, + nil, + nil, + ) + assert.Nil(s.T(), err) + + measurements, err := server.NewMeasurement(localEntity) + assert.Nil(s.T(), err) + + var electricalConnection api.ElectricalConnectionServerInterface + electricalConnection, err = server.NewElectricalConnection(localEntity) + assert.Nil(s.T(), err) + + electricalConnectionId := model.ElectricalConnectionIdType(111) + constraints := make([]model.MeasurementConstraintsDataType, 0) + electricalConnection = mocks.NewElectricalConnectionServerInterface(s.T()) + electricalConnection.(*mocks.ElectricalConnectionServerInterface).EXPECT().AddParameterDescription(mock.Anything).Return(nil) + + err = mpc.configureMonitorEnergy( + measurements, + electricalConnection, + &electricalConnectionId, + &constraints, + ) + + assert.NotNil(s.T(), err) // could not add parameter description 1 + mpc.energyConfig.ValueConstraintsConsumption = nil + + err = mpc.configureMonitorEnergy( + measurements, + electricalConnection, + &electricalConnectionId, + &constraints, + ) + + assert.NotNil(s.T(), err) // could not add parameter description 2 +} + +func (s *MuMpcUsecaseSuite) Test_configureMonitorCurrent() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + mockedDevice := spineMocks.NewDeviceLocalInterface(s.T()) + mockedEvents := spineMocks.NewEventsManagerInterface(s.T()) + mockedEvents.EXPECT().Subscribe(mock.Anything).Return(nil).Maybe() + mockedDevice.EXPECT().Events().Return(mockedEvents).Maybe() + localEntity.EXPECT().Device().Return(mockedDevice) + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().DataCopy(mock.Anything).Return(nil).Maybe() + anyFeature.EXPECT().UpdateData(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + + localEntity.EXPECT().FeatureOfTypeAndRole(mock.Anything, mock.Anything).Return(anyFeature).Maybe() + + monitorPowerConfig := MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + monitorCurrentConfig := MonitorCurrentConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + mpc, err := NewMPC( + localEntity, + s.Event, + &monitorPowerConfig, + nil, + &monitorCurrentConfig, + nil, + nil, + ) + assert.Nil(s.T(), err) + + measurements, err := server.NewMeasurement(localEntity) + assert.Nil(s.T(), err) + + var electricalConnection api.ElectricalConnectionServerInterface + electricalConnection, err = server.NewElectricalConnection(localEntity) + assert.Nil(s.T(), err) + + electricalConnectionId := model.ElectricalConnectionIdType(111) + constraints := make([]model.MeasurementConstraintsDataType, 0) + electricalConnection = mocks.NewElectricalConnectionServerInterface(s.T()) + electricalConnection.(*mocks.ElectricalConnectionServerInterface).EXPECT().AddParameterDescription(mock.Anything).Return(nil).Maybe() + + constellationsToCheck := []model.ElectricalConnectionPhaseNameType{ + model.ElectricalConnectionPhaseNameTypeA, + model.ElectricalConnectionPhaseNameTypeB, + model.ElectricalConnectionPhaseNameTypeC, + } + + for _, phaseConstellation := range constellationsToCheck { + mpc.powerConfig.ConnectedPhases = phaseConstellation + + err = mpc.configureMonitorCurrent( + measurements, + electricalConnection, + &electricalConnectionId, + &constraints, + ) + + assert.NotNil(s.T(), err) // could not add parameter description + } + + // test when using phase to phase + monitorVoltageConfig := MonitorVoltageConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeAb: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + mpcInstance, err := NewMPC(s.localEntity, s.Event, &monitorPowerConfig, nil, nil, &monitorVoltageConfig, nil) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.configureMonitorVoltage(measurements, electricalConnection, &electricalConnectionId, &constraints) + assert.NotNil(s.T(), err) + +} + +func (s *MuMpcUsecaseSuite) Test_configureMonitorVoltage() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + mockedDevice := spineMocks.NewDeviceLocalInterface(s.T()) + mockedEvents := spineMocks.NewEventsManagerInterface(s.T()) + mockedEvents.EXPECT().Subscribe(mock.Anything).Return(nil).Maybe() + mockedDevice.EXPECT().Events().Return(mockedEvents).Maybe() + localEntity.EXPECT().Device().Return(mockedDevice) + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().DataCopy(mock.Anything).Return(nil).Maybe() + anyFeature.EXPECT().UpdateData(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + + localEntity.EXPECT().FeatureOfTypeAndRole(mock.Anything, mock.Anything).Return(anyFeature).Maybe() + + monitorPowerConfig := MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + monitorVoltageConfig := MonitorVoltageConfig{ + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeAb: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeBc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeAc: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + mpc, err := NewMPC( + localEntity, + s.Event, + &monitorPowerConfig, + nil, + nil, + &monitorVoltageConfig, + nil, + ) + assert.Nil(s.T(), err) + + measurements, err := server.NewMeasurement(localEntity) + assert.Nil(s.T(), err) + + var electricalConnection api.ElectricalConnectionServerInterface + electricalConnection, err = server.NewElectricalConnection(localEntity) + assert.Nil(s.T(), err) + + electricalConnectionId := model.ElectricalConnectionIdType(111) + constraints := make([]model.MeasurementConstraintsDataType, 0) + + electricalConnection = mocks.NewElectricalConnectionServerInterface(s.T()) + electricalConnection.(*mocks.ElectricalConnectionServerInterface).EXPECT().AddParameterDescription(mock.Anything).Return(nil).Maybe() + + constellationsToCheck := []model.ElectricalConnectionPhaseNameType{ + model.ElectricalConnectionPhaseNameTypeA, + model.ElectricalConnectionPhaseNameTypeB, + model.ElectricalConnectionPhaseNameTypeC, + } + + for _, phaseConstellation := range constellationsToCheck { + mpc.powerConfig.ConnectedPhases = phaseConstellation + + err = mpc.configureMonitorVoltage( + measurements, + electricalConnection, + &electricalConnectionId, + &constraints, + ) + + assert.NotNil(s.T(), err) // could not add parameter description + } +} + +func (s *MuMpcUsecaseSuite) Test_configureMonitorFrequency() { + localEntity := spineMocks.NewEntityLocalInterface(s.T()) + mockedDevice := spineMocks.NewDeviceLocalInterface(s.T()) + mockedEvents := spineMocks.NewEventsManagerInterface(s.T()) + mockedEvents.EXPECT().Subscribe(mock.Anything).Return(nil).Maybe() + mockedDevice.EXPECT().Events().Return(mockedEvents).Maybe() + localEntity.EXPECT().Device().Return(mockedDevice) + + anyFeature := spineMocks.NewFeatureLocalInterface(s.T()) + anyFeature.EXPECT().DataCopy(mock.Anything).Return(nil) + anyFeature.EXPECT().UpdateData(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + localEntity.EXPECT().FeatureOfTypeAndRole(mock.Anything, mock.Anything).Return(anyFeature) + + monitorPowerConfig := MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePerPhase: PhaseMeasurementSourceMap{ + model.ElectricalConnectionPhaseNameTypeA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + model.ElectricalConnectionPhaseNameTypeC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + } + + monitorFrequencyConfig := MonitorFrequencyConfig{ + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(100), + ValueStepSize: model.NewScaledNumberType(1), + }), + } + + mpc, err := NewMPC( + localEntity, + s.Event, + &monitorPowerConfig, + nil, + nil, + nil, + &monitorFrequencyConfig, + ) + assert.Nil(s.T(), err) + + measurements, err := server.NewMeasurement(localEntity) + assert.Nil(s.T(), err) + + var electricalConnection api.ElectricalConnectionServerInterface + electricalConnection, err = server.NewElectricalConnection(localEntity) + assert.Nil(s.T(), err) + + electricalConnectionId := model.ElectricalConnectionIdType(111) + constraints := make([]model.MeasurementConstraintsDataType, 0) + electricalConnection = mocks.NewElectricalConnectionServerInterface(s.T()) + electricalConnection.(*mocks.ElectricalConnectionServerInterface).EXPECT().AddParameterDescription(mock.Anything).Return(nil) + + err = mpc.configureMonitorFrequency( + measurements, + electricalConnection, + &electricalConnectionId, + &constraints, + ) + assert.NotNil(s.T(), err) // could not add parameter description +} + +func (s *MuMpcUsecaseSuite) TestAddFeatures() { + // Testing function AddFeatures() and cover the paths that newMeasurement will return error in it + // Covering first path when calling newMeasurement returns nil + { + s.mockedService = mocks.NewServiceInterface(s.T()) + s.mockedService.EXPECT().AddUseCase(mock.Anything).Return(nil).Maybe() + s.mockedLocalDevice = spineMocks.NewDeviceLocalInterface(s.T()) + s.mockedService.EXPECT().LocalDevice().Return(s.mockedLocalDevice).Maybe() + s.mockedLocalEntity = spineMocks.NewEntityLocalInterface(s.T()) + s.mockedLocalDevice.EXPECT().EntityForType(mock.Anything).Return(s.mockedLocalEntity).Maybe() + s.mockedLocalFeature = spineMocks.NewFeatureLocalInterface(s.T()) + s.mockedLocalEntity.EXPECT().GetOrAddFeature(mock.Anything, mock.Anything).Return(s.mockedLocalFeature).Maybe() + s.mockedLocalEntity.EXPECT().Device().Return(s.mockedLocalDevice).Maybe() + mockedEvents := spineMocks.NewEventsManagerInterface(s.T()) + mockedEvents.EXPECT().Subscribe(mock.Anything).Return(nil).Maybe() + s.mockedLocalDevice.EXPECT().Events().Return(mockedEvents).Maybe() + s.mockedLocalFeature.EXPECT().AddFunctionType(mock.Anything, mock.Anything, mock.Anything).Return().Maybe() + s.mockedLocalEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(nil).Once().Maybe() + + powerConfig := &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + mpcInstance, err := NewMPC(s.mockedLocalEntity, s.Event, powerConfig, nil, nil, nil, nil) + + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.NotNil(s.T(), err) + } + + // Test covering when calling newElectricalConnection and return error + { + s.mockedLocalEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(s.mockedLocalFeature).Maybe() + s.mockedLocalEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(nil).Once().Maybe() + + powerConfig := &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + mpcInstance, err := NewMPC(s.mockedLocalEntity, s.Event, powerConfig, nil, nil, nil, nil) + + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.NotNil(s.T(), err) + } + + // Test covering when calling GetOrAddIdForDescription Return nil + { + err := errors.New("test") + mErr := model.ErrorType{} + s.mockedElectricalConnectionFeature = mocks.NewElectricalConnectionServerInterface(s.T()) + s.mockedElectricalConnectionFeature.EXPECT().GetOrAddIdForDescription(mock.Anything).Return(nil, err).Maybe() + s.mockedLocalEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeServer).Return(s.mockedLocalFeature).Maybe() + s.mockedLocalEntity.EXPECT().FeatureOfTypeAndRole(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer).Return(s.mockedLocalFeature).Maybe() + s.mockedLocalFeature.EXPECT().UpdateData(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(util.Ptr(mErr)).Maybe() + s.mockedLocalFeature.EXPECT().DataCopy(mock.Anything).Return(util.Ptr(model.ElectricalConnectionDescriptionListDataType{})).Maybe() + powerConfig := &MonitorPowerConfig{ + ConnectedPhases: model.ElectricalConnectionPhaseNameTypeAbc, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + } + mpcInstance, err := NewMPC(s.mockedLocalEntity, s.Event, powerConfig, nil, nil, nil, nil) + + assert.Nil(s.T(), err) + assert.NotNil(s.T(), mpcInstance) + + err = mpcInstance.AddFeatures() + assert.NotNil(s.T(), err) + } +} diff --git a/usecases/usecase/events.go b/usecases/usecase/events.go index 73c62cbc..eef8137a 100644 --- a/usecases/usecase/events.go +++ b/usecases/usecase/events.go @@ -91,7 +91,7 @@ func (u *UseCaseBase) useCaseDataUpdate( } for _, entity := range entitiesToCheck { - if !slices.Contains(u.validEntityTypes, entity.EntityType()) { + if !u.allEntityTypesValid && !slices.Contains(u.validEntityTypes, entity.EntityType()) { continue } diff --git a/usecases/usecase/testhelper_test.go b/usecases/usecase/testhelper_test.go index 3579a95d..68653235 100644 --- a/usecases/usecase/testhelper_test.go +++ b/usecases/usecase/testhelper_test.go @@ -110,6 +110,7 @@ func (s *UseCaseSuite) BeforeTest(suiteName, testName string) { useCaseUpdateEvent, validActorTypes, validEntityTypes, + false, ) } diff --git a/usecases/usecase/usecase.go b/usecases/usecase/usecase.go index 1d388866..2cd425a1 100644 --- a/usecases/usecase/usecase.go +++ b/usecases/usecase/usecase.go @@ -24,8 +24,9 @@ type UseCaseBase struct { availableEntityScenarios []api.RemoteEntityScenarios // map of scenarios and their availability for each compatible remote entity - validActorTypes []model.UseCaseActorType // valid remote actor types for this use case - validEntityTypes []model.EntityTypeType // valid remote entity types for this use case + validActorTypes []model.UseCaseActorType // valid remote actor types for this use case + validEntityTypes []model.EntityTypeType // valid remote entity types for this use case + allEntityTypesValid bool mux sync.Mutex } @@ -56,6 +57,7 @@ func NewUseCaseBase( useCaseUpdateEvent api.EventType, validActorTypes []model.UseCaseActorType, validEntityTypes []model.EntityTypeType, + allEntityTypesValid bool, ) *UseCaseBase { ucb := &UseCaseBase{ LocalEntity: localEntity, @@ -68,6 +70,7 @@ func NewUseCaseBase( useCaseUpdateEvent: useCaseUpdateEvent, validActorTypes: validActorTypes, validEntityTypes: validEntityTypes, + allEntityTypesValid: allEntityTypesValid, } _ = localEntity.Device().Events().Subscribe(ucb) @@ -113,6 +116,10 @@ func (u *UseCaseBase) IsCompatibleEntityType(entity spineapi.EntityRemoteInterfa return false } + if u.allEntityTypesValid { + return true + } + return slices.Contains(u.validEntityTypes, entity.EntityType()) } diff --git a/usecases/usecase/usecase_test.go b/usecases/usecase/usecase_test.go index 58a4a524..299963f2 100644 --- a/usecases/usecase/usecase_test.go +++ b/usecases/usecase/usecase_test.go @@ -23,6 +23,10 @@ func (s *UseCaseSuite) Test() { result = s.uc.IsCompatibleEntityType(payload.Entity) assert.True(s.T(), result) + s.uc.allEntityTypesValid = true + result = s.uc.IsCompatibleEntityType(payload.Entity) + assert.True(s.T(), result) + usecaseFilter := model.UseCaseFilterType{ Actor: useCaseActor, UseCaseName: useCaseName,