Skip to content

Commit 6eb1154

Browse files
committed
Enable differentiation between unset phase values and 0 phase values
- changes return type of MeasurementPhaseSpecificDataForFilter to a map from PhaseName to value instead of a list
1 parent 9f94f4f commit 6eb1154

File tree

8 files changed

+147
-54
lines changed

8 files changed

+147
-54
lines changed

usecases/api/ma_mgcp.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"github.com/enbility/eebus-go/api"
55
spineapi "github.com/enbility/spine-go/api"
6+
"github.com/enbility/spine-go/model"
67
)
78

89
// Actor: Monitoring Appliance
@@ -66,15 +67,15 @@ type MaMGCPInterface interface {
6667
// return values:
6768
// - positive values are used for consumption
6869
// - negative values are used for production
69-
CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
70+
CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)
7071

7172
// Scenario 6
7273

7374
// return the voltage phase details at the grid connection point
7475
//
7576
// parameters:
7677
// - entity: the entity of the device (e.g. SMGW)
77-
VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
78+
VoltagePerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)
7879

7980
// Scenario 7
8081

usecases/api/ma_mpc.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"github.com/enbility/eebus-go/api"
55
spineapi "github.com/enbility/spine-go/api"
6+
"github.com/enbility/spine-go/model"
67
)
78

89
// Actor: Monitoring Appliance
@@ -30,7 +31,7 @@ type MaMPCInterface interface {
3031
// possible errors:
3132
// - ErrDataNotAvailable if no such limit is (yet) available
3233
// - and others
33-
PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
34+
PowerPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)
3435

3536
// Scenario 2
3637

@@ -61,15 +62,15 @@ type MaMPCInterface interface {
6162
// return values
6263
// - positive values are used for consumption
6364
// - negative values are used for production
64-
CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
65+
CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)
6566

6667
// Scenario 4
6768

6869
// return the phase specific voltage details
6970
//
7071
// parameters:
7172
// - entity: the entity of the device (e.g. EVSE)
72-
VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
73+
VoltagePerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)
7374

7475
// Scenario 5
7576

