automate-em 0.0.1
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/LGPL3-LICENSE +165 -0
- data/README.textile +48 -0
- data/Rakefile +40 -0
- data/app/models/control_system.rb +20 -0
- data/app/models/controller_device.rb +21 -0
- data/app/models/controller_http_service.rb +17 -0
- data/app/models/controller_logic.rb +5 -0
- data/app/models/controller_zone.rb +10 -0
- data/app/models/dependency.rb +20 -0
- data/app/models/server.rb +12 -0
- data/app/models/setting.rb +38 -0
- data/app/models/trusted_device.rb +63 -0
- data/app/models/user_zone.rb +10 -0
- data/app/models/zone.rb +16 -0
- data/db/migrate/20111001022500_init.rb +147 -0
- data/db/migrate/20111017213801_one_time_key.rb +9 -0
- data/db/migrate/20111021071632_encrypt_setting.rb +9 -0
- data/db/migrate/20111110075444_servers.rb +15 -0
- data/db/migrate/20111114074538_default_port.rb +9 -0
- data/db/migrate/20111122073055_makebreak.rb +9 -0
- data/db/migrate/20111211062846_create_controller_http_services.rb +18 -0
- data/lib/automate-em.rb +155 -0
- data/lib/automate-em/constants.rb +6 -0
- data/lib/automate-em/core/communicator.rb +318 -0
- data/lib/automate-em/core/modules.rb +373 -0
- data/lib/automate-em/core/resolver_pool.rb +76 -0
- data/lib/automate-em/core/system.rb +356 -0
- data/lib/automate-em/device/datagram_server.rb +111 -0
- data/lib/automate-em/device/device.rb +140 -0
- data/lib/automate-em/device/device_connection.rb +689 -0
- data/lib/automate-em/device/tcp_control.rb +210 -0
- data/lib/automate-em/engine.rb +36 -0
- data/lib/automate-em/interfaces/OLD CODE/deferred.rb +67 -0
- data/lib/automate-em/interfaces/OLD CODE/telnet/ansi.rb +137 -0
- data/lib/automate-em/interfaces/OLD CODE/telnet/telnet.rb +137 -0
- data/lib/automate-em/interfaces/html5.rb +302 -0
- data/lib/automate-em/logic/logic.rb +76 -0
- data/lib/automate-em/service/http_service.rb +584 -0
- data/lib/automate-em/service/service.rb +48 -0
- data/lib/automate-em/status.rb +89 -0
- data/lib/automate-em/utilities.rb +195 -0
- data/lib/automate-em/version.rb +3 -0
- data/lib/generators/module/USAGE +8 -0
- data/lib/generators/module/module_generator.rb +47 -0
- data/lib/tasks/automate-em_tasks.rake +5 -0
- data/test/automate-em_test.rb +7 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +56 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +15 -0
- metadata +328 -0
data/app/models/zone.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Zone < ActiveRecord::Base
|
2
|
+
has_many :user_zones
|
3
|
+
has_many :controller_zones
|
4
|
+
|
5
|
+
has_many :groups, :through => :user_zones
|
6
|
+
has_many :control_systems, :through => :controller_zones
|
7
|
+
|
8
|
+
has_many :settings, :as => :object, :dependent => :destroy
|
9
|
+
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
|
14
|
+
validates_presence_of :name
|
15
|
+
validates_uniqueness_of :name
|
16
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
class Init < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
#
|
4
|
+
# These represent individual systems
|
5
|
+
#
|
6
|
+
create_table :control_systems do |t|
|
7
|
+
t.string :name, :allow_null => false
|
8
|
+
t.text :description
|
9
|
+
t.boolean :active, :default => true, :allow_null => false
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Describe modules that can be loaded and exist on the system
|
16
|
+
#
|
17
|
+
create_table :dependencies do |t|
|
18
|
+
t.references :dependency
|
19
|
+
|
20
|
+
t.string :classname, :allow_null => false
|
21
|
+
t.string :filename, :allow_null => false
|
22
|
+
|
23
|
+
t.string :module_name, :allow_null => false # Type name (Projector)
|
24
|
+
t.string :actual_name, :allow_null => false # Real name (All NEC Projectors)
|
25
|
+
t.text :description
|
26
|
+
|
27
|
+
t.datetime :version_loaded
|
28
|
+
|
29
|
+
t.timestamps
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Device and Logic instances
|
34
|
+
# => Priority provides load order (high priorities load first)
|
35
|
+
#
|
36
|
+
create_table :controller_devices do |t|
|
37
|
+
t.references :control_system,:allow_null => false
|
38
|
+
t.references :dependency, :allow_null => false
|
39
|
+
|
40
|
+
t.string :ip, :allow_null => false
|
41
|
+
t.integer :port,:allow_null => false
|
42
|
+
|
43
|
+
t.boolean :tls, :default => false, :allow_null => false
|
44
|
+
t.boolean :udp, :default => false, :allow_null => false
|
45
|
+
|
46
|
+
t.integer :priority, :default => 0
|
47
|
+
|
48
|
+
t.string :custom_name # projector_left
|
49
|
+
|
50
|
+
t.timestamps
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table :controller_logics do |t|
|
54
|
+
t.references :control_system,:allow_null => false
|
55
|
+
t.references :dependency, :allow_null => false
|
56
|
+
|
57
|
+
t.integer :priority, :default => 0
|
58
|
+
|
59
|
+
t.string :custom_name
|
60
|
+
|
61
|
+
t.timestamps
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Settings relating to Dependencies, Devices instances, Interfaces and Interface Instances
|
66
|
+
#
|
67
|
+
create_table :settings do |t|
|
68
|
+
t.references :object, :polymorphic => true
|
69
|
+
|
70
|
+
t.string :name, :allow_null => false
|
71
|
+
t.text :description
|
72
|
+
|
73
|
+
t.integer :value_type, :allow_null => false
|
74
|
+
|
75
|
+
t.float :float_value
|
76
|
+
t.integer :integer_value # doubles as boolean (type -1)
|
77
|
+
t.text :text_value
|
78
|
+
t.datetime :datetime_value
|
79
|
+
|
80
|
+
t.timestamps
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Describes the interfaces avaliable
|
85
|
+
#
|
86
|
+
#create_table :guis do |t|
|
87
|
+
# t.string :name, :allow_null => false
|
88
|
+
# t.text :description
|
89
|
+
|
90
|
+
# t.datetime :version # Cache update indicator
|
91
|
+
# t.text :render_path # Path of file to render
|
92
|
+
|
93
|
+
# Location information here for
|
94
|
+
# access if the device is local
|
95
|
+
|
96
|
+
# t.timestamps
|
97
|
+
#end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Zones can be used to classify the various control systems
|
101
|
+
# => General Type (meeting room, seminar room)
|
102
|
+
# => Floor, Building, Campus
|
103
|
+
# This allows fine grained access control for users
|
104
|
+
#
|
105
|
+
create_table :zones do |t|
|
106
|
+
t.string :name, :allow_null => false
|
107
|
+
t.text :description
|
108
|
+
|
109
|
+
t.timestamps
|
110
|
+
end
|
111
|
+
|
112
|
+
create_table :user_zones do |t|
|
113
|
+
t.references :group
|
114
|
+
t.references :zone
|
115
|
+
|
116
|
+
t.integer :privilege_map # if not null overrides user default privilege map (user zones OR'ed)
|
117
|
+
|
118
|
+
t.timestamps
|
119
|
+
end
|
120
|
+
|
121
|
+
create_table :controller_zones do |t|
|
122
|
+
t.references :control_system
|
123
|
+
t.references :zone
|
124
|
+
|
125
|
+
t.timestamps
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Trusted devices
|
130
|
+
# => System 0 cannot trust any interface as does not have an explicit controller id
|
131
|
+
#
|
132
|
+
create_table :trusted_devices do |t|
|
133
|
+
t.references :user, :allow_null => false
|
134
|
+
t.references :control_system,:allow_null => false
|
135
|
+
|
136
|
+
t.string :description, :allow_null => false
|
137
|
+
t.text :notes
|
138
|
+
|
139
|
+
t.string :one_time_key
|
140
|
+
t.string :next_key
|
141
|
+
t.datetime :expires # Expire devices (staff member leaving etc)
|
142
|
+
t.datetime :last_authenticated # Cache update indicator
|
143
|
+
|
144
|
+
t.timestamps
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Servers < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :servers do |t|
|
4
|
+
t.boolean :online, :allow_null => false, :default => true
|
5
|
+
|
6
|
+
t.string :hostname, :allow_null => false
|
7
|
+
|
8
|
+
t.text :notes
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def down
|
13
|
+
#drop table
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateControllerHttpServices < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :controller_http_services do |t|
|
4
|
+
|
5
|
+
t.references :control_system,:allow_null => false
|
6
|
+
t.references :dependency, :allow_null => false
|
7
|
+
|
8
|
+
t.string :uri, :allow_null => false
|
9
|
+
|
10
|
+
t.integer :priority, :default => 0, :allow_null => false
|
11
|
+
t.string :custom_name # projector_left
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
add_column :dependencies, :default_uri, :string
|
17
|
+
end
|
18
|
+
end
|
data/lib/automate-em.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require "automate-em/engine"
|
2
|
+
|
3
|
+
#
|
4
|
+
# TODO:: Use autoload here to avoid loading these unless control is running!
|
5
|
+
#
|
6
|
+
|
7
|
+
#
|
8
|
+
# STD LIB
|
9
|
+
#
|
10
|
+
require 'observer'
|
11
|
+
require 'yaml'
|
12
|
+
require 'thread'
|
13
|
+
require 'monitor'
|
14
|
+
require 'Socket' # for DNS lookups
|
15
|
+
require 'Logger'
|
16
|
+
|
17
|
+
|
18
|
+
#
|
19
|
+
# Gems
|
20
|
+
#
|
21
|
+
require 'rubygems'
|
22
|
+
require 'eventmachine'
|
23
|
+
require 'em-priority-queue'
|
24
|
+
require 'em-http'
|
25
|
+
require 'rufus/scheduler'
|
26
|
+
require 'ipaddress'
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# Library Files
|
31
|
+
#
|
32
|
+
require 'automate-em/constants.rb'
|
33
|
+
require 'automate-em/utilities.rb'
|
34
|
+
require 'automate-em/status.rb'
|
35
|
+
|
36
|
+
require 'automate-em/core/resolver_pool.rb'
|
37
|
+
require 'automate-em/core/modules.rb'
|
38
|
+
require 'automate-em/core/communicator.rb'
|
39
|
+
require 'automate-em/core/system.rb'
|
40
|
+
|
41
|
+
require 'automate-em/device/device.rb'
|
42
|
+
require 'automate-em/device/device_connection.rb'
|
43
|
+
require 'automate-em/device/datagram_server.rb'
|
44
|
+
require 'automate-em/device/tcp_control.rb'
|
45
|
+
|
46
|
+
require 'automate-em/service/service.rb'
|
47
|
+
require 'automate-em/service/http_service.rb'
|
48
|
+
|
49
|
+
require 'automate-em/logic/logic.rb'
|
50
|
+
|
51
|
+
require 'automate-em/interfaces/html5.rb'
|
52
|
+
|
53
|
+
|
54
|
+
module AutomateEm
|
55
|
+
|
56
|
+
|
57
|
+
def self.load_paths= (paths)
|
58
|
+
@@load_paths = ([] << paths) # TODO:: this doesn't work
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def self.scheduler
|
63
|
+
@@scheduler
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def self.resolver
|
68
|
+
@@resolver
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def self.get_log_level(level)
|
73
|
+
if level.nil?
|
74
|
+
return Logger::INFO
|
75
|
+
else
|
76
|
+
return case level.downcase.to_sym
|
77
|
+
when :debug
|
78
|
+
Logger::DEBUG
|
79
|
+
when :warn
|
80
|
+
Logger::WARN
|
81
|
+
when :error
|
82
|
+
Logger::ERROR
|
83
|
+
else
|
84
|
+
Logger::INFO
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def self.boot
|
91
|
+
|
92
|
+
#
|
93
|
+
# System level logger
|
94
|
+
#
|
95
|
+
if Rails.env.production?
|
96
|
+
System.logger = Logger.new(Rails.root.join('log/system.log').to_s, 10, 4194304)
|
97
|
+
else
|
98
|
+
System.logger = Logger.new(STDOUT)
|
99
|
+
end
|
100
|
+
System.logger.formatter = proc { |severity, datetime, progname, msg|
|
101
|
+
"#{datetime.strftime("%d/%m/%Y @ %I:%M%p")} #{severity}: #{System} - #{msg}\n"
|
102
|
+
}
|
103
|
+
|
104
|
+
@@resolver = ResolverPool.new
|
105
|
+
|
106
|
+
EventMachine.run do
|
107
|
+
#
|
108
|
+
# Enable the scheduling system
|
109
|
+
#
|
110
|
+
@@scheduler = Rufus::Scheduler.start_new
|
111
|
+
|
112
|
+
EM.defer do
|
113
|
+
System.logger.debug "Started with #{EM.get_max_timers} timers avaliable"
|
114
|
+
System.logger.debug "Started with #{EM.threadpool_size} threads in pool"
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Start the UDP server
|
119
|
+
#
|
120
|
+
EM.open_datagram_socket "0.0.0.0", 0, DatagramServer
|
121
|
+
|
122
|
+
#
|
123
|
+
# Load the system based on the database
|
124
|
+
#
|
125
|
+
ControlSystem.all.each do |controller|
|
126
|
+
EM.defer do
|
127
|
+
begin
|
128
|
+
System.logger.debug "Booting #{controller.name}"
|
129
|
+
System.new_system(controller, Rails.configuration.automate.log_level)
|
130
|
+
rescue => e
|
131
|
+
AutomateEm.print_error(AutomateEm::System.logger, e, {
|
132
|
+
:message => "Error during boot",
|
133
|
+
:level => Logger::FATAL
|
134
|
+
})
|
135
|
+
EventMachine::stop_event_loop
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Emit connection counts for logging
|
142
|
+
#
|
143
|
+
@@scheduler.every '10m' do
|
144
|
+
System.logger.info "There are #{EM.connection_count} connections to this server"
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# We should AutoLoad the interfaces as plugins rather than having them in the core
|
149
|
+
#
|
150
|
+
EM.add_timer(20) do
|
151
|
+
System.start_websockets
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
#
|
2
|
+
# There should be one communicator per system that handles the interface interfaces
|
3
|
+
# This will pass on registered status requirements and call functions as requested
|
4
|
+
#
|
5
|
+
# Interfaces will have to have a base class that abstracts the event machine code so that
|
6
|
+
# nothing runs on the reactor thread.
|
7
|
+
#
|
8
|
+
module AutomateEm
|
9
|
+
class Communicator
|
10
|
+
|
11
|
+
def initialize(system)
|
12
|
+
@system = system
|
13
|
+
|
14
|
+
@command_lock = Mutex.new
|
15
|
+
@status_lock = Mutex.new
|
16
|
+
|
17
|
+
@status_register = {}
|
18
|
+
@connected_interfaces = {}
|
19
|
+
|
20
|
+
@shutdown = true
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@system.logger
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def shutdown
|
30
|
+
@status_lock.synchronize {
|
31
|
+
@shutdown = true
|
32
|
+
@system.modules.each_value do |mod|
|
33
|
+
mod.instance.delete_observer(self)
|
34
|
+
end
|
35
|
+
@status_register = {}
|
36
|
+
@connected_interfaces.each_key do |soc|
|
37
|
+
soc.shutdown
|
38
|
+
end
|
39
|
+
}
|
40
|
+
logger.debug "-- Communicator shutdown"
|
41
|
+
end
|
42
|
+
|
43
|
+
def start(nolog = false) # Logging isn't active for the very first of these
|
44
|
+
@status_lock.synchronize {
|
45
|
+
@shutdown = false
|
46
|
+
}
|
47
|
+
logger.debug "-- Communicator started" unless nolog
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
#
|
52
|
+
# Systems avaliable to this user
|
53
|
+
#
|
54
|
+
def self.system_list(user)
|
55
|
+
response = {:ids => [], :names => []}
|
56
|
+
|
57
|
+
if user.class == User
|
58
|
+
user.control_systems.select('control_systems.id, control_systems.name').each do |controller|
|
59
|
+
if !!System[controller.name.to_sym]
|
60
|
+
response[:ids] << controller.id
|
61
|
+
response[:names] << controller.name
|
62
|
+
end
|
63
|
+
end # We ignore token requests here as they should know the system they can connect to
|
64
|
+
end
|
65
|
+
return response
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Set the system to communicate with
|
70
|
+
# Up to interfaces to maintain stability here (They should deal with errors)
|
71
|
+
#
|
72
|
+
def self.select(user, interface, system)
|
73
|
+
System.logger.debug "-- Interface #{interface.class} attempting to select system #{system}"
|
74
|
+
if system == 0
|
75
|
+
return nil unless user[:system_admin]
|
76
|
+
System.communicator.attach(interface)
|
77
|
+
else
|
78
|
+
sys = nil
|
79
|
+
|
80
|
+
if user.class == User
|
81
|
+
sys = user.control_systems.select('control_systems.name').where('control_systems.id = ? AND control_systems.active = ?', system.to_i, true).first
|
82
|
+
|
83
|
+
elsif user.class == TrustedDevice && user.control_system_id == system.to_i
|
84
|
+
sys = User.find(user.user_id).control_systems.select('control_systems.name, control_systems.active').where('control_systems.id = ?', system.to_i).first
|
85
|
+
if sys.nil?
|
86
|
+
#
|
87
|
+
# Kill comms, this key is not valid
|
88
|
+
# Invalidate key
|
89
|
+
#
|
90
|
+
user.expires = Time.now
|
91
|
+
user.save
|
92
|
+
interface.shutdown
|
93
|
+
return nil
|
94
|
+
elsif sys.active == false
|
95
|
+
#
|
96
|
+
# System offline... Disconnect
|
97
|
+
#
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
system = sys.nil? ? nil : sys.name.to_sym
|
103
|
+
if System[system].nil?
|
104
|
+
interface.shutdown #kill comms
|
105
|
+
return nil
|
106
|
+
end
|
107
|
+
|
108
|
+
System.logger.debug "-- Interface #{interface.class} selected system #{system}"
|
109
|
+
return System[system].communicator.attach(interface)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
#
|
117
|
+
# Keep track of connected systems
|
118
|
+
#
|
119
|
+
def disconnected(interface)
|
120
|
+
@status_lock.synchronize {
|
121
|
+
status_array = @connected_interfaces.delete(interface)
|
122
|
+
status_array.each do |status_hash|
|
123
|
+
status_hash.delete(interface)
|
124
|
+
end
|
125
|
+
#
|
126
|
+
# TODO::Refactor required
|
127
|
+
# This still isn't perfect as we could be observing modules we are not using...
|
128
|
+
#
|
129
|
+
}
|
130
|
+
logger.debug "-- Interface #{interface.class} disconnected" unless logger.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
#
|
135
|
+
# Keep track of status events
|
136
|
+
#
|
137
|
+
def register(interface, mod, status, &block)
|
138
|
+
mod_sym = mod.class == String ? mod.to_sym : mod # remember the symbol used by the interface to reference this module
|
139
|
+
status = status.to_sym if status.class == String
|
140
|
+
|
141
|
+
if @system.modules[mod_sym].present?
|
142
|
+
mod = @system.modules[mod_sym].instance # most efficient
|
143
|
+
|
144
|
+
theVal = nil
|
145
|
+
@status_lock.synchronize {
|
146
|
+
@status_register[mod] ||= {}
|
147
|
+
@status_register[mod][status] ||= {}
|
148
|
+
@status_register[mod][status][interface] = mod_sym
|
149
|
+
@connected_interfaces[interface] << @status_register[mod][status] unless @connected_interfaces[interface].nil?
|
150
|
+
theVal = mod[status]
|
151
|
+
}
|
152
|
+
|
153
|
+
mod.add_observer(self)
|
154
|
+
logger.debug "-- Interface #{interface.class} registered #{mod_sym}:#{status}"
|
155
|
+
|
156
|
+
#
|
157
|
+
# Send the status to this requestor!
|
158
|
+
# This is the same as in update
|
159
|
+
#
|
160
|
+
if !theVal.nil?
|
161
|
+
begin
|
162
|
+
function = "#{mod_sym.to_s.downcase}_#{status}_changed".to_sym
|
163
|
+
|
164
|
+
if interface.respond_to?(function)
|
165
|
+
interface.__send__(function, theVal)
|
166
|
+
else
|
167
|
+
interface.notify(mod_sym, status, theVal)
|
168
|
+
end
|
169
|
+
rescue => e
|
170
|
+
AutomateEm.print_error(logger, e, {
|
171
|
+
:message => "in communicator.rb, register : bad interface or user module code",
|
172
|
+
:level => Logger::ERROR
|
173
|
+
})
|
174
|
+
end
|
175
|
+
end
|
176
|
+
else
|
177
|
+
logger.warn "in communicator.rb, register : #{interface.class} called register on a bad module name"
|
178
|
+
block.call() unless block.nil? # Block will inform of any errors
|
179
|
+
end
|
180
|
+
rescue => e
|
181
|
+
begin
|
182
|
+
AutomateEm.print_error(logger, e, {
|
183
|
+
:message => "in communicator.rb, register : #{interface.class} failed to register #{mod.inspect}.#{status.inspect}",
|
184
|
+
:level => Logger::ERROR
|
185
|
+
})
|
186
|
+
block.call() unless block.nil? # Block will inform of any errors
|
187
|
+
rescue => x
|
188
|
+
AutomateEm.print_error(logger, x, {
|
189
|
+
:message => "in communicator.rb, register : #{interface.class} provided a bad block",
|
190
|
+
:level => Logger::WARN
|
191
|
+
})
|
192
|
+
end
|
193
|
+
ensure
|
194
|
+
ActiveRecord::Base.clear_active_connections!
|
195
|
+
end
|
196
|
+
|
197
|
+
def unregister(interface, mod, status, &block)
|
198
|
+
mod_sym = mod.to_sym if mod.class == String
|
199
|
+
status = status.to_sym if status.class == String
|
200
|
+
|
201
|
+
mod = @system.modules[mod_sym].instance
|
202
|
+
logger.debug "Interface #{interface.class} unregistered #{mod_sym}:#{status}"
|
203
|
+
|
204
|
+
@status_lock.synchronize {
|
205
|
+
@status_register[mod] ||= {}
|
206
|
+
@status_register[mod][status] ||= {}
|
207
|
+
@status_register[mod][status].delete(interface)
|
208
|
+
@connected_interfaces[interface].delete(@status_register[mod][status]) unless @connected_interfaces[interface].nil?
|
209
|
+
|
210
|
+
#
|
211
|
+
# TODO:: deleteing the observer will delete all status updates
|
212
|
+
# This needs to be more selective.
|
213
|
+
# We only delete the observer if all the mod[status]'s are empty
|
214
|
+
#
|
215
|
+
#if @status_register[mod][status].empty?
|
216
|
+
# mod.delete_observer(self)
|
217
|
+
#end
|
218
|
+
}
|
219
|
+
rescue => e
|
220
|
+
logger.warn "in communicator.rb, unregister : #{interface.class} called unregister when it was not needed"
|
221
|
+
#logger.warn e.message
|
222
|
+
#logger.warn e.backtrace
|
223
|
+
begin
|
224
|
+
block.call() if !block.nil? # Block will inform of any errors
|
225
|
+
rescue => x
|
226
|
+
AutomateEm.print_error(logger, x, {
|
227
|
+
:message => "in communicator.rb, unregister : #{interface.class} provided a bad block",
|
228
|
+
:level => Logger::WARN
|
229
|
+
})
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def update(mod, status, data)
|
234
|
+
@status_lock.synchronize {
|
235
|
+
return if @status_register[mod].nil? || @status_register[mod][status].nil?
|
236
|
+
|
237
|
+
#
|
238
|
+
# Interfaces should implement the notify function
|
239
|
+
# Or a function for that particular event
|
240
|
+
#
|
241
|
+
@status_register[mod][status].each_pair do |interface, mod|
|
242
|
+
EM.defer do # to avoid deadlock
|
243
|
+
begin
|
244
|
+
function = "#{mod.to_s.downcase}_#{status}_changed".to_sym
|
245
|
+
if interface.respond_to?(function) # Can provide a function to deal with status updates
|
246
|
+
interface.__send__(function, data)
|
247
|
+
else
|
248
|
+
interface.notify(mod, status, data)
|
249
|
+
end
|
250
|
+
rescue => e
|
251
|
+
AutomateEm.print_error(logger, e, {
|
252
|
+
:message => "in communicator.rb, update : bad interface or user module code",
|
253
|
+
:level => Logger::ERROR
|
254
|
+
})
|
255
|
+
ensure
|
256
|
+
ActiveRecord::Base.clear_active_connections!
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
}
|
261
|
+
rescue => e
|
262
|
+
AutomateEm.print_error(logger, e, {
|
263
|
+
:message => "in communicator.rb, update : This should never happen",
|
264
|
+
:level => Logger::ERROR
|
265
|
+
})
|
266
|
+
end
|
267
|
+
|
268
|
+
#
|
269
|
+
# Pass commands to the selected system
|
270
|
+
#
|
271
|
+
def send_command(mod, command, *args, &block)
|
272
|
+
#
|
273
|
+
# Accept String, argument array
|
274
|
+
# String
|
275
|
+
#
|
276
|
+
mod = mod.to_sym if mod.class == String
|
277
|
+
logger.debug "-- Command requested #{mod}.#{command}(#{args})"
|
278
|
+
|
279
|
+
begin
|
280
|
+
@command_lock.synchronize {
|
281
|
+
@system.modules[mod].instance.public_send(command, *args) # Not send string however call function command
|
282
|
+
}
|
283
|
+
rescue => e
|
284
|
+
AutomateEm.print_error(logger, e, {
|
285
|
+
:message => "module #{mod} in communicator.rb, send_command : command unavaliable or bad module code",
|
286
|
+
:level => Logger::WARN
|
287
|
+
})
|
288
|
+
begin
|
289
|
+
block.call() unless block.nil? # Block will inform of any errors
|
290
|
+
rescue => x
|
291
|
+
AutomateEm.print_error(logger, x, {
|
292
|
+
:message => "in communicator.rb, send_command : interface provided bad block",
|
293
|
+
:level => Logger::ERROR
|
294
|
+
})
|
295
|
+
end
|
296
|
+
ensure
|
297
|
+
ActiveRecord::Base.clear_active_connections!
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
def attach(interface)
|
303
|
+
@status_lock.synchronize {
|
304
|
+
return nil if @shutdown || interface.nil?
|
305
|
+
@connected_interfaces[interface] = [] unless @connected_interfaces.include?(interface)
|
306
|
+
}
|
307
|
+
return self
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
protected
|
312
|
+
|
313
|
+
|
314
|
+
def unregister_all(interface)
|
315
|
+
# TODO:: Important to stop memory leaks
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|