Closes #34 Implement MIDI peripherals
Implement device concept
Cleaning project

Reviewed-on: #35
This commit was merged in pull request #35.
This commit is contained in:
2025-12-02 18:02:17 +00:00
parent 932c288a9c
commit 4fbb75ad19
42 changed files with 1638 additions and 1564 deletions

View File

@@ -7,136 +7,76 @@ import (
"sync"
"github.com/rs/zerolog/log"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// PeripheralEvent is trigger by the finders when the scan is complete
type PeripheralEvent string
// PeripheralStatus is the peripheral status (DISCONNECTED => CONNECTING => DEACTIVATED => ACTIVATED)
type PeripheralStatus string
const (
// PeripheralArrival is triggerd when a peripheral has been connected to the system
PeripheralArrival PeripheralEvent = "PERIPHERAL_ARRIVAL"
// PeripheralRemoval is triggered when a peripheral has been disconnected from the system
PeripheralRemoval PeripheralEvent = "PERIPHERAL_REMOVAL"
// PeripheralStatusUpdated is triggered when a peripheral status has been updated (disconnected - connecting - deactivated - activated)
PeripheralStatusUpdated PeripheralEvent = "PERIPHERAL_STATUS"
// PeripheralEventEmitted is triggered when a peripheral event is emitted
PeripheralEventEmitted PeripheralEvent = "PERIPHERAL_EVENT_EMITTED"
// PeripheralStatusDisconnected : peripheral is now disconnected
PeripheralStatusDisconnected PeripheralStatus = "PERIPHERAL_DISCONNECTED"
// PeripheralStatusConnecting : peripheral is now connecting
PeripheralStatusConnecting PeripheralStatus = "PERIPHERAL_CONNECTING"
// PeripheralStatusDeactivated : peripheral is now deactivated
PeripheralStatusDeactivated PeripheralStatus = "PERIPHERAL_DEACTIVATED"
// PeripheralStatusActivated : peripheral is now activated
PeripheralStatusActivated PeripheralStatus = "PERIPHERAL_ACTIVATED"
)
// HardwareManager is the class who manages the hardware
type HardwareManager struct {
// 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
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
}
// NewHardwareManager creates a new HardwareManager
func NewHardwareManager() *HardwareManager {
// NewManager creates a new hardware manager
func NewManager() *Manager {
log.Trace().Str("package", "hardware").Msg("Hardware instance created")
return &HardwareManager{
finders: make(map[string]PeripheralFinder),
peripherals: make([]Peripheral, 0),
peripheralsScanTrigger: make(chan struct{}),
return &Manager{
providers: make(map[string]Provider, 0),
devices: make(map[string]*Device, 0),
DetectedEndpoints: make(map[string]Endpoint, 0),
SavedEndpoints: make(map[string]EndpointInfo, 0),
}
}
// Start starts to find new peripheral events
func (h *HardwareManager) Start(ctx context.Context) error {
// Initialize all the finders and their callback functions
for finderName, finder := range h.finders {
err := finder.Initialize()
// Start starts to find new endpoint events
func (h *Manager) Start(ctx context.Context) error {
for providerName, provider := range h.providers {
// Initialize the provider
err := provider.Initialize()
if err != nil {
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to initialize finder")
log.Err(err).Str("file", "hardware").Str("providerName", providerName).Msg("unable to initialize provider")
return err
}
finder.OnArrival(func(p PeripheralInfo) {
runtime.EventsEmit(ctx, string(PeripheralArrival), p)
})
finder.OnRemoval(func(p PeripheralInfo) {
runtime.EventsEmit(ctx, string(PeripheralRemoval), p)
})
err = finder.Start(ctx)
// Set callback functions
provider.OnArrival(h.OnEndpointArrival)
provider.OnRemoval(h.OnEndpointRemoval)
// Start the provider
err = provider.Start(ctx)
if err != nil {
log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to start finder")
log.Err(err).Str("file", "hardware").Str("providerName", providerName).Msg("unable to start provider")
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
}
// GetFinder returns a register finder
func (h *HardwareManager) GetFinder(finderName string) (PeripheralFinder, error) {
finder, exists := h.finders[finderName]
if !exists {
log.Error().Str("file", "hardware").Str("finderName", finderName).Msg("unable to get the finder")
return nil, fmt.Errorf("unable to locate the '%s' finder", finderName)
}
log.Debug().Str("file", "hardware").Str("finderName", finderName).Msg("got finder")
return finder, nil
}
// RegisterFinder registers a new peripherals finder
func (h *HardwareManager) RegisterFinder(finder PeripheralFinder) {
h.finders[finder.GetName()] = finder
log.Info().Str("file", "hardware").Str("finderName", finder.GetName()).Msg("finder registered")
}
// Scan scans all the peripherals for the registered finders
func (h *HardwareManager) 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 *HardwareManager) WaitStop() error {
func (h *Manager) WaitStop() error {
log.Trace().Str("file", "hardware").Msg("closing the hardware manager")
// Closing trigger channel
close(h.peripheralsScanTrigger)
// Stop each finder
// Stop each provider
var errs []error
for name, f := range h.finders {
for name, f := range h.providers {
if err := f.WaitStop(); err != nil {
errs = append(errs, fmt.Errorf("%s: %w", name, err))
}
}
// Wait for all the endpoints to close
log.Trace().Str("file", "MIDIProvider").Msg("closing all MIDI endpoints")
for registeredEndpointSN, registeredEndpoint := range h.DetectedEndpoints {
err := registeredEndpoint.WaitStop()
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", registeredEndpointSN, err))
}
}
// Wait for goroutines to finish
h.wg.Wait()