Skip to content

Commit

Permalink
Major updates
Browse files Browse the repository at this point in the history
- Rewrite heartbeat management
  - Runs on the local device as binding and subscription manager
  - Updates local data function data store, which will then automatically trigger notifications to all subscribers
  - Only handles heartbeat creation (and sending)!
  - Ensures heartbeat related requirements (heartbeat id global, data only updated on an actual heartbeat, etc.)
  - A heartbeat interval has to be provided when initialising the service!
  - Heartbeats automatically stop/resume depending on existing subscribers
  - Heartbeat Manager currently only works for a single entity requiring/providing heartbeats
- Update binding and subscription managers
  - Add tests
  - Use sync.Mutex to avoid race conditions
  - Use new DeepCopy helper to allow safe editing of model data for its own usage reasons and avoid crashes
  - Fix non working Remove implementations
  - Pass localDevice on initialisation instead on each function call
- Update service configuration
  - Add list of entityTypes that will be created, at least 1 is required to be provided
  - Add heartbeat timeout
- Update services API
  - Remove publich functions that just pass through accessible localDevice functions
- Update features
  - update feature creation API to require corresponding local entity instead of local device
  - move tests into features_test package
- Update DeviceLocalImpl API
  - remove FeatureByTypeAndRole, instead the EntityLocalImpl FeatureOfTypeAndRole should be used
- Add a new helper `DeepCopy` to easily copy SPINE model data structures, which is sometimes required to be used to avoid race conditions
- Update spine sender
  - Add local cache for sent notify messages and public interface method to search the cache
  - The cache allows to find notify messages causing the remote device to send an error message != 0 and implement some means of error handling for these
  - Fix linter warning because of duplicate file names for mock and implementation
  - Update generated mock
- Add tests for EntityLocalImpl
- Various data race fixes
- Update various pieces to use new/changed APIs
  • Loading branch information
