diff --git a/frontend/src/components/Settings/InputsOutputsContent.svelte b/frontend/src/components/Settings/InputsOutputsContent.svelte index 578aed7..7ecb948 100644 --- a/frontend/src/components/Settings/InputsOutputsContent.svelte +++ b/frontend/src/components/Settings/InputsOutputsContent.svelte @@ -4,13 +4,13 @@ import { t, _ } from 'svelte-i18n' import { generateToast, needProjectSave, peripherals, colors } from "../../stores"; import { get } from "svelte/store" - import { UpdatePeripheralSettings, GetPeripheralSettings, RemovePeripheral, AddPeripheral, CreatePeripheral } from "../../../wailsjs/go/main/App"; + import { UpdatePeripheralSettings, GetPeripheralSettings, RemovePeripheral, AddPeripheral } from "../../../wailsjs/go/main/App"; import RoundedButton from "../General/RoundedButton.svelte"; // Create the peripheral to the project function createPeripheral(peripheral){ // Create the peripheral to the project (backend) - CreatePeripheral(peripheral) + AddPeripheral(peripheral) .catch((error) => { console.log("Unable to create the peripheral: " + error) generateToast('danger', 'bx-error', $_("addPeripheralErrorToast")) diff --git a/hardware/FTDIFinder.go b/hardware/FTDIFinder.go index aa0e20e..728e5d1 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, bool) // When a peripheral arrives - onRemoval func(context.Context, Peripheral) // When a peripheral goes away + onArrival func(context.Context, Peripheral) // 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, bool)) { +func (f *FTDIFinder) OnArrival(cb func(context.Context, Peripheral)) { 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) error { - return nil +func (f *FTDIFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) (PeripheralInfo, error) { + return PeripheralInfo{}, 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, false) + f.onArrival(ctx, peripheral) } } } diff --git a/hardware/MIDIFinder.go b/hardware/MIDIFinder.go index 4a1e2f0..705d8af 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, bool) // When a peripheral arrives - onRemoval func(context.Context, Peripheral) // When a peripheral goes away + onArrival func(context.Context, Peripheral) // 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, bool)) { +func (f *MIDIFinder) OnArrival(cb func(context.Context, Peripheral)) { 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) error { - return nil +func (f *MIDIFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) (PeripheralInfo, error) { + return PeripheralInfo{}, 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, false) + f.onArrival(ctx, discovery) } } } diff --git a/hardware/OS2LFinder.go b/hardware/OS2LFinder.go index bca9819..efbc12b 100644 --- a/hardware/OS2LFinder.go +++ b/hardware/OS2LFinder.go @@ -18,41 +18,36 @@ type OS2LFinder struct { detected map[string]*OS2LPeripheral // The list of saved peripherals - eventBus EventBus // The hardware event bus - - onArrival func(context.Context, Peripheral, bool) // When a peripheral arrives + onArrival func(context.Context, Peripheral) // When a peripheral arrives onRemoval func(context.Context, Peripheral) // When a peripheral goes away } // NewOS2LFinder creates a new OS2L finder -func NewOS2LFinder(eventBus EventBus) *OS2LFinder { +func NewOS2LFinder() *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, bool)) { +func (f *OS2LFinder) OnArrival(cb func(context.Context, Peripheral)) { f.onArrival = cb } -// OnRemoval i the callback when a peripheral goes away +// OnRemoval if the callback when a peripheral goes away func (f *OS2LFinder) OnRemoval(cb func(context.Context, Peripheral)) { f.onRemoval = cb } // Create creates a new peripheral, based on the peripheral information (manually created) -func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) error { +func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo 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))) @@ -61,7 +56,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 PeripheralInfo{}, fmt.Errorf("unable to create the OS2L peripheral: %w", err) } // Set the event callback @@ -72,9 +67,9 @@ func (f *OS2LFinder) Create(ctx context.Context, peripheralInfo PeripheralInfo) f.detected[peripheralInfo.SerialNumber] = peripheral if f.onArrival != nil { - f.onArrival(ctx, peripheral, true) // Ask to register the peripheral in the project + f.onArrival(ctx, peripheral) // Ask to register the peripheral in the project } - return err + return peripheralInfo, err } // Remove removes an existing peripheral (manually created) diff --git a/hardware/hardware.go b/hardware/hardware.go index ffeb290..be3d195 100644 --- a/hardware/hardware.go +++ b/hardware/hardware.go @@ -40,144 +40,102 @@ 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 wg sync.WaitGroup - 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 + finders map[string]PeripheralFinder // The map of peripherals finders + DetectedPeripherals 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(map[string]Peripheral, 0), - savedPeripherals: make(map[string]PeripheralInfo, 0), - eventBus: EventBus{ - make(map[string][]PeripheralFinder), - }, + finders: make(map[string]PeripheralFinder), + DetectedPeripherals: make(map[string]Peripheral, 0), + SavedPeripherals: make(map[string]PeripheralInfo, 0), } } -// 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) { +func (h *Manager) RegisterPeripheral(ctx context.Context, peripheralInfo 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 + // Create the peripheral from its finder (if needed) + if finder, found := h.finders[peripheralInfo.ProtocolName]; found { + var err error + peripheralInfo, err = finder.Create(ctx, peripheralInfo) + if err != nil { + return "", err + } } - h.savedPeripherals[peripheralData.SerialNumber] = peripheralData + // Do not save if the peripheral doesn't have a S/N + if peripheralInfo.SerialNumber == "" { + return "", fmt.Errorf("serial number is empty for this peripheral") + } - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected) + h.SavedPeripherals[peripheralInfo.SerialNumber] = peripheralInfo + + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralInfo, PeripheralStatusDisconnected) // If already detected, connect it - if peripheral, ok := h.peripherals[peripheralData.SerialNumber]; ok { + if peripheral, ok := h.DetectedPeripherals[peripheralInfo.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") + log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralInfo.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") + log.Err(err).Str("file", "FTDIFinder").Str("peripheralSN", peripheralInfo.SerialNumber).Msg("unable to activate the FTDI peripheral") return } }() } // Emits the event in the hardware - runtime.EventsEmit(ctx, string(PeripheralLoad), peripheralData) + runtime.EventsEmit(ctx, string(PeripheralLoad), peripheralInfo) - return peripheralData.SerialNumber, nil + return peripheralInfo.SerialNumber, nil } // UnregisterPeripheral unregisters an existing peripheral -func (h *Manager) UnregisterPeripheral(ctx context.Context, peripheralData PeripheralInfo) error { +func (h *Manager) UnregisterPeripheral(ctx context.Context, peripheralInfo PeripheralInfo) error { h.mu.Lock() defer h.mu.Unlock() - if peripheral, detected := h.peripherals[peripheralData.SerialNumber]; detected { + if peripheral, detected := h.DetectedPeripherals[peripheralInfo.SerialNumber]; detected { // Deactivating peripheral err := peripheral.Deactivate(ctx) if err != nil { - log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to deactivate the peripheral") + log.Err(err).Str("sn", peripheralInfo.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") + log.Err(err).Str("sn", peripheralInfo.SerialNumber).Msg("unable to disconnect the peripheral") return nil } - // Emit the unload event - h.eventBus.EmitUnsavedPeripheral(ctx, peripheral) + // Remove the peripheral from its finder (if needed) + if finder, found := h.finders[peripheralInfo.ProtocolName]; found { + err = finder.Remove(ctx, peripheral) + if err != nil { + return err + } + } } - delete(h.savedPeripherals, peripheralData.SerialNumber) - runtime.EventsEmit(ctx, string(PeripheralUnload), peripheralData) + delete(h.SavedPeripherals, peripheralInfo.SerialNumber) + runtime.EventsEmit(ctx, string(PeripheralUnload), peripheralInfo) return nil } @@ -185,10 +143,10 @@ func (h *Manager) UnregisterPeripheral(ctx context.Context, peripheralData Perip // GetPeripheralSettings gets the peripheral settings func (h *Manager) GetPeripheralSettings(peripheralSN string) (map[string]any, error) { // Return the specified peripheral - peripheral, found := h.peripherals[peripheralSN] + peripheral, found := h.DetectedPeripherals[peripheralSN] if !found { // Peripheral not detected, return the last settings saved - if savedPeripheral, isFound := h.savedPeripherals[peripheralSN]; isFound { + if savedPeripheral, isFound := h.SavedPeripherals[peripheralSN]; isFound { return savedPeripheral.Settings, nil } return nil, fmt.Errorf("unable to found the peripheral") @@ -198,7 +156,7 @@ func (h *Manager) GetPeripheralSettings(peripheralSN string) (map[string]any, er // SetPeripheralSettings sets the peripheral settings func (h *Manager) SetPeripheralSettings(ctx context.Context, peripheralSN string, settings map[string]any) error { - peripheral, found := h.peripherals[peripheralSN] + peripheral, found := h.DetectedPeripherals[peripheralSN] if !found { return fmt.Errorf("unable to found the FTDI peripheral") } @@ -210,7 +168,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.eventBus)) + h.RegisterFinder(NewOS2LFinder()) h.RegisterFinder(NewMIDIFinder(3 * time.Second)) for finderName, finder := range h.finders { @@ -237,17 +195,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, needSave bool) { - // Save the peripheral in the project if needed - if needSave { - h.RegisterPeripheral(ctx, peripheral.GetInfo()) - } - +func (h *Manager) OnPeripheralArrival(ctx context.Context, peripheral Peripheral) { // Add the peripheral to the detected hardware - h.peripherals[peripheral.GetInfo().SerialNumber] = peripheral + h.DetectedPeripherals[peripheral.GetInfo().SerialNumber] = peripheral // If the peripheral is saved in the project, connect it - if _, saved := h.savedPeripherals[peripheral.GetInfo().SerialNumber]; saved { + if _, saved := h.SavedPeripherals[peripheral.GetInfo().SerialNumber]; saved { h.wg.Add(1) go func(p Peripheral) { defer h.wg.Done() @@ -286,7 +239,7 @@ func (h *Manager) OnPeripheralRemoval(ctx context.Context, peripheral Peripheral }(peripheral) // Remove the peripheral from the hardware - delete(h.peripherals, peripheral.GetInfo().SerialNumber) + delete(h.DetectedPeripherals, peripheral.GetInfo().SerialNumber) // TODO: Update the Peripheral reference in the corresponding devices runtime.EventsEmit(ctx, string(PeripheralRemoval), peripheral.GetInfo()) @@ -323,7 +276,7 @@ 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 { + for registeredPeripheralSN, registeredPeripheral := range h.DetectedPeripherals { err := registeredPeripheral.WaitStop() if err != nil { errs = append(errs, fmt.Errorf("%s: %w", registeredPeripheralSN, err)) diff --git a/hardware/interfaces.go b/hardware/interfaces.go index e66cf65..a4bf9db 100644 --- a/hardware/interfaces.go +++ b/hardware/interfaces.go @@ -45,12 +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) 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 + Initialize() error // Initializes the protocol + Create(ctx context.Context, peripheralInfo PeripheralInfo) (PeripheralInfo, error) // Manually create a peripheral + Remove(ctx context.Context, peripheral Peripheral) error // Manually remove 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 } diff --git a/peripherals.go b/peripherals.go index b14179d..05ff09c 100644 --- a/peripherals.go +++ b/peripherals.go @@ -7,12 +7,6 @@ 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 diff --git a/project.go b/project.go index be227d2..7dcbb16 100644 --- a/project.go +++ b/project.go @@ -140,8 +140,8 @@ func (a *App) OpenProject(projectInfo ProjectInfo) error { // CloseCurrentProject closes the current project func (a *App) CloseCurrentProject() error { - // Unregistrer all peripherals of the project - projectPeripherals := a.projectInfo.PeripheralsInfo + // Unregister all peripherals of the project + projectPeripherals := a.hardwareManager.SavedPeripherals for key, value := range projectPeripherals { err := a.hardwareManager.UnregisterPeripheral(a.ctx, value) if err != nil { @@ -196,6 +196,9 @@ func (a *App) SaveProject() (string, error) { a.projectSave = fmt.Sprintf("%04d%02d%02d%02d%02d%02d%s", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second(), projectExtension) log.Debug().Str("file", "project").Str("newProjectSave", a.projectSave).Msg("projectSave is null, getting a new one") } + // Get hardware info + a.projectInfo.PeripheralsInfo = a.hardwareManager.SavedPeripherals + // Marshal the project data, err := yaml.Marshal(a.projectInfo) if err != nil { log.Err(err).Str("file", "project").Any("projectInfo", a.projectInfo).Msg("unable to format the project information")