Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions usecases/api/ma_mgcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"github.com/enbility/eebus-go/api"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
)

// Actor: Monitoring Appliance
Expand Down Expand Up @@ -66,15 +67,15 @@ type MaMGCPInterface interface {
// return values:
// - positive values are used for consumption
// - negative values are used for production
CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)

// Scenario 6

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

// Scenario 7

Expand Down
7 changes: 4 additions & 3 deletions usecases/api/ma_mpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"github.com/enbility/eebus-go/api"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
)

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

// Scenario 2

Expand Down Expand Up @@ -61,15 +62,15 @@ type MaMPCInterface interface {
// return values
// - positive values are used for consumption
// - negative values are used for production
CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)

// Scenario 4

// return the phase specific voltage details
//
// parameters:
// - entity: the entity of the device (e.g. EVSE)
VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error)
VoltagePerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error)

// Scenario 5

Expand Down
40 changes: 27 additions & 13 deletions usecases/internal/measurement.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func MeasurementPhaseSpecificDataForFilter(
measurementFilter model.MeasurementDescriptionDataType,
energyDirection model.EnergyDirectionType,
validPhaseNameTypes []model.ElectricalConnectionPhaseNameType,
) ([]float64, error) {
) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
measurement, err := client.NewMeasurement(localEntity, remoteEntity)
electricalConnection, err1 := client.NewElectricalConnection(localEntity, remoteEntity)
if err != nil || err1 != nil {
Expand All @@ -28,23 +28,37 @@ func MeasurementPhaseSpecificDataForFilter(
return nil, api.ErrDataNotAvailable
}

var result []float64
result := make(map[model.ElectricalConnectionPhaseNameType]float64, len(validPhaseNameTypes))

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
}
filter := model.ElectricalConnectionParameterDescriptionDataType{
MeasurementId: item.MeasurementId,
}
param, err := electricalConnection.GetParameterDescriptionsForFilter(filter)
if err != nil || len(param) == 0 {
// error getting parameter description
continue
}

var phaseName model.ElectricalConnectionPhaseNameType
if param[0].AcMeasuredPhases != nil {
phaseName = *param[0].AcMeasuredPhases
} else if validPhaseNameTypes == nil {
// if we're not filtering by valid phase names, allow acMeasuredPhases to be unset
phaseName = model.ElectricalConnectionPhaseNameTypeNone
} else {
// error getting parameter description
continue
}

if validPhaseNameTypes != nil &&
!slices.Contains(validPhaseNameTypes, phaseName) {
// ignore phase measurements not specified in validPhaseNameTypes
continue
}

if energyDirection != "" {
Expand All @@ -70,7 +84,7 @@ func MeasurementPhaseSpecificDataForFilter(

value := item.Value.GetValue()

result = append(result, value)
result[phaseName] = value
}

return result, nil
Expand Down
172 changes: 170 additions & 2 deletions usecases/internal/measurement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
ucapi.PhaseNameMapping,
)
assert.Nil(s.T(), err)
assert.Equal(s.T(), 0, len(data))
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{}, data)

elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
Expand Down Expand Up @@ -160,7 +160,7 @@
ucapi.PhaseNameMapping,
)
assert.Nil(s.T(), err)
assert.Equal(s.T(), []float64{10, 10, 10}, data)
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"a": 10, "b": 10, "c": 10}, data)

measData = &model.MeasurementListDataType{
MeasurementData: []model.MeasurementDataType{
Expand Down Expand Up @@ -196,3 +196,171 @@
assert.NotNil(s.T(), err)
assert.Nil(s.T(), data)
}

func (s *InternalSuite) Test_MeasurementSinglePhaseSpecificDataForFilter() {
measurementType := model.MeasurementTypeTypePower
commodityType := model.CommodityTypeTypeElectricity
scopeType := model.ScopeTypeTypeACPower
energyDirection := model.EnergyDirectionTypeConsume

filter := model.MeasurementDescriptionDataType{
MeasurementType: &measurementType,
CommodityType: &commodityType,
ScopeType: &scopeType,
}

// set up ElectricalConnection
elDescData := &model.ElectricalConnectionDescriptionListDataType{
ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{
{
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume),
},
},
}

elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
{
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB),
},
},
}

rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer)

_, fErr := rElFeature.UpdateData(true, model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil)
assert.Nil(s.T(), fErr)

_, fErr = rElFeature.UpdateData(true, model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil)
assert.Nil(s.T(), fErr)

descData := &model.MeasurementDescriptionListDataType{
MeasurementDescriptionData: []model.MeasurementDescriptionDataType{
{
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
MeasurementType: util.Ptr(model.MeasurementTypeTypePower),
CommodityType: util.Ptr(model.CommodityTypeTypeElectricity),
ScopeType: util.Ptr(model.ScopeTypeTypeACPower),
},
},
}

rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer)
_, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil)
assert.Nil(s.T(), fErr)

measData := &model.MeasurementListDataType{
MeasurementData: []model.MeasurementDataType{
{
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
Value: model.NewScaledNumberType(10),
},
},
}

_, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementListData, measData, nil, nil)
assert.Nil(s.T(), fErr)

data, err := MeasurementPhaseSpecificDataForFilter(
s.localEntity,
s.monitoredEntity,
filter,
energyDirection,
ucapi.PhaseNameMapping,
)
assert.Nil(s.T(), err)
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"b": 10}, data)

data, err = MeasurementPhaseSpecificDataForFilter(
s.localEntity,
s.monitoredEntity,
filter,
energyDirection,
nil,
)
assert.Nil(s.T(), err)
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"b": 10}, data)

}

