From a82f86cae56fd064f63c6595caca4c7cdf9768c7 Mon Sep 17 00:00:00 2001 From: Nils Prommersberger Date: Tue, 15 Oct 2024 11:52:57 +0200 Subject: [PATCH] Make the mpc configurable. And support option stuff. --- usecases/api/mu_mpc.go | 199 ++++++++++ usecases/mu/mpc/config.go | 95 +++++ usecases/mu/mpc/public.go | 533 +++++++++++++++++-------- usecases/mu/mpc/public_test.go | 37 +- usecases/mu/mpc/testhelper_test.go | 39 +- usecases/mu/mpc/usecase.go | 601 +++++++++++++++++++---------- 6 files changed, 1126 insertions(+), 378 deletions(-) create mode 100644 usecases/api/mu_mpc.go create mode 100644 usecases/mu/mpc/config.go diff --git a/usecases/api/mu_mpc.go b/usecases/api/mu_mpc.go new file mode 100644 index 00000000..a4ba093f --- /dev/null +++ b/usecases/api/mu_mpc.go @@ -0,0 +1,199 @@ +package api + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/spine-go/model" + "time" +) + +// Actor: Monitoring Unit +// UseCase: Monitoring of Power Consumption +type MuMPCInterface interface { + // ------------------------- 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() ([]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() ([]float64, error) + + // Scenario 4 + + // get the phase specific voltage details + // + // possible errors: + // - ErrMissingData if the id is not available + // - and others + VoltagePerPhase() ([]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 ...api.MeasurementDataForID) 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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, + ) api.MeasurementDataForID + + // 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, + ) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID + + // 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) api.MeasurementDataForID +} diff --git a/usecases/mu/mpc/config.go b/usecases/mu/mpc/config.go new file mode 100644 index 00000000..c892af91 --- /dev/null +++ b/usecases/mu/mpc/config.go @@ -0,0 +1,95 @@ +package mpc + +import ( + "github.com/enbility/spine-go/model" + "strings" +) + +type ConnectedPhases string + +const ConnectedPhasesA ConnectedPhases = "a" +const ConnectedPhasesB ConnectedPhases = "b" +const ConnectedPhasesC ConnectedPhases = "c" +const ConnectedPhasesAB ConnectedPhases = "ab" +const ConnectedPhasesBC ConnectedPhases = "bc" +const ConnectedPhasesCA ConnectedPhases = "ac" +const ConnectedPhasesABC ConnectedPhases = "abc" + +// 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 ConnectedPhases // The phases that are measured + + ValueSourceTotal *model.MeasurementValueSourceType // The source of the values from the acPowerTotal (not optional) + ValueSourcePhaseA *model.MeasurementValueSourceType // The source of the values from the acPower for phase A (shall be set if the phase is supported) + ValueSourcePhaseB *model.MeasurementValueSourceType // The source of the values from the acPower for phase B (shall be set if the phase is supported) + ValueSourcePhaseC *model.MeasurementValueSourceType // The source of the values from the acPower for phase C (shall be set if the phase is supported) + + ValueConstraintsTotal *model.MeasurementConstraintsDataType // The constraints for the acPowerTotal (optional can be nil) + ValueConstraintsPhaseA *model.MeasurementConstraintsDataType // The constraints for the acPower for phase A (optional can be nil) + ValueConstraintsPhaseB *model.MeasurementConstraintsDataType // The constraints for the acPower for phase B (optional can be nil) + ValueConstraintsPhaseC *model.MeasurementConstraintsDataType // The constraints for the acPower for phase C (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) (needs 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) (needs 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 { + ValueSourcePhaseA *model.MeasurementValueSourceType // The source of the values for phase A (shall be set if the phase is supported) + ValueSourcePhaseB *model.MeasurementValueSourceType // The source of the values for phase B (shall be set if the phase is supported) + ValueSourcePhaseC *model.MeasurementValueSourceType // The source of the values for phase C (shall be set if the phase is supported) + + ValueConstraintsPhaseA *model.MeasurementConstraintsDataType // The constraints for the current for phase A (optional can be nil) (needs ValueSourcePhaseA to be set) + ValueConstraintsPhaseB *model.MeasurementConstraintsDataType // The constraints for the current for phase B (optional can be nil) (needs ValueSourcePhaseB to be set) + ValueConstraintsPhaseC *model.MeasurementConstraintsDataType // The constraints for the current for phase C (optional can be nil) (needs ValueSourcePhaseC 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 { + ValueSourcePhaseA *model.MeasurementValueSourceType // The source of the values for phase A (shall be set if the phase is supported) + ValueSourcePhaseB *model.MeasurementValueSourceType // The source of the values for phase B (shall be set if the phase is supported) + ValueSourcePhaseC *model.MeasurementValueSourceType // The source of the values for phase C (shall be set if the phase is supported) + + 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) + + SupportPhaseToPhase bool // If the use case shall support phase to phase voltage monitoring + ValueSourcePhaseAToB *model.MeasurementValueSourceType // The source of the values for phase A to B (shall be set if the phases are supported and SupportPhaseToPhase is true) + ValueSourcePhaseBToC *model.MeasurementValueSourceType // The source of the values for phase B to C (shall be set if the phases are supported and SupportPhaseToPhase is true) + ValueSourcePhaseCToA *model.MeasurementValueSourceType // The source of the values for phase C to A (shall be set if the phases are supported and SupportPhaseToPhase is true) + + 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 monitor use case +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) +} + +func (c *MonitorPowerConfig) SupportsPhases(phase []string) bool { + phasesString := string(c.ConnectedPhases) + supports := true + for _, p := range phase { + if !strings.Contains(phasesString, p) { + supports = false + break + } + } + return supports +} diff --git a/usecases/mu/mpc/public.go b/usecases/mu/mpc/public.go index a19b857a..b3302a7d 100644 --- a/usecases/mu/mpc/public.go +++ b/usecases/mu/mpc/public.go @@ -3,6 +3,8 @@ package mpc import ( "github.com/enbility/eebus-go/api" "github.com/enbility/eebus-go/features/server" + "github.com/enbility/spine-go/model" + "time" ) // ------------------------- Getters ------------------------- // @@ -28,26 +30,19 @@ func (e *MPC) Power() (float64, error) { // - ErrMissingData if the id is not available // - and others func (e *MPC) PowerPerPhase() ([]float64, error) { - if e.acPower[0] == nil || e.acPower[1] == nil || e.acPower[2] == nil { - return nil, api.ErrMissingData - } - - phaseA, err := e.getMeasurementDataForId(e.acPower[0]) - if err != nil { - return nil, err - } - - phaseB, err := e.getMeasurementDataForId(e.acPower[1]) - if err != nil { - return nil, err - } - - phaseC, err := e.getMeasurementDataForId(e.acPower[2]) - if err != nil { - return nil, err + powerPerPhase := make([]float64, 0) + + for _, id := range e.acPower { + if id != nil { + power, err := e.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + powerPerPhase = append(powerPerPhase, power) + } } - return []float64{phaseA, phaseB, phaseC}, nil + return powerPerPhase, nil } // Scenario 2 @@ -93,26 +88,19 @@ func (e *MPC) EnergyProduced() (float64, error) { // - ErrMissingData if the id is not available // - and others func (e *MPC) CurrentPerPhase() ([]float64, error) { - if e.acCurrent[0] == nil || e.acCurrent[1] == nil || e.acCurrent[2] == nil { - return nil, api.ErrMissingData - } - - phaseA, err := e.getMeasurementDataForId(e.acCurrent[0]) - if err != nil { - return nil, err - } - - phaseB, err := e.getMeasurementDataForId(e.acCurrent[1]) - if err != nil { - return nil, err - } - - phaseC, err := e.getMeasurementDataForId(e.acCurrent[2]) - if err != nil { - return nil, err + currentPerPhase := make([]float64, 0) + + for _, id := range e.acCurrent { + if id != nil { + current, err := e.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + currentPerPhase = append(currentPerPhase, current) + } } - return []float64{phaseA, phaseB, phaseC}, nil + return currentPerPhase, nil } // Scenario 4 @@ -123,43 +111,19 @@ func (e *MPC) CurrentPerPhase() ([]float64, error) { // - ErrMissingData if the id is not available // - and others func (e *MPC) VoltagePerPhase() ([]float64, error) { + voltagePerPhase := make([]float64, 0) + for _, id := range e.acVoltage { - if id == nil { - return nil, api.ErrMissingData + if id != nil { + voltage, err := e.getMeasurementDataForId(id) + if err != nil { + return nil, err + } + voltagePerPhase = append(voltagePerPhase, voltage) } } - phaseA, err := e.getMeasurementDataForId(e.acVoltage[0]) - if err != nil { - return nil, err - } - - phaseB, err := e.getMeasurementDataForId(e.acVoltage[1]) - if err != nil { - return nil, err - } - - phaseC, err := e.getMeasurementDataForId(e.acVoltage[2]) - if err != nil { - return nil, err - } - - phaseAToB, err := e.getMeasurementDataForId(e.acVoltage[3]) - if err != nil { - return nil, err - } - - phaseBToC, err := e.getMeasurementDataForId(e.acVoltage[4]) - if err != nil { - return nil, err - } - - phaseCToA, err := e.getMeasurementDataForId(e.acVoltage[5]) - if err != nil { - return nil, err - } - - return []float64{phaseA, phaseB, phaseC, phaseAToB, phaseBToC, phaseCToA}, nil + return voltagePerPhase, nil } // Scenario 5 @@ -179,163 +143,420 @@ func (e *MPC) Frequency() (float64, error) { // ------------------------- 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(measurementDataForIds ...api.MeasurementDataForID) error { + measurements, err := server.NewMeasurement(e.LocalEntity) + if err != nil { + return err + } + + return measurements.UpdateDataForIds(measurementDataForIds) +} + // Scenario 1 -// use MPC.MeasuredAcPowerTotal in MPC.Update to set the momentary active power consumption or production -func (e *MPC) MeasuredAcPowerTotal(acPowerTotal float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { return api.MeasurementDataForID{ - Data: measuredValue(acPowerTotal), - Id: *e.acPowerTotal, + Data: measurementData( + acPowerTotal, + timestamp, + e.powerConfig.ValueSourceTotal, + valueState, + nil, + nil, + ), + Id: *e.acPowerTotal, } } -// use MPC.MeasuredAcPowerPhaseA in MPC.Update to set the momentary active power consumption or production per phase -func (e *MPC) MeasuredAcPowerPhaseA(acPowerPhaseA float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acPower[0] == nil { + panic("acPowerPhaseA is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(acPowerPhaseA), - Id: *e.acPower[0], + Data: measurementData( + acPowerPhaseA, + timestamp, + e.powerConfig.ValueSourcePhaseA, + valueState, + nil, + nil, + ), + Id: *e.acPower[0], } } -// use MPC.MeasuredAcPowerPhaseB in MPC.Update to set the momentary active power consumption or production per phase -func (e *MPC) MeasuredAcPowerPhaseB(acPowerPhaseB float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acPower[1] == nil { + panic("acPowerPhaseB is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(acPowerPhaseB), - Id: *e.acPower[1], + Data: measurementData( + acPowerPhaseB, + timestamp, + e.powerConfig.ValueSourcePhaseB, + valueState, + nil, + nil, + ), + Id: *e.acPower[1], } } -// use MPC.MeasuredAcPowerPhaseC in MPC.Update to set the momentary active power consumption or production per phase -func (e *MPC) MeasuredAcPowerPhaseC(acPowerPhaseC float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acPower[2] == nil { + panic("acPowerPhaseC is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(acPowerPhaseC), - Id: *e.acPower[2], + Data: measurementData( + acPowerPhaseC, + timestamp, + e.powerConfig.ValueSourcePhaseC, + valueState, + nil, + nil, + ), + Id: *e.acPower[2], } } // Scenario 2 -// use MPC.MeasuredAcEnergyConsumed in MPC.Update to set the total feed in energy -func (e *MPC) MeasuredAcEnergyConsumed(energyConsumed float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acEnergyConsumed == nil { + panic("acEnergyConsumed is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(energyConsumed), - Id: *e.acEnergyConsumed, + Data: measurementData( + energyConsumed, + timestamp, + e.energyConfig.ValueSourceConsumption, + valueState, + evaluationStart, + evaluationEnd, + ), + Id: *e.acEnergyConsumed, } } -// use MPC.MeasuredAcEnergyProduced in MPC.Update to set the total feed in energy -func (e *MPC) MeasuredAcEnergyProduced(energyProduced float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acEnergyProduced == nil { + panic("acEnergyProduced is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(energyProduced), - Id: *e.acEnergyProduced, + Data: measurementData( + energyProduced, + timestamp, + e.energyConfig.ValueSourceProduction, + valueState, + evaluationStart, + evaluationEnd, + ), + Id: *e.acEnergyProduced, } } // Scenario 3 -// use MPC.MeasuredAcCurrentPhaseA in MPC.Update to set the momentary phase specific current consumption or production -func (e *MPC) MeasuredAcCurrentPhaseA(acCurrentPhaseA float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acCurrent[0] == nil { + panic("acCurrentPhaseA is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(acCurrentPhaseA), - Id: *e.acCurrent[0], + Data: measurementData( + acCurrentPhaseA, + timestamp, + e.currentConfig.ValueSourcePhaseA, + valueState, + nil, + nil, + ), + Id: *e.acCurrent[0], } } -// use MPC.MeasuredAcCurrentPhaseB in MPC.Update to set the momentary phase specific current consumption or production -func (e *MPC) MeasuredAcCurrentPhaseB(acCurrentPhaseB float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acCurrent[1] == nil { + panic("acCurrentPhaseB is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(acCurrentPhaseB), - Id: *e.acCurrent[1], + Data: measurementData( + acCurrentPhaseB, + timestamp, + e.currentConfig.ValueSourcePhaseB, + valueState, + nil, + nil, + ), + Id: *e.acCurrent[1], } } -// use MPC.MeasuredAcCurrentPhaseC in MPC.Update to set the momentary phase specific current consumption or production -func (e *MPC) MeasuredAcCurrentPhaseC(acCurrentPhaseC float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acCurrent[2] == nil { + panic("acCurrentPhaseC is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(acCurrentPhaseC), - Id: *e.acCurrent[2], + Data: measurementData( + acCurrentPhaseC, + timestamp, + e.currentConfig.ValueSourcePhaseC, + valueState, + nil, + nil, + ), + Id: *e.acCurrent[2], } } // Scenario 4 -// use MPC.MeasuredAcVoltagePhaseA in MPC.Update to set the phase specific voltage details -func (e *MPC) MeasuredAcVoltagePhaseA(voltagePhaseA float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acVoltage[0] == nil { + panic("acVoltagePhaseA is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(voltagePhaseA), - Id: *e.acVoltage[0], + Data: measurementData( + voltagePhaseA, + timestamp, + e.voltageConfig.ValueSourcePhaseA, + valueState, + nil, + nil, + ), + Id: *e.acVoltage[0], } } -// use MPC.MeasuredAcVoltagePhaseB in MPC.Update to set the phase specific voltage details -func (e *MPC) MeasuredAcVoltagePhaseB(voltagePhaseB float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acVoltage[1] == nil { + panic("acVoltagePhaseB is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(voltagePhaseB), - Id: *e.acVoltage[1], + Data: measurementData( + voltagePhaseB, + timestamp, + e.voltageConfig.ValueSourcePhaseB, + valueState, + nil, + nil, + ), + Id: *e.acVoltage[1], } } -// use MPC.MeasuredAcVoltagePhaseC in MPC.Update to set the phase specific voltage details -func (e *MPC) MeasuredAcVoltagePhaseC(voltagePhaseC float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acVoltage[2] == nil { + panic("acVoltagePhaseC is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(voltagePhaseC), - Id: *e.acVoltage[2], + Data: measurementData( + voltagePhaseC, + timestamp, + e.voltageConfig.ValueSourcePhaseC, + valueState, + nil, + nil, + ), + Id: *e.acVoltage[2], } } -// use MPC.MeasuredAcVoltagePhaseAToB in MPC.Update to set the phase specific voltage details -func (e *MPC) MeasuredAcVoltagePhaseAToB(voltagePhaseAToB float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acVoltage[3] == nil { + panic("acVoltagePhaseAToB is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(voltagePhaseAToB), - Id: *e.acVoltage[3], + Data: measurementData( + voltagePhaseAToB, + timestamp, + e.voltageConfig.ValueSourcePhaseAToB, + valueState, + nil, + nil, + ), + Id: *e.acVoltage[3], } } -// use MPC.MeasuredAcVoltagePhaseBToC in MPC.Update to set the phase specific voltage details -func (e *MPC) MeasuredAcVoltagePhaseBToC(voltagePhaseBToC float64) api.MeasurementDataForID { +// 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, +) api.MeasurementDataForID { + if e.acVoltage[4] == nil { + panic("acVoltagePhaseBToC is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(voltagePhaseBToC), - Id: *e.acVoltage[4], + Data: measurementData( + voltagePhaseBToC, + timestamp, + e.voltageConfig.ValueSourcePhaseBToC, + valueState, + nil, + nil, + ), + Id: *e.acVoltage[4], } } -// use MPC.MeasuredAcVoltagePhaseCToA in MPC.Update to set the phase specific voltage details -func (e *MPC) MeasuredAcVoltagePhaseCToA(voltagePhaseCToA float64) api.MeasurementDataForID { +// 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) UpdateDataVoltagePhaseCToA( + voltagePhaseCToA float64, + timestamp *time.Time, + valueState *model.MeasurementValueStateType, +) api.MeasurementDataForID { + if e.acVoltage[5] == nil { + panic("acVoltagePhaseCToA is not supported, please check the configuration") + } return api.MeasurementDataForID{ - Data: measuredValue(voltagePhaseCToA), - Id: *e.acVoltage[5], + Data: measurementData( + voltagePhaseCToA, + timestamp, + e.voltageConfig.ValueSourcePhaseCToA, + valueState, + nil, + nil, + ), + Id: *e.acVoltage[5], } } // Scenario 5 -// use MPC.MeasuredAcFrequency in MPC.Update to set the frequency -func (e *MPC) MeasuredAcFrequency(frequency float64) api.MeasurementDataForID { - return api.MeasurementDataForID{ - Data: measuredValue(frequency), - Id: *e.acFrequency, +// 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, +) api.MeasurementDataForID { + if e.acFrequency == nil { + panic("acFrequency is not supported, please check the configuration") } -} - -// Update the measurement data - -// use MPC.Update to update the measurement data -// use it like this: -// -// mpc.Update( -// mpc.MeasuredAcPowerTotal(1000), -// mpc.MeasuredAcPowerPhaseA(500), -// ... -// ) -// -// possible errors: -// - ErrMissingData if the id is not available -// - and others -func (e *MPC) Update(measurementDataForIds ...api.MeasurementDataForID) error { - measurements, err := server.NewMeasurement(e.LocalEntity) - if err != nil { - return err + return api.MeasurementDataForID{ + Data: measurementData( + frequency, + timestamp, + e.frequencyConfig.ValueSource, + valueState, + nil, + nil, + ), + Id: *e.acFrequency, } - - return measurements.UpdateDataForIds(measurementDataForIds) } diff --git a/usecases/mu/mpc/public_test.go b/usecases/mu/mpc/public_test.go index a09a2fa6..bea94e79 100644 --- a/usecases/mu/mpc/public_test.go +++ b/usecases/mu/mpc/public_test.go @@ -1,12 +1,15 @@ package mpc import ( + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" "github.com/stretchr/testify/assert" + "time" ) func (s *MuMPCSuite) Test_Power() { err := s.sut.Update( - s.sut.MeasuredAcPowerTotal(5.0), + s.sut.UpdateDataPowerTotal(5.0, util.Ptr(time.Now()), nil), ) assert.Nil(s.T(), err) @@ -17,9 +20,9 @@ func (s *MuMPCSuite) Test_Power() { func (s *MuMPCSuite) Test_PowerPerPhase() { err := s.sut.Update( - s.sut.MeasuredAcPowerPhaseA(5.0), - s.sut.MeasuredAcPowerPhaseB(6.0), - s.sut.MeasuredAcPowerPhaseC(7.0), + 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) @@ -30,7 +33,7 @@ func (s *MuMPCSuite) Test_PowerPerPhase() { func (s *MuMPCSuite) Test_EnergyConsumed() { err := s.sut.Update( - s.sut.MeasuredAcEnergyConsumed(5.0), + s.sut.UpdateDataEnergyConsumed(5.0, util.Ptr(time.Now()), nil, util.Ptr(time.Now()), util.Ptr(time.Now())), ) assert.Nil(s.T(), err) @@ -41,7 +44,7 @@ func (s *MuMPCSuite) Test_EnergyConsumed() { func (s *MuMPCSuite) Test_EnergyProduced() { err := s.sut.Update( - s.sut.MeasuredAcEnergyProduced(5.0), + s.sut.UpdateDataEnergyProduced(5.0, nil, nil, nil, nil), ) assert.Nil(s.T(), err) @@ -52,25 +55,25 @@ func (s *MuMPCSuite) Test_EnergyProduced() { func (s *MuMPCSuite) Test_CurrentPerPhase() { err := s.sut.Update( - s.sut.MeasuredAcCurrentPhaseA(5.0), - s.sut.MeasuredAcCurrentPhaseB(5.0), - s.sut.MeasuredAcCurrentPhaseC(5.0), + 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) currentPerPhases, err := s.sut.CurrentPerPhase() assert.Nil(s.T(), err) - assert.Equal(s.T(), []float64{5.0, 5.0, 5.0}, currentPerPhases) + assert.Equal(s.T(), []float64{5.0, 3.0, 1.0}, currentPerPhases) } func (s *MuMPCSuite) Test_VoltagePerPhase() { err := s.sut.Update( - s.sut.MeasuredAcVoltagePhaseA(5.0), - s.sut.MeasuredAcVoltagePhaseB(6.0), - s.sut.MeasuredAcVoltagePhaseC(7.0), - s.sut.MeasuredAcVoltagePhaseAToB(8.0), - s.sut.MeasuredAcVoltagePhaseBToC(9.0), - s.sut.MeasuredAcVoltagePhaseCToA(10.0), + 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) @@ -81,7 +84,7 @@ func (s *MuMPCSuite) Test_VoltagePerPhase() { func (s *MuMPCSuite) Test_Frequency() { err := s.sut.Update( - s.sut.MeasuredAcFrequency(5.0), + s.sut.UpdateDataFrequency(5.0, nil, nil), ) assert.Nil(s.T(), err) diff --git a/usecases/mu/mpc/testhelper_test.go b/usecases/mu/mpc/testhelper_test.go index f5c3c572..df3140da 100644 --- a/usecases/mu/mpc/testhelper_test.go +++ b/usecases/mu/mpc/testhelper_test.go @@ -8,6 +8,7 @@ import ( 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" "testing" @@ -70,7 +71,43 @@ func (s *MuMPCSuite) BeforeTest(suiteName, testName string) { mockRemoteFeature.EXPECT().Operations().Return(nil).Maybe() localEntity := s.service.LocalDevice().EntityForType(model.EntityTypeTypeInverter) - s.sut = NewMPC(localEntity, s.Event) + s.sut, _ = NewMPC( + localEntity, + s.Event, + &MonitorPowerConfig{ + ConnectedPhases: ConnectedPhasesABC, + ValueSourceTotal: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorEnergyConfig{ + ValueSourceProduction: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourceConsumption: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorCurrentConfig{ + ValueSourcePhaseA: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseB: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + ValueSourcePhaseC: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + &MonitorVoltageConfig{ + SupportPhaseToPhase: true, + 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), + ValueConstraints: util.Ptr(model.MeasurementConstraintsDataType{ + ValueRangeMin: model.NewScaledNumberType(0), + ValueRangeMax: model.NewScaledNumberType(100), + ValueStepSize: model.NewScaledNumberType(1), + }), + }, + ) s.sut.AddFeatures() s.sut.AddUseCase() diff --git a/usecases/mu/mpc/usecase.go b/usecases/mu/mpc/usecase.go index 46dade50..8538a05f 100644 --- a/usecases/mu/mpc/usecase.go +++ b/usecases/mu/mpc/usecase.go @@ -1,6 +1,7 @@ package mpc import ( + "errors" "time" "github.com/enbility/eebus-go/api" @@ -15,6 +16,12 @@ import ( type MPC struct { *usecase.UseCaseBase + powerConfig *MonitorPowerConfig + energyConfig *MonitorEnergyConfig + currentConfig *MonitorCurrentConfig + voltageConfig *MonitorVoltageConfig + frequencyConfig *MonitorFrequencyConfig + acPowerTotal *model.MeasurementIdType acPower [3]*model.MeasurementIdType acEnergyConsumed *model.MeasurementIdType @@ -24,11 +31,19 @@ type MPC struct { acFrequency *model.MeasurementIdType } -// At the moment the MPC use case configures itself as a 3-phase meter by default (ABC). func NewMPC( localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCallback, -) *MPC { + 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{ { @@ -88,30 +103,46 @@ func NewMPC( ) uc := &MPC{ - UseCaseBase: u, + UseCaseBase: u, + powerConfig: monitorPowerConfig, + energyConfig: monitorEnergyConfig, + currentConfig: monitorCurrentConfig, + voltageConfig: monitorVoltageConfig, + frequencyConfig: monitorFrequencyConfig, } _ = spine.Events.Subscribe(uc) - return uc + return uc, nil } func (e *MPC) AddFeatures() { // server features - f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) - f.AddFunctionType(model.FunctionTypeElectricalConnectionDescriptionListData, true, false) - f.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + electricalConnectionFeatrue := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + electricalConnectionFeatrue.AddFunctionType(model.FunctionTypeElectricalConnectionDescriptionListData, true, false) + electricalConnectionFeatrue.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) - f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer) - f.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, true, false) - f.AddFunctionType(model.FunctionTypeMeasurementConstraintsListData, true, false) - f.AddFunctionType(model.FunctionTypeMeasurementListData, true, false) + measurementFeature := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + 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 { panic(err) } + var phases = [][]string{ + {"a"}, + {"b"}, + {"c"}, + {"a", "b"}, + {"b", "c"}, + {"c", "a"}, + } + + var constraints = make([]model.MeasurementConstraintsDataType, 0) + e.acPowerTotal = measurements.AddDescription(model.MeasurementDescriptionDataType{ MeasurementType: util.Ptr(model.MeasurementTypeTypePower), CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), @@ -119,54 +150,116 @@ func (e *MPC) AddFeatures() { ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), }) + if e.powerConfig.ValueConstraintsTotal != nil { + e.powerConfig.ValueConstraintsTotal.MeasurementId = e.acPowerTotal + constraints = append(constraints, *e.powerConfig.ValueConstraintsTotal) + } + + acPowerConstraints := []*model.MeasurementConstraintsDataType{ + e.powerConfig.ValueConstraintsPhaseA, + e.powerConfig.ValueConstraintsPhaseB, + e.powerConfig.ValueConstraintsPhaseC, + } for id := 0; id < len(e.acPower); id++ { - e.acPower[id] = measurements.AddDescription(model.MeasurementDescriptionDataType{ - MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + if e.powerConfig.SupportsPhases(phases[id]) { + e.acPower[id] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + }) + if acPowerConstraints[id] != nil { + acPowerConstraints[id].MeasurementId = e.acPower[id] + constraints = append(constraints, *acPowerConstraints[id]) + } + } + } + + 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.UnitOfMeasurementTypeW), - ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyConsumed), }) + if e.energyConfig.ValueConstraintsConsumption != nil { + e.energyConfig.ValueConstraintsConsumption.MeasurementId = e.acEnergyConsumed + constraints = append(constraints, *e.energyConfig.ValueConstraintsConsumption) + } } - 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), - }) - - 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), - }) - - for id := 0; id < len(e.acCurrent); id++ { - e.acCurrent[id] = measurements.AddDescription(model.MeasurementDescriptionDataType{ - MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + 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.UnitOfMeasurementTypeA), - ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyProduced), }) + if e.energyConfig.ValueConstraintsProduction != nil { + e.energyConfig.ValueConstraintsProduction.MeasurementId = e.acEnergyProduced + constraints = append(constraints, *e.energyConfig.ValueConstraintsProduction) + } } + acCurrentConstraints := []*model.MeasurementConstraintsDataType{ + e.currentConfig.ValueConstraintsPhaseA, + e.currentConfig.ValueConstraintsPhaseB, + e.currentConfig.ValueConstraintsPhaseC, + } + for id := 0; id < len(e.acCurrent); id++ { + if e.powerConfig.SupportsPhases(phases[id]) { + e.acCurrent[id] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }) + if acCurrentConstraints[id] != nil { + acCurrentConstraints[id].MeasurementId = e.acCurrent[id] + constraints = append(constraints, *acCurrentConstraints[id]) + } + } + } + + acVoltageConstraints := []*model.MeasurementConstraintsDataType{ + e.voltageConfig.ValueConstraintsPhaseA, + e.voltageConfig.ValueConstraintsPhaseB, + e.voltageConfig.ValueConstraintsPhaseC, + e.voltageConfig.ValueConstraintsPhaseAToB, + e.voltageConfig.ValueConstraintsPhaseBToC, + e.voltageConfig.ValueConstraintsPhaseCToA, + } for id := 0; id < len(e.acVoltage); id++ { - e.acVoltage[id] = measurements.AddDescription(model.MeasurementDescriptionDataType{ - MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + if e.powerConfig.SupportsPhases(phases[id]) { + if len(phases[id]) == 2 && !e.voltageConfig.SupportPhaseToPhase { + continue + } + e.acVoltage[id] = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }) + if acVoltageConstraints[id] != nil { + acVoltageConstraints[id].MeasurementId = e.acVoltage[id] + constraints = append(constraints, *acVoltageConstraints[id]) + } + } + } + + if e.frequencyConfig != nil { + e.acFrequency = measurements.AddDescription(model.MeasurementDescriptionDataType{ + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), - Unit: util.Ptr(model.UnitOfMeasurementTypeV), - ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + Unit: util.Ptr(model.UnitOfMeasurementTypeHz), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), }) + if e.frequencyConfig.ValueConstraints != nil { + e.frequencyConfig.ValueConstraints.MeasurementId = e.acFrequency + constraints = append(constraints, *e.frequencyConfig.ValueConstraints) + } } - 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), - }) - electricalConnection, err := server.NewElectricalConnection(e.LocalEntity) if err != nil { panic(err) @@ -186,7 +279,7 @@ func (e *MPC) AddFeatures() { ElectricalConnectionId: util.Ptr(idEc1), MeasurementId: e.acPowerTotal, VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameType(e.powerConfig.ConnectedPhases)), AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), @@ -196,159 +289,240 @@ func (e *MPC) AddFeatures() { panic("error adding parameter description") } - p21 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acPower[0], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP21 := electricalConnection.AddParameterDescription(p21) - if idP21 == nil { - panic("error adding parameter description") - } - - p22 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acPower[1], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), - AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP22 := electricalConnection.AddParameterDescription(p22) - if idP22 == nil { - panic("error adding parameter description") - } - - p23 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acPower[2], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), - AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP23 := electricalConnection.AddParameterDescription(p23) - if idP23 == nil { - panic("error adding parameter description") - } - - p3 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acEnergyConsumed, - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - } - idP3 := electricalConnection.AddParameterDescription(p3) - if idP3 == nil { - panic("error adding parameter description") - } - - p4 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acEnergyProduced, - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - } - idP4 := electricalConnection.AddParameterDescription(p4) - if idP4 == nil { - panic("error adding parameter description") - } - - p51 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acCurrent[0], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP51 := electricalConnection.AddParameterDescription(p51) - if idP51 == nil { - panic("error adding parameter description") - } - - p52 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acCurrent[1], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP52 := electricalConnection.AddParameterDescription(p52) - if idP52 == nil { - panic("error adding parameter description") - } - - p53 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acCurrent[2], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP53 := electricalConnection.AddParameterDescription(p53) - if idP53 == nil { - panic("error adding parameter description") - } - - p61 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acVoltage[0], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), - AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP61 := electricalConnection.AddParameterDescription(p61) - if idP61 == nil { - panic("error adding parameter description") - } - - p62 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acVoltage[1], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), - AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP62 := electricalConnection.AddParameterDescription(p62) - if idP62 == nil { - panic("error adding parameter description") - } - - p63 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acVoltage[2], - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), - AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), - AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), - AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), - } - idP63 := electricalConnection.AddParameterDescription(p63) - if idP63 == nil { - panic("error adding parameter description") - } - - p7 := model.ElectricalConnectionParameterDescriptionDataType{ - ElectricalConnectionId: util.Ptr(idEc1), - MeasurementId: e.acFrequency, - VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), - } - idP7 := electricalConnection.AddParameterDescription(p7) - if idP7 == nil { - panic("error adding parameter description") + if e.acPower[0] != nil { + p21 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acPower[0], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP21 := electricalConnection.AddParameterDescription(p21) + if idP21 == nil { + panic("error adding parameter description") + } + } + + if e.acPower[1] != nil { + p22 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acPower[1], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP22 := electricalConnection.AddParameterDescription(p22) + if idP22 == nil { + panic("error adding parameter description") + } + } + + if e.acPower[2] != nil { + p23 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acPower[2], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP23 := electricalConnection.AddParameterDescription(p23) + if idP23 == nil { + panic("error adding parameter description") + } + } + + if e.acEnergyConsumed != nil { + p3 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acEnergyConsumed, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + } + idP3 := electricalConnection.AddParameterDescription(p3) + if idP3 == nil { + panic("error adding parameter description") + } + } + + if e.acEnergyProduced != nil { + p4 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acEnergyProduced, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + } + idP4 := electricalConnection.AddParameterDescription(p4) + if idP4 == nil { + panic("error adding parameter description") + } + } + + if e.acCurrent[0] != nil { + p51 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acCurrent[0], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP51 := electricalConnection.AddParameterDescription(p51) + if idP51 == nil { + panic("error adding parameter description") + } + } + + if e.acCurrent[1] != nil { + p52 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acCurrent[1], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP52 := electricalConnection.AddParameterDescription(p52) + if idP52 == nil { + panic("error adding parameter description") + } + } + + if e.acCurrent[2] != nil { + p53 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acCurrent[2], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP53 := electricalConnection.AddParameterDescription(p53) + if idP53 == nil { + panic("error adding parameter description") + } + } + + if e.acVoltage[0] != nil { + p61 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acVoltage[0], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP61 := electricalConnection.AddParameterDescription(p61) + if idP61 == nil { + panic("error adding parameter description") + } + } + + if e.acVoltage[1] != nil { + p62 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acVoltage[1], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP62 := electricalConnection.AddParameterDescription(p62) + if idP62 == nil { + panic("error adding parameter description") + } + } + + if e.acVoltage[2] != nil { + p63 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acVoltage[2], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP63 := electricalConnection.AddParameterDescription(p63) + if idP63 == nil { + panic("error adding parameter description") + } + } + + if e.acVoltage[3] != nil { + p64 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acVoltage[3], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP64 := electricalConnection.AddParameterDescription(p64) + if idP64 == nil { + panic("error adding parameter description") + } + } + + if e.acVoltage[4] != nil { + p65 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acVoltage[4], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP65 := electricalConnection.AddParameterDescription(p65) + if idP65 == nil { + panic("error adding parameter description") + } + } + + if e.acVoltage[5] != nil { + p66 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acVoltage[5], + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeApparent), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + } + idP66 := electricalConnection.AddParameterDescription(p66) + if idP66 == nil { + panic("error adding parameter description") + } + } + + if e.acFrequency != nil { + p7 := model.ElectricalConnectionParameterDescriptionDataType{ + ElectricalConnectionId: util.Ptr(idEc1), + MeasurementId: e.acFrequency, + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + } + idP7 := electricalConnection.AddParameterDescription(p7) + if idP7 == nil { + panic("error adding parameter description") + } + } + + if len(constraints) > 0 { + measurementFeature.UpdateData( + model.FunctionTypeMeasurementConstraintsListData, + &model.MeasurementConstraintsListDataType{ + MeasurementConstraintsData: constraints, + }, nil, nil, + ) } } @@ -370,12 +544,31 @@ func (e *MPC) getMeasurementDataForId(id *model.MeasurementIdType) (float64, err return data.Value.GetValue(), nil } -func measuredValue(value float64) model.MeasurementDataType { - return model.MeasurementDataType{ +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), - Timestamp: model.NewAbsoluteOrRelativeTimeTypeFromTime(time.Now()), Value: model.NewScaledNumberType(value), - ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), - ValueState: util.Ptr(model.MeasurementValueStateTypeNormal), + 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 }