update log4om

This commit is contained in:
2025-10-17 00:33:56 +05:30
parent b66ab53df4
commit 6705661d81
14 changed files with 230 additions and 50 deletions

View File

@@ -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() {

View File

@@ -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:

View File

@@ -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"`

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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`);

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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)

View File

@@ -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()
}