diff --git a/examples/smartmeter/main.go b/examples/smartmeter/main.go new file mode 100644 index 00000000..a4a6a53d --- /dev/null +++ b/examples/smartmeter/main.go @@ -0,0 +1,304 @@ +package main + +import ( + "crypto/ecdsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "log" + "math" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/service" + ucapi "github.com/enbility/eebus-go/usecases/api" + + gcpmgcp "github.com/enbility/eebus-go/usecases/gcp/mgcp" + mumpc "github.com/enbility/eebus-go/usecases/mu/mpc" + shipapi "github.com/enbility/ship-go/api" + "github.com/enbility/ship-go/cert" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" +) + +var remoteSki string + +type smartmeter struct { + myService *service.Service + + ucgcpmgcp ucapi.GcpMGCPInterface + ucmumpc ucapi.MuMPCInterface + + gridPowerLimitFactor float64 + gridPower float64 + gridPowerPerPhase []float64 + gridConsumedEnergy float64 + gridFeedInEnergy float64 + gridCurrentPerPhase []float64 + gridVoltagePerPhase []float64 + gridFrequency float64 +} + +func (h *smartmeter) run() { + var err error + var certificate tls.Certificate + + if len(os.Args) == 5 { + remoteSki = os.Args[2] + + certificate, err = tls.LoadX509KeyPair(os.Args[3], os.Args[4]) + if err != nil { + usage() + log.Fatal(err) + } + } else { + certificate, err = cert.CreateCertificate("Demo", "Demo", "DE", "Demo-Unit-01") + if err != nil { + log.Fatal(err) + } + + pemdata := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certificate.Certificate[0], + }) + fmt.Println(string(pemdata)) + + b, err := x509.MarshalECPrivateKey(certificate.PrivateKey.(*ecdsa.PrivateKey)) + if err != nil { + log.Fatal(err) + } + pemdata = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + fmt.Println(string(pemdata)) + } + + port, err := strconv.Atoi(os.Args[1]) + if err != nil { + usage() + log.Fatal(err) + } + + configuration, err := api.NewConfiguration( + "Demo", "Demo", "SmartMeter", "123456789", + []shipapi.DeviceCategoryType{shipapi.DeviceCategoryTypeMetering}, + model.DeviceTypeTypeSubmeter, + []model.EntityTypeType{model.EntityTypeTypeGridConnectionPointOfPremises, model.EntityTypeTypeSubMeterElectricity}, + port, certificate, time.Second*4) + if err != nil { + log.Fatal(err) + } + configuration.SetAlternateIdentifier("Demo-SmartMeter-123456789") + + h.myService = service.NewService(configuration, h) + h.myService.SetLogging(h) + + if err = h.myService.Setup(); err != nil { + fmt.Println(err) + return + } + + localEntityGCPP := h.myService.LocalDevice().EntityForType(model.EntityTypeTypeGridConnectionPointOfPremises) + h.ucgcpmgcp = gcpmgcp.NewMGCP(localEntityGCPP, h.OnMGCPEvent) + h.myService.AddUseCase(h.ucgcpmgcp) + + localEntitySME := h.myService.LocalDevice().EntityForType(model.EntityTypeTypeSubMeterElectricity) + h.ucmumpc = mumpc.NewMPC(localEntitySME, h.OnMPCEvent) + h.myService.AddUseCase(h.ucmumpc) + + if len(remoteSki) == 0 { + os.Exit(0) + } + + h.gridPowerLimitFactor = 70 + h.gridPower = 3000 + h.gridPowerPerPhase = []float64{900, 1000, 1100} + h.gridConsumedEnergy = 12345 + h.gridFeedInEnergy = -1000 + h.gridCurrentPerPhase = []float64{10, 20, 30} + h.gridVoltagePerPhase = []float64{229, 230, 231} + h.gridFrequency = 50 + + ticker := time.NewTicker(3000 * time.Millisecond) + go func() { + for range ticker.C { + _ = h.ucgcpmgcp.SetPower(math.Abs(h.gridPower)) + _ = h.ucmumpc.SetPower(math.Abs(h.gridPower)) + switch h.gridPower { + case 3000: + h.gridPower = 3100 + case 3100: + h.gridPower = -3000 + case -3000: + h.gridPower = 2900 + case 2900: + h.gridPower = 3000 + } + + _ = h.ucmumpc.SetPowerPerPhase(h.gridPowerPerPhase) + tmp := h.gridPowerPerPhase[0] + h.gridPowerPerPhase[0] = h.gridPowerPerPhase[1] + h.gridPowerPerPhase[1] = h.gridPowerPerPhase[2] + h.gridPowerPerPhase[2] = tmp + + _ = h.ucgcpmgcp.SetEnergyConsumed(h.gridConsumedEnergy) + _ = h.ucmumpc.SetEnergyConsumed(h.gridConsumedEnergy) + h.gridConsumedEnergy++ + + _ = h.ucgcpmgcp.SetEnergyFeedIn(h.gridFeedInEnergy) + _ = h.ucmumpc.SetEnergyProduced(h.gridFeedInEnergy) + h.gridFeedInEnergy-- + + _ = h.ucgcpmgcp.SetCurrentPerPhase(h.gridCurrentPerPhase) + _ = h.ucmumpc.SetCurrentPerPhase(h.gridCurrentPerPhase) + tmp = h.gridCurrentPerPhase[0] + h.gridCurrentPerPhase[0] = h.gridCurrentPerPhase[1] + h.gridCurrentPerPhase[1] = h.gridCurrentPerPhase[2] + h.gridCurrentPerPhase[2] = tmp + + _ = h.ucgcpmgcp.SetVoltagePerPhase(h.gridVoltagePerPhase) + _ = h.ucmumpc.SetVoltagePerPhase(h.gridVoltagePerPhase) + tmp = h.gridVoltagePerPhase[0] + h.gridVoltagePerPhase[0] = h.gridVoltagePerPhase[1] + h.gridVoltagePerPhase[1] = h.gridVoltagePerPhase[2] + h.gridVoltagePerPhase[2] = tmp + + _ = h.ucgcpmgcp.SetFrequency(math.Abs(h.gridFrequency)) + _ = h.ucmumpc.SetFrequency(math.Abs(h.gridFrequency)) + switch h.gridFrequency { + case 50: + h.gridFrequency = 51 + case 51: + h.gridFrequency = -50 + case -50: + h.gridFrequency = 49 + case 49: + h.gridFrequency = 50 + } + } + }() + + h.myService.Start() + // defer h.myService.Shutdown() +} + +// Monitoring Appliance MGCP Event Handler + +func (h *smartmeter) OnMGCPEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} + +// Monitored Unit MPC Event Handler + +func (h *smartmeter) OnMPCEvent(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event api.EventType) { +} + +// EEBUSServiceHandler + +func (h *smartmeter) RemoteSKIConnected(service api.ServiceInterface, ski string) { + time.AfterFunc(1*time.Second, func() { + _ = h.ucgcpmgcp.SetPowerLimitationFactor(h.gridPowerLimitFactor) + }) +} + +func (h *smartmeter) RemoteSKIDisconnected(service api.ServiceInterface, ski string) {} + +func (h *smartmeter) VisibleRemoteServicesUpdated(service api.ServiceInterface, entries []shipapi.RemoteService) { + for _, element := range entries { + if element.Ski == remoteSki { + service := h.myService.RemoteServiceForSKI(element.Ski) + service.SetTrusted(true) + } + } +} + +func (h *smartmeter) ServiceShipIDUpdate(ski string, shipdID string) {} + +func (h *smartmeter) ServicePairingDetailUpdate(ski string, detail *shipapi.ConnectionStateDetail) { + if ski == remoteSki && detail.State() == shipapi.ConnectionStateRemoteDeniedTrust { + fmt.Println("The remote service denied trust. Exiting.") + h.myService.CancelPairingWithSKI(ski) + h.myService.UnregisterRemoteSKI(ski) + h.myService.Shutdown() + os.Exit(0) + } +} + +func (h *smartmeter) AllowWaitingForTrust(ski string) bool { + return ski == remoteSki +} + +// main app +func usage() { + fmt.Println("First Run:") + fmt.Println(" go run /examples/smartmeter/main.go ") + fmt.Println() + fmt.Println("General Usage:") + fmt.Println(" go run /examples/smartmeter/main.go ") +} + +func main() { + if len(os.Args) < 2 { + usage() + return + } + + h := smartmeter{} + h.run() + + // Clean exit to make sure mdns shutdown is invoked + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM) + <-sig + // User exit +} + +// Logging interface + +func (h *smartmeter) Trace(args ...interface{}) { + h.print("TRACE", args...) +} + +func (h *smartmeter) Tracef(format string, args ...interface{}) { + h.printFormat("TRACE", format, args...) +} + +func (h *smartmeter) Debug(args ...interface{}) { + h.print("DEBUG", args...) +} + +func (h *smartmeter) Debugf(format string, args ...interface{}) { + h.printFormat("DEBUG", format, args...) +} + +func (h *smartmeter) Info(args ...interface{}) { + h.print("INFO ", args...) +} + +func (h *smartmeter) Infof(format string, args ...interface{}) { + h.printFormat("INFO ", format, args...) +} + +func (h *smartmeter) Error(args ...interface{}) { + h.print("ERROR", args...) +} + +func (h *smartmeter) Errorf(format string, args ...interface{}) { + h.printFormat("ERROR", format, args...) +} + +func (h *smartmeter) currentTimestamp() string { + return time.Now().Format("2006-01-02 15:04:05") +} + +func (h *smartmeter) print(msgType string, args ...interface{}) { + value := fmt.Sprintln(args...) + fmt.Printf("%s %s %s", h.currentTimestamp(), msgType, value) +} + +func (h *smartmeter) printFormat(msgType, format string, args ...interface{}) { + value := fmt.Sprintf(format, args...) + fmt.Println(h.currentTimestamp(), msgType, value) +} diff --git a/usecases/api/gcp_mgcp.go b/usecases/api/gcp_mgcp.go new file mode 100644 index 00000000..ac6f9f2e --- /dev/null +++ b/usecases/api/gcp_mgcp.go @@ -0,0 +1,56 @@ +package api + +import ( + "github.com/enbility/eebus-go/api" +) + +// Actor: Grid Connection Point +// UseCase: Monitoring of Grid Connection Point +type GcpMGCPInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // set the current power limitation factor + SetPowerLimitationFactor(value float64) (resultErr error) + + // Scenario 2 + + // set the momentary power consumption or production at the grid connection point + // + // parameters: + // - positive values are used for consumption + // - negative values are used for production + SetPower(value float64) (resultErr error) + + // Scenario 3 + + // set the total feed in energy at the grid connection point + // + // parameters: + // - negative values are used for production + SetEnergyFeedIn(value float64) (resultErr error) + + // Scenario 4 + + // set the total consumption energy at the grid connection point + // + // parameters: + // - positive values are used for consumption + SetEnergyConsumed(value float64) (resultErr error) + + // Scenario 5 + + // set the current phase details at the grid connection point + SetCurrentPerPhase(value []float64) (resultErr error) + + // Scenario 6 + + // set the voltage phase details at the grid connection point + SetVoltagePerPhase(value []float64) (resultErr error) + + // Scenario 7 + + // set frequency at the grid connection point + SetFrequency(value float64) (resultErr error) +} diff --git a/usecases/api/mu_mpc.go b/usecases/api/mu_mpc.go new file mode 100644 index 00000000..e2a5eeaf --- /dev/null +++ b/usecases/api/mu_mpc.go @@ -0,0 +1,80 @@ +package api + +import ( + "github.com/enbility/eebus-go/api" +) + +// Actor: Monitored Unit +// UseCase: Monitoring of Power Consumption +type MuMPCInterface interface { + api.UseCaseInterface + + // Scenario 1 + + // return the momentary active power consumption or production + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + SetPower(value float64) (resultErr error) + + // return the momentary active phase specific power consumption or production per phase + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // possible errors: + // - ErrDataNotAvailable if no such limit is (yet) available + // - and others + SetPowerPerPhase(value []float64) (resultErr error) + + // Scenario 2 + + // return the total consumption energy + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // - positive values are used for consumption + SetEnergyConsumed(value float64) (resultErr error) + + // return the total feed in energy + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // return values: + // - negative values are used for production + SetEnergyProduced(value float64) (resultErr error) + + // Scenario 3 + + // return the momentary phase specific current consumption or production + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + // + // return values + // - positive values are used for consumption + // - negative values are used for production + SetCurrentPerPhase(value []float64) (resultErr error) + + // Scenario 4 + + // return the phase specific voltage details + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + SetVoltagePerPhase(value []float64) (resultErr error) + + // Scenario 5 + + // return frequency + // + // parameters: + // - entity: the entity of the device (e.g. EVSE) + SetFrequency(value float64) (resultErr error) +} diff --git a/usecases/gcp/mgcp/public.go b/usecases/gcp/mgcp/public.go new file mode 100644 index 00000000..2ec3003c --- /dev/null +++ b/usecases/gcp/mgcp/public.go @@ -0,0 +1,129 @@ +package mgcp + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" +) + +func (e *MGCP) SetValue(value float64, mid model.MeasurementIdType) error { + measure, err := server.NewMeasurement(e.LocalEntity) + + if err != nil { + return err + } + + ids := []api.MeasurementDataForID{ + { + Id: mid, + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(value), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + } + + return measure.UpdateDataForIds(ids) +} + +func (e *MGCP) SetValues(values []float64, mids []model.MeasurementIdType) error { + measure, err := server.NewMeasurement(e.LocalEntity) + + if err != nil { + return err + } + + ids := []api.MeasurementDataForID{ + { + Id: mids[0], + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(values[0]), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + { + Id: mids[1], + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(values[1]), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + { + Id: mids[2], + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(values[2]), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + } + + return measure.UpdateDataForIds(ids) +} + +// Scenario 2 + +// set the current power limitation factor +func (e *MGCP) SetPowerLimitationFactor(value float64) error { + dcs, err := server.NewDeviceConfiguration(e.LocalEntity) + + if err != nil { + return err + } + + val := &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(value), + } + _ = dcs.UpdateKeyValueDataForFilter( + model.DeviceConfigurationKeyValueDataType{ + Value: val, + IsValueChangeable: util.Ptr(true), + }, + nil, + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor), + }, + ) + + return nil +} + +// set the current power +func (e *MGCP) SetPower(value float64) error { + return e.SetValue(value, model.MeasurementIdType(0)) +} + +// Scenario 3 + +// set the EnergyFeedIn +func (e *MGCP) SetEnergyFeedIn(value float64) error { + return e.SetValue(value, model.MeasurementIdType(1)) +} + +// Scenario 4 + +// set the EnergyConsumed +func (e *MGCP) SetEnergyConsumed(value float64) error { + return e.SetValue(value, model.MeasurementIdType(2)) +} + +// // Scenario 5 + +// set the CurrentPerPhase +func (e *MGCP) SetCurrentPerPhase(values []float64) error { + return e.SetValues(values, []model.MeasurementIdType{model.MeasurementIdType(3), model.MeasurementIdType(4), model.MeasurementIdType(5)}) +} + +// // Scenario 6 + +// set the VoltagePerPhase +func (e *MGCP) SetVoltagePerPhase(values []float64) error { + return e.SetValues(values, []model.MeasurementIdType{model.MeasurementIdType(6), model.MeasurementIdType(7), model.MeasurementIdType(8)}) +} + +// Scenario 7 + +// set the Frequency +func (e *MGCP) SetFrequency(value float64) error { + return e.SetValue(value, model.MeasurementIdType(9)) +} diff --git a/usecases/gcp/mgcp/types.go b/usecases/gcp/mgcp/types.go new file mode 100644 index 00000000..083d5864 --- /dev/null +++ b/usecases/gcp/mgcp/types.go @@ -0,0 +1,10 @@ +package mgcp + +import "github.com/enbility/eebus-go/api" + +const ( + // Update of the list of remote entities supporting the Use Case + // + // Use `RemoteEntities` to get the current data + UseCaseSupportUpdate api.EventType = "gcp-mgcp-UseCaseSupportUpdate" +) diff --git a/usecases/gcp/mgcp/usecase.go b/usecases/gcp/mgcp/usecase.go new file mode 100644 index 00000000..545b2dbf --- /dev/null +++ b/usecases/gcp/mgcp/usecase.go @@ -0,0 +1,364 @@ +package mgcp + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + ucapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/eebus-go/usecases/usecase" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/enbility/spine-go/util" +) + +type MGCP struct { + *usecase.UseCaseBase +} + +var _ ucapi.GcpMGCPInterface = (*MGCP)(nil) + +// Add support for the Monitoring of Grid Connection Point (MGCP) use case +// as a Grid Connection Point actor +// +// Parameters: +// - localEntity: The local entity which should support the use case +// - eventCB: The callback to be called when an event is triggered (optional, can be nil) +func NewMGCP(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCallback) *MGCP { + validActorTypes := []model.UseCaseActorType{model.UseCaseActorTypeGridConnectionPoint} + validEntityTypes := []model.EntityTypeType{ + model.EntityTypeTypeCEM, + model.EntityTypeTypeGridConnectionPointOfPremises, + } + useCaseScenarios := []api.UseCaseScenario{ + { + Scenario: model.UseCaseScenarioSupportType(1), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(2), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(3), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(4), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(5), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(6), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(7), + Mandatory: true, + }, + } + + usecase := usecase.NewUseCaseBase( + localEntity, + model.UseCaseActorTypeGridConnectionPoint, + model.UseCaseNameTypeMonitoringOfGridConnectionPoint, + "1.0.0", + "release", + useCaseScenarios, + eventCB, + UseCaseSupportUpdate, + validActorTypes, + validEntityTypes, + ) + + uc := &MGCP{ + UseCaseBase: usecase, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +func (e *MGCP) AddFeatures() { + // server features + f := e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeDeviceConfiguration, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeDeviceConfigurationKeyValueListData, true, true) + + if dcs, err := server.NewDeviceConfiguration(e.LocalEntity); err == nil { + dcs.AddKeyValueDescription( + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor), + ValueType: util.Ptr(model.DeviceConfigurationKeyValueTypeTypeScaledNumber), + Unit: util.Ptr(model.UnitOfMeasurementTypepct), + }, + ) + + value := &model.DeviceConfigurationKeyValueValueType{ + ScaledNumber: model.NewScaledNumberType(0), + } + _ = dcs.UpdateKeyValueDataForFilter( + model.DeviceConfigurationKeyValueDataType{ + Value: value, + IsValueChangeable: util.Ptr(true), + }, + nil, + model.DeviceConfigurationKeyValueDescriptionDataType{ + KeyName: util.Ptr(model.DeviceConfigurationKeyNameTypePvCurtailmentLimitFactor), + }, + ) + } + + f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeMeasurementListData, true, false) + + descDataList := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: util.Ptr(model.MeasurementIdType(0)), + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(1)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeGridFeedIn), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(2)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeGridConsumption), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(3)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(4)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(5)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(6)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(7)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(8)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(9)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeHz), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + }, + }, + } + + f.SetData(model.FunctionTypeMeasurementDescriptionListData, descDataList) + + if _, err := server.NewMeasurement(e.LocalEntity); err == nil { + } + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: util.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(3)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(4)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(5)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(6)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(7)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(8)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(9)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + } + + f.SetData(model.FunctionTypeMeasurementListData, measData) + + f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeElectricalConnectionCharacteristicListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, false) + + descList := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + AcConnectedPhases: util.Ptr(uint(3)), + }, + }, + } + + f.SetData(model.FunctionTypeElectricalConnectionDescriptionListData, descList) + + paramDescList := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(0)), // acPowerTotal + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(1)), // gridFeedIn + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(2)), // gridConsumption + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(3)), // current a + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(4)), // current b + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(5)), // current c + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(6)), // voltage a + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(7)), // voltage b + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(8)), // voltage c + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(9)), // frequency + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + }, + }, + } + + f.SetData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDescList) + + if _, err := server.NewElectricalConnection(e.LocalEntity); err == nil { + } +} diff --git a/usecases/mu/mpc/public.go b/usecases/mu/mpc/public.go new file mode 100644 index 00000000..0e53aada --- /dev/null +++ b/usecases/mu/mpc/public.go @@ -0,0 +1,107 @@ +package mpc + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/util" +) + +func (e *MPC) SetValue(value float64, mid model.MeasurementIdType) error { + measure, err := server.NewMeasurement(e.LocalEntity) + + if err != nil { + return err + } + + ids := []api.MeasurementDataForID{ + { + Id: mid, + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(value), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + } + + return measure.UpdateDataForIds(ids) +} + +func (e *MPC) SetValues(values []float64, mids []model.MeasurementIdType) error { + measure, err := server.NewMeasurement(e.LocalEntity) + + if err != nil { + return err + } + + ids := []api.MeasurementDataForID{ + { + Id: mids[0], + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(values[0]), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + { + Id: mids[1], + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(values[1]), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + { + Id: mids[2], + Data: model.MeasurementDataType{ + Value: model.NewScaledNumberType(values[2]), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + }, + }, + } + + return measure.UpdateDataForIds(ids) +} + +// Scenario 1 + +// set the current power +func (e *MPC) SetPower(value float64) error { + return e.SetValue(value, model.MeasurementIdType(0)) +} + +// set the PowerPerPhase +func (e *MPC) SetPowerPerPhase(values []float64) error { + return e.SetValues(values, []model.MeasurementIdType{model.MeasurementIdType(1), model.MeasurementIdType(2), model.MeasurementIdType(3)}) +} + +// Scenario 2 + +// set the EnergyConsumed +func (e *MPC) SetEnergyConsumed(value float64) error { + return e.SetValue(value, model.MeasurementIdType(4)) +} + +// set the EnergyProduced +func (e *MPC) SetEnergyProduced(value float64) error { + return e.SetValue(value, model.MeasurementIdType(5)) +} + +// Scenario 3 + +// set the CurrentPerPhase +func (e *MPC) SetCurrentPerPhase(values []float64) error { + return e.SetValues(values, []model.MeasurementIdType{model.MeasurementIdType(6), model.MeasurementIdType(7), model.MeasurementIdType(8)}) +} + +// Scenario 4 + +// set the VoltagePerPhase +func (e *MPC) SetVoltagePerPhase(values []float64) error { + return e.SetValues(values, []model.MeasurementIdType{model.MeasurementIdType(9), model.MeasurementIdType(10), model.MeasurementIdType(11)}) +} + +// Scenario 5 + +// set the Frequency +func (e *MPC) SetFrequency(value float64) error { + return e.SetValue(value, model.MeasurementIdType(12)) +} diff --git a/usecases/mu/mpc/types.go b/usecases/mu/mpc/types.go new file mode 100644 index 00000000..67f2d3f0 --- /dev/null +++ b/usecases/mu/mpc/types.go @@ -0,0 +1,10 @@ +package mpc + +import "github.com/enbility/eebus-go/api" + +const ( + // Update of the list of remote entities supporting the Use Case + // + // Use `RemoteEntities` to get the current data + UseCaseSupportUpdate api.EventType = "mu-mpc-UseCaseSupportUpdate" +) diff --git a/usecases/mu/mpc/usecase.go b/usecases/mu/mpc/usecase.go new file mode 100644 index 00000000..95f6bb62 --- /dev/null +++ b/usecases/mu/mpc/usecase.go @@ -0,0 +1,404 @@ +package mpc + +import ( + "github.com/enbility/eebus-go/api" + "github.com/enbility/eebus-go/features/server" + ucapi "github.com/enbility/eebus-go/usecases/api" + "github.com/enbility/eebus-go/usecases/usecase" + spineapi "github.com/enbility/spine-go/api" + "github.com/enbility/spine-go/model" + "github.com/enbility/spine-go/spine" + "github.com/enbility/spine-go/util" +) + +type MPC struct { + *usecase.UseCaseBase +} + +var _ ucapi.MuMPCInterface = (*MPC)(nil) + +// Add support for the Monitoring of Power Consumption (MPC) use case +// as a Monitored Unit actor +// +// Parameters: +// - localEntity: The local entity which should support the use case +// - eventCB: The callback to be called when an event is triggered (optional, can be nil) +func NewMPC(localEntity spineapi.EntityLocalInterface, eventCB api.EntityEventCallback) *MPC { + validActorTypes := []model.UseCaseActorType{model.UseCaseActorTypeMonitoredUnit} + validEntityTypes := []model.EntityTypeType{ + model.EntityTypeTypeCompressor, + model.EntityTypeTypeElectricalImmersionHeater, + model.EntityTypeTypeEVSE, + model.EntityTypeTypeHeatPumpAppliance, + model.EntityTypeTypeInverter, + model.EntityTypeTypeSmartEnergyAppliance, + model.EntityTypeTypeSubMeterElectricity, + } + useCaseScenarios := []api.UseCaseScenario{ + { + Scenario: model.UseCaseScenarioSupportType(1), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(2), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(3), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(4), + Mandatory: true, + }, + { + Scenario: model.UseCaseScenarioSupportType(5), + Mandatory: true, + }, + } + + usecase := usecase.NewUseCaseBase( + localEntity, + model.UseCaseActorTypeMonitoredUnit, + model.UseCaseNameTypeMonitoringOfPowerConsumption, + "1.0.0", + "release", + useCaseScenarios, + eventCB, + UseCaseSupportUpdate, + validActorTypes, + validEntityTypes, + ) + + uc := &MPC{ + UseCaseBase: usecase, + } + + _ = spine.Events.Subscribe(uc) + + return uc +} + +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) + + descList := &model.ElectricalConnectionDescriptionListDataType{ + ElectricalConnectionDescriptionData: []model.ElectricalConnectionDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + PowerSupplyType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + PositiveEnergyDirection: util.Ptr(model.EnergyDirectionTypeConsume), + AcConnectedPhases: util.Ptr(uint(3)), + }, + }, + } + + f.SetData(model.FunctionTypeElectricalConnectionDescriptionListData, descList) + + paramDescList := &model.ElectricalConnectionParameterDescriptionListDataType{ + ElectricalConnectionParameterDescriptionData: []model.ElectricalConnectionParameterDescriptionDataType{ + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(0)), // power total + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeAbc), + AcMeasuredInReferenceTo: util.Ptr(model.ElectricalConnectionPhaseNameTypeNeutral), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(1)), // power a + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(2)), // power b + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(3)), // power c + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(4)), // energy consumed + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(5)), // energy produced + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(6)), // current a + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeA), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(7)), // current b + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeB), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(8)), // current c + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + AcMeasuredPhases: util.Ptr(model.ElectricalConnectionPhaseNameTypeC), + AcMeasurementType: util.Ptr(model.ElectricalConnectionAcMeasurementTypeTypeReal), + AcMeasurementVariant: util.Ptr(model.ElectricalConnectionMeasurandVariantTypeRms), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(9)), // voltage a + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(10)), // voltage b + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(11)), // voltage c + 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), + }, + { + ElectricalConnectionId: util.Ptr(model.ElectricalConnectionIdType(0)), + MeasurementId: util.Ptr(model.MeasurementIdType(12)), // frequency + VoltageType: util.Ptr(model.ElectricalConnectionVoltageTypeTypeAc), + }, + }, + } + + f.SetData(model.FunctionTypeElectricalConnectionParameterDescriptionListData, paramDescList) + + if _, err := server.NewElectricalConnection(e.LocalEntity); err == nil { + } + + f = e.LocalEntity.GetOrAddFeature(model.FeatureTypeTypeMeasurement, model.RoleTypeServer) + f.AddFunctionType(model.FunctionTypeMeasurementDescriptionListData, true, false) + f.AddFunctionType(model.FunctionTypeMeasurementListData, true, false) + + descDataList := &model.MeasurementDescriptionListDataType{ + MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ + { + MeasurementId: util.Ptr(model.MeasurementIdType(0)), + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPowerTotal), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(1)), + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(2)), + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(3)), + MeasurementType: util.Ptr(model.MeasurementTypeTypePower), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeW), + ScopeType: util.Ptr(model.ScopeTypeTypeACPower), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(4)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyConsumed), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(5)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeEnergy), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeWh), + ScopeType: util.Ptr(model.ScopeTypeTypeACEnergyProduced), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(6)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(7)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(8)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeCurrent), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeA), + ScopeType: util.Ptr(model.ScopeTypeTypeACCurrent), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(9)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(10)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(11)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeVoltage), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeV), + ScopeType: util.Ptr(model.ScopeTypeTypeACVoltage), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(12)), + MeasurementType: util.Ptr(model.MeasurementTypeTypeFrequency), + CommodityType: util.Ptr(model.CommodityTypeTypeElectricity), + Unit: util.Ptr(model.UnitOfMeasurementTypeHz), + ScopeType: util.Ptr(model.ScopeTypeTypeACFrequency), + }, + }, + } + + f.SetData(model.FunctionTypeMeasurementDescriptionListData, descDataList) + + if _, err := server.NewMeasurement(e.LocalEntity); err == nil { + } + + measData := &model.MeasurementListDataType{ + MeasurementData: []model.MeasurementDataType{ + { + MeasurementId: util.Ptr(model.MeasurementIdType(0)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(1)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(2)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(3)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(4)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(5)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(6)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(7)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(8)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(9)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(10)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(11)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + { + MeasurementId: util.Ptr(model.MeasurementIdType(12)), + Value: model.NewScaledNumberType(0), + ValueType: util.Ptr(model.MeasurementValueTypeTypeValue), + ValueSource: util.Ptr(model.MeasurementValueSourceTypeMeasuredValue), + }, + }, + } + + f.SetData(model.FunctionTypeMeasurementListData, measData) +}