From 4bb79e92dd8a8aea7243b80b145e1a8976955c46 Mon Sep 17 00:00:00 2001 From: windyboy Date: Sat, 20 Jul 2024 18:26:17 +0800 Subject: [PATCH] refactor: Rename AFTN struct field from "Text" to "Body" --- internal/domain/aviation.go | 28 ++++++ internal/parsers/aviation_parser.go | 77 ++++++++++++++++ internal/parsers/aviation_parser_test.go | 112 +++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 internal/domain/aviation.go create mode 100644 internal/parsers/aviation_parser.go create mode 100644 internal/parsers/aviation_parser_test.go diff --git a/internal/domain/aviation.go b/internal/domain/aviation.go new file mode 100644 index 0000000..02cbda2 --- /dev/null +++ b/internal/domain/aviation.go @@ -0,0 +1,28 @@ +package domain + +/* +ZCZC TMQ1324 150631 +FF ZBTJZPZX +150630 ZBACZQZX +*/ +/* +Start Indicator: ZCZC +Message ID: TMQ1324 +Date-Time: 150631 +Priority Indicator: FF +Primary Address: ZBTJZPZX +Secondary Addresses: 150630 ZBACZQZX +Originator and Originator Date-Time: Not present in this example but handled if present. +*/ +// ParsedMessage holds the parsed data from an aviation message +type ParsedMessage struct { + StartIndicator string // Start of the message indicator + MessageID string // Unique identifier for the message + DateTime string // Date and time the message was created + PriorityIndicator string // Priority level of the message + PrimaryAddress string // Primary recipient address + SecondaryAddresses []string // Additional recipient addresses + Originator string // Sender of the message + OriginatorDateTime string // Date and time when the originator sent the message + BodyAndFooter string +} diff --git a/internal/parsers/aviation_parser.go b/internal/parsers/aviation_parser.go new file mode 100644 index 0000000..84e3cf9 --- /dev/null +++ b/internal/parsers/aviation_parser.go @@ -0,0 +1,77 @@ +package parsers + +import ( + "caatsm/internal/domain" + "strings" +) + +// ParseHeader parses the header of the message and returns a ParsedMessage struct +func ParseHeader(fullMessage string) domain.ParsedMessage { + var ( + startIndicator string + messageID string + dateTime string + priorityIndicator string + primaryAddress string + secondaryAddresses []string + originator string + originatorDateTime string + bodyAndFooter strings.Builder + headerEnded bool + ) + + fullMessage = strings.TrimSpace(fullMessage) + lines := strings.Split(fullMessage, "\n") + + for lineCounter, line := range lines { + switch lineCounter { + case 0: + if strings.HasPrefix(line, "ZCZC") { + parts := strings.Fields(line) + if len(parts) >= 3 { + startIndicator = parts[0] + messageID = parts[1] + dateTime = parts[2] + } + } + case 1: + parts := strings.Fields(line) + if len(parts) >= 2 { + priorityIndicator = parts[0] + primaryAddress = parts[1] + } + default: + if !headerEnded { + if strings.TrimSpace(line) == "." { + continue + } else if strings.HasPrefix(line, ".") { + originatorInfo := strings.Fields(line[1:]) + if len(originatorInfo) >= 2 { + originator = originatorInfo[0] + originatorDateTime = originatorInfo[1] + } + headerEnded = true + } else if strings.HasPrefix(line, "BEGIN PART") || strings.HasPrefix(line, "(") { + headerEnded = true + bodyAndFooter.WriteString(line + "\n") + } else { + secondaryAddresses = append(secondaryAddresses, line) + } + } else { + bodyAndFooter.WriteString(line + "\n") + } + } + } + + return domain.ParsedMessage{ + StartIndicator: startIndicator, + MessageID: messageID, + DateTime: dateTime, + PriorityIndicator: priorityIndicator, + PrimaryAddress: primaryAddress, + SecondaryAddresses: secondaryAddresses, + Originator: originator, + OriginatorDateTime: originatorDateTime, + BodyAndFooter: bodyAndFooter.String(), + } +} diff --git a/internal/parsers/aviation_parser_test.go b/internal/parsers/aviation_parser_test.go new file mode 100644 index 0000000..69e99ca --- /dev/null +++ b/internal/parsers/aviation_parser_test.go @@ -0,0 +1,112 @@ +package parsers + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Aviation Parser", func() { + Describe("ParseHeader", func() { + It("should parse the header correctly", func() { + message := ` + ZCZC TAF6789 160530 +QU TSNZPCA +. +QU PEKUDCA TSNUOCA TSNZPCA TSNUFCA +.TAF WSSS 160500Z 1606/1712 20010KT 9999 SCT018 + BECMG 1608/1610 24012KT 9999 SCT018 + TEMPO 1610/1612 4000 SHRA BKN012 + BECMG 1612/1614 18008KT 9999 SCT020 + +BEGIN PART 02 + +(FORECAST AMENDMENT +VALID 1606/1700 +THUNDERSTORMS EXPECTED +ALTERNATE ROUTES ADVISED) + +NNNN` + parsedHeader := ParseHeader(message) + Expect(parsedHeader.StartIndicator).To(Equal("ZCZC")) + Expect(parsedHeader.MessageID).To(Equal("TAF6789")) + Expect(parsedHeader.DateTime).To(Equal("160530")) + Expect(parsedHeader.PriorityIndicator).To(Equal("QU")) + Expect(parsedHeader.PrimaryAddress).To(Equal("TSNZPCA")) + Expect(parsedHeader.SecondaryAddresses).To(Equal([]string{"QU PEKUDCA TSNUOCA TSNZPCA TSNUFCA"})) + + }) + + It("should parse the header correctly with originator information", func() { + message := ` +ZCZC NOTAM1122 171000 +QU TSNZPCA +. +QU PEKUDCA TSNUOCA TSNZPCA TSNUFCA +.SELOZKE 170999 + +BEGIN PART 01 + +RUNWAY MAINTENANCE NOTICE. + +- MAINTENANCE MANAGER: JOHN DOE + +RUNWAY 09/27 WILL BE CLOSED FOR MAINTENANCE FROM 0800Z TO 1600Z. + +- AIRPORT OPERATIONS: SIGN . . . . . . . . . . + +WE ACKNOWLEDGE THE RUNWAY CLOSURE. + +- CONTROL TOWER: + + SIGN . . . . . . . . . . + +BEGIN PART 02 + +(ALERT MESSAGE - WEATHER WARNING +VALID 1500Z - 1800Z +SEVERE THUNDERSTORM FORECASTED +ALL DEPARTURES/ARRIVALS EXPECTED TO BE DELAYED) + +NNNN` + parsedHeader := ParseHeader(message) + Expect(parsedHeader.StartIndicator).To(Equal("ZCZC")) + Expect(parsedHeader.MessageID).To(Equal("NOTAM1122")) + Expect(parsedHeader.DateTime).To(Equal("171000")) + Expect(parsedHeader.PriorityIndicator).To(Equal("QU")) + Expect(parsedHeader.PrimaryAddress).To(Equal("TSNZPCA")) + Expect(parsedHeader.SecondaryAddresses).To(Equal([]string{"QU PEKUDCA TSNUOCA TSNZPCA TSNUFCA"})) + Expect(parsedHeader.Originator).To(Equal("SELOZKE")) + Expect(parsedHeader.OriginatorDateTime).To(Equal("170999")) + }) + + }) + + Context("with NOTAM message", func() { + It("should parse the header correctly", func() { + message := ` +ZCZC NOTAM1234 230715 +GG EDDNZEZN +. +GG EDDNYNYX +.BERLINTWR 230714 + +Q) EDMM/QOATT/IV/BO/A/000/999/4814N01120E005 +A) EDDM +B) 2307150600 C) 2307151800 +E) AERODROME CONTROL TOWER HOURS OF SERVICE + 0600-1800 DUE TO MAINTENANCE +NNNN` + + parsedMessage := ParseHeader(message) + Expect(parsedMessage.StartIndicator).To(Equal("ZCZC")) + Expect(parsedMessage.MessageID).To(Equal("NOTAM1234")) + Expect(parsedMessage.DateTime).To(Equal("230715")) + Expect(parsedMessage.PriorityIndicator).To(Equal("GG")) + Expect(parsedMessage.PrimaryAddress).To(Equal("EDDNZEZN")) + Expect(parsedMessage.SecondaryAddresses).To(Equal([]string{"GG EDDNYNYX"})) + Expect(parsedMessage.Originator).To(Equal("BERLINTWR")) + Expect(parsedMessage.OriginatorDateTime).To(Equal("230714")) + Expect(parsedMessage.BodyAndFooter).To(ContainSubstring("Q) EDMM/QOATT/IV/BO/A/000/999/4814N01120E005\nA) EDDM\nB) 2307150600 C) 2307151800\nE) AERODROME CONTROL TOWER HOURS OF SERVICE\n 0600-1800 DUE TO MAINTENANCE\nNNNN")) + }) + }) +})