usecases/internal/measurement.go

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func MeasurementPhaseSpecificDataForFilter(
1616
measurementFilter model.MeasurementDescriptionDataType,
1717
energyDirection model.EnergyDirectionType,
1818
validPhaseNameTypes []model.ElectricalConnectionPhaseNameType,
19-
) ([]float64, error) {
19+
) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
2020
measurement, err := client.NewMeasurement(localEntity, remoteEntity)
2121
electricalConnection, err1 := client.NewElectricalConnection(localEntity, remoteEntity)
2222
if err != nil || err1 != nil {
@@ -28,34 +28,28 @@ func MeasurementPhaseSpecificDataForFilter(
2828
return nil, api.ErrDataNotAvailable
2929
}
3030

31-
var result []float64
32-
if validPhaseNameTypes != nil {
33-
// pre-allocate result array for each possible phase so we can add phases to it in arbitrary order
34-
result = make([]float64, len(validPhaseNameTypes))
35-
}
31+
result := make(map[model.ElectricalConnectionPhaseNameType]float64, len(validPhaseNameTypes))
3632

3733
for _, item := range data {
3834
if item.Value == nil || item.MeasurementId == nil {
3935
continue
4036
}
4137

42-
phaseIndex := -1
43-
if validPhaseNameTypes != nil {
44-
filter := model.ElectricalConnectionParameterDescriptionDataType{
45-
MeasurementId: item.MeasurementId,
46-
}
47-
param, err := electricalConnection.GetParameterDescriptionsForFilter(filter)
48-
if err != nil || len(param) == 0 || param[0].AcMeasuredPhases == nil {
49-
// error getting parameter description
50-
continue
51-
}
38+
filter := model.ElectricalConnectionParameterDescriptionDataType{
39+
MeasurementId: item.MeasurementId,
40+
}
41+
param, err := electricalConnection.GetParameterDescriptionsForFilter(filter)
42+
if err != nil || len(param) == 0 || param[0].AcMeasuredPhases == nil {
43+
// error getting parameter description
44+
continue
45+
}
5246

53-
// calculate the offset into result for the measured phase
54-
phaseIndex = slices.Index(validPhaseNameTypes, *param[0].AcMeasuredPhases)
55-
if phaseIndex == -1 {
56-
// ignore phase measurements not specified in validPhaseNameTypes
57-
continue
58-
}
47+
// calculate the offset into result for the measured phase
48+
phaseName := *param[0].AcMeasuredPhases
49+
if validPhaseNameTypes != nil &&
50+
!slices.Contains(validPhaseNameTypes, phaseName) {
51+
// ignore phase measurements not specified in validPhaseNameTypes
52+
continue
5953
}
6054

6155
if energyDirection != "" {
@@ -81,13 +75,7 @@ func MeasurementPhaseSpecificDataForFilter(
8175

8276
value := item.Value.GetValue()
8377

84-
if validPhaseNameTypes == nil {
85-
// measurement is not for a specific phase
86-
result = append(result, value)
87-
} else {
88-
// measurement is for a specific phase, store the value at the corresponding phaseIndex
89-
result[phaseIndex] = value
90-
}
78+
result[phaseName] = value
9179
}
9280

9381
return result, nil

usecases/internal/measurement_test.go

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (s *InternalSuite) Test_MeasurementPhaseSpecificDataForFilter() {
114114
ucapi.PhaseNameMapping,
115115
)
116116
assert.Nil(s.T(), err)
117-
assert.Equal(s.T(), []float64{0, 0, 0}, data)
117+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{}, data)
118118

119119
elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
120120
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
@@ -160,7 +160,7 @@ func (s *InternalSuite) Test_MeasurementPhaseSpecificDataForFilter() {
160160
ucapi.PhaseNameMapping,
161161
)
162162
assert.Nil(s.T(), err)
163-
assert.Equal(s.T(), []float64{10, 10, 10}, data)
163+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"a": 10, "b": 10, "c": 10}, data)
164164

165165
measData = &model.MeasurementListDataType{
166166
MeasurementData: []model.MeasurementDataType{
@@ -272,6 +272,95 @@ func (s *InternalSuite) Test_MeasurementSinglePhaseSpecificDataForFilter() {
272272
ucapi.PhaseNameMapping,
273273
)
274274
assert.Nil(s.T(), err)
275-
assert.Equal(s.T(), []float64{0, 10, 0}, data)
275+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"b": 10}, data)
276+
277+
data, err = MeasurementPhaseSpecificDataForFilter(
278+
s.localEntity,
279+
s.monitoredEntity,
280+
filter,
281+
energyDirection,
282+
nil,
283+
)
284+
assert.Nil(s.T(), err)
285+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"b": 10}, data)
286+
287+
}
288+
289+
func (s *InternalSuite) Test_MeasurementTotalPhaseSpecificDataForFilter() {
290+
measurementType := model.MeasurementTypeTypePower
291+
commodityType := model.CommodityTypeTypeElectricity
292+
scopeType := model.ScopeTypeTypeACPowerTotal
293+
energyDirection := model.EnergyDirectionTypeConsume
294+
295+
filter := model.MeasurementDescriptionDataType{
296+
MeasurementType: &measurementType,
297+
CommodityType: &commodityType,
298+
ScopeType: &scopeType,
299+
}
300+
301+
// set up ElectricalConnection
302+
elDescData := &model.ElectricalConnectionDescriptionListDataType{
303+
ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{
304+
{
305+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
306+
PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume),
307+
},
308+
},
309+
}
310+
311+
elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
312+
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
313+
{
314+
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
315+
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
316+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc),
317+
},
318+
},
319+
}
320+
321+
rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer)
322+
323+
_, fErr := rElFeature.UpdateData(true, model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil)
324+
assert.Nil(s.T(), fErr)
325+
326+
_, fErr = rElFeature.UpdateData(true, model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil)
327+
assert.Nil(s.T(), fErr)
328+
329+
descData := &model.MeasurementDescriptionListDataType{
330+
MeasurementDescriptionData: []model.MeasurementDescriptionDataType{
331+
{
332+
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
333+
MeasurementType: &measurementType,
334+
CommodityType: &commodityType,
335+
ScopeType: &scopeType,
336+
},
337+
},
338+
}
339+
340+
rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer)
341+
_, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil)
342+
assert.Nil(s.T(), fErr)
343+
344+
measData := &model.MeasurementListDataType{
345+
MeasurementData: []model.MeasurementDataType{
346+
{
347+
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
348+
Value: model.NewScaledNumberType(10),
349+
},
350+
},
351+
}
352+
353+
_, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementListData, measData, nil, nil)
354+
assert.Nil(s.T(), fErr)
355+
356+
data, err := MeasurementPhaseSpecificDataForFilter(
357+
s.localEntity,
358+
s.monitoredEntity,
359+
filter,
360+
energyDirection,
361+
nil,
362+
)
363+
assert.Nil(s.T(), err)
364+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"abc": 10}, data)
276365

277366
}

usecases/ma/mgcp/public.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,13 @@ func (e *MGCP) Power(entity spineapi.EntityRemoteInterface) (float64, error) {
7878
return 0, api.ErrDataNotAvailable
7979
}
8080

81-
return data[0], nil
81+
for _, k := range data {
82+
// If the Monitored Unit is connected to less than three phases, one of the other combinations like "a" or "ab" are allowed instead of "abc".
83+
// The values "a", "b", and "c" are permitted if and only if only one
84+
return k, nil
85+
}
86+
// unreachable
87+
return 0, api.ErrDataNotAvailable
8288
}
8389

