2018-10-29 01:49:45 +03:00
|
|
|
package server
|
2016-03-19 17:16:19 +03:00
|
|
|
|
|
|
|
import (
|
2016-10-03 21:37:16 +03:00
|
|
|
"math"
|
2020-03-22 21:54:56 +03:00
|
|
|
"sort"
|
2016-05-23 23:01:42 +03:00
|
|
|
"strconv"
|
2016-12-28 21:16:28 +03:00
|
|
|
"time"
|
2016-03-19 17:16:19 +03:00
|
|
|
|
2018-10-11 00:25:40 +03:00
|
|
|
"github.com/tidwall/geojson"
|
2019-01-06 20:23:57 +03:00
|
|
|
"github.com/tidwall/geojson/geo"
|
2018-10-11 00:25:40 +03:00
|
|
|
"github.com/tidwall/geojson/geometry"
|
2016-12-15 20:00:08 +03:00
|
|
|
"github.com/tidwall/gjson"
|
2018-10-11 00:25:40 +03:00
|
|
|
"github.com/tidwall/tile38/internal/glob"
|
2016-03-19 17:16:19 +03:00
|
|
|
)
|
|
|
|
|
2016-04-03 05:16:36 +03:00
|
|
|
// FenceMatch executes a fence match returns back json messages for fence detection.
|
2018-11-24 01:53:33 +03:00
|
|
|
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, metas []FenceMeta, details *commandDetails) []string {
|
2016-12-29 18:50:54 +03:00
|
|
|
msgs := fenceMatch(hookName, sw, fence, metas, details)
|
2016-12-15 20:00:08 +03:00
|
|
|
if len(fence.accept) == 0 {
|
|
|
|
return msgs
|
|
|
|
}
|
2018-08-14 03:05:30 +03:00
|
|
|
nmsgs := make([]string, 0, len(msgs))
|
2016-12-15 20:00:08 +03:00
|
|
|
for _, msg := range msgs {
|
2018-08-14 03:05:30 +03:00
|
|
|
if fence.accept[gjson.Get(msg, "command").String()] {
|
2016-12-15 20:00:08 +03:00
|
|
|
nmsgs = append(nmsgs, msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nmsgs
|
|
|
|
}
|
2016-12-29 18:50:54 +03:00
|
|
|
func appendHookDetails(b []byte, hookName string, metas []FenceMeta) []byte {
|
|
|
|
if len(hookName) > 0 {
|
|
|
|
b = append(b, `,"hook":`...)
|
|
|
|
b = appendJSONString(b, hookName)
|
|
|
|
}
|
|
|
|
if len(metas) > 0 {
|
|
|
|
b = append(b, `,"meta":{`...)
|
|
|
|
for i, meta := range metas {
|
|
|
|
if i > 0 {
|
|
|
|
b = append(b, ',')
|
|
|
|
}
|
|
|
|
b = appendJSONString(b, meta.Name)
|
|
|
|
b = append(b, ':')
|
|
|
|
b = appendJSONString(b, meta.Value)
|
|
|
|
}
|
|
|
|
b = append(b, '}')
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
2018-10-11 00:25:40 +03:00
|
|
|
|
|
|
|
func objIsSpatial(obj geojson.Object) bool {
|
|
|
|
_, ok := obj.(geojson.Spatial)
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2016-12-29 18:50:54 +03:00
|
|
|
func hookJSONString(hookName string, metas []FenceMeta) string {
|
|
|
|
return string(appendHookDetails(nil, hookName, metas))
|
|
|
|
}
|
2018-08-14 20:48:28 +03:00
|
|
|
func fenceMatch(
|
|
|
|
hookName string, sw *scanWriter, fence *liveFenceSwitches,
|
2018-11-24 01:53:33 +03:00
|
|
|
metas []FenceMeta, details *commandDetails,
|
2018-08-14 20:48:28 +03:00
|
|
|
) []string {
|
2016-03-19 17:16:19 +03:00
|
|
|
if details.command == "drop" {
|
2018-08-14 03:05:30 +03:00
|
|
|
return []string{
|
|
|
|
`{"command":"drop"` + hookJSONString(hookName, metas) +
|
2020-03-22 21:54:56 +03:00
|
|
|
`,"key":` + jsonString(details.key) +
|
2018-08-14 03:05:30 +03:00
|
|
|
`,"time":` + jsonTimeFormat(details.timestamp) + `}`,
|
|
|
|
}
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
2016-12-28 21:16:28 +03:00
|
|
|
if len(fence.glob) > 0 && !(len(fence.glob) == 1 && fence.glob[0] == '*') {
|
|
|
|
match, _ := glob.Match(fence.glob, details.id)
|
|
|
|
if !match {
|
|
|
|
return nil
|
|
|
|
}
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
2018-10-11 00:25:40 +03:00
|
|
|
if details.obj == nil || !objIsSpatial(details.obj) {
|
2016-03-19 17:16:19 +03:00
|
|
|
return nil
|
|
|
|
}
|
2016-12-28 21:16:28 +03:00
|
|
|
if details.command == "fset" {
|
|
|
|
sw.mu.Lock()
|
|
|
|
nofields := sw.nofields
|
|
|
|
sw.mu.Unlock()
|
|
|
|
if nofields {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if details.command == "del" {
|
2018-08-14 03:05:30 +03:00
|
|
|
return []string{
|
2018-08-14 20:48:28 +03:00
|
|
|
`{"command":"del"` + hookJSONString(hookName, metas) +
|
2020-03-22 03:48:31 +03:00
|
|
|
`,"key":` + jsonString(details.key) +
|
2018-08-14 20:48:28 +03:00
|
|
|
`,"id":` + jsonString(details.id) +
|
2018-08-14 03:05:30 +03:00
|
|
|
`,"time":` + jsonTimeFormat(details.timestamp) + `}`,
|
|
|
|
}
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
2018-08-14 20:48:28 +03:00
|
|
|
var roamNearbys, roamFaraways []roamMatch
|
Lua scripting feature. (#224)
* Start on lua scripting
* Implement evalsha, script load, script exists, and script flush
* Type conversions from lua to resp/json.
Refactor to make luastate and luascripts persistent in the controller.
* Change controller.command and all underlying commands to return resp.Value.
Serialize only during the ouput.
* First stab at tile38 call from lua
* Change tile38 into tile38.call in Lua
* Property return errors from scripts
* Minor refactoring. No locking on script run
* Cleanup/refactoring
* Create a pool of 5 lua states, allow for more as needed. Refactor.
* Use safe map for scripts. Add a limit for max number of lua states. Refactor.
* Refactor
* Refactor script commands into atomic, read-only, and non-atomic classes.
Proper locking for all three classes.
Add tests for scripts
* More tests for scripts
* Properly escape newlines in lua-produced errors
* Better test for readonly failure
* Correctly convert ok/err messages between lua and resp.
Add pcall, sha1hex, error_reply, status_reply functions to tile38 namespace in lua.
* Add pcall test. Change writeErr to work with string argument
* Make sure eval/evalsha never attempt to write AOF
* Add eval-set and eval-get to benchmarks
* Fix eval benchmark tests, add more
* Improve benchmarks
* Optimizations and refactoring.
* Add lua memtest
* Typo
* Add dependency
* golint fixes
* gofmt fixes
* Add scripting commands to the core/commands.json
* Use ARGV for args inside lua
2017-10-05 18:20:40 +03:00
|
|
|
var detect = "outside"
|
2016-03-19 17:16:19 +03:00
|
|
|
if fence != nil {
|
2016-05-23 23:01:42 +03:00
|
|
|
if fence.roam.on {
|
|
|
|
if details.command == "set" {
|
2018-08-14 20:48:28 +03:00
|
|
|
roamNearbys, roamFaraways =
|
2020-03-22 21:54:56 +03:00
|
|
|
fenceMatchRoam(sw.s, fence, details.id,
|
|
|
|
details.oldObj, details.obj)
|
2021-04-02 00:59:12 +03:00
|
|
|
if len(roamNearbys) == 0 && len(roamFaraways) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2016-04-02 17:20:30 +03:00
|
|
|
}
|
2016-05-23 23:01:42 +03:00
|
|
|
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 {
|
|
|
|
detect = "inside"
|
|
|
|
} else if match1 && !match2 {
|
|
|
|
detect = "exit"
|
|
|
|
} else if !match1 && match2 {
|
|
|
|
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 {
|
2018-10-11 00:25:40 +03:00
|
|
|
ls := geojson.NewLineString(geometry.NewLine(
|
|
|
|
[]geometry.Point{
|
|
|
|
details.oldObj.Center(),
|
|
|
|
details.obj.Center(),
|
2018-10-22 05:08:56 +03:00
|
|
|
}, nil))
|
2016-05-23 23:01:42 +03:00
|
|
|
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) {
|
|
|
|
detect = "cross"
|
|
|
|
}
|
|
|
|
if temp {
|
|
|
|
fence.cmd = "within"
|
|
|
|
}
|
2016-04-02 17:20:30 +03:00
|
|
|
}
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-28 21:16:28 +03:00
|
|
|
|
2016-04-02 17:20:30 +03:00
|
|
|
if details.fmap == nil {
|
2016-03-19 17:16:19 +03:00
|
|
|
return nil
|
|
|
|
}
|
2017-02-24 16:03:11 +03:00
|
|
|
for {
|
|
|
|
if fence.detect != nil && !fence.detect[detect] {
|
|
|
|
if detect == "enter" {
|
|
|
|
detect = "inside"
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if detect == "exit" {
|
|
|
|
detect = "outside"
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
break
|
2016-12-28 21:16:28 +03:00
|
|
|
}
|
2016-04-03 00:13:20 +03:00
|
|
|
sw.mu.Lock()
|
2021-07-08 16:45:31 +03:00
|
|
|
var meters float64
|
|
|
|
distance := Distance{false, meters}
|
2019-04-23 21:16:55 +03:00
|
|
|
if fence.distance && fence.obj != nil {
|
2021-07-08 16:45:31 +03:00
|
|
|
meters = details.obj.Distance(fence.obj)
|
|
|
|
distance.ready = true
|
|
|
|
distance.meters = meters
|
2017-02-24 16:25:50 +03:00
|
|
|
}
|
2016-04-02 17:20:30 +03:00
|
|
|
sw.fmap = details.fmap
|
2016-03-19 17:16:19 +03:00
|
|
|
sw.fullFields = true
|
2018-10-29 01:49:45 +03:00
|
|
|
sw.msg.OutputType = JSON
|
2017-01-10 19:49:48 +03:00
|
|
|
sw.writeObject(ScanWriterParams{
|
2021-03-30 21:49:01 +03:00
|
|
|
id: details.id,
|
|
|
|
o: details.obj,
|
|
|
|
fields: details.fields,
|
|
|
|
noLock: true,
|
|
|
|
distance: distance,
|
|
|
|
distOutput: fence.distance,
|
2017-01-10 19:49:48 +03:00
|
|
|
})
|
2017-02-08 14:16:54 +03:00
|
|
|
|
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
|
|
|
|
2018-08-14 03:05:30 +03:00
|
|
|
res := sw.wr.String()
|
2016-03-19 17:16:19 +03:00
|
|
|
sw.wr.Reset()
|
2016-12-15 21:37:38 +03:00
|
|
|
if len(res) > 0 && res[0] == ',' {
|
2016-05-23 23:40:08 +03:00
|
|
|
res = res[1:]
|
|
|
|
}
|
2016-03-19 17:16:19 +03:00
|
|
|
if sw.output == outputIDs {
|
2018-08-14 03:05:30 +03:00
|
|
|
res = `{"id":` + string(res) + `}`
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
2016-04-03 00:13:20 +03:00
|
|
|
sw.mu.Unlock()
|
|
|
|
|
2016-12-06 20:30:48 +03:00
|
|
|
if fence.groups == nil {
|
|
|
|
fence.groups = make(map[string]string)
|
|
|
|
}
|
|
|
|
groupkey := details.key + ":" + details.id
|
|
|
|
var group string
|
|
|
|
var ok bool
|
|
|
|
if detect == "enter" {
|
|
|
|
group = bsonID()
|
|
|
|
fence.groups[groupkey] = group
|
|
|
|
} else if detect == "cross" {
|
|
|
|
group = bsonID()
|
|
|
|
delete(fence.groups, groupkey)
|
|
|
|
} else {
|
|
|
|
group, ok = fence.groups[groupkey]
|
|
|
|
if !ok {
|
|
|
|
group = bsonID()
|
|
|
|
fence.groups[groupkey] = group
|
|
|
|
}
|
|
|
|
}
|
2018-08-14 03:05:30 +03:00
|
|
|
var msgs []string
|
2016-04-01 22:46:39 +03:00
|
|
|
if fence.detect == nil || fence.detect[detect] {
|
2017-02-12 17:06:56 +03:00
|
|
|
if len(res) > 0 && res[0] == '{' {
|
2018-08-14 03:05:30 +03:00
|
|
|
msgs = append(msgs, makemsg(details.command, group, detect,
|
|
|
|
hookName, metas, details.key, details.timestamp, res[1:]))
|
2017-02-12 17:06:56 +03:00
|
|
|
} else {
|
2018-08-14 03:05:30 +03:00
|
|
|
msgs = append(msgs, string(res))
|
2016-04-01 22:46:39 +03:00
|
|
|
}
|
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-12-29 18:50:54 +03:00
|
|
|
msgs = append(msgs, makemsg(details.command, group, "inside", hookName, metas, details.key, details.timestamp, res[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-12-29 18:50:54 +03:00
|
|
|
msgs = append(msgs, makemsg(details.command, group, "outside", hookName, metas, details.key, details.timestamp, res[1:]))
|
2016-04-01 22:46:39 +03:00
|
|
|
}
|
2016-05-23 23:01:42 +03:00
|
|
|
case "roam":
|
|
|
|
if len(msgs) > 0 {
|
2018-08-14 03:05:30 +03:00
|
|
|
var nmsgs []string
|
2021-04-02 00:59:12 +03:00
|
|
|
for _, msg := range msgs {
|
|
|
|
cmd := gjson.Get(msg, "command")
|
|
|
|
if cmd.Exists() && cmd.String() != "set" {
|
|
|
|
nmsgs = append(nmsgs, msg)
|
|
|
|
}
|
|
|
|
}
|
2018-08-14 20:48:28 +03:00
|
|
|
for i := range roamNearbys {
|
|
|
|
nmsg := extendRoamMessage(sw, fence,
|
|
|
|
"nearby", msgs[0], roamNearbys[i])
|
|
|
|
nmsgs = append(nmsgs, string(nmsg))
|
|
|
|
}
|
|
|
|
for i := range roamFaraways {
|
|
|
|
nmsg := extendRoamMessage(sw, fence,
|
|
|
|
"faraway", msgs[0], roamFaraways[i])
|
2018-08-14 03:05:30 +03:00
|
|
|
nmsgs = append(nmsgs, string(nmsg))
|
2016-05-23 23:01:42 +03:00
|
|
|
}
|
|
|
|
msgs = nmsgs
|
|
|
|
}
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
|
|
|
return msgs
|
|
|
|
}
|
|
|
|
|
2018-08-14 20:48:28 +03:00
|
|
|
func extendRoamMessage(
|
|
|
|
sw *scanWriter, fence *liveFenceSwitches,
|
|
|
|
kind string, baseMsg string, match roamMatch,
|
|
|
|
) string {
|
|
|
|
// hack off the last '}'
|
|
|
|
nmsg := []byte(baseMsg[:len(baseMsg)-1])
|
|
|
|
nmsg = append(nmsg, `,"`+kind+`":{"key":`...)
|
|
|
|
nmsg = appendJSONString(nmsg, fence.roam.key)
|
|
|
|
nmsg = append(nmsg, `,"id":`...)
|
|
|
|
nmsg = appendJSONString(nmsg, match.id)
|
|
|
|
nmsg = append(nmsg, `,"object":`...)
|
2018-10-11 00:25:40 +03:00
|
|
|
nmsg = match.obj.AppendJSON(nmsg)
|
2018-08-14 20:48:28 +03:00
|
|
|
nmsg = append(nmsg, `,"meters":`...)
|
|
|
|
nmsg = strconv.AppendFloat(nmsg,
|
|
|
|
math.Floor(match.meters*1000)/1000, 'f', -1, 64)
|
|
|
|
if fence.roam.scan != "" {
|
|
|
|
nmsg = append(nmsg, `,"scan":[`...)
|
2019-10-30 20:17:59 +03:00
|
|
|
col := sw.s.getCol(fence.roam.key)
|
2018-08-14 20:48:28 +03:00
|
|
|
if col != nil {
|
|
|
|
obj, _, ok := col.Get(match.id)
|
|
|
|
if ok {
|
2018-10-11 00:25:40 +03:00
|
|
|
nmsg = append(nmsg, `{"id":`...)
|
|
|
|
nmsg = appendJSONString(nmsg, match.id)
|
|
|
|
nmsg = append(nmsg, `,"self":true,"object":`...)
|
|
|
|
nmsg = obj.AppendJSON(nmsg)
|
|
|
|
nmsg = append(nmsg, '}')
|
2018-08-14 20:48:28 +03:00
|
|
|
}
|
|
|
|
pattern := match.id + fence.roam.scan
|
|
|
|
iterator := func(
|
|
|
|
oid string, o geojson.Object, fields []float64,
|
|
|
|
) bool {
|
|
|
|
if oid == match.id {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if matched, _ := glob.Match(pattern, oid); matched {
|
2018-10-11 00:25:40 +03:00
|
|
|
nmsg = append(nmsg, `,{"id":`...)
|
|
|
|
nmsg = appendJSONString(nmsg, oid)
|
|
|
|
nmsg = append(nmsg, `,"object":`...)
|
|
|
|
nmsg = o.AppendJSON(nmsg)
|
|
|
|
nmsg = append(nmsg, '}')
|
2018-08-14 20:48:28 +03:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
g := glob.Parse(pattern, false)
|
|
|
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
2019-04-24 15:09:41 +03:00
|
|
|
col.Scan(false, nil, nil, iterator)
|
2018-08-14 20:48:28 +03:00
|
|
|
} else {
|
|
|
|
col.ScanRange(g.Limits[0], g.Limits[1],
|
2019-04-24 15:09:41 +03:00
|
|
|
false, nil, nil, iterator)
|
2018-08-14 20:48:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
nmsg = append(nmsg, ']')
|
|
|
|
}
|
|
|
|
|
|
|
|
nmsg = append(nmsg, '}')
|
|
|
|
|
|
|
|
// re-add the last '}'
|
|
|
|
nmsg = append(nmsg, '}')
|
|
|
|
return string(nmsg)
|
|
|
|
}
|
|
|
|
|
2018-08-14 03:05:30 +03:00
|
|
|
func makemsg(
|
|
|
|
command, group, detect, hookName string,
|
|
|
|
metas []FenceMeta, key string, t time.Time, tail string,
|
|
|
|
) string {
|
2016-12-15 21:37:38 +03:00
|
|
|
var buf []byte
|
|
|
|
buf = append(append(buf, `{"command":"`...), command...)
|
|
|
|
buf = append(append(buf, `","group":"`...), group...)
|
|
|
|
buf = append(append(buf, `","detect":"`...), detect...)
|
2016-12-29 18:50:54 +03:00
|
|
|
buf = append(buf, '"')
|
|
|
|
buf = appendHookDetails(buf, hookName, metas)
|
2016-12-28 21:16:28 +03:00
|
|
|
buf = appendJSONString(append(buf, `,"key":`...), key)
|
|
|
|
buf = appendJSONTimeFormat(append(buf, `,"time":`...), t)
|
2016-12-15 21:37:38 +03:00
|
|
|
buf = append(append(buf, ','), tail...)
|
2018-08-14 03:05:30 +03:00
|
|
|
return string(buf)
|
2016-12-15 21:37:38 +03:00
|
|
|
}
|
|
|
|
|
2016-03-19 17:16:19 +03:00
|
|
|
func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
2018-10-11 00:25:40 +03:00
|
|
|
gobj, _ := obj.(geojson.Object)
|
|
|
|
if gobj == nil {
|
2016-03-19 17:16:19 +03:00
|
|
|
return false
|
|
|
|
}
|
2016-05-23 23:01:42 +03:00
|
|
|
if fence.roam.on {
|
|
|
|
// we need to check this object against
|
|
|
|
return false
|
|
|
|
}
|
2018-10-11 00:25:40 +03:00
|
|
|
switch fence.cmd {
|
|
|
|
case "nearby":
|
|
|
|
// nearby is an INTERSECT on a Circle
|
|
|
|
return gobj.Intersects(fence.obj)
|
|
|
|
case "within":
|
|
|
|
return gobj.Within(fence.obj)
|
|
|
|
case "intersects":
|
|
|
|
return gobj.Intersects(fence.obj)
|
2016-03-19 17:16:19 +03:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2016-05-23 23:01:42 +03:00
|
|
|
|
2020-03-22 21:54:56 +03:00
|
|
|
func fenceMatchNearbys(
|
2019-10-30 20:17:59 +03:00
|
|
|
s *Server, fence *liveFenceSwitches,
|
2020-03-22 21:54:56 +03:00
|
|
|
id string, obj geojson.Object,
|
|
|
|
) (nearbys []roamMatch) {
|
|
|
|
if obj == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2019-10-30 20:17:59 +03:00
|
|
|
col := s.getCol(fence.roam.key)
|
2016-05-23 23:01:42 +03:00
|
|
|
if col == nil {
|
2020-03-22 21:54:56 +03:00
|
|
|
return nil
|
2016-05-23 23:01:42 +03:00
|
|
|
}
|
2019-01-06 20:23:57 +03:00
|
|
|
center := obj.Center()
|
|
|
|
minLat, minLon, maxLat, maxLon :=
|
|
|
|
geo.RectFromCenter(center.Y, center.X, fence.roam.meters)
|
|
|
|
rect := geometry.Rect{
|
|
|
|
Min: geometry.Point{X: minLon, Y: minLat},
|
|
|
|
Max: geometry.Point{X: maxLon, Y: maxLat},
|
|
|
|
}
|
2019-04-24 15:09:41 +03:00
|
|
|
col.Intersects(geojson.NewRect(rect), 0, nil, nil, func(
|
2020-03-22 21:54:56 +03:00
|
|
|
id2 string, obj2 geojson.Object, fields []float64,
|
2018-10-11 00:25:40 +03:00
|
|
|
) bool {
|
2020-03-22 21:54:56 +03:00
|
|
|
if s.hasExpired(fence.roam.key, id2) {
|
|
|
|
return true // skip expired
|
2018-10-11 00:25:40 +03:00
|
|
|
}
|
|
|
|
var idMatch bool
|
2020-03-22 21:54:56 +03:00
|
|
|
if id2 == id {
|
2018-10-11 00:25:40 +03:00
|
|
|
return true // skip self
|
|
|
|
}
|
2020-03-22 21:54:56 +03:00
|
|
|
meters := obj.Distance(obj2)
|
|
|
|
if meters > fence.roam.meters {
|
|
|
|
return true // skip outside radius
|
|
|
|
}
|
2018-10-11 00:25:40 +03:00
|
|
|
if fence.roam.pattern {
|
2020-03-22 21:54:56 +03:00
|
|
|
idMatch, _ = glob.Match(fence.roam.id, id2)
|
2018-10-11 00:25:40 +03:00
|
|
|
} else {
|
2020-03-22 21:54:56 +03:00
|
|
|
idMatch = fence.roam.id == id2
|
2018-10-11 00:25:40 +03:00
|
|
|
}
|
|
|
|
if !idMatch {
|
2020-03-22 21:54:56 +03:00
|
|
|
return true // skip non-id match
|
2018-10-11 00:25:40 +03:00
|
|
|
}
|
|
|
|
match := roamMatch{
|
2020-03-22 21:54:56 +03:00
|
|
|
id: id2,
|
2018-10-11 00:25:40 +03:00
|
|
|
obj: obj2,
|
|
|
|
meters: obj.Distance(obj2),
|
|
|
|
}
|
2020-03-22 21:54:56 +03:00
|
|
|
nearbys = append(nearbys, match)
|
2018-10-11 00:25:40 +03:00
|
|
|
return true
|
|
|
|
})
|
2020-03-22 21:54:56 +03:00
|
|
|
return nearbys
|
|
|
|
}
|
2018-08-23 02:26:27 +03:00
|
|
|
|
2020-03-22 21:54:56 +03:00
|
|
|
func fenceMatchRoam(
|
|
|
|
s *Server, fence *liveFenceSwitches,
|
|
|
|
id string, old, obj geojson.Object,
|
|
|
|
) (nearbys, faraways []roamMatch) {
|
|
|
|
oldNearbys := fenceMatchNearbys(s, fence, id, old)
|
|
|
|
newNearbys := fenceMatchNearbys(s, fence, id, obj)
|
|
|
|
// Go through all matching objects in new-nearbys and old-nearbys.
|
|
|
|
for i := 0; i < len(oldNearbys); i++ {
|
|
|
|
var match bool
|
|
|
|
var j int
|
|
|
|
for ; j < len(newNearbys); j++ {
|
2020-06-18 16:43:18 +03:00
|
|
|
if newNearbys[j].id == oldNearbys[i].id {
|
2020-03-22 21:54:56 +03:00
|
|
|
match = true
|
|
|
|
break
|
2018-08-14 20:48:28 +03:00
|
|
|
}
|
|
|
|
}
|
2020-03-22 21:54:56 +03:00
|
|
|
if match {
|
|
|
|
// dwelling, more from old-nearbys
|
|
|
|
oldNearbys[i] = oldNearbys[len(oldNearbys)-1]
|
|
|
|
oldNearbys = oldNearbys[:len(oldNearbys)-1]
|
2020-03-25 22:47:55 +03:00
|
|
|
i--
|
2020-03-22 21:54:56 +03:00
|
|
|
if fence.nodwell {
|
|
|
|
// no dwelling allowed, remove from both lists
|
|
|
|
newNearbys[j] = newNearbys[len(newNearbys)-1]
|
|
|
|
newNearbys = newNearbys[:len(newNearbys)-1]
|
|
|
|
}
|
2018-08-14 20:48:28 +03:00
|
|
|
}
|
|
|
|
}
|
2020-03-22 21:54:56 +03:00
|
|
|
faraways, nearbys = oldNearbys, newNearbys
|
2020-03-25 23:01:11 +03:00
|
|
|
// ensure the faraways distances are to the new object
|
2020-03-22 21:54:56 +03:00
|
|
|
for i := 0; i < len(faraways); i++ {
|
|
|
|
faraways[i].meters = faraways[i].obj.Distance(obj)
|
|
|
|
}
|
2020-03-25 23:01:11 +03:00
|
|
|
sortRoamMatches(faraways)
|
|
|
|
sortRoamMatches(nearbys)
|
2020-03-22 21:54:56 +03:00
|
|
|
return nearbys, faraways
|
2016-05-23 23:01:42 +03:00
|
|
|
}
|
2020-03-25 23:01:11 +03:00
|
|
|
|
|
|
|
// sortRoamMatches stable sorts roam matches
|
|
|
|
func sortRoamMatches(matches []roamMatch) {
|
|
|
|
sort.Slice(matches, func(i, j int) bool {
|
|
|
|
if matches[i].meters < matches[j].meters {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if matches[i].meters > matches[j].meters {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return matches[i].id < matches[j].id
|
|
|
|
})
|
|
|
|
}
|