mirror of https://github.com/tidwall/tile38.git
Add SCAN to Roaming Geofences
Can now get back more details about an object: NEARBY people FENCE ROAM people * 5000 SCAN :* For more information see #96 Thanks @amorskoy for suggesting feature in #93 Closes #96
This commit is contained in:
parent
6c52f3f3f1
commit
ef74a63c79
|
@ -207,7 +207,7 @@ func (c *Controller) writeAOF(value resp.Value, d *commandDetailsT) error {
|
||||||
|
|
||||||
func (c *Controller) queueHooks(d *commandDetailsT) error {
|
func (c *Controller) queueHooks(d *commandDetailsT) error {
|
||||||
// big list of all of the messages
|
// big list of all of the messages
|
||||||
var hmsgs []string
|
var hmsgs [][]byte
|
||||||
var hooks []*Hook
|
var hooks []*Hook
|
||||||
// find the hook by the key
|
// find the hook by the key
|
||||||
if hm, ok := c.hookcols[d.key]; ok {
|
if hm, ok := c.hookcols[d.key]; ok {
|
||||||
|
@ -230,7 +230,7 @@ func (c *Controller) queueHooks(d *commandDetailsT) error {
|
||||||
for _, msg := range hmsgs {
|
for _, msg := range hmsgs {
|
||||||
c.qidx++ // increment the log id
|
c.qidx++ // increment the log id
|
||||||
key := hookLogPrefix + uint64ToString(c.qidx)
|
key := hookLogPrefix + uint64ToString(c.qidx)
|
||||||
_, _, err := tx.Set(key, msg, hookLogSetDefaults())
|
_, _, err := tx.Set(key, string(msg), hookLogSetDefaults())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package controller
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/tidwall/tile38/controller/glob"
|
"github.com/tidwall/tile38/controller/glob"
|
||||||
|
@ -14,26 +13,26 @@ import (
|
||||||
var tmfmt = "2006-01-02T15:04:05.999999999Z07:00"
|
var tmfmt = "2006-01-02T15:04:05.999999999Z07:00"
|
||||||
|
|
||||||
// FenceMatch executes a fence match returns back json messages for fence detection.
|
// FenceMatch executes a fence match returns back json messages for fence detection.
|
||||||
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) []string {
|
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) [][]byte {
|
||||||
msgs := fenceMatch(hookName, sw, fence, details)
|
msgs := fenceMatch(hookName, sw, fence, details)
|
||||||
if len(fence.accept) == 0 {
|
if len(fence.accept) == 0 {
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
nmsgs := make([]string, 0, len(msgs))
|
nmsgs := make([][]byte, 0, len(msgs))
|
||||||
for _, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
if fence.accept[gjson.Get(msg, "command").String()] {
|
if fence.accept[gjson.GetBytes(msg, "command").String()] {
|
||||||
nmsgs = append(nmsgs, msg)
|
nmsgs = append(nmsgs, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nmsgs
|
return nmsgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) []string {
|
func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) [][]byte {
|
||||||
jshookName := jsonString(hookName)
|
jshookName := jsonString(hookName)
|
||||||
jstime := jsonString(details.timestamp.Format(tmfmt))
|
jstime := jsonString(details.timestamp.Format(tmfmt))
|
||||||
pattern := fence.glob
|
pattern := fence.glob
|
||||||
if details.command == "drop" {
|
if details.command == "drop" {
|
||||||
return []string{`{"command":"drop","hook":` + jshookName + `,"time":` + jstime + `}`}
|
return [][]byte{[]byte(`{"command":"drop","hook":` + jshookName + `,"time":` + jstime + `}`)}
|
||||||
}
|
}
|
||||||
match := true
|
match := true
|
||||||
if pattern != "" && pattern != "*" {
|
if pattern != "" && pattern != "*" {
|
||||||
|
@ -114,7 +113,7 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if details.command == "del" {
|
if details.command == "del" {
|
||||||
return []string{`{"command":"del","hook":` + jshookName + `,"id":` + jsonString(details.id) + `,"time":` + jstime + `}`}
|
return [][]byte{[]byte(`{"command":"del","hook":` + jshookName + `,"id":` + jsonString(details.id) + `,"time":` + jstime + `}`)}
|
||||||
}
|
}
|
||||||
if details.fmap == nil {
|
if details.fmap == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -129,16 +128,14 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res := sw.wr.String()
|
res := make([]byte, sw.wr.Len())
|
||||||
resb := make([]byte, len(res))
|
copy(res, sw.wr.Bytes())
|
||||||
copy(resb, res)
|
|
||||||
sw.wr.Reset()
|
sw.wr.Reset()
|
||||||
res = string(resb)
|
if len(res) > 0 && res[0] == ',' {
|
||||||
if strings.HasPrefix(res, ",") {
|
|
||||||
res = res[1:]
|
res = res[1:]
|
||||||
}
|
}
|
||||||
if sw.output == outputIDs {
|
if sw.output == outputIDs {
|
||||||
res = `{"id":` + res + `}`
|
res = []byte(`{"id":` + string(res) + `}`)
|
||||||
}
|
}
|
||||||
sw.mu.Unlock()
|
sw.mu.Unlock()
|
||||||
|
|
||||||
|
@ -164,30 +161,72 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
|
|
||||||
jskey := jsonString(details.key)
|
jskey := jsonString(details.key)
|
||||||
|
|
||||||
ores := res
|
msgs := make([][]byte, 0, 4)
|
||||||
msgs := make([]string, 0, 4)
|
|
||||||
if fence.detect == nil || fence.detect[detect] {
|
if fence.detect == nil || fence.detect[detect] {
|
||||||
if strings.HasPrefix(ores, "{") {
|
if len(res) > 0 && res[0] == '{' {
|
||||||
res = `{"command":"` + details.command + `","group":"` + group + `","detect":"` + detect + `","hook":` + jshookName + `,"key":` + jskey + `,"time":` + jstime + `,` + ores[1:]
|
res = makemsg(details.command, group, detect, jshookName, jskey, jstime, res[1:])
|
||||||
}
|
}
|
||||||
msgs = append(msgs, res)
|
msgs = append(msgs, res)
|
||||||
}
|
}
|
||||||
switch detect {
|
switch detect {
|
||||||
case "enter":
|
case "enter":
|
||||||
if fence.detect == nil || fence.detect["inside"] {
|
if fence.detect == nil || fence.detect["inside"] {
|
||||||
msgs = append(msgs, `{"command":"`+details.command+`","group":"`+group+`","detect":"inside","hook":`+jshookName+`,"key":`+jskey+`,"time":`+jstime+`,`+ores[1:])
|
msgs = append(msgs, makemsg(details.command, group, "inside", jshookName, jskey, jstime, res[1:]))
|
||||||
}
|
}
|
||||||
case "exit", "cross":
|
case "exit", "cross":
|
||||||
if fence.detect == nil || fence.detect["outside"] {
|
if fence.detect == nil || fence.detect["outside"] {
|
||||||
msgs = append(msgs, `{"command":"`+details.command+`","group":"`+group+`","detect":"outside","hook":`+jshookName+`,"key":`+jskey+`,"time":`+jstime+`,`+ores[1:])
|
msgs = append(msgs, makemsg(details.command, group, "outside", jshookName, jskey, jstime, res[1:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
case "roam":
|
case "roam":
|
||||||
if len(msgs) > 0 {
|
if len(msgs) > 0 {
|
||||||
var nmsgs []string
|
var nmsgs [][]byte
|
||||||
msg := msgs[0][:len(msgs[0])-1]
|
msg := msgs[0][:len(msgs[0])-1]
|
||||||
for i, id := range roamids {
|
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)+`}}`)
|
|
||||||
|
nmsg := append([]byte(nil), msg...)
|
||||||
|
nmsg = append(nmsg, `,"nearby":{"key":`...)
|
||||||
|
nmsg = append(nmsg, jsonString(roamkeys[i])...)
|
||||||
|
nmsg = append(nmsg, `,"id":`...)
|
||||||
|
nmsg = append(nmsg, jsonString(id)...)
|
||||||
|
nmsg = append(nmsg, `,"meters":`...)
|
||||||
|
nmsg = append(nmsg, strconv.FormatFloat(roammeters[i], 'f', -1, 64)...)
|
||||||
|
|
||||||
|
if fence.roam.scan != "" {
|
||||||
|
nmsg = append(nmsg, `,"scan":[`...)
|
||||||
|
|
||||||
|
func() {
|
||||||
|
sw.c.mu.Lock()
|
||||||
|
defer sw.c.mu.Unlock()
|
||||||
|
col := sw.c.getCol(roamkeys[i])
|
||||||
|
if col != nil {
|
||||||
|
obj, _, ok := col.Get(id)
|
||||||
|
if ok {
|
||||||
|
nmsg = append(nmsg, `{"id":`+jsonString(id)+`,"self":true,"object":`+obj.JSON()+`}`...)
|
||||||
|
}
|
||||||
|
pattern := id + fence.roam.scan
|
||||||
|
iterator := func(oid string, o geojson.Object, fields []float64) bool {
|
||||||
|
if oid == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if matched, _ := glob.Match(pattern, oid); matched {
|
||||||
|
nmsg = append(nmsg, `,{"id":`+jsonString(oid)+`,"object":`+o.JSON()+`}`...)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
g := glob.Parse(pattern, false)
|
||||||
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
||||||
|
col.Scan(0, false, iterator)
|
||||||
|
} else {
|
||||||
|
col.ScanRange(0, g.Limits[0], g.Limits[1], false, iterator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
nmsg = append(nmsg, ']')
|
||||||
|
}
|
||||||
|
|
||||||
|
nmsg = append(nmsg, '}')
|
||||||
|
nmsg = append(nmsg, '}')
|
||||||
|
nmsgs = append(nmsgs, nmsg)
|
||||||
}
|
}
|
||||||
msgs = nmsgs
|
msgs = nmsgs
|
||||||
}
|
}
|
||||||
|
@ -195,6 +234,18 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makemsg(command, group, detect, jshookName, jskey, jstime string, tail []byte) []byte {
|
||||||
|
var buf []byte
|
||||||
|
buf = append(append(buf, `{"command":"`...), command...)
|
||||||
|
buf = append(append(buf, `","group":"`...), group...)
|
||||||
|
buf = append(append(buf, `","detect":"`...), detect...)
|
||||||
|
buf = append(append(buf, `","hook":`...), jshookName...)
|
||||||
|
buf = append(append(buf, `,"key":`...), jskey...)
|
||||||
|
buf = append(append(buf, `,"time":`...), jstime...)
|
||||||
|
buf = append(append(buf, ','), tail...)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -31,6 +31,7 @@ type roamSwitches struct {
|
||||||
id string
|
id string
|
||||||
pattern bool
|
pattern bool
|
||||||
meters float64
|
meters float64
|
||||||
|
scan string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s liveFenceSwitches) Error() string {
|
func (s liveFenceSwitches) Error() string {
|
||||||
|
@ -242,6 +243,19 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
||||||
err = errInvalidArgument(smeters)
|
err = errInvalidArgument(smeters)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scan string
|
||||||
|
if vs, scan, ok = tokenval(vs); ok {
|
||||||
|
if strings.ToLower(scan) != "scan" {
|
||||||
|
err = errInvalidArgument(scan)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if vs, scan, ok = tokenval(vs); !ok || scan == "" {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.roam.scan = scan
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(vs) != 0 {
|
if len(vs) != 0 {
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
|
|
Loading…
Reference in New Issue