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
@@ -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
|