motel 0.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +5 -3
- data/bin/motel-client +211 -0
- data/bin/motel-rage-client +80 -0
- data/bin/{server/main.rb → motel-server} +0 -3
- data/conf/motel-schema.xml +30 -5
- data/lib/motel.rb +3 -1
- data/lib/motel/callbacks.rb +139 -0
- data/lib/motel/location.rb +23 -4
- data/lib/motel/movement_strategies/elliptical.rb +1 -0
- data/lib/motel/movement_strategies/linear.rb +1 -0
- data/lib/motel/movement_strategy.rb +4 -3
- data/lib/motel/runner.rb +136 -38
- data/lib/motel/simrpc_adapter.rb +190 -0
- data/spec/callbacks_spec.rb +156 -0
- data/spec/dsl_spec.rb +5 -5
- data/spec/location_spec.rb +15 -10
- data/spec/runner_spec.rb +24 -10
- data/spec/simrpc_spec.rb +142 -21
- data/spec/spec_helper.rb +1 -0
- metadata +48 -13
- data/bin/clients/simple/main.rb +0 -131
- data/lib/motel/simrpc.rb +0 -131
- data/lib/motel/thread_pool.rb +0 -51
data/lib/motel/location.rb
CHANGED
@@ -7,6 +7,8 @@ require 'motel/movement_strategy'
|
|
7
7
|
|
8
8
|
module Motel
|
9
9
|
|
10
|
+
# FIXME Motel locations need concurrent access protection, add here (?)
|
11
|
+
|
10
12
|
# A Location defines an optional parent location and the x,y,z
|
11
13
|
# cartesian coordinates of the location relative to that parent.
|
12
14
|
# If parent is not specified x,y,z are ignored and this location
|
@@ -27,6 +29,9 @@ class Location
|
|
27
29
|
# array of callbacks to be invoked on movement
|
28
30
|
attr_accessor :movement_callbacks
|
29
31
|
|
32
|
+
# Array of callbacks to be invoked on proximity
|
33
|
+
attr_accessor :proximity_callbacks
|
34
|
+
|
30
35
|
# a generic association which this location can belong to
|
31
36
|
attr_accessor :entity
|
32
37
|
|
@@ -34,8 +39,13 @@ class Location
|
|
34
39
|
# default to the stopped movement strategy
|
35
40
|
@movement_strategy = MovementStrategies::Stopped.instance
|
36
41
|
@movement_callbacks = []
|
42
|
+
@proximity_callbacks = []
|
37
43
|
@children = []
|
38
44
|
|
45
|
+
@x = nil
|
46
|
+
@y = nil
|
47
|
+
@z = nil
|
48
|
+
|
39
49
|
@id = args[:id] if args.has_key? :id
|
40
50
|
@parent_id = args[:parent_id] if args.has_key? :parent_id
|
41
51
|
@x = args[:x] if args.has_key? :x
|
@@ -44,10 +54,6 @@ class Location
|
|
44
54
|
@parent = args[:parent] if args.has_key? :parent
|
45
55
|
@parent.children.push self unless @parent.nil? || @parent.children.include?(self)
|
46
56
|
@movement_strategy = args[:movement_strategy] if args.has_key? :movement_strategy
|
47
|
-
|
48
|
-
@x = 0 if @x.nil?
|
49
|
-
@y = 0 if @y.nil?
|
50
|
-
@z = 0 if @z.nil?
|
51
57
|
end
|
52
58
|
|
53
59
|
# update this location's attributes to match other's set attributes
|
@@ -60,6 +66,11 @@ class Location
|
|
60
66
|
@parent_id = location.parent_id unless location.parent_id.nil?
|
61
67
|
end
|
62
68
|
|
69
|
+
# return this locations coordinates in an array
|
70
|
+
def coordinates
|
71
|
+
[@x, @y, @z]
|
72
|
+
end
|
73
|
+
|
63
74
|
# return this location's root location
|
64
75
|
def root
|
65
76
|
return self if parent.nil?
|
@@ -95,6 +106,14 @@ class Location
|
|
95
106
|
return parent.total_z + z
|
96
107
|
end
|
97
108
|
|
109
|
+
# return the distance between this location and specified other
|
110
|
+
def -(location)
|
111
|
+
dx = x - location.x
|
112
|
+
dy = y - location.y
|
113
|
+
dz = z - location.z
|
114
|
+
Math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
|
115
|
+
end
|
116
|
+
|
98
117
|
end
|
99
118
|
|
100
119
|
end # module Motel
|
@@ -46,6 +46,7 @@ class Elliptical < MovementStrategy
|
|
46
46
|
@direction_minor_x = 0 if @direction_minor_x.nil?
|
47
47
|
@direction_minor_y = 1 if @direction_minor_y.nil?
|
48
48
|
@direction_minor_z = 0 if @direction_minor_z.nil?
|
49
|
+
super(args)
|
49
50
|
|
50
51
|
@direction_major_x, @direction_major_y, @direction_major_z =
|
51
52
|
normalize(@direction_major_x, @direction_major_y, @direction_major_z)
|
@@ -23,6 +23,7 @@ class Linear < MovementStrategy
|
|
23
23
|
@direction_vector_y = args[:direction_vector_y] if args.has_key? :direction_vector_y
|
24
24
|
@direction_vector_z = args[:direction_vector_z] if args.has_key? :direction_vector_z
|
25
25
|
@speed = args[:speed] if args.has_key? :speed
|
26
|
+
super(args)
|
26
27
|
|
27
28
|
# normalize direction vector
|
28
29
|
@direction_vector_x, @direction_vector_y, @direction_vector_z =
|
@@ -11,13 +11,14 @@ module Motel
|
|
11
11
|
# MovementStrategy subclasses define the rules and params which
|
12
12
|
# a location changes its position.
|
13
13
|
class MovementStrategy
|
14
|
+
attr_accessor :id, :type
|
15
|
+
|
14
16
|
attr_accessor :step_delay
|
15
17
|
|
16
18
|
def initialize(args = {})
|
17
|
-
@step_delay =
|
18
|
-
@movement_callbacks = []
|
19
|
+
@step_delay = 1
|
19
20
|
|
20
|
-
@step_delay = args[:step_delay] if args.has_key? :step_delay
|
21
|
+
@step_delay = args[:step_delay] if args.has_key?(:step_delay) && !args[:step_delay].nil?
|
21
22
|
end
|
22
23
|
|
23
24
|
# default movement strategy is to do nothing
|
data/lib/motel/runner.rb
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
7
7
|
|
8
8
|
require 'singleton'
|
9
|
-
require 'motel/thread_pool'
|
10
9
|
|
11
10
|
module Motel
|
12
11
|
|
@@ -17,44 +16,79 @@ module Motel
|
|
17
16
|
class Runner
|
18
17
|
include Singleton
|
19
18
|
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
# for testing purposes
|
24
|
-
attr_reader :thread_pool, :terminate, :run_thread
|
19
|
+
# For testing purposes
|
20
|
+
attr_reader :terminate, :run_thread
|
25
21
|
|
26
22
|
def initialize(args = {})
|
23
|
+
# is set to true upon runner termination
|
27
24
|
@terminate = false
|
28
|
-
|
29
|
-
|
25
|
+
|
26
|
+
# TODO use ruby tree to store locations w/ heirarchy
|
27
|
+
# management queues, locations to be scheduled and locations to be run
|
28
|
+
@schedule_queue = []
|
29
|
+
@run_queue = []
|
30
|
+
|
31
|
+
|
32
|
+
# locks protecting queues from concurrent access and conditions indicating queues have items
|
33
|
+
@schedule_lock = Mutex.new
|
34
|
+
@run_lock = Mutex.new
|
35
|
+
@schedule_cv = ConditionVariable.new
|
36
|
+
@run_cv = ConditionVariable.new
|
30
37
|
|
31
38
|
@run_thread = nil
|
32
|
-
@run_delay = 2 # FIXME scale delay (only needed if locations is empty or has very few simple elements)
|
33
39
|
end
|
34
40
|
|
41
|
+
# Return complete list of locations being managed/tracked
|
42
|
+
def locations
|
43
|
+
# need conccurrent protection here, or copy the elements into another array and return that?
|
44
|
+
@schedule_queue + @run_queue
|
45
|
+
end
|
46
|
+
|
47
|
+
|
35
48
|
# Empty the list of locations being managed/tracked
|
36
49
|
def clear
|
37
|
-
@
|
38
|
-
@
|
39
|
-
|
50
|
+
@schedule_lock.synchronize {
|
51
|
+
@run_lock.synchronize {
|
52
|
+
@schedule_queue.clear
|
53
|
+
@run_queue.clear
|
54
|
+
}}
|
40
55
|
end
|
41
56
|
|
42
|
-
#
|
57
|
+
# Add location to runner to be managed, after this is called, the location's
|
43
58
|
# movement strategy's move method will be invoked periodically
|
44
59
|
def run(location)
|
45
|
-
@
|
46
|
-
|
47
|
-
|
60
|
+
@schedule_lock.synchronize {
|
61
|
+
# autogenerate location.id if nil
|
62
|
+
if location.id.nil?
|
63
|
+
@run_lock.synchronize {
|
64
|
+
i = 1
|
65
|
+
until false
|
66
|
+
break if @schedule_queue.find { |l| l.id == i }.nil? && @run_queue.find { |l| l.id == i }.nil?
|
67
|
+
i += 1
|
68
|
+
end
|
69
|
+
location.id = i
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
Logger.debug "adding location #{location.id} to runner queue"
|
74
|
+
@schedule_queue.push location
|
75
|
+
@schedule_cv.signal
|
48
76
|
}
|
77
|
+
return location
|
78
|
+
end
|
79
|
+
|
80
|
+
# Wrapper around run, except return 'self' when done
|
81
|
+
def <<(location)
|
82
|
+
run(location)
|
83
|
+
return self
|
49
84
|
end
|
50
85
|
|
51
86
|
# Start moving the locations. If :async => true is passed in, this will immediately
|
52
87
|
# return, else this will block until stop is called.
|
53
88
|
def start(args = {})
|
54
|
-
num_threads = 5
|
55
|
-
num_threads = args[:num_threads] if args.has_key? :num_threads
|
89
|
+
@num_threads = 5
|
90
|
+
@num_threads = args[:num_threads] if args.has_key? :num_threads
|
56
91
|
@terminate = false
|
57
|
-
@thread_pool = ThreadPool.new(num_threads)
|
58
92
|
|
59
93
|
if args.has_key?(:async) && args[:async]
|
60
94
|
Logger.debug "starting async motel runner"
|
@@ -70,7 +104,12 @@ class Runner
|
|
70
104
|
def stop
|
71
105
|
Logger.debug "stopping motel runner"
|
72
106
|
@terminate = true
|
73
|
-
@
|
107
|
+
@schedule_lock.synchronize {
|
108
|
+
@schedule_cv.signal
|
109
|
+
}
|
110
|
+
@run_lock.synchronize {
|
111
|
+
@run_cv.signal
|
112
|
+
}
|
74
113
|
join
|
75
114
|
Logger.debug "motel runner stopped"
|
76
115
|
end
|
@@ -85,35 +124,94 @@ class Runner
|
|
85
124
|
|
86
125
|
# Internal helper method performing main runner operations
|
87
126
|
def run_cycle
|
88
|
-
#
|
89
|
-
|
127
|
+
# location ids which are currently being run -> their run timestamp
|
128
|
+
location_timestamps = {}
|
129
|
+
|
130
|
+
# scheduler thread, to add locations to the run queue
|
131
|
+
scheduler = Thread.new {
|
132
|
+
until @terminate
|
133
|
+
tqueue = []
|
134
|
+
locs_to_run = []
|
135
|
+
empty_queue = true
|
136
|
+
min_delay = nil
|
137
|
+
|
138
|
+
@schedule_lock.synchronize {
|
139
|
+
# if no locations are to be scheduled, block until there are
|
140
|
+
@schedule_cv.wait(@schedule_lock) if @schedule_queue.empty?
|
141
|
+
@schedule_queue.each { |l| tqueue << l }
|
142
|
+
}
|
143
|
+
|
144
|
+
# run through each location to be scheduled to run, see which ones are due
|
145
|
+
tqueue.each { |loc|
|
146
|
+
location_timestamps[loc.id] = Time.now unless location_timestamps.has_key?(loc.id)
|
147
|
+
locs_to_run << loc if loc.movement_strategy.step_delay < Time.now - location_timestamps[loc.id]
|
148
|
+
}
|
149
|
+
|
150
|
+
# add those the the run queue, signal runner to start operations if blocking
|
151
|
+
@schedule_lock.synchronize {
|
152
|
+
@run_lock.synchronize{
|
153
|
+
locs_to_run.each { |loc| @run_queue << loc ; @schedule_queue.delete(loc) }
|
154
|
+
empty_queue = (@schedule_queue.size == 0)
|
155
|
+
@run_cv.signal unless locs_to_run.empty?
|
156
|
+
}
|
157
|
+
}
|
90
158
|
|
159
|
+
# if there are locations still to be scheduled, sleep for the smallest step_delay
|
160
|
+
unless empty_queue
|
161
|
+
# we use locations instead of @schedule_queue here since a when the scheduler is
|
162
|
+
# sleeping a loc w/ a smaller step_delay may complete running and be added back to the scheduler
|
163
|
+
min_delay= locations.sort { |a,b|
|
164
|
+
a.movement_strategy.step_delay <=> b.movement_strategy.step_delay
|
165
|
+
}.first.movement_strategy.step_delay
|
166
|
+
sleep min_delay
|
167
|
+
end
|
168
|
+
end
|
169
|
+
}
|
170
|
+
|
171
|
+
# until we are told to stop
|
91
172
|
until @terminate
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
173
|
+
locs_to_schedule = []
|
174
|
+
tqueue = []
|
175
|
+
|
176
|
+
@run_lock.synchronize{
|
177
|
+
# wait until we have locations to run
|
178
|
+
@run_cv.wait(@run_lock) if @run_queue.empty?
|
179
|
+
@run_queue.each { |l| tqueue << l }
|
96
180
|
}
|
97
181
|
|
98
|
-
|
99
|
-
|
100
|
-
|
182
|
+
# run through each location to be run, perform actual movement, invoke callbacks
|
183
|
+
tqueue.each { |loc|
|
184
|
+
Logger.debug "runner moving location #{loc.id} at #{loc.coordinates.join(",")} via #{loc.movement_strategy.class.to_s}"
|
101
185
|
|
102
|
-
|
103
|
-
|
186
|
+
# store the old location coordinates for comparison after the movement
|
187
|
+
old_coords = [loc.x, loc.y, loc.z]
|
104
188
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
}
|
189
|
+
elapsed = Time.now - location_timestamps[loc.id]
|
190
|
+
loc.movement_strategy.move loc, elapsed
|
191
|
+
location_timestamps[loc.id] = Time.now
|
109
192
|
|
110
|
-
|
111
|
-
|
193
|
+
# TODO invoke these async so as not to hold up the runner
|
194
|
+
# make sure to keep these in sync w/ those invoked in the simrpc adapter "update_location" handler
|
195
|
+
loc.movement_callbacks.each { |callback|
|
196
|
+
callback.invoke(loc, *old_coords)
|
112
197
|
}
|
198
|
+
loc.proximity_callbacks.each { |callback|
|
199
|
+
callback.invoke(loc)
|
200
|
+
}
|
201
|
+
|
202
|
+
locs_to_schedule << loc
|
113
203
|
}
|
114
204
|
|
115
|
-
|
205
|
+
# add locations back to schedule queue
|
206
|
+
@run_lock.synchronize{
|
207
|
+
@schedule_lock.synchronize{
|
208
|
+
locs_to_schedule.each { |loc| @schedule_queue << loc ; @run_queue.delete(loc) }
|
209
|
+
@schedule_cv.signal unless locs_to_schedule.empty?
|
210
|
+
}
|
211
|
+
}
|
116
212
|
end
|
213
|
+
|
214
|
+
scheduler.join
|
117
215
|
end
|
118
216
|
|
119
217
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# Motel simrpc adapter
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
require 'simrpc'
|
7
|
+
|
8
|
+
module Motel
|
9
|
+
|
10
|
+
# Motel::Server defines a server endpoint which manages locations
|
11
|
+
# and responds to simrpc requests
|
12
|
+
class Server
|
13
|
+
def initialize(args = {})
|
14
|
+
simrpc_args = args
|
15
|
+
simrpc_args[:id] = "location-server"
|
16
|
+
|
17
|
+
# create a simprc node
|
18
|
+
@simrpc_node = Simrpc::Node.new(simrpc_args)
|
19
|
+
|
20
|
+
# register handlers for the various motel simrpc methods
|
21
|
+
@simrpc_node.handle_method("get_location") { |location_id|
|
22
|
+
Logger.info "received get location #{location_id} request"
|
23
|
+
loc = nil
|
24
|
+
begin
|
25
|
+
loc = Runner.instance.locations.find { |loc| loc.id == location_id }
|
26
|
+
# FIXME traverse all of loc's descendants, and if remote location
|
27
|
+
# server is specified, send request to get child location, swapping
|
28
|
+
# it in for the one thats there
|
29
|
+
rescue Exception => e
|
30
|
+
Logger.warn "get location #{location_id} failed w/ exception #{e}"
|
31
|
+
end
|
32
|
+
Logger.info "get location #{location_id} request returning #{loc}"
|
33
|
+
loc
|
34
|
+
}
|
35
|
+
|
36
|
+
@simrpc_node.handle_method("create_location") { |location|
|
37
|
+
Logger.info "received create location request"
|
38
|
+
location = Location.new if location.nil?
|
39
|
+
ret = location
|
40
|
+
begin
|
41
|
+
location.x = 0 if location.x.nil?
|
42
|
+
location.y = 0 if location.y.nil?
|
43
|
+
location.z = 0 if location.z.nil?
|
44
|
+
|
45
|
+
# TODO decendants support w/ remote option (create additional locations on other servers)
|
46
|
+
Runner.instance.run location
|
47
|
+
|
48
|
+
rescue Exception => e
|
49
|
+
Logger.warn "create location failed w/ exception #{e}"
|
50
|
+
ret = nil
|
51
|
+
end
|
52
|
+
Logger.info "create location request created and returning #{ret.id}"
|
53
|
+
ret
|
54
|
+
}
|
55
|
+
|
56
|
+
@simrpc_node.handle_method("update_location") { |location|
|
57
|
+
Logger.info "received update location #{location.id} request"
|
58
|
+
success = true
|
59
|
+
if location.nil?
|
60
|
+
success = false
|
61
|
+
else
|
62
|
+
rloc = Runner.instance.locations.find { |loc| loc.id == location.id }
|
63
|
+
begin
|
64
|
+
# store the old location coordinates for comparison after the movement
|
65
|
+
old_coords = [location.x, location.y, location.z]
|
66
|
+
|
67
|
+
# FIXME XXX big problem/bug here, client must always specify location.movement_strategy, else location constructor will set it to stopped
|
68
|
+
# FIXME this should halt location movement, update location, then start it again
|
69
|
+
Logger.info "updating location #{location.id} with #{location}/#{location.movement_strategy}"
|
70
|
+
rloc.update(location)
|
71
|
+
|
72
|
+
# FIXME trigger location movement & proximity callbacks (make sure to keep these in sync w/ those invoked the the runner)
|
73
|
+
# right now we can't do this because a single simrpc node can't handle multiple sent message response, see FIXME XXX in lib/simrpc/node.rb
|
74
|
+
#rloc.movement_callbacks.each { |callback|
|
75
|
+
# callback.invoke(rloc, *old_coords)
|
76
|
+
#}
|
77
|
+
#rloc.proximity_callbacks.each { |callback|
|
78
|
+
# callback.invoke(rloc)
|
79
|
+
#}
|
80
|
+
|
81
|
+
rescue Exception => e
|
82
|
+
Logger.warn "update location #{location.id} failed w/ exception #{e}"
|
83
|
+
success = false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
Logger.info "update location #{location.id} returning #{success}"
|
87
|
+
success
|
88
|
+
}
|
89
|
+
|
90
|
+
@simrpc_node.handle_method("subscribe_to_location_movement") { |client_id, location_id, min_distance, min_x, min_y, min_z|
|
91
|
+
Logger.info "subscribe client #{client_id} to location #{location_id} movement request received"
|
92
|
+
loc = Runner.instance.locations.find { |loc| loc.id == location_id }
|
93
|
+
success = true
|
94
|
+
if loc.nil?
|
95
|
+
success = false
|
96
|
+
else
|
97
|
+
callback = Callbacks::Movement.new :min_distance => min_distance, :min_x => min_x, :min_y => min_y, :min_z => min_z,
|
98
|
+
:handler => lambda { |location, d, dx, dy, dz|
|
99
|
+
# send location to client
|
100
|
+
@simrpc_node.send_method("location_moved", client_id, location, d, dx, dy, dz)
|
101
|
+
}
|
102
|
+
loc.movement_callbacks.push callback
|
103
|
+
end
|
104
|
+
Logger.info "subscribe client #{client_id} to location #{location_id} movement returning #{success}"
|
105
|
+
success
|
106
|
+
}
|
107
|
+
|
108
|
+
@simrpc_node.handle_method("subscribe_to_locations_proximity") { |client_id, location1_id, location2_id, event, max_distance, max_x, max_y, max_z|
|
109
|
+
Logger.info "subscribe client #{client_id} to location #{location1_id}/#{location2_id} proximity request received"
|
110
|
+
loc1 = Runner.instance.locations.find { |loc| loc.id == location1_id }
|
111
|
+
loc2 = Runner.instance.locations.find { |loc| loc.id == location2_id }
|
112
|
+
success = true
|
113
|
+
if loc1.nil? || loc2.nil?
|
114
|
+
success = false
|
115
|
+
else
|
116
|
+
callback = Callbacks::Proximity.new :to_location => loc2, :event => event, :max_distance => max_distance, :max_x => max_x, :max_y => max_y, :max_z => max_z,
|
117
|
+
:handler => lambda { |location1, location2|
|
118
|
+
# send locations to client
|
119
|
+
@simrpc_node.send_method("locations_proximity", client_id, location1, location2)
|
120
|
+
}
|
121
|
+
loc1.proximity_callbacks.push callback
|
122
|
+
end
|
123
|
+
Logger.info "subscribe client #{client_id} to location #{location1_id}/#{location2_id} proximity request returning #{success}"
|
124
|
+
success
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def terminate
|
129
|
+
@simrpc_node.terminate
|
130
|
+
end
|
131
|
+
|
132
|
+
def join
|
133
|
+
@simrpc_node.join
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Client defines a client endpoint that performs
|
138
|
+
# a request against a Motel Server
|
139
|
+
class Client
|
140
|
+
# Set to a callable object that will take a location and distance moved
|
141
|
+
attr_writer :on_location_moved
|
142
|
+
|
143
|
+
# Set to a callable object that will take two locations
|
144
|
+
attr_writer :on_locations_proximity
|
145
|
+
|
146
|
+
# Initialize the client with various args, all of which are passed onto Simrpc::Node constructor
|
147
|
+
def initialize(args = {})
|
148
|
+
simrpc_args = args
|
149
|
+
simrpc_args[:destination] = "location-server"
|
150
|
+
|
151
|
+
@simrpc_node = Simrpc::Node.new(simrpc_args)
|
152
|
+
end
|
153
|
+
|
154
|
+
def join
|
155
|
+
@simrpc_node.join
|
156
|
+
end
|
157
|
+
|
158
|
+
def request(target, *args)
|
159
|
+
method_missing(target, *args)
|
160
|
+
end
|
161
|
+
|
162
|
+
# pass simrpc method requests right onto the simrpc node
|
163
|
+
def method_missing(method_id, *args)
|
164
|
+
# special case for subsscribe_to_location,
|
165
|
+
if method_id == :subscribe_to_location_movement
|
166
|
+
# add simrpc node id onto args list
|
167
|
+
args.unshift @simrpc_node.id
|
168
|
+
|
169
|
+
# handle location updates from the server, & issue subscribe request
|
170
|
+
@simrpc_node.handle_method("location_moved") { |location, d, dx, dy, dz|
|
171
|
+
Logger.info "location #{location.id} moved"
|
172
|
+
@on_location_moved.call(location, d, dx, dy, dz) unless @on_location_moved.nil?
|
173
|
+
}
|
174
|
+
|
175
|
+
elsif method_id == :subscribe_to_locations_proximity
|
176
|
+
# add simrpc node id onto args list
|
177
|
+
args.unshift @simrpc_node.id
|
178
|
+
|
179
|
+
# handle location proximity events from the server, & issue subscribe request
|
180
|
+
@simrpc_node.handle_method("locations_proximity") { |location1, location2|
|
181
|
+
Logger.info "location #{location1.id}/#{location2.id} proximity"
|
182
|
+
@on_locations_proximity.call(location1, location2) unless @on_locations_proximity.nil?
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
@simrpc_node.method_missing(method_id, *args)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|