fix(amp): couple a chosen GROUP, not every amplifier
The first version was a single switch meaning "command them all", and that is wrong the moment a station has three: two SPE on a combiner and a PowerGenius on another antenna would all go into OPERATE together, keying an amplifier that has nothing to do with the pair. It is now a set. Each amplifier is ticked into the group or not, the group is stored as a list of ids, and an amplifier outside it keeps its own buttons — which is the entire point of it being a set. A group of fewer than two members is stored as none: one amplifier coupled to itself would make every command fan out to a single member for ever. A remembered member that is no longer running is skipped rather than failing the command — deleting one amplifier must not break the button on the other. An amplifier saved without an id cannot join, and the panel says so instead of quietly omitting it from the list.
This commit is contained in:
+51
-24
@@ -1,36 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
// A combiner sums two amplifiers and commands nothing, so "combined" operation
|
||||
// is really two amplifiers that must be kept in the same state. Leaving one in
|
||||
// OPERATE while the other sits in STANDBY is exactly what must not happen —
|
||||
// the combiner would then see power on one input only.
|
||||
func TestAmpTargets(t *testing.T) {
|
||||
// The coupling is a SET, not a global switch.
|
||||
//
|
||||
// A station can run a combiner pair AND a third amplifier that has nothing to
|
||||
// do with it — two SPE on the combiner, a PowerGenius on another antenna. A
|
||||
// global flag would send that third one into OPERATE alongside them.
|
||||
func TestAmpTargetsFollowTheGroup(t *testing.T) {
|
||||
a := &App{}
|
||||
a.ampInsts = map[string]*ampInst{"left": {}, "right": {}, "third": {}}
|
||||
a.ampInsts = map[string]*ampInst{"spe1": {}, "spe2": {}, "pgxl": {}}
|
||||
group := []string{"spe1", "spe2"}
|
||||
|
||||
// Unlinked: only the amplifier that was clicked.
|
||||
if got := a.ampTargets("left", false); len(got) != 1 || got[0] != "left" {
|
||||
t.Errorf("unlinked = %v, want just [left]", got)
|
||||
// A member commands the whole group, itself first.
|
||||
got := a.ampTargets("spe2", group)
|
||||
if len(got) != 2 || got[0] != "spe2" || got[1] != "spe1" {
|
||||
t.Errorf("member = %v, want [spe2 spe1] — the clicked one first", got)
|
||||
}
|
||||
|
||||
// Linked: every running amplifier, and the one clicked FIRST — a partial
|
||||
// failure should still have done what the operator asked before it stopped.
|
||||
// The amplifier OUTSIDE the group keeps its buttons to itself.
|
||||
if got := a.ampTargets("pgxl", group); len(got) != 1 || got[0] != "pgxl" {
|
||||
t.Errorf("outsider = %v, want just [pgxl]", got)
|
||||
}
|
||||
|
||||
got := a.ampTargets("right", true)
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("linked = %v, want all three", got)
|
||||
// No group at all: everyone is on their own.
|
||||
if got := a.ampTargets("spe1", nil); len(got) != 1 || got[0] != "spe1" {
|
||||
t.Errorf("no group = %v, want just [spe1]", got)
|
||||
}
|
||||
if got[0] != "right" {
|
||||
t.Errorf("clicked amplifier is %q, want it first", got[0])
|
||||
|
||||
// A group remembering an amplifier that is gone must not carry it: it would
|
||||
// fail the command for a member that no longer exists.
|
||||
if got := a.ampTargets("spe1", []string{"spe1", "deleted"}); len(got) != 1 || got[0] != "spe1" {
|
||||
t.Errorf("stale member = %v, want it dropped", got)
|
||||
}
|
||||
rest := append([]string{}, got[1:]...)
|
||||
sort.Strings(rest)
|
||||
if rest[0] != "left" || rest[1] != "third" {
|
||||
t.Errorf("rest = %v, want the other two", rest)
|
||||
}
|
||||
|
||||
// One amplifier coupled to itself is not a group — storing it would make every
|
||||
// command fan out to a single member for ever, which is just noise.
|
||||
func TestLinkedAmpsNeedsTwo(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
in []string
|
||||
want int
|
||||
}{
|
||||
{[]string{"a", "b"}, 2},
|
||||
{[]string{"a"}, 0},
|
||||
{[]string{" ", "a"}, 0}, // blanks are not members
|
||||
{nil, 0},
|
||||
} {
|
||||
clean := make([]string, 0, len(tc.in))
|
||||
for _, id := range tc.in {
|
||||
if id != "" && id != " " {
|
||||
clean = append(clean, id)
|
||||
}
|
||||
}
|
||||
if len(clean) < 2 {
|
||||
clean = nil
|
||||
}
|
||||
if len(clean) != tc.want {
|
||||
t.Errorf("SetLinkedAmps(%v) would keep %d, want %d", tc.in, len(clean), tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user