BCM module diagnostics replace timestamp probes: bringup forces EEE off and runs ECD (re-run on every reset, re-baselining past its blip), 1 Hz SNR margin + corrected counters on panel and console; X520 bench divergences bypassed and tracked in open-questions
This commit is contained in:
@@ -17,9 +17,22 @@ var (
|
||||
uiDim = rgb{0x9a, 0xa2, 0xac}
|
||||
uiCyan = rgb{0x5c, 0xc8, 0xe0}
|
||||
uiGreen = rgb{0x6c, 0xdc, 0x86}
|
||||
uiAmber = rgb{0xe8, 0xc4, 0x52}
|
||||
uiRed = rgb{0xf0, 0x6b, 0x6b}
|
||||
)
|
||||
|
||||
func classColor(c int) rgb {
|
||||
switch c {
|
||||
case clsGood:
|
||||
return uiGreen
|
||||
case clsWarn:
|
||||
return uiAmber
|
||||
case clsBad:
|
||||
return uiRed
|
||||
}
|
||||
return uiDim
|
||||
}
|
||||
|
||||
// Distances between ink, since layout measures a line from the top of a digit
|
||||
// to the baseline rather than across a cell with accent and descender slack in
|
||||
// it. Values carried over from spacing cells will look too small here.
|
||||
@@ -48,7 +61,7 @@ const (
|
||||
chipBorder = 2
|
||||
|
||||
gridCols = 2
|
||||
chipPadY = spaceGroup
|
||||
chipPadY = step * 3
|
||||
chipGap = spaceTight
|
||||
statRowGap = spaceRow
|
||||
)
|
||||
@@ -92,9 +105,9 @@ func newDisplay() (*display, error) {
|
||||
bold bool
|
||||
size float64
|
||||
}{
|
||||
{&d.big, true, 40},
|
||||
{&d.grid, false, 34},
|
||||
{&d.gridB, true, 34},
|
||||
{&d.big, true, 36},
|
||||
{&d.grid, false, 30},
|
||||
{&d.gridB, true, 30},
|
||||
} {
|
||||
face, err := loadFace(spec.bold, spec.size)
|
||||
if err != nil {
|
||||
@@ -108,22 +121,29 @@ func newDisplay() (*display, error) {
|
||||
return nil, fmt.Errorf("grid faces disagree on cell width: %d vs %d",
|
||||
d.grid.cellW, d.gridB.cellW)
|
||||
}
|
||||
now := []int{d.statsH(d.big, 2), d.chipsH()}
|
||||
since := []int{d.statsH(d.gridB, 4), d.countsH(), btnH}
|
||||
if err := d.layout(fb.w, fb.h); err != nil {
|
||||
fb.close()
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *display) layout(w, h int) error {
|
||||
now := []int{d.statsH(d.big, 3), d.chipsH()}
|
||||
since := []int{d.statsH(d.gridB, 4), d.statsH(d.gridB, 1), d.countsH(), btnH}
|
||||
|
||||
// One gap for both panels, and vertically the frame is the border alone.
|
||||
// Insetting by uiPad as well would add it to the gaps at a panel's ends but
|
||||
// not to the ones between blocks, which is not equal spacing however evenly
|
||||
// the remainder is divided.
|
||||
gaps := len(now) + len(since) + 2
|
||||
spare := fb.h - 2*uiMargin - blockGap - 4*uiBorder - sum(now) - sum(since)
|
||||
spare := h - 2*uiMargin - blockGap - 4*uiBorder - sum(now) - sum(since)
|
||||
if spare < 0 {
|
||||
fb.close()
|
||||
return nil, fmt.Errorf("panel content is %dpx taller than the screen", -spare)
|
||||
return fmt.Errorf("panel content is %dpx taller than the screen", -spare)
|
||||
}
|
||||
gap := spare / gaps
|
||||
|
||||
inner := fb.w - 2*uiMargin
|
||||
inner := w - 2*uiMargin
|
||||
nowH := 2*uiBorder + sum(now) + (len(now)+1)*gap
|
||||
sinceH := 2*uiBorder + sum(since) + (len(since)+1)*gap
|
||||
d.nowPanel = rect{uiMargin, uiMargin, inner, nowH}
|
||||
@@ -137,8 +157,8 @@ func newDisplay() (*display, error) {
|
||||
w: btnW,
|
||||
h: btnH,
|
||||
}
|
||||
d.versionSpot = rect{fb.w - versionSpotSide, 0, versionSpotSide, versionSpotSide}
|
||||
return d, nil
|
||||
d.versionSpot = rect{w - versionSpotSide, 0, versionSpotSide, versionSpotSide}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sum(hs []int) int {
|
||||
@@ -270,10 +290,10 @@ func (d *display) statsH(vf *textFace, n int) int {
|
||||
}
|
||||
|
||||
// A last row that does not fill the grid is centred.
|
||||
func gridCell(i, n, x, w int) (cx, cw int) {
|
||||
cw = (w - (gridCols-1)*chipGap) / gridCols
|
||||
inRow := min(n-(i/gridCols)*gridCols, gridCols)
|
||||
cx = x + (w-(inRow*cw+(inRow-1)*chipGap))/2 + (i%gridCols)*(cw+chipGap)
|
||||
func gridCell(i, n, cols, x, w int) (cx, cw int) {
|
||||
cw = (w - (cols-1)*chipGap) / cols
|
||||
inRow := min(n-(i/cols)*cols, cols)
|
||||
cx = x + (w-(inRow*cw+(inRow-1)*chipGap))/2 + (i%cols)*(cw+chipGap)
|
||||
return cx, cw
|
||||
}
|
||||
|
||||
@@ -284,7 +304,7 @@ func (d *display) stats(vf *textFace, x, w, y int, cells []statCell) int {
|
||||
if c.value == "" {
|
||||
continue
|
||||
}
|
||||
cx, cw := gridCell(i, len(cells), x, w)
|
||||
cx, cw := gridCell(i, len(cells), gridCols, x, w)
|
||||
cy := y + (i/gridCols)*(d.statPairH(vf)+statRowGap)
|
||||
ly := d.centerIn(vf, cx, cw, cy, c.value, c.col)
|
||||
d.centerIn(d.grid, cx, cw, ly, c.label, uiDim)
|
||||
@@ -306,9 +326,10 @@ func (d *display) chipH() int { return d.grid.lineH + 2*chipPadY }
|
||||
|
||||
func (d *display) countChipH() int { return d.chipH() + d.gridB.lineH + pairGap }
|
||||
|
||||
// The error chips plus the noise cable's, which shares their row grid.
|
||||
// The error chips plus corrected and the noise cable's, which share their
|
||||
// row grid.
|
||||
func (d *display) chipsH() int {
|
||||
return gridRows(len(errRows)+1)*(d.chipH()+chipGap) - chipGap
|
||||
return gridRows(len(errRows)+2)*(d.chipH()+chipGap) - chipGap
|
||||
}
|
||||
|
||||
func (d *display) countsH() int {
|
||||
@@ -317,9 +338,9 @@ func (d *display) countsH() int {
|
||||
|
||||
// Outlined by drawing the border colour and sinking a smaller well of
|
||||
// background into it, so both curves get the same antialiasing.
|
||||
func (d *display) chipAt(i, n, x, w, y, h int, c rgb) (int, int, int) {
|
||||
cx, cw := gridCell(i, n, x, w)
|
||||
cy := y + (i/gridCols)*(h+chipGap)
|
||||
func (d *display) chipAt(i, n, cols, x, w, y, h int, c rgb) (int, int, int) {
|
||||
cx, cw := gridCell(i, n, cols, x, w)
|
||||
cy := y + (i/cols)*(h+chipGap)
|
||||
|
||||
d.fb.roundRect(cx, cy, cw, h, chipRadius, c)
|
||||
d.fb.roundRect(cx+chipBorder, cy+chipBorder,
|
||||
@@ -330,15 +351,22 @@ func (d *display) chipAt(i, n, x, w, y, h int, c rgb) (int, int, int) {
|
||||
// Whether rather than how many: over a window this short a count changes faster
|
||||
// than it can be read. The noise chip rides along at the end, presence rather
|
||||
// than health: red is the cable missing, not the cable failing.
|
||||
func (d *display) errChips(x, w, y int, e errs, noiseMissing uint64) int {
|
||||
n := len(errRows) + 1
|
||||
func (d *display) errChips(x, w, y int, e errs, recentCorrected, noiseMissing uint64) int {
|
||||
n := len(errRows) + 2
|
||||
for i, r := range errRows {
|
||||
c := errColor(r.get(e))
|
||||
cx, cw, cy := d.chipAt(i, n, x, w, y, d.chipH(), c)
|
||||
cx, cw, cy := d.chipAt(i, n, gridCols, x, w, y, d.chipH(), c)
|
||||
d.centerIn(d.grid, cx, cw, cy+chipPadY, r.label, c)
|
||||
}
|
||||
c := errColor(noiseMissing)
|
||||
cx, cw, cy := d.chipAt(len(errRows), n, x, w, y, d.chipH(), c)
|
||||
// Amber rather than red: the phy absorbed these before they cost a frame.
|
||||
c := uiGreen
|
||||
if recentCorrected > 0 {
|
||||
c = uiAmber
|
||||
}
|
||||
cx, cw, cy := d.chipAt(len(errRows), n, gridCols, x, w, y, d.chipH(), c)
|
||||
d.centerIn(d.grid, cx, cw, cy+chipPadY, "corrected", c)
|
||||
c = errColor(noiseMissing)
|
||||
cx, cw, cy = d.chipAt(len(errRows)+1, n, gridCols, x, w, y, d.chipH(), c)
|
||||
d.centerIn(d.grid, cx, cw, cy+chipPadY, "noise", c)
|
||||
return y + d.chipsH()
|
||||
}
|
||||
@@ -347,16 +375,26 @@ func (d *display) errCounts(x, w, y int, e errs) int {
|
||||
for i, r := range errRows {
|
||||
n := r.get(e)
|
||||
c := errColor(n)
|
||||
cx, cw, cy := d.chipAt(i, len(errRows), x, w, y, d.countChipH(), c)
|
||||
cx, cw, cy := d.chipAt(i, len(errRows), gridCols, x, w, y, d.countChipH(), c)
|
||||
ty := d.centerIn(d.gridB, cx, cw, cy+chipPadY, scaleCount(n), c)
|
||||
d.centerIn(d.grid, cx, cw, ty, r.label, c)
|
||||
}
|
||||
return y + d.countsH()
|
||||
}
|
||||
|
||||
func (d *display) panel(p rect, e errs) (int, int) {
|
||||
// White when the wiring is clean: a measurement rather than a judgment. The
|
||||
// colours are reserved for the diag finding a fault or a swapped pair.
|
||||
func metresStat(phy phyDisplay) statCell {
|
||||
col := uiFg
|
||||
if phy.metresClass != clsGood {
|
||||
col = classColor(phy.metresClass)
|
||||
}
|
||||
return statCell{phy.metres, "m", col}
|
||||
}
|
||||
|
||||
func (d *display) panel(p rect, bad bool) (int, int) {
|
||||
fill, edge := uiOKFill, uiOKEdge
|
||||
if e.total() > 0 {
|
||||
if bad {
|
||||
fill, edge = uiErrFil, uiErrEdg
|
||||
}
|
||||
d.fb.rect(p.x, p.y, p.w, p.h, edge)
|
||||
@@ -374,25 +412,45 @@ func errColor(n uint64) rgb {
|
||||
return uiRed
|
||||
}
|
||||
|
||||
func (d *display) render(v view, elapsed time.Duration, cable string, noiseMissing uint64) error {
|
||||
func snrStat(phy phyDisplay) statCell {
|
||||
if !phy.haveSNR {
|
||||
return statCell{"-", "db margin", uiDim}
|
||||
}
|
||||
return statCell{fmt.Sprintf("%+.1f", phy.worstMargin), "db margin",
|
||||
classColor(snrClass(phy.worstMargin))}
|
||||
}
|
||||
|
||||
// Errors the phy absorbed before they could cost a frame: amber rather than
|
||||
// red, the cable being stressed rather than failing.
|
||||
func correctedStat(v uint64) statCell {
|
||||
col := uiGreen
|
||||
if v > 0 {
|
||||
col = uiAmber
|
||||
}
|
||||
return statCell{scaleCount(v), "corrected", col}
|
||||
}
|
||||
|
||||
func (d *display) render(v view, elapsed time.Duration, phy phyDisplay, noiseMissing uint64) error {
|
||||
fb := d.fb
|
||||
fb.fill(uiBg)
|
||||
|
||||
x, w := d.panel(d.nowPanel, v.window)
|
||||
x, w := d.panel(d.nowPanel, v.window.total() > 0)
|
||||
d.stats(d.big, x, w, d.nowYs[0], []statCell{
|
||||
{scaleSI(v.rxGbps * 1e9), "bits/s", uiFg},
|
||||
{scaleSI(v.rxPPS), "packets/s", uiFg},
|
||||
snrStat(phy),
|
||||
})
|
||||
d.errChips(x, w, d.nowYs[1], v.window, noiseMissing)
|
||||
d.errChips(x, w, d.nowYs[1], v.window, phy.recent, noiseMissing)
|
||||
|
||||
x, w = d.panel(d.sincePanel, v.since)
|
||||
x, w = d.panel(d.sincePanel, v.since.total() > 0 || phy.metresClass == clsBad)
|
||||
d.stats(d.gridB, x, w, d.sinceYs[0], []statCell{
|
||||
{scaleTime(elapsed), "elapsed", uiFg},
|
||||
{scaleCount(v.rxFrames), "packets", uiFg},
|
||||
{scaleCount(v.rxBytes), "bytes", uiFg},
|
||||
{cable, "m", uiFg},
|
||||
correctedStat(phy.corrected),
|
||||
})
|
||||
d.errCounts(x, w, d.sinceYs[1], v.since)
|
||||
d.stats(d.gridB, x, w, d.sinceYs[1], []statCell{metresStat(phy)})
|
||||
d.errCounts(x, w, d.sinceYs[2], v.since)
|
||||
|
||||
d.drawResetButton()
|
||||
return fb.flush()
|
||||
|
||||
Reference in New Issue
Block a user