package hardware import ( "context" "fmt" "sync" "time" "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 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" // debounceDuration = 500 * time.Millisecond ) var ( debounceTimer *time.Timer ) // HardwareManager is the class who manages the hardware type HardwareManager struct { finders map[string]PeripheralFinder // The map of peripherals finders peripherals []Peripheral // The current list of peripherals peripheralsScanTrigger chan struct{} // Trigger the peripherals scans goWait sync.WaitGroup // Wait for goroutines to terminate } // NewHardwareManager creates a new HardwareManager func NewHardwareManager() *HardwareManager { log.Trace().Str("package", "hardware").Msg("Hardware instance created") return &HardwareManager{ finders: make(map[string]PeripheralFinder), peripherals: make([]Peripheral, 0), peripheralsScanTrigger: make(chan struct{}), } } // Start starts to find new peripheral events func (h *HardwareManager) Start(ctx context.Context) error { for finderName, finder := range h.finders { err := finder.Initialize() if err != nil { log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to initialize finder") return err } err = finder.Start(ctx) if err != nil { log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to start finder") return err } } h.goWait.Add(1) go func() { defer h.goWait.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") } // // GetPeripheral gets the peripheral object from the parent finder // func (h *HardwareManager) GetPeripheral(finderName string, peripheralID string) (Peripheral, bool) { // // Get the finder // parentFinder, found := h.finders[finderName] // // If no finder found, return false // if !found { // log.Error().Str("file", "hardware").Str("finderName", finderName).Msg("unable to get the finder") // return nil, false // } // log.Trace().Str("file", "hardware").Str("finderName", parentFinder.GetName()).Msg("finder got") // // Contact the finder to get the peripheral // return parentFinder.GetPeripheral(peripheralID) // } // Scan scans all the peripherals for the registered finders func (h *HardwareManager) Scan() error { h.peripheralsScanTrigger <- struct{}{} return nil } // Stop stops the hardware manager func (h *HardwareManager) Stop() error { log.Trace().Str("file", "hardware").Msg("closing the hardware manager") // Stop each finder for finderName, finder := range h.finders { err := finder.Stop() if err != nil { log.Err(err).Str("file", "hardware").Str("finderName", finderName).Msg("unable to stop the finder") } } // Wait for goroutines to finish h.goWait.Wait() log.Info().Str("file", "hardware").Msg("hardware manager stopped") return nil } // emitPeripheralsChanges compares the old and new peripherals to determine which ones have been added or removed. func emitPeripheralsChanges(ctx context.Context, oldPeripherals map[string]PeripheralInfo, newPeripherals map[string]PeripheralInfo) { log.Trace().Any("oldList", oldPeripherals).Any("newList", newPeripherals).Msg("emitting peripherals changes to the front") // Identify removed peripherals: present in the old list but not in the new list for oldPeriphName := range oldPeripherals { if _, exists := newPeripherals[oldPeriphName]; !exists { runtime.EventsEmit(ctx, string(PeripheralRemoval), oldPeripherals[oldPeriphName]) log.Trace().Str("file", "hardware").Str("event", string(PeripheralRemoval)).Msg("emit peripheral removal event") } } // Identify added peripherals: present in the new list but not in the old list for newPeriphName := range newPeripherals { if _, exists := oldPeripherals[newPeriphName]; !exists { runtime.EventsEmit(ctx, string(PeripheralArrival), newPeripherals[newPeriphName]) log.Trace().Str("file", "hardware").Str("event", string(PeripheralArrival)).Msg("emit peripheral arrival event") } } }