motel 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +8 -0
- data/LICENSE +661 -0
- data/README +0 -0
- data/conf/amqp.yml +13 -0
- data/conf/database.yml +41 -0
- data/db/migrate/001_create_locations_and_movement_strategies.rb +45 -0
- data/db/migrate/002_create_linear_movement_strategy.rb +23 -0
- data/db/migrate/003_create_elliptical_movement_strategy.rb +52 -0
- data/lib/motel/common.rb +54 -0
- data/lib/motel/loader.rb +47 -0
- data/lib/motel/models/elliptical.rb +259 -0
- data/lib/motel/models/linear.rb +76 -0
- data/lib/motel/models/location.rb +109 -0
- data/lib/motel/models/movement_strategy.rb +82 -0
- data/lib/motel/models/stopped.rb +16 -0
- data/lib/motel/runner.rb +135 -0
- data/lib/motel/semaphore.rb +58 -0
- data/lib/motel.rb +13 -0
- metadata +82 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
# The Location 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/models/movement_strategy'
|
7
|
+
|
8
|
+
module Motel
|
9
|
+
module Models
|
10
|
+
|
11
|
+
# A Location defines an optional parent location and the x,y,z
|
12
|
+
# cartesian coordinates of the location relative to that parent.
|
13
|
+
# If parent is not specified x,y,z are ignored and this location
|
14
|
+
# is assumed to be the 'center' of the system to which it belongs
|
15
|
+
# Also is related to a movable object defining the parameters
|
16
|
+
# of the locations movement
|
17
|
+
class Location < ActiveRecord::Base
|
18
|
+
# a location may have a parent and/or act as a parent to others
|
19
|
+
belongs_to :location, :foreign_key => :parent_id
|
20
|
+
has_many :locations, :foreign_key => :parent_id, :dependent => :destroy
|
21
|
+
|
22
|
+
belongs_to :movement_strategy
|
23
|
+
|
24
|
+
# a generic association which this location can belong to
|
25
|
+
belongs_to :entity, :polymorphic => true
|
26
|
+
#validates_presense_of [ :entity_type, :entity_id ]
|
27
|
+
|
28
|
+
# FIXME add optional remote_location_server field
|
29
|
+
|
30
|
+
alias :parent :location
|
31
|
+
alias :parent= :location=
|
32
|
+
alias :children :locations
|
33
|
+
alias :children= :locations=
|
34
|
+
|
35
|
+
# return this location's root location
|
36
|
+
def root
|
37
|
+
return self if parent.nil?
|
38
|
+
return parent.root
|
39
|
+
end
|
40
|
+
|
41
|
+
# traverse all chilren recursively, calling block for each
|
42
|
+
def traverse_descendants(&bl)
|
43
|
+
children.each { |child|
|
44
|
+
bl.call child
|
45
|
+
child.traverse_descendants &bl
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# default to the stopped movement strategy if not set on validation
|
50
|
+
before_validation :default_movement_strategy
|
51
|
+
def default_movement_strategy
|
52
|
+
self.movement_strategy = MovementStrategy.stopped if self.movement_strategy.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
public
|
56
|
+
validates_presence_of :movement_strategy
|
57
|
+
|
58
|
+
validates_presence_of [:x, :y, :z],
|
59
|
+
:unless => Proc.new { |location| location.location.nil? }
|
60
|
+
|
61
|
+
validates_presence_of :location,
|
62
|
+
:unless => Proc.new { |location| location.x.nil? &&
|
63
|
+
location.y.nil? &&
|
64
|
+
location.z.nil? }
|
65
|
+
|
66
|
+
public
|
67
|
+
|
68
|
+
# return non-nil attributes in hash
|
69
|
+
def to_h
|
70
|
+
result = {}
|
71
|
+
#result[:id] = id unless id.nil?
|
72
|
+
result[:parent_id] = parent_id unless parent_id.nil?
|
73
|
+
result[:x] = x unless x.nil?
|
74
|
+
result[:y] = y unless y.nil?
|
75
|
+
result[:z] = z unless z.nil?
|
76
|
+
return result
|
77
|
+
end
|
78
|
+
|
79
|
+
# convert location to a string
|
80
|
+
def to_s
|
81
|
+
"id:#{id}; parent_id:#{parent_id}; parent: #{parent.nil? ? "nil" : "notnil"}; entity type: #{entity_type}; x:#{x}; y:#{y}; z:#{z}; " +
|
82
|
+
"movement_strategy:#{movement_strategy.to_s}; children:#{locations.size}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# return sum of the x values of this location and all its parents,
|
86
|
+
# eg the absolute 'x' of the location
|
87
|
+
def total_x
|
88
|
+
return 0 if location.nil?
|
89
|
+
return location.total_x + x
|
90
|
+
end
|
91
|
+
|
92
|
+
# return sum of the y values of this location and all its parents
|
93
|
+
# eg the absolute 'y' of the location
|
94
|
+
def total_y
|
95
|
+
return 0 if location.nil?
|
96
|
+
return location.total_y + y
|
97
|
+
end
|
98
|
+
|
99
|
+
# return sum of the z values of this location and all its parents
|
100
|
+
# eg the absolute 'z' of the location
|
101
|
+
def total_z
|
102
|
+
return 0 if location.nil?
|
103
|
+
return location.total_z + z
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end # module Models
|
109
|
+
end # module Motel
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# The 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/location'
|
8
|
+
|
9
|
+
module Motel
|
10
|
+
module Models
|
11
|
+
|
12
|
+
# MovementStrategy subclasses define the rules and params which
|
13
|
+
# a location changes its position.
|
14
|
+
class MovementStrategy < ActiveRecord::Base
|
15
|
+
# strategy is associated with location to move it
|
16
|
+
has_many :locations, :dependent => :destroy
|
17
|
+
|
18
|
+
# callbacks invoked when this object is moved
|
19
|
+
attr_accessor :movement_callbacks
|
20
|
+
|
21
|
+
# every movement strategy needs a type
|
22
|
+
validates_presence_of :type
|
23
|
+
|
24
|
+
# as more types are supported, add them here
|
25
|
+
validates_inclusion_of :type,
|
26
|
+
:in => %w( Stopped Linear Elliptical )
|
27
|
+
|
28
|
+
# step delay is recommended number of seconds
|
29
|
+
# a runner should sleep for between move invocations
|
30
|
+
validates_presence_of :step_delay
|
31
|
+
|
32
|
+
# default step_delay if not set
|
33
|
+
before_validation :default_step_delay
|
34
|
+
def default_step_delay
|
35
|
+
self.step_delay = 5 if step_delay.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
# use after_initialize instead of initialize
|
39
|
+
# http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html
|
40
|
+
def after_initialize
|
41
|
+
@movement_callbacks = []
|
42
|
+
end
|
43
|
+
|
44
|
+
# default movement strategy is to do nothing
|
45
|
+
def move(location, elapsed_seconds)
|
46
|
+
end
|
47
|
+
|
48
|
+
# retreive the 'stopped' movement strategy
|
49
|
+
def self.stopped
|
50
|
+
ms = MovementStrategy.find(:first, :conditions => "type = 'Stopped'")
|
51
|
+
ms.nil? ? Stopped.new(:step_delay => 5) : ms
|
52
|
+
end
|
53
|
+
|
54
|
+
# return subclass corresponding to specified strategy type
|
55
|
+
def self.factory(strategy_type)
|
56
|
+
return MovementStrategy if strategy_type.nil?
|
57
|
+
|
58
|
+
if strategy_type.downcase == "stopped" || strategy_type == "Motel::Models::Stopped"
|
59
|
+
return Stopped
|
60
|
+
elsif strategy_type.downcase == "linear" || strategy_type == "Motel::Models::Linear"
|
61
|
+
return Linear
|
62
|
+
elsif strategy_type.downcase == "elliptical" || strategy_type == "Motel::Models::Elliptical"
|
63
|
+
return Elliptical
|
64
|
+
end
|
65
|
+
|
66
|
+
return MovementStrategy
|
67
|
+
end
|
68
|
+
|
69
|
+
# convert movement strategy to a hash
|
70
|
+
def to_h
|
71
|
+
{}
|
72
|
+
end
|
73
|
+
|
74
|
+
# convert movement strategy to a string
|
75
|
+
def to_s
|
76
|
+
"id:#{id}; type:#{self.class}" # XXX should be self.type but ruby will complain, this works for now
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end # module Models
|
82
|
+
end # module Motel
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# The Stopped 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/models/movement_strategy'
|
7
|
+
|
8
|
+
module Motel
|
9
|
+
module Models
|
10
|
+
|
11
|
+
# Stopped is the default MovementStrategy which does nothing
|
12
|
+
class Stopped < MovementStrategy
|
13
|
+
end
|
14
|
+
|
15
|
+
end # module Models
|
16
|
+
end # module Motel
|
data/lib/motel/runner.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
# Defines the LocationRunner and Runner classes, core to the motel engine,
|
2
|
+
# and is responsible for managing locations and moving them according to
|
3
|
+
# their corresponding movement_strategies
|
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
|
+
# A LocationRunner runs a Location by moving it via
|
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
|
+
}
|
63
|
+
|
64
|
+
## delay as long as the strategy tells us to
|
65
|
+
sleep location.movement_strategy.step_delay
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# A Runner manages groups of LocationRunner instances
|
73
|
+
# Restricts use to a single instance, obtained via the
|
74
|
+
# 'get' method
|
75
|
+
class Runner
|
76
|
+
|
77
|
+
private
|
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
|
88
|
+
end
|
89
|
+
|
90
|
+
public
|
91
|
+
|
92
|
+
# singleton getter
|
93
|
+
def self.get
|
94
|
+
@@singleton_instance = Runner.new if !defined? @@singleton_instance || @@singleton_instance.nil?
|
95
|
+
return @@singleton_instance
|
96
|
+
end
|
97
|
+
|
98
|
+
attr_reader :location_runners
|
99
|
+
|
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
|
+
end
|
105
|
+
|
106
|
+
# clear all location_runners
|
107
|
+
def clear
|
108
|
+
@location_runners.clear
|
109
|
+
end
|
110
|
+
|
111
|
+
# Terminate all run cycles, stopping all location movements.
|
112
|
+
# After this the runner cannot be used again
|
113
|
+
def terminate
|
114
|
+
@terminate = true
|
115
|
+
@runners_lock.synchronize{
|
116
|
+
@location_runners.each { |runner|
|
117
|
+
runner.terminate
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
# Run a location using this runner. If the location
|
123
|
+
# is already being run by this runner, do nothing.
|
124
|
+
# A new thread will be launched to run the actual move cycle
|
125
|
+
def run(location)
|
126
|
+
@runners_lock.synchronize{
|
127
|
+
unless @location_runners.find { |l| l.location.id == location.id}
|
128
|
+
@location_runners.push LocationRunner.new(location)
|
129
|
+
end
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end # module motel
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# $Id: semaphore.rb,v 1.2 2003/03/15 20:10:10 fukumoto Exp $
|
3
|
+
#
|
4
|
+
# Copied unmodified from:
|
5
|
+
# http://www.imasy.or.jp/~fukumoto/ruby/semaphore.rb
|
6
|
+
# Originally licensed under The Ruby License:
|
7
|
+
# http://raa.ruby-lang.org/project/semaphore/
|
8
|
+
|
9
|
+
class CountingSemaphore
|
10
|
+
|
11
|
+
def initialize(initvalue = 0)
|
12
|
+
@counter = initvalue
|
13
|
+
@waiting_list = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def wait
|
17
|
+
Thread.critical = true
|
18
|
+
if (@counter -= 1) < 0
|
19
|
+
@waiting_list.push(Thread.current)
|
20
|
+
Thread.stop
|
21
|
+
end
|
22
|
+
self
|
23
|
+
ensure
|
24
|
+
Thread.critical = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def signal
|
28
|
+
Thread.critical = true
|
29
|
+
begin
|
30
|
+
if (@counter += 1) <= 0
|
31
|
+
t = @waiting_list.shift
|
32
|
+
t.wakeup if t
|
33
|
+
end
|
34
|
+
rescue ThreadError
|
35
|
+
retry
|
36
|
+
end
|
37
|
+
self
|
38
|
+
ensure
|
39
|
+
Thread.critical = false
|
40
|
+
end
|
41
|
+
|
42
|
+
alias down wait
|
43
|
+
alias up signal
|
44
|
+
alias P wait
|
45
|
+
alias V signal
|
46
|
+
|
47
|
+
def exclusive
|
48
|
+
wait
|
49
|
+
yield
|
50
|
+
ensure
|
51
|
+
signal
|
52
|
+
end
|
53
|
+
|
54
|
+
alias synchronize exclusive
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
Semaphore = CountingSemaphore
|
data/lib/motel.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# include all motel modules
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# See COPYING for the License of this software
|
5
|
+
|
6
|
+
lib = File.dirname(__FILE__)
|
7
|
+
|
8
|
+
require lib + '/motel/conf'
|
9
|
+
require lib + '/motel/runner'
|
10
|
+
require lib + '/motel/loader'
|
11
|
+
require lib + '/motel/simrpc'
|
12
|
+
|
13
|
+
Dir[lib + '/motel/models/*.rb'].each { |model| require model }
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.2"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mohammed Morsi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-04 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.1.1
|
24
|
+
version:
|
25
|
+
description: Motel is a library to track and move the locations of objects in a 3D environment.
|
26
|
+
email: movitto@yahoo.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
files:
|
34
|
+
- conf/amqp.yml
|
35
|
+
- conf/database.yml
|
36
|
+
- db/migrate/001_create_locations_and_movement_strategies.rb
|
37
|
+
- db/migrate/002_create_linear_movement_strategy.rb
|
38
|
+
- db/migrate/003_create_elliptical_movement_strategy.rb
|
39
|
+
- lib/motel/models/location.rb
|
40
|
+
- lib/motel/models/movement_strategy.rb
|
41
|
+
- lib/motel/models/stopped.rb
|
42
|
+
- lib/motel/models/linear.rb
|
43
|
+
- lib/motel/models/elliptical.rb
|
44
|
+
- lib/motel/common.rb
|
45
|
+
- lib/motel/loader.rb
|
46
|
+
- lib/motel/runner.rb
|
47
|
+
- lib/motel/semaphore.rb
|
48
|
+
- lib/motel.rb
|
49
|
+
- LICENSE
|
50
|
+
- COPYING
|
51
|
+
- README
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://morsi.org/projects/motel
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --inline-source
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.3.1
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.3.5
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Motel is a library to track and move the locations of objects in a 3D environment.
|
81
|
+
test_files: []
|
82
|
+
|