Skip to content

Commit 83d3c15

Browse files
Implement MPC usecase for the MonitoredUnit actor type
Co-authored-by: Simon Thelen <[email protected]>
1 parent bbc6418 commit 83d3c15

File tree

10 files changed

+778
-2
lines changed

10 files changed

+778
-2
lines changed

usecases/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,8 @@ Actors:
3434
Use Cases:
3535
- `mpc`: Monitoring of Power Consumption
3636
- `mgcp`: Monitoring of Grid Connection Point
37+
38+
- `mu`: Monitored Unit
39+
40+
Use Cases:
41+
- `mpc`: Monitoring of Power Consumption

usecases/api/mu_mpc.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package api
2+
3+
import (
4+
"github.com/enbility/eebus-go/api"
5+
)
6+
7+
// Actor: Monitored Unit
8+
// UseCase: Monitoring of Power Consumption
9+
type MuMPCInterface interface {
10+
api.UseCaseInterface
11+
12+
// Scenario 1
13+
14+
// set the momentary active power consumption or production
15+
//
16+
// parameters:
17+
// - power: the active power
18+
SetPower(power float64) error
19+
20+
// set the momentary active phase specific power consumption or production per phase
21+
//
22+
// parameters:
23+
// - phaseA: the active power of phase A
24+
// - phaseB: the active power of phase B
25+
// - phaseC: the active power of phase C
26+
SetPowerPerPhase(phaseA, phaseB, phaseC float64) error
27+
28+
// Scenario 2
29+
30+
// set the total consumption energy
31+
//
32+
// parameters:
33+
// - consumed: the total consumption energy
34+
SetEnergyConsumed(consumed float64) error
35+
36+
// set the total feed in energy
37+
//
38+
// parameters:
39+
// - produced: the total feed in energy
40+
SetEnergyProduced(produced float64) error
41+
42+
// Scenario 3
43+
44+
// set the momentary phase specific current consumption or production
45+
//
46+
// parameters:
47+
// - phaseA: the current of phase A
48+
// - phaseB: the current of phase B
49+
// - phaseC: the current of phase C
50+
SetCurrentPerPhase(phaseA, phaseB, phaseC float64) error
51+
52+
// Scenario 4
53+
54+
// set the phase specific voltage details
55+
//
56+
// parameters:
57+
// - phaseA: the voltage of phase A
58+
// - phaseB: the voltage of phase B
59+
// - phaseC: the voltage of phase C
60+
SetVoltagePerPhase(phaseA, phaseB, phaseC float64) error
61+
62+
// Scenario 5
63+
64+
// set frequency
65+
//
66+
// parameters:
67+
// - frequency: the frequency
68+
SetFrequency(frequency float64) error
69+
}

usecases/mu/mpc/events.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package mpc
2+
3+
import (
4+
spineapi "github.com/enbility/spine-go/api"
5+
)
6+
7+
// handle SPINE events
8+
func (e *MPC) HandleEvent(payload spineapi.EventPayload) {
9+
// No events supported
10+
}

