-
Notifications
You must be signed in to change notification settings - Fork 41
MDT Support #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
MDT Support #123
Changes from all commits
06abbce
8fac6e3
8ac366d
5ec187d
cf71f9f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package api | ||
|
|
||
| import ( | ||
| "github.com/enbility/eebus-go/api" | ||
| spineapi "github.com/enbility/spine-go/api" | ||
| ) | ||
|
|
||
| // Actor: Monitoring Appliance | ||
| // UseCase: Monitoring of DHW Temperature | ||
| type MaMDTInterface interface { | ||
| api.UseCaseInterface | ||
|
|
||
| // Scenario 1 | ||
|
|
||
| // return the momentary temperature of the domestic hot water circuit | ||
| // | ||
| // parameters: | ||
| // - entity: the entity of the device (e.g. DHWCircuit) | ||
| // | ||
| // possible errors: | ||
| // - ErrDataNotAvailable if no such limit is (yet) available | ||
| // - and others | ||
| Temperature(entity spineapi.EntityRemoteInterface) (float64, error) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also need to think about how we want to handle different units. According to the UC spec, the measurement can be reported in different units. I talked to Andreas and we said it makes sense to let the user request the temperatures in different units, so this interface needs to be adapted. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| package mdt | ||
|
|
||
| import ( | ||
| "github.com/enbility/eebus-go/features/client" | ||
| internal "github.com/enbility/eebus-go/usecases/internal" | ||
| "github.com/enbility/ship-go/logging" | ||
| spineapi "github.com/enbility/spine-go/api" | ||
| "github.com/enbility/spine-go/model" | ||
| "github.com/enbility/spine-go/util" | ||
| ) | ||
|
|
||
| // handle SPINE events | ||
| func (e *MDT) HandleEvent(payload spineapi.EventPayload) { | ||
| // only about events from a DHWCircuit entity or device changes for this remote device | ||
|
|
||
| if !e.IsCompatibleEntityType(payload.Entity) { | ||
| return | ||
| } | ||
|
|
||
| if internal.IsEntityConnected(payload) { | ||
| e.deviceConnected(payload.Entity) | ||
| return | ||
| } | ||
|
|
||
| if payload.EventType != spineapi.EventTypeDataChange || | ||
| payload.ChangeType != spineapi.ElementChangeUpdate { | ||
| return | ||
| } | ||
|
|
||
| switch payload.Data.(type) { | ||
| case *model.MeasurementDescriptionListDataType: | ||
| e.deviceMeasurementDescriptionDataUpdate(payload.Entity) | ||
|
|
||
| case *model.MeasurementListDataType: | ||
| e.deviceMeasurementDataUpdate(payload) | ||
| } | ||
| } | ||
|
|
||
| // process required steps when a device is connected | ||
| func (e *MDT) deviceConnected(entity spineapi.EntityRemoteInterface) { | ||
| if measurement, err := client.NewMeasurement(e.LocalEntity, entity); err == nil { | ||
| if !measurement.HasSubscription() { | ||
| if _, err := measurement.Subscribe(); err != nil { | ||
| logging.Log().Error(err) | ||
| } | ||
| } | ||
|
|
||
| // get measurement parameters | ||
| if _, err := measurement.RequestDescriptions(nil, nil); err != nil { | ||
| logging.Log().Error(err) | ||
| } | ||
|
|
||
| if _, err := measurement.RequestConstraints(nil, nil); err != nil { | ||
| logging.Log().Error(err) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // the measurement descriptiondata of a device was updated | ||
| func (e *MDT) deviceMeasurementDescriptionDataUpdate(entity spineapi.EntityRemoteInterface) { | ||
| if measurement, err := client.NewMeasurement(e.LocalEntity, entity); err == nil { | ||
| // measurement descriptions received, now get the data | ||
| if _, err := measurement.RequestData(nil, nil); err != nil { | ||
| logging.Log().Error("Error getting measurement list values:", err) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // the measurement data of a device was updated | ||
| func (e *MDT) deviceMeasurementDataUpdate(payload spineapi.EventPayload) { | ||
| if measurement, err := client.NewMeasurement(e.LocalEntity, payload.Entity); err == nil { | ||
| // Scenario 1 | ||
| filter := model.MeasurementDescriptionDataType{ | ||
| ScopeType: util.Ptr(model.ScopeTypeTypeDhwTemperature), | ||
| } | ||
| if measurement.CheckEventPayloadDataForFilter(payload.Data, filter) && e.EventCB != nil { | ||
| e.EventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateDhwTemperature) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| package mdt | ||
|
|
||
| import ( | ||
| spineapi "github.com/enbility/spine-go/api" | ||
| "github.com/enbility/spine-go/model" | ||
| "github.com/enbility/spine-go/util" | ||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func (s *MaMDTSuite) Test_Events() { | ||
| payload := spineapi.EventPayload{ | ||
| Entity: s.mockRemoteEntity, | ||
| } | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.Entity = s.monitoredEntity | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.EventType = spineapi.EventTypeEntityChange | ||
| payload.ChangeType = spineapi.ElementChangeAdd | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.ChangeType = spineapi.ElementChangeRemove | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.EventType = spineapi.EventTypeDataChange | ||
| payload.ChangeType = spineapi.ElementChangeAdd | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.EventType = spineapi.EventTypeDataChange | ||
| payload.ChangeType = spineapi.ElementChangeUpdate | ||
| payload.Data = util.Ptr(model.MeasurementDescriptionListDataType{}) | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.Data = util.Ptr(model.MeasurementListDataType{}) | ||
| s.sut.HandleEvent(payload) | ||
|
|
||
| payload.Data = util.Ptr(model.NodeManagementUseCaseDataType{}) | ||
| s.sut.HandleEvent(payload) | ||
| } | ||
|
|
||
| func (s *MaMDTSuite) Test_Failures() { | ||
| s.sut.deviceConnected(s.mockRemoteEntity) | ||
|
|
||
| s.sut.deviceMeasurementDescriptionDataUpdate(s.mockRemoteEntity) | ||
| } | ||
|
|
||
| func (s *MaMDTSuite) Test_deviceMeasurementDataUpdate() { | ||
| payload := spineapi.EventPayload{ | ||
| Ski: remoteSki, | ||
| Device: s.remoteDevice, | ||
| Entity: s.monitoredEntity, | ||
| } | ||
| s.sut.deviceMeasurementDataUpdate(payload) | ||
| assert.False(s.T(), s.eventCalled) | ||
|
|
||
| descData := &model.MeasurementDescriptionListDataType{ | ||
| MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ | ||
| { | ||
| MeasurementId: util.Ptr(model.MeasurementIdType(0)), | ||
| ScopeType: util.Ptr(model.ScopeTypeTypeDhwTemperature), | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) | ||
| _, fErr := rFeature.UpdateData(true, model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) | ||
| assert.Nil(s.T(), fErr) | ||
|
|
||
| s.sut.deviceMeasurementDataUpdate(payload) | ||
| assert.False(s.T(), s.eventCalled) | ||
|
|
||
| data := &model.MeasurementListDataType{ | ||
| MeasurementData: []model.MeasurementDataType{ | ||
| { | ||
| MeasurementId: util.Ptr(model.MeasurementIdType(0)), | ||
| Value: model.NewScaledNumberType(10), | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| payload.Data = data | ||
|
|
||
| s.sut.deviceMeasurementDataUpdate(payload) | ||
| assert.True(s.T(), s.eventCalled) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package mdt | ||
|
|
||
| import ( | ||
| "github.com/enbility/eebus-go/api" | ||
| "github.com/enbility/eebus-go/features/client" | ||
| spineapi "github.com/enbility/spine-go/api" | ||
| "github.com/enbility/spine-go/model" | ||
| "github.com/enbility/spine-go/util" | ||
| ) | ||
|
|
||
| // Scenario 1 | ||
|
|
||
| // return the momentary temperature of the domestic hot water circuit | ||
| // | ||
| // possible errors: | ||
| // - ErrDataNotAvailable if no such limit is (yet) available | ||
| // - and others | ||
| func (e *MDT) Temperature(entity spineapi.EntityRemoteInterface) (float64, error) { | ||
| if !e.IsCompatibleEntityType(entity) { | ||
| return 0, api.ErrNoCompatibleEntity | ||
| } | ||
|
|
||
| measurement, err := client.NewMeasurement(e.LocalEntity, entity) | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
|
|
||
| filter := model.MeasurementDescriptionDataType{ | ||
| MeasurementType: util.Ptr(model.MeasurementTypeTypeTemperature), | ||
| CommodityType: util.Ptr(model.CommodityTypeTypeDomestichotwater), | ||
| ScopeType: util.Ptr(model.ScopeTypeTypeDhwTemperature), | ||
| } | ||
| data, err := measurement.GetDataForFilter(filter) | ||
| if err != nil || len(data) == 0 || data[0].Value == nil { | ||
| return 0, api.ErrDataNotAvailable | ||
| } | ||
|
|
||
| // take the first item | ||
| value := data[0].Value | ||
|
|
||
| return value.GetValue(), nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package mdt | ||
|
|
||
| import ( | ||
| "github.com/enbility/spine-go/model" | ||
| "github.com/enbility/spine-go/util" | ||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func (s *MaMDTSuite) Test_Temperature() { | ||
| data, err := s.sut.Temperature(s.mockRemoteEntity) | ||
| assert.NotNil(s.T(), err) | ||
| assert.Equal(s.T(), 0.0, data) | ||
|
|
||
| data, err = s.sut.Temperature(s.monitoredEntity) | ||
| assert.NotNil(s.T(), err) | ||
| assert.Equal(s.T(), 0.0, data) | ||
|
|
||
| descData := &model.MeasurementDescriptionListDataType{ | ||
| MeasurementDescriptionData: []model.MeasurementDescriptionDataType{ | ||
| { | ||
| MeasurementId: util.Ptr(model.MeasurementIdType(0)), | ||
| MeasurementType: util.Ptr(model.MeasurementTypeTypeTemperature), | ||
| CommodityType: util.Ptr(model.CommodityTypeTypeDomestichotwater), | ||
| ScopeType: util.Ptr(model.ScopeTypeTypeDhwTemperature), | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| rFeature := s.remoteDevice.FeatureByEntityTypeAndRole(s.monitoredEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeServer) | ||
| _, fErr := rFeature.UpdateData(true, model.FunctionTypeMeasurementDescriptionListData, descData, nil, nil) | ||
| assert.Nil(s.T(), fErr) | ||
|
|
||
| data, err = s.sut.Temperature(s.monitoredEntity) | ||
| assert.NotNil(s.T(), err) | ||
| assert.Equal(s.T(), 0.0, data) | ||
|
|
||
| measData := &model.MeasurementListDataType{ | ||
| MeasurementData: []model.MeasurementDataType{ | ||
| { | ||
| MeasurementId: util.Ptr(model.MeasurementIdType(0)), | ||
| Value: model.NewScaledNumberType(55), | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| _, fErr = rFeature.UpdateData(true, model.FunctionTypeMeasurementListData, measData, nil, nil) | ||
| assert.Nil(s.T(), fErr) | ||
|
|
||
| data, err = s.sut.Temperature(s.monitoredEntity) | ||
| assert.Nil(s.T(), err) | ||
| assert.Equal(s.T(), 55.0, data) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the Use Case specification, every reading of a DHW temperature can be in a different state: normal, out of range, erroneous. We have to think about how we can handle this.