package client import ( "fmt" "slices" "strings" "time" "github.com/theater/picomap/lib/msgpack" "go.bug.st/serial" "go.bug.st/serial/enumerator" ) func ListSerial() ([]string, error) { ports, err := enumerator.GetDetailedPortsList() if err != nil { return nil, fmt.Errorf("enumerating ports: %w", err) } type entry struct { name string serial string } var entries []entry for _, p := range ports { if p.IsUSB && p.VID == "2E8A" && strings.HasPrefix(p.Name, "/dev/cu.") { entries = append(entries, entry{p.Name, p.SerialNumber}) } } slices.SortFunc(entries, func(a, b entry) int { return strings.Compare(a.serial, b.serial) }) var result []string for _, e := range entries { result = append(result, e.name) } return result, nil } type serialTransport struct { port serial.Port portName string dec *msgpack.Decoder } func NewSerial(portName string, timeout time.Duration) (*Client, error) { port, err := serial.Open(portName, &serial.Mode{BaudRate: 115200}) if err != nil { return nil, fmt.Errorf("opening %s: %w", portName, err) } t := &serialTransport{port: port, portName: portName, dec: msgpack.NewDecoder(port)} return &Client{transport: t, timeout: timeout}, nil } func (t *serialTransport) Send(data []byte) error { _, err := t.port.Write(data) return err } func (t *serialTransport) SetReadTimeout(timeout time.Duration) { t.port.SetReadTimeout(timeout) } func (t *serialTransport) Recv() ([]byte, string, error) { var raw msgpack.RawMessage if err := t.dec.Decode(&raw); err != nil { return nil, "", err } return []byte(raw), t.portName, nil } func (t *serialTransport) Broadcast() bool { return false } func (t *serialTransport) Close() error { return t.port.Close() }