The code changes in `aviation_parser_test.go` update the ARR body parsing logic. The `ParseHeader` function now correctly handles ARR bodies with optional time components. This ensures accurate parsing of ARR bodies and improves the overall functionality of the aviation parser.
311 lines
10 KiB
Go
311 lines
10 KiB
Go
package config
|
|
|
|
import (
|
|
"caatsm/pkg/utils"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
var MyConfig *Config
|
|
|
|
type Config struct {
|
|
Nats NatsConfig
|
|
Subscription SubscriptionConfig
|
|
Timeouts TimeoutsConfig
|
|
}
|
|
|
|
type NatsConfig struct {
|
|
Client string
|
|
URL string
|
|
Cluster string
|
|
}
|
|
|
|
type SubscriptionConfig struct {
|
|
Topic string `mapstructure:"topic"`
|
|
QueueGroup string `mapstructure:"queue_group"`
|
|
}
|
|
|
|
type TimeoutsConfig struct {
|
|
Server time.Duration `mapstructure:"server"`
|
|
ReconnectWait time.Duration `mapstructure:"reconnect_wait"`
|
|
Close time.Duration `mapstructure:"close"`
|
|
AckWait time.Duration `mapstructure:"ack_wait"`
|
|
}
|
|
|
|
type BodyConfig struct {
|
|
Patterns []PatternConfig
|
|
}
|
|
|
|
type PatternConfig struct {
|
|
Pattern string
|
|
Comments string
|
|
Expression *regexp.Regexp
|
|
}
|
|
|
|
// LoggerConfig represents the configuration for the logger.
|
|
type LoggerConfig struct {
|
|
ZapConfig zap.Config `json:"zapConfig"`
|
|
LumberjackConfig LumberjackConfig `json:"lumberjackConfig"`
|
|
}
|
|
|
|
// LumberjackConfig represents the configuration for lumberjack logging.
|
|
type LumberjackConfig struct {
|
|
Filename string `json:"filename"`
|
|
MaxSize int `json:"maxSize"`
|
|
MaxBackups int `json:"maxBackups"`
|
|
MaxAge int `json:"maxAge"`
|
|
Compress bool `json:"compress"`
|
|
}
|
|
|
|
const (
|
|
EnvProd = "prod"
|
|
// logConfigFile = "configs/log_config.json"
|
|
// arrPatternString represents the regular expression pattern used to match arrival patterns.
|
|
// The pattern matches strings in the format: "(TYPE-NUMBER-SSR-DEPARTURE-ARRIVAL)".
|
|
// The pattern captures the following named groups:
|
|
// - category: the three-letter category code
|
|
// - number: the alphanumeric flight number
|
|
// - ssr: the alphanumeric SSR code
|
|
// - departure: the four-letter departure airport code
|
|
// - arrival: the four-letter arrival airport code
|
|
arrPatternString = `^\((?P<category>[A-Z]{3})\-(?P<number>[A-Z0-9]+)(\-(?P<ssr>[A-Z]+[0-9]+))?\-(?P<departure>[A-Z]{4})\-(?P<arrival>[A-Z]{4})(?P<time>\d{4})\)$`
|
|
|
|
// depPatternString represents the regular expression pattern used to match departure patterns.
|
|
// The pattern matches strings in the format: "(TYPE-NUMBER-SSR-DEPARTURE-DEPARTURE_TIME-ARRIVAL)".
|
|
// The pattern captures the following named groups:
|
|
// - category: the three-letter category code
|
|
// - number: the alphanumeric flight number
|
|
// - ssr: the alphanumeric SSR code
|
|
// - departure: the four-letter departure airport code
|
|
// - departure_time: the four-digit departure time
|
|
// - arrival: the four-letter arrival airport code
|
|
depPatternString = `^\((?P<category>[A-Z]{3})-(?P<number>[A-Z0-9]+)(\/(?P<ssr>[A-Z0-9]+))?-(?P<departure>[A-Z]{4})(?P<departure_time>\d{4})-(?P<arrival>[A-Z]{4})\)$`
|
|
|
|
// fplPatternString is a regular expression designed to parse and extract detailed information from formatted flight plan strings.
|
|
// The flight plan string is expected to follow a specific format, encapsulated by parentheses and containing various segments separated by hyphens.
|
|
// Each segment captures a different aspect of the flight plan, as detailed below:
|
|
//
|
|
// - category: Matches a three-letter flight category code.
|
|
// - number: Matches the flight number, consisting of letters followed by digits.
|
|
// - indicator: Matches a two-letter indicator.
|
|
// - aircraft: Matches the aircraft registration or type, which may include a slash and an optional letter.
|
|
// - surve: Matches surveillance equipment information, capturing any characters in this segment.
|
|
// - departure: Matches the four-letter code of the departure airport.
|
|
// - departure_time: Matches the four-digit departure time.
|
|
// - speed: Matches the speed, consisting of letters followed by digits.
|
|
// - level: Matches the flight level, which can include letters and digits.
|
|
// - route: Matches the flight route, capturing any characters in this segment.
|
|
// - destination: Matches the four-letter code of the destination airport.
|
|
// - estt: Matches the four-digit estimated time of arrival.
|
|
// - alter: Matches the four-letter code of an alternate airport.
|
|
// - pbn: Matches performance-based navigation equipment information.
|
|
// - nav: Matches navigation equipment information.
|
|
// - reg: Matches the aircraft registration.
|
|
// - eet: Matches the estimated elapsed time, including the four-letter code and four-digit time.
|
|
// - sel: Matches the SELCAL code.
|
|
// - performance: Matches a single letter indicating the aircraft's performance category.
|
|
// - rif: Matches reroute information, including any characters in this segment.
|
|
// - remark: Matches remarks, capturing any characters in this segment.
|
|
//
|
|
// The regular expression uses named capture groups for each segment, allowing for easy extraction of specific information from a matched flight plan string.
|
|
// fplPatternString = `\((?P<category>[A-Z]{3})\-(?P<number>[A-Z]+\d+)\-(?P<indicator>[A-Z]{2})(.*\n)?(.*\n)?\-(?P<aircraft>[A-Z]+\d+\/?[A-Z]?)\s*\-(?P<surve>.*)(.*\n)?\-(?P<departure>[A-Z]{4})(?P<departure_time>\d{4})(.*\n)?\-(?P<speed>[A-Z]+\d+)(?P<level>[A-Z0-9]+)\s(?P<route>.*)(.*\n)?\-(?P<destination>[A-Z]{4})(?P<estt>\d{4})\s(?P<alter>[A-Z]{4})(.*\n)?\-(?P<pbn>PBN\/[A-Z0-9]+)\s(?P<nav>NAV\/\w+)\sREG\/(?P<reg>[A-Z0-9]+)\sEET\/(?P<eet>\w{4}\d{4})\sSEL\/(?P<sel>\w+)\sPER\/(?P<performance>\w)\sRIF\/(?P<rif>\w+\s[A-Z0-9]+\s[A-Z]+)\s*RMK\/(?P<remark>.*)\)$`
|
|
fplPatternString = `\((?P<category>[A-Z]{3})-(?P<number>[A-Z]+\d+)-(?P<indicator>[A-Z]{2})\n` +
|
|
`-(?P<aircraft>[A-Z]+\d+\/?[A-Z]?)\n` +
|
|
`-(?P<surve>.*)\n` +
|
|
`-(?P<departure>[A-Z]{4})(?P<departure_time>\d{4})\n` +
|
|
`-(?P<speed>[A-Z]+\d+)(?P<level>[A-Z0-9]+)\s+(?P<route>.*)\n` +
|
|
`-(?P<destination>[A-Z]{4})(?P<estt>\d{4})\s+(?P<alter>[A-Z]{4})\n` +
|
|
`-(?P<pbn>PBN\/[A-Z0-9]+)\s+(?P<nav>NAV\/\w+)\s+REG\/(?P<reg>[A-Z0-9]+)\s+EET\/(?P<eet>\w{4}\d{4})\s+SEL\/(?P<sel>\w+)\s+PER\/(?P<performance>\w)\s+RIF\/(?P<rif>\w+\s+[A-Z0-9]+\s+[A-Z]+)\s*RMK\/(?P<remark>.*)\)`
|
|
)
|
|
|
|
// Initialize the bodyPatterns map
|
|
var bodyPatterns = map[string]BodyConfig{
|
|
"ARR": {
|
|
Patterns: []PatternConfig{
|
|
{
|
|
Pattern: arrPatternString,
|
|
Comments: "Pattern for ARR message",
|
|
Expression: regexp.MustCompile(arrPatternString),
|
|
},
|
|
},
|
|
},
|
|
"DEP": {
|
|
Patterns: []PatternConfig{
|
|
{
|
|
Pattern: depPatternString,
|
|
Comments: "Pattern for DEP message",
|
|
Expression: regexp.MustCompile(depPatternString),
|
|
},
|
|
},
|
|
},
|
|
"FPL": {
|
|
Patterns: []PatternConfig{
|
|
{
|
|
Pattern: fplPatternString,
|
|
Comments: "Pattern for FPL message",
|
|
Expression: regexp.MustCompile(fplPatternString),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func GetBodyPatterns() map[string]BodyConfig {
|
|
return bodyPatterns
|
|
}
|
|
|
|
func SetMyConfig(cfg *Config) {
|
|
MyConfig = cfg
|
|
}
|
|
|
|
func GetMyConfig() *Config {
|
|
if MyConfig == nil {
|
|
cfg, err := LoadConfig()
|
|
if err != nil {
|
|
fmt.Printf("error loading config: %v", err)
|
|
}
|
|
MyConfig = cfg
|
|
}
|
|
return MyConfig
|
|
}
|
|
|
|
// LoadConfig loads the configuration from a file
|
|
func LoadConfig() (*Config, error) {
|
|
// log := utils.Logger
|
|
env := os.Getenv("GO_ENV")
|
|
if env == "" {
|
|
env = "dev"
|
|
}
|
|
// log.Infof("Environment: %s", env)
|
|
|
|
viper.SetConfigType("toml")
|
|
viper.SetConfigName("config." + env)
|
|
viper.AddConfigPath("configs")
|
|
viper.SetEnvPrefix("tele")
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
errMsg := fmt.Sprintf("error reading config file for environment '%s': %v", env, err)
|
|
// log.Error(errMsg)
|
|
return nil, fmt.Errorf(errMsg)
|
|
}
|
|
|
|
// log.Debug("Config file read successfully")
|
|
// log.Debugf("Config keys: %v", viper.AllKeys())
|
|
|
|
var config Config
|
|
if err := viper.Unmarshal(&config); err != nil {
|
|
errMsg := fmt.Sprintf("unable to decode config into struct for environment '%s': %v", env, err)
|
|
// log.Error(errMsg)
|
|
return nil, fmt.Errorf(errMsg)
|
|
}
|
|
// loadLoggerConfig()
|
|
|
|
// log.Debugf("Config loaded: %+v", config)
|
|
return &config, nil
|
|
}
|
|
|
|
// ValidateConfig validates the loaded configuration
|
|
func ValidateConfig(cfg *Config) error {
|
|
// log := utils.Logger
|
|
|
|
if cfg.Nats.Client == "" {
|
|
return fmt.Errorf("nats client is required")
|
|
}
|
|
if cfg.Nats.URL == "" {
|
|
return fmt.Errorf("nats URL is required")
|
|
}
|
|
if cfg.Subscription.Topic == "" {
|
|
return fmt.Errorf("subscription topic is required")
|
|
}
|
|
// fmt.Println("config validation passed")
|
|
return nil
|
|
}
|
|
|
|
func loadLoggerConfig() {
|
|
// log := utils.Logger
|
|
env := os.Getenv("GO_ENV")
|
|
if env == "" {
|
|
env = "dev"
|
|
}
|
|
// log.Infof("Environment: %s", env)
|
|
|
|
viper.SetConfigType("json")
|
|
viper.SetConfigName("logger." + env)
|
|
viper.AddConfigPath("configs")
|
|
// viper.SetEnvPrefix("tele")
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
errMsg := fmt.Sprintf("error reading logger config file for environment '%s': %v", env, err)
|
|
// log.Error(errMsg)
|
|
panic(errMsg)
|
|
}
|
|
|
|
// log.Debug("Logger config file read successfully")
|
|
// log.Debugf("Logger config keys: %v", viper.AllKeys())
|
|
|
|
var config LoggerConfig
|
|
if err := viper.Unmarshal(&config); err != nil {
|
|
errMsg := fmt.Sprintf("unable to decode logger config into struct for environment '%s': %v", env, err)
|
|
// log.Error(errMsg)
|
|
// return nil, fmt.Errorf(errMsg)
|
|
panic(errMsg)
|
|
}
|
|
var logWriter zapcore.WriteSyncer
|
|
if env == EnvProd {
|
|
logWriter = zapcore.AddSync(&lumberjack.Logger{
|
|
Filename: config.LumberjackConfig.Filename,
|
|
MaxSize: config.LumberjackConfig.MaxSize,
|
|
MaxBackups: config.LumberjackConfig.MaxBackups,
|
|
MaxAge: config.LumberjackConfig.MaxAge,
|
|
Compress: config.LumberjackConfig.Compress,
|
|
})
|
|
} else {
|
|
logWriter = zapcore.AddSync(os.Stdout)
|
|
}
|
|
|
|
encoder := zapcore.NewJSONEncoder(config.ZapConfig.EncoderConfig)
|
|
level := parseLogLevel(config.ZapConfig.Level.String())
|
|
|
|
core := zapcore.NewCore(
|
|
encoder,
|
|
logWriter,
|
|
level,
|
|
)
|
|
|
|
log := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
|
utils.Logger = log.Sugar()
|
|
// log.Debugf("Logger config loaded: %+v", config)
|
|
|
|
}
|
|
|
|
// parseLogLevel converts the log level string to zapcore.Level.
|
|
func parseLogLevel(level string) zapcore.Level {
|
|
switch level {
|
|
case "debug":
|
|
return zapcore.DebugLevel
|
|
case "info":
|
|
return zapcore.InfoLevel
|
|
case "warn":
|
|
return zapcore.WarnLevel
|
|
case "error":
|
|
return zapcore.ErrorLevel
|
|
case "dpanic":
|
|
return zapcore.DPanicLevel
|
|
case "panic":
|
|
return zapcore.PanicLevel
|
|
case "fatal":
|
|
return zapcore.FatalLevel
|
|
default:
|
|
return zapcore.InfoLevel
|
|
}
|
|
}
|