Files
car-radio/car-radio/fusion/fdim/display.go
T
2026-10-27 21:13:36 -05:00

111 lines
2.5 KiB
Go

// Package fdim is the front display (FDIM): a generic Display API, a driver
// that turns core frames into minimal display updates, and an emulator link.
package fdim
import (
"errors"
"fmt"
"sync"
)
// Display is the generic FDIM API. Implementations: Memory (tests), NetDisplay
// (talks to the FDIM emulator terminal), or a real CAN driver.
//
// Rows are exactly 20 characters wide; the driver pads and maps characters.
type Display interface {
// DisplayText replaces both text rows.
DisplayText(row1, row2 string) error
// SetSourceLabel sets the source label box (e.g. "SYNC", "AUX"; "" clears).
SetSourceLabel(label string) error
// SetClock sets the info-area clock text; "" hides the clock.
SetClock(text string) error
// KeepAlive sends whatever the FDIM needs to stay awake.
KeepAlive() error
Close() error
}
// ErrDown is returned by fake displays that are told to fail.
var ErrDown = errors.New("fdim: display down")
// Memory is an in-memory Display for tests.
type Memory struct {
mu sync.Mutex
Row1, Row2 string
Label string
Clock string
Calls []string
KeepAlives int
Failing bool
}
func (m *Memory) rec(format string, a ...any) error {
m.Calls = append(m.Calls, fmt.Sprintf(format, a...))
if m.Failing {
return ErrDown
}
return nil
}
func (m *Memory) DisplayText(r1, r2 string) error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.rec("text %q %q", r1, r2); err != nil {
return err
}
m.Row1, m.Row2 = r1, r2
return nil
}
func (m *Memory) SetSourceLabel(l string) error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.rec("label %q", l); err != nil {
return err
}
m.Label = l
return nil
}
func (m *Memory) SetClock(t string) error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.rec("clock %q", t); err != nil {
return err
}
m.Clock = t
return nil
}
func (m *Memory) KeepAlive() error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.rec("keepalive"); err != nil {
return err
}
m.KeepAlives++
return nil
}
func (m *Memory) Close() error { return nil }
// Snapshot returns the current contents safely.
func (m *Memory) Snapshot() (row1, row2, label, clock string) {
m.mu.Lock()
defer m.mu.Unlock()
return m.Row1, m.Row2, m.Label, m.Clock
}
// SetFailing makes every call fail (or succeed again).
func (m *Memory) SetFailing(b bool) {
m.mu.Lock()
m.Failing = b
m.mu.Unlock()
}
// CallCount returns the number of calls recorded so far.
func (m *Memory) CallCount() int {
m.mu.Lock()
defer m.mu.Unlock()
return len(m.Calls)
}