package main import ( "errors" "flag" "fmt" "path/filepath" "log" "os" "os/signal" "syscall" "github.com/jroimartin/gocui" "github.com/karanveersp/store" "gopkg.in/yaml.v2" ) const usage = `Usage of ptb - the PACTOR-IP-Bridge: -c, --configfile string Name of config file (default "Config.yaml") -d, --daemon Daemon mode (no TUI) -l, --logfile string Path to the log file ` const ( StatusTCPCmdActive uint8 = 1 << iota //00000001 StatusTCPDataActive //00000010 ) type TCPServer struct { Protocol chan string ToPactor *ByteFIFO FromPactor *ByteFIFO ToTRX *ByteFIFO FromTRX *ByteFIFO VARAMode bool DaemonMode bool GPSdMode bool NMEAPassthrough bool DeviceType string Status uint8 Command struct { Cmd *StringFIFO Response *StringFIFO } Data struct { Data *ByteFIFO Response *ByteFIFO } GPSStream *StringFIFO // NMEA steam from PTC to gpsd server, see gpsd.go } func NewTCPServer(varamode bool, daemonmode bool, gpsdmode bool, nmeapassthrough bool) *TCPServer { return &TCPServer{ Protocol: make(chan string, 1024), ToPactor: NewByteFIFO(1024), FromPactor: NewByteFIFO(1024), ToTRX: NewByteFIFO(1024), FromTRX: NewByteFIFO(1024), VARAMode: varamode, DaemonMode: daemonmode, GPSdMode: gpsdmode, NMEAPassthrough: nmeapassthrough, DeviceType: "", Status: 0, Command: struct { Cmd *StringFIFO Response *StringFIFO }{Cmd: NewStringFIFO(), Response: NewStringFIFO()}, Data: struct { Data *ByteFIFO Response *ByteFIFO }{Data: NewByteFIFO(10240), Response: NewByteFIFO(10240)}, GPSStream: NewStringFIFO(), } } type Userconfig struct { Device string `yaml:"device"` Baudrate int `yaml:"baudrate"` Mycall string `yaml:"mycall"` ServerAddress string `yaml:"server_address"` DataAddress string `yaml:"data_address"` GPSdAddress string `yaml:"gpsd_address"` NMEAPassthrough bool `yaml:"nmeapassthrough"` CmdLineInit string `yaml:"cmdline_init"` StartwithVaraMode bool `yaml:"vara_mode"` } func configmanage(Config *Userconfig, path string) error { cf := store.GetApplicationDirPath() + string(os.PathSeparator) + path _, err := os.Stat(cf) if err != nil { log.Println("loadConfig error:", err) log.Println("Config file not found. Creating new one") Config = &Userconfig{Device: "/tmp/ttyUSB0", Baudrate: 9600, Mycall: "N0CALL", ServerAddress: "127.0.0.1:8300", DataAddress: "127.0.0.1:8301", GPSdAddress: "", NMEAPassthrough: false, CmdLineInit: "", StartwithVaraMode: false} if err := store.Save("Config.yaml", Config); err != nil { log.Println("failed to save the Config file: ", err) return err } return errors.New(fmt.Sprintf("new Config file %s created. Please edit the Config file and restart the application", cf)) } if err := store.Load(path, Config); err != nil { return err } if Config.Device != "" { //log.Println("Config loaded:", Config) return nil } return errors.New(fmt.Sprintf("Empty device name in Config file. Please edit the Config file %s and restart the application", cf)) } func init() { store.Init("dl1thm.pactortcpbridge") store.Register("ini", yaml.Marshal, yaml.Unmarshal) } var s *TCPServer // s is a pointer to TCPServer, managing communication via channels and FIFO queues for command and data handling. func main() { tmpfile := filepath.FromSlash(os.TempDir() + "/ptb.log") // read command line arguments -- the standard flag module unfortunately doesn't know shorthands var configfile string flag.StringVar(&configfile, "configfile", "Config.yaml", "Name of config file") flag.StringVar(&configfile, "c", "Config.yaml", "Name of config file") var logfile string flag.StringVar(&logfile, "logfile", tmpfile, "Name of log file") flag.StringVar(&logfile, "l", tmpfile, "Name of log file") var daemonMode bool flag.BoolVar(&daemonMode, "daemon", false, "Daemon mode (no TUI)") flag.BoolVar(&daemonMode, "d", false, "Daemon mode (no TUI)") flag.Usage = func() { fmt.Print(usage) } flag.Parse() f, err := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatalf("error opening file: %v", err) } defer f.Close() log.SetOutput(f) fmt.Println("logging to " + logfile) // read config var Config Userconfig //c := store.GetApplicationDirPath() err = configmanage(&Config, configfile) if err != nil { fmt.Println(err) os.Exit(1) } s = NewTCPServer(Config.StartwithVaraMode, daemonMode, Config.GPSdAddress != "", Config.NMEAPassthrough) fmt.Println("Initializing PACTOR modem, please wait...") m, err := OpenModem(Config.Device, Config.Baudrate, Config.Mycall, "", Config.CmdLineInit) if err != nil { log.Panicln(err) } defer m.Close() go tcpCmdServer(&Config) go tcpDataServer(&Config) if Config.GPSdAddress != "" { go startGPSdTCPServer(Config.GPSdAddress) go readAndBroadcast() } if true { // TODO: later change to Config go trxControl() } if !daemonMode { // Initialize the gocui GUI g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) go protocolUpdate(g) go pactorUpdate(g) go statusUpdate(g) // Set keybindings if err := keybindings(g); err != nil { log.Panicln(err) } // Start the main TUI loop if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } else { // daemon mode: just print a message and wait for CTRL-C done := make(chan os.Signal, 1) signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) fmt.Println("pactortcpbridge running, press ctrl+c to end the process...") <-done } }