diff --git a/app.go b/app.go index 33abf17..a4426ac 100644 --- a/app.go +++ b/app.go @@ -3,8 +3,12 @@ package main import ( "context" "dmxconnect/hardware" + genericmidi "dmxconnect/hardware/genericMIDI" + "dmxconnect/hardware/genericftdi" + "dmxconnect/hardware/os2l" "fmt" "io" + "time" "github.com/rs/zerolog/log" @@ -29,6 +33,10 @@ type App struct { func NewApp() *App { // Create a new hadware manager hardwareManager := hardware.NewManager() + // Register all the providers to use as hardware scanners + hardwareManager.RegisterProvider(genericftdi.NewProvider(3 * time.Second)) + hardwareManager.RegisterProvider(os2l.NewProvider()) + hardwareManager.RegisterProvider(genericmidi.NewProvider(3 * time.Second)) return &App{ hardwareManager: hardwareManager, projectSave: "", diff --git a/build.bat b/build.bat index b10b859..bbe008e 100644 --- a/build.bat +++ b/build.bat @@ -26,8 +26,8 @@ echo [INFO] Git version detected: %GIT_TAG% echo [INFO] Mode selectionne : %MODE% echo [INFO] Moving to the C++ folder... -cd /d "%~dp0hardware\cpp" || ( - echo [ERROR] Impossible d'accéder à hardware\cpp +cd /d "%~dp0hardware\genericftdi\cpp" || ( + echo [ERROR] Impossible d'accéder à hardware\genericftdi\cpp exit /b 1 ) diff --git a/hardware/cpp/generate.bat b/hardware/cpp/generate.bat deleted file mode 100644 index 4deba9c..0000000 --- a/hardware/cpp/generate.bat +++ /dev/null @@ -1,22 +0,0 @@ -@REM windres dmxSender.rc dmxSender.o -@REM windres detectFTDI.rc detectFTDI.o - -@REM g++ -o dmxSender.exe dmxSender.cpp dmxSender.o -I"include" -L"lib" -lftd2xx -mwindows -@REM g++ -o detectFTDI.exe detectFTDI.cpp detectFTDI.o -I"include" -L"lib" -lftd2xx -mwindows - -@REM g++ -o dmxSender.exe dmxSender.cpp -I"include" -L"lib" -lftd2xx -@REM g++ -o detectFTDI.exe detectFTDI.cpp -I"include" -L"lib" -lftd2xx - -@REM g++ -c dmxSender.cpp -o dmxSender.o -I"include" -L"lib" -lftd2xx -mwindows - -@REM g++ -c dmxSender_wrapper.cpp -o dmxSender_wrapper.o -I"include" -L"lib" -lftd2xx -mwindows - -@REM g++ -shared -o ../../build/bin/libdmxSender.dll src/dmxSender.cpp -fPIC -Wl,--out-implib,../../build/bin/libdmxSender.dll.a -L"lib" -lftd2xx - -@REM Compiling DETECTFTDI library -g++ -shared -o ../../build/bin/libdetectFTDI.dll src/detectFTDI.cpp -fPIC -Wl,--out-implib,../../build/bin/libdetectFTDI.dll.a -L"lib" -lftd2xx -mwindows - -@REM Compiling DMXSENDER library -g++ -shared -o ../../build/bin/libdmxSender.dll src/dmxSender.cpp -fPIC -Wl,--out-implib,../../build/bin/libdmxSender.dll.a -L"lib" -lftd2xx -mwindows - -@REM g++ -shared -o libdmxSender.so dmxSender.cpp -fPIC -I"include" -L"lib" -lftd2xx -mwindows \ No newline at end of file diff --git a/hardware/FTDIEndpoint.go b/hardware/genericftdi/FTDIEndpoint.go similarity index 70% rename from hardware/FTDIEndpoint.go rename to hardware/genericftdi/FTDIEndpoint.go index 8a5bc1b..2724036 100644 --- a/hardware/FTDIEndpoint.go +++ b/hardware/genericftdi/FTDIEndpoint.go @@ -1,7 +1,8 @@ -package hardware +package genericftdi import ( "context" + "dmxconnect/hardware" "sync" "unsafe" @@ -13,36 +14,36 @@ import ( /* #include -#cgo LDFLAGS: -L${SRCDIR}/../build/bin -ldmxSender +#cgo LDFLAGS: -L${SRCDIR}/../../build/bin -ldmxSender #include "cpp/include/dmxSenderBridge.h" */ import "C" -// FTDIEndpoint contains the data of an FTDI endpoint -type FTDIEndpoint struct { +// Endpoint contains the data of an FTDI endpoint +type Endpoint struct { wg sync.WaitGroup - info EndpointInfo // The endpoint basic data - dmxSender unsafe.Pointer // The command object for piloting the DMX ouptut + info hardware.EndpointInfo // The endpoint basic data + dmxSender unsafe.Pointer // The command object for piloting the DMX ouptut } -// NewFTDIEndpoint creates a new FTDI endpoint -func NewFTDIEndpoint(info EndpointInfo) *FTDIEndpoint { +// NewEndpoint creates a new FTDI endpoint +func NewEndpoint(info hardware.EndpointInfo) *Endpoint { log.Info().Str("file", "FTDIEndpoint").Str("name", info.Name).Str("s/n", info.SerialNumber).Msg("FTDI endpoint created") - return &FTDIEndpoint{ + return &Endpoint{ info: info, dmxSender: nil, } } // Connect connects the FTDI endpoint -func (p *FTDIEndpoint) Connect(ctx context.Context) error { +func (p *Endpoint) Connect(ctx context.Context) error { // Check if the device has already been created if p.dmxSender != nil { return errors.Errorf("the DMX device has already been created!") } - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusConnecting) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusConnecting) // Create the DMX sender p.dmxSender = C.dmx_create() @@ -51,7 +52,7 @@ func (p *FTDIEndpoint) Connect(ctx context.Context) error { serialNumber := C.CString(p.info.SerialNumber) defer C.free(unsafe.Pointer(serialNumber)) if C.dmx_connect(p.dmxSender, serialNumber) != C.DMX_OK { - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDisconnected) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) log.Error().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("unable to connect the DMX device") return errors.Errorf("unable to connect '%s'", p.info.SerialNumber) } @@ -63,13 +64,13 @@ func (p *FTDIEndpoint) Connect(ctx context.Context) error { _ = p.Disconnect(ctx) }() - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDeactivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("DMX device connected successfully") return nil } // Disconnect disconnects the FTDI endpoint -func (p *FTDIEndpoint) Disconnect(ctx context.Context) error { +func (p *Endpoint) Disconnect(ctx context.Context) error { // Check if the device has already been created if p.dmxSender == nil { return errors.Errorf("the DMX device has not been connected!") @@ -81,12 +82,12 @@ func (p *FTDIEndpoint) Disconnect(ctx context.Context) error { // Reset the pointer to the endpoint p.dmxSender = nil - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDisconnected) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) return nil } // Activate activates the FTDI endpoint -func (p *FTDIEndpoint) Activate(ctx context.Context) error { +func (p *Endpoint) Activate(ctx context.Context) error { // Check if the device has already been created if p.dmxSender == nil { return errors.Errorf("the DMX sender has not been created!") @@ -103,14 +104,14 @@ func (p *FTDIEndpoint) Activate(ctx context.Context) error { C.dmx_setValue(p.dmxSender, C.uint16_t(1), C.uint8_t(255)) C.dmx_setValue(p.dmxSender, C.uint16_t(5), C.uint8_t(255)) - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusActivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusActivated) log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("DMX device activated successfully") return nil } // Deactivate deactivates the FTDI endpoint -func (p *FTDIEndpoint) Deactivate(ctx context.Context) error { +func (p *Endpoint) Deactivate(ctx context.Context) error { // Check if the device has already been created if p.dmxSender == nil { return errors.Errorf("the DMX device has not been created!") @@ -123,18 +124,18 @@ func (p *FTDIEndpoint) Deactivate(ctx context.Context) error { return errors.Errorf("unable to deactivate the DMX sender!") } - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDeactivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("DMX device deactivated successfully") return nil } // SetSettings sets a specific setting for this endpoint -func (p *FTDIEndpoint) SetSettings(ctx context.Context, settings map[string]any) error { +func (p *Endpoint) SetSettings(ctx context.Context, settings map[string]any) error { return errors.Errorf("unable to set the settings: not implemented") } // SetDeviceProperty sends a command to the specified device -func (p *FTDIEndpoint) SetDeviceProperty(ctx context.Context, channelNumber uint32, channelValue byte) error { +func (p *Endpoint) SetDeviceProperty(ctx context.Context, channelNumber uint32, channelValue byte) error { // Check if the device has already been created if p.dmxSender == nil { return errors.Errorf("the DMX device has not been created!") @@ -153,17 +154,17 @@ func (p *FTDIEndpoint) SetDeviceProperty(ctx context.Context, channelNumber uint } // GetSettings gets the endpoint settings -func (p *FTDIEndpoint) GetSettings() map[string]interface{} { +func (p *Endpoint) GetSettings() map[string]interface{} { return map[string]interface{}{} } // GetInfo gets all the endpoint information -func (p *FTDIEndpoint) GetInfo() EndpointInfo { +func (p *Endpoint) GetInfo() hardware.EndpointInfo { return p.info } // WaitStop wait about the endpoint to close -func (p *FTDIEndpoint) WaitStop() error { +func (p *Endpoint) WaitStop() error { log.Info().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("waiting for FTDI endpoint to close...") p.wg.Wait() log.Info().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("FTDI endpoint closed!") diff --git a/hardware/FTDIProvider.go b/hardware/genericftdi/FTDIProvider.go similarity index 72% rename from hardware/FTDIProvider.go rename to hardware/genericftdi/FTDIProvider.go index 33dc369..ae5b8a2 100644 --- a/hardware/FTDIProvider.go +++ b/hardware/genericftdi/FTDIProvider.go @@ -1,7 +1,8 @@ -package hardware +package genericftdi import ( "context" + "dmxconnect/hardware" "fmt" goRuntime "runtime" "sync" @@ -13,45 +14,45 @@ import ( /* #include -#cgo LDFLAGS: -L${SRCDIR}/../build/bin -ldetectFTDI +#cgo LDFLAGS: -L${SRCDIR}/../../build/bin -ldetectFTDI #include "cpp/include/detectFTDIBridge.h" */ import "C" -// FTDIProvider manages all the FTDI endpoints -type FTDIProvider struct { +// Provider manages all the FTDI endpoints +type Provider struct { wg sync.WaitGroup mu sync.Mutex - detected map[string]*FTDIEndpoint // Detected endpoints + detected map[string]*Endpoint // Detected endpoints scanEvery time.Duration // Scans endpoints periodically - onArrival func(context.Context, Endpoint) // When a endpoint arrives - onRemoval func(context.Context, Endpoint) // When a endpoint goes away + onArrival func(context.Context, hardware.Endpoint) // When a endpoint arrives + onRemoval func(context.Context, hardware.Endpoint) // When a endpoint goes away } -// NewFTDIProvider creates a new FTDI provider -func NewFTDIProvider(scanEvery time.Duration) *FTDIProvider { +// NewProvider creates a new FTDI provider +func NewProvider(scanEvery time.Duration) *Provider { log.Trace().Str("file", "FTDIProvider").Msg("FTDI provider created") - return &FTDIProvider{ + return &Provider{ scanEvery: scanEvery, - detected: make(map[string]*FTDIEndpoint), + detected: make(map[string]*Endpoint), } } // OnArrival is the callback function when a new endpoint arrives -func (f *FTDIProvider) OnArrival(cb func(context.Context, Endpoint)) { +func (f *Provider) OnArrival(cb func(context.Context, hardware.Endpoint)) { f.onArrival = cb } // OnRemoval i the callback when a endpoint goes away -func (f *FTDIProvider) OnRemoval(cb func(context.Context, Endpoint)) { +func (f *Provider) OnRemoval(cb func(context.Context, hardware.Endpoint)) { f.onRemoval = cb } // Initialize initializes the FTDI provider -func (f *FTDIProvider) Initialize() error { +func (f *Provider) Initialize() error { // Check platform if goRuntime.GOOS != "windows" { log.Error().Str("file", "FTDIProvider").Str("platform", goRuntime.GOOS).Msg("FTDI provider not compatible with your platform") @@ -62,17 +63,17 @@ func (f *FTDIProvider) Initialize() error { } // Create creates a new endpoint, based on the endpoint information (manually created) -func (f *FTDIProvider) Create(ctx context.Context, endpointInfo EndpointInfo) (EndpointInfo, error) { - return EndpointInfo{}, nil +func (f *Provider) Create(ctx context.Context, endpointInfo hardware.EndpointInfo) (hardware.EndpointInfo, error) { + return hardware.EndpointInfo{}, nil } // Remove removes an existing endpoint (manually created) -func (f *FTDIProvider) Remove(ctx context.Context, endpoint Endpoint) error { +func (f *Provider) Remove(ctx context.Context, endpoint hardware.Endpoint) error { return nil } // Start starts the provider and search for endpoints -func (f *FTDIProvider) Start(ctx context.Context) error { +func (f *Provider) Start(ctx context.Context) error { f.wg.Add(1) go func() { ticker := time.NewTicker(f.scanEvery) @@ -96,12 +97,12 @@ func (f *FTDIProvider) Start(ctx context.Context) error { } // GetName returns the name of the driver -func (f *FTDIProvider) GetName() string { +func (f *Provider) GetName() string { return "FTDI" } // scanEndpoints scans the FTDI endpoints -func (f *FTDIProvider) scanEndpoints(ctx context.Context) error { +func (f *Provider) scanEndpoints(ctx context.Context) error { log.Trace().Str("file", "FTDIProvider").Msg("FTDI scan triggered") count := int(C.get_endpoints_number()) @@ -117,7 +118,7 @@ func (f *FTDIProvider) scanEndpoints(ctx context.Context) error { C.get_ftdi_devices((*C.FTDIEndpointC)(devicesPtr), C.int(count)) - currentMap := make(map[string]EndpointInfo) + currentMap := make(map[string]hardware.EndpointInfo) for i := 0; i < count; i++ { d := devices[i] @@ -126,7 +127,7 @@ func (f *FTDIProvider) scanEndpoints(ctx context.Context) error { desc := C.GoString(d.description) // isOpen := d.isOpen != 0 - currentMap[sn] = EndpointInfo{ + currentMap[sn] = hardware.EndpointInfo{ SerialNumber: sn, Name: desc, // IsOpen: isOpen, @@ -144,7 +145,7 @@ func (f *FTDIProvider) scanEndpoints(ctx context.Context) error { // If the scanned endpoint isn't in the detected list, create it if _, known := f.detected[sn]; !known { - endpoint := NewFTDIEndpoint(endpointData) + endpoint := NewEndpoint(endpointData) if f.onArrival != nil { f.onArrival(ctx, endpoint) @@ -169,7 +170,7 @@ func (f *FTDIProvider) scanEndpoints(ctx context.Context) error { } // WaitStop stops the provider -func (f *FTDIProvider) WaitStop() error { +func (f *Provider) WaitStop() error { log.Trace().Str("file", "FTDIProvider").Msg("stopping the FTDI provider...") // Wait for goroutines to stop diff --git a/hardware/genericftdi/cpp/generate.bat b/hardware/genericftdi/cpp/generate.bat new file mode 100644 index 0000000..0efe750 --- /dev/null +++ b/hardware/genericftdi/cpp/generate.bat @@ -0,0 +1,7 @@ +@REM Compiling DETECTFTDI library +g++ -shared -o ../../../build/bin/libdetectFTDI.dll src/detectFTDI.cpp -fPIC -Wl,--out-implib,../../../build/bin/libdetectFTDI.dll.a -L"lib" -lftd2xx -mwindows + +@REM Compiling DMXSENDER library +g++ -shared -o ../../../build/bin/libdmxSender.dll src/dmxSender.cpp -fPIC -Wl,--out-implib,../../../build/bin/libdmxSender.dll.a -L"lib" -lftd2xx -mwindows + +@REM g++ -shared -o libdmxSender.so dmxSender.cpp -fPIC -I"include" -L"lib" -lftd2xx -mwindows \ No newline at end of file diff --git a/hardware/cpp/include/detectFTDIBridge.h b/hardware/genericftdi/cpp/include/detectFTDIBridge.h similarity index 100% rename from hardware/cpp/include/detectFTDIBridge.h rename to hardware/genericftdi/cpp/include/detectFTDIBridge.h diff --git a/hardware/cpp/include/dmxSenderBridge.h b/hardware/genericftdi/cpp/include/dmxSenderBridge.h similarity index 100% rename from hardware/cpp/include/dmxSenderBridge.h rename to hardware/genericftdi/cpp/include/dmxSenderBridge.h diff --git a/hardware/cpp/lib/ftd2xx.lib b/hardware/genericftdi/cpp/lib/ftd2xx.lib similarity index 100% rename from hardware/cpp/lib/ftd2xx.lib rename to hardware/genericftdi/cpp/lib/ftd2xx.lib diff --git a/hardware/cpp/src/detectFTDI.cpp b/hardware/genericftdi/cpp/src/detectFTDI.cpp similarity index 100% rename from hardware/cpp/src/detectFTDI.cpp rename to hardware/genericftdi/cpp/src/detectFTDI.cpp diff --git a/hardware/cpp/src/detectFTDI.h b/hardware/genericftdi/cpp/src/detectFTDI.h similarity index 100% rename from hardware/cpp/src/detectFTDI.h rename to hardware/genericftdi/cpp/src/detectFTDI.h diff --git a/hardware/cpp/src/dmxSender.cpp b/hardware/genericftdi/cpp/src/dmxSender.cpp similarity index 100% rename from hardware/cpp/src/dmxSender.cpp rename to hardware/genericftdi/cpp/src/dmxSender.cpp diff --git a/hardware/cpp/src/dmxSender.h b/hardware/genericftdi/cpp/src/dmxSender.h similarity index 100% rename from hardware/cpp/src/dmxSender.h rename to hardware/genericftdi/cpp/src/dmxSender.h diff --git a/hardware/cpp/src/ftd2xx.h b/hardware/genericftdi/cpp/src/ftd2xx.h similarity index 100% rename from hardware/cpp/src/ftd2xx.h rename to hardware/genericftdi/cpp/src/ftd2xx.h diff --git a/hardware/cpp/test/detectFTDI_test.cpp b/hardware/genericftdi/cpp/test/detectFTDI_test.cpp similarity index 100% rename from hardware/cpp/test/detectFTDI_test.cpp rename to hardware/genericftdi/cpp/test/detectFTDI_test.cpp diff --git a/hardware/cpp/test/dmxSender_test.cpp b/hardware/genericftdi/cpp/test/dmxSender_test.cpp similarity index 100% rename from hardware/cpp/test/dmxSender_test.cpp rename to hardware/genericftdi/cpp/test/dmxSender_test.cpp diff --git a/hardware/MIDIEndpoint.go b/hardware/genericmidi/MIDIEndpoint.go similarity index 52% rename from hardware/MIDIEndpoint.go rename to hardware/genericmidi/MIDIEndpoint.go index ce03e87..7d2e615 100644 --- a/hardware/MIDIEndpoint.go +++ b/hardware/genericmidi/MIDIEndpoint.go @@ -1,7 +1,8 @@ -package hardware +package genericmidi import ( "context" + "dmxconnect/hardware" "fmt" "sync" @@ -10,30 +11,30 @@ import ( "gitlab.com/gomidi/midi" ) -// MIDIDevice represents the device to control -type MIDIDevice struct { - ID string // Device ID - Mapping MappingInfo // Device mapping configuration +// Device represents the device to control +type Device struct { + ID string // Device ID + Mapping hardware.MappingInfo // Device mapping configuration } // ------------------- // -// MIDIEndpoint contains the data of a MIDI endpoint -type MIDIEndpoint struct { +// Endpoint contains the data of a MIDI endpoint +type Endpoint struct { wg sync.WaitGroup inputPorts []midi.In outputsPorts []midi.Out - info EndpointInfo // The endpoint info + info hardware.EndpointInfo // The endpoint info settings map[string]interface{} // The settings of the endpoint - devices []MIDIDevice // All the MIDI devices that the endpoint can handle + devices []Device // All the MIDI devices that the endpoint can handle } -// NewMIDIEndpoint creates a new MIDI endpoint -func NewMIDIEndpoint(endpointData EndpointInfo, inputs []midi.In, outputs []midi.Out) *MIDIEndpoint { +// NewEndpoint creates a new MIDI endpoint +func NewEndpoint(endpointData hardware.EndpointInfo, inputs []midi.In, outputs []midi.Out) *Endpoint { log.Trace().Str("file", "MIDIEndpoint").Str("name", endpointData.Name).Str("s/n", endpointData.SerialNumber).Msg("MIDI endpoint created") - return &MIDIEndpoint{ + return &Endpoint{ info: endpointData, inputPorts: inputs, outputsPorts: outputs, @@ -42,19 +43,19 @@ func NewMIDIEndpoint(endpointData EndpointInfo, inputs []midi.In, outputs []midi } // Connect connects the MIDI endpoint -func (p *MIDIEndpoint) Connect(ctx context.Context) error { - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusConnecting) +func (p *Endpoint) Connect(ctx context.Context) error { + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusConnecting) // Open input ports for _, port := range p.inputPorts { err := port.Open() if err != nil { - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDisconnected) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) return fmt.Errorf("unable to open the MIDI IN port: %w", err) } port.SetListener(func(msg []byte, delta int64) { // Emit the event to the front - runtime.EventsEmit(ctx, string(EndpointEventEmitted), p.info.SerialNumber, msg) + runtime.EventsEmit(ctx, string(hardware.EndpointEventEmitted), p.info.SerialNumber, msg) log.Debug().Str("message", string(msg)).Int64("delta", delta).Msg("message received") }) log.Info().Str("name", port.String()).Msg("port open successfully") @@ -67,13 +68,13 @@ func (p *MIDIEndpoint) Connect(ctx context.Context) error { _ = p.Disconnect(ctx) }() - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDeactivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) return nil } // Disconnect disconnects the MIDI endpoint -func (p *MIDIEndpoint) Disconnect(ctx context.Context) error { +func (p *Endpoint) Disconnect(ctx context.Context) error { // Close all inputs ports for _, port := range p.inputPorts { err := port.Close() @@ -81,47 +82,47 @@ func (p *MIDIEndpoint) Disconnect(ctx context.Context) error { return fmt.Errorf("unable to close the MIDI IN port: %w", err) } } - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDisconnected) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) return nil } // Activate activates the MIDI endpoint -func (p *MIDIEndpoint) Activate(ctx context.Context) error { - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusActivated) +func (p *Endpoint) Activate(ctx context.Context) error { + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusActivated) return nil } // Deactivate deactivates the MIDI endpoint -func (p *MIDIEndpoint) Deactivate(ctx context.Context) error { - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDeactivated) +func (p *Endpoint) Deactivate(ctx context.Context) error { + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) return nil } // SetSettings sets a specific setting for this endpoint -func (p *MIDIEndpoint) SetSettings(ctx context.Context, settings map[string]any) error { +func (p *Endpoint) SetSettings(ctx context.Context, settings map[string]any) error { p.settings = settings return nil } // SetDeviceProperty - not implemented for this kind of endpoint -func (p *MIDIEndpoint) SetDeviceProperty(context.Context, uint32, byte) error { +func (p *Endpoint) SetDeviceProperty(context.Context, uint32, byte) error { return nil } // GetSettings gets the endpoint settings -func (p *MIDIEndpoint) GetSettings() map[string]interface{} { +func (p *Endpoint) GetSettings() map[string]interface{} { return p.settings } // GetInfo gets the endpoint information -func (p *MIDIEndpoint) GetInfo() EndpointInfo { +func (p *Endpoint) GetInfo() hardware.EndpointInfo { return p.info } // WaitStop wait about the endpoint to close -func (p *MIDIEndpoint) WaitStop() error { +func (p *Endpoint) WaitStop() error { log.Info().Str("file", "MIDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("waiting for MIDI endpoint to close...") p.wg.Wait() log.Info().Str("file", "MIDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("MIDI endpoint closed!") diff --git a/hardware/MIDIProvider.go b/hardware/genericmidi/MIDIProvider.go similarity index 78% rename from hardware/MIDIProvider.go rename to hardware/genericmidi/MIDIProvider.go index ef9a86b..2f0f88d 100644 --- a/hardware/MIDIProvider.go +++ b/hardware/genericmidi/MIDIProvider.go @@ -1,7 +1,8 @@ -package hardware +package genericmidi import ( "context" + "dmxconnect/hardware" "fmt" "regexp" "strconv" @@ -13,56 +14,56 @@ import ( "gitlab.com/gomidi/rtmididrv" ) -// MIDIProvider represents how the protocol is defined -type MIDIProvider struct { +// Provider represents how the protocol is defined +type Provider struct { wg sync.WaitGroup mu sync.Mutex - detected map[string]*MIDIEndpoint // Detected endpoints + detected map[string]*Endpoint // Detected endpoints scanEvery time.Duration // Scans endpoints periodically - onArrival func(context.Context, Endpoint) // When a endpoint arrives - onRemoval func(context.Context, Endpoint) // When a endpoint goes away + onArrival func(context.Context, hardware.Endpoint) // When a endpoint arrives + onRemoval func(context.Context, hardware.Endpoint) // When a endpoint goes away } -// NewMIDIProvider creates a new MIDI provider -func NewMIDIProvider(scanEvery time.Duration) *MIDIProvider { +// NewProvider creates a new MIDI provider +func NewProvider(scanEvery time.Duration) *Provider { log.Trace().Str("file", "MIDIProvider").Msg("MIDI provider created") - return &MIDIProvider{ + return &Provider{ scanEvery: scanEvery, - detected: make(map[string]*MIDIEndpoint), + detected: make(map[string]*Endpoint), } } // OnArrival is the callback function when a new endpoint arrives -func (f *MIDIProvider) OnArrival(cb func(context.Context, Endpoint)) { +func (f *Provider) OnArrival(cb func(context.Context, hardware.Endpoint)) { f.onArrival = cb } // OnRemoval i the callback when a endpoint goes away -func (f *MIDIProvider) OnRemoval(cb func(context.Context, Endpoint)) { +func (f *Provider) OnRemoval(cb func(context.Context, hardware.Endpoint)) { f.onRemoval = cb } // Initialize initializes the MIDI driver -func (f *MIDIProvider) Initialize() error { +func (f *Provider) Initialize() error { log.Trace().Str("file", "MIDIProvider").Msg("MIDI provider initialized") return nil } // Create creates a new endpoint, based on the endpoint information (manually created) -func (f *MIDIProvider) Create(ctx context.Context, endpointInfo EndpointInfo) (EndpointInfo, error) { - return EndpointInfo{}, nil +func (f *Provider) Create(ctx context.Context, endpointInfo hardware.EndpointInfo) (hardware.EndpointInfo, error) { + return hardware.EndpointInfo{}, nil } // Remove removes an existing endpoint (manually created) -func (f *MIDIProvider) Remove(ctx context.Context, endpoint Endpoint) error { +func (f *Provider) Remove(ctx context.Context, endpoint hardware.Endpoint) error { return nil } // Start starts the provider and search for endpoints -func (f *MIDIProvider) Start(ctx context.Context) error { +func (f *Provider) Start(ctx context.Context) error { f.wg.Add(1) go func() { ticker := time.NewTicker(f.scanEvery) @@ -86,7 +87,7 @@ func (f *MIDIProvider) Start(ctx context.Context) error { } // WaitStop stops the provider -func (f *MIDIProvider) WaitStop() error { +func (f *Provider) WaitStop() error { log.Trace().Str("file", "MIDIProvider").Msg("stopping the MIDI provider...") // Wait for goroutines to stop @@ -97,7 +98,7 @@ func (f *MIDIProvider) WaitStop() error { } // GetName returns the name of the driver -func (f *MIDIProvider) GetName() string { +func (f *Provider) GetName() string { return "MIDI" } @@ -123,8 +124,8 @@ func splitStringAndNumber(input string) (string, int, error) { } // scanEndpoints scans the MIDI endpoints -func (f *MIDIProvider) scanEndpoints(ctx context.Context) error { - currentMap := make(map[string]*MIDIEndpoint) +func (f *Provider) scanEndpoints(ctx context.Context) error { + currentMap := make(map[string]*Endpoint) drv, err := rtmididrv.New() if err != nil { @@ -148,8 +149,8 @@ func (f *MIDIProvider) scanEndpoints(ctx context.Context) error { sn := strings.ReplaceAll(strings.ToLower(baseName), " ", "_") if _, ok := currentMap[sn]; !ok { - currentMap[sn] = &MIDIEndpoint{ - info: EndpointInfo{ + currentMap[sn] = &Endpoint{ + info: hardware.EndpointInfo{ Name: baseName, SerialNumber: sn, ProtocolName: "MIDI", @@ -177,8 +178,8 @@ func (f *MIDIProvider) scanEndpoints(ctx context.Context) error { sn := strings.ReplaceAll(strings.ToLower(baseName), " ", "_") if _, ok := currentMap[sn]; !ok { - currentMap[sn] = &MIDIEndpoint{ - info: EndpointInfo{ + currentMap[sn] = &Endpoint{ + info: hardware.EndpointInfo{ Name: baseName, SerialNumber: sn, ProtocolName: "MIDI", @@ -196,7 +197,7 @@ func (f *MIDIProvider) scanEndpoints(ctx context.Context) error { for sn, discovery := range currentMap { if _, known := f.detected[sn]; !known { - endpoint := NewMIDIEndpoint(discovery.info, discovery.inputPorts, discovery.outputsPorts) + endpoint := NewEndpoint(discovery.info, discovery.inputPorts, discovery.outputsPorts) f.detected[sn] = endpoint diff --git a/hardware/hardware.go b/hardware/hardware.go index 7186458..898476b 100644 --- a/hardware/hardware.go +++ b/hardware/hardware.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "sync" - "time" "github.com/rs/zerolog/log" "github.com/wailsapp/wails/v2/pkg/runtime" @@ -165,12 +164,6 @@ func (h *Manager) SetEndpointSettings(ctx context.Context, endpointSN string, se // Start starts to find new endpoint events func (h *Manager) Start(ctx context.Context) error { - - // Register all the providers to use as hardware scanners - h.RegisterProvider(NewFTDIProvider(3 * time.Second)) - h.RegisterProvider(NewOS2LProvider()) - h.RegisterProvider(NewMIDIProvider(3 * time.Second)) - for providerName, provider := range h.providers { // Initialize the provider diff --git a/hardware/OS2LEndpoint.go b/hardware/os2l/OS2LEndpoint.go similarity index 75% rename from hardware/OS2LEndpoint.go rename to hardware/os2l/OS2LEndpoint.go index e5dfffb..8018116 100644 --- a/hardware/OS2LEndpoint.go +++ b/hardware/os2l/OS2LEndpoint.go @@ -1,7 +1,8 @@ -package hardware +package os2l import ( "context" + "dmxconnect/hardware" "encoding/json" "fmt" "net" @@ -13,8 +14,8 @@ import ( "github.com/wailsapp/wails/v2/pkg/runtime" ) -// OS2LMessage represents an OS2L message -type OS2LMessage struct { +// Message represents an OS2L message +type Message struct { Event string `json:"evt"` Name string `json:"name"` State string `json:"state"` @@ -22,22 +23,22 @@ type OS2LMessage struct { Param float64 `json:"param"` } -// OS2LEndpoint contains the data of an OS2L endpoint -type OS2LEndpoint struct { +// Endpoint contains the data of an OS2L endpoint +type Endpoint struct { wg sync.WaitGroup - info EndpointInfo // The basic info for this endpoint - serverIP string // OS2L server IP - serverPort int // OS2L server port - listener net.Listener // Net listener (TCP) - listenerCancel context.CancelFunc // Call this function to cancel the endpoint activation + info hardware.EndpointInfo // The basic info for this endpoint + serverIP string // OS2L server IP + serverPort int // OS2L server port + 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 } // NewOS2LEndpoint creates a new OS2L endpoint -func NewOS2LEndpoint(endpointData EndpointInfo) (*OS2LEndpoint, error) { - endpoint := &OS2LEndpoint{ +func NewOS2LEndpoint(endpointData hardware.EndpointInfo) (*Endpoint, error) { + endpoint := &Endpoint{ info: endpointData, listener: nil, eventCallback: nil, @@ -47,13 +48,13 @@ func NewOS2LEndpoint(endpointData EndpointInfo) (*OS2LEndpoint, error) { } // SetEventCallback sets the callback for returning events -func (p *OS2LEndpoint) SetEventCallback(eventCallback func(any)) { +func (p *Endpoint) SetEventCallback(eventCallback func(any)) { p.eventCallback = eventCallback } // Connect connects the OS2L endpoint -func (p *OS2LEndpoint) Connect(ctx context.Context) error { - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusConnecting) +func (p *Endpoint) Connect(ctx context.Context) error { + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusConnecting) var err error addr := net.TCPAddr{Port: p.serverPort, IP: net.ParseIP(p.serverIP)} @@ -61,18 +62,18 @@ func (p *OS2LEndpoint) Connect(ctx context.Context) error { 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(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDisconnected) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) return fmt.Errorf("unable to set the OS2L TCP listener") } - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDeactivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) log.Info().Str("file", "OS2LEndpoint").Msg("OS2L endpoint connected") return nil } // handleMessage handles an OS2L message -func (p *OS2LEndpoint) handleMessage(raw []byte) error { - message := OS2LMessage{} +func (p *Endpoint) handleMessage(raw []byte) error { + message := Message{} err := json.Unmarshal(raw, &message) if err != nil { return fmt.Errorf("Unable to parse the OS2L message: %w", err) @@ -86,7 +87,7 @@ func (p *OS2LEndpoint) handleMessage(raw []byte) error { } // loadSettings check and load the settings in the endpoint -func (p *OS2LEndpoint) loadSettings(settings map[string]any) error { +func (p *Endpoint) loadSettings(settings map[string]any) error { // Check if the IP exists serverIP, found := settings["os2lIp"] if !found { @@ -119,21 +120,21 @@ func (p *OS2LEndpoint) loadSettings(settings map[string]any) error { } // Disconnect disconnects the MIDI endpoint -func (p *OS2LEndpoint) Disconnect(ctx context.Context) error { +func (p *Endpoint) Disconnect(ctx context.Context) error { // Close the TCP listener if not null if p.listener != nil { p.listener.Close() } - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDisconnected) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) log.Info().Str("file", "OS2LEndpoint").Msg("OS2L endpoint disconnected") return nil } // Activate activates the OS2L endpoint -func (p *OS2LEndpoint) Activate(ctx context.Context) error { +func (p *Endpoint) Activate(ctx context.Context) error { // Create a derived context to handle deactivation var listenerCtx context.Context listenerCtx, p.listenerCancel = context.WithCancel(ctx) @@ -198,7 +199,7 @@ func (p *OS2LEndpoint) Activate(ctx context.Context) error { } } }() - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusActivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusActivated) log.Info().Str("file", "OS2LEndpoint").Msg("OS2L endpoint activated") @@ -206,7 +207,7 @@ func (p *OS2LEndpoint) Activate(ctx context.Context) error { } // Deactivate deactivates the OS2L endpoint -func (p *OS2LEndpoint) Deactivate(ctx context.Context) error { +func (p *Endpoint) Deactivate(ctx context.Context) error { if p.listener == nil { return fmt.Errorf("the listener isn't defined") } @@ -216,14 +217,14 @@ func (p *OS2LEndpoint) Deactivate(ctx context.Context) error { p.listenerCancel() } - runtime.EventsEmit(ctx, string(EndpointStatusUpdated), p.GetInfo(), EndpointStatusDeactivated) + runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDeactivated) log.Info().Str("file", "OS2LEndpoint").Msg("OS2L endpoint deactivated") return nil } // SetSettings sets a specific setting for this endpoint -func (p *OS2LEndpoint) SetSettings(ctx context.Context, settings map[string]any) error { +func (p *Endpoint) 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) @@ -260,12 +261,12 @@ func (p *OS2LEndpoint) SetSettings(ctx context.Context, settings map[string]any) } // SetDeviceProperty - not implemented for this kind of endpoint -func (p *OS2LEndpoint) SetDeviceProperty(context.Context, uint32, byte) error { +func (p *Endpoint) SetDeviceProperty(context.Context, uint32, byte) error { return nil } // GetSettings gets the endpoint settings -func (p *OS2LEndpoint) GetSettings() map[string]any { +func (p *Endpoint) GetSettings() map[string]any { return map[string]any{ "os2lIp": p.serverIP, "os2lPort": p.serverPort, @@ -273,12 +274,12 @@ func (p *OS2LEndpoint) GetSettings() map[string]any { } // GetInfo gets the endpoint information -func (p *OS2LEndpoint) GetInfo() EndpointInfo { +func (p *Endpoint) GetInfo() hardware.EndpointInfo { return p.info } // WaitStop stops the endpoint -func (p *OS2LEndpoint) WaitStop() error { +func (p *Endpoint) WaitStop() error { log.Info().Str("file", "OS2LEndpoint").Str("s/n", p.info.SerialNumber).Msg("waiting for OS2L endpoint to close...") p.wg.Wait() log.Info().Str("file", "OS2LEndpoint").Str("s/n", p.info.SerialNumber).Msg("OS2L endpoint closed!") diff --git a/hardware/OS2LProvider.go b/hardware/os2l/OS2LProvider.go similarity index 59% rename from hardware/OS2LProvider.go rename to hardware/os2l/OS2LProvider.go index 208d888..ee50540 100644 --- a/hardware/OS2LProvider.go +++ b/hardware/os2l/OS2LProvider.go @@ -1,7 +1,8 @@ -package hardware +package os2l import ( "context" + "dmxconnect/hardware" "fmt" "math/rand" "strings" @@ -11,43 +12,43 @@ import ( "github.com/wailsapp/wails/v2/pkg/runtime" ) -// OS2LProvider represents how the protocol is defined -type OS2LProvider struct { +// Provider represents how the protocol is defined +type Provider struct { wg sync.WaitGroup mu sync.Mutex - detected map[string]*OS2LEndpoint // The list of saved endpoints + detected map[string]*Endpoint // The list of saved endpoints - onArrival func(context.Context, Endpoint) // When a endpoint arrives - onRemoval func(context.Context, Endpoint) // When a endpoint goes away + onArrival func(context.Context, hardware.Endpoint) // When a endpoint arrives + onRemoval func(context.Context, hardware.Endpoint) // When a endpoint goes away } -// NewOS2LProvider creates a new OS2L provider -func NewOS2LProvider() *OS2LProvider { +// NewProvider creates a new OS2L provider +func NewProvider() *Provider { log.Trace().Str("file", "OS2LProvider").Msg("OS2L provider created") - return &OS2LProvider{ - detected: make(map[string]*OS2LEndpoint), + return &Provider{ + detected: make(map[string]*Endpoint), } } // Initialize initializes the provider -func (f *OS2LProvider) Initialize() error { +func (f *Provider) Initialize() error { log.Trace().Str("file", "OS2LProvider").Msg("OS2L provider initialized") return nil } // OnArrival is the callback function when a new endpoint arrives -func (f *OS2LProvider) OnArrival(cb func(context.Context, Endpoint)) { +func (f *Provider) OnArrival(cb func(context.Context, hardware.Endpoint)) { f.onArrival = cb } // OnRemoval if the callback when a endpoint goes away -func (f *OS2LProvider) OnRemoval(cb func(context.Context, Endpoint)) { +func (f *Provider) OnRemoval(cb func(context.Context, hardware.Endpoint)) { f.onRemoval = cb } // Create creates a new endpoint, based on the endpoint information (manually created) -func (f *OS2LProvider) Create(ctx context.Context, endpointInfo EndpointInfo) (EndpointInfo, error) { +func (f *Provider) Create(ctx context.Context, endpointInfo hardware.EndpointInfo) (hardware.EndpointInfo, error) { // If the SerialNumber is empty, generate another one if endpointInfo.SerialNumber == "" { endpointInfo.SerialNumber = strings.ToUpper(fmt.Sprintf("%08x", rand.Intn(1<<32))) @@ -56,12 +57,12 @@ func (f *OS2LProvider) Create(ctx context.Context, endpointInfo EndpointInfo) (E // Create a new OS2L endpoint endpoint, err := NewOS2LEndpoint(endpointInfo) if err != nil { - return EndpointInfo{}, fmt.Errorf("unable to create the OS2L endpoint: %w", err) + return hardware.EndpointInfo{}, fmt.Errorf("unable to create the OS2L endpoint: %w", err) } // Set the event callback endpoint.SetEventCallback(func(event any) { - runtime.EventsEmit(ctx, string(EndpointEventEmitted), endpointInfo.SerialNumber, event) + runtime.EventsEmit(ctx, string(hardware.EndpointEventEmitted), endpointInfo.SerialNumber, event) }) f.detected[endpointInfo.SerialNumber] = endpoint @@ -73,7 +74,7 @@ func (f *OS2LProvider) Create(ctx context.Context, endpointInfo EndpointInfo) (E } // Remove removes an existing endpoint (manually created) -func (f *OS2LProvider) Remove(ctx context.Context, endpoint Endpoint) error { +func (f *Provider) Remove(ctx context.Context, endpoint hardware.Endpoint) error { if f.onRemoval != nil { f.onRemoval(ctx, endpoint) } @@ -82,18 +83,18 @@ func (f *OS2LProvider) Remove(ctx context.Context, endpoint Endpoint) error { } // GetName returns the name of the driver -func (f *OS2LProvider) GetName() string { +func (f *Provider) GetName() string { return "OS2L" } // Start starts the provider -func (f *OS2LProvider) Start(ctx context.Context) error { +func (f *Provider) Start(ctx context.Context) error { // No endpoints to scan here return nil } // WaitStop stops the provider -func (f *OS2LProvider) WaitStop() error { +func (f *Provider) WaitStop() error { log.Trace().Str("file", "OS2LProvider").Msg("stopping the OS2L provider...") // Waiting internal tasks