update
This commit is contained in:
parent
20952252be
commit
9ddc3ce347
123
TCPClient.go
Normal file
123
TCPClient.go
Normal file
@ -0,0 +1,123 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var spotRe *regexp.Regexp = regexp.MustCompile(`DX\sde\s([\w\d]+).*:\s+(\d+.\d)\s+([\w\d]+)\s+(CW|SSB|FT8|FT4|RTTY|USB|LSB)?\s+(.*)\s\s\s+([\d]+\w{1})`)
|
||||
|
||||
type TelnetClient struct {
|
||||
Login string
|
||||
Password string
|
||||
Address string
|
||||
Port string
|
||||
Timeout time.Duration
|
||||
LogWriter *bufio.Writer
|
||||
Reader *bufio.Reader
|
||||
Writer *bufio.Writer
|
||||
Conn *net.TCPConn
|
||||
TCPServer TCPServer
|
||||
FlexClient FlexClient
|
||||
MsgChan chan string
|
||||
CmdChan chan string
|
||||
SpotChan chan TelnetSpot
|
||||
Log *log.Logger
|
||||
}
|
||||
|
||||
func (tc *TelnetClient) setDefaultParams() {
|
||||
if tc.Timeout == 0 {
|
||||
tc.Timeout = 600 * time.Second
|
||||
}
|
||||
if tc.LogWriter == nil {
|
||||
tc.LogWriter = bufio.NewWriter(os.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TelnetClient) StartClient() {
|
||||
var err error
|
||||
|
||||
addr, err := net.ResolveTCPAddr("tcp", tc.Address+":"+tc.Port)
|
||||
if err != nil {
|
||||
tc.Log.Error("cannot resolve Telnet Client address:", err)
|
||||
}
|
||||
|
||||
tc.setDefaultParams()
|
||||
tc.Conn, err = net.DialTCP("tcp", nil, addr)
|
||||
if err != nil {
|
||||
tc.Log.Error("cannot connect to Telnet Client:", err)
|
||||
}
|
||||
tc.Log.Infof("connected to %s:%s", tc.Address, tc.Port)
|
||||
|
||||
err = tc.Conn.SetKeepAlive(true)
|
||||
if err != nil {
|
||||
tc.Log.Error("error while setting keep alive:", err)
|
||||
}
|
||||
|
||||
tc.Reader = bufio.NewReader(tc.Conn)
|
||||
tc.Writer = bufio.NewWriter(tc.Conn)
|
||||
|
||||
go tc.ReadLine()
|
||||
}
|
||||
|
||||
func (tc *TelnetClient) Close() {
|
||||
tc.Writer.WriteString("bye")
|
||||
}
|
||||
|
||||
func (tc *TelnetClient) SetFilters() {
|
||||
if Cfg.Cluster.FT8 {
|
||||
tc.Write([]byte("set/ft8\r\n"))
|
||||
tc.Log.Info("FT8 is on as defined in the config file")
|
||||
}
|
||||
|
||||
if Cfg.Cluster.Skimmer {
|
||||
tc.Write([]byte("set/skimmer\r\n"))
|
||||
tc.Log.Info("Skimmer is on as defined in the config file")
|
||||
}
|
||||
|
||||
if !Cfg.Cluster.FT8 {
|
||||
tc.Write([]byte("set/noft8\r\n"))
|
||||
tc.Log.Info("FT8 is off as defined in the config file")
|
||||
}
|
||||
|
||||
if !Cfg.Cluster.Skimmer {
|
||||
tc.Write([]byte("set/noskimmer\r\n"))
|
||||
tc.Log.Info("Skimmer is off as defined in the config file")
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TelnetClient) ReadLine() {
|
||||
for {
|
||||
message, err := tc.Reader.ReadString('\n')
|
||||
if err != nil {
|
||||
tc.Log.Errorf("Error reading message: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(message, "Login: \r\n") || strings.Contains(message, "Please enter your call: \r\n") {
|
||||
tc.Log.Info("Found login prompt...sending callsign")
|
||||
tc.Write([]byte(tc.Login + "\r\n"))
|
||||
time.Sleep(time.Second * 2)
|
||||
tc.SetFilters()
|
||||
}
|
||||
|
||||
ProcessTelnetSpot(spotRe, message, tc.SpotChan, tc.Log)
|
||||
tc.MsgChan <- message
|
||||
}
|
||||
}
|
||||
|
||||
// Write sends raw data to remove telnet server
|
||||
func (tc *TelnetClient) Write(data []byte) (n int, err error) {
|
||||
n, err = tc.Writer.Write(data)
|
||||
if err == nil {
|
||||
err = tc.Writer.Flush()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
125
TCPServer.go
Normal file
125
TCPServer.go
Normal file
@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
type TCPServer struct {
|
||||
Address string
|
||||
Port string
|
||||
Clients map[net.Conn]bool
|
||||
Mutex sync.Mutex
|
||||
LogWriter *bufio.Writer
|
||||
Reader *bufio.Reader
|
||||
Writer *bufio.Writer
|
||||
Conn net.Conn
|
||||
Listener net.Listener
|
||||
MsgChan chan string
|
||||
CmdChan chan string
|
||||
Log *log.Logger
|
||||
}
|
||||
|
||||
func NewTCPServer(address string, port string, log *log.Logger) *TCPServer {
|
||||
return &TCPServer{
|
||||
Address: address,
|
||||
Port: port,
|
||||
Clients: make(map[net.Conn]bool),
|
||||
MsgChan: make(chan string),
|
||||
CmdChan: make(chan string),
|
||||
Log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TCPServer) StartServer() {
|
||||
s.LogWriter = bufio.NewWriter(os.Stdout)
|
||||
s.Listener, err = net.Listen("tcp", Cfg.Telnet.Host+":"+Cfg.Telnet.Port)
|
||||
if err != nil {
|
||||
s.Log.Info("could not create telnet server")
|
||||
}
|
||||
|
||||
defer s.Listener.Close()
|
||||
|
||||
s.Log.Info("telnet server listening on %s:%s", Cfg.Telnet.Host, Cfg.Telnet.Port)
|
||||
|
||||
go func() {
|
||||
for message := range s.MsgChan {
|
||||
s.broadcastMessage(message)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
s.Conn, err = s.Listener.Accept()
|
||||
s.Log.Info("client connected", s.Conn.RemoteAddr().String())
|
||||
if err != nil {
|
||||
s.Log.Error("could not accept connections to telnet server")
|
||||
continue
|
||||
}
|
||||
s.Mutex.Lock()
|
||||
s.Clients[s.Conn] = true
|
||||
s.Mutex.Unlock()
|
||||
|
||||
go s.handleConnection()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TCPServer) handleConnection() {
|
||||
defer s.Conn.Close()
|
||||
s.Conn.Write([]byte("Welcome to the FlexDXCluster telnet server! Type 'bye' to exit.\n"))
|
||||
|
||||
// s.Conn.Write([]byte(`To ALL de XV9Q <0234Z> : Clicked on "JH2UNG"\n`))
|
||||
|
||||
reader := bufio.NewReader(s.Conn)
|
||||
for {
|
||||
|
||||
message, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
s.Mutex.Lock()
|
||||
delete(s.Clients, s.Conn)
|
||||
s.Mutex.Unlock()
|
||||
s.Log.Info("client disconnected")
|
||||
return
|
||||
}
|
||||
|
||||
message = strings.TrimSpace(message)
|
||||
|
||||
if message == "bye" {
|
||||
s.Mutex.Lock()
|
||||
delete(s.Clients, s.Conn)
|
||||
s.Mutex.Unlock()
|
||||
s.Log.Info("client disconnected")
|
||||
return
|
||||
}
|
||||
|
||||
s.Log.Info("Message reçu du client: %s\n", message)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TCPServer) Write(message string) (n int, err error) {
|
||||
n, err = s.Writer.Write([]byte(message))
|
||||
if err == nil {
|
||||
err = s.Writer.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *TCPServer) broadcastMessage(message string) {
|
||||
s.Mutex.Lock()
|
||||
defer s.Mutex.Unlock()
|
||||
for client := range s.Clients {
|
||||
_, err := client.Write([]byte(message))
|
||||
if err != nil {
|
||||
fmt.Println("Erreur lors de l'envoi du message au client:", client.RemoteAddr())
|
||||
}
|
||||
}
|
||||
}
|
24
clublog.go
Normal file
24
clublog.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func CheckClubogDXCC(callsign string) (string, error) {
|
||||
// Clublog check DXCC
|
||||
clublogURL := "https://clublog.org/dxcc?call=" + callsign + "&api=5767f19333363a9ef432ee9cd4141fe76b8adf38"
|
||||
resp, err := http.Get(clublogURL)
|
||||
if err != nil {
|
||||
fmt.Println("error while getting DXCC from Clublog")
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println("could not get dxcc from clublog", err)
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
69
config.go
Normal file
69
config.go
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
Cfg *Config
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
SQLite struct {
|
||||
SQLitePath string `yaml:"sqlite_path"`
|
||||
Callsign string `yaml:"callsign"`
|
||||
} `yaml:"sqlite"`
|
||||
|
||||
Cluster struct {
|
||||
Server string `yaml:"server"`
|
||||
Port string `yaml:"port"`
|
||||
Login string `yaml:"login"`
|
||||
Skimmer bool `yaml:"skimmer"`
|
||||
FT8 bool `yaml:"ft8"`
|
||||
} `yaml:"cluster"`
|
||||
|
||||
Flex struct {
|
||||
IP string `yaml:"ip"`
|
||||
SpotLife string `yaml:"spot_life"`
|
||||
} `yaml:"flex"`
|
||||
|
||||
Clublog struct {
|
||||
Api string `yaml:"api"`
|
||||
} `yaml:"clublog"`
|
||||
|
||||
Telnet struct {
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
} `yaml:"telnet"`
|
||||
}
|
||||
|
||||
func NewConfig(configPath string) error {
|
||||
config := &Config{}
|
||||
file, err := os.Open(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
d := yaml.NewDecoder(file)
|
||||
|
||||
if err := d.Decode(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Cfg = config
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateConfigPath(path string) error {
|
||||
s, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.IsDir() {
|
||||
return fmt.Errorf("'%s' is a directory, not a normal file", path)
|
||||
}
|
||||
return nil
|
||||
}
|
19
config.yml
Normal file
19
config.yml
Normal file
@ -0,0 +1,19 @@
|
||||
general:
|
||||
log_level: DEBUG
|
||||
sqlite:
|
||||
sqlite_path: 'C:\Perso\Seafile\Radio\Logs\Log4OM\Vietnam.SQLite'
|
||||
callsign: XV9Q
|
||||
cluster:
|
||||
server: dxc.k0xm.net
|
||||
port: 7300
|
||||
login: xv9q-5
|
||||
skimmer: true
|
||||
ft8: false
|
||||
flex:
|
||||
ip: 10.10.10.120
|
||||
spot_life: 300
|
||||
clublog:
|
||||
api: 5767f19333363a9ef432ee9cd4141fe76b8adf38
|
||||
telnet:
|
||||
host: 0.0.0.0
|
||||
port: 7301
|
289
database.go
Normal file
289
database.go
Normal file
@ -0,0 +1,289 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Contact struct {
|
||||
Callsign string
|
||||
Band string
|
||||
Mode string
|
||||
DXCC string
|
||||
StationCallsign string
|
||||
Country string
|
||||
}
|
||||
|
||||
type ContactsRepository struct {
|
||||
db *sql.DB
|
||||
Log *log.Logger
|
||||
}
|
||||
|
||||
type FlexDXClusterRepository struct {
|
||||
db *sql.DB
|
||||
Log *log.Logger
|
||||
}
|
||||
|
||||
func NewContactsRepository(filePath string, log *log.Logger) *ContactsRepository {
|
||||
db, err := sql.Open("sqlite3", filePath)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot open db", err)
|
||||
}
|
||||
return &ContactsRepository{
|
||||
db: db,
|
||||
Log: log}
|
||||
}
|
||||
|
||||
func NewFlexDXDatabase(filePath string, log *log.Logger) *FlexDXClusterRepository {
|
||||
|
||||
db, err := sql.Open("sqlite3", filePath)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot open db", err)
|
||||
}
|
||||
|
||||
log.Info("Opening SQLite database")
|
||||
|
||||
_, err = db.ExecContext(
|
||||
context.Background(),
|
||||
`CREATE TABLE IF NOT EXISTS "spots" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"commandNumber" INTEGER NOT NULL UNIQUE,
|
||||
"flexSpotNumber" INTEGER UNIQUE,
|
||||
"dx" TEXT NOT NULL,
|
||||
"freqMhz" TEXT,
|
||||
"freqHz" TEXT,
|
||||
"band" TEXT,
|
||||
"mode" TEXT,
|
||||
"spotter" INTEGER,
|
||||
"flexMode" TEXT,
|
||||
"source" TEXT,
|
||||
"timestamp" INTEGER,
|
||||
"lifeTime" TEXT,
|
||||
"priority" TEXT,
|
||||
"comment" TEXT,
|
||||
"color" TEXT,
|
||||
"backgroundColor" INTEGER,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
)`,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Cannot create table", err)
|
||||
}
|
||||
|
||||
return &FlexDXClusterRepository{
|
||||
db: db,
|
||||
Log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ContactsRepository) ListByCountry(countryID string) ([]*Contact, error) {
|
||||
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ?", countryID)
|
||||
if err != nil {
|
||||
log.Error("could not query database", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contacts := []*Contact{}
|
||||
for rows.Next() {
|
||||
c := Contact{}
|
||||
if err := rows.Scan(&c.Callsign, &c.Band, &c.Mode, &c.DXCC, &c.StationCallsign, &c.Country); err != nil {
|
||||
log.Error("could not query database", err)
|
||||
return nil, err
|
||||
}
|
||||
contacts = append(contacts, &c)
|
||||
}
|
||||
return contacts, nil
|
||||
}
|
||||
|
||||
func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([]*Contact, error) {
|
||||
|
||||
modeUSB := "USB"
|
||||
modeLSB := "LSB"
|
||||
|
||||
if mode == "USB" || mode == "LSB" {
|
||||
|
||||
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND (mode = ? OR mode = ?)", countryID, modeLSB, modeUSB)
|
||||
if err != nil {
|
||||
log.Error("could not query database", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contacts := []*Contact{}
|
||||
for rows.Next() {
|
||||
c := Contact{}
|
||||
if err := rows.Scan(&c.Callsign, &c.Band, &c.Mode, &c.DXCC, &c.StationCallsign, &c.Country); err != nil {
|
||||
log.Error("could not query database", err)
|
||||
return nil, err
|
||||
}
|
||||
contacts = append(contacts, &c)
|
||||
}
|
||||
return contacts, nil
|
||||
|
||||
} else {
|
||||
|
||||
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND mode = ?", countryID, mode)
|
||||
if err != nil {
|
||||
log.Error("could not query the database", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contacts := []*Contact{}
|
||||
for rows.Next() {
|
||||
c := Contact{}
|
||||
if err := rows.Scan(&c.Callsign, &c.Band, &c.Mode, &c.DXCC, &c.StationCallsign, &c.Country); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
contacts = append(contacts, &c)
|
||||
}
|
||||
return contacts, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ContactsRepository) ListByCountryBand(countryID string, band string) ([]*Contact, error) {
|
||||
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND band = ?", countryID, band)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contacts := []*Contact{}
|
||||
for rows.Next() {
|
||||
c := Contact{}
|
||||
if err := rows.Scan(&c.Callsign, &c.Band, &c.Mode, &c.DXCC, &c.StationCallsign, &c.Country); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
contacts = append(contacts, &c)
|
||||
}
|
||||
return contacts, nil
|
||||
}
|
||||
|
||||
func (r *ContactsRepository) ListByCallSign(callSign string, band string, mode string) ([]*Contact, error) {
|
||||
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE callsign = ? AND band = ? AND mode = ?", callSign, band, mode)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contacts := []*Contact{}
|
||||
for rows.Next() {
|
||||
c := Contact{}
|
||||
if err := rows.Scan(&c.Callsign, &c.Band, &c.Mode, &c.DXCC, &c.StationCallsign, &c.Country); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
contacts = append(contacts, &c)
|
||||
}
|
||||
return contacts, nil
|
||||
}
|
||||
|
||||
func (r *FlexDXClusterRepository) FindDXSameBand(spot FlexSpot) (*FlexSpot, error) {
|
||||
rows, err := r.db.Query("SELECT * from spots WHERE dx = ? AND band = ?", spot.DX, spot.Band)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := FlexSpot{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.TimeStamp, &s.LifeTime, &s.Priority,
|
||||
&s.Comment, &s.Color, &s.BackgroundColor); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func (r *FlexDXClusterRepository) CreateSpot(spot FlexSpot) {
|
||||
query := "INSERT INTO `spots` (`commandNumber`, `flexSpotNumber`, `dx`, `freqMhz`, `freqHz`, `band`, `mode`, `spotter`, `flexMode`, `source`, `timestamp`, `lifeTime`, `priority`, `comment`, `color`, `backgroundColor`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
insertResult, err := r.db.ExecContext(context.Background(), query, spot.CommandNumber, spot.CommandNumber, spot.DX, spot.FrequencyMhz, spot.FrequencyHz, spot.Band, spot.Mode, spot.SpotterCallsign, spot.FlexMode, spot.Source, time.Now().Unix(), spot.LifeTime, spot.Priority, spot.Comment, spot.Color, spot.BackgroundColor)
|
||||
if err != nil {
|
||||
log.Errorf("cannot insert spot in database: %s", err)
|
||||
}
|
||||
|
||||
_, err = insertResult.LastInsertId()
|
||||
if err != nil {
|
||||
log.Errorf("impossible to retrieve last inserted id: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *FlexDXClusterRepository) UpdateSpotSameBand(spot FlexSpot) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *FlexDXClusterRepository) FindSpotByCommandNumber(commandNumber string) (*FlexSpot, error) {
|
||||
rows, err := r.db.Query("SELECT * from spots WHERE commandNumber = ?", commandNumber)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := FlexSpot{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.TimeStamp, &s.LifeTime, &s.Priority,
|
||||
&s.Comment, &s.Color, &s.BackgroundColor); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func (r *FlexDXClusterRepository) FindSpotByFlexSpotNumber(spotNumber string) (*FlexSpot, error) {
|
||||
rows, err := r.db.Query("SELECT * from spots WHERE flexSpotNumber = ?", spotNumber)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := FlexSpot{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.TimeStamp, &s.LifeTime, &s.Priority,
|
||||
&s.Comment, &s.Color, &s.BackgroundColor); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func (r *FlexDXClusterRepository) UpdateFlexSpotNumberByID(flexSpot string, spot FlexSpot) (*FlexSpot, error) {
|
||||
flexSpotNumberInt, _ := strconv.Atoi(flexSpot)
|
||||
rows, err := r.db.Query(`UPDATE spots SET flexSpotNumber = ? WHERE id = ? RETURNING *`, flexSpotNumberInt, spot.ID)
|
||||
if err != nil {
|
||||
r.Log.Errorf("could not update database: %s", err)
|
||||
}
|
||||
|
||||
s := FlexSpot{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.TimeStamp, &s.LifeTime, &s.Priority,
|
||||
&s.Comment, &s.Color, &s.BackgroundColor); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func DeleteDatabase(filePath string, log *log.Logger) {
|
||||
_, err := os.Stat(filePath)
|
||||
if !os.IsNotExist(err) {
|
||||
err := os.Remove(filePath)
|
||||
if err != nil {
|
||||
log.Error("could not delete existing database")
|
||||
}
|
||||
log.Info("deleting existing database")
|
||||
}
|
||||
}
|
BIN
flex.sqlite
Normal file
BIN
flex.sqlite
Normal file
Binary file not shown.
226
flexradio.go
Normal file
226
flexradio.go
Normal file
@ -0,0 +1,226 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var command 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
|
||||
LifeTime string
|
||||
Priority string
|
||||
Comment string
|
||||
Color string
|
||||
BackgroundColor string
|
||||
}
|
||||
|
||||
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
|
||||
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),
|
||||
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 dial flex client:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fc.Log.Infof("connected to %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()
|
||||
}
|
||||
|
||||
func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) (n int, err error) {
|
||||
|
||||
freq := FreqMhztoHz(spot.Frequency)
|
||||
|
||||
flexSpot := FlexSpot{
|
||||
CommandNumber: command,
|
||||
DX: spot.DX,
|
||||
FrequencyMhz: freq,
|
||||
FrequencyHz: spot.Frequency,
|
||||
Band: spot.Band,
|
||||
Mode: spot.Mode,
|
||||
Source: "FlexDXCluster",
|
||||
SpotterCallsign: spot.Spotter,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
LifeTime: Cfg.Flex.SpotLife,
|
||||
Comment: spot.Comment,
|
||||
Color: "#eaeaea",
|
||||
BackgroundColor: "#000000",
|
||||
Priority: "5",
|
||||
}
|
||||
|
||||
if spot.NewDXCC {
|
||||
flexSpot.Color = "#3bf908"
|
||||
flexSpot.Priority = "1"
|
||||
flexSpot.BackgroundColor = "#000000"
|
||||
}
|
||||
|
||||
if spot.NewBand || spot.NewMode && spot.Mode != "" {
|
||||
flexSpot.Color = "#f9f508"
|
||||
flexSpot.Priority = "2"
|
||||
flexSpot.BackgroundColor = "#000000"
|
||||
}
|
||||
|
||||
if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked {
|
||||
flexSpot.Color = "#eaeaea"
|
||||
flexSpot.Priority = "5"
|
||||
flexSpot.BackgroundColor = "#000000"
|
||||
}
|
||||
|
||||
if spot.CallsignWorked {
|
||||
flexSpot.Color = "#000000"
|
||||
flexSpot.BackgroundColor = "#00c0c0"
|
||||
flexSpot.Priority = "5"
|
||||
}
|
||||
|
||||
flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
|
||||
flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "]"
|
||||
|
||||
srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot)
|
||||
if err != nil {
|
||||
fc.Log.Error("could not find the DX in the database: ", err)
|
||||
}
|
||||
|
||||
var stringSpot string
|
||||
spotLife, _ := strconv.Atoi(Cfg.Flex.SpotLife)
|
||||
spotTime := time.Unix(srcFlexSpot.TimeStamp, 0)
|
||||
elapsed := time.Since(spotTime)
|
||||
|
||||
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)
|
||||
} else if srcFlexSpot.DX != "" && elapsed > time.Duration(spotLife) {
|
||||
fc.Repo.UpdateSpotSameBand(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)
|
||||
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band && srcFlexSpot.FrequencyMhz != flexSpot.FrequencyMhz && elapsed < time.Duration(spotLife) {
|
||||
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.CommandNumber, flexSpot.FrequencyMhz,
|
||||
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
|
||||
}
|
||||
|
||||
fc.Write(stringSpot)
|
||||
|
||||
command++
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (fc *FlexClient) ReadLine() {
|
||||
for {
|
||||
message, err := fc.Reader.ReadString(byte('\n'))
|
||||
if err != nil {
|
||||
fc.Log.Errorf("error reading message: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
// regTriggerSpot := *regexp.MustCompile(`.*\|spot\s(\d+)\striggered.*`)
|
||||
// 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 <0233z> : Clicked on %s at %s`, Cfg.SQLite.Callsign, spot.DX, spot.FrequencyHz)
|
||||
// if len(fc.TCPServer.Clients) > 0 {
|
||||
// fc.MsgChan <- msg
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
msg := strings.TrimSpace(message)
|
||||
fc.Log.Info(msg)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
194659
flexradio.log
Normal file
194659
flexradio.log
Normal file
File diff suppressed because it is too large
Load Diff
19
go.mod
Normal file
19
go.mod
Normal file
@ -0,0 +1,19 @@
|
||||
module git.rouggy.com/rouggy/FlexDXCluster
|
||||
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/mattn/go-sqlite3 v1.14.23
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
)
|
35
go.sum
Normal file
35
go.sum
Normal file
@ -0,0 +1,35 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
|
||||
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
63
logger/log.go
Normal file
63
logger/log.go
Normal file
@ -0,0 +1,63 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
prefixed "github.com/x-cray/logrus-prefixed-formatter"
|
||||
)
|
||||
|
||||
var Log *log.Logger
|
||||
|
||||
func NewLog() *log.Logger {
|
||||
f, err := os.OpenFile("flexradio.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
w := io.MultiWriter(os.Stdout, f)
|
||||
|
||||
Log := &log.Logger{
|
||||
Out: w,
|
||||
Level: log.DebugLevel,
|
||||
Formatter: &prefixed.TextFormatter{
|
||||
DisableColors: false,
|
||||
TimestampFormat: "02-01-2006 15:04:05",
|
||||
FullTimestamp: true,
|
||||
ForceFormatting: true,
|
||||
},
|
||||
}
|
||||
|
||||
return Log
|
||||
}
|
||||
|
||||
// Info ...
|
||||
func Info(format string, v ...interface{}) {
|
||||
log.Infof(format, v...)
|
||||
}
|
||||
|
||||
// Warn ...
|
||||
func Warn(format string, v ...interface{}) {
|
||||
log.Warnf(format, v...)
|
||||
}
|
||||
|
||||
// Error ...
|
||||
func Error(format string, v ...interface{}) {
|
||||
log.Errorf(format, v...)
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
// ConfigError ...
|
||||
ConfigError = "%v type=config.error"
|
||||
|
||||
// HTTPError ...
|
||||
HTTPError = "%v type=http.error"
|
||||
|
||||
// HTTPWarn ...
|
||||
HTTPWarn = "%v type=http.warn"
|
||||
|
||||
// HTTPInfo ...
|
||||
HTTPInfo = "%v type=http.info"
|
||||
)
|
193
spot.go
Normal file
193
spot.go
Normal file
@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type TelnetSpot struct {
|
||||
DX string
|
||||
Spotter string
|
||||
Frequency string
|
||||
Mode string
|
||||
Band string
|
||||
Time string
|
||||
DXCC string
|
||||
Comment string
|
||||
CommandNumber int
|
||||
FlexSpotNumber int
|
||||
NewDXCC bool
|
||||
NewBand bool
|
||||
NewMode bool
|
||||
CallsignWorked bool
|
||||
SpotChan chan TelnetSpot
|
||||
}
|
||||
|
||||
func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChan chan TelnetSpot, log *log.Logger) {
|
||||
match := re.FindStringSubmatch(spotRaw)
|
||||
|
||||
if len(match) != 0 {
|
||||
spot := TelnetSpot{
|
||||
DX: match[3],
|
||||
Spotter: match[1],
|
||||
Frequency: match[2],
|
||||
Mode: match[4],
|
||||
Comment: strings.Trim(match[5], " "),
|
||||
Time: match[6],
|
||||
}
|
||||
|
||||
spot.GetBand()
|
||||
spot.GuessMode()
|
||||
spot.DXCC, _ = CheckClubogDXCC(spot.DX)
|
||||
spot.CallsignWorked = false
|
||||
spot.NewBand = false
|
||||
spot.NewMode = false
|
||||
spot.NewDXCC = false
|
||||
|
||||
contactRepo := NewContactsRepository(Cfg.SQLite.SQLitePath, log)
|
||||
|
||||
defer contactRepo.db.Close()
|
||||
|
||||
contacts, _ := contactRepo.ListByCountry(spot.DXCC)
|
||||
contactsMode, _ := contactRepo.ListByCountryMode(spot.DXCC, spot.Mode)
|
||||
contactsBand, _ := contactRepo.ListByCountryBand(spot.DXCC, spot.Band)
|
||||
contactsCall, _ := contactRepo.ListByCallSign(spot.DX, spot.Band, spot.Mode)
|
||||
|
||||
if len(contacts) == 0 {
|
||||
switch spot.DXCC {
|
||||
case "997":
|
||||
spot.NewDXCC = false
|
||||
case "1000":
|
||||
spot.NewDXCC = false
|
||||
default:
|
||||
spot.NewDXCC = true
|
||||
}
|
||||
} else if len(contactsMode) == 0 {
|
||||
spot.NewMode = true
|
||||
} else if len(contactsBand) == 0 {
|
||||
spot.NewBand = true
|
||||
} else if len(contactsCall) > 0 {
|
||||
spot.CallsignWorked = true
|
||||
}
|
||||
|
||||
if spot.NewDXCC {
|
||||
log.Infof("(** New DXCC **) DX: %s - Spotter: %s - Freq: %s - Band: %s - Mode: %s - Comment: %s - Time: %s - Command: %v, FlexSpot: %v",
|
||||
spot.DX, spot.Spotter, spot.Frequency, spot.Band, spot.Mode, spot.Comment, spot.Time, spot.CommandNumber, spot.FlexSpotNumber)
|
||||
}
|
||||
|
||||
if !spot.NewDXCC && spot.NewBand && spot.NewMode {
|
||||
log.Infof("(** New Band/Mode **) DX: %s - Spotter: %s - Freq: %s - Band: %s - Mode: %s - Comment: %s - Time: %s - DXCC: %s",
|
||||
spot.DX, spot.Spotter, spot.Frequency, spot.Band, spot.Mode, spot.Comment, spot.Time, spot.DXCC)
|
||||
}
|
||||
|
||||
if !spot.NewDXCC && spot.NewBand && !spot.NewMode {
|
||||
log.Infof("(** New Band **) DX: %s - Spotter: %s - Freq: %s - Band: %s - Mode: %s - Comment: %s - Time: %s - DXCC: %s",
|
||||
spot.DX, spot.Spotter, spot.Frequency, spot.Band, spot.Mode, spot.Comment, spot.Time, spot.DXCC)
|
||||
}
|
||||
|
||||
if !spot.NewDXCC && !spot.NewBand && spot.NewMode {
|
||||
log.Infof("(** New Mode **) DX: %s - Spotter: %s - Freq: %s - Band: %s - Mode: %s - Comment: %s - Time: %s - DXCC: %s",
|
||||
spot.DX, spot.Spotter, spot.Frequency, spot.Band, spot.Mode, spot.Comment, spot.Time, spot.DXCC)
|
||||
}
|
||||
|
||||
if !spot.NewDXCC && !spot.NewBand && !spot.NewMode && spot.CallsignWorked {
|
||||
log.Infof("(** Worked **) DX: %s - Spotter: %s - Freq: %s - Band: %s - Mode: %s - Comment: %s - Time: %s - DXCC: %s",
|
||||
spot.DX, spot.Spotter, spot.Frequency, spot.Band, spot.Mode, spot.Comment, spot.Time, spot.DXCC)
|
||||
}
|
||||
|
||||
if !spot.NewDXCC && !spot.NewBand && !spot.NewMode {
|
||||
log.Infof("DX: %s - Spotter: %s - Freq: %s - Band: %s - Mode: %s - Comment: %s - Time: %s - DXCC: %s",
|
||||
spot.DX, spot.Spotter, spot.Frequency, spot.Band, spot.Mode, spot.Comment, spot.Time, spot.DXCC)
|
||||
}
|
||||
|
||||
SpotChan <- spot
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (spot *TelnetSpot) GetBand() {
|
||||
switch true {
|
||||
case strings.HasPrefix(spot.Frequency, "1.8"):
|
||||
spot.Band = "160M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "LSB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "3"):
|
||||
spot.Band = "80M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "LSB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "7"):
|
||||
spot.Band = "40M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "LSB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "10"):
|
||||
spot.Band = "30M"
|
||||
case strings.HasPrefix(spot.Frequency, "14"):
|
||||
spot.Band = "20M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "USB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "18"):
|
||||
spot.Band = "17M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "USB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "21"):
|
||||
spot.Band = "15M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "USB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "24"):
|
||||
spot.Band = "12M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "USB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "28"):
|
||||
spot.Band = "10M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "USB"
|
||||
}
|
||||
case strings.HasPrefix(spot.Frequency, "29"):
|
||||
spot.Band = "10M"
|
||||
if spot.Mode == "SSB" {
|
||||
spot.Mode = "USB"
|
||||
}
|
||||
default:
|
||||
spot.Band = "N/A"
|
||||
}
|
||||
}
|
||||
|
||||
func (spot *TelnetSpot) GuessMode() {
|
||||
if spot.Mode == "" {
|
||||
freqInt, err := strconv.ParseFloat(spot.Frequency, 32)
|
||||
if err != nil {
|
||||
fmt.Println("could not convert frequency string in float64:", err)
|
||||
}
|
||||
|
||||
switch spot.Band {
|
||||
case "160M":
|
||||
if freqInt <= 1840 && freqInt >= 1800 {
|
||||
spot.Mode = "CW"
|
||||
}
|
||||
case "40M":
|
||||
if freqInt <= 7074 && freqInt >= 7000 {
|
||||
spot.Mode = "CW"
|
||||
} else if freqInt <= 7079 && freqInt > 7074 {
|
||||
spot.Mode = "FT8"
|
||||
} else if freqInt <= 7079 && freqInt > 7074 {
|
||||
spot.Mode = "FT8"
|
||||
} else if freqInt <= 7079 && freqInt > 7074 {
|
||||
spot.Mode = "FT8"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
34
utils.go
Normal file
34
utils.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func FreqMhztoHz(freq string) string {
|
||||
frequency, err := strconv.ParseFloat(freq, 64)
|
||||
if err != nil {
|
||||
log.Println("could not convert frequency string to int", err)
|
||||
}
|
||||
|
||||
frequency = frequency / 1000
|
||||
|
||||
return strconv.FormatFloat(frequency, 'f', 6, 64)
|
||||
}
|
||||
|
||||
func FreqHztoMhz(freq string) string {
|
||||
frequency, err := strconv.ParseFloat(freq, 64)
|
||||
if err != nil {
|
||||
log.Println("could not convert frequency string to int", err)
|
||||
}
|
||||
|
||||
frequency = frequency * 1000
|
||||
|
||||
return strconv.FormatFloat(frequency, 'f', 6, 64)
|
||||
}
|
||||
|
||||
func roundFloat(val float64, precision uint) float64 {
|
||||
ratio := math.Pow(10, float64(precision))
|
||||
return math.Round(val*ratio) / ratio
|
||||
}
|
Loading…
Reference in New Issue
Block a user