| @@ -59,6 +59,23 @@ type gpuStatus struct { | |||||
| Error string `json:"error"` | Error string `json:"error"` | ||||
| } | } | ||||
| type signalSnapshot struct { | |||||
| mu sync.RWMutex | |||||
| signals []detector.Signal | |||||
| } | |||||
| func (s *signalSnapshot) set(sig []detector.Signal) { | |||||
| s.mu.Lock() | |||||
| defer s.mu.Unlock() | |||||
| s.signals = append([]detector.Signal(nil), sig...) | |||||
| } | |||||
| func (s *signalSnapshot) get() []detector.Signal { | |||||
| s.mu.RLock() | |||||
| defer s.mu.RUnlock() | |||||
| return append([]detector.Signal(nil), s.signals...) | |||||
| } | |||||
| func (g *gpuStatus) set(active bool, err error) { | func (g *gpuStatus) set(active bool, err error) { | ||||
| g.mu.Lock() | g.mu.Lock() | ||||
| defer g.mu.Unlock() | defer g.mu.Unlock() | ||||
| @@ -310,7 +327,9 @@ func main() { | |||||
| RingSeconds: cfg.Recorder.RingSeconds, | RingSeconds: cfg.Recorder.RingSeconds, | ||||
| }, cfg.CenterHz, decodeMap) | }, cfg.CenterHz, decodeMap) | ||||
| go runDSP(ctx, srcMgr, cfg, det, window, h, eventFile, eventMu, dspUpdates, gpuState, recMgr) | |||||
| sigSnap := &signalSnapshot{} | |||||
| go runDSP(ctx, srcMgr, cfg, det, window, h, eventFile, eventMu, dspUpdates, gpuState, recMgr, sigSnap) | |||||
| upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { | upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { | ||||
| origin := r.Header.Get("Origin") | origin := r.Header.Get("Origin") | ||||
| @@ -488,6 +507,15 @@ func main() { | |||||
| _ = json.NewEncoder(w).Encode(evs) | _ = json.NewEncoder(w).Encode(evs) | ||||
| }) | }) | ||||
| http.HandleFunc("/api/signals", func(w http.ResponseWriter, r *http.Request) { | |||||
| w.Header().Set("Content-Type", "application/json") | |||||
| if sigSnap == nil { | |||||
| _ = json.NewEncoder(w).Encode([]detector.Signal{}) | |||||
| return | |||||
| } | |||||
| _ = json.NewEncoder(w).Encode(sigSnap.get()) | |||||
| }) | |||||
| http.HandleFunc("/api/recordings", func(w http.ResponseWriter, r *http.Request) { | http.HandleFunc("/api/recordings", func(w http.ResponseWriter, r *http.Request) { | ||||
| if r.Method != http.MethodGet { | if r.Method != http.MethodGet { | ||||
| http.Error(w, "method not allowed", http.StatusMethodNotAllowed) | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) | ||||
| @@ -585,7 +613,7 @@ func main() { | |||||
| _ = server.Shutdown(ctxTimeout) | _ = server.Shutdown(ctxTimeout) | ||||
| } | } | ||||
| func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det *detector.Detector, window []float64, h *hub, eventFile *os.File, eventMu *sync.RWMutex, updates <-chan dspUpdate, gpuState *gpuStatus, rec *recorder.Manager) { | |||||
| func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det *detector.Detector, window []float64, h *hub, eventFile *os.File, eventMu *sync.RWMutex, updates <-chan dspUpdate, gpuState *gpuStatus, rec *recorder.Manager, sigSnap *signalSnapshot) { | |||||
| ticker := time.NewTicker(cfg.FrameInterval()) | ticker := time.NewTicker(cfg.FrameInterval()) | ||||
| defer ticker.Stop() | defer ticker.Stop() | ||||
| logTicker := time.NewTicker(5 * time.Second) | logTicker := time.NewTicker(5 * time.Second) | ||||
| @@ -735,6 +763,9 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det * | |||||
| signals[i].Class = cls | signals[i].Class = cls | ||||
| } | } | ||||
| } | } | ||||
| if sigSnap != nil { | |||||
| sigSnap.set(signals) | |||||
| } | |||||
| eventMu.Lock() | eventMu.Lock() | ||||
| for _, ev := range finished { | for _, ev := range finished { | ||||
| _ = enc.Encode(ev) | _ = enc.Encode(ev) | ||||