motel 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|