Adapting interfaces

This commit is contained in:
2025-11-29 17:42:42 +01:00
parent ea46430b71
commit 848d2758d7
11 changed files with 264 additions and 564 deletions

View File

@@ -38,68 +38,188 @@ const (
// Manager is the class who manages the hardware
type Manager struct {
mu sync.Mutex
wg sync.WaitGroup
finders map[string]PeripheralFinder // The map of peripherals finders
peripherals []*Peripheral // The current list of peripherals
peripheralsScanTrigger chan struct{} // Trigger the peripherals scans
finders map[string]PeripheralFinder // The map of peripherals finders
peripherals map[string]Peripheral // The current list of peripherals
savedPeripherals map[string]PeripheralInfo // The list of stored peripherals
}
// NewManager creates a new hardware manager
func NewManager() *Manager {
log.Trace().Str("package", "hardware").Msg("Hardware instance created")
return &Manager{
finders: make(map[string]PeripheralFinder),
peripherals: make([]*Peripheral, 0),
peripheralsScanTrigger: make(chan struct{}),
finders: make(map[string]PeripheralFinder),
peripherals: make(map[string]Peripheral, 0),
savedPeripherals: make(map[string]PeripheralInfo, 0),
}
}
// RegisterPeripheral registers a new peripheral
func (h *Manager) RegisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) (string, error) {
h.mu.Lock()
defer h.mu.Unlock()
h.savedPeripherals[peripheralData.SerialNumber] = peripheralData
runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected)
// If already detected, connect it
if peripheral, ok := h.peripherals[peripheralData.SerialNumber]; ok {
h.wg.Add(1)
go func() {
defer h.wg.Done()
err := peripheral.Connect(ctx)
if err != nil {
log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to connect the peripheral")
return
}
// Peripheral connected, activate it
err = peripheral.Activate(ctx)
if err != nil {
log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to activate the FTDI peripheral")
return
}
}()
}
// Emits the event in the hardware
runtime.EventsEmit(ctx, "LOAD_PERIPHERAL", peripheralData)
return peripheralData.SerialNumber, nil
}
// UnregisterPeripheral unregisters an existing peripheral
func (h *Manager) UnregisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) error {
h.mu.Lock()
defer h.mu.Unlock()
if peripheral, detected := h.peripherals[peripheralData.SerialNumber]; detected {
// Deactivating peripheral
err := peripheral.Deactivate(ctx)
if err != nil {
log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to deactivate the peripheral")
return nil
}
// Disconnecting peripheral
err = peripheral.Disconnect(ctx)
if err != nil {
log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to disconnect the peripheral")
return nil
}
}
delete(h.savedPeripherals, peripheralData.SerialNumber)
runtime.EventsEmit(ctx, "UNLOAD_PERIPHERAL", peripheralData)
return nil
}
// GetPeripheralSettings gets the peripheral settings
func (h *Manager) GetPeripheralSettings(peripheralSN string) (map[string]any, error) {
// Return the specified peripheral
peripheral, found := h.peripherals[peripheralSN]
if !found {
// Peripheral not detected, return the last settings saved
if savedPeripheral, isFound := h.savedPeripherals[peripheralSN]; isFound {
return savedPeripheral.Settings, nil
}
return nil, fmt.Errorf("unable to found the peripheral")
}
return peripheral.GetSettings(), nil
}
// SetPeripheralSettings sets the peripheral settings
func (h *Manager) SetPeripheralSettings(ctx context.Context, peripheralSN string, settings map[string]any) error {
peripheral, found := h.peripherals[peripheralSN]
if !found {
return fmt.Errorf("unable to found the FTDI peripheral")
}
return peripheral.SetSettings(ctx, settings)
}
// Start starts to find new peripheral events
func (h *Manager) Start(ctx context.Context) error {
// Initialize all the finders and their callback functions
// Register all the finders to use as hardware scanners
h.RegisterFinder(NewFTDIFinder(3 * time.Second))
h.RegisterFinder(NewOS2LFinder())
h.RegisterFinder(NewMIDIFinder(3 * time.Second))
for finderName, finder := range h.finders {
// Initialize the finder
err := finder.Initialize()
if err != nil {
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to initialize finder")
return err
}
finder.OnArrival(func(p PeripheralInfo) {
runtime.EventsEmit(ctx, string(PeripheralArrival), p)
})
finder.OnRemoval(func(p PeripheralInfo) {
runtime.EventsEmit(ctx, string(PeripheralRemoval), p)
})
// Set callback functions
finder.OnArrival(h.OnPeripheralArrival)
finder.OnRemoval(h.OnPeripheralRemoval)
// Start the finder
err = finder.Start(ctx)
if err != nil {
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to start finder")
return err
}
}
// Periodically scan all the finders
h.wg.Add(1)
go func() {
defer h.wg.Done()
for {
select {
case <-ctx.Done():
return
case <-h.peripheralsScanTrigger:
for finderName, finder := range h.finders {
log.Trace().Str("file", "hardware").Str("finderName", finderName).Msg("force a finder to scan peripherals")
finder.ForceScan()
}
}
}
}()
return nil
}
// OnPeripheralArrival is called when a peripheral arrives in the system
func (h *Manager) OnPeripheralArrival(ctx context.Context, peripheral Peripheral) {
// Add the peripheral to the detected hardware
h.peripherals[peripheral.GetInfo().SerialNumber] = peripheral
// If the peripheral is saved in the project, connect it
if _, saved := h.savedPeripherals[peripheral.GetInfo().SerialNumber]; saved {
h.wg.Add(1)
go func(p Peripheral) {
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 peripheral")
return
}
err = p.Activate(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to activate the FTDI peripheral")
return
}
}(peripheral)
}
// TODO: Update the Peripheral reference in the corresponding devices
runtime.EventsEmit(ctx, string(PeripheralArrival), peripheral.GetInfo())
}
// OnPeripheralRemoval is called when a peripheral exits the system
func (h *Manager) OnPeripheralRemoval(ctx context.Context, peripheral Peripheral) {
// Properly deactivating and disconnecting the peripheral
h.wg.Add(1)
go func(p Peripheral) {
defer h.wg.Done()
err := p.Deactivate(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to deactivate peripheral after disconnection")
}
err = p.Disconnect(ctx)
if err != nil {
log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to disconnect the peripheral after disconnection")
}
}(peripheral)
// Remove the peripheral from the hardware
delete(h.peripherals, peripheral.GetInfo().SerialNumber)
// TODO: Update the Peripheral reference in the corresponding devices
runtime.EventsEmit(ctx, string(PeripheralRemoval), peripheral.GetInfo())
}
// GetFinder returns a register finder
func (h *Manager) GetFinder(finderName string) (PeripheralFinder, error) {
finder, exists := h.finders[finderName]
@@ -117,23 +237,10 @@ func (h *Manager) RegisterFinder(finder PeripheralFinder) {
log.Info().Str("file", "hardware").Str("finderName", finder.GetName()).Msg("finder registered")
}
// Scan scans all the peripherals for the registered finders
func (h *Manager) Scan() error {
select {
case h.peripheralsScanTrigger <- struct{}{}:
return nil
default:
return fmt.Errorf("scan trigger not available (manager stopped?)")
}
}
// WaitStop stops the hardware manager
func (h *Manager) WaitStop() error {
log.Trace().Str("file", "hardware").Msg("closing the hardware manager")
// Closing trigger channel
close(h.peripheralsScanTrigger)
// Stop each finder
var errs []error
for name, f := range h.finders {
@@ -142,6 +249,15 @@ func (h *Manager) WaitStop() error {
}
}
// Wait for all the peripherals to close
log.Trace().Str("file", "MIDIFinder").Msg("closing all MIDI peripherals")
for registeredPeripheralSN, registeredPeripheral := range h.peripherals {
err := registeredPeripheral.WaitStop()
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", registeredPeripheralSN, err))
}
}
// Wait for goroutines to finish
h.wg.Wait()