111 lines
2.5 KiB
Go
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)
|
|
}
|