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 {
|
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)
|
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) {
|
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 {
|
if err := sp.port.SetReadTimeout(wait); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload, err := uartframe.ReadFrame(sp.port, nil)
|
payload, err := uartframe.ReadFrame(sp.port, nil, wait)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err := sp.port.SetReadTimeout(readWait); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload, err := uartframe.ReadFrame(sp.port, nil)
|
payload, err := uartframe.ReadFrame(sp.port, nil, readWait)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -562,7 +562,7 @@ func waitOtaStatus(sp *serialPort, want uint32, timeout time.Duration, onPrepari
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readOtaStatus(sp *serialPort) (*pb.OtaStatusPayload, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read response: %w", err)
|
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
|
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) {
|
func (m *managedSerial) exchangePayload(payload []byte, cmdName string) ([]byte, error) {
|
||||||
return m.exchangePayloadVia(m.withPort, payload, cmdName)
|
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) }()
|
defer func() { _ = s.port.SetReadTimeout(readTimeout) }()
|
||||||
|
|
||||||
respPayload, err := uartframe.ReadFrame(s.port, nil)
|
respPayload, err := uartframe.ReadFrame(s.port, nil, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read response: %w", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read response: %w", err)
|
return nil, fmt.Errorf("read response: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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.
|
// 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 {
|
if buf == nil {
|
||||||
buf = make([]byte, 256)
|
buf = make([]byte, 256)
|
||||||
}
|
}
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
|
|
||||||
|
var deadline time.Time
|
||||||
|
if maxWait > 0 {
|
||||||
|
deadline = time.Now().Add(maxWait)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
if !deadline.IsZero() && !time.Now().Before(deadline) {
|
||||||
|
return nil, ErrTimeout
|
||||||
|
}
|
||||||
n, err := r.Read(buf)
|
n, err := r.Read(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user