Skip to content

Commit

Permalink
merged mdns_interface branch and mainly consists of bugfixes
Browse files Browse the repository at this point in the history
- websockets ping/pong
- mdns more reliable on windows
- minor webui improvements
- better documentation
- changed "tcp" command to "lan"
  • Loading branch information
dh1tw committed Dec 8, 2017
1 parent d3895bb commit ffbe591
Show file tree
Hide file tree
Showing 16 changed files with 420 additions and 220 deletions.
37 changes: 12 additions & 25 deletions cmd/enumerate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@ import (
"github.com/spf13/cobra"
)

var discoverCmd = &cobra.Command{
var enumerateCmd = &cobra.Command{
Use: "enumerate",
Short: "discover and list all available rotators on the network",
Long: `discover and list all available rotators on the network
This command performs a mDNS query on your local network and will report all
found rotators with their parameters.`,
Run: discoverMDNS,
Run: enumerateMDNS,
}

func init() {
RootCmd.AddCommand(discoverCmd)
RootCmd.AddCommand(enumerateCmd)
}

func discoverMDNS(cmd *cobra.Command, args []string) {
func enumerateMDNS(cmd *cobra.Command, args []string) {

fmt.Println("\n...discovering rotators (please wait)")
rots := discovery.LookupRotators()
rots, err := discovery.LookupRotators()
if err != nil {
fmt.Println(err)
}

if err := tmpl.Execute(os.Stdout, rots); err != nil {
fmt.Println(err)
Expand All @@ -40,27 +43,11 @@ Found {{. | len}} rotator(s) on this network
{{range .}}Rotator:
Name: {{.Name}}
URL: {{.URL}}
Host: {{.Host}}
Address IPv6: {{.AddrV6}}
Address IPv4: {{.AddrV4}}
Host: {{.Host}}{{if .AddrV4}}
Address IPv4: {{.AddrV4}}{{else}}
Address IPv6: {{.AddrV6}}{{end}}
Port: {{.Port}}
{{end}}
`,
))

// var tmpl = template.Must(template.New("").Parse(
// `
// Found {{. | len}} rotator(s) on this network

// {{range .}}Rotator:
// Name: {{.Name}}
// URL: {{.URL}}
// Host: {{.Host}}{{if .AddrV6}}
// Address IPv6: {{.AddrV6}}{{else}}
// Address IPv4: {{.AddrV4}}{{end}}
// Port: {{.Port}}

// {{end}}
// `,
// ))
190 changes: 116 additions & 74 deletions cmd/tcp_server.go → cmd/lan_server.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package cmd

import (
b64 "encoding/base64"
"encoding/json"
"bytes"
"fmt"
"log"
"net"
"os"
"os/signal"
"strings"
"time"

"github.com/hashicorp/mdns"
"github.com/micro/mdns"
"github.com/spf13/cobra"
"github.com/spf13/viper"

Expand All @@ -21,38 +21,61 @@ import (
// _ "net/http/pprof"
)

var tcpServerCmd = &cobra.Command{
Use: "tcp",
Short: "expose a rotator to the network",
Long: `expose a rotator to the network`,
Run: tcpServer,
var lanServerCmd = &cobra.Command{
Use: "lan",
Short: "expose a rotator on your local network",
Long: `
The local lan server allows you to expose a rotator to a local area network.
By default, the rotator will only be listening on the loopback adapter. In
order to make it available and discoverable on the local network, a network
connected adapter has to be selected.
remoteRotator supports access via TCP, emulating the Yaesu GS232 protocol
(disabled by default) and through a web interface (HTTP + Websocket).
You can select the following rotator types:
1. Yaesu (GS232 compatible)
2. Dummy (great for testing)
remoteRotator allows to assign a series of meta data to a rotator:
1. Name
2. Azimuth/Elevation minimum value
3. Azimuth/Elevation maximum value
4. Azimuth Mechanical stop
These metadata enhance the rotators view (e.g. showing overlap) in the web
interface and can also help to limit for example the rotators range if it does
not support full 360°.
`,
Run: lanServer,
}

func init() {
serverCmd.AddCommand(tcpServerCmd)

tcpServerCmd.Flags().BoolP("tcp-enabled", "", true, "enable TCP Server")
tcpServerCmd.Flags().StringP("tcp-host", "u", "127.0.0.1", "Host (use '0.0.0.0' to listen on all network adapters)")
tcpServerCmd.Flags().IntP("tcp-port", "p", 7373, "TCP Port")
tcpServerCmd.Flags().BoolP("http-enabled", "", true, "enable HTTP Server")
tcpServerCmd.Flags().StringP("http-host", "w", "127.0.0.1", "Host (use '0.0.0.0' to listen on all network adapters)")
tcpServerCmd.Flags().IntP("http-port", "k", 7070, "Port for the HTTP access to the rotator")
tcpServerCmd.Flags().BoolP("discovery-enabled", "", true, "make rotator discoverable on the network")
tcpServerCmd.Flags().StringP("portname", "P", "/dev/ttyACM0", "portname / path to the rotator (e.g. COM1)")
tcpServerCmd.Flags().IntP("baudrate", "b", 9600, "baudrate")
tcpServerCmd.Flags().StringP("type", "t", "yaesu", "Rotator type (supported: yaesu, dummy")
tcpServerCmd.Flags().StringP("name", "n", "myRotator", "Name tag for the rotator")
tcpServerCmd.Flags().BoolP("has-azimuth", "", true, "rotator supports Azimuth")
tcpServerCmd.Flags().BoolP("has-elevation", "", false, "rotator supports Elevation")
tcpServerCmd.Flags().DurationP("pollingrate", "", time.Second*1, "rotator polling rate")
tcpServerCmd.Flags().IntP("azimuth-min", "", 0, "metadata: minimum azimuth (in deg)")
tcpServerCmd.Flags().IntP("azimuth-max", "", 360, "metadata: maximum azimuth (in deg)")
tcpServerCmd.Flags().IntP("azimuth-stop", "", 0, "metadata: mechanical azimuth stop (in deg)")
tcpServerCmd.Flags().IntP("elevation-min", "", 0, "metadata: minimum elevation (in deg)")
tcpServerCmd.Flags().IntP("elevation-max", "", 180, "metadata: maximum elevation (in deg)")
serverCmd.AddCommand(lanServerCmd)

lanServerCmd.Flags().BoolP("tcp-enabled", "", false, "enable TCP Server")
lanServerCmd.Flags().StringP("tcp-host", "u", "127.0.0.1", "Host (use '0.0.0.0' to listen on all network adapters)")
lanServerCmd.Flags().IntP("tcp-port", "p", 7373, "TCP Port")
lanServerCmd.Flags().BoolP("http-enabled", "", true, "enable HTTP Server")
lanServerCmd.Flags().StringP("http-host", "w", "127.0.0.1", "Host (use '0.0.0.0' to listen on all network adapters)")
lanServerCmd.Flags().IntP("http-port", "k", 7070, "Port for the HTTP access to the rotator")
lanServerCmd.Flags().BoolP("discovery-enabled", "", true, "make rotator discoverable on the network")
lanServerCmd.Flags().StringP("portname", "P", "/dev/ttyACM0", "portname / path to the rotator (e.g. COM1)")
lanServerCmd.Flags().IntP("baudrate", "b", 9600, "baudrate")
lanServerCmd.Flags().StringP("type", "t", "yaesu", "Rotator type (supported: yaesu, dummy")
lanServerCmd.Flags().StringP("name", "n", "myRotator", "Name tag for the rotator")
lanServerCmd.Flags().BoolP("has-azimuth", "", true, "rotator supports Azimuth")
lanServerCmd.Flags().BoolP("has-elevation", "", false, "rotator supports Elevation")
lanServerCmd.Flags().DurationP("pollingrate", "", time.Second*1, "rotator polling rate")
lanServerCmd.Flags().IntP("azimuth-min", "", 0, "metadata: minimum azimuth (in deg)")
lanServerCmd.Flags().IntP("azimuth-max", "", 360, "metadata: maximum azimuth (in deg)")
lanServerCmd.Flags().IntP("azimuth-stop", "", 0, "metadata: mechanical azimuth stop (in deg)")
lanServerCmd.Flags().IntP("elevation-min", "", 0, "metadata: minimum elevation (in deg)")
lanServerCmd.Flags().IntP("elevation-max", "", 180, "metadata: maximum elevation (in deg)")
}

func tcpServer(cmd *cobra.Command, args []string) {
func lanServer(cmd *cobra.Command, args []string) {

// Try to read config file
if err := viper.ReadInConfig(); err == nil {
Expand Down Expand Up @@ -134,13 +157,6 @@ func tcpServer(cmd *cobra.Command, args []string) {
os.Exit(1)
}

// check if values from config file / pflags are valid
// if !checkParameterValues() {
// --> tcp & http host must be valid hostnames -> catch panic & exit gracefully

// os.Exit(-1)
// }

// go func() {
// log.Println(http.ListenAndServe("0.0.0.0:6060", http.DefaultServeMux))
// }()
Expand All @@ -166,7 +182,8 @@ func tcpServer(cmd *cobra.Command, args []string) {
h := &hub.Hub{}

rotatorError := make(chan struct{})
rotatorShutdown := make(chan struct{})

var r rotator.Rotator

switch strings.ToUpper(viper.GetString("rotator.type")) {

Expand All @@ -183,20 +200,16 @@ func tcpServer(cmd *cobra.Command, args []string) {
elMin := yaesu.ElevationMin(viper.GetInt("rotator.elevation-min"))
elMax := yaesu.ElevationMax(viper.GetInt("rotator.elevation-max"))
azStop := yaesu.AzimuthStop(viper.GetInt("rotator.azimuth-stop"))
errorCh := yaesu.ErrorCh(rotatorError)

yaesu, err := yaesu.NewYaesu(name, interval, evHandler,
yaesu, err := yaesu.New(name, interval, evHandler,
spPortName, baudrate, hasAzimuth, hasElevation, azMin, azMax, elMin,
elMax, azStop)
elMax, azStop, errorCh)
if err != nil {
fmt.Println("unable to initialize YAESU rotator:", err)
os.Exit(1)
}
h, err = hub.NewHub(yaesu)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
yaesu.Start(rotatorError, rotatorShutdown)
r = yaesu

case "DUMMY":
evHandler := dummy.EventHandler(yaesuEventHandler)
Expand All @@ -209,24 +222,25 @@ func tcpServer(cmd *cobra.Command, args []string) {
elMax := dummy.ElevationMax(viper.GetInt("rotator.elevation-max"))
azStop := dummy.AzimuthStop(viper.GetInt("rotator.azimuth-stop"))

dummyRotator, err := dummy.NewDummyRotator(name, evHandler, hasAzimuth, hasElevation, azMin, azMax, azStop, elMin, elMax)
dummyRotator, err := dummy.New(name, evHandler, hasAzimuth, hasElevation, azMin, azMax, azStop, elMin, elMax)
if err != nil {
fmt.Println("unable to initialize Dummy rotator:", err)
os.Exit(1)
}
h, err = hub.NewHub(dummyRotator)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

dummyRotator.Start(rotatorShutdown)
r = dummyRotator

default:
log.Printf("unknown rotator type (%v)\n", viper.GetString("rotator.type"))
os.Exit(1)
}

h, err := hub.NewHub(r)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

tcpError := make(chan bool)

// start TCP server
Expand All @@ -241,25 +255,13 @@ func tcpServer(cmd *cobra.Command, args []string) {
go h.ListenHTTP(viper.GetString("http.host"), viper.GetInt("http.port"), wsError)
}

// shutdownWg := sync.WaitGroup{}
// start mDNS server
mDNSShutdown := make(chan struct{})

if viper.GetBool("discovery.enabled") {
go func() {
mDNSService, err := mdns.NewMDNSService(viper.GetString("rotator.name"),
"rotators.shackbus", "", "", viper.GetInt("http.port"), nil, nil)

if err != nil {
log.Printf("unable to start mDNS discovery service: %s\n", err)
log.Println("mDNS discovery is disabled")
return
}

mDNSServer, _ := mdns.NewServer(&mdns.Config{Zone: mDNSService})
defer mDNSServer.Shutdown()
<-mDNSShutdown
}()
if err := startMdnsServer(mDNSShutdown); err != nil {
log.Println(err)
}
}

// Channel to handle OS signals
Expand All @@ -272,7 +274,7 @@ func tcpServer(cmd *cobra.Command, args []string) {
select {
case sig := <-osSignals:
if sig == os.Interrupt {
close(rotatorShutdown)
r.Close()
close(mDNSShutdown)
return
}
Expand All @@ -289,12 +291,52 @@ func tcpServer(cmd *cobra.Command, args []string) {

}

func encodeInfo(i rotator.Info) (string, error) {
res, err := json.Marshal(i)
func startMdnsServer(shutdown <-chan struct{}) error {

if !viper.GetBool("http.enabled") {
return fmt.Errorf("discovery disabled; the HTTP server must be enabled and accessible over a network interface (e.g. 0.0.0.0)")
}

netif := net.ParseIP(viper.GetString("http.host"))

if bytes.Compare(netif, net.IPv4zero) != 0 &&
bytes.Compare(netif, net.IPv6zero) != 0 &&
bytes.Compare(netif, getOutboundIP()) != 0 {
return fmt.Errorf("discovery disabled; the HTTP server must listen on an accessible network interface (e.g. 0.0.0.0)")
}

go func() {
mDNSService, err := mdns.NewMDNSService(viper.GetString("rotator.name"),
"_rotator._tcp", "", "", viper.GetInt("http.port"),
[]net.IP{getOutboundIP()}, nil)

if err != nil {
log.Printf("discovery disabled; unable to start mDNS service: %s\n", err)
return
}

mDNSServer, err := mdns.NewServer(&mdns.Config{Zone: mDNSService})
if err != nil {
log.Printf("discovery disabled; unable to start mDNS service: %s\n", err)
return
}
defer mDNSServer.Shutdown()
<-shutdown
}()

return nil
}

// Get preferred outbound ip of this machine
func getOutboundIP() net.IP {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return "", err
log.Println("No network adapter detected; Using Loopback only")
return net.IPv4(127, 0, 0, 1)
}
defer conn.Close()

localAddr := conn.LocalAddr().(*net.UDPAddr)

uEnc := b64.URLEncoding.EncodeToString(res)
return uEnc, nil
return localAddr.IP
}
4 changes: 2 additions & 2 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ var serverCmd = &cobra.Command{
Short: "remoteRotator Server",
Long: `Run a remoteRotator server
Start a remoteRotator server using a specific transportation protocol.
Start a remoteRotator server using a specific transportation protocols.
`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Please select a transportation protocol (--help for available options)")
fmt.Println("Please select the server type (--help for available options)")
},
}

Expand Down
Loading

0 comments on commit ffbe591

Please sign in to comment.