8490
// Scenario 3
@@ -170,7 +176,7 @@ func (e *MGCP) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, e
170176
// - ErrDataNotAvailable if no such value is (yet) available
171177
// - ErrDataInvalid if the currently available data is invalid and should be ignored
172178
// - and others
173-
func (e *MGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
179+
func (e *MGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
174180
if !e.IsCompatibleEntityType(entity) {
175181
return nil, api.ErrNoCompatibleEntity
176182
}
@@ -191,7 +197,7 @@ func (e *MGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64
191197
// - ErrDataNotAvailable if no such value is (yet) available
192198
// - ErrDataInvalid if the currently available data is invalid and should be ignored
193199
// - and others
194-
func (e *MGCP) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
200+
func (e *MGCP) VoltagePerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
195201
if !e.IsCompatibleEntityType(entity) {
196202
return nil, api.ErrNoCompatibleEntity
197203
}

usecases/ma/mgcp/public_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ func (s *GcpMGCPSuite) Test_Power() {
118118
{
119119
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
120120
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
121+
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA),
121122
},
122123
},
123124
}
@@ -316,7 +317,7 @@ func (s *GcpMGCPSuite) Test_CurrentPerPhase() {
316317

317318
data, err = s.sut.CurrentPerPhase(s.smgwEntity)
318319
assert.Nil(s.T(), err)
319-
assert.Equal(s.T(), []float64{0, 0, 0}, data)
320+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{}, data)
320321

321322
elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
322323
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
@@ -356,7 +357,7 @@ func (s *GcpMGCPSuite) Test_CurrentPerPhase() {
356357

357358
data, err = s.sut.CurrentPerPhase(s.smgwEntity)
358359
assert.Nil(s.T(), err)
359-
assert.Equal(s.T(), []float64{10, 10, 10}, data)
360+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"a": 10, "b": 10, "c": 10}, data)
360361
}
361362

362363
func (s *GcpMGCPSuite) Test_VoltagePerPhase() {
@@ -421,7 +422,7 @@ func (s *GcpMGCPSuite) Test_VoltagePerPhase() {
421422

422423
data, err = s.sut.VoltagePerPhase(s.smgwEntity)
423424
assert.Nil(s.T(), err)
424-
assert.Equal(s.T(), []float64{0, 0, 0}, data)
425+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{}, data)
425426

426427
elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
427428
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
@@ -449,7 +450,7 @@ func (s *GcpMGCPSuite) Test_VoltagePerPhase() {
449450

450451
data, err = s.sut.VoltagePerPhase(s.smgwEntity)
451452
assert.Nil(s.T(), err)
452-
assert.Equal(s.T(), []float64{230, 230, 230}, data)
453+
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"a": 230, "b": 230, "c": 230}, data)
453454
}
454455

455456
func (s *GcpMGCPSuite) Test_Frequency() {

usecases/ma/mpc/public.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ func (e *MPC) Power(entity spineapi.EntityRemoteInterface) (float64, error) {
3636
return 0, api.ErrDataNotAvailable
3737
}
3838

39-
return values[0], nil
39+
for _, k := range values {
40+
// If the Monitored Unit is connected to less than three phases, one of the other combinations like "a" or "ab" are allowed instead of "abc".
41+
// The values "a", "b", and "c" are permitted if and only if only one
42+
return k, nil
43+
}
44+
// unreachable
45+
return 0, api.ErrDataNotAvailable
4046
}
4147

4248
// return the momentary active phase specific power consumption or production per phase
@@ -45,7 +51,7 @@ func (e *MPC) Power(entity spineapi.EntityRemoteInterface) (float64, error) {
4551
// - ErrDataNotAvailable if no such value is (yet) available
4652
// - ErrDataInvalid if the currently available data is invalid and should be ignored
4753
// - and others
48-
func (e *MPC) PowerPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
54+
func (e *MPC) PowerPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
4955
if !e.IsCompatibleEntityType(entity) {
5056
return nil, api.ErrNoCompatibleEntity
5157
}
@@ -157,7 +163,7 @@ func (e *MPC) EnergyProduced(entity spineapi.EntityRemoteInterface) (float64, er
157163
// - ErrDataNotAvailable if no such value is (yet) available
158164
// - ErrDataInvalid if the currently available data is invalid and should be ignored
159165
// - and others
160-
func (e *MPC) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
166+
func (e *MPC) CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
161167
if !e.IsCompatibleEntityType(entity) {
162168
return nil, api.ErrNoCompatibleEntity
163169
}
@@ -178,7 +184,7 @@ func (e *MPC) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64,
178184
// - ErrDataNotAvailable if no such value is (yet) available
179185
// - ErrDataInvalid if the currently available data is invalid and should be ignored
180186
// - and others
181-
func (e *MPC) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
187+
func (e *MPC) VoltagePerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
182188
if !e.IsCompatibleEntityType(entity) {
183189
return nil, api.ErrNoCompatibleEntity
184190
}

0 commit comments

Comments
 (0)