Skip to content

Commit

Permalink
Cleanup and some documentation
Browse files Browse the repository at this point in the history
- Move device, entity and feature interfaces into their own files
- Remove non needed functions
- Rename a few interface functions to make them more descriptive
- Add interface documentations for device, entity, features, functions
- Reorder implementations to group similar implementations
- General cleanup
- Add more mutex locking were obvious
  • Loading branch information
DerAndereAndi committed Jan 26, 2024
1 parent 6851be3 commit e26a43d
Show file tree
Hide file tree
Showing 35 changed files with 955 additions and 760 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,29 @@ Basic understanding of the EEBUS concepts SHIP and SPINE to use this library is
This repository was started as part of the [eebus-go](https://github.com/enbility/eebus-go) before it was moved into its own repository and this separate go package.

Basic understanding of the EEBUS concepts SHIP and SPINE to use this library is required. Please check the corresponding specifications on the [EEBUS downloads website](https://www.eebus.org/media-downloads/).

## Packages

### api

This package contains required interfaces. They are used extensivly to be able to mock everything and implement tests that focus specificaly on a limited set of interface implementations

### integrationtests

This packge contains tests that cover implementations of multiple packages in concert.

### mocks

This package contains auto generated mocks for the interfaces defined in the api package using [Mockery](https://github.com/vektra/mockery).

### model

This package contains the go represenation of the SPINE data model. It makes use of go tags for proper JSON serialization and also for implementing generic SPINE feature to function and data mapping.

### spine

This package contains the implementation for working with the SPINE devices, entites, features, functions and data.

### util

This package contains generic helpers used by most of the packages, e.g. for working with pointers.
94 changes: 80 additions & 14 deletions api/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,123 @@ import (

/* Device */

// This interface defines the functions being common to local and remote devices
// A device corresponds to a SPINE device, see SPINE Introduction Chapter 2.2
type DeviceInterface interface {
// Get the device address
Address() *model.AddressDeviceType
// Get the device type
DeviceType() *model.DeviceTypeType
// Get the device feature set
FeatureSet() *model.NetworkManagementFeatureSetType
// Get the device destination data
DestinationData() model.NodeManagementDestinationDataType
}

// This interface defines all the required functions need to implement a local device
type DeviceLocalInterface interface {
DeviceInterface
RemoveRemoteDeviceConnection(ski string)
AddRemoteDeviceForSki(ski string, rDevice DeviceRemoteInterface)
// Setup a new remote device with a given SKI and triggers SPINE requesting device details
SetupRemoteDevice(ski string, writeI shipapi.ShipConnectionDataWriterInterface) shipapi.ShipConnectionDataReaderInterface
// Add a DeviceRemoteInterface implementation (used in SetupRemoteDevice and in tests)
AddRemoteDeviceForSki(ski string, rDevice DeviceRemoteInterface)

// Request NodeManagmentDetailedDiscovery Data from a remote device
RequestRemoteDetailedDiscoveryData(rDevice DeviceRemoteInterface) (*model.MsgCounterType, *model.ErrorType)

// Remove a remote device and its connection
RemoveRemoteDeviceConnection(ski string)
// Remove a remote device (used in RemoveRemoteDeviceConnection and in tests)
RemoveRemoteDevice(ski string)

// Get a list of connected remote devices DeviceRemoteInterface implementations
RemoteDevices() []DeviceRemoteInterface
// Get a connected remote device DeviceRemoteInterface implementation for a given device address
RemoteDeviceForAddress(address model.AddressDeviceType) DeviceRemoteInterface
// Get a connected remote device DeviceRemoteInterface implementation for a SKI
RemoteDeviceForSki(ski string) DeviceRemoteInterface
ProcessCmd(datagram model.DatagramType, remoteDevice DeviceRemoteInterface) error
NodeManagement() NodeManagementInterface
SubscriptionManager() SubscriptionManagerInterface
BindingManager() BindingManagerInterface
HeartbeatManager() HeartbeatManagerInterface

// Add a new entity to the device
// It should trigger a notification of all remote devices about this new entity
AddEntity(entity EntityLocalInterface)
// Remove a entity from the device
// It should trigger a notification of all remote devices about this removed entity
RemoveEntity(entity EntityLocalInterface)
// Get a list of all entities EntityLocalInterface implementations
Entities() []EntityLocalInterface
// Get an entity EntityLocalInterface implementation for a given entity address
Entity(id []model.AddressEntityType) EntityLocalInterface
// Get the first entity EntityLocalInterface implementation for a given entity type
EntityForType(entityType model.EntityTypeType) EntityLocalInterface

// Get a FeatureLocalInterface implementation for a given feature address
FeatureByAddress(address *model.FeatureAddressType) FeatureLocalInterface

// Process incoming SPINE datagram
ProcessCmd(datagram model.DatagramType, remoteDevice DeviceRemoteInterface) error

// Get the node management
NodeManagement() NodeManagementInterface

// Get the bindings manager
BindingManager() BindingManagerInterface

// Get the subscription manager
SubscriptionManager() SubscriptionManagerInterface
// 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
}

// This interface defines all the required functions need to implement a remote device
type DeviceRemoteInterface interface {
DeviceInterface
// Get the SKI of a remote device
Ski() string
SetAddress(address *model.AddressDeviceType)
HandleSpineMesssage(message []byte) (*model.MsgCounterType, error)
Sender() SenderInterface

// Add a new entity EntityRemoteInterface implementation
AddEntity(entity EntityRemoteInterface)

// Remove an entity for a given address and return the entity that was removed
RemoveEntityByAddress(addr []model.AddressEntityType) EntityRemoteInterface

// Get an entity EntityRemoteInterface implementation for a given address
Entity(id []model.AddressEntityType) EntityRemoteInterface
// Get all entities EntityRemoteInterface implementations
Entities() []EntityRemoteInterface

// Get a feature FeatureRemoteInterface implementation for a given address
FeatureByAddress(address *model.FeatureAddressType) FeatureRemoteInterface
RemoveByAddress(addr []model.AddressEntityType) EntityRemoteInterface
// Get a feature FeatureRemoteInterface implementation from a given entity EntityRemoteInterface implementation by the feature type and feature role
FeatureByEntityTypeAndRole(entity EntityRemoteInterface, featureType model.FeatureTypeType, role model.RoleType) FeatureRemoteInterface
UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType)
AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemoteInterface, error)
AddEntity(entity EntityRemoteInterface) EntityRemoteInterface

// Process incoming data payload
HandleSpineMesssage(message []byte) (*model.MsgCounterType, error)

// Get the SenderInterface implementation
Sender() SenderInterface

// Get the devices usecase data
UseCases() []model.UseCaseInformationDataType
// Verify if the device supports a usecase depending on the usecase actor, name, scenarios and requires server features
VerifyUseCaseScenariosAndFeaturesSupport(
usecaseActor model.UseCaseActorType,
usecaseName model.UseCaseNameType,
scenarios []model.UseCaseScenarioSupportType,
serverFeatures []model.FeatureTypeType,
) bool

// Update the devices address, type and featureset based on NetworkManagementDeviceDescriptionData
UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType)

// Add entities and their features using provided NodeManagementDetailedDiscoveryData
AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]EntityRemoteInterface, error)

