210 lines
5.6 KiB
Go
210 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
//log "github.com/fangdingjun/go-log"
|
|
"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
|
|
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),
|
|
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 !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
|
|
}
|
|
}
|