DerAndereAndi committed Dec 29, 2023
1 parent 335c5c6 commit a56f838
Show file tree
Hide file tree
Showing 52 changed files with 936 additions and 417 deletions.
4 changes: 3 additions & 1 deletion cmd/evse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ func (h *evse) run() {

configuration, err := service.NewConfiguration(
"Demo", "Demo", "EVSE", "234567890",
model.DeviceTypeTypeChargingStation, port, certificate, 230)
model.DeviceTypeTypeChargingStation,
[]model.EntityTypeType{model.EntityTypeTypeEVSE},
port, certificate, 230, time.Second*4)
if err != nil {
log.Fatal(err)
}
Expand Down
4 changes: 3 additions & 1 deletion cmd/hems/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ func (h *hems) run() {

configuration, err := service.NewConfiguration(
"Demo", "Demo", "HEMS", "123456789",
model.DeviceTypeTypeEnergyManagementSystem, port, certificate, 230)
model.DeviceTypeTypeEnergyManagementSystem,
[]model.EntityTypeType{model.EntityTypeTypeCEM},
port, certificate, 230, time.Second*4)
if err != nil {
log.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions features/deviceclassification.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type DeviceClassification struct {
*FeatureImpl
}

func NewDeviceClassification(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*DeviceClassification, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, spineLocalDevice, entity)
func NewDeviceClassification(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceClassification, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceClassification, localRole, remoteRole, localEntity, remoteEntity)
if err != nil {
return nil, err
}
Expand Down
11 changes: 6 additions & 5 deletions features/deviceclassification_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package features
package features_test

import (
"testing"

"github.com/enbility/eebus-go/features"
"github.com/enbility/eebus-go/spine"
"github.com/enbility/eebus-go/spine/model"
"github.com/enbility/eebus-go/util"
Expand All @@ -17,10 +18,10 @@ func TestDeviceClassificationSuite(t *testing.T) {
type DeviceClassificationSuite struct {
suite.Suite

localDevice *spine.DeviceLocalImpl
localEntity *spine.EntityLocalImpl
remoteEntity *spine.EntityRemoteImpl

deviceClassification *DeviceClassification
deviceClassification *features.DeviceClassification
sentMessage []byte
}

Expand All @@ -31,7 +32,7 @@ func (s *DeviceClassificationSuite) WriteSpineMessage(message []byte) {
}

func (s *DeviceClassificationSuite) BeforeTest(suiteName, testName string) {
s.localDevice, s.remoteEntity = setupFeatures(
s.localEntity, s.remoteEntity = setupFeatures(
s.T(),
s,
[]featureFunctions{
Expand All @@ -45,7 +46,7 @@ func (s *DeviceClassificationSuite) BeforeTest(suiteName, testName string) {
)

var err error
s.deviceClassification, err = NewDeviceClassification(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity)
s.deviceClassification, err = features.NewDeviceClassification(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), s.deviceClassification)
}
Expand Down
4 changes: 2 additions & 2 deletions features/deviceconfiguration.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type DeviceConfiguration struct {
*FeatureImpl
}

func NewDeviceConfiguration(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*DeviceConfiguration, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, spineLocalDevice, entity)
func NewDeviceConfiguration(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceConfiguration, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceConfiguration, localRole, remoteRole, localEntity, remoteEntity)
if err != nil {
return nil, err
}
Expand Down
11 changes: 6 additions & 5 deletions features/deviceconfiguration_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package features
package features_test

import (
"testing"

"github.com/enbility/eebus-go/features"
"github.com/enbility/eebus-go/spine"
"github.com/enbility/eebus-go/spine/model"
"github.com/enbility/eebus-go/util"
Expand All @@ -17,10 +18,10 @@ func TestDeviceConfigurationSuite(t *testing.T) {
type DeviceConfigurationSuite struct {
suite.Suite

localDevice *spine.DeviceLocalImpl
localEntity *spine.EntityLocalImpl
remoteEntity *spine.EntityRemoteImpl

deviceConfiguration *DeviceConfiguration
deviceConfiguration *features.DeviceConfiguration
sentMessage []byte
}

Expand All @@ -31,7 +32,7 @@ func (s *DeviceConfigurationSuite) WriteSpineMessage(message []byte) {
}

func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) {
s.localDevice, s.remoteEntity = setupFeatures(
s.localEntity, s.remoteEntity = setupFeatures(
s.T(),
s,
[]featureFunctions{
Expand All @@ -46,7 +47,7 @@ func (s *DeviceConfigurationSuite) BeforeTest(suiteName, testName string) {
)

var err error
s.deviceConfiguration, err = NewDeviceConfiguration(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity)
s.deviceConfiguration, err = features.NewDeviceConfiguration(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), s.deviceConfiguration)
}
Expand Down
4 changes: 2 additions & 2 deletions features/devicediagnosis.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type DeviceDiagnosis struct {
*FeatureImpl
}

func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*DeviceDiagnosis, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, spineLocalDevice, entity)
func NewDeviceDiagnosis(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*DeviceDiagnosis, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeDeviceDiagnosis, localRole, remoteRole, localEntity, remoteEntity)
if err != nil {
return nil, err
}
Expand Down
11 changes: 6 additions & 5 deletions features/devicediagnosis_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package features
package features_test

import (
"testing"

"github.com/enbility/eebus-go/features"
"github.com/enbility/eebus-go/spine"
"github.com/enbility/eebus-go/spine/model"
"github.com/enbility/eebus-go/util"
Expand All @@ -17,10 +18,10 @@ func TestDeviceDiagnosisSuite(t *testing.T) {
type DeviceDiagnosisSuite struct {
suite.Suite

localDevice *spine.DeviceLocalImpl
localEntity *spine.EntityLocalImpl
remoteEntity *spine.EntityRemoteImpl

deviceDiagnosis *DeviceDiagnosis
deviceDiagnosis *features.DeviceDiagnosis
sentMessage []byte
}

Expand All @@ -31,7 +32,7 @@ func (s *DeviceDiagnosisSuite) WriteSpineMessage(message []byte) {
}

func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) {
s.localDevice, s.remoteEntity = setupFeatures(
s.localEntity, s.remoteEntity = setupFeatures(
s.T(),
s,
[]featureFunctions{
Expand All @@ -45,7 +46,7 @@ func (s *DeviceDiagnosisSuite) BeforeTest(suiteName, testName string) {
)

var err error
s.deviceDiagnosis, err = NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity)
s.deviceDiagnosis, err = features.NewDeviceDiagnosis(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), s.deviceDiagnosis)
}
Expand Down
4 changes: 2 additions & 2 deletions features/electricalconnection.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type ElectricalConnection struct {
*FeatureImpl
}

func NewElectricalConnection(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*ElectricalConnection, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, spineLocalDevice, entity)
func NewElectricalConnection(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*ElectricalConnection, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeElectricalConnection, localRole, remoteRole, localEntity, remoteEntity)
if err != nil {
return nil, err
}
Expand Down
11 changes: 6 additions & 5 deletions features/electricalconnection_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package features
package features_test

import (
"testing"

"github.com/enbility/eebus-go/features"
"github.com/enbility/eebus-go/spine"
"github.com/enbility/eebus-go/spine/model"
"github.com/enbility/eebus-go/util"
Expand All @@ -17,10 +18,10 @@ func TestElectricalConnectionSuite(t *testing.T) {
type ElectricalConnectionSuite struct {
suite.Suite

localDevice *spine.DeviceLocalImpl
localEntity *spine.EntityLocalImpl
remoteEntity *spine.EntityRemoteImpl

electricalConnection *ElectricalConnection
electricalConnection *features.ElectricalConnection
sentMessage []byte
}

Expand All @@ -31,7 +32,7 @@ func (s *ElectricalConnectionSuite) WriteSpineMessage(message []byte) {
}

func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) {
s.localDevice, s.remoteEntity = setupFeatures(
s.localEntity, s.remoteEntity = setupFeatures(
s.T(),
s,
[]featureFunctions{
Expand All @@ -47,7 +48,7 @@ func (s *ElectricalConnectionSuite) BeforeTest(suiteName, testName string) {
)

var err error
s.electricalConnection, err = NewElectricalConnection(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity)
s.electricalConnection, err = features.NewElectricalConnection(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), s.electricalConnection)
}
Expand Down
20 changes: 11 additions & 9 deletions features/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,26 @@ type FeatureImpl struct {
remoteRole model.RoleType

spineLocalDevice *spine.DeviceLocalImpl
localEntity *spine.EntityLocalImpl

featureLocal spine.FeatureLocal
featureRemote *spine.FeatureRemoteImpl

device *spine.DeviceRemoteImpl
entity *spine.EntityRemoteImpl
remoteDevice *spine.DeviceRemoteImpl
remoteEntity *spine.EntityRemoteImpl
}

var _ Feature = (*FeatureImpl)(nil)

func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*FeatureImpl, error) {
func NewFeatureImpl(featureType model.FeatureTypeType, localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*FeatureImpl, error) {
f := &FeatureImpl{
featureType: featureType,
localRole: localRole,
remoteRole: remoteRole,
spineLocalDevice: spineLocalDevice,
device: entity.Device(),
entity: entity,
spineLocalDevice: localEntity.Device(),
localEntity: localEntity,
remoteDevice: remoteEntity.Device(),
remoteEntity: remoteEntity,
}

var err error
Expand Down Expand Up @@ -95,12 +97,12 @@ func (f *FeatureImpl) requestData(function model.FunctionType, selectors any, el

// internal helper method for getting local and remote feature for a given featureType and a given remoteDevice
func (f *FeatureImpl) getLocalClientAndRemoteServerFeatures() (spine.FeatureLocal, *spine.FeatureRemoteImpl, error) {
if f.entity == nil {
if f.remoteEntity == nil {
return nil, nil, errors.New("invalid remote entity provided")
}

featureLocal := f.spineLocalDevice.FeatureByTypeAndRole(f.featureType, f.localRole)
featureRemote := f.entity.Device().FeatureByEntityTypeAndRole(f.entity, f.featureType, f.remoteRole)
featureLocal := f.localEntity.FeatureOfTypeAndRole(f.featureType, f.localRole)
featureRemote := f.remoteEntity.Device().FeatureByEntityTypeAndRole(f.remoteEntity, f.featureType, f.remoteRole)

if featureLocal == nil {
return nil, nil, errors.New("local feature not found")
Expand Down
13 changes: 8 additions & 5 deletions features/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package features
package features_test

import (
"time"

"github.com/enbility/eebus-go/spine"
"github.com/enbility/eebus-go/spine/model"
"github.com/enbility/eebus-go/util"
Expand All @@ -12,9 +14,9 @@ type featureFunctions struct {
functions []model.FunctionType
}

func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (*spine.DeviceLocalImpl, *spine.EntityRemoteImpl) {
func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, featureFunctions []featureFunctions) (*spine.EntityLocalImpl, *spine.EntityRemoteImpl) {
localDevice := spine.NewDeviceLocalImpl("TestBrandName", "TestDeviceModel", "TestSerialNumber", "TestDeviceCode",
"TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart)
"TestDeviceAddress", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart, time.Second*4)
localEntity := spine.NewEntityLocalImpl(localDevice, model.EntityTypeTypeCEM, spine.NewAddressEntityType([]uint{1}))
localDevice.AddEntity(localEntity)

Expand All @@ -24,7 +26,8 @@ func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, feature
}

remoteDeviceName := "remoteDevice"
remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "test", dataCon)
sender := spine.NewSender(dataCon)
remoteDevice := spine.NewDeviceRemoteImpl(localDevice, "test", sender)
data := &model.NodeManagementDetailedDiscoveryDataType{
DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{
Description: &model.NetworkManagementDeviceDescriptionDataType{
Expand Down Expand Up @@ -81,5 +84,5 @@ func setupFeatures(t assert.TestingT, dataCon spine.SpineDataConnection, feature
assert.NotNil(t, remoteEntities)
assert.NotEqual(t, 0, len(remoteEntities))

return localDevice, remoteEntities[0]
return localEntity, remoteEntities[0]
}
4 changes: 2 additions & 2 deletions features/identification.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type Identification struct {
*FeatureImpl
}

func NewIdentification(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*Identification, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, spineLocalDevice, entity)
func NewIdentification(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*Identification, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeIdentification, localRole, remoteRole, localEntity, remoteEntity)
if err != nil {
return nil, err
}
Expand Down
11 changes: 6 additions & 5 deletions features/identification_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package features
package features_test

import (
"testing"

"github.com/enbility/eebus-go/features"
"github.com/enbility/eebus-go/spine"
"github.com/enbility/eebus-go/spine/model"
"github.com/enbility/eebus-go/util"
Expand All @@ -17,10 +18,10 @@ func TestIdentificationSuite(t *testing.T) {
type IdentificationSuite struct {
suite.Suite

localDevice *spine.DeviceLocalImpl
localEntity *spine.EntityLocalImpl
remoteEntity *spine.EntityRemoteImpl

identification *Identification
identification *features.Identification
sentMessage []byte
}

Expand All @@ -31,7 +32,7 @@ func (s *IdentificationSuite) WriteSpineMessage(message []byte) {
}

func (s *IdentificationSuite) BeforeTest(suiteName, testName string) {
s.localDevice, s.remoteEntity = setupFeatures(
s.localEntity, s.remoteEntity = setupFeatures(
s.T(),
s,
[]featureFunctions{
Expand All @@ -45,7 +46,7 @@ func (s *IdentificationSuite) BeforeTest(suiteName, testName string) {
)

var err error
s.identification, err = NewIdentification(model.RoleTypeServer, model.RoleTypeClient, s.localDevice, s.remoteEntity)
s.identification, err = features.NewIdentification(model.RoleTypeServer, model.RoleTypeClient, s.localEntity, s.remoteEntity)
assert.Nil(s.T(), err)
assert.NotNil(s.T(), s.identification)
}
Expand Down
4 changes: 2 additions & 2 deletions features/incentivetable.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type IncentiveTable struct {
*FeatureImpl
}

func NewIncentiveTable(localRole, remoteRole model.RoleType, spineLocalDevice *spine.DeviceLocalImpl, entity *spine.EntityRemoteImpl) (*IncentiveTable, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, spineLocalDevice, entity)
func NewIncentiveTable(localRole, remoteRole model.RoleType, localEntity *spine.EntityLocalImpl, remoteEntity *spine.EntityRemoteImpl) (*IncentiveTable, error) {
feature, err := NewFeatureImpl(model.FeatureTypeTypeIncentiveTable, localRole, remoteRole, localEntity, remoteEntity)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit a56f838

Please sign in to comment.