// Package localuart находит и открывает USB-UART адаптер напрямую, без // демона — для standalone-версии приложения (internal/localctl). package localuart import ( "fmt" "sort" "strings" "go.bug.st/serial/enumerator" ) // knownVID — VID распространённых чипов USB-UART мостов. Порты с таким VID // считаются вероятным переходником "UART to USB" и предлагаются первыми. var knownVID = map[string]bool{ "0403": true, // FTDI "1A86": true, // WCH CH340 / CH341 "10C4": true, // Silicon Labs CP210x "067B": true, // Prolific PL2303 } // Candidate — один найденный USB-serial порт. type Candidate struct { Name string VID string PID string } // ListCandidates возвращает USB-serial порты, обнаруженные в системе; // порты с известным VID переходника идут первыми. func ListCandidates() ([]Candidate, error) { ports, err := enumerator.GetDetailedPortsList() if err != nil { return nil, err } var known, other []Candidate for _, p := range ports { if !p.IsUSB { continue } c := Candidate{ Name: p.Name, VID: strings.ToUpper(p.VID), PID: strings.ToUpper(p.PID), } if knownVID[c.VID] { known = append(known, c) } else { other = append(other, c) } } sort.Slice(known, func(i, j int) bool { return known[i].Name < known[j].Name }) sort.Slice(other, func(i, j int) bool { return other[i].Name < other[j].Name }) return append(known, other...), nil } // PickPort возвращает наиболее вероятный порт UART-to-USB переходника. func PickPort() (string, error) { candidates, err := ListCandidates() if err != nil { return "", fmt.Errorf("не удалось получить список портов: %w", err) } if len(candidates) == 0 { return "", fmt.Errorf("USB-UART адаптер не найден") } return candidates[0].Name, nil }