From d1fda9f0759732f5b3af1a4f2145cd294b736788 Mon Sep 17 00:00:00 2001 From: Valentin Boulanger Date: Sat, 29 Nov 2025 20:06:04 +0100 Subject: [PATCH] resolved created peripheral --- .../Settings/InputsOutputsContent.svelte | 4 +- frontend/src/lang/en.json | 1 + hardware/FTDIFinder.go | 12 ++-- hardware/MIDIFinder.go | 12 ++-- hardware/OS2LFinder.go | 21 +++--- hardware/hardware.go | 72 ++++++++++++++++++- hardware/interfaces.go | 15 ++-- peripherals.go | 17 ++--- 8 files changed, 112 insertions(+), 42 deletions(-) diff --git a/frontend/src/components/Settings/InputsOutputsContent.svelte b/frontend/src/components/Settings/InputsOutputsContent.svelte index 591be60..578aed7 100644 --- a/frontend/src/components/Settings/InputsOutputsContent.svelte +++ b/frontend/src/components/Settings/InputsOutputsContent.svelte @@ -7,9 +7,9 @@ import { UpdatePeripheralSettings, GetPeripheralSettings, RemovePeripheral, AddPeripheral, CreatePeripheral } from "../../../wailsjs/go/main/App"; import RoundedButton from "../General/RoundedButton.svelte"; - // Create a new peripheral + // Create the peripheral to the project function createPeripheral(peripheral){ - // Add the peripheral to the project (backend) + // Create the peripheral to the project (backend) CreatePeripheral(peripheral) .catch((error) => { console.log("Unable to create the peripheral: " + error) diff --git a/frontend/src/lang/en.json b/frontend/src/lang/en.json index f153022..9ea4f79 100644 --- a/frontend/src/lang/en.json +++ b/frontend/src/lang/en.json @@ -48,6 +48,7 @@ "projectSavedToast": "The project has been saved", "projectSaveErrorToast": "Unable to save the project:", "addPeripheralErrorToast": "Unable to add this peripheral to project", + "createPeripheralErrorToast": "Unable to create this peripheral", "removePeripheralErrorToast": "Unable to remove this peripheral from project", "os2lPeripheralCreatedToast": "Your OS2L peripheral has been created", "os2lPeripheralCreateErrorToast": "Unable to create the OS2L peripheral", diff --git a/hardware/FTDIFinder.go b/hardware/FTDIFinder.go index d0ce37a..aa0e20e 100644 --- a/hardware/FTDIFinder.go +++ b/hardware/FTDIFinder.go @@ -27,8 +27,8 @@ type FTDIFinder struct { scanEvery time.Duration // Scans peripherals periodically - onArrival func(context.Context, Peripheral) // When a peripheral arrives - onRemoval func(context.Context, Peripheral) // When a peripheral goes away + onArrival func(context.Context, Peripheral, bool) // When a peripheral arrives + onRemoval func(context.Context, Peripheral) // When a peripheral goes away } // NewFTDIFinder creates a new FTDI finder @@ -41,7 +41,7 @@ func NewFTDIFinder(scanEvery time.Duration) *FTDIFinder { } // OnArrival is the callback function when a new peripheral arrives -func (f *FTDIFinder) OnArrival(cb func(context.Context, Peripheral)) { +func (f *FTDIFinder) OnArrival(cb func(context.Context, Peripheral, bool)) { f.onArrival = cb } @@ -62,8 +62,8 @@ func (f *FTDIFinder) Initialize() error { } // Create creates a new peripheral, based on the peripheral information (manually created) -func (f *FTDIFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) (string, error) { - return "", nil +func (f *FTDIFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) error { + return nil } // Remove removes an existing peripheral (manually created) @@ -147,7 +147,7 @@ func (f *FTDIFinder) scanPeripherals(ctx context.Context) error { peripheral := NewFTDIPeripheral(peripheralData) if f.onArrival != nil { - f.onArrival(ctx, peripheral) + f.onArrival(ctx, peripheral, false) } } } diff --git a/hardware/MIDIFinder.go b/hardware/MIDIFinder.go index ec3929b..4a1e2f0 100644 --- a/hardware/MIDIFinder.go +++ b/hardware/MIDIFinder.go @@ -22,8 +22,8 @@ type MIDIFinder struct { scanEvery time.Duration // Scans peripherals periodically - onArrival func(context.Context, Peripheral) // When a peripheral arrives - onRemoval func(context.Context, Peripheral) // When a peripheral goes away + onArrival func(context.Context, Peripheral, bool) // When a peripheral arrives + onRemoval func(context.Context, Peripheral) // When a peripheral goes away } // NewMIDIFinder creates a new MIDI finder @@ -36,7 +36,7 @@ func NewMIDIFinder(scanEvery time.Duration) *MIDIFinder { } // OnArrival is the callback function when a new peripheral arrives -func (f *MIDIFinder) OnArrival(cb func(context.Context, Peripheral)) { +func (f *MIDIFinder) OnArrival(cb func(context.Context, Peripheral, bool)) { f.onArrival = cb } @@ -52,8 +52,8 @@ func (f *MIDIFinder) Initialize() error { } // Create creates a new peripheral, based on the peripheral information (manually created) -func (f *MIDIFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) (string, error) { - return "", nil +func (f *MIDIFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) error { + return nil } // Remove removes an existing peripheral (manually created) @@ -201,7 +201,7 @@ func (f *MIDIFinder) scanPeripherals(ctx context.Context) error { f.detected[sn] = peripheral if f.onArrival != nil { - f.onArrival(ctx, discovery) + f.onArrival(ctx, discovery, false) } } } diff --git a/hardware/OS2LFinder.go b/hardware/OS2LFinder.go index a1ae9f9..bca9819 100644 --- a/hardware/OS2LFinder.go +++ b/hardware/OS2LFinder.go @@ -18,26 +18,31 @@ type OS2LFinder struct { detected map[string]*OS2LPeripheral // The list of saved peripherals - onArrival func(context.Context, Peripheral) // When a peripheral arrives - onRemoval func(context.Context, Peripheral) // When a peripheral goes away + eventBus EventBus // The hardware event bus + + onArrival func(context.Context, Peripheral, bool) // When a peripheral arrives + onRemoval func(context.Context, Peripheral) // When a peripheral goes away } // NewOS2LFinder creates a new OS2L finder -func NewOS2LFinder() *OS2LFinder { +func NewOS2LFinder(eventBus EventBus) *OS2LFinder { log.Trace().Str("file", "OS2LFinder").Msg("OS2L finder created") return &OS2LFinder{ detected: make(map[string]*OS2LPeripheral), + eventBus: eventBus, } } // Initialize initializes the finder func (f *OS2LFinder) Initialize() error { + // Subscribe to all OS2L peripherals that are added to the project + f.eventBus.SubscribeType(f.GetName(), f) log.Trace().Str("file", "OS2LFinder").Msg("OS2L finder initialized") return nil } // OnArrival is the callback function when a new peripheral arrives -func (f *OS2LFinder) OnArrival(cb func(context.Context, Peripheral)) { +func (f *OS2LFinder) OnArrival(cb func(context.Context, Peripheral, bool)) { f.onArrival = cb } @@ -47,7 +52,7 @@ func (f *OS2LFinder) OnRemoval(cb func(context.Context, Peripheral)) { } // Create creates a new peripheral, based on the peripheral information (manually created) -func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) (string, error) { +func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) error { // If the SerialNumber is empty, generate another one if peripheralInfo.SerialNumber == "" { peripheralInfo.SerialNumber = strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32))) @@ -56,7 +61,7 @@ func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) // Create a new OS2L peripheral peripheral, err := NewOS2LPeripheral(peripheralInfo) if err != nil { - return "", fmt.Errorf("unable to create the OS2L peripheral: %w", err) + return fmt.Errorf("unable to create the OS2L peripheral: %w", err) } // Set the event callback @@ -67,9 +72,9 @@ func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) f.detected[peripheralInfo.SerialNumber] = peripheral if f.onArrival != nil { - f.onArrival(ctx, peripheral) + f.onArrival(ctx, peripheral, true) // Ask to register the peripheral in the project } - return peripheralInfo.SerialNumber, err + return err } // Remove removes an existing peripheral (manually created) diff --git a/hardware/hardware.go b/hardware/hardware.go index 641e682..ffeb290 100644 --- a/hardware/hardware.go +++ b/hardware/hardware.go @@ -40,6 +40,51 @@ const ( PeripheralStatusActivated PeripheralStatus = "PERIPHERAL_ACTIVATED" ) +// EventBus handles events between hardware and finders +type EventBus struct { + register map[string][]PeripheralFinder // Register is a map[peripheralType (string)][]Finder +} + +// SubscribeType is called from finders that want to subscribe for a peripheral loaded in project +func (b *EventBus) SubscribeType(protocolName string, finder PeripheralFinder) { + // Check if we need to initialize the slice (key not found) + if _, found := b.register[protocolName]; !found { + b.register[protocolName] = make([]PeripheralFinder, 0) + } + // Add the finder to the list of subscription + b.register[protocolName] = append(b.register[protocolName], finder) +} + +// EmitSavedPeripheral emits a peripheral loaded event for all the finders suscribed to the peripheral type +func (b *EventBus) EmitSavedPeripheral(ctx context.Context, peripheralInfo PeripheralInfo) (string, error) { + finders, found := b.register[peripheralInfo.ProtocolName] + if !found { + return "", nil + } + for _, finder := range finders { + err := finder.Create(ctx, peripheralInfo) + if err != nil { + log.Err(err).Str("finder", finder.GetName()).Str("S/N", peripheralInfo.SerialNumber).Msg("unable to create the peripheral") + } + } + return "", nil +} + +// EmitUnsavedPeripheral emits a peripheral unloaded event for all the finders suscribed to the peripheral type +func (b *EventBus) EmitUnsavedPeripheral(ctx context.Context, peripheral Peripheral) (string, error) { + finders, found := b.register[peripheral.GetInfo().ProtocolName] + if !found { + return "", nil + } + for _, finder := range finders { + err := finder.Remove(ctx, peripheral) + if err != nil { + log.Err(err).Str("finder", finder.GetName()).Str("S/N", peripheral.GetInfo().SerialNumber).Msg("unable to remove the peripheral") + } + } + return "", nil +} + // Manager is the class who manages the hardware type Manager struct { mu sync.Mutex @@ -48,6 +93,8 @@ type Manager struct { 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 + + eventBus EventBus // The hardware event bus } // NewManager creates a new hardware manager @@ -57,14 +104,28 @@ func NewManager() *Manager { finders: make(map[string]PeripheralFinder), peripherals: make(map[string]Peripheral, 0), savedPeripherals: make(map[string]PeripheralInfo, 0), + eventBus: EventBus{ + make(map[string][]PeripheralFinder), + }, } } +// CreatePeripheral asks the providers to create a new peripheral +func (h *Manager) CreatePeripheral(ctx context.Context, peripheralInfo PeripheralInfo) { + // Emit an event to the hardware event bus + h.eventBus.EmitSavedPeripheral(ctx, peripheralInfo) +} + // RegisterPeripheral registers a new peripheral func (h *Manager) RegisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) (string, error) { h.mu.Lock() defer h.mu.Unlock() + // Do not save if the peripheral doesn't have a S/N + if peripheralData.SerialNumber == "" { + return "", nil + } + h.savedPeripherals[peripheralData.SerialNumber] = peripheralData runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected) @@ -112,6 +173,8 @@ func (h *Manager) UnregisterPeripheral(ctx context.Context, peripheralData Perip log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to disconnect the peripheral") return nil } + // Emit the unload event + h.eventBus.EmitUnsavedPeripheral(ctx, peripheral) } delete(h.savedPeripherals, peripheralData.SerialNumber) runtime.EventsEmit(ctx, string(PeripheralUnload), peripheralData) @@ -147,7 +210,7 @@ func (h *Manager) Start(ctx context.Context) error { // Register all the finders to use as hardware scanners h.RegisterFinder(NewFTDIFinder(3 * time.Second)) - h.RegisterFinder(NewOS2LFinder()) + h.RegisterFinder(NewOS2LFinder(h.eventBus)) h.RegisterFinder(NewMIDIFinder(3 * time.Second)) for finderName, finder := range h.finders { @@ -174,7 +237,12 @@ func (h *Manager) Start(ctx context.Context) error { } // OnPeripheralArrival is called when a peripheral arrives in the system -func (h *Manager) OnPeripheralArrival(ctx context.Context, peripheral Peripheral) { +func (h *Manager) OnPeripheralArrival(ctx context.Context, peripheral Peripheral, needSave bool) { + // Save the peripheral in the project if needed + if needSave { + h.RegisterPeripheral(ctx, peripheral.GetInfo()) + } + // Add the peripheral to the detected hardware h.peripherals[peripheral.GetInfo().SerialNumber] = peripheral diff --git a/hardware/interfaces.go b/hardware/interfaces.go index e630adf..e66cf65 100644 --- a/hardware/interfaces.go +++ b/hardware/interfaces.go @@ -45,11 +45,12 @@ type PeripheralInfo struct { // PeripheralFinder represents how compatible peripheral drivers are implemented type PeripheralFinder interface { - Initialize() error // Initializes the protocol - Create(ctx context.Context, peripheralInfo PeripheralInfo) (string, error) // Manually create a peripheral - OnArrival(cb func(context.Context, Peripheral)) // Callback function when a peripheral arrives - OnRemoval(cb func(context.Context, Peripheral)) // Callback function when a peripheral goes away - Start(context.Context) error // Start the detection - WaitStop() error // Waiting for finder to close - GetName() string // Get the name of the finder + Initialize() error // Initializes the protocol + Create(ctx context.Context, peripheralInfo PeripheralInfo) error // Manually create a peripheral + Remove(ctx context.Context, peripheral Peripheral) error // Manually remove a peripheral + OnArrival(cb func(context.Context, Peripheral, bool)) // Callback function when a peripheral arrives + OnRemoval(cb func(context.Context, Peripheral)) // Callback function when a peripheral goes away + Start(context.Context) error // Start the detection + WaitStop() error // Waiting for finder to close + GetName() string // Get the name of the finder } diff --git a/peripherals.go b/peripherals.go index b9ad7f6..b14179d 100644 --- a/peripherals.go +++ b/peripherals.go @@ -7,6 +7,12 @@ import ( "github.com/rs/zerolog/log" ) +// CreatePeripheral creates a new peripheral from the hardware manager +func (a *App) CreatePeripheral(peripheralData hardware.PeripheralInfo) (string, error) { + a.hardwareManager.CreatePeripheral(a.ctx, peripheralData) + return "", nil +} + // AddPeripheral adds a peripheral to the project func (a *App) AddPeripheral(peripheralData hardware.PeripheralInfo) (string, error) { // Register this new peripheral @@ -29,17 +35,6 @@ func (a *App) AddPeripheral(peripheralData hardware.PeripheralInfo) (string, err return peripheralData.SerialNumber, nil } -// CreatePeripheral creates a new peripheral -func (a *App) CreatePeripheral(peripheralData hardware.PeripheralInfo) (string, error) { - // Get the appropriate finder to create the device - finder, err := a.hardwareManager.GetFinder(peripheralData.ProtocolName) - if err != nil { - return "", fmt.Errorf("unable to find the appropriate finder (%s): %w", peripheralData.ProtocolName, err) - } - // Manually create the peripheral from its finder - return finder.Create(a.ctx, peripheralData) -} - // GetPeripheralSettings gets the peripheral settings func (a *App) GetPeripheralSettings(protocolName, peripheralSN string) (map[string]any, error) { return a.hardwareManager.GetPeripheralSettings(peripheralSN)