Cable verdict folded into one console cell (length green, fault name red, xover amber, fail; diag rule lines and counters-reset line gone), reset fires at the press not diag completion, and the measure's blip is suppressed at every counter source instead of counted-hidden-reverted: worker error adds, loss-window write-offs (quiet slides state without charging; leaving the quiet era presumes in-window holes delivered so the gap's tail is never charged late), NIC poller tracks raw without accumulating, socket drops discarded, modules un-primed by the measure itself; display hold deleted so pre-existing errors no longer blink out during a measure

This commit is contained in:
flamingcow
2026-08-17 11:25:31 -07:00
parent 3d7105073e
commit e3c3f945c5
10 changed files with 220 additions and 188 deletions
+35 -65
View File
@@ -769,6 +769,15 @@ func (m *phyModule) reset() {
m.mu.Unlock()
}
// Latches drained after a measure belong to its blip: un-priming makes the
// next poll an origin only, without discarding the pre-measure totals.
func (m *phyModule) forgive() {
m.mu.Lock()
m.primed = false
m.recentDelta = 0
m.mu.Unlock()
}
type phyModView struct {
fresh bool
link bool
@@ -802,6 +811,7 @@ type cableInfo struct {
ecd ecdResult
maps [2]byte
haveMaps [2]bool
failed bool
}
func (c cableInfo) metresString() string {
@@ -845,8 +855,6 @@ type phyDisplay struct {
metresClass int
}
func pairLetter(i int) string { return string(rune('A' + i)) }
// Each end resolves MDI on its own, so a swap at either known end counts; an
// end with no readable map abstains.
func pairSwapped(i int, c cableInfo) bool {
@@ -858,32 +866,35 @@ func pairSwapped(i int, c cableInfo) bool {
return false
}
// The whole cable verdict in one cell: the length when healthy, the first
// faulted pair's verdict when not, "xover" for a pair swap, "fail" for a diag
// that never delivered.
func cableSummary(cable cableInfo, measuring bool) (string, int) {
if measuring {
return "...", clsNone
return "-", clsNone
}
anyData, anyFault, anySwap := false, false, false
if cable.failed {
return "fail", clsBad
}
anyData, anySwap := false, false
for i, v := range cable.ecd.verdicts {
if v != 0 {
anyData = true
}
if v != 0 && v != pairOK {
anyFault = true
return pairVerdicts[v], clsBad
}
if pairSwapped(i, cable) {
anySwap = true
}
}
s := cable.metresString()
switch {
case !anyData:
return "-", clsNone
case anyFault:
return s, clsBad
case anySwap:
return s, clsWarn
return "xover", clsWarn
}
return s, clsGood
return cable.metresString(), clsGood
}
// The margin is the worst pair across the ends that measure SNR (the Wiitek's
@@ -977,6 +988,7 @@ func measureCable(mods []*phyModule, waitRelink bool, done *atomic.Bool) (cableI
}
defer func() {
for _, m := range mods {
m.forgive()
m.busy.Store(false)
}
}()
@@ -1010,83 +1022,45 @@ func measureCable(mods []*phyModule, waitRelink bool, done *atomic.Bool) (cableI
}
type cableDiag struct {
mods []*phyModule
completed chan error
mods []*phyModule
mu sync.Mutex
info cableInfo
running bool
// While set, every failure counter is suppressed at its source: the
// measure's own link blip is never counted anywhere, rather than counted,
// hidden and reverted.
measuring atomic.Bool
mu sync.Mutex
info cableInfo
}
func newCableDiag(mods []*phyModule, info cableInfo) *cableDiag {
return &cableDiag{mods: mods, completed: make(chan error, 1), info: info}
return &cableDiag{mods: mods, info: info}
}
func (c *cableDiag) snapshot() (cableInfo, bool) {
c.mu.Lock()
defer c.mu.Unlock()
return c.info, c.running
return c.info, c.measuring.Load()
}
func (c *cableDiag) kick(done *atomic.Bool) bool {
c.mu.Lock()
if c.running {
c.mu.Unlock()
if !c.measuring.CompareAndSwap(false, true) {
return false
}
c.running = true
c.mu.Unlock()
go func() {
defer holdPanic()
info, _, err := measureCable(c.mods, true, done)
if err != nil {
info = cableInfo{}
info = cableInfo{failed: true}
}
c.mu.Lock()
c.info = info
c.running = false
c.mu.Unlock()
select {
case c.completed <- err:
default:
}
c.measuring.Store(false)
}()
return true
}
func mapString(m byte, have bool) string {
if !have {
return "unread"
}
if m == pairIdentityMap {
return "straight"
}
out := make([]string, 4)
for i := range out {
out[i] = pairLetter(int(m>>(2*i)) & 3)
}
return "swapped to " + strings.Join(out, "")
}
func verdictString(r ecdResult) string {
bad := []string{}
for i, v := range r.verdicts {
if v != pairOK {
s, ok := pairVerdicts[v]
if !ok {
s = fmt.Sprintf("%d", v)
}
bad = append(bad, fmt.Sprintf("%s %s at %dm", pairLetter(i), s, r.metres[i]))
}
}
if len(bad) > 0 {
return strings.Join(bad, ", ")
}
return fmt.Sprintf("all pairs ok, %d/%d/%d/%d m",
r.metres[0], r.metres[1], r.metres[2], r.metres[3])
}
func openModules(names [2]string) ([]*phyModule, [2]string, error) {
mods := make([]*phyModule, 0, 2)
var idents [2]string
@@ -1218,7 +1192,3 @@ func moduleChecks(mods []*phyModule, names [2]string) []checkResult {
return out
}
func cableLine(c cableInfo) string {
return fmt.Sprintf("%s; map %s / %s",
verdictString(c.ecd), mapString(c.maps[0], c.haveMaps[0]), mapString(c.maps[1], c.haveMaps[1]))
}