update log4om
This commit is contained in:
10
TCPClient.go
10
TCPClient.go
@@ -206,6 +206,11 @@ func (c *TCPClient) SetFilters() {
|
||||
Log.Info("FT4: On")
|
||||
}
|
||||
|
||||
if Cfg.Cluster.Beacon {
|
||||
c.Write([]byte("set/beacon\r\n"))
|
||||
Log.Info("Beacon: On")
|
||||
}
|
||||
|
||||
if !Cfg.Cluster.FT8 {
|
||||
c.Write([]byte("set/noft8\r\n"))
|
||||
Log.Info("FT8: Off")
|
||||
@@ -220,6 +225,11 @@ func (c *TCPClient) SetFilters() {
|
||||
c.Write([]byte("set/noskimmer\r\n"))
|
||||
Log.Info("Skimmer: Off")
|
||||
}
|
||||
|
||||
if !Cfg.Cluster.Beacon {
|
||||
c.Write([]byte("set/nobeacon\r\n"))
|
||||
Log.Info("Beacon: Off")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TCPClient) ReadLine() {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
general:
|
||||
delete_log_file_at_start: true
|
||||
callsign: XXXXX # Log4OM Callsign used to check if you get spotted by someone
|
||||
QRALocator: JN36dg
|
||||
log_to_file: true
|
||||
log_level: INFO # INFO or DEBUG or WARN
|
||||
telnetserver: true # not in use for now
|
||||
flexradiospot: true # not in use for now
|
||||
sendFreqModeToLog4OM: false # if not using a Flex then turn this on to send Freq and Mode to Log4OM which should in turn change the freq on the radio
|
||||
# Spot colors, if empty then default, colors in HEX AARRGGBB format
|
||||
spot_color_new_entity:
|
||||
background_color_new_entity:
|
||||
@@ -38,6 +40,7 @@ cluster:
|
||||
skimmer: true
|
||||
ft8: false
|
||||
ft4: false
|
||||
beacon: false
|
||||
command: "SET/FILTER DOC/PASS 1A,3A,4O,9A,9H,C3,CT,CU,DL,E7,EA,EA6,EI,ER,ES,EU,F,G,GD,GI,GJ,GM,GU,GW,HA,HB,HB0,HV,I,IS,IT9,JW,JX,LA,LX,LY,LZ,OE,OH,OH0,OJ0,OK,OM,ON,OY,OZ,PA,S5,SM,SP,SV,SV5,SV9,T7,TA1,TF,TK,UA,UR,YL,YO,YU,Z6,Z3" #"SET/FILTER DOC/PASS 1A,3A,4O,9A,9H,C3,CT,CU,DL,E7,EA,EA6,EI,ER,ES,EU,F,G,GD,GI,GJ,GM,GU,GW,HA,HB,HB0,HV,I,IS,IT9,JW,JX,LA,LX,LY,LZ,OE,OH,OH0,OJ0,OK,OM,ON,OY,OZ,PA,S5,SM,SP,SV,SV5,SV9,T7,TA1,TF,TK,UA,UR,YL,YO,YU,Z6,Z3,ZA,ZB"
|
||||
login_prompt: "login:"
|
||||
flex:
|
||||
|
||||
@@ -19,6 +19,7 @@ type Config struct {
|
||||
TelnetServer bool `yaml:"telnetserver"`
|
||||
FlexRadioSpot bool `yaml:"flexradiospot"`
|
||||
SpotColorNewEntity string `yaml:"spot_color_new_entity"`
|
||||
sendFreqModeToLog4OM bool `yaml:"sendFreqModeToLog4OM"`
|
||||
BackgroundColorNewEntity string `yaml:"background_color_new_entity"`
|
||||
SpotColorNewBand string `yaml:"spot_color_new_band"`
|
||||
BackgroundColorNewBand string `yaml:"background_color_new_band"`
|
||||
@@ -56,6 +57,7 @@ type Config struct {
|
||||
Skimmer bool `yaml:"skimmer"`
|
||||
FT8 bool `yaml:"ft8"`
|
||||
FT4 bool `yaml:"ft4"`
|
||||
Beacon bool `yaml:"beacon"`
|
||||
Command string `yaml:"command"`
|
||||
LoginPrompt string `yaml:"login_prompt"`
|
||||
} `yaml:"cluster"`
|
||||
|
||||
@@ -315,7 +315,7 @@ func (fc *FlexClient) ReadLine() {
|
||||
}
|
||||
|
||||
// Sending the callsign to Log4OM
|
||||
SendUDPMessage("<CALLSIGN>" + spot.DX)
|
||||
SendUDPMessage([]byte("<CALLSIGN>" + spot.DX))
|
||||
}
|
||||
|
||||
// Status when a spot is deleted
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
// spot-worker.js - Web Worker pour traiter les spots
|
||||
|
||||
let lastProcessedData = null;
|
||||
|
||||
self.onmessage = function(e) {
|
||||
|
||||
const { type, data, messageId } = e.data;
|
||||
|
||||
const { type, data, messageId } = e.data;
|
||||
|
||||
// Libérer l'ancienne référence
|
||||
lastProcessedData = null;
|
||||
|
||||
switch(type) {
|
||||
case 'FILTER_SPOTS':
|
||||
const filtered = filterSpots(data.spots, data.filters, data.watchlist);
|
||||
// ✅ AJOUTER messageId à la réponse
|
||||
self.postMessage({ type: 'FILTERED_SPOTS', data: filtered, messageId });
|
||||
lastProcessedData = null;
|
||||
break;
|
||||
|
||||
|
||||
case 'SORT_SPOTS':
|
||||
const sorted = sortSpots(data.spots, data.sortBy, data.sortOrder);
|
||||
// ✅ AJOUTER messageId à la réponse
|
||||
self.postMessage({ type: 'SORTED_SPOTS', data: sorted, messageId });
|
||||
lastProcessedData = null;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
console.error('Unknown worker message type:', type);
|
||||
}
|
||||
|
||||
@@ -78,13 +78,27 @@
|
||||
$: {
|
||||
if (spotFilters.showAll) {
|
||||
filteredSpots = spots;
|
||||
isFiltering = false;
|
||||
if (filterTimeout) {
|
||||
clearTimeout(filterTimeout);
|
||||
filterTimeout = null;
|
||||
}
|
||||
} else {
|
||||
if (filterTimeout) clearTimeout(filterTimeout);
|
||||
if (filterTimeout) {
|
||||
clearTimeout(filterTimeout);
|
||||
}
|
||||
|
||||
filterTimeout = setTimeout(async () => {
|
||||
isFiltering = true;
|
||||
filteredSpots = await spotWorker.filterSpots(spots, spotFilters, watchlist);
|
||||
isFiltering = false;
|
||||
try {
|
||||
filteredSpots = await spotWorker.filterSpots(spots, spotFilters, watchlist);
|
||||
} catch (error) {
|
||||
console.error('Filter error:', error);
|
||||
filteredSpots = spots;
|
||||
} finally {
|
||||
isFiltering = false;
|
||||
filterTimeout = null;
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
}
|
||||
@@ -220,7 +234,7 @@
|
||||
wsStatus = 'connected';
|
||||
reconnectAttempts = 0;
|
||||
errorMessage = '';
|
||||
showToast('Connected to server', 'success');
|
||||
showToast('✅ Connected to DX Cluster', 'connection');
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
@@ -273,9 +287,9 @@
|
||||
case 'spots':
|
||||
const newSpots = message.data || [];
|
||||
|
||||
// Détecter si votre indicatif a été spotté
|
||||
if (stats.myCallsign && newSpots.length > 0) {
|
||||
newSpots.forEach(spot => {
|
||||
// Vérifier si c'est votre callsign ET qu'on ne l'a pas déjà notifié
|
||||
if (spot.DX === stats.myCallsign && !notifiedSpots.has(spot.ID)) {
|
||||
notifiedSpots.add(spot.ID);
|
||||
showToast(
|
||||
@@ -285,16 +299,22 @@
|
||||
}
|
||||
});
|
||||
|
||||
if (notifiedSpots.size > 100) {
|
||||
// ✅ Nettoyer les anciens IDs (garder seulement 200 derniers)
|
||||
if (notifiedSpots.size > 200) {
|
||||
const arr = Array.from(notifiedSpots);
|
||||
notifiedSpots = new Set(arr.slice(-100));
|
||||
notifiedSpots = new Set(arr.slice(-200));
|
||||
}
|
||||
}
|
||||
|
||||
spots = newSpots;
|
||||
|
||||
// ✅ Debounce la sauvegarde du cache (toutes les 30 secondes max)
|
||||
if (spots.length > 0) {
|
||||
spotCache.saveSpots(spots).catch(err => console.error('Cache save error:', err));
|
||||
if (window.cacheSaveTimeout) clearTimeout(window.cacheSaveTimeout);
|
||||
window.cacheSaveTimeout = setTimeout(() => {
|
||||
spotCache.saveSpots(spots).catch(err => console.error('Cache save error:', err));
|
||||
window.cacheSaveTimeout = null; // ✅ Nettoyer la référence
|
||||
}, 30000); // 30 secondes
|
||||
}
|
||||
break;
|
||||
case 'spotters':
|
||||
@@ -359,13 +379,13 @@
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
showToast(`${callsign} Sent - Radio tuned on ${frequency} in ${mode}`, 'success');
|
||||
showToast(`📻 Tuned to ${callsign} • ${frequency} • ${mode}`, 'radio');
|
||||
} else {
|
||||
showToast('Failed to send', 'error');
|
||||
showToast('❌ Failed to send to radio', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending callsign:', error);
|
||||
showToast(`Error: ${error.message}`, 'error');
|
||||
showToast(`❌ Connection error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,11 +400,13 @@
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
stats.filters[filterName] = value;
|
||||
showToast(`Filter ${filterName} updated`, 'success');
|
||||
const filterLabel = filterName.toUpperCase();
|
||||
const status = value ? 'ON' : 'OFF';
|
||||
showToast(`🔧 ${filterLabel} filter ${status}`, 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating filter:', error);
|
||||
showToast(`Update error: ${error.message}`, 'error');
|
||||
showToast(`❌ Failed to update filter: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +425,7 @@ async function shutdownApp() {
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||
wsStatus = 'disconnected';
|
||||
|
||||
showToast('FlexDXCluster shutting down...', 'info');
|
||||
showToast('⚡ Shutting down FlexDXCluster...', 'warning');
|
||||
|
||||
// ✅ Envoyer la commande de shutdown au backend
|
||||
const response = await fetch('/api/shutdown', {
|
||||
@@ -434,7 +456,7 @@ async function shutdownApp() {
|
||||
} catch (error) {
|
||||
console.error('Error shutting down:', error);
|
||||
if (!isShuttingDown) {
|
||||
showToast(`Cannot shutdown: ${error.message}`, 'error');
|
||||
showToast(`❌ Shutdown failed: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,6 +518,26 @@ async function shutdownApp() {
|
||||
window.removeEventListener('sendSpot', handleSendSpot);
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
console.log('Cleaning up App...');
|
||||
|
||||
// ✅ Nettoyer tous les timeouts
|
||||
if (filterTimeout) {
|
||||
clearTimeout(filterTimeout);
|
||||
filterTimeout = null;
|
||||
}
|
||||
|
||||
if (window.cacheSaveTimeout) {
|
||||
clearTimeout(window.cacheSaveTimeout);
|
||||
window.cacheSaveTimeout = null;
|
||||
}
|
||||
|
||||
notifiedSpots.clear();
|
||||
|
||||
console.log('App cleanup complete');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div class="bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white min-h-screen p-2">
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<p class="text-xs text-slate-400 mt-1">Clients</p>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 bg-slate-800/50 backdrop-blur rounded-lg p-3 border border-slate-700/50">
|
||||
<div class="col-span-3 bg-slate-800/50 backdrop-blur rounded-lg p-3 border border-slate-700/50">
|
||||
<div class="flex items-center justify-center gap-6 h-full">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
@@ -85,6 +85,18 @@
|
||||
<div class="relative w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||
<span class="text-sm font-medium">FT4</span>
|
||||
</label>
|
||||
|
||||
<!-- ✅ AJOUTER ce switch Beacon -->
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={stats.filters.beacon}
|
||||
on:change={(e) => handleFilterChange('beacon', e.target.checked)}
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<div class="relative w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||
<span class="text-sm font-medium">Beacon</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
export let message;
|
||||
export let type = 'info'; // 'success', 'error', 'warning', 'info', 'milestone', 'band'
|
||||
export let type = 'info';
|
||||
|
||||
const icons = {
|
||||
success: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>`,
|
||||
@@ -9,28 +9,44 @@
|
||||
info: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>`,
|
||||
milestone: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"></path>`,
|
||||
band: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>`,
|
||||
mycall: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"></path>`
|
||||
mycall: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"></path>`,
|
||||
radio: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.348 14.651a3.75 3.75 0 010-5.303m5.304 0a3.75 3.75 0 010 5.303m-7.425 2.122a6.75 6.75 0 010-9.546m9.546 0a6.75 6.75 0 010 9.546M5.106 18.894c-3.808-3.808-3.808-9.98 0-13.789m13.788 0c3.808 3.808 3.808 9.981 0 13.79M12 12h.008v.007H12V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"></path>`,
|
||||
connection: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>`
|
||||
};
|
||||
|
||||
const colors = {
|
||||
success: 'bg-green-500',
|
||||
error: 'bg-red-500',
|
||||
warning: 'bg-orange-500',
|
||||
info: 'bg-blue-500',
|
||||
milestone: 'bg-gradient-to-r from-purple-500 to-pink-500',
|
||||
band: 'bg-gradient-to-r from-orange-500 to-amber-500',
|
||||
mycall: 'bg-gradient-to-r from-red-500 to-pink-500'
|
||||
success: 'from-green-500 to-emerald-600',
|
||||
error: 'from-red-500 to-rose-600',
|
||||
warning: 'from-orange-500 to-amber-600',
|
||||
info: 'from-blue-500 to-cyan-600',
|
||||
milestone: 'from-purple-500 to-pink-500',
|
||||
band: 'from-orange-500 to-amber-500',
|
||||
mycall: 'from-red-500 to-pink-500',
|
||||
radio: 'from-indigo-500 to-purple-600',
|
||||
connection: 'from-green-400 to-emerald-500'
|
||||
};
|
||||
|
||||
// Animation d'entrée plus dynamique
|
||||
let visible = false;
|
||||
$: if (message) {
|
||||
visible = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="fixed bottom-5 right-5 {colors[type]} text-white px-5 py-3 rounded-lg shadow-lg z-50 animate-in slide-in-from-bottom-5 duration-300 min-w-[300px] backdrop-blur-sm">
|
||||
<div class="flex items-center gap-3">
|
||||
{#if icons[type]}
|
||||
<svg class="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
{@html icons[type]}
|
||||
</svg>
|
||||
{/if}
|
||||
<span class="font-medium text-sm">{message}</span>
|
||||
<div class="fixed bottom-5 right-5 z-50 animate-in slide-in-from-bottom-5 duration-300" class:opacity-0={!visible}>
|
||||
<div class="bg-gradient-to-r {colors[type]} text-white px-5 py-4 rounded-xl shadow-2xl backdrop-blur-sm min-w-[350px] max-w-[500px] border border-white/20">
|
||||
<div class="flex items-start gap-3">
|
||||
{#if icons[type]}
|
||||
<div class="flex-shrink-0 w-8 h-8 bg-white/20 rounded-lg flex items-center justify-center">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
{@html icons[type]}
|
||||
</svg>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-semibold text-sm leading-relaxed break-words">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,6 +63,6 @@
|
||||
}
|
||||
|
||||
.animate-in {
|
||||
animation: slide-in-from-bottom 0.3s ease-out;
|
||||
animation: slide-in-from-bottom 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
</style>
|
||||
@@ -57,14 +57,24 @@ class SpotCache {
|
||||
const transaction = this.db.transaction(['spots'], 'readwrite');
|
||||
const store = transaction.objectStore('spots');
|
||||
|
||||
// Vider d'abord le store
|
||||
// ✅ Vider d'abord le store
|
||||
await store.clear();
|
||||
|
||||
// Ajouter tous les spots avec un timestamp
|
||||
// ✅ Sauvegarder par batch de 100 pour éviter surcharge mémoire
|
||||
const timestamp = Date.now();
|
||||
spots.forEach(spot => {
|
||||
store.put({ ...spot, timestamp });
|
||||
});
|
||||
const batchSize = 100;
|
||||
|
||||
for (let i = 0; i < spots.length; i += batchSize) {
|
||||
const batch = spots.slice(i, i + batchSize);
|
||||
batch.forEach(spot => {
|
||||
store.put({ ...spot, timestamp });
|
||||
});
|
||||
|
||||
// ✅ Petite pause pour libérer la mémoire
|
||||
if (i + batchSize < spots.length) {
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
}
|
||||
|
||||
await this.waitForTransaction(transaction);
|
||||
console.log(`✅ Saved ${spots.length} spots to cache`);
|
||||
|
||||
@@ -44,7 +44,17 @@ class SpotWorkerManager {
|
||||
|
||||
const messageId = ++this.messageId;
|
||||
|
||||
// ✅ Créer un timeout pour éviter les callbacks orphelins
|
||||
const timeoutId = setTimeout(() => {
|
||||
if (this.callbacks.has(messageId)) {
|
||||
console.warn('Worker callback timeout, cleaning up');
|
||||
this.callbacks.delete(messageId);
|
||||
resolve(spots); // Fallback sur les spots non filtrés
|
||||
}
|
||||
}, 5000); // 5 secondes max
|
||||
|
||||
this.callbacks.set(messageId, (filteredSpots) => {
|
||||
clearTimeout(timeoutId); // ✅ Nettoyer le timeout
|
||||
resolve(filteredSpots);
|
||||
});
|
||||
|
||||
@@ -66,7 +76,17 @@ class SpotWorkerManager {
|
||||
|
||||
const messageId = ++this.messageId;
|
||||
|
||||
// ✅ Timeout pour éviter les callbacks orphelins
|
||||
const timeoutId = setTimeout(() => {
|
||||
if (this.callbacks.has(messageId)) {
|
||||
console.warn('Worker callback timeout, cleaning up');
|
||||
this.callbacks.delete(messageId);
|
||||
resolve(spots);
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
this.callbacks.set(messageId, (sortedSpots) => {
|
||||
clearTimeout(timeoutId); // ✅ Nettoyer le timeout
|
||||
resolve(sortedSpots);
|
||||
});
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -12,6 +12,7 @@ require (
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -21,6 +21,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -58,6 +59,7 @@ type Filters struct {
|
||||
Skimmer bool `json:"skimmer"`
|
||||
FT8 bool `json:"ft8"`
|
||||
FT4 bool `json:"ft4"`
|
||||
Beacon bool `json:"beacon"`
|
||||
}
|
||||
|
||||
type APIResponse struct {
|
||||
@@ -91,6 +93,20 @@ type WatchlistSpot struct {
|
||||
WorkedBandMode bool `json:"workedBandMode"`
|
||||
}
|
||||
|
||||
type RemoteControlRequestFreq struct {
|
||||
XMLName xml.Name `xml:"RemoteControlRequest"`
|
||||
MessageId string `xml:"MessageId"`
|
||||
RemoteControlMessage string `xml:"RemoteControlMessage"`
|
||||
Frequency string `xml:"Frequency"`
|
||||
}
|
||||
|
||||
type RemoteControlRequestMode struct {
|
||||
XMLName xml.Name `xml:"RemoteControlRequest"`
|
||||
MessageId string `xml:"MessageId"`
|
||||
RemoteControlMessage string `xml:"RemoteControlMessage"`
|
||||
Mode string `xml:"Mode"`
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true // Allow all origins in development
|
||||
@@ -461,6 +477,7 @@ func (s *HTTPServer) calculateStats() Stats {
|
||||
Skimmer: Cfg.Cluster.Skimmer,
|
||||
FT8: Cfg.Cluster.FT8,
|
||||
FT4: Cfg.Cluster.FT4,
|
||||
Beacon: Cfg.Cluster.Beacon,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -523,6 +540,7 @@ type FilterRequest struct {
|
||||
Skimmer *bool `json:"skimmer,omitempty"`
|
||||
FT8 *bool `json:"ft8,omitempty"`
|
||||
FT4 *bool `json:"ft4,omitempty"`
|
||||
Beacon *bool `json:"beacon,omitempty"`
|
||||
}
|
||||
|
||||
func (s *HTTPServer) updateFilters(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -564,6 +582,16 @@ func (s *HTTPServer) updateFilters(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if req.Beacon != nil {
|
||||
if *req.Beacon {
|
||||
commands = append(commands, "set/beacon")
|
||||
Cfg.Cluster.Beacon = true
|
||||
} else {
|
||||
commands = append(commands, "set/nobeacon")
|
||||
Cfg.Cluster.Beacon = false
|
||||
}
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
s.TCPClient.CmdChan <- cmd
|
||||
}
|
||||
@@ -760,9 +788,39 @@ func (s *HTTPServer) handleSendCallsign(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
SendUDPMessage("<CALLSIGN>" + req.Callsign)
|
||||
SendUDPMessage([]byte("<CALLSIGN>" + req.Callsign))
|
||||
s.Log.Infof("Sent callsign %s to Log4OM via UDP (127.0.0.1:2241)", req.Callsign)
|
||||
|
||||
if Cfg.General.sendFreqModeToLog4OM {
|
||||
freqLog4OM := strings.Replace(req.Frequency, ".", "", 1)
|
||||
|
||||
xmlRequestFreq := RemoteControlRequestFreq{
|
||||
MessageId: uuid.New().String(), // Generate a new unique ID
|
||||
RemoteControlMessage: "SetTxFrequency", // Note: Typo matches your required format
|
||||
Frequency: freqLog4OM,
|
||||
}
|
||||
|
||||
xmlRequestMode := RemoteControlRequestMode{
|
||||
MessageId: uuid.New().String(), // Generate a new unique ID
|
||||
RemoteControlMessage: "SetMode", // Note: Typo matches your required format
|
||||
Mode: req.Mode,
|
||||
}
|
||||
|
||||
xmlBytesFreq, err := xml.MarshalIndent(xmlRequestFreq, "", " ")
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to marshal XML: %v", err)
|
||||
} else {
|
||||
SendUDPMessage([]byte(xmlBytesFreq))
|
||||
}
|
||||
|
||||
xmlBytesMode, err := xml.MarshalIndent(xmlRequestMode, "", " ")
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to marshal XML: %v", err)
|
||||
} else {
|
||||
SendUDPMessage([]byte(xmlBytesMode))
|
||||
}
|
||||
}
|
||||
|
||||
if req.Frequency != "" && s.FlexClient != nil && s.FlexClient.IsConnected {
|
||||
tuneCmd := fmt.Sprintf("C%v|slice tune 0 %s", CommandNumber, req.Frequency)
|
||||
s.FlexClient.Write(tuneCmd)
|
||||
|
||||
4
utils.go
4
utils.go
@@ -59,12 +59,12 @@ func CheckSignal(TCPClient *TCPClient, TCPServer *TCPServer, FlexClient *FlexCli
|
||||
}
|
||||
}
|
||||
|
||||
func SendUDPMessage(message string) {
|
||||
func SendUDPMessage(data []byte) {
|
||||
conn, err := net.Dial("udp", "127.0.0.1:2241")
|
||||
if err != nil {
|
||||
fmt.Printf("Some error %v", err)
|
||||
return
|
||||
}
|
||||
conn.Write([]byte(message))
|
||||
conn.Write(data)
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user