Harden BLE host link and expose latency tools in the dashboard.
Add BLE RTT/stream tests and live tick median in the UI, fix concurrent WebSocket writes and late BATTERY replies, and skip 2M PHY updates that assert under ESP-NOW coexistence. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type bleRttAPIRequest struct {
|
||||
Count *int `json:"count"`
|
||||
Warmup *int `json:"warmup"`
|
||||
SettleS *float64 `json:"settle_s"`
|
||||
}
|
||||
|
||||
type bleStreamAPIRequest struct {
|
||||
ClientID *uint32 `json:"client_id"`
|
||||
PeriodMs *uint32 `json:"period_ms"`
|
||||
Count *int `json:"count"`
|
||||
TolMs *float64 `json:"tol_ms"`
|
||||
TimeoutS *float64 `json:"timeout_s"`
|
||||
}
|
||||
|
||||
func mountBleTestAPI(mux *http.ServeMux, link *managedHost) {
|
||||
mux.HandleFunc("/api/ble-rtt", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
serveBleRtt(w, r, link)
|
||||
})
|
||||
mux.HandleFunc("/api/ble-stream", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
serveBleStreamTest(w, r, link)
|
||||
})
|
||||
}
|
||||
|
||||
func requireBLEHost(w http.ResponseWriter, link *managedHost) bool {
|
||||
if link.Transport() != hostTransportBLE || !link.IsConnected() {
|
||||
writeJSON(w, http.StatusBadRequest, bleRttResult{
|
||||
Error: "BLE host required (connect via BLE, DIP8=1)",
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func serveBleRtt(w http.ResponseWriter, r *http.Request, link *managedHost) {
|
||||
if !requireBLEHost(w, link) {
|
||||
return
|
||||
}
|
||||
var body bleRttAPIRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil && !errors.Is(err, io.EOF) {
|
||||
writeJSON(w, http.StatusBadRequest, bleRttResult{Error: "invalid JSON"})
|
||||
return
|
||||
}
|
||||
count, warmup := 50, 5
|
||||
settle := 0.0
|
||||
if body.Count != nil {
|
||||
count = *body.Count
|
||||
}
|
||||
if body.Warmup != nil {
|
||||
warmup = *body.Warmup
|
||||
}
|
||||
if body.SettleS != nil {
|
||||
settle = *body.SettleS
|
||||
}
|
||||
|
||||
if !link.beginPausePoll() {
|
||||
writeJSON(w, http.StatusConflict, bleRttResult{Error: "host busy"})
|
||||
return
|
||||
}
|
||||
defer link.endPausePoll()
|
||||
|
||||
out := measureBleRtt(link, count, warmup, time.Duration(settle*float64(time.Second)))
|
||||
status := http.StatusOK
|
||||
if !out.Success {
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
writeJSON(w, status, out)
|
||||
}
|
||||
|
||||
func serveBleStreamTest(w http.ResponseWriter, r *http.Request, link *managedHost) {
|
||||
if !requireBLEHost(w, link) {
|
||||
return
|
||||
}
|
||||
var body bleStreamAPIRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil && !errors.Is(err, io.EOF) {
|
||||
writeJSON(w, http.StatusBadRequest, bleStreamResult{Error: "invalid JSON"})
|
||||
return
|
||||
}
|
||||
count := 50
|
||||
periodMs := uint32(16)
|
||||
tolMs := 2.0
|
||||
timeoutS := 15.0
|
||||
var clientID uint32
|
||||
if body.Count != nil {
|
||||
count = *body.Count
|
||||
}
|
||||
if body.PeriodMs != nil {
|
||||
periodMs = *body.PeriodMs
|
||||
}
|
||||
if body.TolMs != nil {
|
||||
tolMs = *body.TolMs
|
||||
}
|
||||
if body.TimeoutS != nil {
|
||||
timeoutS = *body.TimeoutS
|
||||
}
|
||||
if body.ClientID != nil {
|
||||
clientID = *body.ClientID
|
||||
}
|
||||
|
||||
if !link.beginPausePoll() {
|
||||
writeJSON(w, http.StatusConflict, bleStreamResult{Error: "host busy"})
|
||||
return
|
||||
}
|
||||
defer link.endPausePoll()
|
||||
|
||||
out := measureBleStream(link, clientID, periodMs, count, tolMs, timeoutS)
|
||||
status := http.StatusOK
|
||||
if !out.Success {
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
writeJSON(w, status, out)
|
||||
}
|
||||
Reference in New Issue
Block a user