motel 0.2 → 0.3
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/README.rdoc +35 -0
- data/Rakefile +48 -0
- data/bin/clients/simple/main.rb +131 -0
- data/bin/server/main.rb +61 -0
- data/conf/motel-schema.xml +72 -0
- data/lib/motel/common.rb +35 -11
- data/lib/motel/dsl.rb +12 -0
- data/lib/motel/exceptions.rb +14 -0
- data/lib/motel/location.rb +100 -0
- data/lib/motel/{models → movement_strategies}/elliptical.rb +51 -89
- data/lib/motel/movement_strategies/linear.rb +54 -0
- data/lib/motel/movement_strategies/stopped.rb +18 -0
- data/lib/motel/movement_strategy.rb +29 -0
- data/lib/motel/runner.rb +94 -108
- data/lib/motel/simrpc.rb +131 -0
- data/lib/motel/thread_pool.rb +51 -0
- data/lib/motel.rb +9 -5
- data/spec/common_spec.rb +77 -0
- data/spec/dsl_spec.rb +27 -0
- data/spec/location_spec.rb +109 -0
- data/spec/movement_strategies/elliptical_spec.rb +90 -0
- data/spec/movement_strategies/linear_spec.rb +51 -0
- data/spec/movement_strategies/stopped_spec.rb +39 -0
- data/spec/movement_strategy_spec.rb +24 -0
- data/spec/runner_spec.rb +45 -0
- data/spec/simrpc_spec.rb +85 -0
- data/spec/spec_helper.rb +26 -0
- metadata +36 -34
- data/README +0 -0
- data/conf/amqp.yml +0 -13
- data/conf/database.yml +0 -41
- data/db/migrate/001_create_locations_and_movement_strategies.rb +0 -45
- data/db/migrate/002_create_linear_movement_strategy.rb +0 -23
- data/db/migrate/003_create_elliptical_movement_strategy.rb +0 -52
- data/lib/motel/loader.rb +0 -47
- data/lib/motel/models/linear.rb +0 -76
- data/lib/motel/models/location.rb +0 -109
- data/lib/motel/models/movement_strategy.rb +0 -82
- data/lib/motel/models/stopped.rb +0 -16
@@ -1,13 +1,13 @@
|
|
1
1
|
# The Elliptcial MovementStrategy model definition
|
2
2
|
#
|
3
|
-
# Copyright (C)
|
4
|
-
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
5
|
|
6
6
|
require 'motel/common'
|
7
|
-
require 'motel/
|
7
|
+
require 'motel/movement_strategy'
|
8
8
|
|
9
9
|
module Motel
|
10
|
-
module
|
10
|
+
module MovementStrategies
|
11
11
|
|
12
12
|
# The Elliptical MovementStrategy moves a location
|
13
13
|
# on an elliptical path described by major/minor
|
@@ -19,41 +19,44 @@ module Models
|
|
19
19
|
# Lastly a speed value is required indicating the
|
20
20
|
# angular velocity of the location.
|
21
21
|
class Elliptical < MovementStrategy
|
22
|
+
attr_accessor :relative_to, :speed
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
self.direction_major_x, self.direction_major_y, self.direction_major_z = dx, dy, dz
|
38
|
-
|
39
|
-
dx, dy, dz =
|
40
|
-
normalize(direction_minor_x, direction_minor_y, direction_minor_z)
|
41
|
-
self.direction_minor_x, self.direction_minor_y, self.direction_minor_z = dx, dy, dz
|
42
|
-
end
|
24
|
+
attr_accessor :eccentricity, :semi_latus_rectum
|
25
|
+
|
26
|
+
attr_accessor :direction_major_x, :direction_major_y, :direction_major_z,
|
27
|
+
:direction_minor_x, :direction_minor_y, :direction_minor_z
|
28
|
+
|
29
|
+
def initialize(args = {})
|
30
|
+
@relative_to = args[:relative_to] if args.has_key? :relative_to
|
31
|
+
@speed = args[:speed] if args.has_key? :speed
|
32
|
+
@eccentricity = args[:eccentricity] if args.has_key? :eccentricity
|
33
|
+
@semi_latus_rectum = args[:semi_latus_rectum] if args.has_key? :semi_latus_rectum
|
34
|
+
|
35
|
+
@direction_major_x = args[:direction_major_x] if args.has_key? :direction_major_x
|
36
|
+
@direction_major_y = args[:direction_major_y] if args.has_key? :direction_major_y
|
37
|
+
@direction_major_z = args[:direction_major_z] if args.has_key? :direction_major_z
|
43
38
|
|
39
|
+
@direction_minor_x = args[:direction_minor_x] if args.has_key? :direction_minor_x
|
40
|
+
@direction_minor_y = args[:direction_minor_y] if args.has_key? :direction_minor_y
|
41
|
+
@direction_minor_z = args[:direction_minor_z] if args.has_key? :direction_minor_z
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
@direction_major_x = 1 if @direction_major_x.nil?
|
44
|
+
@direction_major_y = 0 if @direction_major_y.nil?
|
45
|
+
@direction_major_z = 0 if @direction_major_z.nil?
|
46
|
+
@direction_minor_x = 0 if @direction_minor_x.nil?
|
47
|
+
@direction_minor_y = 1 if @direction_minor_y.nil?
|
48
|
+
@direction_minor_z = 0 if @direction_minor_z.nil?
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
@direction_major_x, @direction_major_y, @direction_major_z =
|
51
|
+
normalize(@direction_major_x, @direction_major_y, @direction_major_z)
|
52
|
+
|
53
|
+
@direction_minor_x, @direction_minor_y, @direction_minor_z =
|
54
|
+
normalize(@direction_minor_x, @direction_minor_y, @direction_minor_z)
|
55
|
+
|
56
|
+
unless orthogonal?(@direction_major_x, @direction_major_y, @direction_major_z, @direction_minor_x, @direction_minor_y, @direction_minor_z)
|
57
|
+
raise InvalidMovementStrategy.new("elliptical direction vectors not orthogonal")
|
58
|
+
end
|
59
|
+
end
|
57
60
|
|
58
61
|
def e
|
59
62
|
eccentricity
|
@@ -62,11 +65,6 @@ class Elliptical < MovementStrategy
|
|
62
65
|
eccentricity= v
|
63
66
|
end
|
64
67
|
|
65
|
-
# the semi_latus_rectum of the ellipse
|
66
|
-
validates_presence_of :semi_latus_rectum
|
67
|
-
validates_numericality_of :semi_latus_rectum,
|
68
|
-
:greater_than_or_equal_to => 0
|
69
|
-
|
70
68
|
def p
|
71
69
|
semi_latus_rectum
|
72
70
|
end
|
@@ -78,56 +76,22 @@ class Elliptical < MovementStrategy
|
|
78
76
|
RELATIVE_TO_CENTER = "center"
|
79
77
|
RELATIVE_TO_FOCI = "foci"
|
80
78
|
|
81
|
-
# must be relative to a parent center or foci
|
82
|
-
validates_presence_of :relative_to
|
83
|
-
validates_inclusion_of :relative_to,
|
84
|
-
:in => [ RELATIVE_TO_CENTER, RELATIVE_TO_FOCI ]
|
85
|
-
|
86
|
-
# ActiveRecord::Base::validate
|
87
|
-
def validate
|
88
|
-
errors.add("direction vectors must be orthogonal") unless orthogonal?(direction_major_x, direction_major_y, direction_major_z,
|
89
|
-
direction_minor_x, direction_minor_y, direction_minor_z)
|
90
|
-
end
|
91
|
-
|
92
|
-
# convert non-nil elliptical movement strategy attributes to a hash
|
93
|
-
def to_h
|
94
|
-
result = {}
|
95
|
-
result[:speed] = speed unless speed.nil?
|
96
|
-
result[:eccentricity] = eccentricity unless eccentricity.nil?
|
97
|
-
result[:semi_latus_rectum] = semi_latus_rectum unless semi_latus_rectum.nil?
|
98
|
-
result[:relative_to] = relative_to unless relative_to.nil?
|
99
|
-
result[:direction_major_x] = direction_major_x unless direction_major_x.nil?
|
100
|
-
result[:direction_major_y] = direction_major_y unless direction_major_y.nil?
|
101
|
-
result[:direction_major_z] = direction_major_z unless direction_major_z.nil?
|
102
|
-
result[:direction_minor_x] = direction_minor_x unless direction_minor_x.nil?
|
103
|
-
result[:direction_minor_y] = direction_minor_y unless direction_minor_y.nil?
|
104
|
-
result[:direction_minor_z] = direction_minor_z unless direction_minor_z.nil?
|
105
|
-
return result
|
106
|
-
end
|
107
|
-
|
108
|
-
# convert elliptical movement strategy to a string
|
109
|
-
def to_s
|
110
|
-
super + "; speed:#{speed}; eccentricity:#{eccentricity}; semi_latus_rectum:#{semi_latus_rectum}; relative_to:#{relative_to}; " +
|
111
|
-
"direction_major_x:#{direction_major_x}; direction_major_y:#{direction_major_y}; direction_major_z:#{direction_major_z}; " +
|
112
|
-
"direction_minor_x:#{direction_minor_x}; direction_minor_y:#{direction_minor_y}; direction_minor_z:#{direction_minor_z}"
|
113
|
-
end
|
114
|
-
|
115
79
|
# Motel::Models::MovementStrategy::move
|
116
80
|
def move(location, elapsed_seconds)
|
117
|
-
# make sure this movement strategy is valid
|
118
|
-
unless valid?
|
119
|
-
|
120
|
-
|
121
|
-
end
|
81
|
+
# FIXME make sure this movement strategy is valid
|
82
|
+
#unless valid?
|
83
|
+
# Logger.warn "elliptical movement strategy not valid, not proceeding with move"
|
84
|
+
# return
|
85
|
+
#end
|
122
86
|
|
123
|
-
|
124
|
-
unless location_valid? location
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
87
|
+
## FIXME make sure location is on ellipse
|
88
|
+
#unless location_valid? location
|
89
|
+
# cx,cy,cz = closest_coordinates location
|
90
|
+
# Logger.warn "location #{location} not on ellipse, the closest location is #{cl}, not proceeding with move"
|
91
|
+
# return
|
92
|
+
#end
|
129
93
|
|
130
|
-
Logger.debug "moving location #{location} via elliptical movement strategy"
|
94
|
+
Logger.debug "moving location #{location.id} via elliptical movement strategy"
|
131
95
|
|
132
96
|
# calculate distance moved and update x,y,z accordingly
|
133
97
|
distance = speed * elapsed_seconds
|
@@ -136,8 +100,6 @@ class Elliptical < MovementStrategy
|
|
136
100
|
location.x = nX
|
137
101
|
location.y = nY
|
138
102
|
location.z = nZ
|
139
|
-
|
140
|
-
Logger.debug "moved location #{location} via elliptical movement strategy"
|
141
103
|
end
|
142
104
|
|
143
105
|
private
|
@@ -255,5 +217,5 @@ class Elliptical < MovementStrategy
|
|
255
217
|
|
256
218
|
end
|
257
219
|
|
258
|
-
end # module
|
220
|
+
end # module MovementStrategies
|
259
221
|
end # module Motel
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# The Linear MovementStrategy model definition
|
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 'motel/common'
|
7
|
+
require 'motel/movement_strategy'
|
8
|
+
|
9
|
+
module Motel
|
10
|
+
module MovementStrategies
|
11
|
+
|
12
|
+
# The Linear MovementStrategy moves a location
|
13
|
+
# in a linear manner as defined by a
|
14
|
+
# unit direction vector and a floating point
|
15
|
+
# speed
|
16
|
+
class Linear < MovementStrategy
|
17
|
+
attr_accessor :direction_vector_x, :direction_vector_y, :direction_vector_z
|
18
|
+
|
19
|
+
attr_accessor :speed
|
20
|
+
|
21
|
+
def initialize(args = {})
|
22
|
+
@direction_vector_x = args[:direction_vector_x] if args.has_key? :direction_vector_x
|
23
|
+
@direction_vector_y = args[:direction_vector_y] if args.has_key? :direction_vector_y
|
24
|
+
@direction_vector_z = args[:direction_vector_z] if args.has_key? :direction_vector_z
|
25
|
+
@speed = args[:speed] if args.has_key? :speed
|
26
|
+
|
27
|
+
# normalize direction vector
|
28
|
+
@direction_vector_x, @direction_vector_y, @direction_vector_z =
|
29
|
+
normalize(@direction_vector_x, @direction_vector_y, @direction_vector_z)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Motel::Models::MovementStrategy::move
|
34
|
+
def move(location, elapsed_seconds)
|
35
|
+
#unless valid?
|
36
|
+
# Logger.warn "linear movement strategy not valid, not proceeding with move"
|
37
|
+
# return
|
38
|
+
#end
|
39
|
+
|
40
|
+
Logger.debug "moving location #{location.id} via linear movement strategy " +
|
41
|
+
"#{speed} #{direction_vector_x}/#{direction_vector_y}/#{direction_vector_z}"
|
42
|
+
|
43
|
+
# calculate distance and update x,y,z accordingly
|
44
|
+
distance = speed * elapsed_seconds
|
45
|
+
|
46
|
+
location.x += distance * direction_vector_x
|
47
|
+
location.y += distance * direction_vector_y
|
48
|
+
location.z += distance * direction_vector_z
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end # module Models
|
54
|
+
end # module Motel
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# The Stopped MovementStrategy model definition
|
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 'singleton'
|
7
|
+
require 'motel/movement_strategy'
|
8
|
+
|
9
|
+
module Motel
|
10
|
+
module MovementStrategies
|
11
|
+
|
12
|
+
# Stopped is the default MovementStrategy which does nothing
|
13
|
+
class Stopped < MovementStrategy
|
14
|
+
include Singleton
|
15
|
+
end
|
16
|
+
|
17
|
+
end # module MovementStrategies
|
18
|
+
end # module Motel
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# The MovementStrategy entity
|
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 'motel/common'
|
7
|
+
require 'motel/location'
|
8
|
+
|
9
|
+
module Motel
|
10
|
+
|
11
|
+
# MovementStrategy subclasses define the rules and params which
|
12
|
+
# a location changes its position.
|
13
|
+
class MovementStrategy
|
14
|
+
attr_accessor :step_delay
|
15
|
+
|
16
|
+
def initialize(args = {})
|
17
|
+
@step_delay = 5
|
18
|
+
@movement_callbacks = []
|
19
|
+
|
20
|
+
@step_delay = args[:step_delay] if args.has_key? :step_delay
|
21
|
+
end
|
22
|
+
|
23
|
+
# default movement strategy is to do nothing
|
24
|
+
def move(location, elapsed_seconds)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end # module Motel
|
data/lib/motel/runner.rb
CHANGED
@@ -2,133 +2,119 @@
|
|
2
2
|
# and is responsible for managing locations and moving them according to
|
3
3
|
# their corresponding movement_strategies
|
4
4
|
#
|
5
|
-
# Copyright (C)
|
6
|
-
#
|
5
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
6
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
7
7
|
|
8
|
-
|
8
|
+
require 'singleton'
|
9
|
+
require 'motel/thread_pool'
|
9
10
|
|
10
|
-
|
11
|
-
# its associated MovementStrategy.
|
12
|
-
class LocationRunner
|
13
|
-
attr_reader :location
|
14
|
-
attr_reader :run_thread
|
15
|
-
|
16
|
-
def initialize(location)
|
17
|
-
return if location.nil? || location.id.nil? || location.class != Location
|
18
|
-
|
19
|
-
@terminate = false
|
20
|
-
@location = location
|
21
|
-
@location_lock = Mutex.new
|
22
|
-
Logger.info " running location " + location.to_s
|
23
|
-
|
24
|
-
# TODO at some point use a thread pool approach
|
25
|
-
@run_thread = Thread.new { run_move_cycle(location) }
|
26
|
-
end
|
27
|
-
|
28
|
-
# Terminate run cycle, stopping location movement.
|
29
|
-
# After this the runner cannot be used again
|
30
|
-
def terminate
|
31
|
-
@terminate = true
|
32
|
-
@location_lock.synchronize{
|
33
|
-
unless run_thread.nil?
|
34
|
-
@run_thread.join
|
35
|
-
@run_thread = nil
|
36
|
-
end
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
# Launched in a seperate thread, run_move_cycle
|
43
|
-
# runs a location according to its associated
|
44
|
-
# movement strategy with a specified delay between
|
45
|
-
# runs. Terminated when the Runner.terminate
|
46
|
-
# method is invoked
|
47
|
-
def run_move_cycle(location)
|
48
|
-
# track the time between runs
|
49
|
-
start_time = Time.now
|
50
|
-
|
51
|
-
# run until we are instructed not to
|
52
|
-
until(@terminate) do
|
53
|
-
Logger.debug "runner invoking move on location " + location.to_s + " via " + location.movement_strategy.type.to_s + " movement strategy"
|
54
|
-
|
55
|
-
## perform the actual move
|
56
|
-
location.movement_strategy.move location, start_time - Time.now
|
57
|
-
start_time = Time.now
|
58
|
-
|
59
|
-
# TODO see if we've actually moved b4 invoking callbacks
|
60
|
-
location.movement_strategy.movement_callbacks.each { |callback|
|
61
|
-
callback.call(location)
|
62
|
-
}
|
11
|
+
module Motel
|
63
12
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
13
|
+
# Motel::Runner is a singleton class/object which acts as the primary
|
14
|
+
# mechanism to run locations in the system. It contains a thread pool
|
15
|
+
# which contains a specified number of threads which to move the managed
|
16
|
+
# locations in accordance to their location strategies.
|
17
|
+
class Runner
|
18
|
+
include Singleton
|
68
19
|
|
69
|
-
|
20
|
+
# locations being managed
|
21
|
+
attr_accessor :locations
|
70
22
|
|
23
|
+
# for testing purposes
|
24
|
+
attr_reader :thread_pool, :terminate, :run_thread
|
71
25
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
26
|
+
def initialize(args = {})
|
27
|
+
@terminate = false
|
28
|
+
@locations = []
|
29
|
+
@locations_lock = Mutex.new
|
76
30
|
|
77
|
-
|
78
|
-
|
79
|
-
# Default class constructor
|
80
|
-
# private as runner should be accessed through singleton 'get' method
|
81
|
-
def initialize
|
82
|
-
# set to true to terminate the runner
|
83
|
-
@terminate = false
|
84
|
-
|
85
|
-
# locations is a list of instances of LocationRunner to manage
|
86
|
-
@location_runners = []
|
87
|
-
@runners_lock = Mutex.new
|
31
|
+
@run_thread = nil
|
32
|
+
@run_delay = 2 # FIXME scale delay (only needed if locations is empty or has very few simple elements)
|
88
33
|
end
|
89
34
|
|
90
|
-
|
35
|
+
# Empty the list of locations being managed/tracked
|
36
|
+
def clear
|
37
|
+
@locations_lock.synchronize {
|
38
|
+
@locations.clear
|
39
|
+
}
|
40
|
+
end
|
91
41
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
42
|
+
# add location to runner to be managed, after this is called, the location's
|
43
|
+
# movement strategy's move method will be invoked periodically
|
44
|
+
def run(location)
|
45
|
+
@locations_lock.synchronize {
|
46
|
+
Logger.debug "adding location #{location.id} to run queue"
|
47
|
+
@locations.push location
|
48
|
+
}
|
96
49
|
end
|
97
50
|
|
98
|
-
|
51
|
+
# Start moving the locations. If :async => true is passed in, this will immediately
|
52
|
+
# return, else this will block until stop is called.
|
53
|
+
def start(args = {})
|
54
|
+
num_threads = 5
|
55
|
+
num_threads = args[:num_threads] if args.has_key? :num_threads
|
56
|
+
@terminate = false
|
57
|
+
@thread_pool = ThreadPool.new(num_threads)
|
58
|
+
|
59
|
+
if args.has_key?(:async) && args[:async]
|
60
|
+
Logger.debug "starting async motel runner"
|
61
|
+
@run_thread = Thread.new { run_cycle }
|
62
|
+
else
|
63
|
+
Logger.debug "starting motel runner"
|
64
|
+
run_cycle
|
65
|
+
end
|
99
66
|
|
100
|
-
# helper method, return all locations associated w/ runners
|
101
|
-
def locations
|
102
|
-
[] if @location_runners.nil? || @location_runners.size == 0
|
103
|
-
@location_runners.collect { |runner| runner.location }
|
104
67
|
end
|
105
68
|
|
106
|
-
#
|
107
|
-
def
|
108
|
-
|
69
|
+
# Stop locations movement
|
70
|
+
def stop
|
71
|
+
Logger.debug "stopping motel runner"
|
72
|
+
@terminate = true
|
73
|
+
@thread_pool.shutdown
|
74
|
+
join
|
75
|
+
Logger.debug "motel runner stopped"
|
109
76
|
end
|
110
77
|
|
111
|
-
#
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
@runners_lock.synchronize{
|
116
|
-
@location_runners.each { |runner|
|
117
|
-
runner.terminate
|
118
|
-
}
|
119
|
-
}
|
78
|
+
# Block until runner is shutdown before returning
|
79
|
+
def join
|
80
|
+
@run_thread.join unless @run_thread.nil?
|
81
|
+
@run_thread = nil
|
120
82
|
end
|
121
83
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
84
|
+
private
|
85
|
+
|
86
|
+
# Internal helper method performing main runner operations
|
87
|
+
def run_cycle
|
88
|
+
# track time between runs
|
89
|
+
start_time = Time.now
|
90
|
+
|
91
|
+
until @terminate
|
92
|
+
# copy locations into temp 2nd array so we're not holding up lock on locations array
|
93
|
+
tlocations = []
|
94
|
+
@locations_lock.synchronize {
|
95
|
+
@locations.each { |loc| tlocations.push loc }
|
96
|
+
}
|
97
|
+
|
98
|
+
tlocations.each { |loc|
|
99
|
+
@thread_pool.dispatch(loc) { |loc|
|
100
|
+
Logger.debug "runner moving location #{loc.id} via #{loc.movement_strategy.class.to_s}"
|
101
|
+
|
102
|
+
loc.movement_strategy.move loc, start_time - Time.now
|
103
|
+
start_time = Time.now
|
104
|
+
|
105
|
+
# TODO see if loc coordinates changed b4 doing this
|
106
|
+
loc.movement_callbacks.each { |callback|
|
107
|
+
callback.call(loc)
|
108
|
+
}
|
109
|
+
|
110
|
+
## delay as long as the strategy tells us to
|
111
|
+
sleep loc.movement_strategy.step_delay
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
sleep @run_delay
|
116
|
+
end
|
117
|
+
end
|
132
118
|
|
133
119
|
end
|
134
120
|
|
data/lib/motel/simrpc.rb
ADDED
@@ -0,0 +1,131 @@
|
|
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_id|
|
37
|
+
Logger.info "received create location #{location_id} request"
|
38
|
+
success = true
|
39
|
+
begin
|
40
|
+
Runner.instance.run Location.new(:id => location_id)
|
41
|
+
# TODO decendants support w/ remote option (create additional locations on other servers)
|
42
|
+
rescue Exception => e
|
43
|
+
Logger.warn "create location #{location_id} failed w/ exception #{e}"
|
44
|
+
success = false
|
45
|
+
end
|
46
|
+
Logger.info "create location #{location_id} request returning #{success}"
|
47
|
+
success
|
48
|
+
}
|
49
|
+
|
50
|
+
@simrpc_node.handle_method("update_location") { |location|
|
51
|
+
Logger.info "received update location #{location.id} request"
|
52
|
+
success = true
|
53
|
+
if location.nil?
|
54
|
+
success = false
|
55
|
+
else
|
56
|
+
rloc = Runner.instance.locations.find { |loc| loc.id == location.id }
|
57
|
+
begin
|
58
|
+
Logger.info "updating location #{location.id} with #{location}/#{location.movement_strategy}"
|
59
|
+
rloc.update(location)
|
60
|
+
rescue Exception => e
|
61
|
+
Logger.warn "update location #{location.id} failed w/ exception #{e}"
|
62
|
+
success = false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
Logger.info "update location #{location.id} returning #{success}"
|
66
|
+
success
|
67
|
+
}
|
68
|
+
|
69
|
+
@simrpc_node.handle_method("subscribe_to_location") { |location_id, client_id|
|
70
|
+
Logger.info "subscribe client #{client_id} to location #{location_id} request received"
|
71
|
+
loc = Runner.instance.locations.find { |loc| loc.id == location_id }
|
72
|
+
success = true
|
73
|
+
if loc.nil?
|
74
|
+
success = false
|
75
|
+
else
|
76
|
+
loc.movement_callbacks.push lambda { |location|
|
77
|
+
# send location to client
|
78
|
+
@simrpc_node.send_method("location_moved", client_id, location)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
Logger.info "subscribe client #{client_id} to location #{location_id} returning #{success}"
|
82
|
+
success
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def join
|
87
|
+
@simrpc_node.join
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Client defines a client endpoint that performs
|
92
|
+
# a request against a Motel Server
|
93
|
+
class Client
|
94
|
+
# should be a callable object that takes a location to be
|
95
|
+
# invoked when the server sends a location to the client
|
96
|
+
attr_writer :on_location_received
|
97
|
+
|
98
|
+
# Initialize the client with various args, all of which are passed onto Simrpc::Node constructor
|
99
|
+
def initialize(args = {})
|
100
|
+
simrpc_args = args
|
101
|
+
simrpc_args[:destination] = "location-server"
|
102
|
+
|
103
|
+
@simrpc_node = Simrpc::Node.new(simrpc_args)
|
104
|
+
end
|
105
|
+
|
106
|
+
def join
|
107
|
+
@simrpc_node.join
|
108
|
+
end
|
109
|
+
|
110
|
+
def request(target, *args)
|
111
|
+
method_missing(target, *args)
|
112
|
+
end
|
113
|
+
|
114
|
+
# pass simrpc method requests right onto the simrpc node
|
115
|
+
def method_missing(method_id, *args)
|
116
|
+
# special case for subsscribe_to_location,
|
117
|
+
if method_id == :subscribe_to_location
|
118
|
+
# add simrpc node id onto args list
|
119
|
+
args.push @simrpc_node.id
|
120
|
+
|
121
|
+
# handle location updates from the server, & issue subscribe request
|
122
|
+
@simrpc_node.handle_method("location_moved") { |location|
|
123
|
+
Logger.info "location #{location.id} moved"
|
124
|
+
@on_location_received.call(location) unless @on_location_received.nil?
|
125
|
+
}
|
126
|
+
end
|
127
|
+
@simrpc_node.method_missing(method_id, *args)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|