From 67b8011fe8ba51e399a46cb62fed96a268a2f732 Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 4 Oct 2025 18:19:35 +0200 Subject: [PATCH] update index --- httpserver.go | 51 +++++++ static/index.html | 368 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 324 insertions(+), 95 deletions(-) diff --git a/httpserver.go b/httpserver.go index 92919d5..1459bc4 100644 --- a/httpserver.go +++ b/httpserver.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "net/http" "os" "sync" @@ -33,6 +34,8 @@ type Stats struct { TotalContacts int `json:"totalContacts"` ClusterStatus string `json:"clusterStatus"` FlexStatus string `json:"flexStatus"` + MyCallsign string `json:"myCallsign"` + Mode string `json:"mode"` Filters Filters `json:"filters"` } @@ -46,6 +49,11 @@ type APIResponse struct { Success bool `json:"success"` Data interface{} `json:"data,omitempty"` Error string `json:"error,omitempty"` + Message string `json:"message,omitempty"` +} + +type SendCallsignRequest struct { + Callsign string `json:"callsign"` } func NewHTTPServer(flexRepo *FlexDXClusterRepository, contactRepo *Log4OMContactsRepository, @@ -81,6 +89,7 @@ func (s *HTTPServer) setupRoutes() { api.HandleFunc("/send-command", s.sendCommand).Methods("POST", "OPTIONS") api.HandleFunc("/filters", s.updateFilters).Methods("POST", "OPTIONS") api.HandleFunc("/shutdown", s.shutdownApp).Methods("POST", "OPTIONS") + api.HandleFunc("/send-callsign", s.handleSendCallsign).Methods("POST", "OPTIONS") // Serve static files (dashboard) s.Router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static"))) @@ -145,6 +154,7 @@ func (s *HTTPServer) getStats(w http.ResponseWriter, r *http.Request) { TotalContacts: contacts, ClusterStatus: clusterStatus, FlexStatus: flexStatus, + MyCallsign: Cfg.General.Callsign, // Nouveau Filters: Filters{ Skimmer: Cfg.Cluster.Skimmer, FT8: Cfg.Cluster.FT8, @@ -270,6 +280,47 @@ func (s *HTTPServer) updateFilters(w http.ResponseWriter, r *http.Request) { s.sendJSON(w, APIResponse{Success: true, Data: map[string]string{"message": "Filters updated successfully"}}) } +func (s *HTTPServer) handleSendCallsign(w http.ResponseWriter, r *http.Request) { + var req struct { + Callsign string `json:"callsign"` + Frequency string `json:"frequency"` + Mode string `json:"mode"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"}) + return + } + + if req.Callsign == "" { + s.sendJSON(w, APIResponse{Success: false, Error: "Callsign is required"}) + return + } + + // Envoyer l'indicatif à Log4OM via UDP + SendUDPMessage("" + req.Callsign) + s.Log.Infof("Sent callsign %s to Log4OM via UDP (127.0.0.1:2241)", req.Callsign) + + // Tuner la radio Flex sur la fréquence si elle est fournie + if req.Frequency != "" && s.FlexClient != nil && s.FlexClient.IsConnected { + // Commande TUNE pour le slice 0 selon l'API FlexRadio + tuneCmd := fmt.Sprintf("C%v|slice tune 0 %s", CommandNumber, req.Frequency) + s.FlexClient.Write(tuneCmd) + CommandNumber++ + time.Sleep(time.Millisecond * 500) + modeCmd := fmt.Sprintf("C%v|slice s 0 mode=%s", CommandNumber, req.Mode) + s.FlexClient.Write(modeCmd) + CommandNumber++ + s.Log.Infof("Sent TUNE command to Flex: %s", tuneCmd) + } + + s.sendJSON(w, APIResponse{ + Success: true, + Message: "Callsign sent to Log4OM and radio tuned", + Data: map[string]string{"callsign": req.Callsign, "frequency": req.Frequency}, + }) +} + func (s *HTTPServer) sendJSON(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) diff --git a/static/index.html b/static/index.html index 190a2fb..8e4969c 100644 --- a/static/index.html +++ b/static/index.html @@ -23,9 +23,60 @@ ::-webkit-scrollbar-track { background: rgb(15 23 42); } ::-webkit-scrollbar-thumb { background: rgb(51 65 85); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: rgb(71 85 105); } + + .toast { + position: fixed; + top: 20px; + right: 20px; + padding: 12px 20px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); + z-index: 1000; + animation: slideIn 0.3s ease-out; + } + + @keyframes slideIn { + from { transform: translateX(400px); opacity: 0; } + to { transform: translateX(0); opacity: 1; } + } + + @keyframes slideOut { + from { transform: translateX(0); opacity: 1; } + to { transform: translateX(400px); opacity: 0; } + } + + .toast.hiding { + animation: slideOut 0.3s ease-out forwards; + } + + .toast-success { background: rgb(34 197 94); color: white; } + .toast-error { background: rgb(239 68 68); color: white; } + .toast-warning { background: rgb(251 146 60); color: white; } + .toast-info { background: rgb(59 130 246); color: white; } + + .error-banner { + background: linear-gradient(to right, rgb(239 68 68), rgb(220 38 38)); + color: white; + padding: 12px; + text-align: center; + font-size: 14px; + border-bottom: 2px solid rgb(185 28 28); + } + + .dx-callsign { + cursor: pointer; + transition: all 0.2s ease; + } + + .dx-callsign:hover { + text-shadow: 0 0 8px rgba(59, 130, 246, 0.8); + transform: scale(1.05); + } +
+