hardware code cleaning

This commit is contained in:
2025-12-01 19:12:01 +01:00
parent 01ffd36a60
commit b864f3ff7b
4 changed files with 281 additions and 258 deletions

View File

@@ -7,214 +7,30 @@ import (
"sync"
"github.com/rs/zerolog/log"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// EndpointEvent is trigger by the providers when the scan is complete
type EndpointEvent string
// EndpointStatus is the endpoint status (DISCONNECTED => CONNECTING => DEACTIVATED => ACTIVATED)
type EndpointStatus string
const (
// EndpointArrival is triggerd when a endpoint has been connected to the system
EndpointArrival EndpointEvent = "PERIPHERAL_ARRIVAL"
// EndpointRemoval is triggered when a endpoint has been disconnected from the system
EndpointRemoval EndpointEvent = "PERIPHERAL_REMOVAL"
// EndpointLoad is triggered when a endpoint is added to the project
EndpointLoad EndpointEvent = "PERIPHERAL_LOAD"
// EndpointUnload is triggered when a endpoint is removed from the project
EndpointUnload EndpointEvent = "PERIPHERAL_UNLOAD"
// EndpointStatusUpdated is triggered when a endpoint status has been updated (disconnected - connecting - deactivated - activated)
EndpointStatusUpdated EndpointEvent = "PERIPHERAL_STATUS"
// EndpointEventEmitted is triggered when a endpoint event is emitted
EndpointEventEmitted EndpointEvent = "PERIPHERAL_EVENT_EMITTED"
// EndpointStatusDisconnected : endpoint is now disconnected
EndpointStatusDisconnected EndpointStatus = "PERIPHERAL_DISCONNECTED"
// EndpointStatusConnecting : endpoint is now connecting
EndpointStatusConnecting EndpointStatus = "PERIPHERAL_CONNECTING"
// EndpointStatusDeactivated : endpoint is now deactivated
EndpointStatusDeactivated EndpointStatus = "PERIPHERAL_DEACTIVATED"
// EndpointStatusActivated : endpoint is now activated
EndpointStatusActivated EndpointStatus = "PERIPHERAL_ACTIVATED"
)
// MappingInfo is the configuration for each device
type MappingInfo struct {
DeviceInfo struct {
Name string `yaml:"name"`
Manufacturer string `yaml:"manufacturer"`
Type string `yaml:"type"`
} `yaml:"device"`
Features map[string]any `yaml:"features"`
}
// Device represents the methods used to manage a device (logic element include in a Endpoint)
type Device interface {
Configure() error // Load the mapping for the device
}
// Endpoint represents the methods used to manage a endpoint (input or output hardware)
type Endpoint interface {
Connect(context.Context) error // Connect the endpoint
// SetEventCallback(func(any)) // Callback is called when an event is emitted from the endpoint
Disconnect(context.Context) error // Disconnect the endpoint
Activate(context.Context) error // Activate the endpoint
Deactivate(context.Context) error // Deactivate the endpoint
// AddDevice(Device) error // Add a device to the endpoint
// RemoveDevice(Device) error // Remove a device to the endpoint
GetSettings() map[string]any // Get the endpoint settings
SetSettings(context.Context, map[string]any) error // Set a endpoint setting
SetDeviceProperty(context.Context, uint32, byte) error // Update a device property
WaitStop() error // Properly close the endpoint
GetInfo() EndpointInfo // Get the endpoint information
}
// EndpointInfo represents a endpoint information
type EndpointInfo struct {
Name string `yaml:"name"` // Name of the endpoint
SerialNumber string `yaml:"sn"` // S/N of the endpoint
ProtocolName string `yaml:"protocol"` // Protocol name of the endpoint
Settings map[string]any `yaml:"settings"` // Endpoint settings
}
// EndpointProvider represents how compatible endpoint drivers are implemented
type EndpointProvider interface {
Initialize() error // Initializes the protocol
Create(ctx context.Context, endpointInfo EndpointInfo) (EndpointInfo, error) // Manually create a endpoint
Remove(ctx context.Context, endpoint Endpoint) error // Manually remove a endpoint
OnArrival(cb func(context.Context, Endpoint)) // Callback function when a endpoint arrives
OnRemoval(cb func(context.Context, Endpoint)) // Callback function when a endpoint goes away
Start(context.Context) error // Start the detection
WaitStop() error // Waiting for provider to close
GetName() string // Get the name of the provider
}
// Manager is the class who manages the hardware
type Manager struct {
mu sync.Mutex
wg sync.WaitGroup
providers map[string]EndpointProvider // The map of endpoints providers
DetectedEndpoints map[string]Endpoint // The current list of endpoints
SavedEndpoints map[string]EndpointInfo // The list of stored endpoints
providers map[string]Provider // The map of endpoints providers
devices map[string]Device // The map of devices
DetectedEndpoints map[string]Endpoint // The current list of endpoints
SavedEndpoints map[string]EndpointInfo // The list of stored endpoints
}
// NewManager creates a new hardware manager
func NewManager() *Manager {
log.Trace().Str("package", "hardware").Msg("Hardware instance created")
return &Manager{
providers: make(map[string]EndpointProvider),
providers: make(map[string]Provider, 0),
devices: make(map[string]Device, 0),
DetectedEndpoints: make(map[string]Endpoint, 0),
SavedEndpoints: make(map[string]EndpointInfo, 0),
}
}
// RegisterEndpoint registers a new endpoint
func (h *Manager) RegisterEndpoint(ctx context.Context, endpointInfo EndpointInfo) (string, error) {
h.mu.Lock()
defer h.mu.Unlock()
// Create the endpoint from its provider (if needed)
if provider, found := h.providers[endpointInfo.ProtocolName]; found {
var err error
endpointInfo, err = provider.Create(ctx, endpointInfo)
if err != nil {
return "", err
}
}
// Do not save if the endpoint doesn't have a S/N
if endpointInfo.SerialNumber == "" {
return "", fmt.Errorf("serial number is empty for this endpoint")
}
h.SavedEndpoints[endpointInfo.SerialNumber] = endpointInfo
runtime.EventsEmit(ctx, string(EndpointStatusUpdated), endpointInfo, EndpointStatusDisconnected)
// If already detected, connect it
if endpoint, ok := h.DetectedEndpoints[endpointInfo.SerialNumber]; ok {
h.wg.Add(1)
go func() {
defer h.wg.Done()
err := endpoint.Connect(ctx)
if err != nil {
log.Err(err).Str("file", "FTDIProvider").Str("endpointSN", endpointInfo.SerialNumber).Msg("unable to connect the endpoint")
return
}
// Endpoint connected, activate it
err = endpoint.Activate(ctx)
if err != nil {
log.Err(err).Str("file", "FTDIProvider").Str("endpointSN", endpointInfo.SerialNumber).Msg("unable to activate the FTDI endpoint")
return
}
}()
}
// Emits the event in the hardware
runtime.EventsEmit(ctx, string(EndpointLoad), endpointInfo)
return endpointInfo.SerialNumber, nil
}
// UnregisterEndpoint unregisters an existing endpoint
func (h *Manager) UnregisterEndpoint(ctx context.Context, endpointInfo EndpointInfo) error {
h.mu.Lock()
defer h.mu.Unlock()
if endpoint, detected := h.DetectedEndpoints[endpointInfo.SerialNumber]; detected {
// Deactivating endpoint
err := endpoint.Deactivate(ctx)
if err != nil {
log.Err(err).Str("sn", endpointInfo.SerialNumber).Msg("unable to deactivate the endpoint")
return nil
}
// Disconnecting endpoint
err = endpoint.Disconnect(ctx)
if err != nil {
log.Err(err).Str("sn", endpointInfo.SerialNumber).Msg("unable to disconnect the endpoint")
return nil
}
// Remove the endpoint from its provider (if needed)
if provider, found := h.providers[endpointInfo.ProtocolName]; found {
err = provider.Remove(ctx, endpoint)
if err != nil {
return err
}
}
}
delete(h.SavedEndpoints, endpointInfo.SerialNumber)
runtime.EventsEmit(ctx, string(EndpointUnload), endpointInfo)
return nil
}
// GetEndpointSettings gets the endpoint settings
func (h *Manager) GetEndpointSettings(endpointSN string) (map[string]any, error) {
// Return the specified endpoint
endpoint, found := h.DetectedEndpoints[endpointSN]
if !found {
// Endpoint not detected, return the last settings saved
if savedEndpoint, isFound := h.SavedEndpoints[endpointSN]; isFound {
return savedEndpoint.Settings, nil
}
return nil, fmt.Errorf("unable to found the endpoint")
}
return endpoint.GetSettings(), nil
}
// SetEndpointSettings sets the endpoint settings
func (h *Manager) SetEndpointSettings(ctx context.Context, endpointSN string, settings map[string]any) error {
endpoint, found := h.DetectedEndpoints[endpointSN]
if !found {
return fmt.Errorf("unable to found the FTDI endpoint")
}
return endpoint.SetSettings(ctx, settings)
}
// Start starts to find new endpoint events
func (h *Manager) Start(ctx context.Context) error {
for providerName, provider := range h.providers {
@@ -240,74 +56,6 @@ func (h *Manager) Start(ctx context.Context) error {
return nil
}
// OnEndpointArrival is called when a endpoint arrives in the system
func (h *Manager) OnEndpointArrival(ctx context.Context, endpoint Endpoint) {
// Add the endpoint to the detected hardware
h.DetectedEndpoints[endpoint.GetInfo().SerialNumber] = endpoint
// If the endpoint is saved in the project, connect it
if _, saved := h.SavedEndpoints[endpoint.GetInfo().SerialNumber]; saved {
h.wg.Add(1)
go func(p Endpoint) {
defer h.wg.Done()
err := p.Connect(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to connect the FTDI endpoint")
return
}
err = p.Activate(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to activate the FTDI endpoint")
return
}
}(endpoint)
}
// TODO: Update the Endpoint reference in the corresponding devices
runtime.EventsEmit(ctx, string(EndpointArrival), endpoint.GetInfo())
}
// OnEndpointRemoval is called when a endpoint exits the system
func (h *Manager) OnEndpointRemoval(ctx context.Context, endpoint Endpoint) {
// Properly deactivating and disconnecting the endpoint
h.wg.Add(1)
go func(p Endpoint) {
defer h.wg.Done()
err := p.Deactivate(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to deactivate endpoint after disconnection")
}
err = p.Disconnect(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to disconnect the endpoint after disconnection")
}
}(endpoint)
// Remove the endpoint from the hardware
delete(h.DetectedEndpoints, endpoint.GetInfo().SerialNumber)
// TODO: Update the Endpoint reference in the corresponding devices
runtime.EventsEmit(ctx, string(EndpointRemoval), endpoint.GetInfo())
}
// GetProvider returns a register provider
func (h *Manager) GetProvider(providerName string) (EndpointProvider, error) {
provider, exists := h.providers[providerName]
if !exists {
log.Error().Str("file", "hardware").Str("providerName", providerName).Msg("unable to get the provider")
return nil, fmt.Errorf("unable to locate the '%s' provider", providerName)
}
log.Debug().Str("file", "hardware").Str("providerName", providerName).Msg("got provider")
return provider, nil
}
// RegisterProvider registers a new endpoints provider
func (h *Manager) RegisterProvider(provider EndpointProvider) {
h.providers[provider.GetName()] = provider
log.Info().Str("file", "hardware").Str("providerName", provider.GetName()).Msg("provider registered")
}
// WaitStop stops the hardware manager
func (h *Manager) WaitStop() error {
log.Trace().Str("file", "hardware").Msg("closing the hardware manager")