Skip to content

Commit

Permalink
Implement MPC usecase for the MonitoredUnit actor type
Browse files Browse the repository at this point in the history
Co-authored-by: Simon Thelen <[email protected]>
  • Loading branch information
nils-prommersberger and sthelen-enqs committed Aug 29, 2024
1 parent bbc6418 commit 83d3c15
Show file tree
Hide file tree
Showing 10 changed files with 778 additions and 2 deletions.
5 changes: 5 additions & 0 deletions usecases/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@ Actors:
Use Cases:
- `mpc`: Monitoring of Power Consumption
- `mgcp`: Monitoring of Grid Connection Point

- `mu`: Monitored Unit

Use Cases:
- `mpc`: Monitoring of Power Consumption
69 changes: 69 additions & 0 deletions usecases/api/mu_mpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package api

import (
"github.com/enbility/eebus-go/api"
)

// Actor: Monitored Unit
// UseCase: Monitoring of Power Consumption
type MuMPCInterface interface {
api.UseCaseInterface

// Scenario 1

// set the momentary active power consumption or production
//
// parameters:
// - power: the active power
SetPower(power float64) error

// set the momentary active phase specific power consumption or production per phase
//
// parameters:
// - phaseA: the active power of phase A
// - phaseB: the active power of phase B
// - phaseC: the active power of phase C
SetPowerPerPhase(phaseA, phaseB, phaseC float64) error

// Scenario 2

// set the total consumption energy
//
// parameters:
// - consumed: the total consumption energy
SetEnergyConsumed(consumed float64) error

// set the total feed in energy
//
// parameters:
// - produced: the total feed in energy
SetEnergyProduced(produced float64) error

// Scenario 3

// set the momentary phase specific current consumption or production
//
// parameters:
// - phaseA: the current of phase A
// - phaseB: the current of phase B
// - phaseC: the current of phase C
SetCurrentPerPhase(phaseA, phaseB, phaseC float64) error

// Scenario 4

// set the phase specific voltage details
//
// parameters:
// - phaseA: the voltage of phase A
// - phaseB: the voltage of phase B
// - phaseC: the voltage of phase C
SetVoltagePerPhase(phaseA, phaseB, phaseC float64) error

// Scenario 5

// set frequency
//
// parameters:
// - frequency: the frequency
SetFrequency(frequency float64) error
}
10 changes: 10 additions & 0 deletions usecases/mu/mpc/events.go
Original file line number Diff line number Diff line change
@@ -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 supported
}
160 changes: 160 additions & 0 deletions usecases/mu/mpc/public.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package mpc

import (
"github.com/enbility/eebus-go/api"
)

// Scenario 1

// set the momentary active power consumption or production
//
// possible errors:
// - ErrMissingData if the id is not available
// - and others
func (e *MPC) SetPower(power float64) error {
if e.acPowerTotal == nil {
return api.ErrMissingData
}

err := e.setMeasurementDataForId(e.acPowerTotal, power)
if err != nil {
return err
}

return nil
}

// set the momentary active power consumption or production per phase
//
// possible errors:
// - ErrMissingData if the id is not available
// - and others
func (e *MPC) SetPowerPerPhase(phaseA, phaseB, phaseC float64) error {
if e.acPower[0] == nil || e.acPower[1] == nil || e.acPower[2] == nil {
return api.ErrMissingData
}

err := e.setMeasurementDataForId(e.acPower[0], phaseA)
if err != nil {
return err
}

err = e.setMeasurementDataForId(e.acPower[1], phaseB)
if err != nil {
return err
}

err = e.setMeasurementDataForId(e.acPower[2], phaseC)
if err != nil {
return err
}

return nil
}

// Scenario 2

// set the total consumption energy
//
// - positive values are used for consumption
func (e *MPC) SetEnergyConsumed(energy float64) error {
if e.acEnergyConsumed == nil {
return api.ErrMissingData
}

err := e.setMeasurementDataForId(e.acEnergyConsumed, energy)
if err != nil {
return err
}

return nil
}

// set the total feed in energy
//
// - negative values are used for production
func (e *MPC) SetEnergyProduced(energy float64) error {
if e.acEnergyProduced == nil {
return api.ErrMissingData
}

err := e.setMeasurementDataForId(e.acEnergyProduced, energy)
if err != nil {
return err
}

return nil
}

// Scenario 3

// set the momentary phase specific current consumption or production
//
// - positive values are used for consumption
// - negative values are used for production
func (e *MPC) SetCurrentPerPhase(phaseA, phaseB, phaseC float64) error {
if e.acCurrent[0] == nil || e.acCurrent[1] == nil || e.acCurrent[2] == nil {
return api.ErrMissingData
}

err := e.setMeasurementDataForId(e.acCurrent[0], phaseA)
if err != nil {
return err
}

err = e.setMeasurementDataForId(e.acCurrent[1], phaseB)
if err != nil {
return err
}

err = e.setMeasurementDataForId(e.acCurrent[2], phaseC)
if err != nil {
return err
}

return nil
}

