automate-em 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,373 @@
|
|
1
|
+
module AutomateEm
|
2
|
+
class Modules
|
3
|
+
@@modules = {} # modules (dependency_id => module class)
|
4
|
+
@@load_lock = Object.new.extend(MonitorMixin) #Mutex.new
|
5
|
+
@@loading = nil
|
6
|
+
|
7
|
+
def self.[] (dep_id)
|
8
|
+
@@load_lock.mon_synchronize {
|
9
|
+
@@modules[dep_id]
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.load_module(dep)
|
14
|
+
@@load_lock.mon_synchronize {
|
15
|
+
begin
|
16
|
+
found = false
|
17
|
+
|
18
|
+
Rails.configuration.automate.module_paths.each do |path|
|
19
|
+
if File.exists?("#{path}/#{dep.filename}")
|
20
|
+
load "#{path}/#{dep.filename}"
|
21
|
+
found = true
|
22
|
+
break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if not found
|
27
|
+
raise "File not found!"
|
28
|
+
end
|
29
|
+
|
30
|
+
@@modules[dep.id] = dep.classname.classify.constantize
|
31
|
+
rescue => e
|
32
|
+
AutomateEm.print_error(System.logger, e, {
|
33
|
+
:message => "device module #{dep.actual_name} error whilst loading",
|
34
|
+
:level => Logger::ERROR
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# TODO:: Consider allowing different dependancies use the same connection
|
44
|
+
# Means only the first will call received - others must use recieve blocks
|
45
|
+
#
|
46
|
+
class DeviceModule
|
47
|
+
@@instances = {} # db id => @instance
|
48
|
+
@@dbentry = {} # db id => db instance
|
49
|
+
@@devices = {} # ip:port:udp => @instance
|
50
|
+
@@lookup = {} # @instance => db id array
|
51
|
+
@@lookup_lock = Mutex.new
|
52
|
+
|
53
|
+
def initialize(system, controllerDevice)
|
54
|
+
@@lookup_lock.synchronize {
|
55
|
+
if @@instances[controllerDevice.id].nil?
|
56
|
+
@system = system
|
57
|
+
@device = controllerDevice.id
|
58
|
+
@@dbentry[controllerDevice.id] = controllerDevice
|
59
|
+
end
|
60
|
+
}
|
61
|
+
instantiate_module(controllerDevice)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def unload # should never be called on the reactor thread so no need to defer
|
66
|
+
|
67
|
+
@instance.base.shutdown(@system)
|
68
|
+
|
69
|
+
@@lookup_lock.synchronize {
|
70
|
+
db = @@lookup[@instance].delete(@device)
|
71
|
+
@@instances.delete(db)
|
72
|
+
db = @@dbentry.delete(db)
|
73
|
+
dev = "#{db.ip}:#{db.port}:#{db.udp}"
|
74
|
+
|
75
|
+
if @@lookup[@instance].empty?
|
76
|
+
@@lookup.delete(@instance)
|
77
|
+
if db.udp
|
78
|
+
$datagramServer.remove_device(db)
|
79
|
+
end
|
80
|
+
@@devices.delete(dev)
|
81
|
+
end
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def self.lookup(instance)
|
87
|
+
@@lookup_lock.synchronize {
|
88
|
+
return @@dbentry[@@lookup[instance][0]]
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.instance_of(db_id)
|
93
|
+
@@lookup_lock.synchronize {
|
94
|
+
return @@instances[db_id]
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
attr_reader :instance
|
100
|
+
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
|
105
|
+
def instantiate_module(controllerDevice)
|
106
|
+
if Modules[controllerDevice.dependency_id].nil?
|
107
|
+
Modules.load_module(controllerDevice.dependency) # This is the re-load code function (live bug fixing - removing functions does not work)
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
baselookup = "#{controllerDevice.ip}:#{controllerDevice.port}:#{controllerDevice.udp}"
|
112
|
+
|
113
|
+
@@lookup_lock.lock
|
114
|
+
if @@devices[baselookup].nil?
|
115
|
+
|
116
|
+
#
|
117
|
+
# Instance of a user module
|
118
|
+
#
|
119
|
+
@instance = Modules[controllerDevice.dependency_id].new(controllerDevice.tls, controllerDevice.makebreak)
|
120
|
+
@instance.join_system(@system)
|
121
|
+
@@instances[@device] = @instance
|
122
|
+
@@devices[baselookup] = @instance
|
123
|
+
@@lookup[@instance] = [@device]
|
124
|
+
@@lookup_lock.unlock #UNLOCK!! so we can lookup settings in on_load
|
125
|
+
|
126
|
+
devBase = nil
|
127
|
+
|
128
|
+
loaded = Proc.new {
|
129
|
+
EM.defer do
|
130
|
+
if @instance.respond_to?(:on_load)
|
131
|
+
begin
|
132
|
+
@instance.on_load
|
133
|
+
rescue => e
|
134
|
+
AutomateEm.print_error(System.logger, e, {
|
135
|
+
:message => "device module #{@instance.class} error whilst calling: on_load",
|
136
|
+
:level => Logger::ERROR
|
137
|
+
})
|
138
|
+
ensure
|
139
|
+
ActiveRecord::Base.clear_active_connections!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if controllerDevice.udp
|
144
|
+
|
145
|
+
devBase.call_connected # UDP is stateless (always connected)
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
}
|
150
|
+
|
151
|
+
if !controllerDevice.udp
|
152
|
+
res = ResolverJob.new(controllerDevice.ip)
|
153
|
+
res.callback {|ip|
|
154
|
+
EM.connect ip, controllerDevice.port, Device::Base, @instance
|
155
|
+
loaded.call
|
156
|
+
}
|
157
|
+
res.errback {|error|
|
158
|
+
EM.defer do
|
159
|
+
System.logger.info error.message + " connecting to #{controllerDevice.dependency.actual_name} @ #{controllerDevice.ip} in #{controllerDevice.control_system.name}"
|
160
|
+
end
|
161
|
+
EM.connect "127.0.0.1", 10, Device::Base, @instance # Connect to a nothing port until the device name is found or updated
|
162
|
+
loaded.call
|
163
|
+
}
|
164
|
+
else
|
165
|
+
#
|
166
|
+
# Load UDP device here
|
167
|
+
# Create UDP base
|
168
|
+
# Add device to server
|
169
|
+
# => TODO::test!!
|
170
|
+
# Call connected
|
171
|
+
#
|
172
|
+
devBase = DatagramBase.new(@instance)
|
173
|
+
$datagramServer.add_device(controllerDevice, devBase)
|
174
|
+
loaded.call
|
175
|
+
end
|
176
|
+
|
177
|
+
#@@devices[baselookup] = Modules.loading # set in device_connection (see todo above)
|
178
|
+
else
|
179
|
+
#
|
180
|
+
# add parent may lock at this point!
|
181
|
+
#
|
182
|
+
@instance = @@devices[baselookup]
|
183
|
+
@@lookup[@instance] << @device
|
184
|
+
@@instances[@device] = @instance
|
185
|
+
EM.defer do
|
186
|
+
@instance.join_system(@system)
|
187
|
+
end
|
188
|
+
@@lookup_lock.unlock #UNLOCK!!
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
class ServiceModule
|
195
|
+
@@instances = {} # db id => @instance
|
196
|
+
@@dbentry = {} # db id => db instance
|
197
|
+
@@services = {} # uri => @instance
|
198
|
+
@@lookup = {} # @instance => db id array
|
199
|
+
@@lookup_lock = Mutex.new
|
200
|
+
|
201
|
+
def initialize(system, controllerService)
|
202
|
+
@@lookup_lock.synchronize {
|
203
|
+
if @@instances[controllerService.id].nil?
|
204
|
+
@system = system
|
205
|
+
@service = controllerService.id
|
206
|
+
@@dbentry[controllerService.id] = controllerService
|
207
|
+
end
|
208
|
+
}
|
209
|
+
instantiate_module(controllerService)
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
def unload # should never be called on the reactor thread so no need to defer
|
214
|
+
|
215
|
+
@instance.base.shutdown(@system)
|
216
|
+
|
217
|
+
@@lookup_lock.synchronize {
|
218
|
+
db = @@lookup[@instance].delete(@service)
|
219
|
+
@@instances.delete(db)
|
220
|
+
db = @@dbentry.delete(db)
|
221
|
+
|
222
|
+
if @@lookup[@instance].empty?
|
223
|
+
@@lookup.delete(@instance)
|
224
|
+
@@services.delete(db.uri)
|
225
|
+
end
|
226
|
+
}
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def self.lookup(instance)
|
231
|
+
@@lookup_lock.synchronize {
|
232
|
+
return @@dbentry[@@lookup[instance][0]]
|
233
|
+
}
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.instance_of(db_id)
|
237
|
+
@@lookup_lock.synchronize {
|
238
|
+
return @@instances[db_id]
|
239
|
+
}
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
attr_reader :instance
|
244
|
+
|
245
|
+
|
246
|
+
protected
|
247
|
+
|
248
|
+
|
249
|
+
def instantiate_module(controllerService)
|
250
|
+
if Modules[controllerService.dependency_id].nil?
|
251
|
+
Modules.load_module(controllerService.dependency) # This is the re-load code function (live bug fixing - removing functions does not work)
|
252
|
+
end
|
253
|
+
|
254
|
+
@@lookup_lock.lock
|
255
|
+
if @@services[controllerService.uri].nil?
|
256
|
+
|
257
|
+
#
|
258
|
+
# Instance of a user module
|
259
|
+
#
|
260
|
+
@instance = Modules[controllerService.dependency_id].new
|
261
|
+
@instance.join_system(@system)
|
262
|
+
@@instances[@service] = @instance
|
263
|
+
@@services[controllerService.uri] = @instance
|
264
|
+
@@lookup[@instance] = [@service]
|
265
|
+
@@lookup_lock.unlock #UNLOCK
|
266
|
+
|
267
|
+
HttpService.new(@instance, controllerService)
|
268
|
+
|
269
|
+
|
270
|
+
if @instance.respond_to?(:on_load)
|
271
|
+
begin
|
272
|
+
@instance.on_load
|
273
|
+
rescue => e
|
274
|
+
AutomateEm.print_error(System.logger, e, {
|
275
|
+
:message => "service module #{@instance.class} error whilst calling: on_load",
|
276
|
+
:level => Logger::ERROR
|
277
|
+
})
|
278
|
+
ensure
|
279
|
+
ActiveRecord::Base.clear_active_connections!
|
280
|
+
end
|
281
|
+
end
|
282
|
+
else
|
283
|
+
#
|
284
|
+
# add parent may lock at this point!
|
285
|
+
#
|
286
|
+
@instance = @@services[controllerService.uri]
|
287
|
+
@@lookup[@instance] << @service
|
288
|
+
@@instances[@service] = @instance
|
289
|
+
EM.defer do
|
290
|
+
@instance.join_system(@system)
|
291
|
+
end
|
292
|
+
@@lookup_lock.unlock #UNLOCK
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
class LogicModule
|
299
|
+
@@instances = {} # id => @instance
|
300
|
+
@@lookup = {} # @instance => DB Record
|
301
|
+
@@lookup_lock = Mutex.new
|
302
|
+
|
303
|
+
|
304
|
+
def initialize(system, controllerLogic)
|
305
|
+
@@lookup_lock.synchronize {
|
306
|
+
if @@instances[controllerLogic.id].nil?
|
307
|
+
instantiate_module(controllerLogic, system)
|
308
|
+
end
|
309
|
+
}
|
310
|
+
if @instance.respond_to?(:on_load)
|
311
|
+
begin
|
312
|
+
@instance.on_load
|
313
|
+
rescue => e
|
314
|
+
AutomateEm.print_error(System.logger, e, {
|
315
|
+
:message => "logic module #{@instance.class} error whilst calling: on_load",
|
316
|
+
:level => Logger::ERROR
|
317
|
+
})
|
318
|
+
ensure
|
319
|
+
ActiveRecord::Base.clear_active_connections!
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def unload
|
325
|
+
if @instance.respond_to?(:on_unload)
|
326
|
+
begin
|
327
|
+
@instance.on_unload
|
328
|
+
rescue => e
|
329
|
+
AutomateEm.print_error(System.logger, e, {
|
330
|
+
:message => "logic module #{@instance.class} error whilst calling: on_unload",
|
331
|
+
:level => Logger::ERROR
|
332
|
+
})
|
333
|
+
ensure
|
334
|
+
ActiveRecord::Base.clear_active_connections!
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
@instance.clear_active_timers
|
339
|
+
|
340
|
+
@@lookup_lock.synchronize {
|
341
|
+
db = @@lookup.delete(@instance)
|
342
|
+
@@instances.delete(db.id)
|
343
|
+
}
|
344
|
+
end
|
345
|
+
|
346
|
+
def self.lookup(instance)
|
347
|
+
@@lookup_lock.synchronize {
|
348
|
+
return @@lookup[instance]
|
349
|
+
}
|
350
|
+
end
|
351
|
+
|
352
|
+
def self.instance_of(db_id)
|
353
|
+
@@lookup_lock.synchronize {
|
354
|
+
return @@instances[db_id]
|
355
|
+
}
|
356
|
+
end
|
357
|
+
|
358
|
+
attr_reader :instance
|
359
|
+
|
360
|
+
|
361
|
+
protected
|
362
|
+
|
363
|
+
|
364
|
+
def instantiate_module(controllerLogic, system)
|
365
|
+
if Modules[controllerLogic.dependency_id].nil?
|
366
|
+
Modules.load_module(controllerLogic.dependency) # This is the re-load code function (live bug fixing - removing functions does not work)
|
367
|
+
end
|
368
|
+
@instance = Modules[controllerLogic.dependency_id].new(system)
|
369
|
+
@@instances[controllerLogic.id] = @instance
|
370
|
+
@@lookup[@instance] = controllerLogic
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module AutomateEm
|
2
|
+
|
3
|
+
class ResolverPool
|
4
|
+
|
5
|
+
def initialize(size = 30)
|
6
|
+
|
7
|
+
@size = size
|
8
|
+
@jobs = Queue.new
|
9
|
+
|
10
|
+
@pool = Array.new(@size) do |i|
|
11
|
+
|
12
|
+
Thread.new do
|
13
|
+
#Thread.current[:id] = i
|
14
|
+
Thread.current.priority = Thread.current.priority - 1
|
15
|
+
loop do
|
16
|
+
begin
|
17
|
+
job = @jobs.pop
|
18
|
+
job.resolve
|
19
|
+
rescue => e
|
20
|
+
#
|
21
|
+
# Print error here
|
22
|
+
#
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def schedule(job)
|
32
|
+
@jobs << job
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class ResolverJob
|
39
|
+
|
40
|
+
include EM::Deferrable
|
41
|
+
|
42
|
+
def initialize(hostname)
|
43
|
+
if IPAddress.valid? hostname
|
44
|
+
self.succeed(hostname)
|
45
|
+
else
|
46
|
+
@hostname = hostname
|
47
|
+
|
48
|
+
#
|
49
|
+
# Enter self into resolver queue
|
50
|
+
#
|
51
|
+
if EM.reactor_thread?
|
52
|
+
EM.defer do
|
53
|
+
AutomateEm.resolver.schedule(self)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
AutomateEm.resolver.schedule(self)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def resolve
|
62
|
+
begin
|
63
|
+
ip = Resolv.getaddress(@hostname)
|
64
|
+
EM.schedule do
|
65
|
+
self.succeed(ip)
|
66
|
+
end
|
67
|
+
rescue => e
|
68
|
+
EM.schedule do
|
69
|
+
self.fail(e)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,356 @@
|
|
1
|
+
module AutomateEm
|
2
|
+
class System
|
3
|
+
|
4
|
+
@@systems = {:'---GOD---' => self} # system_name => system instance
|
5
|
+
@@controllers = {} # controller_id => system instance
|
6
|
+
@@logger = nil
|
7
|
+
@@communicator = AutomateEm::Communicator.new(self)
|
8
|
+
@@communicator.start(true)
|
9
|
+
@@god_lock = Mutex.new
|
10
|
+
|
11
|
+
|
12
|
+
def self.new_system(controller, log_level = Logger::INFO)
|
13
|
+
begin
|
14
|
+
if controller.class == Fixnum
|
15
|
+
controller = ControlSystem.find(controller)
|
16
|
+
elsif controller.class == String
|
17
|
+
controller = ControlSystem.where('name = ?', controller).first
|
18
|
+
end
|
19
|
+
|
20
|
+
if controller.class != ControlSystem
|
21
|
+
raise 'invalid controller identifier'
|
22
|
+
end
|
23
|
+
|
24
|
+
@@god_lock.lock
|
25
|
+
if @@controllers[controller.id].nil?
|
26
|
+
@@god_lock.unlock
|
27
|
+
sys = System.new(controller, log_level)
|
28
|
+
if controller.active
|
29
|
+
begin
|
30
|
+
sys.start(true) # as this is loading the first time we ignore controller active
|
31
|
+
rescue => e
|
32
|
+
AutomateEm.print_error(@@logger, e, {
|
33
|
+
:message => "Error starting system in new_system, stopping..",
|
34
|
+
:level => Logger::ERROR
|
35
|
+
})
|
36
|
+
begin
|
37
|
+
sys.stop
|
38
|
+
EM.schedule do
|
39
|
+
EM.add_timer(60) do # Attempt a single restart
|
40
|
+
EM.defer do
|
41
|
+
begin
|
42
|
+
sys.start(true)
|
43
|
+
rescue => e
|
44
|
+
begin
|
45
|
+
AutomateEm.print_error(@@logger, e, {
|
46
|
+
:message => "Second start attempt failed..",
|
47
|
+
:level => Logger::ERROR
|
48
|
+
})
|
49
|
+
sys.stop
|
50
|
+
rescue
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
rescue => e
|
57
|
+
AutomateEm.print_error(@@logger, e, {
|
58
|
+
:message => "Error stopping system after problems starting..",
|
59
|
+
:level => Logger::ERROR
|
60
|
+
})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@@god_lock.unlock
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
ActiveRecord::Base.clear_active_connections! # Clear any unused connections
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
#
|
74
|
+
# Reloads a dependency live
|
75
|
+
# This is the re-load code function (live bug fixing - removing functions does not work)
|
76
|
+
#
|
77
|
+
def self.reload(dep)
|
78
|
+
System.logger.info "reloading dependency: #{dep}"
|
79
|
+
|
80
|
+
dep = Dependency.find(dep)
|
81
|
+
Modules.load_module(dep)
|
82
|
+
|
83
|
+
updated = {}
|
84
|
+
dep.devices.select('id').each do |dev|
|
85
|
+
begin
|
86
|
+
inst = DeviceModule.instance_of(dev.id)
|
87
|
+
inst.on_update if (!!!updated[inst]) && inst.respond_to?(:on_update)
|
88
|
+
ensure
|
89
|
+
updated[inst] = true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
updated = {}
|
94
|
+
dep.logics.select('id').each do |log|
|
95
|
+
begin
|
96
|
+
inst = LogicModule.instance_of(log.id)
|
97
|
+
inst.on_update if (!!!updated[inst]) && inst.respond_to?(:on_update)
|
98
|
+
ensure
|
99
|
+
updated[inst] = true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
ActiveRecord::Base.clear_active_connections! # Clear any unused connections
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Allows for system updates on the fly
|
108
|
+
# Dangerous (Could be used to add on the fly interfaces)
|
109
|
+
#
|
110
|
+
def self.force_load_file(path)
|
111
|
+
load path if File.exists?(path) && File.extname(path) == '.rb'
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
#
|
116
|
+
# System Logger
|
117
|
+
#
|
118
|
+
def self.logger
|
119
|
+
@@logger
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.logger=(log)
|
123
|
+
@@logger = log
|
124
|
+
end
|
125
|
+
|
126
|
+
#def self.controllers
|
127
|
+
# @@controllers
|
128
|
+
#end
|
129
|
+
|
130
|
+
#def self.systems
|
131
|
+
# @@systems
|
132
|
+
#end
|
133
|
+
|
134
|
+
def self.communicator
|
135
|
+
@@communicator
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.[] (system)
|
139
|
+
system = system.to_sym if system.class == String
|
140
|
+
@@god_lock.synchronize {
|
141
|
+
@@systems[system]
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
#
|
148
|
+
# For access via communicator as a super user
|
149
|
+
#
|
150
|
+
def self.modules
|
151
|
+
self
|
152
|
+
end
|
153
|
+
def self.instance
|
154
|
+
self
|
155
|
+
end
|
156
|
+
def instance
|
157
|
+
self
|
158
|
+
end
|
159
|
+
# ---------------------------------
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
#
|
164
|
+
# Module accessor
|
165
|
+
#
|
166
|
+
def [] (mod)
|
167
|
+
mod = mod.to_sym if mod.class == String
|
168
|
+
@modules[mod].instance
|
169
|
+
end
|
170
|
+
|
171
|
+
attr_reader :modules
|
172
|
+
attr_reader :communicator
|
173
|
+
attr_reader :controller
|
174
|
+
attr_accessor :logger
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
#
|
179
|
+
# Starts the control system if not running
|
180
|
+
#
|
181
|
+
def start(force = false)
|
182
|
+
System.logger.info "starting #{@controller.name}"
|
183
|
+
@sys_lock.synchronize {
|
184
|
+
@@god_lock.synchronize {
|
185
|
+
@@systems.delete(@controller.name.to_sym)
|
186
|
+
@controller.reload #(:lock => true)
|
187
|
+
@@systems[@controller.name.to_sym] = self
|
188
|
+
}
|
189
|
+
|
190
|
+
if !@controller.active || force
|
191
|
+
if @logger.nil?
|
192
|
+
if Rails.env.production?
|
193
|
+
@logger = Logger.new("#{ROOT_DIR}/interface/log/system_#{@controller.id}.log", 10, 4194304)
|
194
|
+
else
|
195
|
+
@logger = Logger.new(STDOUT)
|
196
|
+
end
|
197
|
+
@logger.formatter = proc { |severity, datetime, progname, msg|
|
198
|
+
"#{datetime.strftime("%d/%m/%Y @ %I:%M%p")} #{severity}: #{@controller.name} - #{msg}\n"
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
@controller.devices.includes(:dependency).each do |device|
|
203
|
+
load_hooks(device, DeviceModule.new(self, device))
|
204
|
+
end
|
205
|
+
|
206
|
+
@controller.services.includes(:dependency).each do |service|
|
207
|
+
load_hooks(service, ServiceModule.new(self, service))
|
208
|
+
end
|
209
|
+
|
210
|
+
@controller.logics.includes(:dependency).each do |logic|
|
211
|
+
load_hooks(logic, LogicModule.new(self, logic))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
@controller.active = true
|
216
|
+
@controller.save
|
217
|
+
|
218
|
+
@communicator.start
|
219
|
+
}
|
220
|
+
end
|
221
|
+
|
222
|
+
#
|
223
|
+
# Stops the current control system
|
224
|
+
# Loops through the module instances.
|
225
|
+
#
|
226
|
+
def stop
|
227
|
+
System.logger.info "stopping #{@controller.name}"
|
228
|
+
@sys_lock.synchronize {
|
229
|
+
stop_nolock
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# Unload and then destroy self
|
235
|
+
#
|
236
|
+
def delete
|
237
|
+
System.logger.info "deleting #{@controller.name}"
|
238
|
+
@sys_lock.synchronize {
|
239
|
+
stop_nolock
|
240
|
+
|
241
|
+
@@god_lock.synchronize {
|
242
|
+
@@systems.delete(@controller.name.to_sym)
|
243
|
+
@@controllers.delete(@controller.id)
|
244
|
+
}
|
245
|
+
|
246
|
+
begin
|
247
|
+
@controller.destroy!
|
248
|
+
rescue
|
249
|
+
# Controller may already be deleted
|
250
|
+
end
|
251
|
+
@modules = nil
|
252
|
+
}
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
#
|
257
|
+
# Log level changing on the fly
|
258
|
+
#
|
259
|
+
def log_level(level)
|
260
|
+
@sys_lock.synchronize {
|
261
|
+
@log_level = AutomateEm::get_log_level(level)
|
262
|
+
if @controller.active
|
263
|
+
@logger.level = @log_level
|
264
|
+
end
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
protected
|
270
|
+
|
271
|
+
|
272
|
+
def stop_nolock
|
273
|
+
|
274
|
+
begin
|
275
|
+
@@god_lock.synchronize {
|
276
|
+
@@systems.delete(@controller.name.to_sym)
|
277
|
+
@controller.reload(:lock => true)
|
278
|
+
@@systems[@controller.name.to_sym] = self
|
279
|
+
}
|
280
|
+
rescue
|
281
|
+
# Assume controller may have been deleted
|
282
|
+
end
|
283
|
+
|
284
|
+
if @controller.active
|
285
|
+
@communicator.shutdown
|
286
|
+
modules_unloaded = {}
|
287
|
+
@modules.each_value do |mod|
|
288
|
+
|
289
|
+
if modules_unloaded[mod] == nil
|
290
|
+
modules_unloaded[mod] = :unloaded
|
291
|
+
mod.unload
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
295
|
+
@modules = {} # Modules no longer referenced. Cleanup time!
|
296
|
+
@logger.close if Rails.env.production?
|
297
|
+
@logger = nil
|
298
|
+
end
|
299
|
+
|
300
|
+
@controller.active = false
|
301
|
+
begin
|
302
|
+
@controller.save
|
303
|
+
rescue
|
304
|
+
# Assume controller may have been deleted
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
def load_hooks(device, mod)
|
310
|
+
module_name = device.dependency.module_name
|
311
|
+
count = 2 # 2 is correct
|
312
|
+
|
313
|
+
#
|
314
|
+
# Loads the modules and auto-names them (display_1, display_2)
|
315
|
+
# The first module of a type has two names (display and display_1 for example)
|
316
|
+
# Load order is controlled by the control_system model based on the ordinal
|
317
|
+
#
|
318
|
+
if not @modules[module_name.to_sym].nil?
|
319
|
+
while @modules["#{module_name}_#{count}".to_sym].present?
|
320
|
+
count += 1
|
321
|
+
end
|
322
|
+
module_name = "#{module_name}_#{count}"
|
323
|
+
else
|
324
|
+
@modules["#{module_name}_1".to_sym] = mod
|
325
|
+
end
|
326
|
+
@modules[module_name.to_sym] = mod
|
327
|
+
|
328
|
+
#
|
329
|
+
# Allow for system specific custom names
|
330
|
+
#
|
331
|
+
if !device.custom_name.nil?
|
332
|
+
@modules[device.custom_name.to_sym] = mod
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
|
337
|
+
def initialize(controller, log_level)
|
338
|
+
|
339
|
+
|
340
|
+
@modules = {} # controller modules :name => module instance (device or logic)
|
341
|
+
@communicator = AutomateEm::Communicator.new(self)
|
342
|
+
@log_level = log_level
|
343
|
+
@controller = controller
|
344
|
+
@sys_lock = Mutex.new
|
345
|
+
|
346
|
+
|
347
|
+
#
|
348
|
+
# Setup the systems links
|
349
|
+
#
|
350
|
+
@@god_lock.synchronize {
|
351
|
+
@@systems[@controller.name.to_sym] = self # it may not be started
|
352
|
+
@@controllers[@controller.id] = self
|
353
|
+
}
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|