Skip to content
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

Add support for heartbeats on multiple local entities #34

Merged
merged 2 commits into from
Sep 15, 2024
Merged
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: 0 additions & 3 deletions api/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ type DeviceLocalInterface interface {
// Send a notify message to remote device subscribing to a specific feature
NotifySubscribers(featureAddress *model.FeatureAddressType, cmd model.CmdType)

// Get the hearbeat manager
HeartbeatManager() HeartbeatManagerInterface

// Get the SPINE data structure for NodeManagementDetailDiscoveryData messages for this device
Information() *model.NodeManagementDetailedDiscoveryDeviceInformationType
}
Expand Down
3 changes: 3 additions & 0 deletions api/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ type EntityLocalInterface interface {
// Get the associated DeviceLocalInterface implementation
Device() DeviceLocalInterface

// Get the hearbeat manager for this entity
HeartbeatManager() HeartbeatManagerInterface

// Add a new feature with a given FeatureLocalInterface implementation
AddFeature(f FeatureLocalInterface)
// Get a FeatureLocalInterface implementation for a given feature type and role or create it if it does not exist yet and return it
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ func beforeTest(
fId uint, ftype model.FeatureTypeType,
frole model.RoleType) (api.DeviceLocalInterface, string, api.DeviceRemoteInterface, *WriteMessageHandler) {
sut := spine.NewDeviceLocal("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode",
"TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
localEntity := spine.NewEntityLocal(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1}))
"TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
localEntity := spine.NewEntityLocal(sut, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1}), time.Second*4)
sut.AddEntity(localEntity)
f := spine.NewFeatureLocal(fId, localEntity, ftype, frole)
localEntity.AddFeature(f)
Expand Down
4 changes: 2 additions & 2 deletions spine/binding_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type BindingManagerSuite struct {

func (s *BindingManagerSuite) BeforeTest(suiteName, testName string) {
s.localDevice = NewDeviceLocal("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode",
"TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
"TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
remoteSki := "TestRemoteSki"

s.writeHandler = &WriteMessageHandler{}
Expand All @@ -40,7 +40,7 @@ func (s *BindingManagerSuite) BeforeTest(suiteName, testName string) {
}

func (suite *BindingManagerSuite) Test_Bindings() {
entity := NewEntityLocal(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1})
entity := NewEntityLocal(suite.localDevice, model.EntityTypeTypeCEM, []model.AddressEntityType{1}, time.Second*4)
suite.localDevice.AddEntity(entity)

localFeature := entity.GetOrAddFeature(model.FeatureTypeTypeDeviceDiagnosis, model.RoleTypeServer)
Expand Down
12 changes: 2 additions & 10 deletions spine/device_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"reflect"
"slices"
"sync"
"time"

shipapi "github.com/enbility/ship-go/api"
"github.com/enbility/ship-go/logging"
Expand All @@ -20,7 +19,6 @@ type DeviceLocal struct {
entities []api.EntityLocalInterface
subscriptionManager api.SubscriptionManagerInterface
bindingManager api.BindingManagerInterface
heartbeatManager api.HeartbeatManagerInterface
nodeManagement *NodeManagement

remoteDevices map[string]api.DeviceRemoteInterface
Expand All @@ -41,8 +39,7 @@ type DeviceLocal struct {
func NewDeviceLocal(
brandName, deviceModel, serialNumber, deviceCode, deviceAddress string,
deviceType model.DeviceTypeType,
featureSet model.NetworkManagementFeatureSetType,
heartbeatTimeout time.Duration) *DeviceLocal {
featureSet model.NetworkManagementFeatureSetType) *DeviceLocal {
address := model.AddressDeviceType(deviceAddress)

var fSet *model.NetworkManagementFeatureSetType
Expand All @@ -61,7 +58,6 @@ func NewDeviceLocal(

res.subscriptionManager = NewSubscriptionManager(res)
res.bindingManager = NewBindingManager(res)
res.heartbeatManager = NewHeartbeatManager(res, res.subscriptionManager, heartbeatTimeout)

res.addDeviceInformation()
return res
Expand Down Expand Up @@ -420,10 +416,6 @@ func (r *DeviceLocal) BindingManager() api.BindingManagerInterface {
return r.bindingManager
}

func (r *DeviceLocal) HeartbeatManager() api.HeartbeatManagerInterface {
return r.heartbeatManager
}

func (r *DeviceLocal) Information() *model.NodeManagementDetailedDiscoveryDeviceInformationType {
res := model.NodeManagementDetailedDiscoveryDeviceInformationType{
Description: &model.NetworkManagementDeviceDescriptionDataType{
Expand Down Expand Up @@ -475,7 +467,7 @@ func (r *DeviceLocal) notifySubscribersOfEntity(entity api.EntityLocalInterface,

func (r *DeviceLocal) addDeviceInformation() {
entityType := model.EntityTypeTypeDeviceInformation
entity := NewEntityLocal(r, entityType, []model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)})
entity := NewEntityLocal(r, entityType, []model.AddressEntityType{model.AddressEntityType(DeviceInformationEntityId)}, 0)

{
r.nodeManagement = NewNodeManagement(entity.NextFeatureId(), entity)
Expand Down
20 changes: 10 additions & 10 deletions spine/device_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (d *DeviceLocalTestSuite) WriteShipMessageWithPayload(msg []byte) {
}

func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() {
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)

ski := "test"
_ = sut.SetupRemoteDevice(ski, d)
Expand All @@ -42,8 +42,8 @@ func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() {
}

func (d *DeviceLocalTestSuite) Test_RemoteDevice() {
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}))
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4)
sut.AddEntity(localEntity)

f := NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient)
Expand Down Expand Up @@ -98,7 +98,7 @@ func (d *DeviceLocalTestSuite) Test_RemoteDevice() {
err := sut.SubscriptionManager().AddSubscription(remote, subscription)
assert.Nil(d.T(), err)

newSubEntity := NewEntityLocal(sut, model.EntityTypeTypeEV, NewAddressEntityType([]uint{1, 1}))
newSubEntity := NewEntityLocal(sut, model.EntityTypeTypeEV, NewAddressEntityType([]uint{1, 1}), time.Second*4)
f = NewFeatureLocal(1, newSubEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer)
f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true)
newSubEntity.AddFeature(f)
Expand Down Expand Up @@ -133,8 +133,8 @@ func (d *DeviceLocalTestSuite) Test_RemoteDevice() {
}

func (d *DeviceLocalTestSuite) Test_ProcessCmd_NotifyError() {
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}))
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4)
localFeature := NewFeatureLocal(50, localEntity, model.FeatureTypeTypeSensing, model.RoleTypeClient)
localEntity.AddFeature(localFeature)
sut.AddEntity(localEntity)
Expand Down Expand Up @@ -178,8 +178,8 @@ func (d *DeviceLocalTestSuite) Test_ProcessCmd_NotifyError() {
}

func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() {
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}))
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4)
sut.AddEntity(localEntity)

ski := "test"
Expand Down Expand Up @@ -229,8 +229,8 @@ func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() {
}

func (d *DeviceLocalTestSuite) Test_ProcessCmd() {
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}))
sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4)
sut.AddEntity(localEntity)

