added http server

This commit is contained in:
Gregory Salaun 2024-09-26 12:24:56 +07:00
parent ab17255bdc
commit eef2b4b938
19 changed files with 131415 additions and 782 deletions

Binary file not shown.

70
HTTPServer.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"html/template"
"net/http"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
)
var tmpl *template.Template
type HTTPServer struct {
router *mux.Router
Log4OMRepo Log4OMContactsRepository
Repo FlexDXClusterRepository
Log *log.Logger
}
func NewHTTPServer(cRepo Log4OMContactsRepository, fRepo FlexDXClusterRepository, log *log.Logger) *HTTPServer {
gRouter := mux.NewRouter()
return &HTTPServer{
router: gRouter,
Log4OMRepo: cRepo,
Repo: fRepo,
Log: log,
}
}
func (s *HTTPServer) SetRoutes() {
s.router.HandleFunc("/", s.Homepage)
s.router.HandleFunc("/spots", s.GetSpots).Methods("GET")
s.router.HandleFunc("/spotscount", s.GetSpotsCount).Methods("GET")
s.router.HandleFunc("/spotters", s.GetSpotters).Methods("GET")
}
func (s *HTTPServer) StartHTTPServer() {
tmpl, _ = template.ParseGlob("templates/*.html")
s.SetRoutes()
s.Log.Infof("starting HTTP server on %s:%s", Cfg.HTTPServer.Host, Cfg.HTTPServer.Port)
err := http.ListenAndServe(Cfg.HTTPServer.Host+":"+Cfg.HTTPServer.Port, s.router)
if err != nil {
s.Log.Warn("cannot start HTTP server: ", err)
}
}
func (s *HTTPServer) Homepage(w http.ResponseWriter, r *http.Request) {
err := tmpl.ExecuteTemplate(w, "home.html", nil)
if err != nil {
s.Log.Error("error executing home template: ", err)
}
}
func (s *HTTPServer) GetSpots(w http.ResponseWriter, r *http.Request) {
spots := s.Repo.GetAllSpots("25")
tmpl.ExecuteTemplate(w, "spot", spots)
}
func (s *HTTPServer) GetSpotsCount(w http.ResponseWriter, r *http.Request) {
spots := s.Repo.GetAllSpots("0")
count := len(spots)
tmpl.ExecuteTemplate(w, "spotCount", count)
}
func (s *HTTPServer) GetSpotters(w http.ResponseWriter, r *http.Request) {
spotters := s.Repo.GetSpotters()
tmpl.ExecuteTemplate(w, "spotters", spotters)
}

View File

