diff --git a/fleprocess/adif_write.go b/fleprocess/adif_write.go index a7e3d20..5073633 100644 --- a/fleprocess/adif_write.go +++ b/fleprocess/adif_write.go @@ -93,6 +93,13 @@ func buildAdif(fullLog []LogLine, adifParams AdifParams) (adifList []string) { if logLine.MyGrid != "" { adifLine.WriteString(adifElement("MY_GRIDSQUARE", logLine.MyGrid)) } + + if logLine.MyLat != "" { + adifLine.WriteString(adifElement("MY_LAT", logLine.MyLat)) + } + if logLine.MyLon != "" { + adifLine.WriteString(adifElement("MY_LON", logLine.MyLon)) + } if logLine.MyCounty != "" { adifLine.WriteString(adifElement("MY_CNTY", logLine.MyCounty)) } diff --git a/fleprocess/adif_write_test.go b/fleprocess/adif_write_test.go index 74b84c8..782cc47 100644 --- a/fleprocess/adif_write_test.go +++ b/fleprocess/adif_write_test.go @@ -111,6 +111,7 @@ func Test_buildAdif(t *testing.T) { sampleFilledLogPOTA2 := []LogLine{ {MyCall: "ON4KJM/P", Call: "S57LC", Date: "2020-05-24", MyGrid: "JO40eu", Time: "1310", Band: "20m", Frequency: "14.045", Mode: "CW", RSTsent: "599", RSTrcvd: "599", GridLoc: "JO50", MyPOTA: "ON-00259", Operator: "ON4KJM", Nickname: "ON-00259-1"}, {MyCall: "ON4KJM/P", Call: "ON4LY", Date: "2020-05-24", MyGrid: "JO40eu", Time: "1312", Band: "20m", Mode: "CW", RSTsent: "559", RSTrcvd: "599", MyPOTA: "ON-00259", Operator: "ON4KJM", POTA: "DL-00001"}, + {MyCall: "ON4KJM/P", Call: "ON4LY", Date: "2020-05-24", MyGrid: "JO40eu", Time: "1312", Band: "20m", Mode: "CW", RSTsent: "559", RSTrcvd: "599", MyPOTA: "ON-00259", Operator: "ON4KJM", POTA: "DL-00001", MyLat: "15.1234567", MyLon: "-123.1234567"}, } expectedOutputPOTA2 := []string{ @@ -120,6 +121,7 @@ func Test_buildAdif(t *testing.T) { "", "ON4KJM/P S57LC 20200524 1310 20m CW 14.045 599 599 JO50 POTA ON-00259 ON4KJM JO40eu ON-00259-1 ", "ON4KJM/P ON4LY 20200524 1312 20m CW 559 599 POTA ON-00259 POTA DL-00001 ON4KJM JO40eu ", + "ON4KJM/P ON4LY 20200524 1312 20m CW 559 599 POTA ON-00259 POTA DL-00001 ON4KJM JO40eu 15.1234567 -123.1234567 ", } type args struct { diff --git a/fleprocess/displayLog.go b/fleprocess/displayLog.go index a96252c..e2a2faa 100644 --- a/fleprocess/displayLog.go +++ b/fleprocess/displayLog.go @@ -78,7 +78,15 @@ func SprintHeaderValues(logLine LogLine) string { if logLine.MyGrid != "" { output.WriteString("MyGrid " + logLine.MyGrid + "\n") } + + if logLine.MyLat != "" { + output.WriteString("MyLat " + logLine.MyLat + "\n") + } + if logLine.MyLon != "" { + output.WriteString("MyLon " + logLine.MyLon + "\n") + } + if logLine.MyCounty != "" { output.WriteString("MyCounty " + logLine.MyCounty + "\n") } diff --git a/fleprocess/load_file.go b/fleprocess/load_file.go index 024af7e..40aec72 100644 --- a/fleprocess/load_file.go +++ b/fleprocess/load_file.go @@ -64,6 +64,8 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL regexpHeaderMySota := regexp.MustCompile(`(?i)^mysota\s+`) regexpHeaderMyPota := regexp.MustCompile(`(?i)^mypota\s+`) regexpHeaderMyGrid := regexp.MustCompile(`(?i)^mygrid\s+`) + regexpHeaderMyLat := regexp.MustCompile(`(?i)^mylat\s+`) + regexpHeaderMyLon := regexp.MustCompile(`(?i)^mylon\s+`) regexpHeaderMyCounty := regexp.MustCompile(`(?i)^mycounty\s+`) regexpHeaderQslMsg := regexp.MustCompile(`(?i)^qslmsg\s+`) regexpHeaderNickname := regexp.MustCompile(`(?i)^nickname\s+`) @@ -74,6 +76,8 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL headerMySOTA := "" headerMyPOTA := "" headerMyGrid := "" + headerMyLat := "" + headerMyLon := "" headerMyCounty := "" headerQslMsg := "" headerNickname := "" @@ -247,6 +251,44 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL continue } + //My Lat + if regexpHeaderMyLat.MatchString(eachline) { + //Attempt to redefine value + if headerMyLat != "" { + errorLog = append(errorLog, fmt.Sprintf("Attempt to redefine MyLat at line %d", lineCount)) + continue + } + errorMsg := "" + myLatList := regexpHeaderMyLat.Split(eachline, -1) + if len(strings.TrimSpace(myLatList[1])) > 0 { + headerMyLat, errorMsg = ValidateLat(strings.TrimSpace(myLatList[1])) + if len(errorMsg) != 0 { + errorLog = append(errorLog, fmt.Sprintf("Invalid \"My Lat\" at line %d: %s (%s)", lineCount, myLatList[1], errorMsg)) + } + } + //If there is no data after the marker, we just skip the data. + continue + } + + //My Lon + if regexpHeaderMyLon.MatchString(eachline) { + //Attempt to redefine value + if headerMyLon != "" { + errorLog = append(errorLog, fmt.Sprintf("Attempt to redefine MyLon at line %d", lineCount)) + continue + } + errorMsg := "" + myLonList := regexpHeaderMyLon.Split(eachline, -1) + if len(strings.TrimSpace(myLonList[1])) > 0 { + headerMyLon, errorMsg = ValidateLon(strings.TrimSpace(myLonList[1])) + if len(errorMsg) != 0 { + errorLog = append(errorLog, fmt.Sprintf("Invalid \"My Lon\" at line %d: %s (%s)", lineCount, myLonList[1], errorMsg)) + } + } + //If there is no data after the marker, we just skip the data. + continue + } + //My County if regexpHeaderMyCounty.MatchString(eachline) { myMyCountyList := regexpHeaderMyCounty.Split(eachline, -1) @@ -295,6 +337,8 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL previousLogLine.MyPOTA = headerMyPOTA previousLogLine.MySOTA = headerMySOTA previousLogLine.MyGrid = headerMyGrid + previousLogLine.MyLat = headerMyLat + previousLogLine.MyLon = headerMyLon previousLogLine.MyCounty = headerMyCounty previousLogLine.QSLmsg = headerQslMsg //previousLogLine.QslMsg is redundant previousLogLine.Nickname = headerNickname diff --git a/fleprocess/load_file_test.go b/fleprocess/load_file_test.go index 6f2a5bd..9a284d0 100644 --- a/fleprocess/load_file_test.go +++ b/fleprocess/load_file_test.go @@ -42,6 +42,8 @@ func TestLoadFile_happyCase(t *testing.T) { dataArray = append(dataArray, "mySota on/on-001") dataArray = append(dataArray, "myPota k-0802") dataArray = append(dataArray, "myGrid jo50") + dataArray = append(dataArray, "myLat 55.5555555") + dataArray = append(dataArray, "myLon -120.01234567") dataArray = append(dataArray, "myCounty Ham County") dataArray = append(dataArray, "QslMsg This is a QSL message") dataArray = append(dataArray, " ") @@ -84,6 +86,14 @@ func TestLoadFile_happyCase(t *testing.T) { if loadedLogFile[0].MySOTA != expectedValue { t.Errorf("Not the expected MySOTA value: %s (expecting %s)", loadedLogFile[0].MySOTA, expectedValue) } + expectedValue = "55.5555555" + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + expectedValue = "-120.01234567" + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } expectedValue = "This is a QSL message" if loadedLogFile[0].QSLmsg != expectedValue { t.Errorf("Not the expected QSL Message from Header value: %s (expecting %s)", loadedLogFile[0].QSLmsg, expectedValue) @@ -386,6 +396,166 @@ func TestLoadFile_happyCase_day(t *testing.T) { os.Remove(temporaryDataFileName) } +func TestLoadFile_redefining_myLat_must_fail(t *testing.T) { + + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLat -55.55") + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + dataArray = append(dataArray, "myLat 55.55") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "-55.55" + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func TestLoadFile_redefining_myLon_must_fail(t *testing.T) { + + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLon -55.55") + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + dataArray = append(dataArray, "myLon 55.55") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "-55.55" + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func Outofrange_myLonHelper(t *testing.T, val string) { + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLon "+val) + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "*" + val + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func TestLoadFile_outofrange_high_myLon_must_fail(t *testing.T) { + Outofrange_myLonHelper(t, "180.0001") +} + +func TestLoadFile_outofrange_low_myLon_must_fail(t *testing.T) { + Outofrange_myLonHelper(t, "-180.0001") +} + +func Outofrange_myLatHelper(t *testing.T, val string) { + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLat "+val) + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "*" + val + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func TestLoadFile_outofrange_high_myLat_must_fail(t *testing.T) { + Outofrange_myLatHelper(t, "90.0001") +} + +func TestLoadFile_outofrange_low_myLat_must_fail(t *testing.T) { + Outofrange_myLatHelper(t, "-90.0001") +} + func TestLoadFile_redefining_myCall_must_fail(t *testing.T) { //Given @@ -1021,6 +1191,8 @@ func TestLoadFile_wrongData(t *testing.T) { dataArray = append(dataArray, "myWwff foobar") dataArray = append(dataArray, "mySota foobar") dataArray = append(dataArray, "myGrid foobar") + dataArray = append(dataArray, "myLat foobar") + dataArray = append(dataArray, "myLon foobar") dataArray = append(dataArray, " ") dataArray = append(dataArray, " #Log") dataArray = append(dataArray, "date 2020-05-23") @@ -1061,6 +1233,12 @@ func TestLoadFile_wrongData(t *testing.T) { if loadedLogFile[0].MyGrid != expectedValue { t.Errorf("Not the expected MyGrid value: %s (expecting %s)", loadedLogFile[0].MyGrid, expectedValue) } + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } expectedValue = "IK5ZVE" if loadedLogFile[0].Call != expectedValue { diff --git a/fleprocess/parse_line.go b/fleprocess/parse_line.go index 07ea668..cd5d309 100644 --- a/fleprocess/parse_line.go +++ b/fleprocess/parse_line.go @@ -34,6 +34,8 @@ type LogLine struct { MyPota string MySota string MyGrid string + MyLat string + MyLon string MyCounty string QslMsgFromHeader string Nickname string diff --git a/fleprocess/validate.go b/fleprocess/validate.go index fea9ab2..e7fe443 100644 --- a/fleprocess/validate.go +++ b/fleprocess/validate.go @@ -21,8 +21,33 @@ import ( "regexp" "strings" "time" + "strconv" ) +// ValidateLat checks if value is within range of +-90 degrees inclusive. +func ValidateLat(lat string) (ref, errorMsg string) { + if val, err := strconv.ParseFloat(lat, 64); err == nil { + if val <= float64(90.0) && val >= float64(-90.0) { + return lat, "" + } + } + + errorMsg = "[" + lat + "] is an invalid lat" + return "*" + lat, errorMsg +} + +// ValidateLon checks if value is within range of +-180 degrees inclusive. +func ValidateLon(lon string) (ref, errorMsg string) { + if val, err := strconv.ParseFloat(lon, 64); err == nil { + if val <= float64(180.0) && val >= float64(-180.0) { + return lon, "" + } + } + + errorMsg = "[" + lon + "] is an invalid lon" + return "*" + lon, errorMsg +} + // ValidateSota verifies whether the supplied string is a valid SOTA reference. // The syntax is: AA/NN-CCC: Association/Name-3-digit numeric Code (e.g. G/CE-001). func ValidateSota(inputStr string) (ref, errorMsg string) {