Fix UART hang after master restart from the dashboard.
ReadFrame now returns on serial timeout instead of looping forever, and serve closes and reopens the port after a master reboot so polling and API commands can resume. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
e4ce18edd8
commit
ab1844ac32
@ -417,9 +417,16 @@ func (m *managedSerial) FindMe(clientID uint32) error {
|
||||
}
|
||||
|
||||
func (m *managedSerial) Restart(clientID uint32) error {
|
||||
return m.withPort(func(sp *serialPort) error {
|
||||
err := m.withPort(func(sp *serialPort) error {
|
||||
return runRestartClient(sp, clientID)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if clientID == 0 {
|
||||
m.recoverAfterMasterRestart()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *serialPort) ledRingProgress(req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) {
|
||||
|
||||
@ -416,7 +416,7 @@ func readUartMessageUntil(sp *serialPort, deadline time.Time, want pb.MessageTyp
|
||||
if err := sp.port.SetReadTimeout(wait); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err := uartframe.ReadFrame(sp.port, nil)
|
||||
payload, err := uartframe.ReadFrame(sp.port, nil, wait)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -536,7 +536,7 @@ func waitOtaStatus(sp *serialPort, want uint32, timeout time.Duration, onPrepari
|
||||
if err := sp.port.SetReadTimeout(readWait); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err := uartframe.ReadFrame(sp.port, nil)
|
||||
payload, err := uartframe.ReadFrame(sp.port, nil, readWait)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -562,7 +562,7 @@ func waitOtaStatus(sp *serialPort, want uint32, timeout time.Duration, onPrepari
|
||||
}
|
||||
|
||||
func readOtaStatus(sp *serialPort) (*pb.OtaStatusPayload, error) {
|
||||
payload, err := uartframe.ReadFrame(sp.port, nil)
|
||||
payload, err := uartframe.ReadFrame(sp.port, nil, readTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
|
||||
@ -105,6 +105,26 @@ func (m *managedSerial) withPortLocked(poll bool, fn func(*serialPort) error) er
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *managedSerial) recoverAfterMasterRestart() {
|
||||
const bootWait = 6 * time.Second
|
||||
|
||||
m.mu.Lock()
|
||||
m.closeLocked()
|
||||
m.mu.Unlock()
|
||||
|
||||
log.Printf("UART: master restart — waiting %s for boot", bootWait)
|
||||
time.Sleep(bootWait)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if err := m.openLocked(); err != nil {
|
||||
log.Printf("UART reconnect after master restart: %v", err)
|
||||
return
|
||||
}
|
||||
flushSerialInput(m.sp)
|
||||
log.Printf("UART %s ready after master restart", m.portName)
|
||||
}
|
||||
|
||||
func (m *managedSerial) exchangePayload(payload []byte, cmdName string) ([]byte, error) {
|
||||
return m.exchangePayloadVia(m.withPort, payload, cmdName)
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ func (s *serialPort) exchangePayloadLocked(payload []byte, cmdName string, timeo
|
||||
}
|
||||
defer func() { _ = s.port.SetReadTimeout(readTimeout) }()
|
||||
|
||||
respPayload, err := uartframe.ReadFrame(s.port, nil)
|
||||
respPayload, err := uartframe.ReadFrame(s.port, nil, timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
@ -113,7 +113,7 @@ func (s *serialPort) exchangeLocked(cmdID byte, cmdName string) ([]byte, error)
|
||||
return nil, fmt.Errorf("write: %w", err)
|
||||
}
|
||||
|
||||
payload, err := uartframe.ReadFrame(s.port, nil)
|
||||
payload, err := uartframe.ReadFrame(s.port, nil, readTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -98,13 +99,22 @@ func (p *Parser) Feed(b byte) (payload []byte, ok bool, err error) {
|
||||
}
|
||||
|
||||
// ReadFrame reads bytes from r until one full frame is parsed or an error occurs.
|
||||
func ReadFrame(r io.Reader, buf []byte) ([]byte, error) {
|
||||
// maxWait bounds total wait time; zero means no limit (serial read timeouts retry forever).
|
||||
func ReadFrame(r io.Reader, buf []byte, maxWait time.Duration) ([]byte, error) {
|
||||
if buf == nil {
|
||||
buf = make([]byte, 256)
|
||||
}
|
||||
parser := NewParser()
|
||||
|
||||
var deadline time.Time
|
||||
if maxWait > 0 {
|
||||
deadline = time.Now().Add(maxWait)
|
||||
}
|
||||
|
||||
for {
|
||||
if !deadline.IsZero() && !time.Now().Before(deadline) {
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
n, err := r.Read(buf)
|
||||
if n > 0 {
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user