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.
Files changed (77) hide show
  1. data/LGPL3-LICENSE +165 -0
  2. data/README.textile +48 -0
  3. data/Rakefile +40 -0
  4. data/app/models/control_system.rb +20 -0
  5. data/app/models/controller_device.rb +21 -0
  6. data/app/models/controller_http_service.rb +17 -0
  7. data/app/models/controller_logic.rb +5 -0
  8. data/app/models/controller_zone.rb +10 -0
  9. data/app/models/dependency.rb +20 -0
  10. data/app/models/server.rb +12 -0
  11. data/app/models/setting.rb +38 -0
  12. data/app/models/trusted_device.rb +63 -0
  13. data/app/models/user_zone.rb +10 -0
  14. data/app/models/zone.rb +16 -0
  15. data/db/migrate/20111001022500_init.rb +147 -0
  16. data/db/migrate/20111017213801_one_time_key.rb +9 -0
  17. data/db/migrate/20111021071632_encrypt_setting.rb +9 -0
  18. data/db/migrate/20111110075444_servers.rb +15 -0
  19. data/db/migrate/20111114074538_default_port.rb +9 -0
  20. data/db/migrate/20111122073055_makebreak.rb +9 -0
  21. data/db/migrate/20111211062846_create_controller_http_services.rb +18 -0
  22. data/lib/automate-em.rb +155 -0
  23. data/lib/automate-em/constants.rb +6 -0
  24. data/lib/automate-em/core/communicator.rb +318 -0
  25. data/lib/automate-em/core/modules.rb +373 -0
  26. data/lib/automate-em/core/resolver_pool.rb +76 -0
  27. data/lib/automate-em/core/system.rb +356 -0
  28. data/lib/automate-em/device/datagram_server.rb +111 -0
  29. data/lib/automate-em/device/device.rb +140 -0
  30. data/lib/automate-em/device/device_connection.rb +689 -0
  31. data/lib/automate-em/device/tcp_control.rb +210 -0
  32. data/lib/automate-em/engine.rb +36 -0
  33. data/lib/automate-em/interfaces/OLD CODE/deferred.rb +67 -0
  34. data/lib/automate-em/interfaces/OLD CODE/telnet/ansi.rb +137 -0
  35. data/lib/automate-em/interfaces/OLD CODE/telnet/telnet.rb +137 -0
  36. data/lib/automate-em/interfaces/html5.rb +302 -0
  37. data/lib/automate-em/logic/logic.rb +76 -0
  38. data/lib/automate-em/service/http_service.rb +584 -0
  39. data/lib/automate-em/service/service.rb +48 -0
  40. data/lib/automate-em/status.rb +89 -0
  41. data/lib/automate-em/utilities.rb +195 -0
  42. data/lib/automate-em/version.rb +3 -0
  43. data/lib/generators/module/USAGE +8 -0
  44. data/lib/generators/module/module_generator.rb +47 -0
  45. data/lib/tasks/automate-em_tasks.rake +5 -0
  46. data/test/automate-em_test.rb +7 -0
  47. data/test/dummy/README.rdoc +261 -0
  48. data/test/dummy/Rakefile +7 -0
  49. data/test/dummy/app/assets/javascripts/application.js +15 -0
  50. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  51. data/test/dummy/app/controllers/application_controller.rb +3 -0
  52. data/test/dummy/app/helpers/application_helper.rb +2 -0
  53. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  54. data/test/dummy/config.ru +4 -0
  55. data/test/dummy/config/application.rb +56 -0
  56. data/test/dummy/config/boot.rb +10 -0
  57. data/test/dummy/config/database.yml +25 -0
  58. data/test/dummy/config/environment.rb +5 -0
  59. data/test/dummy/config/environments/development.rb +37 -0
  60. data/test/dummy/config/environments/production.rb +67 -0
  61. data/test/dummy/config/environments/test.rb +37 -0
  62. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  63. data/test/dummy/config/initializers/inflections.rb +15 -0
  64. data/test/dummy/config/initializers/mime_types.rb +5 -0
  65. data/test/dummy/config/initializers/secret_token.rb +7 -0
  66. data/test/dummy/config/initializers/session_store.rb +8 -0
  67. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  68. data/test/dummy/config/locales/en.yml +5 -0
  69. data/test/dummy/config/routes.rb +4 -0
  70. data/test/dummy/public/404.html +26 -0
  71. data/test/dummy/public/422.html +26 -0
  72. data/test/dummy/public/500.html +25 -0
  73. data/test/dummy/public/favicon.ico +0 -0
  74. data/test/dummy/script/rails +6 -0
  75. data/test/integration/navigation_test.rb +10 -0
  76. data/test/test_helper.rb +15 -0
  77. metadata +328 -0
@@ -0,0 +1,10 @@
1
+ class UserZone < ActiveRecord::Base
2
+ belongs_to :zone
3
+ belongs_to :group
4
+
5
+
6
+ protected
7
+
8
+
9
+ validates_presence_of :group, :zone
10
+ end
@@ -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,9 @@
1
+ class OneTimeKey < ActiveRecord::Migration
2
+ def up
3
+ rename_column :trusted_devices, :description, :reason
4
+ end
5
+
6
+ def down
7
+ rename_column :trusted_devices, :reason, :description
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class EncryptSetting < ActiveRecord::Migration
2
+ def up
3
+ add_column :settings, :encrypt_setting, :boolean, :default => false
4
+ end
5
+
6
+ def down
7
+ remove_column :settings, :encrypt_setting
8
+ end
9
+ 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,9 @@
1
+ class DefaultPort < ActiveRecord::Migration
2
+ def up
3
+ add_column :dependencies, :default_port, :integer
4
+ end
5
+
6
+ def down
7
+ remove_column :dependencies, :default_port
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Makebreak < ActiveRecord::Migration
2
+ def up
3
+ add_column :controller_devices, :makebreak, :boolean, :default => false, :allow_null => false
4
+ end
5
+
6
+ def down
7
+ remove_column :controller_devices, :makebreak
8
+ end
9
+ 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
@@ -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,6 @@
1
+ module AutomateEm
2
+ module Constants
3
+ On = true
4
+ Off = false
5
+ end
6
+ 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