FlexDXCluster/flexradio.go

280 lines
8.2 KiB
Go
Raw Permalink Normal View History

2024-09-23 16:24:50 +07:00
package main
import (
"bufio"
"fmt"
"net"
"os"
2024-09-24 11:57:48 +07:00
"regexp"
2024-09-23 16:24:50 +07:00
"strings"
"time"
log "github.com/sirupsen/logrus"
)
2024-09-24 11:57:48 +07:00
var CommandNumber int = 1
2024-09-23 16:24:50 +07:00
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
2024-09-24 11:57:48 +07:00
UTCTime string
2024-09-23 16:24:50 +07:00
LifeTime string
Priority string
Comment string
Color string
BackgroundColor string
2024-09-26 15:23:05 +07:00
NewDXCC bool
NewBand bool
NewMode bool
Worked bool
2024-09-23 16:24:50 +07:00
}
type FlexClient struct {
2024-09-26 15:23:05 +07:00
Address string
Port string
Timeout time.Duration
LogWriter *bufio.Writer
Reader *bufio.Reader
Writer *bufio.Writer
Conn *net.TCPConn
SpotChan chan TelnetSpot
MsgChan chan string
FlexSpotChan chan FlexSpot
Repo FlexDXClusterRepository
Log *log.Logger
TCPServer TCPServer
2024-09-23 16:24:50 +07:00
}
2024-09-24 11:57:48 +07:00
func NewFlexClient(repo FlexDXClusterRepository, TCPServer TCPServer, log *log.Logger) *FlexClient {
2024-09-23 16:24:50 +07:00
return &FlexClient{
2024-09-26 15:23:05 +07:00
Address: Cfg.Flex.IP,
Port: "4992",
SpotChan: make(chan TelnetSpot),
FlexSpotChan: make(chan FlexSpot),
MsgChan: TCPServer.MsgChan,
Repo: repo,
TCPServer: TCPServer,
Log: log,
2024-09-23 16:24:50 +07:00
}
}
func (fc *FlexClient) StartFlexClient() {
var err error
addr, err := net.ResolveTCPAddr("tcp", fc.Address+":"+fc.Port)
if err != nil {
fc.Log.Error("cannot resolve Telnet Client address:", err)
}
fc.LogWriter = bufio.NewWriter(os.Stdout)
fc.Timeout = 600 * time.Second
fc.Conn, err = net.DialTCP("tcp", nil, addr)
if err != nil {
2024-09-24 11:57:48 +07:00
fc.Log.Error("could not connect to flex radio, exiting...", err)
2024-09-23 16:24:50 +07:00
os.Exit(1)
}
2024-09-26 12:24:56 +07:00
fc.Log.Infof("connected to flex radio at %s:%s", fc.Address, fc.Port)
2024-09-23 16:24:50 +07:00
go func() {
for message := range fc.SpotChan {
fc.SendSpottoFlex(message)
}
}()
fc.Reader = bufio.NewReader(fc.Conn)
fc.Writer = bufio.NewWriter(fc.Conn)
err = fc.Conn.SetKeepAlive(true)
if err != nil {
fc.Log.Error("error while setting keep alive:", err)
}
go fc.ReadLine()
2024-09-24 11:57:48 +07:00
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++
2024-09-26 12:24:56 +07:00
fc.Log.Info("Subscribed to spot on FlexRadio and Deleted all spots from panadapter")
2024-09-23 16:24:50 +07:00
}
2024-09-24 11:57:48 +07:00
func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
2024-09-23 16:24:50 +07:00
freq := FreqMhztoHz(spot.Frequency)
flexSpot := FlexSpot{
2024-09-24 11:57:48 +07:00
CommandNumber: CommandNumber,
2024-09-23 16:24:50 +07:00
DX: spot.DX,
FrequencyMhz: freq,
FrequencyHz: spot.Frequency,
Band: spot.Band,
Mode: spot.Mode,
Source: "FlexDXCluster",
SpotterCallsign: spot.Spotter,
TimeStamp: time.Now().Unix(),
2024-09-24 11:57:48 +07:00
UTCTime: spot.Time,
2024-09-23 16:24:50 +07:00
LifeTime: Cfg.Flex.SpotLife,
Comment: spot.Comment,
Color: "#eaeaea",
BackgroundColor: "#000000",
Priority: "5",
2024-09-26 15:23:05 +07:00
NewDXCC: spot.NewDXCC,
NewBand: spot.NewBand,
NewMode: spot.NewMode,
Worked: spot.CallsignWorked,
2024-09-23 16:24:50 +07:00
}
2024-09-26 15:23:05 +07:00
// If new DXCC
2024-09-23 16:24:50 +07:00
if spot.NewDXCC {
flexSpot.Color = "#3bf908"
flexSpot.Priority = "1"
flexSpot.BackgroundColor = "#000000"
}
2024-09-26 15:23:05 +07:00
// if New Band
if spot.NewBand {
2024-09-23 16:24:50 +07:00
flexSpot.Color = "#f9f508"
flexSpot.Priority = "2"
flexSpot.BackgroundColor = "#000000"
}
2024-09-26 15:23:05 +07:00
// if New Mode
if spot.NewMode {
flexSpot.Color = "#f9a908"
flexSpot.Priority = "2"
flexSpot.BackgroundColor = "#000000"
}
// If not New DXCC nor Mode nor Band and never worked
2024-09-23 16:24:50 +07:00
if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked {
flexSpot.Color = "#eaeaea"
flexSpot.Priority = "5"
flexSpot.BackgroundColor = "#000000"
}
2024-09-26 15:23:05 +07:00
// If station worked already
2024-09-23 16:24:50 +07:00
if spot.CallsignWorked {
flexSpot.Color = "#000000"
flexSpot.BackgroundColor = "#00c0c0"
flexSpot.Priority = "5"
}
2024-09-26 12:24:56 +07:00
if spot.DX == Cfg.SQLite.Callsign {
flexSpot.Color = "#ff0000"
flexSpot.Priority = "2"
flexSpot.BackgroundColor = "#000000"
}
2024-09-23 16:24:50 +07:00
flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
2024-09-26 12:24:56 +07:00
flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "]"
2024-09-23 16:24:50 +07:00
srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot)
if err != nil {
fc.Log.Error("could not find the DX in the database: ", err)
}
2024-09-26 15:23:05 +07:00
// send FlexSpot to HTTP Server
fc.FlexSpotChan <- flexSpot
2024-09-23 16:24:50 +07:00
if srcFlexSpot.DX == "" {
fc.Repo.CreateSpot(flexSpot)
2024-09-24 11:57:48 +07:00
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,
2024-09-23 16:24:50 +07:00
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
2024-09-26 12:24:56 +07:00
CommandNumber++
2024-09-24 11:57:48 +07:00
fc.SendSpot(stringSpot)
2024-09-26 12:24:56 +07:00
2024-09-24 11:57:48 +07:00
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band && srcFlexSpot.FrequencyMhz != flexSpot.FrequencyMhz {
2024-09-23 16:24:50 +07:00
fc.Repo.UpdateSpotSameBand(flexSpot)
2024-09-26 12:24:56 +07:00
stringSpot := fmt.Sprintf("C%v|spot set %v 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, srcFlexSpot.FlexSpotNumber, flexSpot.FrequencyMhz,
2024-09-23 16:24:50 +07:00
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
2024-09-24 11:57:48 +07:00
CommandNumber++
2024-09-26 12:24:56 +07:00
fc.SendSpot(stringSpot)
2024-09-23 16:24:50 +07:00
2024-09-26 12:24:56 +07:00
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band != flexSpot.Band {
fc.Repo.CreateSpot(flexSpot)
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)
}
2024-09-24 11:57:48 +07:00
}
func (fc *FlexClient) SendSpot(stringSpot string) {
fc.Write(stringSpot)
}
2024-09-23 16:24:50 +07:00
func (fc *FlexClient) ReadLine() {
2024-09-24 11:57:48 +07:00
2024-09-23 16:24:50 +07:00
for {
message, err := fc.Reader.ReadString(byte('\n'))
if err != nil {
2024-09-24 11:57:48 +07:00
fc.Log.Errorf("error reading message from flexradio closing program: %s", err)
os.Exit(1)
2024-09-23 16:24:50 +07:00
}
2024-09-26 12:24:56 +07:00
// fc.Log.Info(message)
2024-09-23 16:24:50 +07:00
2024-09-24 11:57:48 +07:00
regRespSpot := *regexp.MustCompile(`R(\d+)\|0\|(\d+)\n`)
respSpot := regRespSpot.FindStringSubmatch(message)
2024-09-23 16:24:50 +07:00
2024-09-24 11:57:48 +07:00
if len(respSpot) > 0 {
spot, _ := fc.Repo.FindSpotByCommandNumber(respSpot[1])
_, err := fc.Repo.UpdateFlexSpotNumberByID(respSpot[2], *spot)
if err != nil {
fc.Log.Errorf("could not update flex spot number in database: %s", err)
}
}
2024-09-23 16:24:50 +07:00
2024-09-24 11:57:48 +07:00
// 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 {
fc.Log.Errorf("could not find spot by flex spot number in database: %s", err)
}
msg := fmt.Sprintf(`To ALL de %s <%s> : Clicked on "%s" at %s`, Cfg.SQLite.Callsign, spot.UTCTime, spot.DX, spot.FrequencyHz)
if len(fc.TCPServer.Clients) > 0 {
fc.MsgChan <- msg
fc.Log.Infof("%s clicked on spot \"%s\" at %s", Cfg.SQLite.Callsign, spot.DX, spot.FrequencyMhz)
}
}
2024-09-23 16:24:50 +07:00
2024-09-24 11:57:48 +07:00
// Status when a spot is deleted
regSpotDeleted := *regexp.MustCompile(`S\d+\|spot (\d+) removed`)
respDelete := regSpotDeleted.FindStringSubmatch(message)
2024-09-23 16:24:50 +07:00
2024-09-24 11:57:48 +07:00
if len(respDelete) > 0 {
2024-09-26 12:24:56 +07:00
spot, _ := fc.Repo.FindSpotByFlexSpotNumber(respDelete[1])
2024-09-24 11:57:48 +07:00
fc.Repo.DeleteSpotByFlexSpotNumber(respDelete[1])
2024-09-26 12:24:56 +07:00
fc.Log.Infof("Spot: DX: %s - Spotter: %s - Freq: %s - Band: %s - FlexID: %v deleted from database", spot.DX, spot.SpotterCallsign, spot.FrequencyHz, spot.Band, respDelete[1])
2024-09-24 11:57:48 +07:00
}
2024-09-23 16:24:50 +07:00
}
}
// Write sends raw data to remove telnet server
func (fc *FlexClient) Write(data string) (n int, err error) {
n, err = fc.Writer.Write([]byte(data + "\n"))
if err == nil {
err = fc.Writer.Flush()
}
return
}