usecases/mu/mpc/public.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package mpc
2+
3+
import (
4+
"github.com/enbility/eebus-go/api"
5+
)
6+
7+
// Scenario 1
8+
9+
// set the momentary active power consumption or production
10+
//
11+
// possible errors:
12+
// - ErrMissingData if the id is not available
13+
// - and others
14+
func (e *MPC) SetPower(power float64) error {
15+
if e.acPowerTotal == nil {
16+
return api.ErrMissingData
17+
}
18+
19+
err := e.setMeasurementDataForId(e.acPowerTotal, power)
20+
if err != nil {
21+
return err
22+
}
23+
24+
return nil
25+
}
26+
27+
// set the momentary active power consumption or production per phase
28+
//
29+
// possible errors:
30+
// - ErrMissingData if the id is not available
31+
// - and others
32+
func (e *MPC) SetPowerPerPhase(phaseA, phaseB, phaseC float64) error {
33+
if e.acPower[0] == nil || e.acPower[1] == nil || e.acPower[2] == nil {
34+
return api.ErrMissingData
35+
}
36+
37+
err := e.setMeasurementDataForId(e.acPower[0], phaseA)
38+
if err != nil {
39+
return err
40+
}
41+
42+
err = e.setMeasurementDataForId(e.acPower[1], phaseB)
43+
if err != nil {
44+
return err
45+
}
46+
47+
err = e.setMeasurementDataForId(e.acPower[2], phaseC)
48+
if err != nil {
49+
return err
50+
}
51+
52+
return nil
53+
}
54+
55+
// Scenario 2
56+
57+
// set the total consumption energy
58+
//
59+
// - positive values are used for consumption
60+
func (e *MPC) SetEnergyConsumed(energy float64) error {
61+
if e.acEnergyConsumed == nil {
62+
return api.ErrMissingData
63+
}
64+
65+
err := e.setMeasurementDataForId(e.acEnergyConsumed, energy)
66+
if err != nil {
67+
return err
68+
}
69+
70+
return nil
71+
}
72+
73+
// set the total feed in energy
74+
//
75+
// - negative values are used for production
76+
func (e *MPC) SetEnergyProduced(energy float64) error {
77+
if e.acEnergyProduced == nil {
78+
return api.ErrMissingData
79+
}
80+
81+
err := e.setMeasurementDataForId(e.acEnergyProduced, energy)
82+
if err != nil {
83+
return err
84+
}
85+
86+
return nil
87+
}
88+
89+
// Scenario 3
90+
91+
// set the momentary phase specific current consumption or production
92+
//
93+
// - positive values are used for consumption
94+
// - negative values are used for production
95+
func (e *MPC) SetCurrentPerPhase(phaseA, phaseB, phaseC float64) error {
96+
if e.acCurrent[0] == nil || e.acCurrent[1] == nil || e.acCurrent[2] == nil {
97+
return api.ErrMissingData
98+
}
99+
100+
err := e.setMeasurementDataForId(e.acCurrent[0], phaseA)
101+
if err != nil {
102+
return err
103+
}
104+
105+
err = e.setMeasurementDataForId(e.acCurrent[1], phaseB)
106+
if err != nil {
107+
return err
108+
}
109+
110+
err = e.setMeasurementDataForId(e.acCurrent[2], phaseC)
111+
if err != nil {
112+
return err
113+
}
114+
115+
return nil
116+
}
117+
118+
// Scenario 4
119+
120+
// set the phase specific voltage details
121+
func (e *MPC) SetVoltagePerPhase(phaseA, phaseB, phaseC float64) error {
122+
for _, id := range e.acVoltage {
123+
if id == nil {
124+
return api.ErrMissingData
125+
}
126+
}
127+
128+
err := e.setMeasurementDataForId(e.acVoltage[0], phaseA)
129+
if err != nil {
130+
return err
131+
}
132+
133+
err = e.setMeasurementDataForId(e.acVoltage[1], phaseB)
134+
if err != nil {
135+
return err
136+
}
137+
138+
err = e.setMeasurementDataForId(e.acVoltage[2], phaseC)
139+
if err != nil {
140+
return err
141+
}
142+
143+
return nil
144+
}
145+
146+
// Scenario 5
147+
148+
// SetFrequency set frequency
149+
func (e *MPC) SetFrequency(frequency float64) error {
150+
if e.acFrequency == nil {
151+
return api.ErrMissingData
152+
}
153+
154+
err := e.setMeasurementDataForId(e.acFrequency, frequency)
155+
if err != nil {
156+
return err
157+
}
158+
159+
return nil
160+
}

usecases/mu/mpc/public_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package mpc
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
)
6+
7+
func (s *MuMPCSuite) Test_Power() {
8+
err := s.sut.SetPower(5.0)
9+
assert.Nil(s.T(), err)
10+
}
11+
12+
func (s *MuMPCSuite) Test_PowerPerPhase() {
13+
err := s.sut.SetPowerPerPhase(5.0, 5.0, 5.0)
14+
assert.Nil(s.T(), err)
15+
}
16+
17+
func (s *MuMPCSuite) Test_EnergyConsumed() {
18+
err := s.sut.SetEnergyConsumed(5.0)
19+
assert.Nil(s.T(), err)
20+
}
21+
22+
func (s *MuMPCSuite) Test_EnergyProduced() {
23+
err := s.sut.SetEnergyProduced(5.0)
24+
assert.Nil(s.T(), err)
25+
}
26+
27+
func (s *MuMPCSuite) Test_CurrentPerPhase() {
28+
err := s.sut.SetCurrentPerPhase(5.0, 5.0, 5.0)
29+
assert.Nil(s.T(), err)
30+
}
31+
32+
func (s *MuMPCSuite) Test_VoltagePerPhase() {
33+
err := s.sut.SetVoltagePerPhase(5.0, 5.0, 5.0)
34+
assert.Nil(s.T(), err)
35+
}
36+
37+
func (s *MuMPCSuite) Test_Frequency() {
38+
err := s.sut.SetFrequency(5.0)
39+
assert.Nil(s.T(), err)
40+
}