// Helper method for checking incoming NodeManagementDetailedDiscoveryEntityInformation data
CheckEntityInformation(initialData bool, entity model.NodeManagementDetailedDiscoveryEntityInformationType) error
}
50 changes: 44 additions & 6 deletions api/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,39 @@ import "github.com/enbility/spine-go/model"

/* Entity */

// This interface defines the functions being common to local and remote entites
// An entity corresponds to a SPINE entity, see SPINE Introduction Chapter 2.2
type EntityInterface interface {
EntityType() model.EntityTypeType
// Get the entity addres
Address() *model.EntityAddressType
// Get the entity type
EntityType() model.EntityTypeType
// Get the entity description
Description() *model.DescriptionType
// Set the entity description
SetDescription(d *model.DescriptionType)
// Get the next incremental feature id
NextFeatureId() uint
}

// This interface defines all the required functions need to implement a local entity
type EntityLocalInterface interface {
EntityInterface
// Get the associated DeviceLocalInterface implementation
Device() DeviceLocalInterface

// 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
GetOrAddFeature(featureType model.FeatureTypeType, role model.RoleType) FeatureLocalInterface
// Get a FeatureLocalInterface implementation for a given feature type and role
FeatureOfTypeAndRole(featureType model.FeatureTypeType, role model.RoleType) FeatureLocalInterface
// Get a FeatureLocalInterface implementation for a given feature address
FeatureOfAddress(addressFeature *model.AddressFeatureType) FeatureLocalInterface
// Get all FeatureLocalInterface implementations
Features() []FeatureLocalInterface
Feature(addressFeature *model.AddressFeatureType) FeatureLocalInterface
Information() *model.NodeManagementDetailedDiscoveryEntityInformationType

// Add a new usecase
AddUseCaseSupport(
actor model.UseCaseActorType,
useCaseName model.UseCaseNameType,
Expand All @@ -29,24 +45,46 @@ type EntityLocalInterface interface {
useCaseAvailable bool,
scenarios []model.UseCaseScenarioSupportType,
)
// Check if a use case is already added
HasUseCaseSupport(
actor model.UseCaseActorType,
useCaseName model.UseCaseNameType) bool
// Remove support for a usecase
RemoveUseCaseSupport(
actor model.UseCaseActorType,
useCaseName model.UseCaseNameType,
)
// Remove all usecases
RemoveAllUseCaseSupports()

// Remove all subscriptions
RemoveAllSubscriptions()

// Remove all bindings
RemoveAllBindings()

// Get the SPINE data structure for NodeManagementDetailDiscoveryData messages for this entity
Information() *model.NodeManagementDetailedDiscoveryEntityInformationType
}

// This interface defines all the required functions need to implement a remote entity
type EntityRemoteInterface interface {
EntityInterface

// Get the associated DeviceRemoteInterface implementation
Device() DeviceRemoteInterface
AddFeature(f FeatureRemoteInterface)
Features() []FeatureRemoteInterface
Feature(addressFeature *model.AddressFeatureType) FeatureRemoteInterface

// Update the device address (only used for the DeviceInformation entity when receiving the DetailDiscovery reply)
UpdateDeviceAddress(address model.AddressDeviceType)

// Add a new feature with a given FeatureLocalInterface implementation
AddFeature(f FeatureRemoteInterface)

// Remove all features
RemoveAllFeatures()

// Get a FeatureRemoteInterface implementation for a given feature address
FeatureOfAddress(addressFeature *model.AddressFeatureType) FeatureRemoteInterface
// Get all FeatureRemoteInterface implementations
Features() []FeatureRemoteInterface
}
76 changes: 63 additions & 13 deletions api/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,116 @@ import (

/* Feature */

// This interface defines the functions being common to local and remote features
// A feature corresponds to a SPINE feature, see SPINE Introduction Chapter 2.2
type FeatureInterface interface {
// Get the feature address
Address() *model.FeatureAddressType
// Get the feature type
Type() model.FeatureTypeType
// Get the feature role
Role() model.RoleType
// Get the feature operations
Operations() map[model.FunctionType]OperationsInterface
// Get the feature description
Description() *model.DescriptionType
// Set the feature description with a given type
SetDescription(desc *model.DescriptionType)
// Set the feature description with a given string
SetDescriptionString(s string)
// Return a descriptive feature summary as a string
String() string
}

// This interface defines all the required functions need to implement a local feature
type FeatureLocalInterface interface {
FeatureInterface
// Get the associated DeviceLocalInterface implementation
Device() DeviceLocalInterface
// Get the associated EntityLocalInterface implementation
Entity() EntityLocalInterface
DataCopy(function model.FunctionType) any
SetData(function model.FunctionType, data any)

// Add a function type with
AddFunctionType(function model.FunctionType, read, write bool)
// Add a FeatureResultInterface implementation to be able to get incoming result messages for this feature
AddResultHandler(handler FeatureResultInterface)
// Add a callback function to be invoked when SPINE message comes in with a given msgCoutnerReference value
AddResultCallback(msgCounterReference model.MsgCounterType, function func(msg ResultMessage))
Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType
AddFunctionType(function model.FunctionType, read, write bool)

// Get a copy of the features data for a given function type
DataCopy(function model.FunctionType) any
// Set the features data for a given function type
SetData(function model.FunctionType, data any)

// Trigger a read request message for a given FeatureRemoteInterface implementation
RequestRemoteData(
function model.FunctionType,
selector any,
elements any,
destination FeatureRemoteInterface) (*model.MsgCounterType, *model.ErrorType)
// Trigger a read request message for a remote ski and feature address
RequestRemoteDataBySenderAddress(
cmd model.CmdType,
sender SenderInterface,
destinationSki string,
destinationAddress *model.FeatureAddressType,
maxDelay time.Duration) (*model.MsgCounterType, *model.ErrorType)
// Trigger a blocking read request message for a given FeatureRemoteInterface implementation
FetchRequestRemoteData(
msgCounter model.MsgCounterType,
destination FeatureRemoteInterface) (any, *model.ErrorType)
Subscribe(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType)
RemoveSubscription(remoteAddress *model.FeatureAddressType)
RemoveAllSubscriptions()
Bind(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType)
RemoveBinding(remoteAddress *model.FeatureAddressType)
RemoveAllBindings()

// Trigger a subscription request to a given feature remote address
SubscribeToRemote(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType)
// Trigger a subscription removal request for a given feature remote address
RemoveRemoteSubscription(remoteAddress *model.FeatureAddressType)
// Trigger subscription removal requests for all subscriptions of this feature
RemoveAllRemoteSubscriptions()

// Trigger a binding request to a given feature remote address
BindToRemote(remoteAdress *model.FeatureAddressType) (*model.MsgCounterType, *model.ErrorType)
// Trigger a binding removal request for a given feature remote address
RemoveRemoteBinding(remoteAddress *model.FeatureAddressType)
// Trigger binding removal requests for all subscriptions of this feature
RemoveAllRemoteBindings()

// Handle an incoming SPINE message for this feature
HandleMessage(message *Message) *model.ErrorType

// Get the SPINE data structure for NodeManagementDetailDiscoveryData messages for this feature
Information() *model.NodeManagementDetailedDiscoveryFeatureInformationType
}

// Interface for local NodeManagement feature
type NodeManagementInterface interface {
FeatureLocalInterface
}

// Interface for working with SPINE result messages
type FeatureResultInterface interface {
// Handle a incoming SPINE result message
HandleResult(ResultMessage)
}

// This interface defines all the required functions need to implement a remote feature
type FeatureRemoteInterface interface {
FeatureInterface
DataCopy(function model.FunctionType) any
UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType)
Sender() SenderInterface

// Get the associated DeviceRemoteInterface implementation
Device() DeviceRemoteInterface
// Get the associated EntityRemoteInterface implementation
Entity() EntityRemoteInterface

// Get a copy of the features data for a given function type
DataCopy(function model.FunctionType) any
// Set the features data for a given function type
UpdateData(function model.FunctionType, data any, filterPartial *model.FilterType, filterDelete *model.FilterType)

// Set the supported operations of the feature for a set of functions
SetOperations(functions []model.FunctionPropertyType)

// Define the maximum response duration
SetMaxResponseDelay(delay *model.MaxResponseDelayType)
// Get the maximum allowed response duration
MaxResponseDelayDuration() time.Duration
}
Loading

0 comments on commit e26a43d

Please sign in to comment.