// Scenario 4

// set the phase specific voltage details
func (e *MPC) SetVoltagePerPhase(phaseA, phaseB, phaseC float64) error {
for _, id := range e.acVoltage {
if id == nil {
return api.ErrMissingData
}
}

err := e.setMeasurementDataForId(e.acVoltage[0], phaseA)
if err != nil {
return err
}

err = e.setMeasurementDataForId(e.acVoltage[1], phaseB)
if err != nil {
return err
}

err = e.setMeasurementDataForId(e.acVoltage[2], phaseC)
if err != nil {
return err
}

return nil
}

// Scenario 5

// SetFrequency set frequency
func (e *MPC) SetFrequency(frequency float64) error {
if e.acFrequency == nil {
return api.ErrMissingData
}

err := e.setMeasurementDataForId(e.acFrequency, frequency)
if err != nil {
return err
}

return nil
}
40 changes: 40 additions & 0 deletions usecases/mu/mpc/public_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package mpc

import (
"github.com/stretchr/testify/assert"
)

func (s *MuMPCSuite) Test_Power() {
err := s.sut.SetPower(5.0)
assert.Nil(s.T(), err)
}

func (s *MuMPCSuite) Test_PowerPerPhase() {
err := s.sut.SetPowerPerPhase(5.0, 5.0, 5.0)
assert.Nil(s.T(), err)
}

func (s *MuMPCSuite) Test_EnergyConsumed() {
err := s.sut.SetEnergyConsumed(5.0)
assert.Nil(s.T(), err)
}

func (s *MuMPCSuite) Test_EnergyProduced() {
err := s.sut.SetEnergyProduced(5.0)
assert.Nil(s.T(), err)
}

func (s *MuMPCSuite) Test_CurrentPerPhase() {
err := s.sut.SetCurrentPerPhase(5.0, 5.0, 5.0)
assert.Nil(s.T(), err)
}

func (s *MuMPCSuite) Test_VoltagePerPhase() {
err := s.sut.SetVoltagePerPhase(5.0, 5.0, 5.0)
assert.Nil(s.T(), err)
}

func (s *MuMPCSuite) Test_Frequency() {
err := s.sut.SetFrequency(5.0)
assert.Nil(s.T(), err)
}
78 changes: 78 additions & 0 deletions usecases/mu/mpc/testhelper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package mpc

import (
"github.com/enbility/eebus-go/api"
"github.com/enbility/eebus-go/mocks"
"github.com/enbility/eebus-go/service"
"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/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"testing"
"time"
)

const remoteSki string = "testremoteski"

func TestMuMPCSuite(t *testing.T) {
suite.Run(t, new(MuMPCSuite))
}

type MuMPCSuite struct {
suite.Suite

sut *MPC

service api.ServiceInterface

remoteDevice spineapi.DeviceRemoteInterface
mockRemoteEntity *spinemocks.EntityRemoteInterface
monitoredEntity spineapi.EntityRemoteInterface
loadControlFeature,
deviceDiagnosisFeature,
deviceConfigurationFeature spineapi.FeatureLocalInterface

eventCalled bool
}

func (s *MuMPCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) {
s.eventCalled = true
}

func (s *MuMPCSuite) BeforeTest(suiteName, testName string) {
s.eventCalled = false
cert, _ := cert.CreateCertificate("test", "test", "DE", "test")
configuration, _ := api.NewConfiguration(
"test", "test", "test", "test",
model.DeviceTypeTypeEnergyManagementSystem,
[]model.EntityTypeType{model.EntityTypeTypeInverter},
9999, cert, time.Second*4)

serviceHandler := mocks.NewServiceReaderInterface(s.T())
serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe()

s.service = service.NewService(configuration, serviceHandler)
_ = s.service.Setup()

mockRemoteDevice := spinemocks.NewDeviceRemoteInterface(s.T())
s.mockRemoteEntity = spinemocks.NewEntityRemoteInterface(s.T())
mockRemoteFeature := spinemocks.NewFeatureRemoteInterface(s.T())
mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe()
mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe()
s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe()
s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe()
entityAddress := &model.EntityAddressType{}
s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe()
mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe()
mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe()
mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe()

localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter)
s.sut = NewMPC(localEntity, s.Event)
s.sut.AddFeatures()
s.sut.AddUseCase()

//s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T())
}
10 changes: 10 additions & 0 deletions usecases/mu/mpc/types.go
Original file line number Diff line number Diff line change
@@ -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"
)
Loading

0 comments on commit 83d3c15

Please sign in to comment.