Files
dmxconnect/hardware/FTDIFinder.go
2024-12-15 13:45:46 +01:00

118 lines
2.9 KiB
Go

package hardware
import (
"bufio"
"context"
_ "embed"
"fmt"
"os"
"os/exec"
goRuntime "runtime"
"strconv"
"strings"
"time"
)
const (
scanDelay = 4 * time.Second // Waiting delay before scanning the FTDI devices
)
// FTDIFinder represents how the protocol is defined
type FTDIFinder struct {
peripherals map[string]Peripheral
}
// NewFTDIFinder creates a new FTDI finder
func NewFTDIFinder() *FTDIFinder {
return &FTDIFinder{
peripherals: make(map[string]Peripheral),
}
}
// Initialize initializes the FTDI finder
func (f *FTDIFinder) Initialize() error {
if goRuntime.GOOS != "windows" {
return fmt.Errorf("<!> The FTDI finder is not compatible with your platform yet (%s)", goRuntime.GOOS)
}
fmt.Println("FTDI finder initialized")
return nil
}
// GetName returns the name of the finder
func (f *FTDIFinder) GetName() string {
return "FTDI Finder"
}
//go:embed third-party/ftdi/detectFTDI.exe
var findFTDI []byte
// Scan scans the FTDI peripherals
func (f *FTDIFinder) Scan(ctx context.Context) error {
time.Sleep(scanDelay)
// Create a temporary file
tempFile, err := os.CreateTemp("", "findFTDI*.exe")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
// Write the embedded executable to the temp file
if _, err := tempFile.Write(findFTDI); err != nil {
return err
}
tempFile.Close()
ftdiPeripherals := make(map[string]Peripheral)
finder := exec.Command(tempFile.Name())
stdout, err := finder.StdoutPipe()
if err != nil {
return fmt.Errorf("Unable to create the stdout pipe: %s", err)
}
stderr, err := finder.StderrPipe()
if err != nil {
return fmt.Errorf("Unable to create the stderr pipe: %s", err)
}
err = finder.Start()
if err != nil {
return fmt.Errorf("Unable to find FTDI devices: %s", err)
}
scannerErr := bufio.NewScanner(stderr)
for scannerErr.Scan() {
return fmt.Errorf("Unable to find FTDI devices: %s", scannerErr.Text())
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
deviceString := scanner.Text()
// The program output is like '0:1:2' where 0 is the location, 1 is the S/N and 2 is the name
deviceInfo := strings.Split(deviceString, ":")
fmt.Println("FTDI device: " + deviceString)
// Convert the location to an integer
location, err := strconv.Atoi(deviceInfo[0])
if err != nil {
location = -1
}
// Add the peripheral to the temporary list
peripheral, err := NewFTDIPeripheral(deviceInfo[2], deviceInfo[1], location)
if err != nil {
return fmt.Errorf("Unable to create the FTDI peripheral: %v", err)
}
ftdiPeripherals[deviceInfo[1]] = peripheral
}
// Compare with the current peripherals to detect arrivals/removals
removedList, addedList := comparePeripherals(f.peripherals, ftdiPeripherals)
// Emit the events
emitPeripheralsEvents(ctx, removedList, PeripheralRemoval)
emitPeripheralsEvents(ctx, addedList, PeripheralArrival)
// Store the new peripherals list
f.peripherals = ftdiPeripherals
return nil
}