tile38/controller/fence.go

220 lines
5.9 KiB
Go
Raw Normal View History

2016-03-19 17:16:19 +03:00
package controller
import (
2016-05-23 23:01:42 +03:00
"strconv"
2016-03-19 17:16:19 +03:00
"strings"
2016-07-12 22:18:16 +03:00
"github.com/tidwall/tile38/controller/glob"
2016-03-30 19:32:38 +03:00
"github.com/tidwall/tile38/controller/server"
2016-03-19 17:16:19 +03:00
"github.com/tidwall/tile38/geojson"
)
2016-04-02 17:20:30 +03:00
var tmfmt = "2006-01-02T15:04:05.999999999Z07:00"
2016-04-03 05:16:36 +03:00
// FenceMatch executes a fence match returns back json messages for fence detection.
2016-04-02 17:20:30 +03:00
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) []string {
2016-04-03 00:13:20 +03:00
jshookName := jsonString(hookName)
jstime := jsonString(details.timestamp.Format(tmfmt))
2016-07-12 22:18:16 +03:00
pattern := fence.glob
2016-03-19 17:16:19 +03:00
if details.command == "drop" {
2016-04-03 00:13:20 +03:00
return []string{`{"cmd":"drop","hook":` + jshookName + `,"time":` + jstime + `}`}
2016-03-19 17:16:19 +03:00
}
match := true
2016-07-12 22:18:16 +03:00
if pattern != "" && pattern != "*" {
match, _ = glob.Match(pattern, details.id)
2016-03-19 17:16:19 +03:00
}
if !match {
return nil
}
2016-04-03 00:13:20 +03:00
sw.mu.Lock()
nofields := sw.nofields
sw.mu.Unlock()
2016-07-10 23:23:50 +03:00
if details.obj == nil || !details.obj.IsGeometry() || (details.command == "fset" && nofields) {
2016-03-19 17:16:19 +03:00
return nil
}
match = false
2016-05-23 23:01:42 +03:00
var roamkeys, roamids []string
var roammeters []float64
2016-03-19 17:16:19 +03:00
detect := "outside"
if fence != nil {
2016-05-23 23:01:42 +03:00
if fence.roam.on {
if details.command == "set" {
// println("roam", fence.roam.key, fence.roam.id, strconv.FormatFloat(fence.roam.meters, 'f', -1, 64))
roamkeys, roamids, roammeters = fenceMatchRoam(sw.c, fence, details.key, details.id, details.obj)
}
if len(roamids) == 0 || len(roamids) != len(roamkeys) {
return nil
2016-04-02 17:20:30 +03:00
}
2016-05-23 23:01:42 +03:00
match = true
detect = "roam"
2016-03-19 17:16:19 +03:00
} else {
2016-05-23 23:01:42 +03:00
// not using roaming
match1 := fenceMatchObject(fence, details.oldObj)
match2 := fenceMatchObject(fence, details.obj)
if match1 && match2 {
match = true
detect = "inside"
} else if match1 && !match2 {
match = true
detect = "exit"
} else if !match1 && match2 {
match = true
detect = "enter"
if details.command == "fset" {
detect = "inside"
}
} else {
if details.command != "fset" {
// Maybe the old object and new object create a line that crosses the fence.
// Must detect for that possibility.
if details.oldObj != nil {
ls := geojson.LineString{
Coordinates: []geojson.Position{
details.oldObj.CalculatedPoint(),
details.obj.CalculatedPoint(),
},
}
temp := false
if fence.cmd == "within" {
// because we are testing if the line croses the area we need to use
// "intersects" instead of "within".
fence.cmd = "intersects"
temp = true
}
if fenceMatchObject(fence, ls) {
//match = true
detect = "cross"
}
if temp {
fence.cmd = "within"
}
2016-04-02 17:20:30 +03:00
}
2016-03-19 17:16:19 +03:00
}
}
}
}
if details.command == "del" {
2016-04-03 00:13:20 +03:00
return []string{`{"command":"del","hook":` + jshookName + `,"id":` + jsonString(details.id) + `,"time":` + jstime + `}`}
2016-03-19 17:16:19 +03:00
}
2016-04-02 17:20:30 +03:00
if details.fmap == nil {
2016-03-19 17:16:19 +03:00
return nil
}
2016-04-03 00:13:20 +03:00
sw.mu.Lock()
2016-04-02 17:20:30 +03:00
sw.fmap = details.fmap
2016-03-19 17:16:19 +03:00
sw.fullFields = true
2016-03-30 19:32:38 +03:00
sw.msg.OutputType = server.JSON
2016-04-03 00:13:20 +03:00
sw.writeObject(details.id, details.obj, details.fields, true)
2016-03-19 17:16:19 +03:00
if sw.wr.Len() == 0 {
2016-04-03 00:13:20 +03:00
sw.mu.Unlock()
2016-03-19 17:16:19 +03:00
return nil
}
2016-05-23 23:01:42 +03:00
2016-03-19 17:16:19 +03:00
res := sw.wr.String()
2016-04-03 00:13:20 +03:00
resb := make([]byte, len(res))
copy(resb, res)
2016-03-19 17:16:19 +03:00
sw.wr.Reset()
2016-05-23 23:01:42 +03:00
res = string(resb)
2016-05-23 23:40:08 +03:00
if strings.HasPrefix(res, ",") {
res = res[1:]
}
2016-03-19 17:16:19 +03:00
if sw.output == outputIDs {
res = `{"id":` + res + `}`
}
2016-04-03 00:13:20 +03:00
sw.mu.Unlock()
jskey := jsonString(details.key)
2016-05-23 23:01:42 +03:00
2016-04-01 22:46:39 +03:00
ores := res
msgs := make([]string, 0, 2)
if fence.detect == nil || fence.detect[detect] {
if strings.HasPrefix(ores, "{") {
2016-04-03 00:13:20 +03:00
res = `{"command":"` + details.command + `","detect":"` + detect + `","hook":` + jshookName + `,"key":` + jskey + `,"time":` + jstime + `,` + ores[1:]
2016-04-01 22:46:39 +03:00
}
msgs = append(msgs, res)
2016-03-19 17:16:19 +03:00
}
switch detect {
case "enter":
2016-04-01 22:46:39 +03:00
if fence.detect == nil || fence.detect["inside"] {
2016-04-03 00:13:20 +03:00
msgs = append(msgs, `{"command":"`+details.command+`","detect":"inside","hook":`+jshookName+`,"key":`+jskey+`,"time":`+jstime+`,`+ores[1:])
2016-04-01 22:46:39 +03:00
}
2016-03-19 17:16:19 +03:00
case "exit", "cross":
2016-04-01 22:46:39 +03:00
if fence.detect == nil || fence.detect["outside"] {
2016-04-03 00:13:20 +03:00
msgs = append(msgs, `{"command":"`+details.command+`","detect":"outside","hook":`+jshookName+`,"key":`+jskey+`,"time":`+jstime+`,`+ores[1:])
2016-04-01 22:46:39 +03:00
}
2016-05-23 23:01:42 +03:00
case "roam":
if len(msgs) > 0 {
var nmsgs []string
msg := msgs[0][:len(msgs[0])-1]
for i, id := range roamids {
nmsgs = append(nmsgs, msg+`,"nearby":{"key":`+jsonString(roamkeys[i])+`,"id":`+jsonString(id)+`,"meters":`+strconv.FormatFloat(roammeters[i], 'f', -1, 64)+`}}`)
}
msgs = nmsgs
}
2016-03-19 17:16:19 +03:00
}
return msgs
}
func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
if obj == nil {
return false
}
2016-05-23 23:01:42 +03:00
if fence.roam.on {
// we need to check this object against
return false
}
2016-03-19 17:16:19 +03:00
if fence.cmd == "nearby" {
return obj.Nearby(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}, fence.meters)
} else if fence.cmd == "within" {
if fence.o != nil {
return obj.Within(fence.o)
}
2016-04-03 05:16:36 +03:00
return obj.WithinBBox(geojson.BBox{
Min: geojson.Position{X: fence.minLon, Y: fence.minLat, Z: 0},
Max: geojson.Position{X: fence.maxLon, Y: fence.maxLat, Z: 0},
})
2016-03-19 17:16:19 +03:00
} else if fence.cmd == "intersects" {
if fence.o != nil {
return obj.Intersects(fence.o)
}
2016-04-03 05:16:36 +03:00
return obj.IntersectsBBox(geojson.BBox{
Min: geojson.Position{X: fence.minLon, Y: fence.minLat, Z: 0},
Max: geojson.Position{X: fence.maxLon, Y: fence.maxLat, Z: 0},
})
2016-03-19 17:16:19 +03:00
}
return false
}
2016-05-23 23:01:42 +03:00
func fenceMatchRoam(c *Controller, fence *liveFenceSwitches, tkey, tid string, obj geojson.Object) (keys, ids []string, meterss []float64) {
c.mu.RLock()
defer c.mu.RUnlock()
col := c.getCol(fence.roam.key)
if col == nil {
return
}
p := obj.CalculatedPoint()
col.Nearby(0, 0, p.Y, p.X, fence.roam.meters,
func(id string, obj geojson.Object, fields []float64) bool {
var match bool
if id == tid {
return true // skip self
}
if fence.roam.pattern {
2016-07-12 22:18:16 +03:00
match, _ = glob.Match(fence.roam.id, id)
2016-05-23 23:01:42 +03:00
} else {
match = fence.roam.id == id
}
if match {
keys = append(keys, fence.roam.key)
ids = append(ids, id)
meterss = append(meterss, obj.CalculatedPoint().DistanceTo(p))
}
return true
},
)
return
}