f1 := NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient)
Expand Down
3 changes: 1 addition & 2 deletions spine/device_remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package spine

import (
"testing"
"time"

"github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
Expand Down Expand Up @@ -32,7 +31,7 @@ func (s *DeviceRemoteSuite) WriteShipMessageWithPayload([]byte) {}
func (s *DeviceRemoteSuite) SetupSuite() {}

func (s *DeviceRemoteSuite) BeforeTest(suiteName, testName string) {
s.localDevice = NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
s.localDevice = NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)

ski := "test"
sender := NewSender(s)
Expand Down
20 changes: 18 additions & 2 deletions spine/entity_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package spine

import (
"sync"
"time"

"github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
Expand All @@ -12,14 +13,25 @@ type EntityLocal struct {
device api.DeviceLocalInterface
features []api.FeatureLocalInterface

heartbeatManager api.HeartbeatManagerInterface

mux sync.Mutex
}

func NewEntityLocal(device api.DeviceLocalInterface, eType model.EntityTypeType, entityAddress []model.AddressEntityType) *EntityLocal {
return &EntityLocal{
func NewEntityLocal(device api.DeviceLocalInterface,
eType model.EntityTypeType,
entityAddress []model.AddressEntityType,
heartbeatTimeout time.Duration) *EntityLocal {
entity := &EntityLocal{
Entity: NewEntity(eType, device.Address(), entityAddress),
device: device,
}
// only needed if the entity address is not DeviceInformationEntityId
if len(entityAddress) > 0 && entityAddress[0] != model.AddressEntityType(DeviceInformationEntityId) {
entity.heartbeatManager = NewHeartbeatManager(entity, heartbeatTimeout)
}

return entity
}

var _ api.EntityLocalInterface = (*EntityLocal)(nil)
Expand All @@ -30,6 +42,10 @@ func (r *EntityLocal) Device() api.DeviceLocalInterface {
return r.device
}

func (r *EntityLocal) HeartbeatManager() api.HeartbeatManagerInterface {
return r.heartbeatManager
}

// Add a feature to the entity if it is not already added
func (r *EntityLocal) AddFeature(f api.FeatureLocalInterface) {
r.mux.Lock()
Expand Down
4 changes: 2 additions & 2 deletions spine/entity_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ type EntityLocalTestSuite struct {
}

func (suite *EntityLocalTestSuite) Test_Entity() {
device := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
entity := NewEntityLocal(device, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}))
device := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
entity := NewEntityLocal(device, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4)
device.AddEntity(entity)

f := NewFeatureLocal(1, entity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient)
Expand Down
2 changes: 1 addition & 1 deletion spine/feature_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (r *FeatureLocal) AddFunctionType(function model.FunctionType, read, write
r.ftype == model.FeatureTypeTypeDeviceDiagnosis &&
function == model.FunctionTypeDeviceDiagnosisHeartbeatData {
// Update HeartbeatManager
r.Device().HeartbeatManager().SetLocalFeature(r.Entity(), r)
r.Entity().HeartbeatManager().SetLocalFeature(r.Entity(), r)
}
}

Expand Down
11 changes: 4 additions & 7 deletions spine/heartbeat_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,25 @@ import (
)

type HeartbeatManager struct {
localDevice api.DeviceLocalInterface
localEntity api.EntityLocalInterface
localFeature api.FeatureLocalInterface

heartBeatNum uint64 // see https://github.com/golang/go/issues/11891
stopHeartbeatC chan struct{}
stopMux sync.Mutex

subscriptionManager api.SubscriptionManagerInterface
heartBeatTimeout *model.DurationType
heartBeatTimeout *model.DurationType

mux sync.Mutex
}

var _ api.HeartbeatManagerInterface = (*HeartbeatManager)(nil)

// Create a new Heartbeat Manager which handles sending of heartbeats
func NewHeartbeatManager(localDevice api.DeviceLocalInterface, subscriptionManager api.SubscriptionManagerInterface, timeout time.Duration) *HeartbeatManager {
func NewHeartbeatManager(localEntity api.EntityLocalInterface, timeout time.Duration) *HeartbeatManager {
h := &HeartbeatManager{
localDevice: localDevice,
subscriptionManager: subscriptionManager,
heartBeatTimeout: model.NewDurationType(timeout),
localEntity: localEntity,
heartBeatTimeout: model.NewDurationType(timeout),
}

return h
Expand Down
Loading
Loading