From d4eabd42e46dc28ca3ef7530266d0cb26bd6f9ed Mon Sep 17 00:00:00 2001 From: windyboy Date: Sat, 20 Jul 2024 12:37:18 +0800 Subject: [PATCH] refactor: Rename AFTN struct field from "Text" to "Body" The code changes rename the "Text" field in the AFTN struct to "Body" to improve clarity and consistency. This change aligns with the naming convention used in other parts of the codebase and enhances code readability. --- internal/domain/aftn.go | 2 +- internal/domain/aftn_test.go | 4 +- internal/domain/sita.go | 114 ++++++++++++++++++ internal/parsers/aftn_parser.go | 10 +- internal/parsers/sita_parser.go | 207 ++++++++++++++++++++++++++++++++ 5 files changed, 329 insertions(+), 8 deletions(-) create mode 100644 internal/domain/sita.go create mode 100644 internal/parsers/sita_parser.go diff --git a/internal/domain/aftn.go b/internal/domain/aftn.go index 28114c0..9734ffe 100644 --- a/internal/domain/aftn.go +++ b/internal/domain/aftn.go @@ -11,7 +11,7 @@ type AFTN struct { Header Header `json:"header"` // 报文头部信息 PriorityAndSender PriorityAndSender `json:"priority_and_sender"` // 优先级和发送地址信息 TimeAndReceiver TimeAndReceiver `json:"time_and_receiver"` // 时间和接收地址信息 - Text string `json:"text"` // 报文内容 + Body string `json:"body"` // 报文内容 ReceivedTime time.Time `json:"received_time"` // 收报时间,表示电报接收到的时间 Category string `json:"category"` BodyData interface{} `json:"body_data"` diff --git a/internal/domain/aftn_test.go b/internal/domain/aftn_test.go index 35cf1f5..596ac2e 100644 --- a/internal/domain/aftn_test.go +++ b/internal/domain/aftn_test.go @@ -26,7 +26,7 @@ var _ = Describe("AFTN", func() { Time: "151524", Receiver: "ZGGGZPZX", }, - Text: "Test message", + Body: "Test message", ReceivedTime: time.Now(), Category: "Test", BodyData: nil, @@ -45,7 +45,7 @@ var _ = Describe("AFTN", func() { Expect(unmarshalled.Header).To(Equal(original.Header)) Expect(unmarshalled.PriorityAndSender).To(Equal(original.PriorityAndSender)) Expect(unmarshalled.TimeAndReceiver).To(Equal(original.TimeAndReceiver)) - Expect(unmarshalled.Text).To(Equal(original.Text)) + Expect(unmarshalled.Body).To(Equal(original.Body)) Expect(unmarshalled.Category).To(Equal(original.Category)) }) }) diff --git a/internal/domain/sita.go b/internal/domain/sita.go new file mode 100644 index 0000000..387f638 --- /dev/null +++ b/internal/domain/sita.go @@ -0,0 +1,114 @@ +package domain + +import ( + "caatsm/pkg/utils" + "fmt" + "time" +) + +/* +QU TSNZPCA + +.HAKUOHU 151234 + +DISPATCH RELEASE: + +HU7670/15MAY ETD1440 B2113/B733 + +DEP:TSN/ALTN:NIL + +ROUTE ALTN:WUH KWL + +DEST:HAK/ALTN:NNG SYX + +FLT RULE:IFR + +TRIP FUEL:9187KGS/20254LBS + +TTL FUEL:13600KGS/29983LBS + +CREW:YANG XIAOHUI/YANG ZHENGYIN + +CREW NUMBER:2/4 + +SI:CFP AND CAUTION:MXSH 8/FIR + +TEL:0898-65756523 + +FAX:0898-65751587 + +DSP SIGN:WUKEYONG + +PIC SINGN: + +(FPL-CHH7670-IS + +-B733/M-SDHIRW/S + +-ZBTJ1440 + +-M074S0980 CG A326 VYK A461 LKO R343 LBN/M074S0950 J427 BHY + + W70 NYB + +-ZJHK0323 ZGNN ZJSY + +-EET/ZHWH0038 ZGZU0144 ZJSA0307 + + REG/B2113 SEL/DGEH + + RMK/ACAS EQPT) + +NNNN + + +*/ + +// SITA defines the structure of a SITA telegram +type SITA struct { + Header SITAHeader `json:"header"` // Header information of the telegram + PriorityAndSender PrioritySender `json:"priority_sender"` // Priority and sender information + TimeAndReceiver TimeReceiver `json:"time_receiver"` // Time and receiver information + Text string `json:"text"` // Content of the telegram + ReceivedTime time.Time `json:"received_time"` // Time the telegram was received + Category string `json:"category"` // Category of the telegram + BodyData interface{} `json:"body_data"` // Additional body data +} + +// SITAHeader defines the header of a SITA telegram +type SITAHeader struct { + StartSignal string `json:"start_signal"` // Start signal indicating the beginning of the telegram + SendID string `json:"send_id"` // Sending ID uniquely identifying the telegram + SendTime string `json:"send_time"` // Sending time in the format DDHHMM +} + +// PrioritySender defines priority and sender address +type PrioritySender struct { + Priority string `json:"priority"` // Priority level + Sender string `json:"sender"` // Sending address +} + +// TimeReceiver defines time and receiver address +type TimeReceiver struct { + Time string `json:"time"` // Time of the telegram + Receiver string `json:"receiver"` // Receiving address +} + +func (h *SITAHeader) Validate() error { + // Validate SendTime format (e.g., DDHHMM) + if len(h.SendTime) != 6 { + err := "invalid send_time format" + + utils.Logger.Error(err) + return fmt.Errorf(err) + } + return nil +} + +func (s *SITA) Validate() error { + if err := s.Header.Validate(); err != nil { + return err + } + // Add more validation as needed + return nil +} diff --git a/internal/parsers/aftn_parser.go b/internal/parsers/aftn_parser.go index f3ea109..6a4e7d1 100644 --- a/internal/parsers/aftn_parser.go +++ b/internal/parsers/aftn_parser.go @@ -60,12 +60,12 @@ func (p *AFTNParser) Parse(rawMessage string) (*domain.AFTN, error) { return nil, err } - text, bodyType, err := parseTextInfo(strings.Join(lines[3:], "\n")) + body, category, err := extractBodyAndCategory(strings.Join(lines[3:], "\n")) if err != nil { return nil, err } - bodyData, err := p.extractBodyData(text) + bodyData, err := p.extractBodyData(body) if err != nil { return nil, err } @@ -79,8 +79,8 @@ func (p *AFTNParser) Parse(rawMessage string) (*domain.AFTN, error) { Header: header, PriorityAndSender: priorityAndSender, TimeAndReceiver: timeAndReceiver, - Text: text, - Category: bodyType, + Body: body, + Category: category, BodyData: aftn, ReceivedTime: time.Now(), }, nil @@ -124,7 +124,7 @@ func parseTimeAndReceiver(line string) (domain.TimeAndReceiver, error) { } // parseTextInfo parses the text and extracts the body type from an AFTN message. -func parseTextInfo(text string) (string, string, error) { +func extractBodyAndCategory(text string) (string, string, error) { match := textPattern.FindStringSubmatch(text) if len(match) > 1 { return match[0], match[1], nil diff --git a/internal/parsers/sita_parser.go b/internal/parsers/sita_parser.go new file mode 100644 index 0000000..73a5c8e --- /dev/null +++ b/internal/parsers/sita_parser.go @@ -0,0 +1,207 @@ +package parsers + +import ( + "caatsm/internal/config" + "caatsm/internal/domain" + "caatsm/pkg/utils" + "fmt" + "regexp" + "strings" + "time" +) + +var ( + sitaTextPattern = regexp.MustCompile(`\(([A-Z]{3})(.*)\)`) + validSitaPriority = regexp.MustCompile(`^(SS|DD|FF|GG|KK)$`) + validSitaAddress = regexp.MustCompile(`^[A-Z]{8}$`) +) + +// SITAParser is responsible for parsing SITA messages. +type SITAParser struct { + bodyPattern map[string]config.BodyConfig // Injected configuration for body patterns. +} + +// NewSITAParser creates a new instance of SITAParser. +func NewSITAParser(myPatterns map[string]config.BodyConfig) *SITAParser { + return &SITAParser{bodyPattern: myPatterns} +} + +// DefaultSITAParser creates a new instance of SITAParser with default patterns. +func DefaultSITAParser() *SITAParser { + return &SITAParser{bodyPattern: config.GetBodyPatterns()} +} + +func (p *SITAParser) GetBodyPatterns() map[string]config.BodyConfig { + return p.bodyPattern +} + +// Parse is the main method to parse a SITA message. +func (p *SITAParser) Parse(rawMessage string) (*domain.SITA, error) { + cleanedText := removeEmptyLines(rawMessage) + lines := strings.Split(cleanedText, "\n") + + if len(lines) < 4 { + return nil, fmt.Errorf("invalid SITA message format: insufficient lines") + } + + header, err := parseSITAHeader(lines[0]) + if err != nil { + return nil, err + } + + priorityAndSender, err := parseSITAPriorityAndSender(lines[1]) + if err != nil { + return nil, err + } + + timeAndReceiver, err := parseSITATimeAndReceiver(lines[2]) + if err != nil { + return nil, err + } + + text, bodyType, err := parseSITATextInfo(strings.Join(lines[3:], "\n")) + if err != nil { + return nil, err + } + + bodyData, err := p.extractBodyData(text) + if err != nil { + return nil, err + } + + sita, err := p.createSITA(bodyData) + if err != nil { + return nil, err + } + + return &domain.SITA{ + Header: header, + PriorityAndSender: priorityAndSender, + TimeAndReceiver: timeAndReceiver, + Text: text, + Category: bodyType, + BodyData: sita, + ReceivedTime: time.Now(), + }, nil +} + +// parseSITAHeader parses the header line of a SITA message. +func parseSITAHeader(line string) (domain.SITAHeader, error) { + parts := strings.Fields(line) + if len(parts) < 3 { + return domain.SITAHeader{}, fmt.Errorf("invalid header format: %s", line) + } + return domain.SITAHeader{ + StartSignal: parts[0], + SendID: parts[1], + SendTime: parts[2], + }, nil +} + +// parseSITAPriorityAndSender parses the priority and sender line of a SITA message. +func parseSITAPriorityAndSender(line string) (domain.PrioritySender, error) { + parts := strings.Fields(line) + if len(parts) < 2 { + return domain.PrioritySender{}, fmt.Errorf("invalid priority and sender format: %s", line) + } + return domain.PrioritySender{ + Priority: parts[0], + Sender: parts[1], + }, nil +} + +// parseSITATimeAndReceiver parses the time and receiver line of a SITA message. +func parseSITATimeAndReceiver(line string) (domain.TimeReceiver, error) { + parts := strings.Fields(line) + if len(parts) < 2 { + return domain.TimeReceiver{}, fmt.Errorf("invalid time and receiver format: %s", line) + } + return domain.TimeReceiver{ + Time: parts[0], + Receiver: parts[1], + }, nil +} + +// parseSITATextInfo parses the text and extracts the body type from a SITA message. +func parseSITATextInfo(text string) (string, string, error) { + match := sitaTextPattern.FindStringSubmatch(text) + if len(match) > 1 { + return match[0], match[1], nil + } + return "", "", fmt.Errorf("invalid text format: %s", text) +} + +func (p *SITAParser) ParseBody(body string) (interface{}, error) { + bodyData, err := p.extractBodyData(body) + if err != nil { + return nil, err + } + + sita, err := p.createSITA(bodyData) + if err != nil { + return nil, err + } + + return sita, nil +} + +// createSITA parses the body data of a SITA message. +func (p *SITAParser) createSITA(data map[string]string) (interface{}, error) { + switch data["type"] { + + default: + return nil, fmt.Errorf("invalid message type: %s", data["type"]) + } +} + +// extractBodyData extracts the body data from a SITA message. +func (p *SITAParser) extractBodyData(text string) (map[string]string, error) { + log := utils.Logger + data := make(map[string]string) + for _, pattern := range p.GetBodyPatterns() { + for i, p := range pattern.Patterns { + log.Debugf("Trying pattern %d: %s\n %s\n", i, p.Comments, p.Pattern) + re := p.Expression + match := re.FindStringSubmatch(text) + if match != nil { + log.Debugf("Matched : %v \n", match) + for i, name := range re.SubexpNames() { + if i != 0 && name != "" { + data[name] = match[i] + } + } + return data, nil + } + log.Debugf("No match for pattern %d\n", i) + } + } + log.Errorf("No matching pattern found for text: %s", text) + return nil, fmt.Errorf("no matching pattern found for text: %s", text) +} + +// ValidateSITA validates the fields of a SITA message. +func ValidateSITA(msg *domain.SITA) error { + if missingSitaFields(msg) { + return fmt.Errorf("invalid SITA message: missing fields") + } + + if !validSitaPriority.MatchString(msg.PriorityAndSender.Priority) { + return fmt.Errorf("invalid priority code: %s", msg.PriorityAndSender.Priority) + } + + if !validSitaAddress.MatchString(msg.TimeAndReceiver.Receiver) || !validSitaAddress.MatchString(msg.PriorityAndSender.Sender) { + return fmt.Errorf("invalid address format") + } + + return nil +} + +func missingSitaFields(msg *domain.SITA) bool { + if msg.Header.StartSignal == "" || msg.Header.SendID == "" || msg.Header.SendTime == "" || + msg.PriorityAndSender.Priority == "" || msg.PriorityAndSender.Sender == "" || + msg.TimeAndReceiver.Time == "" || msg.TimeAndReceiver.Receiver == "" || + msg.Category == "" { + return true + } + return false +}