diff --git a/Makefile b/Makefile index ae2359b0..0f2e7ab8 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ GOARM ?= 7 all: GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go get -t -d -v ./... - GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -ldflags " -X main.stratuxVersion=`git describe --abbrev=0 --tags` -X main.stratuxBuild=`git log -n 1 --pretty=%H`" main/gen_gdl90.go main/traffic.go main/ry835ai.go main/network.go main/managementinterface.go main/sdr.go + GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -ldflags " -X main.stratuxVersion=`git describe --abbrev=0 --tags` -X main.stratuxBuild=`git log -n 1 --pretty=%H`" main/gen_gdl90.go main/traffic.go main/ry835ai.go main/network.go main/managementinterface.go main/sdr.go main/uibroadcast.go cd dump978 && make lib test: diff --git a/main/gen_gdl90.go b/main/gen_gdl90.go index f96252c8..2b82997f 100644 --- a/main/gen_gdl90.go +++ b/main/gen_gdl90.go @@ -475,9 +475,7 @@ type WeatherMessage struct { LocaltimeReceived time.Time } -var weatherMessages []WeatherMessage - -// Send update to attached client. +// Send update to connected websockets. func registerADSBTextMessageReceived(msg string) { x := strings.Split(msg, " ") if len(x) < 5 { @@ -492,16 +490,10 @@ func registerADSBTextMessageReceived(msg string) { wm.Data = strings.Join(x[3:], " ") wm.LocaltimeReceived = time.Now() - //FIXME: Fixed log size currently - determine what works best for the web interface. - n := len(weatherMessages) - if n >= 2500 { - weatherMessages = weatherMessages[1:] - } - - weatherMessages = append(weatherMessages, wm) + wmJSON, _ := json.Marshal(&wm) // Send to weatherUpdate channel for any connected clients. - weatherUpdate <- wm + weatherUpdate.Send(wmJSON) } func parseInput(buf string) ([]byte, uint16) { @@ -712,7 +704,7 @@ func defaultSettings() { globalSettings.ES_Enabled = false //TODO globalSettings.GPS_Enabled = false //TODO //FIXME: Need to change format below. - globalSettings.NetworkOutputs = []networkConnection{{nil, "", 4000, NETWORK_GDL90_STANDARD | NETWORK_AHRS_GDL90, nil, time.Time{}, time.Time{}, 0}, {nil, "", 49002, NETWORK_AHRS_FFSIM, nil, time.Time{}, time.Time{}, 0}} + globalSettings.NetworkOutputs = []networkConnection{{nil, "", 4000, NETWORK_GDL90_STANDARD | NETWORK_AHRS_GDL90, nil, time.Time{}, time.Time{}, 0}, {nil, "", 49002, NETWORK_AHRS_FFSIM, nil, time.Time{}, time.Time{}, 0}} globalSettings.AHRS_Enabled = false globalSettings.DEBUG = false globalSettings.ReplayLog = false //TODO: 'true' for debug builds. @@ -826,7 +818,6 @@ func main() { ADSBTowers = make(map[string]ADSBTower) MsgLog = make([]msg, 0) - weatherMessages = make([]WeatherMessage, 0) crcInit() // Initialize CRC16 table. sdrInit() diff --git a/main/managementinterface.go b/main/managementinterface.go index ac9c82ff..13df2766 100644 --- a/main/managementinterface.go +++ b/main/managementinterface.go @@ -17,24 +17,28 @@ type SettingMessage struct { } // Weather updates channel. -var weatherUpdate chan WeatherMessage -var trafficUpdate chan TrafficInfo +var weatherUpdate *uibroadcaster +var trafficUpdate *uibroadcaster /* The /weather websocket starts off by sending the current buffer of weather messages, then sends updates as they are received. */ func handleWeatherWS(conn *websocket.Conn) { - // Send current buffer. - for _, w := range weatherMessages { - weatherJSON, _ := json.Marshal(&w) - conn.Write(weatherJSON) - } - // Wait for updates and send as they are received. + // Subscribe the socket to receive updates. + weatherUpdate.AddSocket(conn) + + // Connection closes when function returns. Since uibroadcast is writing and we don't need to read anything (for now), just keep it busy. for { - lastUpdate := <-weatherUpdate - weatherJSON, _ := json.Marshal(&lastUpdate) - conn.Write(weatherJSON) + buf := make([]byte, 1024) + _, err := conn.Read(buf) + if err != nil { + break + } + if buf[0] != 0 { // Dummy. + continue + } + time.Sleep(1 * time.Second) } } @@ -50,10 +54,20 @@ func handleTrafficWS(conn *websocket.Conn) { conn.Write(trafficJSON) } trafficMutex.Unlock() + // Subscribe the socket to receive updates. + trafficUpdate.AddSocket(conn) + + // Connection closes when function returns. Since uibroadcast is writing and we don't need to read anything (for now), just keep it busy. for { - lastUpdate := <-trafficUpdate - trafficJSON, _ := json.Marshal(&lastUpdate) - conn.Write(trafficJSON) + buf := make([]byte, 1024) + _, err := conn.Read(buf) + if err != nil { + break + } + if buf[0] != 0 { // Dummy. + continue + } + time.Sleep(1 * time.Second) } } @@ -183,8 +197,8 @@ func handleSettingsSetRequest(w http.ResponseWriter, r *http.Request) { } func managementInterface() { - weatherUpdate = make(chan WeatherMessage, 1024) - trafficUpdate = make(chan TrafficInfo, 1024) + weatherUpdate = NewUIBroadcaster() + trafficUpdate = NewUIBroadcaster() http.Handle("/", http.FileServer(http.Dir("/var/www"))) http.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log")))) @@ -207,7 +221,6 @@ func managementInterface() { s.ServeHTTP(w, req) }) - http.HandleFunc("/getSituation", handleSituationRequest) http.HandleFunc("/getTowers", handleTowersRequest) http.HandleFunc("/getSettings", handleSettingsGetRequest) diff --git a/main/traffic.go b/main/traffic.go index 7d093570..8248e207 100644 --- a/main/traffic.go +++ b/main/traffic.go @@ -3,6 +3,7 @@ package main import ( "bufio" "encoding/hex" + "encoding/json" "math" "net" "strconv" @@ -102,9 +103,12 @@ func sendTrafficUpdates() { // Send update to attached client. func registerTrafficUpdate(ti TrafficInfo) { - if ti.Position_valid { // Don't send unless a valid position exists. - trafficUpdate <- ti + if !ti.Position_valid { // Don't send unless a valid position exists. + return } + + tiJSON, _ := json.Marshal(&ti) + trafficUpdate.Send(tiJSON) } func makeTrafficReport(ti TrafficInfo) { diff --git a/main/uibroadcast.go b/main/uibroadcast.go new file mode 100644 index 00000000..e6c1a65b --- /dev/null +++ b/main/uibroadcast.go @@ -0,0 +1,42 @@ +package main + +import ( + "golang.org/x/net/websocket" +) + +type uibroadcaster struct { + sockets []*websocket.Conn + messages chan []byte +} + +func NewUIBroadcaster() *uibroadcaster { + ret := &uibroadcaster{ + sockets: make([]*websocket.Conn, 0), + messages: make(chan []byte, 1024), + } + go ret.writer() + return ret +} + +func (u *uibroadcaster) Send(msg []byte) { + u.messages <- msg +} + +func (u *uibroadcaster) AddSocket(sock *websocket.Conn) { + u.sockets = append(u.sockets, sock) +} + +func (u *uibroadcaster) writer() { + for { + msg := <-u.messages + // Send to all. + p := make([]*websocket.Conn, 0) // Keep a list of the writeable sockets. + for _, sock := range u.sockets { + _, err := sock.Write(msg) + if err == nil { + p = append(p, sock) + } + } + u.sockets = p // Save the list of writeable sockets. + } +}