motel 0.2

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