first commit
This commit is contained in:
592
flexradio.go
Normal file
592
flexradio.go
Normal file
@@ -0,0 +1,592 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var CommandNumber int = 1
|
||||
|
||||
type FlexSpot struct {
|
||||
ID int
|
||||
CommandNumber int
|
||||
FlexSpotNumber int
|
||||
DX string
|
||||
FrequencyMhz string
|
||||
FrequencyHz string
|
||||
Band string
|
||||
Mode string
|
||||
FlexMode string
|
||||
Source string
|
||||
SpotterCallsign string
|
||||
TimeStamp int64
|
||||
UTCTime string
|
||||
LifeTime string
|
||||
Priority string
|
||||
OriginalComment string
|
||||
Comment string
|
||||
Color string
|
||||
BackgroundColor string
|
||||
NewDXCC bool
|
||||
NewBand bool
|
||||
NewMode bool
|
||||
NewSlot bool
|
||||
Worked bool
|
||||
InWatchlist bool
|
||||
CountryName string
|
||||
DXCC string
|
||||
}
|
||||
|
||||
type Discovery struct {
|
||||
IP string
|
||||
NickName string
|
||||
Model string
|
||||
Serial string
|
||||
Version string
|
||||
}
|
||||
|
||||
type FlexClient struct {
|
||||
Address string
|
||||
Port string
|
||||
Timeout time.Duration
|
||||
LogWriter *bufio.Writer
|
||||
Reader *bufio.Reader
|
||||
Writer *bufio.Writer
|
||||
Conn *net.TCPConn
|
||||
SpotChanToFlex chan TelnetSpot
|
||||
MsgChan chan string
|
||||
Repo FlexDXClusterRepository
|
||||
TCPServer *TCPServer
|
||||
HTTPServer *HTTPServer
|
||||
IsConnected bool
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
reconnectAttempts int
|
||||
maxReconnectAttempts int
|
||||
baseReconnectDelay time.Duration
|
||||
maxReconnectDelay time.Duration
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func NewFlexClient(repo FlexDXClusterRepository, TCPServer *TCPServer, SpotChanToFlex chan TelnetSpot, httpServer *HTTPServer) *FlexClient {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
enabled := Cfg.General.FlexRadioSpot && (Cfg.Flex.IP != "" || Cfg.Flex.Discover)
|
||||
|
||||
return &FlexClient{
|
||||
Port: "4992",
|
||||
SpotChanToFlex: SpotChanToFlex,
|
||||
MsgChan: TCPServer.MsgChan,
|
||||
Repo: repo,
|
||||
TCPServer: TCPServer,
|
||||
IsConnected: false,
|
||||
Enabled: enabled,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
maxReconnectAttempts: -1, // -1 = infini
|
||||
baseReconnectDelay: 5 * time.Second, // Délai initial
|
||||
maxReconnectDelay: 5 * time.Minute, // Max 5 minutes
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FlexClient) calculateBackoff() time.Duration {
|
||||
delay := time.Duration(float64(fc.baseReconnectDelay) * math.Pow(1.5, float64(fc.reconnectAttempts)))
|
||||
|
||||
if delay > fc.maxReconnectDelay {
|
||||
delay = fc.maxReconnectDelay
|
||||
}
|
||||
|
||||
return delay
|
||||
}
|
||||
|
||||
func (fc *FlexClient) resolveAddress() (string, error) {
|
||||
if Cfg.Flex.IP == "" && !Cfg.Flex.Discover {
|
||||
return "", fmt.Errorf("you must either turn FlexRadio Discovery on or provide an IP address")
|
||||
}
|
||||
|
||||
if Cfg.Flex.Discover {
|
||||
Log.Debug("Attempting FlexRadio discovery...")
|
||||
|
||||
// Timeout sur la découverte (10 secondes max)
|
||||
discoveryDone := make(chan struct {
|
||||
success bool
|
||||
discovery *Discovery
|
||||
}, 1)
|
||||
|
||||
go func() {
|
||||
ok, d := DiscoverFlexRadio()
|
||||
discoveryDone <- struct {
|
||||
success bool
|
||||
discovery *Discovery
|
||||
}{ok, d}
|
||||
}()
|
||||
|
||||
select {
|
||||
case result := <-discoveryDone:
|
||||
if result.success {
|
||||
Log.Infof("Found: %s with Nick: %s, Version: %s, Serial: %s - using IP: %s",
|
||||
result.discovery.Model, result.discovery.NickName, result.discovery.Version,
|
||||
result.discovery.Serial, result.discovery.IP)
|
||||
return result.discovery.IP, nil
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
Log.Warn("Discovery timeout after 10 seconds")
|
||||
}
|
||||
|
||||
if Cfg.Flex.IP == "" {
|
||||
return "", fmt.Errorf("could not discover any FlexRadio on the network and no IP provided")
|
||||
}
|
||||
|
||||
Log.Warn("Discovery failed, using configured IP")
|
||||
}
|
||||
|
||||
return Cfg.Flex.IP, nil
|
||||
}
|
||||
|
||||
func (fc *FlexClient) connect() error {
|
||||
ip, err := fc.resolveAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fc.Address = ip
|
||||
|
||||
addr, err := net.ResolveTCPAddr("tcp", fc.Address+":"+fc.Port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot resolve address %s:%s: %w", fc.Address, fc.Port, err)
|
||||
}
|
||||
|
||||
Log.Debugf("Attempting to connect to FlexRadio at %s:%s (attempt %d)",
|
||||
fc.Address, fc.Port, fc.reconnectAttempts+1)
|
||||
|
||||
conn, err := net.DialTimeout("tcp", addr.String(), 10*time.Second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not connect to FlexRadio: %w", err)
|
||||
}
|
||||
|
||||
tcpConn, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
conn.Close()
|
||||
return fmt.Errorf("connection is not a TCP connection")
|
||||
}
|
||||
|
||||
fc.Conn = tcpConn
|
||||
fc.Reader = bufio.NewReader(fc.Conn)
|
||||
fc.Writer = bufio.NewWriter(fc.Conn)
|
||||
fc.IsConnected = true
|
||||
fc.reconnectAttempts = 0
|
||||
|
||||
if err := fc.Conn.SetKeepAlive(true); err != nil {
|
||||
Log.Warn("Could not set keep alive:", err)
|
||||
}
|
||||
|
||||
Log.Infof("✅ Connected to FlexRadio at %s:%s", fc.Address, fc.Port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fc *FlexClient) StartFlexClient() {
|
||||
fc.LogWriter = bufio.NewWriter(os.Stdout)
|
||||
fc.Timeout = 600 * time.Second
|
||||
|
||||
if !fc.Enabled {
|
||||
Log.Info("FlexRadio integration disabled in config - skipping")
|
||||
|
||||
// Consommer les spots pour éviter les blocages
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-fc.ctx.Done():
|
||||
return
|
||||
case <-fc.SpotChanToFlex:
|
||||
// Ignorer les spots
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
// Goroutine pour envoyer les spots au Flex
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-fc.ctx.Done():
|
||||
return
|
||||
case spot := <-fc.SpotChanToFlex:
|
||||
fc.SendSpottoFlex(spot)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-fc.ctx.Done():
|
||||
Log.Info("Flex Client shutting down...")
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// Tentative de connexion
|
||||
err := fc.connect()
|
||||
if err != nil {
|
||||
fc.IsConnected = false
|
||||
fc.reconnectAttempts++
|
||||
|
||||
backoff := fc.calculateBackoff()
|
||||
|
||||
// Message moins alarmiste
|
||||
if fc.reconnectAttempts == 1 {
|
||||
Log.Warnf("FlexRadio not available: %v", err)
|
||||
Log.Info("FlexDXCluster will continue without FlexRadio and retry connection periodically")
|
||||
} else {
|
||||
Log.Debugf("FlexRadio still not available. Next retry in %v", backoff)
|
||||
}
|
||||
|
||||
// Attendre avant de réessayer
|
||||
select {
|
||||
case <-fc.ctx.Done():
|
||||
return
|
||||
case <-time.After(backoff):
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Connexion réussie, initialiser le Flex
|
||||
fc.initializeFlex()
|
||||
|
||||
// Démarrer la lecture (bloquant jusqu'à déconnexion)
|
||||
fc.ReadLine()
|
||||
|
||||
// Si ReadLine se termine (déconnexion), réessayer
|
||||
fc.IsConnected = false
|
||||
Log.Warn("FlexRadio connection lost. Will retry...")
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FlexClient) initializeFlex() {
|
||||
subSpotAllCmd := fmt.Sprintf("C%v|sub spot all", CommandNumber)
|
||||
fc.Write(subSpotAllCmd)
|
||||
CommandNumber++
|
||||
|
||||
clrSpotAllCmd := fmt.Sprintf("C%v|spot clear", CommandNumber)
|
||||
fc.Write(clrSpotAllCmd)
|
||||
CommandNumber++
|
||||
|
||||
Log.Debug("Subscribed to spots on FlexRadio and cleared all spots from panadapter")
|
||||
}
|
||||
|
||||
func (fc *FlexClient) Close() {
|
||||
fc.cancel()
|
||||
if fc.Conn != nil {
|
||||
fc.Conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
|
||||
|
||||
freq := FreqMhztoHz(spot.Frequency)
|
||||
|
||||
flexSpot := FlexSpot{
|
||||
CommandNumber: CommandNumber,
|
||||
DX: spot.DX,
|
||||
FrequencyMhz: freq,
|
||||
FrequencyHz: spot.Frequency,
|
||||
Band: spot.Band,
|
||||
Mode: spot.Mode,
|
||||
Source: "FlexDXCluster",
|
||||
SpotterCallsign: spot.Spotter,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
UTCTime: spot.Time,
|
||||
LifeTime: Cfg.Flex.SpotLife,
|
||||
OriginalComment: spot.Comment,
|
||||
Comment: spot.Comment,
|
||||
Color: "#ffeaeaea",
|
||||
BackgroundColor: "#ff000000",
|
||||
Priority: "5",
|
||||
NewDXCC: spot.NewDXCC,
|
||||
NewBand: spot.NewBand,
|
||||
NewMode: spot.NewMode,
|
||||
NewSlot: spot.NewSlot,
|
||||
Worked: spot.CallsignWorked,
|
||||
InWatchlist: false,
|
||||
CountryName: spot.CountryName,
|
||||
DXCC: spot.DXCC,
|
||||
}
|
||||
|
||||
flexSpot.Comment = flexSpot.Comment + " [" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "] [" + flexSpot.UTCTime + "]"
|
||||
|
||||
if fc.HTTPServer != nil && fc.HTTPServer.Watchlist != nil {
|
||||
if fc.HTTPServer.Watchlist.Matches(flexSpot.DX) {
|
||||
flexSpot.InWatchlist = true
|
||||
flexSpot.Comment = flexSpot.Comment + " [Watchlist]"
|
||||
Log.Infof("🎯 Watchlist match: %s", flexSpot.DX)
|
||||
}
|
||||
}
|
||||
|
||||
// If new DXCC
|
||||
if spot.NewDXCC {
|
||||
flexSpot.Priority = "1"
|
||||
flexSpot.Comment = flexSpot.Comment + " [New DXCC]"
|
||||
if Cfg.General.SpotColorNewEntity != "" && Cfg.General.BackgroundColorNewEntity != "" {
|
||||
flexSpot.Color = Cfg.General.SpotColorNewEntity
|
||||
flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewEntity
|
||||
} else {
|
||||
flexSpot.Color = "#ff3bf908"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
}
|
||||
} else if spot.DX == Cfg.General.Callsign {
|
||||
flexSpot.Priority = "1"
|
||||
if Cfg.General.SpotColorMyCallsign != "" && Cfg.General.BackgroundColorMyCallsign != "" {
|
||||
flexSpot.Color = Cfg.General.SpotColorMyCallsign
|
||||
flexSpot.BackgroundColor = Cfg.General.BackgroundColorMyCallsign
|
||||
} else {
|
||||
flexSpot.Color = "#ffff0000"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
}
|
||||
} else if spot.CallsignWorked {
|
||||
flexSpot.Priority = "5"
|
||||
flexSpot.Comment = flexSpot.Comment + " [Worked]"
|
||||
if Cfg.General.SpotColorWorked != "" && Cfg.General.BackgroundColorWorked != "" {
|
||||
flexSpot.Color = Cfg.General.SpotColorWorked
|
||||
flexSpot.BackgroundColor = Cfg.General.BackgroundColorWorked
|
||||
} else {
|
||||
flexSpot.Color = "#ff000000"
|
||||
flexSpot.BackgroundColor = "#ff00c0c0"
|
||||
}
|
||||
} else if spot.NewMode && spot.NewBand {
|
||||
flexSpot.Priority = "1"
|
||||
flexSpot.Comment = flexSpot.Comment + " [New Band & Mode]"
|
||||
if Cfg.General.SpotColorNewBandMode != "" && Cfg.General.BackgroundColorNewBandMode != "" {
|
||||
flexSpot.Color = Cfg.General.SpotColorNewBandMode
|
||||
flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewBandMode
|
||||
} else {
|
||||
flexSpot.Color = "#ffc603fc"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
}
|
||||
} else if spot.NewMode && !spot.NewBand {
|
||||
flexSpot.Priority = "2"
|
||||
flexSpot.Comment = flexSpot.Comment + " [New Mode]"
|
||||
if Cfg.General.SpotColorNewMode != "" && Cfg.General.BackgroundColorNewMode != "" {
|
||||
flexSpot.Color = Cfg.General.SpotColorNewMode
|
||||
flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewMode
|
||||
} else {
|
||||
flexSpot.Color = "#fff9a908"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
}
|
||||
} else if spot.NewBand && !spot.NewMode {
|
||||
flexSpot.Color = "#fff9f508"
|
||||
flexSpot.Priority = "3"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
flexSpot.Comment = flexSpot.Comment + " [New Band]"
|
||||
} else if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked && spot.NewSlot {
|
||||
flexSpot.Color = "#ff91d2ff"
|
||||
flexSpot.Priority = "5"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
flexSpot.Comment = flexSpot.Comment + " [New Slot]"
|
||||
} else if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked {
|
||||
flexSpot.Color = "#ffeaeaea"
|
||||
flexSpot.Priority = "5"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
} else {
|
||||
flexSpot.Color = "#ffeaeaea"
|
||||
flexSpot.Priority = "5"
|
||||
flexSpot.BackgroundColor = "#ff000000"
|
||||
}
|
||||
|
||||
// Send notification to Gotify
|
||||
Gotify(flexSpot)
|
||||
|
||||
flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
|
||||
|
||||
srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot)
|
||||
if err != nil {
|
||||
Log.Debugf("Could not find the DX in the database: %v", err)
|
||||
}
|
||||
|
||||
var stringSpot string
|
||||
|
||||
if srcFlexSpot.DX == "" {
|
||||
fc.Repo.CreateSpot(flexSpot)
|
||||
CommandNumber++
|
||||
|
||||
if fc.HTTPServer != nil {
|
||||
fc.HTTPServer.broadcast <- WSMessage{
|
||||
Type: "spots",
|
||||
Data: fc.Repo.GetAllSpots("1000"),
|
||||
}
|
||||
}
|
||||
|
||||
if fc.IsConnected {
|
||||
stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
|
||||
flexSpot.CommandNumber, flexSpot.FrequencyMhz,
|
||||
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
|
||||
flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color,
|
||||
flexSpot.BackgroundColor, flexSpot.Priority)
|
||||
CommandNumber++
|
||||
fc.SendSpot(stringSpot)
|
||||
}
|
||||
|
||||
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band {
|
||||
fc.Repo.DeleteSpotByFlexSpotNumber(string(flexSpot.FlexSpotNumber))
|
||||
|
||||
if fc.IsConnected {
|
||||
stringSpot = fmt.Sprintf("C%v|spot remove %v", flexSpot.CommandNumber, srcFlexSpot.FlexSpotNumber)
|
||||
fc.SendSpot(stringSpot)
|
||||
CommandNumber++
|
||||
}
|
||||
|
||||
fc.Repo.CreateSpot(flexSpot)
|
||||
CommandNumber++
|
||||
|
||||
if fc.IsConnected {
|
||||
stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
|
||||
flexSpot.CommandNumber, flexSpot.FrequencyMhz,
|
||||
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
|
||||
flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color,
|
||||
flexSpot.BackgroundColor, flexSpot.Priority)
|
||||
CommandNumber++
|
||||
fc.SendSpot(stringSpot)
|
||||
}
|
||||
|
||||
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band != flexSpot.Band {
|
||||
fc.Repo.CreateSpot(flexSpot)
|
||||
CommandNumber++
|
||||
|
||||
if fc.IsConnected {
|
||||
stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
|
||||
flexSpot.CommandNumber, flexSpot.FrequencyMhz,
|
||||
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
|
||||
flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color,
|
||||
flexSpot.BackgroundColor, flexSpot.Priority)
|
||||
CommandNumber++
|
||||
fc.SendSpot(stringSpot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FlexClient) SendSpot(stringSpot string) {
|
||||
if fc.IsConnected {
|
||||
fc.Write(stringSpot)
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FlexClient) ReadLine() {
|
||||
defer func() {
|
||||
if fc.Conn != nil {
|
||||
fc.Conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-fc.ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// Timeout sur la lecture
|
||||
fc.Conn.SetReadDeadline(time.Now().Add(5 * time.Minute))
|
||||
message, err := fc.Reader.ReadString(byte('\n'))
|
||||
if err != nil {
|
||||
Log.Debugf("Error reading from FlexRadio: %s", err)
|
||||
return
|
||||
}
|
||||
fc.Conn.SetReadDeadline(time.Time{})
|
||||
|
||||
regRespSpot := *regexp.MustCompile(`R(\d+)\|0\|(\d+)\n`)
|
||||
respSpot := regRespSpot.FindStringSubmatch(message)
|
||||
|
||||
if len(respSpot) > 0 {
|
||||
spot, _ := fc.Repo.FindSpotByCommandNumber(respSpot[1])
|
||||
_, err := fc.Repo.UpdateFlexSpotNumberByID(respSpot[2], *spot)
|
||||
if err != nil {
|
||||
Log.Errorf("Could not update Flex spot number in database: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Response when a spot is clicked
|
||||
regTriggerSpot := *regexp.MustCompile(`.*spot (\d+) triggered.*\n`)
|
||||
respTrigger := regTriggerSpot.FindStringSubmatch(message)
|
||||
|
||||
if len(respTrigger) > 0 {
|
||||
spot, err := fc.Repo.FindSpotByFlexSpotNumber(respTrigger[1])
|
||||
if err != nil {
|
||||
Log.Errorf("could not find spot by flex spot number in database: %s", err)
|
||||
}
|
||||
|
||||
// Sending the callsign to Log4OM
|
||||
SendUDPMessage("<CALLSIGN>" + spot.DX)
|
||||
}
|
||||
|
||||
// Status when a spot is deleted
|
||||
regSpotDeleted := *regexp.MustCompile(`S\d+\|spot (\d+) removed`)
|
||||
respDelete := regSpotDeleted.FindStringSubmatch(message)
|
||||
|
||||
if len(respDelete) > 0 {
|
||||
fc.Repo.DeleteSpotByFlexSpotNumber(respDelete[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FlexClient) Write(data string) (n int, err error) {
|
||||
if fc.Conn == nil || fc.Writer == nil || !fc.IsConnected {
|
||||
return 0, fmt.Errorf("not connected to FlexRadio")
|
||||
}
|
||||
|
||||
n, err = fc.Writer.Write([]byte(data + "\n"))
|
||||
if err == nil {
|
||||
err = fc.Writer.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DiscoverFlexRadio() (bool, *Discovery) {
|
||||
if Cfg.Flex.Discover {
|
||||
Log.Debugln("FlexRadio Discovery is turned on...searching for radio on the network")
|
||||
|
||||
pc, err := net.ListenPacket("udp", ":4992")
|
||||
if err != nil {
|
||||
Log.Errorf("Could not receive UDP packets to discover FlexRadio: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
pc.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
for {
|
||||
n, _, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
// Timeout atteint
|
||||
return false, nil
|
||||
}
|
||||
|
||||
discoverRe := regexp.MustCompile(`discovery_protocol_version=.*\smodel=(.*)\sserial=(.*)\sversion=(.*)\snickname=(.*)\scallsign=.*\sip=(.*)\sport=.*`)
|
||||
match := discoverRe.FindStringSubmatch(string(buf[:n]))
|
||||
|
||||
if len(match) > 0 {
|
||||
d := &Discovery{
|
||||
NickName: match[4],
|
||||
Model: match[1],
|
||||
Serial: match[2],
|
||||
Version: match[3],
|
||||
IP: match[5],
|
||||
}
|
||||
return true, d
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.Infoln("FlexRadio Discovery is turned off...using IP provided in the config file")
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
Reference in New Issue
Block a user