@ -12,6 +12,7 @@ import (
) )
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})`) 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})`)
var count int = 0
type TCPClient struct { type TCPClient struct {
Login string Login string
@ -67,7 +68,7 @@ func (c *TCPClient) StartClient() {
if err != nil { if err != nil {
c.Log.Error("cannot connect to Telnet Client:", err) c.Log.Error("cannot connect to Telnet Client:", err)
} }
c.Log.Infof("connected to %s:%s", c.Address, c.Port) c.Log.Infof("connected to DX cluster %s:%s", c.Address, c.Port)
err = c.Conn.SetKeepAlive(true) err = c.Conn.SetKeepAlive(true)
if err != nil { if err != nil {
@ -77,6 +78,14 @@ func (c *TCPClient) StartClient() {
c.Reader = bufio.NewReader(c.Conn) c.Reader = bufio.NewReader(c.Conn)
c.Writer = bufio.NewWriter(c.Conn) c.Writer = bufio.NewWriter(c.Conn)
go func() {
for message := range c.TCPServer.CmdChan {
message := message + "\n"
c.Log.Infof("Received DX Command: %s", message)
c.WriteString(message)
}
}()
go c.ReadLine() go c.ReadLine()
} }
@ -107,6 +116,7 @@ func (c *TCPClient) SetFilters() {
} }
func (c *TCPClient) ReadLine() { func (c *TCPClient) ReadLine() {
for { for {
message, err := c.Reader.ReadString('\n') message, err := c.Reader.ReadString('\n')
if err != nil { if err != nil {
@ -125,6 +135,11 @@ func (c *TCPClient) ReadLine() {
// Send the spot message to TCP server // Send the spot message to TCP server
if len(c.TCPServer.Clients) > 0 { if len(c.TCPServer.Clients) > 0 {
if count == 0 {
// wait 3 seconds before sending messages to allow the client to connect
time.Sleep(time.Second * 3)
count++
}
c.MsgChan <- message c.MsgChan <- message
} }
} }
@ -139,3 +154,12 @@ func (tc *TCPClient) Write(data []byte) (n int, err error) {
return return
} }
func (tc *TCPClient) WriteString(data string) (n int, err error) {
n, err = tc.Writer.Write([]byte(data))
if err == nil {
err = tc.Writer.Flush()
}
return
}

View File

@ -92,20 +92,19 @@ func (s *TCPServer) handleConnection() {
message = strings.TrimSpace(message) message = strings.TrimSpace(message)
s.Log.Infof("Message reçu du client: %s\n", message) // if message is by then disconnect
if message == "bye" {
switch message {
case "bye":
s.Mutex.Lock() s.Mutex.Lock()
delete(s.Clients, s.Conn) delete(s.Clients, s.Conn)
s.Mutex.Unlock() s.Mutex.Unlock()
s.Log.Infof("client %s disconnected", s.Conn.RemoteAddr().String()) s.Log.Infof("client %s disconnected", s.Conn.RemoteAddr().String())
return
case "SH/DX 30":
return
default:
s.Write("cannot identify command\n")
} }
if strings.Contains(message, "DX") && message != "SH/DX 30" {
// send DX spot to the client
s.CmdChan <- message
}
} }
} }

View File

@ -38,6 +38,11 @@ type Config struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port string `yaml:"port"` Port string `yaml:"port"`
} `yaml:"telnet"` } `yaml:"telnet"`
HTTPServer struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
} `yaml:"httpserver"`
} }
func NewConfig(configPath string) error { func NewConfig(configPath string) error {

View File

@ -6,9 +6,9 @@ sqlite:
cluster: cluster:
server: dxc.k0xm.net server: dxc.k0xm.net
port: 7300 port: 7300
login: xv9q-5 login: xv9q-2
skimmer: true skimmer: true
ft8: false ft8: true
flex: flex:
ip: 10.10.10.120 ip: 10.10.10.120
spot_life: 600 spot_life: 600
@ -17,3 +17,6 @@ clublog:
telnet: telnet:
host: 0.0.0.0 host: 0.0.0.0
port: 7301 port: 7301
httpserver:
host: 0.0.0.0
port: 3000

View File

@ -20,7 +20,12 @@ type Contact struct {
Country string Country string
} }
type ContactsRepository struct { type Spotter struct {
Spotter string
NumberofSpots string
}
type Log4OMContactsRepository struct {
db *sql.DB db *sql.DB
Log *log.Logger Log *log.Logger
} }
@ -30,12 +35,12 @@ type FlexDXClusterRepository struct {
Log *log.Logger Log *log.Logger
} }
func NewContactsRepository(filePath string, log *log.Logger) *ContactsRepository { func NewLog4OMContactsRepository(filePath string, log *log.Logger) *Log4OMContactsRepository {
db, err := sql.Open("sqlite3", filePath) db, err := sql.Open("sqlite3", filePath)
if err != nil { if err != nil {
fmt.Println("Cannot open db", err) fmt.Println("Cannot open db", err)
} }
return &ContactsRepository{ return &Log4OMContactsRepository{
db: db, db: db,
Log: log} Log: log}
} }
@ -84,13 +89,15 @@ func NewFlexDXDatabase(filePath string, log *log.Logger) *FlexDXClusterRepositor
} }
} }
func (r *ContactsRepository) ListByCountry(countryID string) ([]*Contact, error) { func (r *Log4OMContactsRepository) ListByCountry(countryID string) ([]*Contact, error) {
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ?", countryID) rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ?", countryID)
if err != nil { if err != nil {
log.Error("could not query database", err) log.Error("could not query database", err)
return nil, err return nil, err
} }
defer rows.Close()
contacts := []*Contact{} contacts := []*Contact{}
for rows.Next() { for rows.Next() {
c := Contact{} c := Contact{}
@ -103,7 +110,7 @@ func (r *ContactsRepository) ListByCountry(countryID string) ([]*Contact, error)
return contacts, nil return contacts, nil
} }
func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([]*Contact, error) { func (r *Log4OMContactsRepository) ListByCountryMode(countryID string, mode string) ([]*Contact, error) {
modeUSB := "USB" modeUSB := "USB"
modeLSB := "LSB" modeLSB := "LSB"
@ -116,6 +123,8 @@ func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([
return nil, err return nil, err
} }
defer rows.Close()
contacts := []*Contact{} contacts := []*Contact{}
for rows.Next() { for rows.Next() {
c := Contact{} c := Contact{}
@ -135,6 +144,8 @@ func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([
return nil, err return nil, err
} }
defer rows.Close()
contacts := []*Contact{} contacts := []*Contact{}
for rows.Next() { for rows.Next() {
c := Contact{} c := Contact{}
@ -149,13 +160,15 @@ func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([
} }
} }
func (r *ContactsRepository) ListByCountryBand(countryID string, band string) ([]*Contact, error) { func (r *Log4OMContactsRepository) 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) rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND band = ?", countryID, band)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return nil, err return nil, err
} }
defer rows.Close()
contacts := []*Contact{} contacts := []*Contact{}
for rows.Next() { for rows.Next() {
c := Contact{} c := Contact{}
@ -168,13 +181,15 @@ func (r *ContactsRepository) ListByCountryBand(countryID string, band string) ([
return contacts, nil return contacts, nil
} }
func (r *ContactsRepository) ListByCallSign(callSign string, band string, mode string) ([]*Contact, error) { func (r *Log4OMContactsRepository) 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) 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 { if err != nil {
fmt.Println(err) fmt.Println(err)
return nil, err return nil, err
} }
defer rows.Close()
contacts := []*Contact{} contacts := []*Contact{}
for rows.Next() { for rows.Next() {
c := Contact{} c := Contact{}
@ -187,13 +202,76 @@ func (r *ContactsRepository) ListByCallSign(callSign string, band string, mode s
return contacts, nil return contacts, nil
} }
func (r *FlexDXClusterRepository) GetAllSpots(limit string) []FlexSpot {
Spots := []FlexSpot{}
var query string
if limit == "0" {
query = "SELECT * from spots ORDER BY id DESC"
} else {
query = fmt.Sprintf("SELECT * from spots ORDER BY id DESC LIMIT %s", limit)
}
rows, err := r.db.Query(query)
if err != nil {
r.Log.Error(err)
return nil
}
defer rows.Close()
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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
&s.Comment, &s.Color, &s.BackgroundColor); err != nil {
fmt.Println(err)
return nil
}
Spots = append(Spots, s)
}
return Spots
}
func (r *FlexDXClusterRepository) GetSpotters() []Spotter {
sList := []Spotter{}
rows, err := r.db.Query("select spotter, count(*) as occurences from spots group by spotter order by occurences desc, spotter limit 3")
if err != nil {
r.Log.Error(err)
return nil
}
defer rows.Close()
s := Spotter{}
for rows.Next() {
if err := rows.Scan(&s.Spotter, &s.NumberofSpots); err != nil {
fmt.Println(err)
return nil
}
sList = append(sList, s)
}
return sList
}
func (r *FlexDXClusterRepository) FindDXSameBand(spot FlexSpot) (*FlexSpot, error) { func (r *FlexDXClusterRepository) FindDXSameBand(spot FlexSpot) (*FlexSpot, error) {
rows, err := r.db.Query("SELECT * from spots WHERE dx = ? AND band = ?", spot.DX, spot.Band) rows, err := r.db.Query("SELECT * from spots WHERE dx = ? AND band = ?", spot.DX, spot.Band)
if err != nil { if err != nil {
fmt.Println(err) r.Log.Error(err)
return nil, err return nil, err
} }
defer rows.Close()
s := FlexSpot{} s := FlexSpot{}
for rows.Next() { 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority, 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
@ -219,8 +297,14 @@ func (r *FlexDXClusterRepository) CreateSpot(spot FlexSpot) {
} }
func (r *FlexDXClusterRepository) UpdateSpotSameBand(spot FlexSpot) { func (r *FlexDXClusterRepository) UpdateSpotSameBand(spot FlexSpot) error {
return _, err := r.db.Exec(`UPDATE spots SET commandNumber = ?, DX = ?, freqMhz = ?, freqHz = ?, band = ?, mode = ?, spotter = ?, flexMode = ?, source = ?, time = ?, timestamp = ?, lifeTime = ?, priority = ?, comment = ?, color = ?, backgroundColor = ? WHERE DX = ? AND band = ?`,
spot.CommandNumber, spot.DX, spot.FrequencyMhz, spot.FrequencyHz, spot.Band, spot.Mode, spot.SpotterCallsign, spot.FlexMode, spot.Source, spot.UTCTime, spot.TimeStamp, spot.LifeTime, spot.Priority, spot.Comment, spot.Color, spot.BackgroundColor, spot.DX, spot.Band)
if err != nil {
r.Log.Errorf("could not update database: %s", err)
return err
}
return nil
} }
func (r *FlexDXClusterRepository) FindSpotByCommandNumber(commandNumber string) (*FlexSpot, error) { func (r *FlexDXClusterRepository) FindSpotByCommandNumber(commandNumber string) (*FlexSpot, error) {
@ -230,6 +314,8 @@ func (r *FlexDXClusterRepository) FindSpotByCommandNumber(commandNumber string)
return nil, err return nil, err
} }
defer rows.Close()
s := FlexSpot{} s := FlexSpot{}
for rows.Next() { 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority, 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
@ -248,6 +334,8 @@ func (r *FlexDXClusterRepository) FindSpotByFlexSpotNumber(spotNumber string) (*
return nil, err return nil, err
} }
defer rows.Close()
s := FlexSpot{} s := FlexSpot{}
for rows.Next() { 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority, 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
@ -266,6 +354,8 @@ func (r *FlexDXClusterRepository) UpdateFlexSpotNumberByID(flexSpotNumber string
r.Log.Errorf("could not update database: %s", err) r.Log.Errorf("could not update database: %s", err)
} }
defer rows.Close()
s := FlexSpot{} s := FlexSpot{}
for rows.Next() { 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority, 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.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,

Binary file not shown.

View File

@ -78,7 +78,7 @@ func (fc *FlexClient) StartFlexClient() {
fc.Log.Error("could not connect to flex radio, exiting...", err) fc.Log.Error("could not connect to flex radio, exiting...", err)
os.Exit(1) os.Exit(1)
} }
fc.Log.Infof("connected to %s:%s", fc.Address, fc.Port) fc.Log.Infof("connected to flex radio at %s:%s", fc.Address, fc.Port)
go func() { go func() {
for message := range fc.SpotChan { for message := range fc.SpotChan {
@ -103,6 +103,8 @@ func (fc *FlexClient) StartFlexClient() {
clrSpotAllCmd := fmt.Sprintf("C%v|spot clear", CommandNumber) clrSpotAllCmd := fmt.Sprintf("C%v|spot clear", CommandNumber)
fc.Write(clrSpotAllCmd) fc.Write(clrSpotAllCmd)
CommandNumber++ CommandNumber++
fc.Log.Info("Subscribed to spot on FlexRadio and Deleted all spots from panadapter")
} }
func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) { func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
@ -151,8 +153,14 @@ func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
flexSpot.Priority = "5" 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 = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "]" flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "]"
srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot) srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot)
if err != nil { if err != nil {
@ -163,29 +171,29 @@ func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
fc.Repo.CreateSpot(flexSpot) 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, 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) flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
CommandNumber++
fc.SendSpot(stringSpot) fc.SendSpot(stringSpot)
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band && srcFlexSpot.FrequencyMhz != flexSpot.FrequencyMhz { } else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band && srcFlexSpot.FrequencyMhz != flexSpot.FrequencyMhz {
fc.Repo.UpdateSpotSameBand(flexSpot) 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, 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) flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
CommandNumber++ CommandNumber++
stringDelete := fmt.Sprintf("C%v|spot remove %v", CommandNumber, srcFlexSpot.FlexSpotNumber) fc.SendSpot(stringSpot)
fc.DeleteAndSendSpot(stringSpot, stringDelete)
}
} 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++ CommandNumber++
fc.SendSpot(stringSpot)
}
} }
func (fc *FlexClient) SendSpot(stringSpot string) { func (fc *FlexClient) SendSpot(stringSpot string) {
fc.Write(stringSpot) fc.Write(stringSpot)
} }
func (fc *FlexClient) DeleteAndSendSpot(stringSpot string, deleteSpot string) {
fc.Write(deleteSpot)
fc.Write(stringSpot)
}
func (fc *FlexClient) ReadLine() { func (fc *FlexClient) ReadLine() {
for { for {
@ -195,10 +203,8 @@ func (fc *FlexClient) ReadLine() {
os.Exit(1) os.Exit(1)
} }
// msgRaw := strings.TrimSpace(message) // fc.Log.Info(message)
// fc.Log.Info(msgRaw)
// Response when spot is added
regRespSpot := *regexp.MustCompile(`R(\d+)\|0\|(\d+)\n`) regRespSpot := *regexp.MustCompile(`R(\d+)\|0\|(\d+)\n`)
respSpot := regRespSpot.FindStringSubmatch(message) respSpot := regRespSpot.FindStringSubmatch(message)
@ -232,8 +238,9 @@ func (fc *FlexClient) ReadLine() {
respDelete := regSpotDeleted.FindStringSubmatch(message) respDelete := regSpotDeleted.FindStringSubmatch(message)
if len(respDelete) > 0 { if len(respDelete) > 0 {
spot, _ := fc.Repo.FindSpotByFlexSpotNumber(respDelete[1])
fc.Repo.DeleteSpotByFlexSpotNumber(respDelete[1]) fc.Repo.DeleteSpotByFlexSpotNumber(respDelete[1])
fc.Log.Infof("deleted spot %v from database", 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])
} }
} }
} }

131789
flexradio.log

File diff suppressed because it is too large Load Diff

1
go.mod
View File

@ -9,6 +9,7 @@ require (
) )
require ( require (
github.com/gorilla/mux v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect

2
go.sum
View File

@ -1,6 +1,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 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-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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=

View File

@ -15,6 +15,7 @@ func NewLog() *log.Logger {
} }
w := io.MultiWriter(os.Stdout, f) w := io.MultiWriter(os.Stdout, f)
// w := io.Writer(os.Stdout)
l := &log.Logger{ l := &log.Logger{
Out: w, Out: w,

35
main.go
View File

@ -3,6 +3,9 @@ package main
import ( import (
"flag" "flag"
"log" "log"
"os"
"os/signal"
"syscall"
"git.rouggy.com/rouggy/FlexDXCluster/logger" "git.rouggy.com/rouggy/FlexDXCluster/logger"
) )
@ -48,16 +51,42 @@ func main() {
DeleteDatabase("./flex.sqlite", log) DeleteDatabase("./flex.sqlite", log)
repo := NewFlexDXDatabase("flex.sqlite", log) fRepo := NewFlexDXDatabase("flex.sqlite", log)
defer fRepo.db.Close()
cRepo := NewLog4OMContactsRepository(Cfg.SQLite.SQLitePath, log)
defer cRepo.db.Close()
TCPServer := NewTCPServer(Cfg.Telnet.Host, Cfg.Telnet.Port, log) TCPServer := NewTCPServer(Cfg.Telnet.Host, Cfg.Telnet.Port, log)
FlexClient := NewFlexClient(*repo, *TCPServer, log) FlexClient := NewFlexClient(*fRepo, *TCPServer, log)
TCPClient := NewTCPClient(*Cfg, TCPServer, FlexClient, log) TCPClient := NewTCPClient(*Cfg, TCPServer, FlexClient, log)
HTTPServer := NewHTTPServer(*cRepo, *fRepo, log)
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
go FlexClient.StartFlexClient() go FlexClient.StartFlexClient()
go TCPClient.StartClient() go TCPClient.StartClient()
go TCPServer.StartServer() go TCPServer.StartServer()
select {} go HTTPServer.StartHTTPServer()
for sig := range sigCh {
log.Infof("received signal: %v, shutting down TCP Client.", sig)
TCPClient.Close()
if err := fRepo.db.Close(); err != nil {
log.Error("failed to close the database connection properly")
os.Exit(1)
}
if err := cRepo.db.Close(); err != nil {
log.Error("failed to close Log4OM database connection properly")
os.Exit(1)
}
os.Exit(0)
}
} }

View File

@ -25,7 +25,6 @@ type TelnetSpot struct {
NewBand bool NewBand bool
NewMode bool NewMode bool
CallsignWorked bool CallsignWorked bool
SpotChan chan TelnetSpot
} }
func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChan chan TelnetSpot, log *log.Logger) { func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChan chan TelnetSpot, log *log.Logger) {
@ -49,7 +48,7 @@ func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChan chan TelnetSp
spot.NewMode = false spot.NewMode = false
spot.NewDXCC = false spot.NewDXCC = false
contactRepo := NewContactsRepository(Cfg.SQLite.SQLitePath, log) contactRepo := NewLog4OMContactsRepository(Cfg.SQLite.SQLitePath, log)
defer contactRepo.db.Close() defer contactRepo.db.Close()

43
templates/home.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="theme-color" content="#18181B">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
<title>FlexDXCluster DashBoard</title>
<style>
.card {
display:inline-block;
margin-right:25px!important;
}
.card-deck {
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
</style>
</head>
<body>
<div class="container" style="width:1000px; margin: 0 auto;">
<div class="card-deck">
<div class="card shadow p-3 mb-5 bg-body rounded" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Spots</h5>
<h6 class="card-subtitle mb-2 text-muted">Current number of spots</h6>
<div class="card-text fs-1 fw-bold text-center" id="spotCount" hx-get="/spotscount" hx-trigger="every 1s" hx-swap="innerHTML"></div>
</div>
</div>
<div class="card shadow p-3 mb-5 bg-body rounded" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Top Spotters</h5>
<div id="spotters" hx-get="/spotters" hx-trigger="every 1s" hx-swap="innerHTML"><p class="card-text"></p></div>
</div>
</div>
</div>
</div>
<div id="spot" style="width:1000px; margin:0 auto;" hx-get="/spots" hx-trigger="every 1s" hx-swap="innerHTML"></div>
</div>
</body>
</html>

31
templates/spot.html Normal file
View File

@ -0,0 +1,31 @@
{{define "spot"}}
<table class="table table-sm table-bordered shadow p-3 mb-5 bg-body rounded">
<thead class="table-light">
<tr>
<th scope="col">DX</th>
<th scope="col">Spotter</th>
<th scope="col">Freq</th>
<th scope="col">Band</th>
<th scope="col">Mode</th>
<th scope="col">UTC Time</th>
<th scope="col">Comment</th>
</tr>
</thead>
<tbody>
{{ range .}}
<tr>
<td class="align-middle">{{ .DX }}</td>
<td class="align-middle">{{ .SpotterCallsign }}</td>
<td class="align-middle">{{ .FrequencyMhz }}</td>
<td class="align-middle">{{ .Band }}</td>
<td class="align-middle">{{ .Mode }}</td>
<td class="align-middle">{{ .UTCTime }}</td>
<td class="align-middle">{{ .Comment }}</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}

5
templates/spotCount.html Normal file
View File

@ -0,0 +1,5 @@
{{ define "spotCount" }}
{{ . }}
{{ end }}

9
templates/spotters.html Normal file
View File

@ -0,0 +1,9 @@
{{ define "spotters" }}
{{ range.}}
<span class="fw-bold">{{ .Spotter }}</span>: <span>{{ .NumberofSpots }} spots</span><br>
{{ end }}
{{ end }}