diff --git a/goTool/client_api.go b/goTool/client_api.go index a57fa05..1ec0cb1 100644 --- a/goTool/client_api.go +++ b/goTool/client_api.go @@ -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) { diff --git a/goTool/ota_upload.go b/goTool/ota_upload.go index 8dfcba6..b1f3ba1 100644 --- a/goTool/ota_upload.go +++ b/goTool/ota_upload.go @@ -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) } diff --git a/goTool/serial_link.go b/goTool/serial_link.go index 2cdcd69..1799a46 100644 --- a/goTool/serial_link.go +++ b/goTool/serial_link.go @@ -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) } diff --git a/goTool/serialport.go b/goTool/serialport.go index a05ad08..f2001f4 100644 --- a/goTool/serialport.go +++ b/goTool/serialport.go @@ -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) } diff --git a/goTool/uart/frame.go b/goTool/uart/frame.go index 63c3795..a49e07b 100644 --- a/goTool/uart/frame.go +++ b/goTool/uart/frame.go @@ -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++ {