Skip to content

Commit ac08aa1

Browse files
authored
fix(channelplan): score current DFS channel and clarify empty state (#518) (#520)
Co-authored-by: gnacho <hnacho@proton.me>
1 parent 99f7324 commit ac08aa1

5 files changed

Lines changed: 51 additions & 2 deletions

File tree

‎app/node_modules‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/home/nacho/temp/opencode-work/netpulse-511/app/node_modules

‎app/public/locales/en/translation.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1400,7 +1400,7 @@
14001400
"currentChannel": "Current channel",
14011401
"recommendedChannel": "Recommended channel",
14021402
"neighbors": "Neighbor APs",
1403-
"noScans": "No recent scans. Enable the agent on a router to collect neighbors.",
1403+
"noScans": "No recent scans for this router. Routers running NetGrip (gateway, APs managed by its panel) do not scan neighbors; routers with the NetPulse agent do. Pick another router from the list (e.g. rt2 or rt4) to see its plan.",
14041404
"noRadios": "This router does not report WiFi radios.",
14051405
"apply": "Apply channel",
14061406
"gateway": " (Gateway)",

‎app/public/locales/es/translation.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1400,7 +1400,7 @@
14001400
"currentChannel": "Canal actual",
14011401
"recommendedChannel": "Canal recomendado",
14021402
"neighbors": "APs vecinos",
1403-
"noScans": "No hay scans recientes. Activa el agente en un router para recoger vecinos.",
1403+
"noScans": "Sin escaneos recientes para este router. Los routers que corren NetGrip (gateway, APs gestionados por su panel) no escanean vecinos; los que llevan el agente NetPulse sí. Selecciona otro router de la lista (p. ej. rt2 o rt4) para ver su plan.",
14041404
"noRadios": "Este dispositivo no reporta radios WiFi.",
14051405
"apply": "Aplicar canal",
14061406
"gateway": " (Puerta de enlace)",

‎server-go/internal/channelplan/store.go‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ func (s *Store) Recommend(routerID string, radios []probe.Radio, within time.Dur
165165
bestCh = ch
166166
}
167167
}
168+
// #518: el canal ACTUAL puede ser DFS (52/100/112/116...) y no estar en
169+
// candidateChannels (solo no-DFS para recomendar). Su score es
170+
// informativo ("cuánto ruido tengo ahora") y debe calcularse igual,
171+
// no quedarse en MaxInt y pintar 9223372036854775807 en la UI.
172+
if currentScore == math.MaxInt {
173+
currentScore = channelScore(byBand[band], r.Channel)
174+
}
168175
if bestCh != 0 && bestScore != math.MaxInt {
169176
rec.Recommended = bestCh
170177
rec.CurrentScore = currentScore

‎server-go/internal/channelplan/store_test.go‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,44 @@ func TestRecommendPasaSeccion(t *testing.T) {
188188
t.Errorf("sin sección el iface debe caer al placeholder: %+v", *nosec)
189189
}
190190
}
191+
192+
func TestRecommendCurrentDfsChannelScoreNotMaxInt(t *testing.T) {
193+
// #518: si el canal ACTUAL es DFS (112) y no está en candidateChannels
194+
// (solo no-DFS), su score debe calcularse igual (es informativo) y no
195+
// quedarse en MaxInt (la UI pintaba 9223372036854775807).
196+
d, err := db.Open(t.TempDir())
197+
if err != nil {
198+
t.Fatalf("db: %v", err)
199+
}
200+
defer d.Close()
201+
202+
st := channelplan.NewStore(d.DB)
203+
now := time.Now().Unix()
204+
scans := []probe.ScanResult{
205+
{Iface: "wlan1", BSSID: "aa:bb:cc:dd:ee:01", SSID: "V", Channel: 112, Freq: 5560, Signal: -60},
206+
{Iface: "wlan1", BSSID: "aa:bb:cc:dd:ee:02", SSID: "W", Channel: 44, Freq: 5220, Signal: -55},
207+
{Iface: "wlan1", BSSID: "aa:bb:cc:dd:ee:03", SSID: "X", Channel: 116, Freq: 5580, Signal: -70},
208+
}
209+
if err := st.SaveScan("rt1", now, scans); err != nil {
210+
t.Fatalf("save: %v", err)
211+
}
212+
213+
radios := []probe.Radio{{Name: "5 GHz", Channel: 112, WidthMhz: 80}}
214+
recs, err := st.Recommend("rt1", radios, time.Hour)
215+
if err != nil {
216+
t.Fatalf("recommend: %v", err)
217+
}
218+
if len(recs) != 1 {
219+
t.Fatalf("esperaba 1 radio, got %d", len(recs))
220+
}
221+
rec := recs[0]
222+
if rec.CurrentScore > 1_000_000_000_000 {
223+
t.Fatalf("currentScore no debe ser MaxInt (canal DFS): %d", rec.CurrentScore)
224+
}
225+
if rec.Recommended == 112 {
226+
t.Errorf("no debería recomendar el DFS 112 ocupado: %+v", rec)
227+
}
228+
if rec.Recommended != 36 {
229+
t.Errorf("debería recomendar el 36 libre (no-DFS), got %d", rec.Recommended)
230+
}
231+
}

0 commit comments

Comments
 (0)