Skip to content
This repository has been archived by the owner on Jun 29, 2024. It is now read-only.

Feature/forward heartbeat #39

Closed
wants to merge 4 commits into from
Closed
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
3 changes: 3 additions & 0 deletions uclpcserver/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type UCLPCServerInterface interface {
// Scenario 3

// this is automatically covered by the SPINE implementation
//
// returns true, if the last heartbeat is within 2 minutes, otherwise false
IsHeartbeatWithinDuration() bool

// Scenario 4

Expand Down
7 changes: 7 additions & 0 deletions uclpcserver/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) {
return
}

if util.IsHeartbeat(localEntity, payload) {
e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateHeartbeat)
return
}

if localEntity == nil ||
payload.EventType != spineapi.EventTypeDataChange ||
payload.ChangeType != spineapi.ElementChangeUpdate ||
Expand Down Expand Up @@ -98,6 +103,7 @@ func (e *UCLPCServer) deviceConnected(payload spineapi.EventPayload) {
// we only found one matching entity, as it should be, subscribe
if len(deviceDiagEntites) == 1 {
if localDeviceDiag, err := util.DeviceDiagnosis(e.service, deviceDiagEntites[0]); err == nil {
e.heartbeatDiag = localDeviceDiag
if _, err := localDeviceDiag.Subscribe(); err != nil {
logging.Log().Debug(err)
}
Expand All @@ -124,6 +130,7 @@ func (e *UCLPCServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload
}

if localDeviceDiag, err := util.DeviceDiagnosis(e.service, payload.Entity); err == nil {
e.heartbeatDiag = localDeviceDiag
if _, err := localDeviceDiag.Subscribe(); err != nil {
logging.Log().Debug(err)
}
Expand Down
8 changes: 8 additions & 0 deletions uclpcserver/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ func (s *UCLPCServerSuite) Test_Events() {
payload.ChangeType = spineapi.ElementChangeAdd
payload.LocalFeature = s.loadControlFeature
s.sut.HandleEvent(payload)

payload.EventType = spineapi.EventTypeDataChange
payload.ChangeType = spineapi.ElementChangeUpdate
payload.Function = model.FunctionTypeDeviceDiagnosisHeartbeatData
payload.LocalFeature = s.deviceDiagnosisFeature
payload.CmdClassifier = eebusutil.Ptr(model.CmdClassifierTypeNotify)
payload.Data = eebusutil.Ptr(model.DeviceDiagnosisHeartbeatDataType{})
s.sut.HandleEvent(payload)
}

func (s *UCLPCServerSuite) Test_deviceConnected() {
Expand Down
10 changes: 10 additions & 0 deletions uclpcserver/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,16 @@ func (e *UCLPCServer) SetFailsafeDurationMinimum(duration time.Duration, changea
return util.SetLocalDeviceConfigurationKeyValue(e.service, keyName, changeable, keyValue)
}

// Scenario 3

func (e *UCLPCServer) IsHeartbeatWithinDuration() bool {
if e.heartbeatDiag == nil {
return false
}

return e.heartbeatDiag.IsHeartbeatWithinDuration(2 * time.Minute)
}

// Scenario 4

// return nominal maximum active (real) power the Controllable System is
Expand Down
38 changes: 38 additions & 0 deletions uclpcserver/public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"time"

"github.com/enbility/cemd/api"
"github.com/enbility/cemd/util"
eebusutil "github.com/enbility/eebus-go/util"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
Expand Down Expand Up @@ -102,6 +103,43 @@ func (s *UCLPCServerSuite) Test_Failsafe() {
assert.Nil(s.T(), err)
}

func (s *UCLPCServerSuite) Test_IsHeartbeatWithinDuration() {
assert.Nil(s.T(), s.sut.heartbeatDiag)

value := s.sut.IsHeartbeatWithinDuration()
assert.False(s.T(), value)

remoteDiagServer := s.monitoredEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer)
assert.NotNil(s.T(), remoteDiagServer)

var err error
s.sut.heartbeatDiag, err = util.DeviceDiagnosis(s.service, s.monitoredEntity)
assert.NotNil(s.T(), remoteDiagServer)
assert.Nil(s.T(), err)

// add heartbeat data to the remoteDiagServer
timestamp := time.Now().Add(-time.Second * 121)
data := &model.DeviceDiagnosisHeartbeatDataType{
Timestamp: model.NewAbsoluteOrRelativeTimeTypeFromTime(timestamp),
HeartbeatCounter: eebusutil.Ptr(uint64(1)),
HeartbeatTimeout: model.NewDurationType(time.Second * 120),
}
err1 := remoteDiagServer.UpdateData(model.FunctionTypeDeviceDiagnosisHeartbeatData, data, nil, nil)
assert.Nil(s.T(), err1)

value = s.sut.IsHeartbeatWithinDuration()
assert.False(s.T(), value)

timestamp = time.Now()
data.Timestamp = model.NewAbsoluteOrRelativeTimeTypeFromTime(timestamp)

err1 = remoteDiagServer.UpdateData(model.FunctionTypeDeviceDiagnosisHeartbeatData, data, nil, nil)
assert.Nil(s.T(), err1)

value = s.sut.IsHeartbeatWithinDuration()
assert.True(s.T(), value)
}

func (s *UCLPCServerSuite) Test_ContractualConsumptionNominalMax() {
value, err := s.sut.ContractualConsumptionNominalMax()
assert.Equal(s.T(), 0.0, value)
Expand Down
6 changes: 6 additions & 0 deletions uclpcserver/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ const (
//
// Use Case LPC, Scenario 2
DataUpdateFailsafeDurationMinimum api.EventType = "uclpcserver-DataUpdateFailsafeDurationMinimum"

// Indicates a notify heartbeat event the application should care of.
// E.g. going into or out of the Failsafe state
//
// Use Case LPC, Scenario 3
DataUpdateHeartbeat api.EventType = "uclpcserver-DataUpdateHeartbeat"
)
3 changes: 3 additions & 0 deletions uclpcserver/uclpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/enbility/cemd/api"
"github.com/enbility/cemd/util"
eebusapi "github.com/enbility/eebus-go/api"
"github.com/enbility/eebus-go/features"
eebusutil "github.com/enbility/eebus-go/util"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
Expand All @@ -23,6 +24,8 @@ type UCLPCServer struct {
pendingMux sync.Mutex
pendingLimits map[model.MsgCounterType]*spineapi.Message

heartbeatDiag *features.DeviceDiagnosis

heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use
}

Expand Down
3 changes: 3 additions & 0 deletions uclppserver/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type UCLPPServerInterface interface {
// Scenario 3

// this is automatically covered by the SPINE implementation
//
// returns true, if the last heartbeat is within 2 minutes, otherwise false
IsHeartbeatWithinDuration() bool

// Scenario 4

Expand Down
7 changes: 7 additions & 0 deletions uclppserver/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func (e *UCLPPServer) HandleEvent(payload spineapi.EventPayload) {
return
}

if util.IsHeartbeat(localEntity, payload) {
e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateHeartbeat)
return
}

if localEntity == nil ||
payload.EventType != spineapi.EventTypeDataChange ||
payload.ChangeType != spineapi.ElementChangeUpdate ||
Expand Down Expand Up @@ -98,6 +103,7 @@ func (e *UCLPPServer) deviceConnected(payload spineapi.EventPayload) {
// we only found one matching entity, as it should be, subscribe
if len(deviceDiagEntites) == 1 {
if localDeviceDiag, err := util.DeviceDiagnosis(e.service, deviceDiagEntites[0]); err == nil {
e.heartbeatDiag = localDeviceDiag
if _, err := localDeviceDiag.Subscribe(); err != nil {
logging.Log().Debug(err)
}
Expand All @@ -124,6 +130,7 @@ func (e *UCLPPServer) subscribeHeartbeatWorkaround(payload spineapi.EventPayload
}

if localDeviceDiag, err := util.DeviceDiagnosis(e.service, payload.Entity); err == nil {
e.heartbeatDiag = localDeviceDiag
if _, err := localDeviceDiag.Subscribe(); err != nil {
logging.Log().Debug(err)
}
Expand Down
8 changes: 8 additions & 0 deletions uclppserver/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ func (s *UCLPPServerSuite) Test_Events() {
payload.ChangeType = spineapi.ElementChangeAdd
payload.LocalFeature = s.loadControlFeature
s.sut.HandleEvent(payload)

payload.EventType = spineapi.EventTypeDataChange
payload.ChangeType = spineapi.ElementChangeUpdate
payload.Function = model.FunctionTypeDeviceDiagnosisHeartbeatData
payload.LocalFeature = s.deviceDiagnosisFeature
payload.CmdClassifier = eebusutil.Ptr(model.CmdClassifierTypeNotify)
payload.Data = eebusutil.Ptr(model.DeviceDiagnosisHeartbeatDataType{})
s.sut.HandleEvent(payload)
}

func (s *UCLPPServerSuite) Test_deviceConnected() {
Expand Down
10 changes: 10 additions & 0 deletions uclppserver/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,16 @@ func (e *UCLPPServer) SetFailsafeDurationMinimum(duration time.Duration, changea
return util.SetLocalDeviceConfigurationKeyValue(e.service, keyName, changeable, keyValue)
}

// Scenario 3

func (e *UCLPPServer) IsHeartbeatWithinDuration() bool {
if e.heartbeatDiag == nil {
return false
}

return e.heartbeatDiag.IsHeartbeatWithinDuration(2 * time.Minute)
}

// Scenario 4

// return nominal maximum active (real) power the Controllable System is
Expand Down
38 changes: 38 additions & 0 deletions uclppserver/public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"time"

"github.com/enbility/cemd/api"
"github.com/enbility/cemd/util"
eebusutil "github.com/enbility/eebus-go/util"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
Expand Down Expand Up @@ -102,6 +103,43 @@ func (s *UCLPPServerSuite) Test_Failsafe() {
assert.Nil(s.T(), err)
}

func (s *UCLPPServerSuite) Test_IsHeartbeatWithinDuration() {
assert.Nil(s.T(), s.sut.heartbeatDiag)

value := s.sut.IsHeartbeatWithinDuration()
assert.False(s.T(), value)

remoteDiagServer := s.monitoredEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer)
assert.NotNil(s.T(), remoteDiagServer)

var err error
s.sut.heartbeatDiag, err = util.DeviceDiagnosis(s.service, s.monitoredEntity)
assert.NotNil(s.T(), remoteDiagServer)
assert.Nil(s.T(), err)

// add heartbeat data to the remoteDiagServer
timestamp := time.Now().Add(-time.Second * 121)
data := &model.DeviceDiagnosisHeartbeatDataType{
Timestamp: model.NewAbsoluteOrRelativeTimeTypeFromTime(timestamp),
HeartbeatCounter: eebusutil.Ptr(uint64(1)),
HeartbeatTimeout: model.NewDurationType(time.Second * 120),
}
err1 := remoteDiagServer.UpdateData(model.FunctionTypeDeviceDiagnosisHeartbeatData, data, nil, nil)
assert.Nil(s.T(), err1)

value = s.sut.IsHeartbeatWithinDuration()
assert.False(s.T(), value)

timestamp = time.Now()
data.Timestamp = model.NewAbsoluteOrRelativeTimeTypeFromTime(timestamp)

err1 = remoteDiagServer.UpdateData(model.FunctionTypeDeviceDiagnosisHeartbeatData, data, nil, nil)
assert.Nil(s.T(), err1)

value = s.sut.IsHeartbeatWithinDuration()
assert.True(s.T(), value)
}

func (s *UCLPPServerSuite) Test_ContractualProductionNominalMax() {
value, err := s.sut.ContractualProductionNominalMax()
assert.Equal(s.T(), 0.0, value)
Expand Down
6 changes: 6 additions & 0 deletions uclppserver/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ const (
//
// Use Case LPC, Scenario 2
DataUpdateFailsafeDurationMinimum api.EventType = "uclppserver-DataUpdateFailsafeDurationMinimum"

// Indicates a notify heartbeat event the application should care of.
// E.g. going into or out of the Failsafe state
//
// Use Case LPP, Scenario 3
DataUpdateHeartbeat api.EventType = "uclpcserver-DataUpdateHeartbeat"
)
3 changes: 3 additions & 0 deletions uclppserver/uclpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/enbility/cemd/api"
"github.com/enbility/cemd/util"
eebusapi "github.com/enbility/eebus-go/api"
"github.com/enbility/eebus-go/features"
eebusutil "github.com/enbility/eebus-go/util"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
Expand All @@ -23,6 +24,8 @@ type UCLPPServer struct {
pendingMux sync.Mutex
pendingLimits map[model.MsgCounterType]*spineapi.Message

heartbeatDiag *features.DeviceDiagnosis

heartbeatKeoWorkaround bool // required because KEO Stack uses multiple identical entities for the same functionality, and it is not clear which to use
}

Expand Down
20 changes: 20 additions & 0 deletions util/heartbeat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package util

import (
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
)

func IsHeartbeat(localEntity spineapi.EntityLocalInterface, payload spineapi.EventPayload) bool {
//revive:disable-next-line
switch payload.Data.(type) {
case *model.DeviceDiagnosisHeartbeatDataType:
return payload.Function == model.FunctionTypeDeviceDiagnosisHeartbeatData &&
payload.EventType == spineapi.EventTypeDataChange &&
payload.ChangeType == spineapi.ElementChangeUpdate &&
payload.CmdClassifier != nil &&
*payload.CmdClassifier == model.CmdClassifierTypeNotify
default:
return false
}
}
Loading