up
This commit is contained in:
11
cmd/server/web/dist/assets/index-Drom3Zfz.js
vendored
Normal file
11
cmd/server/web/dist/assets/index-Drom3Zfz.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
cmd/server/web/dist/assets/index-pnuRxXpy.css
vendored
Normal file
1
cmd/server/web/dist/assets/index-pnuRxXpy.css
vendored
Normal file
File diff suppressed because one or more lines are too long
5
cmd/server/web/dist/index.html
vendored
5
cmd/server/web/dist/index.html
vendored
@@ -7,11 +7,10 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||||
<script type="module" crossorigin src="/assets/index-BqlArLJ0.js"></script>
|
<script type="module" crossorigin src="/assets/index-Drom3Zfz.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Bl7hatTL.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-pnuRxXpy.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
@@ -45,6 +45,10 @@ type Client struct {
|
|||||||
|
|
||||||
onFrequencyChange func(freqMHz float64)
|
onFrequencyChange func(freqMHz float64)
|
||||||
checkTransmitAllowed func() bool
|
checkTransmitAllowed func() bool
|
||||||
|
|
||||||
|
// Track current slice frequency
|
||||||
|
currentFreq float64
|
||||||
|
currentFreqMu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(host string, port int) *Client {
|
func New(host string, port int) *Client {
|
||||||
@@ -59,7 +63,11 @@ func New(host string, port int) *Client {
|
|||||||
lastStatus: &Status{
|
lastStatus: &Status{
|
||||||
Connected: false,
|
Connected: false,
|
||||||
RadioOn: false,
|
RadioOn: false,
|
||||||
|
Tx: false, // Initialisé à false
|
||||||
|
ActiveSlices: 0,
|
||||||
|
Frequency: 0,
|
||||||
},
|
},
|
||||||
|
currentFreq: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,21 +326,108 @@ func (c *Client) handleMessage(msg string) {
|
|||||||
// DEBUG: Log tous les messages reçus
|
// DEBUG: Log tous les messages reçus
|
||||||
log.Printf("FlexRadio RAW: %s", msg)
|
log.Printf("FlexRadio RAW: %s", msg)
|
||||||
|
|
||||||
// Router selon le premier caractère
|
// Vérifier le type de message
|
||||||
switch msg[0] {
|
if len(msg) < 2 {
|
||||||
case 'R': // Réponse à une commande
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages commençant par R (réponses)
|
||||||
|
if msg[0] == 'R' {
|
||||||
c.handleCommandResponse(msg)
|
c.handleCommandResponse(msg)
|
||||||
case 'S': // Message de statut
|
return
|
||||||
c.handleStatusMessage(msg)
|
}
|
||||||
case 'V': // Version/Handle
|
|
||||||
|
// Messages commençant par S (statut)
|
||||||
|
if msg[0] == 'S' {
|
||||||
|
// Enlever le préfixe S
|
||||||
|
msg = msg[1:]
|
||||||
|
|
||||||
|
// Séparer handle et données
|
||||||
|
parts := strings.SplitN(msg, "|", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handle := parts[0]
|
||||||
|
data := parts[1]
|
||||||
|
|
||||||
|
// Parser les paires clé=valeur
|
||||||
|
statusMap := make(map[string]string)
|
||||||
|
pairs := strings.Fields(data)
|
||||||
|
for _, pair := range pairs {
|
||||||
|
if kv := strings.SplitN(pair, "=", 2); len(kv) == 2 {
|
||||||
|
statusMap[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier le type de message
|
||||||
|
if strings.Contains(data, "interlock") {
|
||||||
|
c.handleInterlockStatus(handle, statusMap)
|
||||||
|
} else if strings.Contains(data, "slice") {
|
||||||
|
c.handleSliceStatus(handle, statusMap)
|
||||||
|
} else if strings.Contains(data, "radio") {
|
||||||
|
c.handleRadioStatus(handle, statusMap)
|
||||||
|
} else {
|
||||||
|
// Vérifier si c'est une mise à jour de fréquence
|
||||||
|
if freqStr, ok := statusMap["RF_frequency"]; ok {
|
||||||
|
c.handleFrequencyUpdate(handle, freqStr, statusMap)
|
||||||
|
} else {
|
||||||
|
log.Printf("FlexRadio: Message inconnu (handle=%s): %s", handle, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autres types de messages
|
||||||
|
switch msg[0] {
|
||||||
|
case 'V':
|
||||||
log.Printf("FlexRadio: Version/Handle: %s", msg)
|
log.Printf("FlexRadio: Version/Handle: %s", msg)
|
||||||
case 'M': // Message général
|
case 'M':
|
||||||
log.Printf("FlexRadio: Message: %s", msg)
|
log.Printf("FlexRadio: Message: %s", msg)
|
||||||
default:
|
default:
|
||||||
log.Printf("FlexRadio: Unknown message type: %s", msg)
|
log.Printf("FlexRadio: Type de message inconnu: %s", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleSliceStatus(handle string, statusMap map[string]string) {
|
||||||
|
c.statusMu.Lock()
|
||||||
|
defer c.statusMu.Unlock()
|
||||||
|
|
||||||
|
if c.lastStatus == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le nombre de slices actives
|
||||||
|
c.lastStatus.ActiveSlices = 1
|
||||||
|
|
||||||
|
// Mettre à jour la fréquence
|
||||||
|
if rfFreq, ok := statusMap["RF_frequency"]; ok {
|
||||||
|
if freq, err := strconv.ParseFloat(rfFreq, 64); err == nil && freq > 0 {
|
||||||
|
oldFreq := c.lastStatus.Frequency
|
||||||
|
c.lastStatus.Frequency = freq
|
||||||
|
c.lastStatus.RadioInfo = fmt.Sprintf("Active on %.3f MHz", freq)
|
||||||
|
|
||||||
|
// Déclencher le callback si la fréquence a changé
|
||||||
|
if oldFreq != freq && c.onFrequencyChange != nil {
|
||||||
|
go c.onFrequencyChange(freq)
|
||||||
|
}
|
||||||
|
} else if freq == 0 {
|
||||||
|
// Fréquence 0 = slice inactive
|
||||||
|
c.lastStatus.Frequency = 0
|
||||||
|
c.lastStatus.RadioInfo = "Slice inactive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le mode
|
||||||
|
if mode, ok := statusMap["mode"]; ok {
|
||||||
|
c.lastStatus.Mode = mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// NE PAS utiliser tx du slice pour l'état TX réel
|
||||||
|
// tx=1 dans le slice signifie seulement "capable de TX", pas "en train de TX"
|
||||||
|
// L'état TX réel vient de l'interlock
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) handleCommandResponse(msg string) {
|
func (c *Client) handleCommandResponse(msg string) {
|
||||||
// Format: R<seq>|<status>|<data>
|
// Format: R<seq>|<status>|<data>
|
||||||
parts := strings.SplitN(msg, "|", 3)
|
parts := strings.SplitN(msg, "|", 3)
|
||||||
@@ -381,39 +476,6 @@ func isSliceListResponse(data string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) handleStatusMessage(msg string) {
|
|
||||||
parts := strings.SplitN(msg, "|", 2)
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
handle := parts[0][1:]
|
|
||||||
data := parts[1]
|
|
||||||
|
|
||||||
statusMap := make(map[string]string)
|
|
||||||
pairs := strings.Fields(data)
|
|
||||||
|
|
||||||
for _, pair := range pairs {
|
|
||||||
if kv := strings.SplitN(pair, "=", 2); len(kv) == 2 {
|
|
||||||
statusMap[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case strings.Contains(msg, "interlock"):
|
|
||||||
c.handleInterlockStatus(handle, statusMap)
|
|
||||||
|
|
||||||
case strings.Contains(msg, "slice"):
|
|
||||||
c.handleSliceStatus(handle, statusMap)
|
|
||||||
|
|
||||||
case strings.Contains(msg, "radio"):
|
|
||||||
c.handleRadioStatus(handle, statusMap)
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Printf("FlexRadio: Unknown status (handle=%s): %s", handle, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) handleInterlockStatus(handle string, statusMap map[string]string) {
|
func (c *Client) handleInterlockStatus(handle string, statusMap map[string]string) {
|
||||||
c.statusMu.Lock()
|
c.statusMu.Lock()
|
||||||
defer c.statusMu.Unlock()
|
defer c.statusMu.Unlock()
|
||||||
@@ -424,45 +486,57 @@ func (c *Client) handleInterlockStatus(handle string, statusMap map[string]strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) handleSliceStatus(handle string, statusMap map[string]string) {
|
func (c *Client) handleRadioStatus(handle string, statusMap map[string]string) {
|
||||||
c.statusMu.Lock()
|
c.statusMu.Lock()
|
||||||
defer c.statusMu.Unlock()
|
defer c.statusMu.Unlock()
|
||||||
|
|
||||||
// Quand on reçoit un message de slice, on a au moins une slice active
|
if c.lastStatus == nil {
|
||||||
c.lastStatus.ActiveSlices = 1
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if rfFreq, ok := statusMap["RF_frequency"]; ok {
|
// Mettre à jour les informations radio
|
||||||
if freq, err := strconv.ParseFloat(rfFreq, 64); err == nil && freq > 0 {
|
c.lastStatus.RadioOn = true
|
||||||
|
c.lastStatus.Connected = true
|
||||||
|
|
||||||
|
// Mettre à jour le nombre de slices
|
||||||
|
if slices, ok := statusMap["slices"]; ok {
|
||||||
|
if num, err := strconv.Atoi(slices); err == nil {
|
||||||
|
c.lastStatus.NumSlices = num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le callsign
|
||||||
|
if callsign, ok := statusMap["callsign"]; ok {
|
||||||
|
c.lastStatus.Callsign = callsign
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour les autres infos
|
||||||
|
if nickname, ok := statusMap["nickname"]; ok {
|
||||||
|
c.lastStatus.RadioInfo = fmt.Sprintf("Radio: %s", nickname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleFrequencyUpdate(handle string, freqStr string, statusMap map[string]string) {
|
||||||
|
c.statusMu.Lock()
|
||||||
|
defer c.statusMu.Unlock()
|
||||||
|
|
||||||
|
if c.lastStatus == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser la fréquence
|
||||||
|
if freq, err := strconv.ParseFloat(freqStr, 64); err == nil && freq > 0 {
|
||||||
|
oldFreq := c.lastStatus.Frequency
|
||||||
c.lastStatus.Frequency = freq
|
c.lastStatus.Frequency = freq
|
||||||
c.lastStatus.RadioInfo = fmt.Sprintf("Active on %.3f MHz", freq)
|
c.lastStatus.RadioInfo = fmt.Sprintf("Active on %.3f MHz", freq)
|
||||||
|
|
||||||
if c.onFrequencyChange != nil {
|
// Déclencher le callback si la fréquence a changé
|
||||||
|
if oldFreq != freq && c.onFrequencyChange != nil {
|
||||||
go c.onFrequencyChange(freq)
|
go c.onFrequencyChange(freq)
|
||||||
}
|
}
|
||||||
} else if freq == 0 {
|
|
||||||
// Fréquence 0 dans le message de slice = slice inactive
|
|
||||||
c.lastStatus.Frequency = 0
|
|
||||||
c.lastStatus.RadioInfo = "Slice inactive"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode, ok := statusMap["mode"]; ok {
|
log.Printf("FlexRadio: Frequency update: %s MHz", freqStr)
|
||||||
c.lastStatus.Mode = mode
|
|
||||||
}
|
|
||||||
|
|
||||||
if tx, ok := statusMap["tx"]; ok {
|
|
||||||
c.lastStatus.Tx = (tx == "1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) handleRadioStatus(handle string, statusMap map[string]string) {
|
|
||||||
if slices, ok := statusMap["slices"]; ok {
|
|
||||||
if num, err := strconv.Atoi(slices); err == nil {
|
|
||||||
c.statusMu.Lock()
|
|
||||||
c.lastStatus.NumSlices = num
|
|
||||||
c.statusMu.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) parseInfoResponse(data string) {
|
func (c *Client) parseInfoResponse(data string) {
|
||||||
@@ -506,11 +580,18 @@ func (c *Client) parseInfoResponse(data string) {
|
|||||||
|
|
||||||
c.radioInfoMu.Unlock()
|
c.radioInfoMu.Unlock()
|
||||||
|
|
||||||
|
// Mettre à jour le statut
|
||||||
c.updateRadioStatus(true, "Radio is on")
|
c.updateRadioStatus(true, "Radio is on")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(300 * time.Millisecond)
|
||||||
c.SendSliceList()
|
c.SendSliceList()
|
||||||
|
|
||||||
|
// S'abonner aux mises à jour
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
c.sendCommand("sub slice all")
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
c.sendCommand("sub interlock 0")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,7 +636,10 @@ func (c *Client) updateRadioStatus(isOn bool, info string) {
|
|||||||
c.statusMu.Lock()
|
c.statusMu.Lock()
|
||||||
defer c.statusMu.Unlock()
|
defer c.statusMu.Unlock()
|
||||||
|
|
||||||
if c.lastStatus != nil {
|
if c.lastStatus == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.lastStatus.RadioOn = isOn
|
c.lastStatus.RadioOn = isOn
|
||||||
c.lastStatus.RadioInfo = info
|
c.lastStatus.RadioInfo = info
|
||||||
|
|
||||||
@@ -580,7 +664,6 @@ func (c *Client) updateRadioStatus(isOn bool, info string) {
|
|||||||
c.lastStatus.RadioInfo = "Radio is on without any active slice"
|
c.lastStatus.RadioInfo = "Radio is on without any active slice"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) reconnectionMonitor() {
|
func (c *Client) reconnectionMonitor() {
|
||||||
log.Println("FlexRadio: Reconnection monitor started")
|
log.Println("FlexRadio: Reconnection monitor started")
|
||||||
@@ -716,10 +799,12 @@ func (c *Client) GetStatus() (*Status, error) {
|
|||||||
return &Status{
|
return &Status{
|
||||||
Connected: false,
|
Connected: false,
|
||||||
RadioOn: false,
|
RadioOn: false,
|
||||||
|
Tx: false,
|
||||||
RadioInfo: "Not initialized",
|
RadioInfo: "Not initialized",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Créer une copie
|
||||||
status := *c.lastStatus
|
status := *c.lastStatus
|
||||||
return &status, nil
|
return &status, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ package flexradio
|
|||||||
// Status represents the FlexRadio status
|
// Status represents the FlexRadio status
|
||||||
type Status struct {
|
type Status struct {
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
Frequency float64 `json:"frequency"`
|
RadioOn bool `json:"radio_on"`
|
||||||
|
RadioInfo string `json:"radio_info"`
|
||||||
|
Frequency float64 `json:"frequency"` // Primary frequency in MHz
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Tx bool `json:"tx"`
|
Tx bool `json:"tx"` // Actually transmitting
|
||||||
RadioOn bool `json:"radio_on"` // Radio is powered on and responding
|
ActiveSlices int `json:"active_slices"`
|
||||||
RadioInfo string `json:"radio_info"` // Additional info about radio state
|
NumSlices int `json:"num_slices"`
|
||||||
Callsign string `json:"callsign"` // From info command
|
Callsign string `json:"callsign"`
|
||||||
Model string `json:"model"` // From info command
|
Model string `json:"model"`
|
||||||
SoftwareVer string `json:"software_ver"` // From info command
|
SoftwareVer string `json:"software_ver"`
|
||||||
NumSlices int `json:"num_slices"` // From info command
|
|
||||||
ActiveSlices int `json:"active_slices"` // Count of active slices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InterlockState represents possible interlock states
|
// InterlockState represents possible interlock states
|
||||||
|
|||||||
Reference in New Issue
Block a user