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 }