From 551b4652036948994e47d5f76a22a36574342a10 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 23 May 2024 11:34:50 +0200 Subject: [PATCH] Various updates - Added tests - Changed event name - Fixed possible panic - Corrected/updated heartbeat message check --- uclpcserver/events.go | 3 ++- uclpcserver/events_test.go | 8 ++++++++ uclpcserver/public.go | 4 ++++ uclpcserver/public_test.go | 38 ++++++++++++++++++++++++++++++++++++++ uclpcserver/types.go | 3 ++- uclppserver/events.go | 3 ++- uclppserver/events_test.go | 8 ++++++++ uclppserver/public.go | 4 ++++ uclppserver/public_test.go | 38 ++++++++++++++++++++++++++++++++++++++ uclppserver/types.go | 3 ++- util/heartbeat.go | 6 +++++- 11 files changed, 113 insertions(+), 5 deletions(-) diff --git a/uclpcserver/events.go b/uclpcserver/events.go index 0c85f9f..c2707d1 100644 --- a/uclpcserver/events.go +++ b/uclpcserver/events.go @@ -34,7 +34,8 @@ func (e *UCLPCServer) HandleEvent(payload spineapi.EventPayload) { } if util.IsHeartbeat(localEntity, payload) { - e.eventCB(payload.Ski, payload.Device, payload.Entity, Heartbeat) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateHeartbeat) + return } if localEntity == nil || diff --git a/uclpcserver/events_test.go b/uclpcserver/events_test.go index b3706bc..eb018ba 100644 --- a/uclpcserver/events_test.go +++ b/uclpcserver/events_test.go @@ -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() { diff --git a/uclpcserver/public.go b/uclpcserver/public.go index 4daebe5..e4e1a29 100644 --- a/uclpcserver/public.go +++ b/uclpcserver/public.go @@ -240,6 +240,10 @@ func (e *UCLPCServer) SetFailsafeDurationMinimum(duration time.Duration, changea // Scenario 3 func (e *UCLPCServer) IsHeartbeatWithinDuration() bool { + if e.heartbeatDiag == nil { + return false + } + return e.heartbeatDiag.IsHeartbeatWithinDuration(2 * time.Minute) } diff --git a/uclpcserver/public_test.go b/uclpcserver/public_test.go index 54fa1d6..fc5cc2d 100644 --- a/uclpcserver/public_test.go +++ b/uclpcserver/public_test.go @@ -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" @@ -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) diff --git a/uclpcserver/types.go b/uclpcserver/types.go index da70ccc..2a1895b 100644 --- a/uclpcserver/types.go +++ b/uclpcserver/types.go @@ -36,6 +36,7 @@ const ( // 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 - Heartbeat api.EventType = "Heartbeat" + DataUpdateHeartbeat api.EventType = "uclpcserver-DataUpdateHeartbeat" ) diff --git a/uclppserver/events.go b/uclppserver/events.go index 51d4cac..80448f3 100644 --- a/uclppserver/events.go +++ b/uclppserver/events.go @@ -34,7 +34,8 @@ func (e *UCLPPServer) HandleEvent(payload spineapi.EventPayload) { } if util.IsHeartbeat(localEntity, payload) { - e.eventCB(payload.Ski, payload.Device, payload.Entity, Heartbeat) + e.eventCB(payload.Ski, payload.Device, payload.Entity, DataUpdateHeartbeat) + return } if localEntity == nil || diff --git a/uclppserver/events_test.go b/uclppserver/events_test.go index 5146c2f..51a8611 100644 --- a/uclppserver/events_test.go +++ b/uclppserver/events_test.go @@ -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() { diff --git a/uclppserver/public.go b/uclppserver/public.go index 9d9e209..fe8ac56 100644 --- a/uclppserver/public.go +++ b/uclppserver/public.go @@ -240,6 +240,10 @@ func (e *UCLPPServer) SetFailsafeDurationMinimum(duration time.Duration, changea // Scenario 3 func (e *UCLPPServer) IsHeartbeatWithinDuration() bool { + if e.heartbeatDiag == nil { + return false + } + return e.heartbeatDiag.IsHeartbeatWithinDuration(2 * time.Minute) } diff --git a/uclppserver/public_test.go b/uclppserver/public_test.go index c022d84..a78d88a 100644 --- a/uclppserver/public_test.go +++ b/uclppserver/public_test.go @@ -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" @@ -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) diff --git a/uclppserver/types.go b/uclppserver/types.go index ea7d712..ddf62eb 100644 --- a/uclppserver/types.go +++ b/uclppserver/types.go @@ -36,6 +36,7 @@ const ( // 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 - Heartbeat api.EventType = "Heartbeat" + DataUpdateHeartbeat api.EventType = "uclpcserver-DataUpdateHeartbeat" ) diff --git a/util/heartbeat.go b/util/heartbeat.go index 32ab989..4890620 100644 --- a/util/heartbeat.go +++ b/util/heartbeat.go @@ -9,7 +9,11 @@ func IsHeartbeat(localEntity spineapi.EntityLocalInterface, payload spineapi.Eve //revive:disable-next-line switch payload.Data.(type) { case *model.DeviceDiagnosisHeartbeatDataType: - return payload.Function == "" && *payload.CmdClassifier == model.CmdClassifierTypeNotify + return payload.Function == model.FunctionTypeDeviceDiagnosisHeartbeatData && + payload.EventType == spineapi.EventTypeDataChange && + payload.ChangeType == spineapi.ElementChangeUpdate && + payload.CmdClassifier != nil && + *payload.CmdClassifier == model.CmdClassifierTypeNotify default: return false }