Check failure on line 287 in usecases/internal/measurement_test.go

View workflow job for this annotation

GitHub Actions / Build

unnecessary trailing newline (whitespace)

func (s *InternalSuite) Test_MeasurementTotalPhaseSpecificDataForFilter() {
measurementType := model.MeasurementTypeTypePower
commodityType := model.CommodityTypeTypeElectricity
scopeType := model.ScopeTypeTypeACPowerTotal
energyDirection := model.EnergyDirectionTypeConsume

filter := model.MeasurementDescriptionDataType{
MeasurementType: &measurementType,
CommodityType: &commodityType,
ScopeType: &scopeType,
}

// set up ElectricalConnection
elDescData := &model.ElectricalConnectionDescriptionListDataType{
ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{
{
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume),
},
},
}

elParamData := &model.ElectricalConnectionParameterDescriptionListDataType{
ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{
{
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc),
},
},
}

rElFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer)

_, fErr := rElFeature.UpdateData(true, model.FunctionTypeElectricalConnectionDescriptionListData, elDescData, nil, nil)
assert.Nil(s.T(), fErr)

_, fErr = rElFeature.UpdateData(true, model.FunctionTypeElectricalConnectionParameterDescriptionListData, elParamData, nil, nil)
assert.Nil(s.T(), fErr)

descData := &model.MeasurementDescriptionListDataType{
MeasurementDescriptionData: []model.MeasurementDescriptionDataType{
{
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
MeasurementType: &measurementType,
CommodityType: &commodityType,
ScopeType: &scopeType,
},
},
}

rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer)
_, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil)
assert.Nil(s.T(), fErr)

measData := &model.MeasurementListDataType{
MeasurementData: []model.MeasurementDataType{
{
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
Value: model.NewScaledNumberType(10),
},
},
}

_, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementListData, measData, nil, nil)
assert.Nil(s.T(), fErr)

data, err := MeasurementPhaseSpecificDataForFilter(
s.localEntity,
s.monitoredEntity,
filter,
energyDirection,
nil,
)
assert.Nil(s.T(), err)
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"abc": 10}, data)

}

Check failure on line 366 in usecases/internal/measurement_test.go

View workflow job for this annotation

GitHub Actions / Build

unnecessary trailing newline (whitespace)
12 changes: 9 additions & 3 deletions usecases/ma/mgcp/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ func (e *MGCP) Power(entity spineapi.EntityRemoteInterface) (float64, error) {
return 0, api.ErrDataNotAvailable
}

return data[0], nil
for _, k := range data {
// 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".
// The values "a", "b", and "c" are permitted if and only if only one
return k, nil
}
// unreachable
return 0, api.ErrDataNotAvailable
}

// Scenario 3
Expand Down Expand Up @@ -170,7 +176,7 @@ func (e *MGCP) EnergyConsumed(entity spineapi.EntityRemoteInterface) (float64, e
// - ErrDataNotAvailable if no such value is (yet) available
// - ErrDataInvalid if the currently available data is invalid and should be ignored
// - and others
func (e *MGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
func (e *MGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
if !e.IsCompatibleEntityType(entity) {
return nil, api.ErrNoCompatibleEntity
}
Expand All @@ -191,7 +197,7 @@ func (e *MGCP) CurrentPerPhase(entity spineapi.EntityRemoteInterface) ([]float64
// - ErrDataNotAvailable if no such value is (yet) available
// - ErrDataInvalid if the currently available data is invalid and should be ignored
// - and others
func (e *MGCP) VoltagePerPhase(entity spineapi.EntityRemoteInterface) ([]float64, error) {
func (e *MGCP) VoltagePerPhase(entity spineapi.EntityRemoteInterface) (map[model.ElectricalConnectionPhaseNameType]float64, error) {
if !e.IsCompatibleEntityType(entity) {
return nil, api.ErrNoCompatibleEntity
}
Expand Down
9 changes: 5 additions & 4 deletions usecases/ma/mgcp/public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func (s *GcpMGCPSuite) Test_Power() {
{
ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)),
MeasurementId: util.Ptr(model.MeasurementIdType(0)),
AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA),
},
},
}
Expand Down Expand Up @@ -316,7 +317,7 @@ func (s *GcpMGCPSuite) Test_CurrentPerPhase() {

data, err = s.sut.CurrentPerPhase(s.smgwEntity)
assert.Nil(s.T(), err)
assert.Equal(s.T(), 0, len(data))
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{}, data)

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

data, err = s.sut.CurrentPerPhase(s.smgwEntity)
assert.Nil(s.T(), err)
assert.Equal(s.T(), []float64{10, 10, 10}, data)
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"a": 10, "b": 10, "c": 10}, data)
}

func (s *GcpMGCPSuite) Test_VoltagePerPhase() {
Expand Down Expand Up @@ -421,7 +422,7 @@ func (s *GcpMGCPSuite) Test_VoltagePerPhase() {

data, err = s.sut.VoltagePerPhase(s.smgwEntity)
assert.Nil(s.T(), err)
assert.Equal(s.T(), 0, len(data))
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{}, data)

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

data, err = s.sut.VoltagePerPhase(s.smgwEntity)
assert.Nil(s.T(), err)
assert.Equal(s.T(), []float64{230, 230, 230}, data)
assert.Equal(s.T(), map[model.ElectricalConnectionPhaseNameType]float64{"a": 230, "b": 230, "c": 230}, data)
}

func (s *GcpMGCPSuite) Test_Frequency() {
Expand Down
Loading
Loading