motel 0.2

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/conf/amqp.yml ADDED
@@ -0,0 +1,13 @@
1
+ # motel amqp config
2
+
3
+ production:
4
+ broker: 127.0.0.1
5
+ port: 5672
6
+
7
+ development:
8
+ broker: 127.0.0.1
9
+ port: 5672
10
+
11
+ test:
12
+ broker: 127.0.0.1
13
+ port: 5672
data/conf/database.yml ADDED
@@ -0,0 +1,41 @@
1
+ # motel database config
2
+ #
3
+ # Fedora Install Instructions:
4
+ # sudo yum install ruby-postgres postgresql-server
5
+ # sudo /sbin/service postgresql initdb
6
+ # sudo /sbin/service postgresql start
7
+ # sudo su - postgres
8
+ # createdb motel
9
+ # $ psql motel
10
+ # $-# CREATE USER motel WITH PASSWORD 'motel';
11
+ # $-# GRANT ALL PRIVILEGES ON DATABASE motel to motel;
12
+ # $-# CREATE DATABASE motel_test;
13
+ # $-# GRANT ALL PRIVILEGES ON DATABASE motel_test to motel;
14
+ # $-# CREATE DATABASE motel_development;
15
+ # $-# GRANT ALL PRIVILEGES ON DATABASE motel_development to motel;
16
+ # $-# \q
17
+ # $ exit
18
+
19
+ production:
20
+ adapter: postgresql
21
+ database: motel
22
+ username: motel
23
+ password: motel
24
+ host: localhost
25
+ pool: 20
26
+
27
+ development:
28
+ adapter: postgresql
29
+ database: motel_development
30
+ username: motel
31
+ password: motel
32
+ host: localhost
33
+ pool: 20
34
+
35
+ test:
36
+ adapter: postgresql
37
+ database: motel_test
38
+ username: motel
39
+ password: motel
40
+ host: localhost
41
+ pool: 20
@@ -0,0 +1,45 @@
1
+ # creates the locations and movement_strategies tables
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'motel'
7
+
8
+ # ActiveRecord::Migration 001
9
+ class CreateLocationsAndMovementStrategies < ActiveRecord::Migration
10
+ def self.up
11
+
12
+ create_table :movement_strategies do |t|
13
+ t.string :type, :null => false, :size => 50
14
+ t.float :step_delay, :null => false
15
+ end
16
+
17
+ create_table :locations do |t|
18
+ t.float :x, :default => nil
19
+ t.float :y, :default => nil
20
+ t.float :z, :default => nil
21
+ t.integer :movement_strategy_id, :null => false
22
+
23
+ t.integer :parent_id, :default => nil
24
+ end
25
+
26
+ execute "alter table locations add constraint fk_location_parent
27
+ foreign key(parent_id) references locations(id)"
28
+
29
+ execute "alter table locations add constraint fk_location_movement_strategy
30
+ foreign key(movement_strategy_id) references movement_strategies(id)"
31
+
32
+ execute "alter table locations add constraint root_or_child
33
+ check (parent_id IS NULL AND x IS NULL AND y IS NULL AND z IS NULL OR
34
+ parent_id IS NOT NULL AND x IS NOT NULL AND y IS NOT NULL AND z IS NOT NULL)"
35
+
36
+
37
+ # create the first / default movement strategy 'stopped'
38
+ Motel::Models::Stopped.new(:step_delay => 5).save!
39
+ end
40
+
41
+ def self.down
42
+ drop_table :locations
43
+ drop_table :movement_strategies
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ # creates the neccessary fields for the Linear MovementStrategy
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'motel'
7
+
8
+ # ActiveRecord::Migration 002
9
+ class CreateLinearMovementStrategy < ActiveRecord::Migration
10
+ def self.up
11
+ add_column :movement_strategies, :speed, :float
12
+ add_column :movement_strategies, :direction_vector_x, :float
13
+ add_column :movement_strategies, :direction_vector_y, :float
14
+ add_column :movement_strategies, :direction_vector_z, :float
15
+ end
16
+
17
+ def self.down
18
+ remove_column :movement_strategies, :speed
19
+ remove_column :movement_strategies, :direction_vector_x
20
+ remove_column :movement_strategies, :direction_vector_y
21
+ remove_column :movement_strategies, :direction_vector_z
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ # creates the neccessary fields for the Elliptical MovementStrategy
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'motel'
7
+
8
+ # ActiveRecord::Migration 003
9
+ class CreateEllipticalMovementStrategy < ActiveRecord::Migration
10
+ def self.up
11
+ # we already have speed from linear
12
+ #add_column :movement_strategies, :speed, :float
13
+
14
+ # relative_to field indicates what type of parent
15
+ # this movement stategy is relative to, for Ellilptical
16
+ # strategies, this can be 'center' indicating the parent
17
+ # is the center of the ellipse or 'foci' indicating the
18
+ # parent is on of the ellipse foci's
19
+ add_column :movement_strategies, :relative_to, :string, :size => 50
20
+
21
+ # eccentricity of the ellipse
22
+ add_column :movement_strategies, :eccentricity, :float
23
+
24
+ # semi_latus_rectum of the ellipse
25
+ add_column :movement_strategies, :semi_latus_rectum, :float
26
+
27
+ # unit direction vector of the major axis
28
+ add_column :movement_strategies, :direction_major_x, :float
29
+ add_column :movement_strategies, :direction_major_y, :float
30
+ add_column :movement_strategies, :direction_major_z, :float
31
+
32
+ # unit direction vector of the minor axis
33
+ add_column :movement_strategies, :direction_minor_x, :float
34
+ add_column :movement_strategies, :direction_minor_y, :float
35
+ add_column :movement_strategies, :direction_minor_z, :float
36
+ end
37
+
38
+ def self.down
39
+ # remove in linear strategy
40
+ #remove_column :movement_strategies, :speed
41
+
42
+ remove_column :movement_strategies, :relative_to
43
+ remove_column :movement_strategies, :eccentricity
44
+ remove_column :movement_strategies, :semi_latus_rectum
45
+ remove_column :movement_strategies, :direction_major_x
46
+ remove_column :movement_strategies, :direction_major_y
47
+ remove_column :movement_strategies, :direction_major_z
48
+ remove_column :movement_strategies, :direction_minor_x
49
+ remove_column :movement_strategies, :direction_minor_y
50
+ remove_column :movement_strategies, :direction_minor_z
51
+ end
52
+ end
@@ -0,0 +1,54 @@
1
+ # common & useful methods and other.
2
+ #
3
+ # Things that don't fit elsewhere
4
+ #
5
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
6
+ # See COPYING for the License of this software
7
+
8
+ module Motel
9
+
10
+ # generate a random id
11
+ def gen_uuid
12
+ ["%02x"*4, "%02x"*2, "%02x"*2, "%02x"*2, "%02x"*6].join("-") %
13
+ Array.new(16) {|x| rand(0xff) }
14
+ end
15
+
16
+ # normalize a vector if not normal already,
17
+ # eg divide each component x,y,z by the
18
+ # vector length and return them
19
+ def normalize(x,y,z)
20
+ return x,y,z if x.nil? || y.nil? || z.nil?
21
+
22
+ l = Math.sqrt(x**2 + y**2 + z**2)
23
+ if l != 1
24
+ x /= l
25
+ y /= l
26
+ z /= l
27
+ end
28
+ return x,y,z
29
+ end
30
+
31
+ # determine if two vectors are orthogonal
32
+ def orthogonal?(x1,y1,z1, x2,y2,z2)
33
+ return false if x1.nil? || y1.nil? || z1.nil? || x2.nil? || y2.nil? || z2.nil?
34
+ return (x1 * x2 + y1 * y2 + z1 * z2).abs == 0
35
+ end
36
+
37
+ end # module Motel
38
+
39
+ # provide floating point rounding mechanism
40
+ class Float
41
+ def round_to(precision)
42
+ return nil if precision <= 0
43
+ return (self * 10 ** precision).round.to_f / (10 ** precision)
44
+ end
45
+ end
46
+
47
+ # try to find key equal to method, returning value if found
48
+ #class Hash
49
+ # def method_missing(method, *params)
50
+ # method = method.to_sym
51
+ # return self[method] if self.keys.collect{ |k| k.to_sym }.include?(method)
52
+ # super
53
+ # end
54
+ #end
@@ -0,0 +1,47 @@
1
+ # The Loader class definition
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'motel/runner'
7
+
8
+ module Motel
9
+
10
+ # A Loader loads instances of Location from the db
11
+ # according to any specified conditions, instantiates
12
+ # a new Runner for each of those locations and returns them
13
+ class Loader
14
+
15
+ public
16
+
17
+ # Default class constructor
18
+ def initialize
19
+ end
20
+
21
+ # Static member to load all locations that match a specified
22
+ # condition (ala activerecord) and add it to the singleton Runner
23
+ # instance using it to run the locations.
24
+ def self.Load(conditions = 'parent_id IS NULL')
25
+ locations = Location.find(:all, :conditions => conditions)
26
+ return nil if locations.size == 0
27
+
28
+ locations.each { |location|
29
+ run_location(location)
30
+ }
31
+ return locations.size
32
+ end
33
+
34
+ private
35
+
36
+ # Static internal helper method that adds a location and
37
+ # all its children to the Runner
38
+ def self.run_location(location)
39
+ Runner.get.run location
40
+ location.children.each { |child|
41
+ run_location child
42
+ }
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,259 @@
1
+ # The Elliptcial MovementStrategy model definition
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'motel/common'
7
+ require 'motel/models/movement_strategy'
8
+
9
+ module Motel
10
+ module Models
11
+
12
+ # The Elliptical MovementStrategy moves a location
13
+ # on an elliptical path described by major/minor
14
+ # axis direction vectors, and an eccentricity /
15
+ # semi_latus_rectum. The path equation will
16
+ # also depend on the value of the relative_to
17
+ # field indicating if the parent location
18
+ # is the center or a foci of the ellipse.
19
+ # Lastly a speed value is required indicating the
20
+ # angular velocity of the location.
21
+ class Elliptical < MovementStrategy
22
+
23
+ # Elliptical MovementStrategy must specify x,y,z components of
24
+ # the major and minor axis direction vectors
25
+ validates_presence_of [:direction_major_x,
26
+ :direction_major_y,
27
+ :direction_major_z,
28
+ :direction_minor_x,
29
+ :direction_minor_y,
30
+ :direction_minor_z]
31
+
32
+ # make sure the unit direction vectors are normal
33
+ before_validation :normalize_direction_vectors
34
+ def normalize_direction_vectors
35
+ dx, dy, dz =
36
+ normalize(direction_major_x, direction_major_y, direction_major_z)
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
43
+
44
+
45
+ # Elliptical MovementStrategy must specify the angular velocity
46
+ # at which the location is moving
47
+ validates_presence_of :speed
48
+ validates_numericality_of :speed,
49
+ :greater_than_or_equal_to => 0,
50
+ :less_than_or_equal_to => 2 * Math::PI
51
+
52
+ # the eccentricity of the ellipse
53
+ validates_presence_of :eccentricity
54
+ validates_numericality_of :eccentricity,
55
+ :greater_than_or_equal_to => 0,
56
+ :less_than_or_equal_to => 1
57
+
58
+ def e
59
+ eccentricity
60
+ end
61
+ def e=(v)
62
+ eccentricity= v
63
+ end
64
+
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
+ def p
71
+ semi_latus_rectum
72
+ end
73
+ def p=(v)
74
+ semi_latus_rectum = v
75
+ end
76
+
77
+ # the possible relative_to values
78
+ RELATIVE_TO_CENTER = "center"
79
+ RELATIVE_TO_FOCI = "foci"
80
+
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
+ # Motel::Models::MovementStrategy::move
116
+ def move(location, elapsed_seconds)
117
+ # make sure this movement strategy is valid
118
+ unless valid?
119
+ Logger.warn "elliptical movement strategy not valid, not proceeding with move"
120
+ return
121
+ end
122
+
123
+ # make sure location is on ellipse
124
+ unless location_valid? location
125
+ cx,cy,cz = closest_coordinates location
126
+ Logger.warn "location #{location} not on ellipse, the closest location is #{cl}, not proceeding with move"
127
+ return
128
+ end
129
+
130
+ Logger.debug "moving location #{location} via elliptical movement strategy"
131
+
132
+ # calculate distance moved and update x,y,z accordingly
133
+ distance = speed * elapsed_seconds
134
+
135
+ nX,nY,nZ = coordinates_from_theta(theta(location) + distance)
136
+ location.x = nX
137
+ location.y = nY
138
+ location.z = nZ
139
+
140
+ Logger.debug "moved location #{location} via elliptical movement strategy"
141
+ end
142
+
143
+ private
144
+
145
+ ### internal helper movement methods
146
+
147
+ # return the a,b intercepts of the ellipse
148
+ # p = a(1 - e^2) = b^2 / a
149
+ # e = sqrt(1 - (b/a)^2)
150
+ def intercepts
151
+ a = p / (1 - e**2)
152
+ b = Math.sqrt(p * a)
153
+ return a,b
154
+ end
155
+
156
+ # return the linear eccentricity of the ellipse
157
+ # le = sqrt(a^2 - b^2)
158
+ def linear_eccentricity
159
+ a,b = intercepts
160
+ Math.sqrt(a**2 - b**2);
161
+ end
162
+
163
+ # return the coordinates of the center position
164
+ # C = (-direction_major) * le
165
+ def center
166
+ return 0,0,0 if relative_to == RELATIVE_TO_CENTER
167
+
168
+ a,b = intercepts
169
+ le = linear_eccentricity
170
+
171
+ centerX = -1 * direction_major_x * le;
172
+ centerY = -1 * direction_major_y * le;
173
+ centerZ = -1 * direction_major_z * le;
174
+ return centerX, centerY, centerZ
175
+ end
176
+
177
+ # return the coordinates of a focus position
178
+ # F = direction_major * le
179
+ def focus
180
+ return 0,0,0 if relative_to == RELATIVE_TO_FOCI
181
+
182
+ a,b = intercepts
183
+ le = linear_eccentricity
184
+
185
+ focusX = direction_major_x * le;
186
+ focusY = direction_major_y * le;
187
+ focusZ = direction_major_z * le;
188
+ return focusX, focusY, focusZ
189
+ end
190
+
191
+ # return the origin centered coordiates of a location
192
+ def origin_centered_coordinates(location)
193
+ cX,cY,cZ = center
194
+ return location.x - cX,
195
+ location.y - cY,
196
+ location.z - cZ
197
+ end
198
+
199
+ # return the theta corresponding to the position of a
200
+ # location on the elliptical path.
201
+ #
202
+ # derived formula for theta from x,y,z elliptical path equations (see below) and linear transformations:
203
+ # theta = acos((minY * (x-cX) - minX * (y-cY))/(a*(minY * majX - minX * majY)))
204
+ def theta(location)
205
+ a,b = intercepts
206
+ ocX,ocY,ocZ = origin_centered_coordinates location
207
+
208
+ t = (direction_minor_y * ocX - direction_minor_x * ocY) /
209
+ (a * (direction_minor_y * direction_major_x - direction_minor_x * direction_major_y))
210
+ t= 1.0 if(t>1.0)
211
+ theta = Math.acos(t)
212
+
213
+ # determine if current point is in negative quadrants of min axis coordinate system
214
+ below = ocY < ((direction_minor_x * ocX + direction_minor_z * ocZ) / (-direction_minor_y))
215
+
216
+ # adjust to compenate for acos loss if necessary
217
+ theta = (3 * Math::PI / 2) + (Math::PI / 2 - theta) if (below)
218
+
219
+ return theta;
220
+ end
221
+
222
+ # calculate the x,y,z coordinates of a location on the elliptical
223
+ # path given its theta
224
+ #
225
+ # Elliptical path equation:
226
+ # [x,y,z] = a * cos(theta) * maj + b * sin(theta) * min
227
+ # (if centered at origin)
228
+ def coordinates_from_theta(theta)
229
+ a,b = intercepts
230
+ cX,cY,cZ = center
231
+
232
+ x = cX + a * Math.cos(theta) * direction_major_x + b * Math.sin(theta) * direction_minor_x
233
+ y = cY + a * Math.cos(theta) * direction_major_y + b * Math.sin(theta) * direction_minor_y
234
+ z = cZ + a * Math.cos(theta) * direction_major_z + b * Math.sin(theta) * direction_minor_z
235
+ return x,y,z
236
+ end
237
+
238
+ # return x,y,z coordinates of the closest point on the ellipse to the given location
239
+ def closest_coordinates(location)
240
+ t = theta location
241
+
242
+ return nil if t.nan?
243
+
244
+ return coordinates_from_theta(t)
245
+ end
246
+
247
+ # return boolean indicating if the given location is on the ellipse or not
248
+ def location_valid?(location)
249
+ x,y,z = closest_coordinates(location)
250
+
251
+ return (x - location.x).round_to(4) == 0 &&
252
+ (y - location.y).round_to(4) == 0 &&
253
+ (z - location.z).round_to(4) == 0
254
+ end
255
+
256
+ end
257
+
258
+ end # module Models
259
+ end # module Motel
@@ -0,0 +1,76 @@
1
+ # The Linear MovementStrategy model definition
2
+ #
3
+ # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
4
+ # See COPYING for the License of this software
5
+
6
+ require 'motel/common'
7
+ require 'motel/models/movement_strategy'
8
+
9
+ module Motel
10
+ module Models
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
+
18
+ # Linear MovementStrategy must specify x,y,z components of
19
+ # a unit direction vector
20
+ validates_presence_of [:direction_vector_x,
21
+ :direction_vector_y,
22
+ :direction_vector_z]
23
+
24
+ # make sure the unit direction vector is normal
25
+ before_validation :normalize_direction_vector
26
+ def normalize_direction_vector
27
+ dx, dy, dz =
28
+ normalize(direction_vector_x, direction_vector_y, direction_vector_z)
29
+ self.direction_vector_x, self.direction_vector_y, self.direction_vector_z = dx, dy, dz
30
+ end
31
+
32
+
33
+ # Linear MovementStrategy must specify the speed
34
+ # at which the location is moving
35
+ validates_presence_of :speed
36
+ validates_numericality_of :speed,
37
+ :greater_than_or_equal_to => 0
38
+
39
+ # Motel::Models::MovementStrategy::move
40
+ def move(location, elapsed_seconds)
41
+ unless valid?
42
+ Logger.warn "linear movement strategy not valid, not proceeding with move"
43
+ return
44
+ end
45
+
46
+ Logger.debug "moving location #{location.to_s} via linear movement strategy"
47
+
48
+ # calculate distance and update x,y,z accordingly
49
+ distance = speed * elapsed_seconds
50
+
51
+ location.x += distance * direction_vector_x
52
+ location.y += distance * direction_vector_y
53
+ location.z += distance * direction_vector_z
54
+
55
+ Logger.debug "moved location #{location} via linear movement strategy"
56
+ end
57
+
58
+ # convert non-nil linear movement strategy attributes to a hash
59
+ def to_h
60
+ result = {}
61
+ result[:speed] = speed unless speed.nil?
62
+ result[:direction_vector_x] = direction_vector_x unless direction_vector_x.nil?
63
+ result[:direction_vector_y] = direction_vector_y unless direction_vector_y.nil?
64
+ result[:direction_vector_z] = direction_vector_z unless direction_vector_z.nil?
65
+ return result
66
+ end
67
+
68
+ # convert linear movement strategy to a string
69
+ def to_s
70
+ super + "; speed: #{speed}; direction_vector_x:#{direction_vector_x}; " +
71
+ "direction_vector_y:#{direction_vector_y}; direction_vector_z:#{direction_vector_z}"
72
+ end
73
+ end
74
+
75
+ end # module Models
76
+ end # module Motel