1
0
Fork 0
mirror of https://github.com/on4kjm/FLEcli.git synced 2025-02-12 10:32:29 +01:00
FLEcli/fleprocess/parse_line.go

413 lines
11 KiB
Go
Raw Normal View History

2020-07-29 08:22:13 +02:00
package fleprocess
2020-06-25 21:05:54 +02:00
2020-06-09 21:50:39 +02:00
/*
Copyright © 2020 Jean-Marc Meessen, ON4KJM <on4kjm@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import (
2020-06-23 22:30:59 +02:00
"fmt"
2020-06-09 21:50:39 +02:00
"regexp"
2020-06-23 22:30:59 +02:00
"strconv"
2020-06-09 21:50:39 +02:00
"strings"
)
// LogLine is used to store all the data of a single log line
type LogLine struct {
2020-07-12 22:33:01 +02:00
Date string
MyCall string
Operator string
MyWWFF string
MyPOTA string
2020-07-12 22:33:01 +02:00
MySOTA string
MyPota string
MySota string
2020-09-04 21:42:15 +02:00
MyGrid string
MyLat string
MyLon string
MyCounty string
2020-07-12 22:33:01 +02:00
QslMsgFromHeader string
Nickname string
Mode string
ModeType string
Band string
BandLowerLimit float64
BandUpperLimit float64
Frequency string
Time string
ActualTime string //time actually recorded in FLE
Call string
Comment string
QSLmsg string
OMname string
GridLoc string
RSTsent string
RSTrcvd string
WWFF string
POTA string
2020-07-12 22:33:01 +02:00
SOTA string
2020-06-09 21:50:39 +02:00
}
var regexpIsFullTime = regexp.MustCompile(`^[0-2]{1}[0-9]{3}$`)
var regexpIsTimePart = regexp.MustCompile(`^[0-5]{1}[0-9]{1}$|^[1-9]{1}$`)
var regexpIsOMname = regexp.MustCompile(`^@`)
var regexpIsGridLoc = regexp.MustCompile(`^#`)
var regexpIsRst = regexp.MustCompile(`^[\d]{1,3}$`)
var regexpIsFreq = regexp.MustCompile(`^[\d]+\.[\d]+$`)
var regexpIsSotaKeyWord = regexp.MustCompile(`(?i)^sota$`)
var regexpIsWwffKeyWord = regexp.MustCompile(`(?i)^wwff$`)
var regexpIsPotaKeyWord = regexp.MustCompile(`(?i)^pota$`)
var regexpDatePattern = regexp.MustCompile(`^(\d{2}|\d{4})[-/ .]\d{1,2}[-/ .]\d{1,2}$`)
var regexpIsDateKeyWord = regexp.MustCompile(`(?i)^date$`)
var regexpDayIncrementPattern = regexp.MustCompile(`^\+*$`)
var regexpIsDayKeyword = regexp.MustCompile(`(?i)^day$`)
var regexpKhzPartOfQrg = regexp.MustCompile(`\.\d+`)
2020-06-09 21:50:39 +02:00
// ParseLine cuts a FLE line into useful bits
2020-06-25 21:05:54 +02:00
func ParseLine(inputStr string, previousLine LogLine) (logLine LogLine, errorMsg string) {
2020-06-09 21:50:39 +02:00
//TODO: input null protection?
2020-06-13 21:51:53 +02:00
//Flag telling that we are processing data to the right of the callsign
2020-06-20 14:49:18 +02:00
isRightOfCall := false
2020-06-13 21:51:53 +02:00
//Flag used to know if we are parsing the Sent RST (first) or received RST (second)
haveSentRST := false
2020-06-12 21:15:19 +02:00
//TODO: Make something more intelligent
2020-06-20 14:49:18 +02:00
//TODO: What happens if we have partial lines
2020-06-13 21:51:53 +02:00
previousLine.Call = ""
previousLine.RSTsent = ""
previousLine.RSTrcvd = ""
previousLine.SOTA = ""
previousLine.POTA = ""
previousLine.WWFF = ""
previousLine.OMname = ""
previousLine.GridLoc = ""
previousLine.Comment = ""
2020-07-10 22:49:28 +02:00
previousLine.ActualTime = ""
2020-06-09 21:50:39 +02:00
logLine = previousLine
2020-06-10 22:58:47 +02:00
//TODO: what happens when we have <> or when there are multiple comments
2020-06-11 09:11:04 +02:00
//TODO: Refactor this! it is ugly
2020-06-25 21:05:54 +02:00
comment, inputStr := getBraketedData(inputStr, COMMENT)
2020-06-10 22:58:47 +02:00
if comment != "" {
logLine.Comment = comment
}
2020-06-25 21:05:54 +02:00
QSLmsg, inputStr := getBraketedData(inputStr, QSL)
2020-06-11 22:44:22 +02:00
if QSLmsg != "" {
logLine.QSLmsg = QSLmsg
2020-06-11 09:11:04 +02:00
}
2020-06-09 21:50:39 +02:00
elements := strings.Fields(inputStr)
for _, element := range elements {
// Is it a mode?
2020-06-25 21:05:54 +02:00
if lookupMode(strings.ToUpper(element)) {
2020-06-09 21:50:39 +02:00
logLine.Mode = strings.ToUpper(element)
//TODO: improve this: what if the band is at the end of the line
2020-06-13 21:51:53 +02:00
// Set the default RST depending of the mode
if (logLine.RSTsent == "") || (logLine.RSTrcvd == "") {
// get default RST and Mode category
modeType, defaultReport := getDefaultReport(logLine.Mode)
logLine.ModeType = modeType
logLine.RSTsent = defaultReport
logLine.RSTrcvd = defaultReport
2020-06-13 21:51:53 +02:00
} else {
errorMsg = errorMsg + "Double definitiion of RST"
}
2020-06-09 21:50:39 +02:00
continue
}
2020-09-10 22:09:59 +02:00
//Date?
if regexpDatePattern.MatchString(element) {
//We probably have a date, let's normalize it
errorTxt := ""
normalizedDate := ""
normalizedDate, errorTxt = NormalizeDate(element)
if len(errorTxt) != 0 {
logLine.Date = normalizedDate
errorMsg = errorMsg + fmt.Sprintf("Invalid Date: %s (%s)", element, errorTxt)
} else {
logLine.Date, errorTxt = ValidateDate(normalizedDate)
if len(errorTxt) != 0 {
errorMsg = errorMsg + fmt.Sprintf("Error %s", errorTxt)
}
}
continue
}
// The date keyword is not really useful, skip it
if regexpIsDateKeyWord.MatchString(element) {
continue
}
//Skip the "day" keyword
if regexpIsDayKeyword.MatchString(element) {
continue
}
//Scan the + part
if regexpDayIncrementPattern.MatchString(element) {
increment := len(element)
newDate, dateError := IncrementDate(logLine.Date, increment)
if dateError != "" {
errorMsg = errorMsg + dateError
2020-09-10 22:09:59 +02:00
}
logLine.Date = newDate
continue
}
2020-06-09 21:50:39 +02:00
// Is it a band?
2020-07-14 21:39:31 +02:00
isBandElement, bandLowerLimit, bandUpperLimit, _ := IsBand(element)
2020-06-20 20:38:09 +02:00
if isBandElement {
logLine.Band = strings.ToLower(element)
logLine.BandLowerLimit = bandLowerLimit
logLine.BandUpperLimit = bandUpperLimit
//As a new band is defined, we reset the stored frequency (from previous lines)
// This assumes that the band is defined before frequency
logLine.Frequency = ""
2020-06-09 21:50:39 +02:00
continue
}
2020-06-23 14:03:23 +02:00
// Is it a Frequency?
if regexpIsFreq.MatchString(element) {
khzPart := regexpKhzPartOfQrg.FindStringSubmatch(element)
2020-06-23 22:30:59 +02:00
var qrg float64
2020-06-25 21:05:54 +02:00
qrg, _ = strconv.ParseFloat(element, 32)
if (logLine.BandLowerLimit != 0.0) && (logLine.BandUpperLimit != 0.0) {
2020-06-23 22:30:59 +02:00
if (qrg >= logLine.BandLowerLimit) && (qrg <= logLine.BandUpperLimit) {
//Increase precision to half Khz if data is available
if len(khzPart[0]) > 4 {
//The "." is part of the returned string
logLine.Frequency = fmt.Sprintf("%.4f", qrg)
} else {
logLine.Frequency = fmt.Sprintf("%.3f", qrg)
}
2020-06-23 22:30:59 +02:00
} else {
logLine.Frequency = ""
errorMsg = errorMsg + "Frequency [" + element + "] is invalid for " + logLine.Band + " band."
2020-06-23 22:30:59 +02:00
}
} else {
errorMsg = errorMsg + "Unable to load frequency [" + element + "]: no band defined for that frequency."
2020-06-23 22:30:59 +02:00
}
2020-06-23 14:03:23 +02:00
continue
}
2020-06-11 22:44:22 +02:00
// Is it a call sign ?
if validCallRegexp.MatchString(strings.ToUpper(element)) {
//If it starts with "#",it is a grid definition and not a call
2022-03-15 21:16:53 +01:00
//If the potential callsign contains a dash, it is a Sota reference
if (element[0] != '#') && (!strings.Contains(element, "-")) {
callErrorMsg := ""
logLine.Call, callErrorMsg = ValidateCall(element)
errorMsg = errorMsg + callErrorMsg
isRightOfCall = true
continue
}
2020-06-11 22:44:22 +02:00
}
2020-06-12 21:15:19 +02:00
// Is it a "full" time ?
if !isRightOfCall {
2020-06-20 14:49:18 +02:00
if regexpIsFullTime.MatchString(element) {
2020-06-12 21:15:19 +02:00
logLine.Time = element
2020-07-10 22:49:28 +02:00
logLine.ActualTime = element
2020-06-20 14:49:18 +02:00
continue
}
// Is it a partial time ?
if regexpIsTimePart.MatchString(element) {
if logLine.Time == "" {
logLine.Time = element
2020-07-10 22:49:28 +02:00
logLine.ActualTime = element
2020-06-20 14:49:18 +02:00
} else {
goodPart := logLine.Time[:len(logLine.Time)-len(element)]
logLine.Time = goodPart + element
2020-07-10 22:49:28 +02:00
logLine.ActualTime = goodPart + element
2020-06-20 14:49:18 +02:00
}
continue
2020-06-12 21:15:19 +02:00
}
}
// Is it the OM's name (starting with "@")
if regexpIsOMname.MatchString(element) {
2020-06-25 21:05:54 +02:00
logLine.OMname = strings.TrimLeft(element, "@")
continue
}
// Is it the Grid Locator (starting with "#")
if regexpIsGridLoc.MatchString(element) {
2020-09-04 21:42:15 +02:00
grid := strings.TrimLeft(element, "#")
cleanGrid, callErrorMsg := ValidateGridLocator(grid)
logLine.GridLoc = cleanGrid
errorMsg = errorMsg + callErrorMsg
continue
}
2020-06-12 21:15:19 +02:00
2020-06-20 14:49:18 +02:00
if isRightOfCall {
//This is probably a RST
if regexpIsRst.MatchString(element) {
workRST := ""
switch len(element) {
case 1:
if logLine.ModeType == "CW" {
workRST = "5" + element + "9"
2020-06-25 21:05:54 +02:00
} else {
if logLine.ModeType == "PHONE" {
workRST = "5" + element
2020-06-25 21:05:54 +02:00
}
}
case 2:
if logLine.ModeType == "CW" {
workRST = element + "9"
2020-06-25 21:05:54 +02:00
} else {
if logLine.ModeType == "PHONE" {
workRST = element
2020-06-25 21:05:54 +02:00
}
}
case 3:
if logLine.ModeType == "CW" {
workRST = element
} else {
workRST = "*" + element
errorMsg = errorMsg + "Invalid report [" + element + "] for " + logLine.ModeType + " mode."
}
}
if haveSentRST {
logLine.RSTrcvd = workRST
} else {
logLine.RSTsent = workRST
haveSentRST = true
}
continue
}
2020-08-30 17:39:52 +02:00
// If the "wwff" keyword is used, skip it
if regexpIsWwffKeyWord.MatchString(element) {
// this keyword is not requiered anymore with FLE 3 and doesn't add any value
continue
}
// Is it a "WWFF to WWFF" reference?
2020-06-25 21:05:54 +02:00
workRef, wwffErr := ValidateWwff(element)
if wwffErr == "" {
logLine.WWFF = workRef
continue
}
// If the "pota" keyword is used, skip it
if regexpIsPotaKeyWord.MatchString(element) {
// this keyword is not requiered anymore with FLE 3 and doesn't add any value
continue
}
// Is it a "POTA to POTA" reference?
workRef, potaErr := ValidatePota(element)
if potaErr == "" {
logLine.POTA = workRef
continue
}
// If the "sota" keyword is used, skip it
if regexpIsSotaKeyWord.MatchString(element) {
2020-08-30 17:39:52 +02:00
// this keyword is not requiered anymore with FLE 3 and doesn't add any value
continue
2020-07-29 08:22:13 +02:00
}
// Is it a Summit to Summit (sota) reference?
2020-06-25 21:05:54 +02:00
workRef, sotaErr := ValidateSota(element)
if sotaErr == "" {
logLine.SOTA = workRef
continue
}
2020-06-20 14:49:18 +02:00
}
2020-06-09 21:50:39 +02:00
//If we come here, we could not make sense of what we found
2020-09-10 22:09:59 +02:00
errorMsg = errorMsg + "Unable to make sense of [" + element + "]. "
2020-06-09 21:50:39 +02:00
}
//If no report is present, let's fill it with mode default
if logLine.RSTsent == "" {
_, logLine.RSTsent = getDefaultReport(logLine.Mode)
}
if logLine.RSTrcvd == "" {
_, logLine.RSTrcvd = getDefaultReport(logLine.Mode)
}
2020-06-09 21:50:39 +02:00
2020-06-23 22:30:59 +02:00
//For debug purposes
//fmt.Println("\n", SprintLogRecord(logLine))
2020-06-09 21:50:39 +02:00
return logLine, errorMsg
}
func lookupMode(lookup string) bool {
switch lookup {
case
2020-06-25 21:05:54 +02:00
"CW",
"SSB",
2020-06-09 21:50:39 +02:00
"AM",
"FM",
2020-06-25 21:05:54 +02:00
"RTTY",
2020-06-09 21:50:39 +02:00
"FT8",
"PSK",
"JT65",
"JT9",
"FT4",
"JS8",
"ARDOP",
"ATV",
"C4FM",
"CHIP",
"CLO",
"CONTESTI",
"DIGITALVOICE",
"DOMINO",
"DSTAR",
"FAX",
"FSK441",
"HELL",
"ISCAT",
"JT4",
"JT6M",
"JT44",
"MFSK",
"MSK144",
"MT63",
"OLIVIA",
"OPERA",
"PAC",
"PAX",
"PKT",
"PSK2K",
"Q15",
"QRA64",
"ROS",
"RTTYM",
"SSTV",
"T10",
"THOR",
"THRB",
"TOR",
"V4",
"VOI",
"WINMOR",
2020-06-25 21:05:54 +02:00
"WSPR":
2020-06-09 21:50:39 +02:00
return true
}
return false
}