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/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
|
+
|