package genericftdi import ( "context" "dmxconnect/hardware" "sync" "unsafe" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/wailsapp/wails/v2/pkg/runtime" ) /* #include #cgo LDFLAGS: -L${SRCDIR}/../../build/bin -ldmxSender #include "cpp/include/dmxSenderBridge.h" */ import "C" // Endpoint contains the data of an FTDI endpoint type Endpoint struct { wg sync.WaitGroup info hardware.EndpointInfo // The endpoint basic data dmxSender unsafe.Pointer // The command object for piloting the DMX ouptut } // 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 &Endpoint{ info: info, dmxSender: nil, } } // 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 if p.dmxSender != nil { return errors.Errorf("the DMX device has already been created!") } runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusConnecting) // Create the DMX sender p.dmxSender = C.dmx_create() // Connect the FTDI 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(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) } p.wg.Add(1) go func() { defer p.wg.Done() <-ctx.Done() _ = p.Disconnect(ctx) }() 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 *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!") } // Destroy the dmx sender C.dmx_destroy(p.dmxSender) // Reset the pointer to the endpoint p.dmxSender = nil runtime.EventsEmit(ctx, string(hardware.EndpointStatusUpdated), p.GetInfo(), hardware.EndpointStatusDisconnected) return nil } // Activate activates the FTDI endpoint 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!") } log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("activating FTDI endpoint...") err := C.dmx_activate(p.dmxSender) if err != C.DMX_OK { return errors.Errorf("unable to activate the DMX sender!") } // Test only 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(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 *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!") } log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("deactivating FTDI endpoint...") err := C.dmx_deactivate(p.dmxSender) if err != C.DMX_OK { return errors.Errorf("unable to deactivate the DMX sender!") } 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 *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 *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!") } log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("setting device property on FTDI endpoint...") err := C.dmx_setValue(p.dmxSender, C.uint16_t(channelNumber), C.uint8_t(channelValue)) if err != C.DMX_OK { return errors.Errorf("unable to update the channel value!") } log.Trace().Str("file", "FTDIEndpoint").Str("s/n", p.info.SerialNumber).Msg("device property set on FTDI endpoint successfully") return nil } // GetSettings gets the endpoint settings func (p *Endpoint) GetSettings() map[string]interface{} { return map[string]interface{}{} } // GetInfo gets all the endpoint information func (p *Endpoint) GetInfo() hardware.EndpointInfo { return p.info } // WaitStop wait about the endpoint to close 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!") return nil }