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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions usecases/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ Actors:
Use Cases:
- `mpc`: Monitoring of Power Consumption
- `mgcp`: Monitoring of Grid Connection Point
- `mdt`: Monitoring of DHW Temperature
24 changes: 24 additions & 0 deletions usecases/api/ma_mdt.go
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)
Copy link

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.

Copy link

Choose a reason for hiding this comment

The 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.

}
80 changes: 80 additions & 0 deletions usecases/ma/mdt/events.go
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)
}
}
}
86 changes: 86 additions & 0 deletions usecases/ma/mdt/events_test.go
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)
}
42 changes: 42 additions & 0 deletions usecases/ma/mdt/public.go
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
}
52 changes: 52 additions & 0 deletions usecases/ma/mdt/public_test.go
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)
}
Loading
Loading