FlexDXCluster/flexradio.go
2024-09-26 15:23:05 +07:00

280 lines
8.2 KiB
Go

package main
import (
"bufio"
"fmt"
"net"
"os"
"regexp"
"strings"
"time"
log "github.com/sirupsen/logrus"
)
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
Comment string
Color string
BackgroundColor string
NewDXCC bool
NewBand bool
NewMode bool
Worked bool
}
type FlexClient struct {
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
}
func NewFlexClient(repo FlexDXClusterRepository, TCPServer TCPServer, log *log.Logger) *FlexClient {
return &FlexClient{
Address: Cfg.Flex.IP,
Port: "4992",
SpotChan: make(chan TelnetSpot),
FlexSpotChan: make(chan FlexSpot),
MsgChan: TCPServer.MsgChan,
Repo: repo,
TCPServer: TCPServer,
Log: log,
}
}
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 {
fc.Log.Error("could not connect to flex radio, exiting...", err)
os.Exit(1)
}
fc.Log.Infof("connected to flex radio at %s:%s", fc.Address, fc.Port)
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()
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++
fc.Log.Info("Subscribed to spot on FlexRadio and Deleted all spots from panadapter")
}
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,
Comment: spot.Comment,
Color: "#eaeaea",
BackgroundColor: "#000000",
Priority: "5",
NewDXCC: spot.NewDXCC,
NewBand: spot.NewBand,
NewMode: spot.NewMode,
Worked: spot.CallsignWorked,
}
// If new DXCC
if spot.NewDXCC {
flexSpot.Color = "#3bf908"
flexSpot.Priority = "1"
flexSpot.BackgroundColor = "#000000"
}
// if New Band
if spot.NewBand {
flexSpot.Color = "#f9f508"
flexSpot.Priority = "2"
flexSpot.BackgroundColor = "#000000"
}
// 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
if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked {
flexSpot.Color = "#eaeaea"
flexSpot.Priority = "5"
flexSpot.BackgroundColor = "#000000"
}
// If station worked already
if spot.CallsignWorked {
flexSpot.Color = "#000000"
flexSpot.BackgroundColor = "#00c0c0"
flexSpot.Priority = "5"
}
if spot.DX == Cfg.SQLite.Callsign {
flexSpot.Color = "#ff0000"
flexSpot.Priority = "2"
flexSpot.BackgroundColor = "#000000"
}
flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "]"
srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot)
if err != nil {
fc.Log.Error("could not find the DX in the database: ", err)
}
// send FlexSpot to HTTP Server
fc.FlexSpotChan <- flexSpot
if srcFlexSpot.DX == "" {
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)
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band && srcFlexSpot.FrequencyMhz != flexSpot.FrequencyMhz {
fc.Repo.UpdateSpotSameBand(flexSpot)
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,
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)
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) {
fc.Write(stringSpot)
}
func (fc *FlexClient) ReadLine() {
for {
message, err := fc.Reader.ReadString(byte('\n'))
if err != nil {
fc.Log.Errorf("error reading message from flexradio closing program: %s", err)
os.Exit(1)
}
// fc.Log.Info(message)
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 {
fc.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 {
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)
}
}
// Status when a spot is deleted
regSpotDeleted := *regexp.MustCompile(`S\d+\|spot (\d+) removed`)
respDelete := regSpotDeleted.FindStringSubmatch(message)
if len(respDelete) > 0 {
spot, _ := fc.Repo.FindSpotByFlexSpotNumber(respDelete[1])
fc.Repo.DeleteSpotByFlexSpotNumber(respDelete[1])
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])
}
}
}
// 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
}