usecases/mu/mpc/testhelper_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package mpc
2+
3+
import (
4+
"github.com/enbility/eebus-go/api"
5+
"github.com/enbility/eebus-go/mocks"
6+
"github.com/enbility/eebus-go/service"
7+
"github.com/enbility/ship-go/cert"
8+
spineapi "github.com/enbility/spine-go/api"
9+
spinemocks "github.com/enbility/spine-go/mocks"
10+
"github.com/enbility/spine-go/model"
11+
"github.com/stretchr/testify/mock"
12+
"github.com/stretchr/testify/suite"
13+
"testing"
14+
"time"
15+
)
16+
17+
const remoteSki string = "testremoteski"
18+
19+
func TestMuMPCSuite(t *testing.T) {
20+
suite.Run(t, new(MuMPCSuite))
21+
}
22+
23+
type MuMPCSuite struct {
24+
suite.Suite
25+
26+
sut *MPC
27+
28+
service api.ServiceInterface
29+
30+
remoteDevice spineapi.DeviceRemoteInterface
31+
mockRemoteEntity *spinemocks.EntityRemoteInterface
32+
monitoredEntity spineapi.EntityRemoteInterface
33+
loadControlFeature,
34+
deviceDiagnosisFeature,
35+
deviceConfigurationFeature spineapi.FeatureLocalInterface
36+
37+
eventCalled bool
38+
}
39+
40+
func (s *MuMPCSuite) Event(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) {
41+
s.eventCalled = true
42+
}
43+
44+
func (s *MuMPCSuite) BeforeTest(suiteName, testName string) {
45+
s.eventCalled = false
46+
cert, _ := cert.CreateCertificate("test", "test", "DE", "test")
47+
configuration, _ := api.NewConfiguration(
48+
"test", "test", "test", "test",
49+
model.DeviceTypeTypeEnergyManagementSystem,
50+
[]model.EntityTypeType{model.EntityTypeTypeInverter},
51+
9999, cert, time.Second*4)
52+
53+
serviceHandler := mocks.NewServiceReaderInterface(s.T())
54+
serviceHandler.EXPECT().ServicePairingDetailUpdate(mock.Anything, mock.Anything).Return().Maybe()
55+
56+
s.service = service.NewService(configuration, serviceHandler)
57+
_ = s.service.Setup()
58+
59+
mockRemoteDevice := spinemocks.NewDeviceRemoteInterface(s.T())
60+
s.mockRemoteEntity = spinemocks.NewEntityRemoteInterface(s.T())
61+
mockRemoteFeature := spinemocks.NewFeatureRemoteInterface(s.T())
62+
mockRemoteDevice.EXPECT().FeatureByEntityTypeAndRole(mock.Anything, mock.Anything, mock.Anything).Return(mockRemoteFeature).Maybe()
63+
mockRemoteDevice.EXPECT().Ski().Return(remoteSki).Maybe()
64+
s.mockRemoteEntity.EXPECT().Device().Return(mockRemoteDevice).Maybe()
65+
s.mockRemoteEntity.EXPECT().EntityType().Return(mock.Anything).Maybe()
66+
entityAddress := &model.EntityAddressType{}
67+
s.mockRemoteEntity.EXPECT().Address().Return(entityAddress).Maybe()
68+
mockRemoteFeature.EXPECT().DataCopy(mock.Anything).Return(mock.Anything).Maybe()
69+
mockRemoteFeature.EXPECT().Address().Return(&model.FeatureAddressType{}).Maybe()
70+
mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe()
71+
72+
localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter)
73+
s.sut = NewMPC(localEntity, s.Event)
74+
s.sut.AddFeatures()
75+
s.sut.AddUseCase()
76+
77+
//s.remoteDevice, s.monitoredEntity = setupDevices(s.service, s.T())
78+
}

usecases/mu/mpc/types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package mpc
2+
3+
import "github.com/enbility/eebus-go/api"
4+
5+
const (
6+
// Update of the list of remote entities supporting the Use Case
7+
//
8+
// Use `RemoteEntities` to get the current data
9+
UseCaseSupportUpdate api.EventType = "mu-mpc-UseCaseSupportUpdate"
10+
)

0 commit comments

Comments
 (0)