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" } // GetPeripheral gets the peripheral that correspond to the specified ID func (f *FTDIFinder) GetPeripheral(peripheralID string) (Peripheral, bool) { // Return the specified peripheral peripheral := f.peripherals[peripheralID] if peripheral == nil { fmt.Println("Unable to get the peripheral in the finder") return nil, false } fmt.Println("Peripheral found in the finder") return peripheral, true } //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, f.GetName()) 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 }