From c7d9f35080a5083be562465c61095d38676dd8f9 Mon Sep 17 00:00:00 2001 From: Valentin Boulanger Date: Thu, 13 Nov 2025 16:00:50 +0100 Subject: [PATCH] apply settings for OS2L --- hardware/FTDIFinder.go | 2 +- hardware/OS2LFinder.go | 19 +++--- hardware/OS2LPeripheral.go | 124 +++++++++++++++++++++++++++---------- hardware/interfaces.go | 38 ++++++------ peripherals.go | 11 ++-- project.go | 2 +- 6 files changed, 124 insertions(+), 72 deletions(-) diff --git a/hardware/FTDIFinder.go b/hardware/FTDIFinder.go index c82951c..7e3e0cb 100644 --- a/hardware/FTDIFinder.go +++ b/hardware/FTDIFinder.go @@ -180,7 +180,7 @@ func (f *FTDIFinder) GetPeripheralSettings(peripheralID string) (map[string]inte } // SetPeripheralSettings sets the peripheral settings -func (f *FTDIFinder) SetPeripheralSettings(peripheralID string, settings map[string]interface{}) error { +func (f *FTDIFinder) SetPeripheralSettings(ctx context.Context, peripheralID string, settings map[string]any) error { // Return the specified peripheral // peripheral, found := f.registeredPeripherals[peripheralID] // if !found { diff --git a/hardware/OS2LFinder.go b/hardware/OS2LFinder.go index 16b65c5..26d534d 100644 --- a/hardware/OS2LFinder.go +++ b/hardware/OS2LFinder.go @@ -60,7 +60,7 @@ func (f *OS2LFinder) RegisterPeripheral(ctx context.Context, peripheralData Peri // Create a new OS2L peripheral peripheral, err := NewOS2LPeripheral(peripheralData) if err != nil { - return "", fmt.Errorf("unable to create the OS2L peripheral: %v", err) + return "", fmt.Errorf("unable to create the OS2L peripheral: %w", err) } // Set the event callback peripheral.SetEventCallback(func(event any) { @@ -79,21 +79,17 @@ func (f *OS2LFinder) RegisterPeripheral(ctx context.Context, peripheralData Peri go func() { defer f.wg.Done() // Connect the OS2L peripheral - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusConnecting) err = peripheral.Connect(ctx) if err != nil { - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected) log.Err(err).Str("file", "OS2LFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to connect the peripheral") return } - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDeactivated) // Peripheral connected, activate it err = peripheral.Activate(ctx) if err != nil { log.Err(err).Str("file", "OS2LFinder").Str("peripheralSN", peripheralData.SerialNumber).Msg("unable to activate the OS2L peripheral") return } - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusActivated) }() // Emits the event in the hardware @@ -114,14 +110,12 @@ func (f *OS2LFinder) UnregisterPeripheral(ctx context.Context, peripheralData Pe log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to deactivate the peripheral") return nil } - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDeactivated) // Disconnecting peripheral - err = peripheral.Disconnect() + err = peripheral.Disconnect(ctx) if err != nil { log.Err(err).Str("sn", peripheralData.SerialNumber).Msg("unable to disconnect the peripheral") return nil } - runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), peripheralData, PeripheralStatusDisconnected) } // The OS2L peripheral has gone @@ -138,7 +132,7 @@ func (f *OS2LFinder) GetName() string { } // GetPeripheralSettings gets the peripheral settings -func (f *OS2LFinder) GetPeripheralSettings(peripheralID string) (map[string]interface{}, error) { +func (f *OS2LFinder) GetPeripheralSettings(peripheralID string) (map[string]any, error) { // Return the specified peripheral peripheral, found := f.saved[peripheralID] if !found { @@ -150,15 +144,16 @@ func (f *OS2LFinder) GetPeripheralSettings(peripheralID string) (map[string]inte } // SetPeripheralSettings sets the peripheral settings -func (f *OS2LFinder) SetPeripheralSettings(peripheralID string, settings map[string]interface{}) error { +func (f *OS2LFinder) SetPeripheralSettings(ctx context.Context, peripheralID string, settings map[string]any) error { // Return the specified peripheral peripheral, found := f.saved[peripheralID] if !found { log.Error().Str("file", "OS2LFinder").Str("peripheralID", peripheralID).Msg("unable to get this peripheral from the FTDI finder") return fmt.Errorf("unable to found the peripheral") } - log.Debug().Str("file", "OS2LFinder").Str("peripheralID", peripheralID).Msg("peripheral found by the FTDI finder") - return peripheral.SetSettings(settings) + + // Set the peripheral settings + return peripheral.SetSettings(ctx, settings) } // Start starts the finder diff --git a/hardware/OS2LPeripheral.go b/hardware/OS2LPeripheral.go index c67f971..ee17343 100644 --- a/hardware/OS2LPeripheral.go +++ b/hardware/OS2LPeripheral.go @@ -5,10 +5,12 @@ import ( "encoding/json" "fmt" "net" + "strings" "sync" "time" "github.com/rs/zerolog/log" + "github.com/wailsapp/wails/v2/pkg/runtime" ) // OS2LMessage represents an OS2L message @@ -35,14 +37,13 @@ type OS2LPeripheral struct { // NewOS2LPeripheral creates a new OS2L peripheral func NewOS2LPeripheral(peripheralData PeripheralInfo) (*OS2LPeripheral, error) { - log.Trace().Str("file", "OS2LPeripheral").Str("name", peripheralData.Name).Str("s/n", peripheralData.SerialNumber).Msg("OS2L peripheral created") - return &OS2LPeripheral{ + peripheral := &OS2LPeripheral{ info: peripheralData, - serverIP: "127.0.0.1", - serverPort: 9995, listener: nil, eventCallback: nil, - }, nil + } + log.Trace().Str("file", "OS2LPeripheral").Str("name", peripheralData.Name).Str("s/n", peripheralData.SerialNumber).Msg("OS2L peripheral created") + return peripheral, peripheral.loadSettings(peripheralData.Settings) } // SetEventCallback sets the callback for returning events @@ -50,15 +51,21 @@ func (p *OS2LPeripheral) SetEventCallback(eventCallback func(any)) { p.eventCallback = eventCallback } -// Connect connects the MIDI peripheral +// Connect connects the OS2L peripheral func (p *OS2LPeripheral) Connect(ctx context.Context) error { + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.GetInfo(), PeripheralStatusConnecting) + var err error addr := net.TCPAddr{Port: p.serverPort, IP: net.ParseIP(p.serverIP)} + + log.Debug().Any("addr", addr).Msg("parametres de connexion à la connexion") p.listener, err = net.ListenTCP("tcp", &addr) if err != nil { + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.GetInfo(), PeripheralStatusDisconnected) return fmt.Errorf("unable to set the OS2L TCP listener") } + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.GetInfo(), PeripheralStatusDeactivated) log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral connected") return nil } @@ -78,14 +85,49 @@ func (p *OS2LPeripheral) handleMessage(raw []byte) error { return nil } +// loadSettings check and load the settings in the peripheral +func (p *OS2LPeripheral) loadSettings(settings map[string]any) error { + // Check if the IP exists + serverIP, found := settings["os2lIp"] + if !found { + // Set default IP address + serverIP = "127.0.0.1" + } + // Check if it is a string + ipSetting, ok := serverIP.(string) + if ok { + p.serverIP = ipSetting + } else { + return fmt.Errorf("The specified IP is not a string") + } + // Check if the port exists + serverPort, found := settings["os2lPort"] + if !found { + // Set default port + serverPort = 9995 + } + switch v := serverPort.(type) { + case int: + p.serverPort = v + case float64: + p.serverPort = int(v) // JSON numbers are float64 + default: + return fmt.Errorf("The specified port is not a number, got %T", serverPort) + } + + return nil +} + // Disconnect disconnects the MIDI peripheral -func (p *OS2LPeripheral) Disconnect() error { +func (p *OS2LPeripheral) Disconnect(ctx context.Context) error { // Close the TCP listener if not null if p.listener != nil { p.listener.Close() } + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.GetInfo(), PeripheralStatusDisconnected) + log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral disconnected") return nil } @@ -115,6 +157,9 @@ func (p *OS2LPeripheral) Activate(ctx context.Context) error { if ne, ok := err.(net.Error); ok && ne.Timeout() { continue } + if strings.Contains(err.Error(), "use of closed network connection") || strings.Contains(err.Error(), "invalid argument") { + return + } log.Err(err).Str("file", "OS2LPeripheral").Msg("unable to accept the connection") continue } @@ -153,6 +198,8 @@ func (p *OS2LPeripheral) Activate(ctx context.Context) error { } } }() + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.GetInfo(), PeripheralStatusActivated) + log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral activated") return nil @@ -169,37 +216,46 @@ func (p *OS2LPeripheral) Deactivate(ctx context.Context) error { p.listenerCancel() } + runtime.EventsEmit(ctx, string(PeripheralStatusUpdated), p.GetInfo(), PeripheralStatusDeactivated) + log.Info().Str("file", "OS2LPeripheral").Msg("OS2L peripheral deactivated") return nil } // SetSettings sets a specific setting for this peripheral -func (p *OS2LPeripheral) SetSettings(settings map[string]interface{}) error { - // Check if the IP exists - serverIP, found := settings["os2lIp"] - if !found { - return fmt.Errorf("Unable to find the OS2L server IP") - } - // Check if it is a string - ipSetting, ok := serverIP.(string) - if ok { - p.serverIP = ipSetting - - } else { - return fmt.Errorf("The specified IP is not a string") - } - // Check if the port exists - serverPort, found := settings["os2lPort"] - if !found { - return fmt.Errorf("Unable to find the OS2L server port") - } - // Check if it is a float and convert to int - portFloat, ok := serverPort.(float64) - if ok { - p.serverPort = int(portFloat) - } else { - return fmt.Errorf("The specified port is not an int") +func (p *OS2LPeripheral) SetSettings(ctx context.Context, settings map[string]any) error { + err := p.loadSettings(settings) + if err != nil { + return fmt.Errorf("unable to load settings: %w", err) } + // Reconnect the peripheral + p.wg.Add(1) + go func() { + defer p.wg.Done() + err := p.Deactivate(ctx) + if err != nil { + log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to deactivate") + return + } + err = p.Disconnect(ctx) + if err != nil { + log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to disconnect") + return + } + // Add a sleep to view changes + time.Sleep(500 * time.Millisecond) + err = p.Connect(ctx) + if err != nil { + log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to connect") + return + } + err = p.Activate(ctx) + if err != nil { + log.Err(err).Str("sn", p.GetInfo().SerialNumber).Msg("unable to activate") + return + } + }() + log.Info().Str("sn", p.GetInfo().SerialNumber).Msg("peripheral settings set") return nil } @@ -209,8 +265,8 @@ func (p *OS2LPeripheral) SetDeviceProperty(context.Context, uint32, uint32, byte } // GetSettings gets the peripheral settings -func (p *OS2LPeripheral) GetSettings() map[string]interface{} { - return map[string]interface{}{ +func (p *OS2LPeripheral) GetSettings() map[string]any { + return map[string]any{ "os2lIp": p.serverIP, "os2lPort": p.serverPort, } diff --git a/hardware/interfaces.go b/hardware/interfaces.go index 0b31397..6c9ea82 100644 --- a/hardware/interfaces.go +++ b/hardware/interfaces.go @@ -6,36 +6,36 @@ import "context" type Peripheral interface { Connect(context.Context) error // Connect the peripheral SetEventCallback(func(any)) // Callback is called when an event is emitted from the peripheral - Disconnect() error // Disconnect the peripheral + Disconnect(context.Context) error // Disconnect the peripheral Activate(context.Context) error // Activate the peripheral Deactivate(context.Context) error // Deactivate the peripheral - SetSettings(map[string]interface{}) error // Set a peripheral setting + SetSettings(context.Context, map[string]any) error // Set a peripheral setting SetDeviceProperty(context.Context, uint32, byte) error // Update a device property WaitStop() error // Properly close the peripheral - GetInfo() PeripheralInfo // Get the peripheral information - GetSettings() map[string]interface{} // Get the peripheral settings + GetInfo() PeripheralInfo // Get the peripheral information + GetSettings() map[string]any // Get the peripheral settings } // PeripheralInfo represents a peripheral information type PeripheralInfo struct { - Name string `yaml:"name"` // Name of the peripheral - SerialNumber string `yaml:"sn"` // S/N of the peripheral - ProtocolName string `yaml:"protocol"` // Protocol name of the peripheral - Settings map[string]interface{} `yaml:"settings"` // Peripheral settings + Name string `yaml:"name"` // Name of the peripheral + SerialNumber string `yaml:"sn"` // S/N of the peripheral + ProtocolName string `yaml:"protocol"` // Protocol name of the peripheral + Settings map[string]any `yaml:"settings"` // Peripheral settings } // PeripheralFinder represents how compatible peripheral drivers are implemented type PeripheralFinder interface { - Initialize() error // Initializes the protocol - OnArrival(cb func(p PeripheralInfo)) // Callback function when a peripheral arrives - OnRemoval(cb func(p PeripheralInfo)) // Callback function when a peripheral goes away - Start(context.Context) error // Start the detection - WaitStop() error // Waiting for finder to close - ForceScan() // Explicitly scans for peripherals - RegisterPeripheral(context.Context, PeripheralInfo) (string, error) // Registers a new peripheral data - UnregisterPeripheral(context.Context, PeripheralInfo) error // Unregisters an existing peripheral - GetPeripheralSettings(string) (map[string]interface{}, error) // Gets the peripheral settings - SetPeripheralSettings(string, map[string]interface{}) error // Sets the peripheral settings - GetName() string // Get the name of the finder + Initialize() error // Initializes the protocol + OnArrival(cb func(p PeripheralInfo)) // Callback function when a peripheral arrives + OnRemoval(cb func(p PeripheralInfo)) // Callback function when a peripheral goes away + Start(context.Context) error // Start the detection + WaitStop() error // Waiting for finder to close + ForceScan() // Explicitly scans for peripherals + RegisterPeripheral(context.Context, PeripheralInfo) (string, error) // Registers a new peripheral data + UnregisterPeripheral(context.Context, PeripheralInfo) error // Unregisters an existing peripheral + GetPeripheralSettings(string) (map[string]any, error) // Gets the peripheral settings + SetPeripheralSettings(context.Context, string, map[string]any) error // Sets the peripheral settings + GetName() string // Get the name of the finder } diff --git a/peripherals.go b/peripherals.go index 91ae570..f9fb409 100644 --- a/peripherals.go +++ b/peripherals.go @@ -18,9 +18,10 @@ func (a *App) AddPeripheral(peripheralData hardware.PeripheralInfo) (string, err // Register this new peripheral serialNumber, err := f.RegisterPeripheral(a.ctx, peripheralData) if err != nil { - log.Trace().Str("file", "peripheral").Str("protocolName", peripheralData.ProtocolName).Str("periphID", serialNumber).Msg("device registered to the finder") - return "", fmt.Errorf("unable to register the peripheral '%s'", serialNumber) + return "", fmt.Errorf("unable to register the peripheral '%s': %w", serialNumber, err) } + log.Trace().Str("file", "peripheral").Str("protocolName", peripheralData.ProtocolName).Str("periphID", serialNumber).Msg("device registered to the finder") + // Rewrite the serialnumber for virtual devices peripheralData.SerialNumber = serialNumber @@ -35,7 +36,7 @@ func (a *App) AddPeripheral(peripheralData hardware.PeripheralInfo) (string, err } // GetPeripheralSettings gets the peripheral settings -func (a *App) GetPeripheralSettings(protocolName, peripheralID string) (map[string]interface{}, error) { +func (a *App) GetPeripheralSettings(protocolName, peripheralID string) (map[string]any, error) { // Get the peripheral from its finder f, err := a.hardwareManager.GetFinder(protocolName) if err != nil { @@ -46,7 +47,7 @@ func (a *App) GetPeripheralSettings(protocolName, peripheralID string) (map[stri } // UpdatePeripheralSettings updates a specific setting of a peripheral -func (a *App) UpdatePeripheralSettings(protocolName, peripheralID string, settings map[string]interface{}) error { +func (a *App) UpdatePeripheralSettings(protocolName, peripheralID string, settings map[string]any) error { // Sets the settings with the finder f, err := a.hardwareManager.GetFinder(protocolName) if err != nil { @@ -61,7 +62,7 @@ func (a *App) UpdatePeripheralSettings(protocolName, peripheralID string, settin pInfo.Settings = settings a.projectInfo.PeripheralsInfo[peripheralID] = pInfo // Apply changes in the peripheral - return f.SetPeripheralSettings(peripheralID, pInfo.Settings) + return f.SetPeripheralSettings(a.ctx, peripheralID, pInfo.Settings) } // RemovePeripheral removes a peripheral from the project diff --git a/project.go b/project.go index af7a157..9b9954d 100644 --- a/project.go +++ b/project.go @@ -136,7 +136,7 @@ func (a *App) OpenProject(projectInfo ProjectInfo) error { } _, err = hostFinder.RegisterPeripheral(a.ctx, value) if err != nil { - return fmt.Errorf("unable to register the peripheral S/N '%s'", key) + return fmt.Errorf("unable to register the peripheral S/N '%s': %w", key, err) } } return nil