diff --git a/cmd/goatak_server/client_handler.go b/cmd/goatak_server/client_handler.go index 796b592..17c61c0 100644 --- a/cmd/goatak_server/client_handler.go +++ b/cmd/goatak_server/client_handler.go @@ -33,16 +33,35 @@ type ClientHandler struct { app *App sendChan chan []byte active int32 + ssl bool + user string mx sync.RWMutex } -func NewClientHandler(conn net.Conn, app *App) *ClientHandler { +func NewClientHandler(app *App, conn net.Conn, user string) *ClientHandler { c := &ClientHandler{ conn: conn, app: app, ver: 0, sendChan: make(chan []byte, 10), active: 1, + ssl: false, + user: user, + mx: sync.RWMutex{}, + } + + return c +} + +func NewSSLClientHandler(app *App, conn net.Conn, user string) *ClientHandler { + c := &ClientHandler{ + conn: conn, + app: app, + ver: 0, + sendChan: make(chan []byte, 10), + active: 1, + ssl: true, + user: user, mx: sync.RWMutex{}, } @@ -184,7 +203,7 @@ func (h *ClientHandler) checkFirstMsg(msg *cot.Msg) { } if h.GetCallsign() == "" && msg.TakMessage.GetCotEvent().GetDetail().GetContact() != nil { h.callsign = msg.TakMessage.GetCotEvent().GetDetail().GetContact().Callsign - h.app.AddContact(msg.TakMessage.CotEvent.Uid, model.ContactFromEvent(msg)) + h.app.AddContact(msg.TakMessage.CotEvent.Uid, model.ContactFromMsg(msg)) } } diff --git a/cmd/goatak_server/http_server.go b/cmd/goatak_server/http_server.go index 98ba407..930e4e4 100644 --- a/cmd/goatak_server/http_server.go +++ b/cmd/goatak_server/http_server.go @@ -14,6 +14,13 @@ import ( //go:embed templates var templates embed.FS +type Connection struct { + Uid string `json:"uid"` + User string `json:"user"` + Ssl bool `json:"ssl"` + Ver int32 `json:"ver"` +} + type HttpServer struct { app *App air *air.Air @@ -36,6 +43,7 @@ func NewHttp(app *App, address string, apiAddress string) *HttpServer { a.GET("/map", getMapHandler(app, renderer)) a.GET("/config", getConfigHandler(app)) a.GET("/units", getUnitsHandler(app)) + a.GET("/connections", getConnHandler(app)) a.GET("/stack", getStackHandler()) @@ -154,3 +162,24 @@ func getUnits(app *App) []*model.WebUnit { return units } + +func getConnHandler(app *App) func(req *air.Request, res *air.Response) error { + return func(req *air.Request, res *air.Response) error { + conn := make([]*Connection, 0) + + app.handlers.Range(func(key, value interface{}) bool { + if v, ok := value.(*ClientHandler); ok { + c := &Connection{ + Uid: v.uid, + User: v.user, + Ssl: v.ssl, + Ver: v.ver, + } + conn = append(conn, c) + } + return true + }) + + return res.WriteJSON(conn) + } +} diff --git a/cmd/goatak_server/main.go b/cmd/goatak_server/main.go index 842916b..5783331 100644 --- a/cmd/goatak_server/main.go +++ b/cmd/goatak_server/main.go @@ -184,6 +184,29 @@ func (app *App) GetContact(uid string) *model.Contact { return nil } +func (app *App) ProcessContact(msg *cot.Msg) { + if c := app.GetContact(msg.GetUid()); c != nil { + app.Logger.Infof("update contact %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + c.Update(msg) + } else { + app.Logger.Infof("new contact %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + if msg.GetUid() == app.uid { + return + } + app.units.Store(msg.GetUid(), model.ContactFromMsg(msg)) + } +} + +func (app *App) ProcessUnit(msg *cot.Msg) { + if u := app.GetUnit(msg.GetUid()); u != nil { + app.Logger.Infof("update unit %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + u.Update(msg) + } else { + app.Logger.Infof("new unit %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + app.units.Store(msg.GetUid(), model.UnitFromMsg(msg)) + } +} + func (app *App) AddPoint(uid string, p *model.Point) { if p == nil { return @@ -219,7 +242,7 @@ func (app *App) EventProcessor() { uid = uid[:len(uid)-5] } if c := app.GetContact(uid); c != nil { - c.SetLastSeenNow(nil) + c.Update(nil) } app.SendTo(uid, cot.MakePong()) break @@ -235,14 +258,9 @@ func (app *App) EventProcessor() { msg.GetType(), msg.GetStale().Sub(time.Now())) if msg.IsContact() { - if c := app.GetContact(msg.GetUid()); c != nil { - c.SetLastSeenNow(msg) - } else { - app.Logger.Infof("new contact: uid %s, callsign %s", msg.GetUid(), msg.GetCallsign()) - app.AddContact(msg.GetUid(), model.ContactFromEvent(msg)) - } + app.ProcessContact(msg) } else { - app.AddUnit(msg.GetUid(), model.UnitFromEvent(msg)) + app.ProcessUnit(msg) } // b-m-p-s-p-i digital pointer // b-m-p-s-m point diff --git a/cmd/goatak_server/tcpserver.go b/cmd/goatak_server/tcpserver.go index d1e2922..a9dd784 100644 --- a/cmd/goatak_server/tcpserver.go +++ b/cmd/goatak_server/tcpserver.go @@ -24,7 +24,7 @@ func (app *App) ListenTCP(addr string) (err error) { return err } app.Logger.Infof("TCP connection") - NewClientHandler(conn, app).Start() + NewClientHandler(app, conn, "").Start() } } @@ -77,7 +77,7 @@ func (app *App) ListenSSl(certFile, keyFile, caFile, addr string) error { user := getUser(c1) app.Logger.Infof("user: %s", user) - NewClientHandler(c1, app).Start() + NewSSLClientHandler(app, conn, user).Start() } } diff --git a/cmd/goatak_server/templates/index.html b/cmd/goatak_server/templates/index.html index 1287031..e9ad4b8 100644 --- a/cmd/goatak_server/templates/index.html +++ b/cmd/goatak_server/templates/index.html @@ -4,38 +4,41 @@
ATAK Server
Map
-
-
Contacts
+
Connections
- - - - - - + + + + + + + + + + +
- - {{u.callsign}}{{u.status}}{{printCoords(u.lat, u.lon)}}{{u.tak_version}}
UIDusernameSSLver.
{{c.uid}}{{c.user}}{{c.ssl}}{{c.ver}}
-
Units
+
Contacts
- + + - +
{{u.callsign}}{{u.status}} {{printCoords(u.lat, u.lon)}}{{dt(u.stale_time)}}{{u.tak_version}}
@@ -43,6 +46,21 @@
ATAK Server
+
+
Units
+
+ + + + + + + +
+ + {{u.callsign}}{{printCoords(u.lat, u.lon)}}{{dt(u.stale_time)}}
+
+
Points
diff --git a/cmd/webclient/main.go b/cmd/webclient/main.go index 05bc550..f07baaf 100644 --- a/cmd/webclient/main.go +++ b/cmd/webclient/main.go @@ -307,12 +307,11 @@ func (app *App) sendPing() { } func (app *App) ProcessEvent(msg *cot.Msg) { - switch { case msg.GetType() == "t-x-c-t": app.Logger.Debugf("ping from %s", msg.GetUid()) if c := app.GetContact(msg.GetUid()); c != nil { - c.SetLastSeenNow(nil) + c.Update(nil) } case msg.GetType() == "t-x-c-t-r": app.Logger.Debugf("pong") @@ -328,17 +327,11 @@ func (app *App) ProcessEvent(msg *cot.Msg) { app.Logger.Info("my own info") break } - if c := app.GetContact(msg.GetUid()); c != nil { - app.Logger.Infof("update pos %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) - c.SetLastSeenNow(msg) - } else { - app.Logger.Infof("new contact %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) - app.AddContact(msg.GetUid(), model.ContactFromEvent(msg)) - } + app.ProcessContact(msg) } else { - app.Logger.Infof("new unit %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) - app.AddUnit(msg.GetUid(), model.UnitFromEvent(msg)) + app.ProcessUnit(msg) } + return case strings.HasPrefix(msg.GetType(), "b-"): app.Logger.Infof("point %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) app.AddPoint(msg.GetUid(), model.PointFromEvent(msg)) @@ -347,11 +340,27 @@ func (app *App) ProcessEvent(msg *cot.Msg) { } } -func (app *App) AddUnit(uid string, u *model.Unit) { - if u == nil { - return +func (app *App) ProcessContact(msg *cot.Msg) { + if c := app.GetContact(msg.GetUid()); c != nil { + app.Logger.Infof("update contact %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + c.Update(msg) + } else { + app.Logger.Infof("new contact %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + if msg.GetUid() == app.uid { + return + } + app.units.Store(msg.GetUid(), model.ContactFromMsg(msg)) + } +} + +func (app *App) ProcessUnit(msg *cot.Msg) { + if u := app.GetUnit(msg.GetUid()); u != nil { + app.Logger.Infof("update unit %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + u.Update(msg) + } else { + app.Logger.Infof("new unit %s (%s) %s", msg.GetUid(), msg.GetCallsign(), msg.GetType()) + app.units.Store(msg.GetUid(), model.UnitFromMsg(msg)) } - app.units.Store(uid, u) } func (app *App) AddPoint(uid string, u *model.Point) { @@ -361,17 +370,6 @@ func (app *App) AddPoint(uid string, u *model.Point) { app.units.Store(uid, u) } -func (app *App) GetUnit(uid string) *model.Unit { - if v, ok := app.units.Load(uid); ok { - if unit, ok := v.(*model.Unit); ok { - return unit - } else { - app.Logger.Errorf("invalid object for uid %s: %v", uid, v) - } - } - return nil -} - func (app *App) Remove(uid string) { if _, ok := app.units.Load(uid); ok { app.units.Delete(uid) @@ -390,7 +388,18 @@ func (app *App) GetContact(uid string) *model.Contact { if contact, ok := v.(*model.Contact); ok { return contact } else { - app.Logger.Errorf("invalid object for uid %s: %v", uid, v) + app.Logger.Warnf("invalid object for uid %s: %s", uid, v) + } + } + return nil +} + +func (app *App) GetUnit(uid string) *model.Unit { + if v, ok := app.units.Load(uid); ok { + if contact, ok := v.(*model.Unit); ok { + return contact + } else { + app.Logger.Warnf("invalid unit for uid %s: %s", uid, v) } } return nil @@ -399,13 +408,22 @@ func (app *App) GetContact(uid string) *model.Contact { func (app *App) removeByLink(msg *cot.Msg) { if msg.Detail != nil && msg.Detail.HasChild("link") { uid := msg.Detail.GetFirstChild("link").GetAttr("uid") + typ := msg.Detail.GetFirstChild("link").GetAttr("type") if uid == "" { app.Logger.Errorf("invalid remove message: %s", msg.Detail) return } - app.Logger.Debugf("remove %s by message", uid) - if c := app.GetContact(uid); c != nil { - c.SetOffline() + if v, ok := app.units.Load(uid); ok { + switch vv := v.(type) { + case *model.Contact: + app.Logger.Debugf("remove %s by message", uid) + vv.SetOffline() + return + case *model.Unit, *model.Point: + app.Logger.Debugf("remove unit/point %s type %s by message", uid, typ) + //app.units.Delete(uid) + return + } } } } diff --git a/cot/msg.go b/cot/msg.go index c97b23e..964b631 100644 --- a/cot/msg.go +++ b/cot/msg.go @@ -83,6 +83,30 @@ func (m *Msg) PrintChat() string { return fmt.Sprintf("%s -> %s: \"%s\"", from, to, text) } +func (m *Msg) GetLatLon() (float64, float64) { + if m == nil { + return 0, 0 + } + + return m.TakMessage.GetCotEvent().GetLat(), m.TakMessage.GetCotEvent().GetLon() +} + +func (m *Msg) GetLat() float64 { + if m == nil { + return 0 + } + + return m.TakMessage.GetCotEvent().GetLat() +} + +func (m *Msg) GetLon() float64 { + if m == nil { + return 0 + } + + return m.TakMessage.GetCotEvent().GetLon() +} + func TimeFromMillis(ms uint64) time.Time { return time.Unix(0, 1000000*int64(ms)) } diff --git a/model/geo.go b/model/geo.go new file mode 100644 index 0000000..cb1cde8 --- /dev/null +++ b/model/geo.go @@ -0,0 +1,26 @@ +package model + +import ( + "math" +) + +func DistBea(lat1, lon1, lat2, lon2 float64) (float64, float64) { + toRadian := math.Pi / 180 + // haversine formula + // bearing + y := math.Sin((lon2-lon1)*toRadian) * math.Cos(lat2*toRadian) + x := math.Cos(lat1*toRadian)*math.Sin(lat2*toRadian) - math.Sin(lat1*toRadian)*math.Cos(lat2*toRadian)*math.Cos((lon2-lon1)*toRadian) + bea := math.Atan2(y, x) * 180 / math.Pi + + if bea < 0 { + bea += 360 + } + // distance + R := 6371000. // meters + deltaF := (lat2 - lat1) * toRadian + deltaL := (lon2 - lon1) * toRadian + a := math.Sin(deltaF/2)*math.Sin(deltaF/2) + math.Cos(lat1*toRadian)*math.Cos(lat2*toRadian)*math.Sin(deltaL/2)*math.Sin(deltaL/2) + c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) + dist := R * c + return dist, bea +} diff --git a/model/http.go b/model/http.go index 97356fb..4201375 100644 --- a/model/http.go +++ b/model/http.go @@ -43,27 +43,8 @@ func (c *Contact) ToWeb() *WebUnit { c.mx.RLock() defer c.mx.RUnlock() - evt := c.msg.TakMessage.CotEvent - - w := &WebUnit{ - Uid: c.uid, - Callsign: c.callsign, - Category: "contact", - Time: cot.TimeFromMillis(evt.SendTime), - LastSeen: c.lastSeen, - StaleTime: c.staleTime, - StartTime: c.startTime, - SendTime: c.sendTime, - Type: c.type_, - Lat: evt.Lat, - Lon: evt.Lon, - Hae: evt.Hae, - Speed: evt.GetDetail().GetTrack().GetSpeed(), - Course: evt.GetDetail().GetTrack().GetCourse(), - Team: evt.GetDetail().GetGroup().GetName(), - Role: evt.GetDetail().GetGroup().GetRole(), - Sidc: getSIDC(c.type_), - } + w := c.Item.ToWeb() + w.Category = "contact" if c.online { w.Status = "Online" @@ -71,58 +52,49 @@ func (c *Contact) ToWeb() *WebUnit { w.Status = "Offline" } - if v := evt.GetDetail().GetTakv(); v != nil { + if v := c.msg.TakMessage.GetCotEvent().GetDetail().GetTakv(); v != nil { w.TakVersion = strings.Trim(fmt.Sprintf("%s %s on %s", v.Platform, v.Version, v.Device), " ") } - w.Text, _ = c.msg.Detail.GetChildValue("remarks") return w } func (u *Unit) ToWeb() *WebUnit { - evt := u.msg.TakMessage.CotEvent - - w := &WebUnit{ - Uid: u.uid, - Callsign: u.callsign, - Category: "unit", - Time: cot.TimeFromMillis(evt.SendTime), - LastSeen: u.received, - StaleTime: u.staleTime, - StartTime: u.startTime, - SendTime: u.sendTime, - Type: u.type_, - Lat: evt.Lat, - Lon: evt.Lon, - Hae: evt.Hae, - Speed: evt.GetDetail().GetTrack().GetSpeed(), - Course: evt.GetDetail().GetTrack().GetCourse(), - Team: evt.GetDetail().GetGroup().GetName(), - Role: evt.GetDetail().GetGroup().GetRole(), - Sidc: getSIDC(u.type_), - } - w.Text, _ = u.msg.Detail.GetChildValue("remarks") + w := u.Item.ToWeb() + w.Category = "unit" return w } func (p *Point) ToWeb() *WebUnit { - evt := p.msg.TakMessage.GetCotEvent() + w := p.Item.ToWeb() + w.Category = "point" + + return w +} + +func (i Item) ToWeb() *WebUnit { + evt := i.msg.TakMessage.CotEvent w := &WebUnit{ - Uid: p.uid, - Callsign: p.callsign, - Category: "point", - StaleTime: p.staleTime, - StartTime: p.startTime, - SendTime: p.sendTime, - Type: p.type_, + Uid: i.uid, + Callsign: i.callsign, + Time: cot.TimeFromMillis(evt.SendTime), + LastSeen: i.received, + StaleTime: i.staleTime, + StartTime: i.startTime, + SendTime: i.sendTime, + Type: i.type_, Lat: evt.Lat, Lon: evt.Lon, Hae: evt.Hae, Speed: evt.GetDetail().GetTrack().GetSpeed(), Course: evt.GetDetail().GetTrack().GetCourse(), + Team: evt.GetDetail().GetGroup().GetName(), + Role: evt.GetDetail().GetGroup().GetRole(), + Sidc: getSIDC(i.type_), } - w.Text, _ = p.msg.Detail.GetChildValue("remarks") + + w.Text, _ = i.msg.Detail.GetChildValue("remarks") return w } diff --git a/model/unit.go b/model/unit.go index 4a744a8..edd0136 100644 --- a/model/unit.go +++ b/model/unit.go @@ -1,6 +1,7 @@ package model import ( + "fmt" "sync" "time" @@ -11,7 +12,14 @@ const ( staleContactDelete = time.Minute * 30 ) -type Unit struct { +type Pos struct { + time time.Time + lat float64 + lon float64 + speed float64 +} + +type Item struct { uid string type_ string callsign string @@ -22,32 +30,42 @@ type Unit struct { msg *cot.Msg } +type Unit struct { + Item + mx sync.RWMutex + track []*Pos +} + type Contact struct { - uid string - type_ string - callsign string - staleTime time.Time - startTime time.Time - sendTime time.Time - lastSeen time.Time - msg *cot.Msg - online bool - mx sync.RWMutex + Item + online bool + lastSeen time.Time + mx sync.RWMutex + track []*Pos } type Point struct { - uid string - type_ string - callsign string - staleTime time.Time - startTime time.Time - sendTime time.Time - received time.Time - msg *cot.Msg + Item authorCallsign string authorUid string } +func (c *Contact) String() string { + c.mx.RLock() + defer c.mx.RUnlock() + return fmt.Sprintf("contact: %s %s %s", c.uid, c.type_, c.callsign) +} + +func (u *Unit) String() string { + u.mx.RLock() + defer u.mx.RUnlock() + return fmt.Sprintf("unit: %s %s %s", u.uid, u.type_, u.callsign) +} + +func (p *Point) String() string { + return fmt.Sprintf("point: %s %s %s", p.uid, p.type_, p.callsign) +} + func (u *Unit) GetMsg() *cot.Msg { return u.msg } @@ -97,55 +115,98 @@ func (c *Contact) IsOnline() bool { return c.online } -func ContactFromEvent(msg *cot.Msg) *Contact { +func ContactFromMsg(msg *cot.Msg) *Contact { + pos := &Pos{ + time: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetSendTime()), + lat: msg.TakMessage.GetCotEvent().GetLat(), + lon: msg.TakMessage.GetCotEvent().GetLon(), + speed: msg.TakMessage.GetCotEvent().GetDetail().GetTrack().GetSpeed(), + } + return &Contact{ - uid: msg.TakMessage.GetCotEvent().GetUid(), - callsign: msg.TakMessage.GetCotEvent().GetDetail().GetContact().GetCallsign(), - lastSeen: time.Now(), - staleTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetStaleTime()), - startTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetStartTime()), - sendTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetSendTime()), - type_: msg.TakMessage.GetCotEvent().GetType(), - msg: msg, - online: true, - mx: sync.RWMutex{}, + Item: ItemFromMsg(msg), + online: true, + lastSeen: time.Now(), + mx: sync.RWMutex{}, + track: []*Pos{pos}, } } -func UnitFromEvent(msg *cot.Msg) *Unit { +func UnitFromMsg(msg *cot.Msg) *Unit { + pos := &Pos{ + time: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetSendTime()), + lat: msg.TakMessage.GetCotEvent().GetLat(), + lon: msg.TakMessage.GetCotEvent().GetLon(), + speed: msg.TakMessage.GetCotEvent().GetDetail().GetTrack().GetSpeed(), + } + return &Unit{ - uid: msg.TakMessage.GetCotEvent().GetUid(), - callsign: msg.TakMessage.GetCotEvent().GetDetail().GetContact().GetCallsign(), - staleTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetStaleTime()), - startTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetStartTime()), - sendTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetSendTime()), - type_: msg.TakMessage.GetCotEvent().GetType(), - msg: msg, - received: time.Now(), + Item: ItemFromMsg(msg), + mx: sync.RWMutex{}, + track: []*Pos{pos}, } } func PointFromEvent(msg *cot.Msg) *Point { return &Point{ + Item: ItemFromMsg(msg), + } +} + +func ItemFromMsg(msg *cot.Msg) Item { + return Item{ uid: msg.TakMessage.GetCotEvent().GetUid(), + type_: msg.TakMessage.GetCotEvent().GetType(), callsign: msg.TakMessage.GetCotEvent().GetDetail().GetContact().GetCallsign(), staleTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetStaleTime()), startTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetStartTime()), sendTime: cot.TimeFromMillis(msg.TakMessage.GetCotEvent().GetSendTime()), - type_: msg.TakMessage.GetCotEvent().GetType(), msg: msg, received: time.Now(), } } -func (c *Contact) SetLastSeenNow(msg *cot.Msg) { +func (i *Item) GetLanLon() (float64, float64) { + return i.msg.TakMessage.GetCotEvent().GetLat(), i.msg.TakMessage.GetCotEvent().GetLon() +} + +func (c *Contact) Update(msg *cot.Msg) { c.mx.Lock() defer c.mx.Unlock() c.online = true c.lastSeen = time.Now() if msg != nil { + pos := getPos(c.msg, msg) c.msg = msg + + if pos != nil { + c.track = append(c.track, pos) + + if len(c.track) > 5000 { + c.track = c.track[len(c.track)-5000:] + } + } + } +} + +func (u *Unit) Update(msg *cot.Msg) { + u.mx.Lock() + defer u.mx.Unlock() + + u.Item = ItemFromMsg(msg) + + if msg != nil { + pos := getPos(u.msg, msg) + u.msg = msg + + if pos != nil { + u.track = append(u.track, pos) + + if len(u.track) > 5000 { + u.track = u.track[len(u.track)-5000:] + } + } } } @@ -155,3 +216,34 @@ func (c *Contact) SetOffline() { c.online = false } + +func getPos(msg1, msg2 *cot.Msg) *Pos { + if msg1 == nil || msg2 == nil { + return nil + } + + lat1, lon1 := msg1.GetLatLon() + + if lat1 == 0 && lon1 == 0 { + return nil + } + + lat2, lon2 := msg2.GetLatLon() + + if lat2 == 0 && lon2 == 0 { + return nil + } + + dist, _ := DistBea(lat1, lon1, lat2, lon2) + + if dist > 25 { + return &Pos{ + time: cot.TimeFromMillis(msg2.TakMessage.GetCotEvent().GetSendTime()), + lat: lat2, + lon: lon2, + speed: msg2.TakMessage.GetCotEvent().GetDetail().GetTrack().GetSpeed(), + } + } + + return nil +} diff --git a/staticfiles/static/js/main.js b/staticfiles/static/js/main.js index 9f07f76..02217d8 100644 --- a/staticfiles/static/js/main.js +++ b/staticfiles/static/js/main.js @@ -46,6 +46,7 @@ let app = new Vue({ el: '#app', data: { units: new Map(), + connections: new Map(), alert: null, ts: 0, }, @@ -55,11 +56,15 @@ let app = new Vue({ this.timer = setInterval(this.renew, 3000); }, computed: { + all_conns: function () { + return this.ts && this.connections.values(); + }, }, methods: { renew: function () { let vm = this; let units = vm.units; + let conns = vm.connections; fetch('/units') .then(function (response) { @@ -72,6 +77,17 @@ let app = new Vue({ }); vm.ts += 1; }); + fetch('/connections') + .then(function (response) { + return response.json() + }) + .then(function (data) { + conns.clear(); + data.forEach(function (i) { + conns.set(i.uid, i); + }); + vm.ts += 1; + }); }, byCategory: function (s) { let arr = Array.from(this.units.values()).filter(function (u) {