diff --git a/frontend/src/runtime-events.js b/frontend/src/runtime-events.js index a632f59..613de19 100644 --- a/frontend/src/runtime-events.js +++ b/frontend/src/runtime-events.js @@ -146,6 +146,18 @@ function onEndpointEvent(sn, event) { }, 200); } +// When a new device is added +function onDeviceArrival(deviceInfo) { + console.log("New device arrival") + console.log(deviceInfo) +} + +// When a new device is removed +function onDeviceRemoval(sn){ + console.log("New device removal") + console.log(sn) +} + let initialized = false export function initRuntimeEvents(){ @@ -172,6 +184,12 @@ export function initRuntimeEvents(){ // Handle a endpoint event EventsOn('PERIPHERAL_EVENT_EMITTED', onEndpointEvent) + + // Handle a device arrival + EventsOn('DEVICE_ARRIVAL', onDeviceArrival) + + // Handle a device removal + EventsOn('DEVICE_REMOVAL', onDeviceRemoval) } export function destroyRuntimeEvents(){ @@ -198,4 +216,10 @@ export function destroyRuntimeEvents(){ // Handle a endpoint event EventsOff('PERIPHERAL_EVENT_EMITTED') + + // Handle a device arrival + EventsOff('DEVICE_ARRIVAL') + + // Handle a device removal + EventsOff('DEVICE_REMOVAL') } \ No newline at end of file diff --git a/hardware/devicesHandler.go b/hardware/devicesHandler.go index 7601af3..e8abe7b 100644 --- a/hardware/devicesHandler.go +++ b/hardware/devicesHandler.go @@ -1,5 +1,24 @@ package hardware +import ( + "context" + "fmt" + "math/rand" + "strings" + + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +// DeviceEvent is triggered when a device event changes +type DeviceEvent string + +const ( + // DeviceArrival is triggered when a device arrival + DeviceArrival DeviceEvent = "DEVICE_ARRIVAL" + // DeviceRemoval is triggered when a device removal + DeviceRemoval EndpointEvent = "DEVICE_REMOVAL" +) + // MappingInfo is the configuration for each device type MappingInfo struct { DeviceInfo struct { @@ -10,12 +29,44 @@ type MappingInfo struct { Features map[string]any `yaml:"features"` } -// Device represents the logical to be controlled -type Device struct { +// DeviceInfo represents the device data +type DeviceInfo struct { SerialNumber string // The device s/n Name string // The device name Manufacturer string // The device manufacturer Version string // The device version - - Endpoint Endpoint // The device endpoint which control this device +} + +// Device represents the logical to be controlled +type Device struct { + DeviceInfo DeviceInfo // The device base information + Endpoint Endpoint // The device endpoint which control this device +} + +// AddDevice adds a new device to the manager +func (h *Manager) AddDevice(ctx context.Context, device DeviceInfo, endpoint Endpoint) error { + // If the SerialNumber is empty, generate another one + if device.SerialNumber == "" { + device.SerialNumber = strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32))) + } + + // Add or replace the device to the manager + h.devices[device.SerialNumber] = &Device{device, endpoint} + + // Send the event to the front + runtime.EventsEmit(ctx, string(DeviceArrival), device) + return nil +} + +// RemoveDevice removes a device from the manager +func (h *Manager) RemoveDevice(ctx context.Context, serialNumber string) error { + // Delete the device from the manager + if serialNumber == "" { + return fmt.Errorf("the device s/n is empty") + } + delete(h.devices, serialNumber) + + // Send the event to the front + runtime.EventsEmit(ctx, string(DeviceRemoval), serialNumber) + return nil } diff --git a/hardware/endpointsHandler.go b/hardware/endpointsHandler.go index d307bce..07a5700 100644 --- a/hardware/endpointsHandler.go +++ b/hardware/endpointsHandler.go @@ -41,15 +41,15 @@ const ( type Endpoint interface { Connect(context.Context) error // Connect the endpoint // SetEventCallback(func(any)) // Callback is called when an event is emitted from the endpoint - Disconnect(context.Context) error // Disconnect the endpoint - Activate(context.Context) error // Activate the endpoint - Deactivate(context.Context) error // Deactivate the endpoint - // AddDevice(Device) error // Add a device to the endpoint - // RemoveDevice(Device) error // Remove a device to the endpoint - GetSettings() map[string]any // Get the endpoint settings - SetSettings(context.Context, map[string]any) error // Set a endpoint setting - SetDeviceProperty(context.Context, uint32, byte) error // Update a device property - WaitStop() error // Properly close the endpoint + Disconnect(context.Context) error // Disconnect the endpoint + Activate(context.Context) error // Activate the endpoint + Deactivate(context.Context) error // Deactivate the endpoint + SetDeviceArrivalCallback(func(context.Context, DeviceInfo, Endpoint) error) // Set the callback function when a new device is detected by the endpoint + SetDeviceRemovalCallback(func(context.Context, string) error) // Set the callback function when a device is not detected anymore by the endpoint + GetSettings() map[string]any // Get the endpoint settings + SetSettings(context.Context, map[string]any) error // Set a endpoint setting + SetDeviceProperty(context.Context, uint32, byte) error // Update a device property + WaitStop() error // Properly close the endpoint GetInfo() EndpointInfo // Get the endpoint information } @@ -170,6 +170,10 @@ func (h *Manager) OnEndpointArrival(ctx context.Context, endpoint Endpoint) { // Add the endpoint to the detected hardware h.DetectedEndpoints[endpoint.GetInfo().SerialNumber] = endpoint + // Specify the callback functions to manages devices + endpoint.SetDeviceArrivalCallback(h.AddDevice) + endpoint.SetDeviceRemovalCallback(h.RemoveDevice) + // If the endpoint is saved in the project, connect it if _, saved := h.SavedEndpoints[endpoint.GetInfo().SerialNumber]; saved { h.wg.Add(1) diff --git a/hardware/genericftdi/FTDIEndpoint.go b/hardware/genericftdi/FTDIEndpoint.go index 2724036..1f57a26 100644 --- a/hardware/genericftdi/FTDIEndpoint.go +++ b/hardware/genericftdi/FTDIEndpoint.go @@ -36,6 +36,14 @@ func NewEndpoint(info hardware.EndpointInfo) *Endpoint { } } +// SetDeviceArrivalCallback is called when we need to add a new device to the hardware +func (p *Endpoint) SetDeviceArrivalCallback(adc func(context.Context, hardware.DeviceInfo, hardware.Endpoint) error) { +} + +// SetDeviceRemovalCallback is called when we need to remove a device from the hardware +func (p *Endpoint) SetDeviceRemovalCallback(rdc func(context.Context, string) error) { +} + // Connect connects the FTDI endpoint func (p *Endpoint) Connect(ctx context.Context) error { // Check if the device has already been created diff --git a/hardware/genericmidi/MIDIEndpoint.go b/hardware/genericmidi/MIDIEndpoint.go index 7d2e615..05449ad 100644 --- a/hardware/genericmidi/MIDIEndpoint.go +++ b/hardware/genericmidi/MIDIEndpoint.go @@ -42,6 +42,16 @@ func NewEndpoint(endpointData hardware.EndpointInfo, inputs []midi.In, outputs [ } } +// SetDeviceArrivalCallback is called when we need to add a new device to the hardware +func (p *Endpoint) SetDeviceArrivalCallback(adc func(context.Context, hardware.DeviceInfo, hardware.Endpoint) error) { + +} + +// SetDeviceRemovalCallback is called when we need to remove a device from the hardware +func (p *Endpoint) SetDeviceRemovalCallback(rdc func(context.Context, string) error) { + +} + // Connect connects the MIDI endpoint func (p *Endpoint) Connect(ctx context.Context) error { runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusConnecting) diff --git a/hardware/hardware.go b/hardware/hardware.go index 104b76f..fa2eeac 100644 --- a/hardware/hardware.go +++ b/hardware/hardware.go @@ -15,7 +15,7 @@ type Manager struct { wg sync.WaitGroup providers map[string]Provider // The map of endpoints providers - devices map[string]Device // The map of devices + 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 } @@ -25,7 +25,7 @@ func NewManager() *Manager { log.Trace().Str("package", "hardware").Msg("Hardware instance created") return &Manager{ providers: make(map[string]Provider, 0), - devices: make(map[string]Device, 0), + devices: make(map[string]*Device, 0), DetectedEndpoints: make(map[string]Endpoint, 0), SavedEndpoints: make(map[string]EndpointInfo, 0), } diff --git a/hardware/os2l/OS2LEndpoint.go b/hardware/os2l/OS2LEndpoint.go index 8018116..490fc7e 100644 --- a/hardware/os2l/OS2LEndpoint.go +++ b/hardware/os2l/OS2LEndpoint.go @@ -33,7 +33,9 @@ type Endpoint struct { listener net.Listener // Net listener (TCP) listenerCancel context.CancelFunc // Call this function to cancel the endpoint activation - eventCallback func(any) // This callback is called for returning events + eventCallback func(any) // This callback is called for returning events + addDeviceCallback func(context.Context, hardware.DeviceInfo, hardware.Endpoint) error // Add a device to the hardware + removeDeviceCallback func(context.Context, string) error // Remove a device from the hardware } // NewOS2LEndpoint creates a new OS2L endpoint @@ -52,6 +54,16 @@ func (p *Endpoint) SetEventCallback(eventCallback func(any)) { p.eventCallback = eventCallback } +// SetDeviceArrivalCallback is called when we need to add a new device to the hardware +func (p *Endpoint) SetDeviceArrivalCallback(adc func(context.Context, hardware.DeviceInfo, hardware.Endpoint) error) { + p.addDeviceCallback = adc +} + +// SetDeviceRemovalCallback is called when we need to remove a device from the hardware +func (p *Endpoint) SetDeviceRemovalCallback(rdc func(context.Context, string) error) { + p.removeDeviceCallback = rdc +} + // Connect connects the OS2L endpoint func (p *Endpoint) Connect(ctx context.Context) error { runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusConnecting) @@ -68,6 +80,14 @@ func (p *Endpoint) Connect(ctx context.Context) error { runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) log.Info().Str("file", "OS2LEndpoint").Msg("OS2L endpoint connected") + + // TODO: To remove : simulate a device arrival/removal + p.addDeviceCallback(ctx, hardware.DeviceInfo{ + SerialNumber: "0DE3FF", + Name: "OS2L test device", + Manufacturer: "BlueSig", + Version: "0.1.0-dev", + }, p) return nil } @@ -130,6 +150,8 @@ func (p *Endpoint) Disconnect(ctx context.Context) error { runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) log.Info().Str("file", "OS2LEndpoint").Msg("OS2L endpoint disconnected") + // TODO: To remove : simulate a device arrival/removal + p.removeDeviceCallback(ctx, "0DE3FF") return nil } diff --git a/hardware/os2l/OS2LProvider.go b/hardware/os2l/OS2LProvider.go index ee50540..1e27eab 100644 --- a/hardware/os2l/OS2LProvider.go +++ b/hardware/os2l/OS2LProvider.go @@ -50,6 +50,7 @@ func (f *Provider) OnRemoval(cb func(context.Context, hardware.Endpoint)) { // Create creates a new endpoint, based on the endpoint information (manually created) func (f *Provider) Create(ctx context.Context, endpointInfo hardware.EndpointInfo) (hardware.EndpointInfo, error) { // If the SerialNumber is empty, generate another one + // TODO: Move the serialnumber generator to the endpoint itself if endpointInfo.SerialNumber == "" { endpointInfo.SerialNumber = strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32))) }