package main import ( "fmt" "log" "sync" "time" "go.bug.st/serial" uartframe "powerpod/gotool/uart" ) const readTimeout = 3 * time.Second type serialPort struct { port serial.Port mu sync.Mutex quiet bool } func openSerial(portName string, baud int) (*serialPort, error) { mode := &serial.Mode{ BaudRate: baud, DataBits: 8, Parity: serial.NoParity, StopBits: serial.OneStopBit, } port, err := serial.Open(portName, mode) if err != nil { return nil, err } if err := port.SetReadTimeout(readTimeout); err != nil { port.Close() return nil, err } return &serialPort{port: port}, nil } func (s *serialPort) Close() error { return s.port.Close() } func (s *serialPort) exchangePayload(payload []byte, cmdName string) ([]byte, error) { s.mu.Lock() defer s.mu.Unlock() return s.exchangePayloadLocked(payload, cmdName) } func (s *serialPort) exchangePayloadLocked(payload []byte, cmdName string) ([]byte, error) { if len(payload) == 0 { return nil, fmt.Errorf("empty payload") } frame, err := uartframe.EncodeFrame(payload) if err != nil { return nil, fmt.Errorf("encode frame: %w", err) } if !s.quiet { log.Printf("sending %s command (%d bytes): % x", cmdName, len(frame), frame) } if _, err := s.port.Write(frame); err != nil { return nil, fmt.Errorf("write: %w", err) } respPayload, err := uartframe.ReadFrame(s.port, nil) if err != nil { return nil, fmt.Errorf("read response: %w", err) } if !s.quiet { log.Printf("response payload (%d bytes): % x", len(respPayload), respPayload) } if len(respPayload) == 0 { return nil, fmt.Errorf("empty response payload") } return respPayload, nil } func (s *serialPort) exchange(cmdID byte, cmdName string) ([]byte, error) { s.mu.Lock() defer s.mu.Unlock() return s.exchangeLocked(cmdID, cmdName) } func (s *serialPort) exchangeLocked(cmdID byte, cmdName string) ([]byte, error) { frame, err := uartframe.EncodeFrame([]byte{cmdID}) if err != nil { return nil, fmt.Errorf("encode frame: %w", err) } if !s.quiet { log.Printf("sending %s command (%d bytes): % x", cmdName, len(frame), frame) } if _, err := s.port.Write(frame); err != nil { return nil, fmt.Errorf("write: %w", err) } payload, err := uartframe.ReadFrame(s.port, nil) if err != nil { return nil, fmt.Errorf("read response: %w", err) } if !s.quiet { log.Printf("response payload (%d bytes): % x", len(payload), payload) } if len(payload) == 0 { return nil, fmt.Errorf("empty response payload") } if payload[0] != cmdID { return nil, fmt.Errorf("unexpected command id 0x%02x (want 0x%02x)", payload[0], cmdID) } return payload, nil }