rsmp 0.1.17 → 0.1.19
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +14 -12
- data/README.md +1 -1
- data/config/tlc.yaml +7 -6
- data/documentation/classes.md +62 -0
- data/lib/rsmp/archive.rb +11 -5
- data/lib/rsmp/component.rb +1 -0
- data/lib/rsmp/error.rb +1 -1
- data/lib/rsmp/message.rb +25 -2
- data/lib/rsmp/node.rb +33 -6
- data/lib/rsmp/probe.rb +5 -18
- data/lib/rsmp/proxy.rb +37 -41
- data/lib/rsmp/site.rb +4 -1
- data/lib/rsmp/site_proxy.rb +84 -65
- data/lib/rsmp/supervisor.rb +3 -2
- data/lib/rsmp/supervisor_proxy.rb +50 -14
- data/lib/rsmp/tlc.rb +185 -65
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp/wait.rb +149 -3
- metadata +9 -3
- data/lib/rsmp/supervisor_base.rb +0 -10
data/lib/rsmp/site.rb
CHANGED
@@ -110,7 +110,9 @@ module RSMP
|
|
110
110
|
if @site_settings["reconnect_interval"] != :no
|
111
111
|
# sleep until waken by reconnect() or the reconnect interval passed
|
112
112
|
proxy.set_state :wait_for_reconnect
|
113
|
-
task.with_timeout(@site_settings["reconnect_interval"])
|
113
|
+
task.with_timeout(@site_settings["reconnect_interval"]) do
|
114
|
+
@sleep_condition.wait
|
115
|
+
end
|
114
116
|
else
|
115
117
|
proxy.set_state :cannot_connect
|
116
118
|
break
|
@@ -141,5 +143,6 @@ module RSMP
|
|
141
143
|
proxy.stop
|
142
144
|
end
|
143
145
|
end
|
146
|
+
|
144
147
|
end
|
145
148
|
end
|
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -23,6 +23,11 @@ module RSMP
|
|
23
23
|
start_reader
|
24
24
|
end
|
25
25
|
|
26
|
+
def stop
|
27
|
+
log "Closing connection to site", level: :info
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
26
31
|
def connection_complete
|
27
32
|
super
|
28
33
|
log "Connection to site #{@site_id} established", level: :info
|
@@ -50,6 +55,10 @@ module RSMP
|
|
50
55
|
end
|
51
56
|
end
|
52
57
|
|
58
|
+
def process_deferred
|
59
|
+
supervisor.process_deferred
|
60
|
+
end
|
61
|
+
|
53
62
|
def version_accepted message
|
54
63
|
log "Received Version message for site #{@site_id} using RSMP #{@rsmp_version}", message: message, level: :log
|
55
64
|
start_timer
|
@@ -123,16 +132,38 @@ module RSMP
|
|
123
132
|
@supervisor.site_ids_changed
|
124
133
|
end
|
125
134
|
|
126
|
-
def
|
127
|
-
|
135
|
+
def fetch_status parent_task, options
|
136
|
+
wait_for_status_responses(parent_task,options) do |m_id|
|
137
|
+
request_status options.merge(m_id: m_id)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Convert from a short ruby hash:
|
142
|
+
# {:S0001=>[:signalgroupstatus, :cyclecounter, :basecyclecounter, :stage]}
|
143
|
+
# to an rsmp-style list:
|
144
|
+
# [{"sCI"=>"S0001", "n"=>"signalgroupstatus"}, {"sCI"=>"S0001", "n"=>"cyclecounter"}, {"sCI"=>"S0001", "n"=>"basecyclecounter"}, {"sCI"=>"S0001", "n"=>"stage"}]
|
145
|
+
#
|
146
|
+
# If the input is already an array, just return it
|
147
|
+
def convert_status_list list
|
148
|
+
return list.clone if list.is_a? Array
|
149
|
+
list.map do |status_code_id,names|
|
150
|
+
names.map do |name|
|
151
|
+
{ 'sCI' => status_code_id.to_s, 'n' => name.to_s }
|
152
|
+
end
|
153
|
+
end.flatten
|
154
|
+
end
|
155
|
+
|
156
|
+
def request_status options
|
157
|
+
raise NotReady unless ready?
|
128
158
|
message = RSMP::StatusRequest.new({
|
129
159
|
"ntsOId" => '',
|
130
160
|
"xNId" => '',
|
131
|
-
"cId" => component,
|
132
|
-
"sS" => status_list
|
161
|
+
"cId" => options[:component],
|
162
|
+
"sS" => convert_status_list(options[:status_list]),
|
163
|
+
"mId" => options[:m_id]
|
133
164
|
})
|
134
165
|
send_message message
|
135
|
-
|
166
|
+
message
|
136
167
|
end
|
137
168
|
|
138
169
|
def process_status_response message
|
@@ -140,41 +171,26 @@ module RSMP
|
|
140
171
|
acknowledge message
|
141
172
|
end
|
142
173
|
|
143
|
-
def
|
144
|
-
raise
|
145
|
-
item = @archive.capture(@task, options.merge(
|
146
|
-
type: ['StatusResponse','MessageNotAck'],
|
147
|
-
with_message: true,
|
148
|
-
num: 1
|
149
|
-
)) do |item|
|
150
|
-
if item[:message].type == 'MessageNotAck'
|
151
|
-
next item[:message].attribute('oMId') == options[:message].m_id
|
152
|
-
elsif item[:message].type == 'StatusResponse'
|
153
|
-
next item[:message].attribute('cId') == options[:message].attribute('cId')
|
154
|
-
end
|
155
|
-
end
|
156
|
-
item[:message] if item
|
157
|
-
end
|
158
|
-
|
159
|
-
def subscribe_to_status component, status_list, timeout
|
160
|
-
raise NotReady unless @state == :ready
|
174
|
+
def subscribe_to_status component, status_list, options={}
|
175
|
+
raise NotReady unless ready?
|
161
176
|
message = RSMP::StatusSubscribe.new({
|
162
177
|
"ntsOId" => '',
|
163
178
|
"xNId" => '',
|
164
179
|
"cId" => component,
|
165
|
-
"sS" => status_list
|
180
|
+
"sS" => convert_status_list(status_list),
|
181
|
+
'mId'=>options[:m_id]
|
166
182
|
})
|
167
183
|
send_message message
|
168
|
-
return message
|
184
|
+
return message
|
169
185
|
end
|
170
186
|
|
171
187
|
def unsubscribe_to_status component, status_list
|
172
|
-
raise NotReady unless
|
188
|
+
raise NotReady unless ready?
|
173
189
|
message = RSMP::StatusUnsubscribe.new({
|
174
190
|
"ntsOId" => '',
|
175
191
|
"xNId" => '',
|
176
192
|
"cId" => component,
|
177
|
-
"sS" => status_list
|
193
|
+
"sS" => convert_status_list(status_list)
|
178
194
|
})
|
179
195
|
send_message message
|
180
196
|
message
|
@@ -185,34 +201,20 @@ module RSMP
|
|
185
201
|
acknowledge message
|
186
202
|
end
|
187
203
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
next if options[:sCI] && options[:sCI] != status['sCI']
|
197
|
-
next if options[:n] && options[:n] != status['n']
|
198
|
-
next if options[:q] && options[:q] != status['q']
|
199
|
-
if options[:s].is_a? Regexp
|
200
|
-
next if options[:s] && status['s'] !~ options[:s]
|
201
|
-
else
|
202
|
-
next if options[:s] && options[:s] != status['s']
|
203
|
-
end
|
204
|
-
matching_status = status
|
205
|
-
break
|
206
|
-
end
|
207
|
-
matching_status != nil
|
208
|
-
end
|
209
|
-
if item
|
210
|
-
{ message: item[:message], status: matching_status }
|
204
|
+
def status_match? query, item
|
205
|
+
return false if query[:sCI] && query[:sCI] != item['sCI']
|
206
|
+
return false if query[:n] && query[:n] != item['n']
|
207
|
+
return false if query[:q] && query[:q] != item['q']
|
208
|
+
if query[:s].is_a? Regexp
|
209
|
+
return false if query[:s] && item['s'] !~ query[:s]
|
210
|
+
else
|
211
|
+
return false if query[:s] && item['s'] != query[:s]
|
211
212
|
end
|
213
|
+
true
|
212
214
|
end
|
213
215
|
|
214
216
|
def wait_for_alarm options={}
|
215
|
-
raise ArgumentError unless options[:component]
|
217
|
+
raise ArgumentError.new("component argument is missing") unless options[:component]
|
216
218
|
matching_alarm = nil
|
217
219
|
item = @archive.capture(@task,options.merge(type: "Alarm", with_message: true, num: 1)) do |item|
|
218
220
|
# TODO check components
|
@@ -229,31 +231,49 @@ module RSMP
|
|
229
231
|
end
|
230
232
|
end
|
231
233
|
|
232
|
-
def
|
233
|
-
|
234
|
-
message = RSMP::CommandRequest.new({
|
234
|
+
def send_alarm_acknowledgement component, alarm_code
|
235
|
+
message = RSMP::AlarmAcknowledged.new({
|
235
236
|
"ntsOId" => '',
|
236
237
|
"xNId" => '',
|
237
238
|
"cId" => component,
|
238
|
-
"
|
239
|
+
"aCId" => alarm_code,
|
240
|
+
"xACId" => '',
|
241
|
+
"xNACId" => '',
|
242
|
+
"aSp" => 'Acknowledge'
|
239
243
|
})
|
240
244
|
send_message message
|
241
245
|
message
|
242
246
|
end
|
243
247
|
|
244
|
-
def
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
248
|
+
def wait_for_alarm_acknowledgement_response options
|
249
|
+
raise ArgumentError.new("component argument is missing") unless options[:component]
|
250
|
+
item = @archive.capture(@task,options.merge(
|
251
|
+
num: 1,
|
252
|
+
type: ['AlarmAcknowledgedResponse','MessageNotAck'],
|
253
|
+
with_message: true
|
254
|
+
)) do |item|
|
255
|
+
if item[:message].type == 'MessageNotAck'
|
256
|
+
next item[:message].attribute('oMId') == options[:message].m_id
|
257
|
+
elsif item[:message].type == 'AlarmAcknowledgedResponse'
|
258
|
+
next item[:message].attribute('cId') == options[:message].attribute('cId')
|
259
|
+
end
|
253
260
|
end
|
254
261
|
item[:message] if item
|
255
262
|
end
|
256
263
|
|
264
|
+
def send_command component, args, options={}
|
265
|
+
raise NotReady unless ready?
|
266
|
+
message = RSMP::CommandRequest.new({
|
267
|
+
"ntsOId" => '',
|
268
|
+
"xNId" => '',
|
269
|
+
"cId" => component,
|
270
|
+
"arg" => args,
|
271
|
+
"mId" => options[:m_id]
|
272
|
+
})
|
273
|
+
send_message message
|
274
|
+
message
|
275
|
+
end
|
276
|
+
|
257
277
|
def set_watchdog_interval interval
|
258
278
|
@settings["watchdog_interval"] = interval
|
259
279
|
end
|
@@ -286,6 +306,5 @@ module RSMP
|
|
286
306
|
site_ids_changed
|
287
307
|
end
|
288
308
|
|
289
|
-
|
290
309
|
end
|
291
310
|
end
|
data/lib/rsmp/supervisor.rb
CHANGED
@@ -129,6 +129,7 @@ module RSMP
|
|
129
129
|
@proxies.push proxy
|
130
130
|
|
131
131
|
proxy.run # will run until the site disconnects
|
132
|
+
ensure
|
132
133
|
@proxies.delete proxy
|
133
134
|
site_ids_changed
|
134
135
|
|
@@ -167,13 +168,13 @@ module RSMP
|
|
167
168
|
def wait_for_site site_id, timeout
|
168
169
|
site = find_site site_id
|
169
170
|
return site if site
|
170
|
-
|
171
|
+
wait_for(@site_id_condition,timeout) { find_site site_id }
|
171
172
|
rescue Async::TimeoutError
|
172
173
|
nil
|
173
174
|
end
|
174
175
|
|
175
176
|
def wait_for_site_disconnect site_id, timeout
|
176
|
-
|
177
|
+
wait_for(@site_id_condition,timeout) { true unless find_site site_id }
|
177
178
|
rescue Async::TimeoutError
|
178
179
|
false
|
179
180
|
end
|
@@ -28,6 +28,7 @@ module RSMP
|
|
28
28
|
super
|
29
29
|
connect
|
30
30
|
@logger.unmute @ip, @port
|
31
|
+
log "Connected to superviser at #{@ip}:#{@port}", level: :info
|
31
32
|
start_reader
|
32
33
|
send_version @site_settings['site_id'], @site_settings["rsmp_versions"]
|
33
34
|
rescue Errno::ECONNREFUSED
|
@@ -38,6 +39,12 @@ module RSMP
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
42
|
+
def stop
|
43
|
+
log "Closing connection to supervisor", level: :info
|
44
|
+
super
|
45
|
+
@last_status_sent = nil
|
46
|
+
end
|
47
|
+
|
41
48
|
def connect
|
42
49
|
return if @socket
|
43
50
|
@endpoint = Async::IO::Endpoint.tcp(@ip, @port)
|
@@ -72,14 +79,15 @@ module RSMP
|
|
72
79
|
else
|
73
80
|
super message
|
74
81
|
end
|
75
|
-
rescue UnknownComponent
|
76
|
-
|
77
|
-
rescue UnknownCommand => e
|
78
|
-
dont_acknowledge message, '', e.to_s
|
79
|
-
rescue UnknownStatus => e
|
82
|
+
rescue UnknownComponent, UnknownCommand, UnknownStatus,
|
83
|
+
MessageRejected, MissingAttribute => e
|
80
84
|
dont_acknowledge message, '', e.to_s
|
81
85
|
end
|
82
86
|
|
87
|
+
def process_deferred
|
88
|
+
site.process_deferred
|
89
|
+
end
|
90
|
+
|
83
91
|
def acknowledged_first_ingoing message
|
84
92
|
# TODO
|
85
93
|
# aggregateds status should only be send for later version of rsmp
|
@@ -118,7 +126,7 @@ module RSMP
|
|
118
126
|
message = AggregatedStatus.new({
|
119
127
|
"aSTS" => RSMP.now_string,
|
120
128
|
"cId" => component.c_id,
|
121
|
-
"fP" =>
|
129
|
+
"fP" => 'NormalControl',
|
122
130
|
"fS" => nil,
|
123
131
|
"se" => component.aggregated_status_bools
|
124
132
|
})
|
@@ -177,7 +185,7 @@ module RSMP
|
|
177
185
|
send_message response
|
178
186
|
end
|
179
187
|
|
180
|
-
def process_status_request message
|
188
|
+
def process_status_request message, options={}
|
181
189
|
component_id = message.attributes["cId"]
|
182
190
|
component = @site.find_component component_id
|
183
191
|
log "Received #{message.type}", message: message, level: :log
|
@@ -188,7 +196,8 @@ module RSMP
|
|
188
196
|
response = StatusResponse.new({
|
189
197
|
"cId"=>component_id,
|
190
198
|
"sTs"=>RSMP.now_string,
|
191
|
-
"sS"=>sS
|
199
|
+
"sS"=>sS,
|
200
|
+
"mId" => options[:m_id]
|
192
201
|
})
|
193
202
|
acknowledge message
|
194
203
|
send_message response
|
@@ -213,10 +222,11 @@ module RSMP
|
|
213
222
|
update_list[component] ||= {}
|
214
223
|
|
215
224
|
subs = @status_subscriptions[component]
|
225
|
+
now = RSMP::now_object
|
216
226
|
|
217
227
|
message.attributes["sS"].each do |arg|
|
218
228
|
sCI = arg["sCI"]
|
219
|
-
subcription = {interval: arg["uRt"].to_i, last_sent_at:
|
229
|
+
subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
|
220
230
|
subs[sCI] ||= {}
|
221
231
|
subs[sCI][arg["n"]] = subcription
|
222
232
|
|
@@ -250,18 +260,40 @@ module RSMP
|
|
250
260
|
status_update_timer now if ready?
|
251
261
|
end
|
252
262
|
|
263
|
+
def fetch_last_sent_status component, code, name
|
264
|
+
if @last_status_sent && @last_status_sent[component] && @last_status_sent[component][code]
|
265
|
+
@last_status_sent[component][code][name]
|
266
|
+
else
|
267
|
+
nil
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def store_last_sent_status component, code, name, value
|
272
|
+
@last_status_sent ||= {}
|
273
|
+
@last_status_sent[component] ||= {}
|
274
|
+
@last_status_sent[component][code] ||= {}
|
275
|
+
@last_status_sent[component][code][name] = value
|
276
|
+
end
|
277
|
+
|
253
278
|
def status_update_timer now
|
254
279
|
update_list = {}
|
255
280
|
# go through subscriptons and build a similarly organized list,
|
256
281
|
# that only contains what should be send
|
257
282
|
|
258
283
|
@status_subscriptions.each_pair do |component,by_code|
|
284
|
+
component_object = @site.find_component component
|
259
285
|
by_code.each_pair do |code,by_name|
|
260
286
|
by_name.each_pair do |name,subscription|
|
287
|
+
current = nil
|
261
288
|
if subscription[:interval] == 0
|
262
289
|
# send as soon as the data changes
|
263
|
-
if
|
290
|
+
if component_object
|
291
|
+
current, age = *(component_object.get_status code, name)
|
292
|
+
end
|
293
|
+
last_sent = fetch_last_sent_status component, code, name
|
294
|
+
if current != last_sent
|
264
295
|
should_send = true
|
296
|
+
store_last_sent_status component, code, name, current
|
265
297
|
end
|
266
298
|
else
|
267
299
|
# send at regular intervals
|
@@ -272,8 +304,8 @@ module RSMP
|
|
272
304
|
if should_send
|
273
305
|
subscription[:last_sent_at] = now
|
274
306
|
update_list[component] ||= {}
|
275
|
-
update_list[component][code] ||=
|
276
|
-
update_list[component][code]
|
307
|
+
update_list[component][code] ||= {}
|
308
|
+
update_list[component][code][name] = current
|
277
309
|
end
|
278
310
|
end
|
279
311
|
end
|
@@ -289,8 +321,12 @@ module RSMP
|
|
289
321
|
component = @site.find_component component_id
|
290
322
|
sS = []
|
291
323
|
by_code.each_pair do |code,names|
|
292
|
-
names.map do |status_name|
|
293
|
-
value
|
324
|
+
names.map do |status_name,value|
|
325
|
+
if value
|
326
|
+
quality = 'recent'
|
327
|
+
else
|
328
|
+
value,quality = component.get_status code, status_name
|
329
|
+
end
|
294
330
|
sS << { "sCI" => code,
|
295
331
|
"n" => status_name,
|
296
332
|
"s" => value.to_s,
|
data/lib/rsmp/tlc.rb
CHANGED
@@ -4,13 +4,21 @@ module RSMP
|
|
4
4
|
|
5
5
|
class TrafficController < Component
|
6
6
|
attr_reader :pos, :cycle_time
|
7
|
+
|
7
8
|
def initialize node:, id:, cycle_time:
|
8
9
|
super node: node, id: id, grouped: true
|
9
10
|
@signal_groups = []
|
10
11
|
@detector_logics = []
|
11
12
|
@plans = []
|
12
|
-
@pos = 0
|
13
13
|
@cycle_time = cycle_time
|
14
|
+
@num_traffic_situations = 1
|
15
|
+
@num_inputs = 8
|
16
|
+
|
17
|
+
reset
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@pos = 0
|
14
22
|
@plan = 0
|
15
23
|
@dark_mode = false
|
16
24
|
@yellow_flash = false
|
@@ -22,14 +30,12 @@ module RSMP
|
|
22
30
|
@emergency_route = false
|
23
31
|
@emergency_route_number = 0
|
24
32
|
@traffic_situation = 0
|
25
|
-
@num_traffic_situations = 1
|
26
33
|
@manual_control = false
|
27
34
|
@fixed_time_control = false
|
28
35
|
@isolated_control = false
|
29
36
|
@yellow_flash = false
|
30
37
|
@all_red = false
|
31
38
|
|
32
|
-
@num_inputs = 8
|
33
39
|
@inputs = '0'*@num_inputs
|
34
40
|
@input_activations = '0'*@num_inputs
|
35
41
|
@input_results = '0'*@num_inputs
|
@@ -79,32 +85,24 @@ module RSMP
|
|
79
85
|
|
80
86
|
def handle_command command_code, arg
|
81
87
|
case command_code
|
82
|
-
when 'M0001'
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
when 'M0004'
|
89
|
-
handle_m0004 arg
|
90
|
-
when 'M0005'
|
91
|
-
handle_m0005 arg
|
92
|
-
when 'M0006'
|
93
|
-
handle_m0006 arg
|
94
|
-
when 'M0007'
|
95
|
-
handle_m0007 arg
|
88
|
+
when 'M0001', 'M0002', 'M0003', 'M0004', 'M0005', 'M0006', 'M0007',
|
89
|
+
'M0012', 'M0013', 'M0014', 'M0015', 'M0016', 'M0017', 'M0018',
|
90
|
+
'M0019', 'M0020', 'M0021', 'M0022',
|
91
|
+
'M0103', 'M0104'
|
92
|
+
|
93
|
+
return send("handle_#{command_code.downcase}", arg)
|
96
94
|
else
|
97
95
|
raise UnknownCommand.new "Unknown command #{command_code}"
|
98
96
|
end
|
99
97
|
end
|
100
98
|
|
101
99
|
def handle_m0001 arg
|
102
|
-
@node.verify_security_code arg['securityCode']
|
100
|
+
@node.verify_security_code 2, arg['securityCode']
|
103
101
|
switch_mode arg['status']
|
104
102
|
end
|
105
103
|
|
106
104
|
def handle_m0002 arg
|
107
|
-
@node.verify_security_code arg['securityCode']
|
105
|
+
@node.verify_security_code 2, arg['securityCode']
|
108
106
|
if RSMP::Tlc.from_rsmp_bool(arg['status'])
|
109
107
|
switch_plan arg['timeplan']
|
110
108
|
else
|
@@ -113,32 +111,86 @@ module RSMP
|
|
113
111
|
end
|
114
112
|
|
115
113
|
def handle_m0003 arg
|
116
|
-
@node.verify_security_code arg['securityCode']
|
114
|
+
@node.verify_security_code 2, arg['securityCode']
|
117
115
|
@traffic_situation = arg['traficsituation'].to_i
|
118
116
|
end
|
119
117
|
|
120
118
|
def handle_m0004 arg
|
121
|
-
@node.verify_security_code arg['securityCode']
|
122
|
-
#
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
@node.restart
|
119
|
+
@node.verify_security_code 2, arg['securityCode']
|
120
|
+
# don't restart immeediately, since we need to first send command response
|
121
|
+
# instead, defer an action, which will be handled by the TLC site
|
122
|
+
log "Sheduling restart of TLC", level: :info
|
123
|
+
@node.defer :restart
|
127
124
|
end
|
128
125
|
|
129
126
|
def handle_m0005 arg
|
130
|
-
@node.verify_security_code arg['securityCode']
|
127
|
+
@node.verify_security_code 2, arg['securityCode']
|
131
128
|
@emergency_route = arg['status'] == 'True'
|
132
129
|
@emergency_route_number = arg['emergencyroute'].to_i
|
133
130
|
end
|
134
131
|
|
135
132
|
def handle_m0006 arg
|
136
|
-
@node.verify_security_code arg['securityCode']
|
133
|
+
@node.verify_security_code 2, arg['securityCode']
|
137
134
|
input = arg['input'].to_i
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
@
|
135
|
+
idx = input - 1
|
136
|
+
return unless idx>=0 && input<@num_inputs
|
137
|
+
@input_activations[idx] = (arg['status']=='True' ? '1' : '0')
|
138
|
+
result = @input_activations[idx]=='1' || @inputs[idx]=='1'
|
139
|
+
@input_results[idx] = (result ? '1' : '0')
|
140
|
+
end
|
141
|
+
|
142
|
+
def handle_m0007 arg
|
143
|
+
@node.verify_security_code 2, arg['securityCode']
|
144
|
+
set_fixed_time_control arg['status']
|
145
|
+
end
|
146
|
+
|
147
|
+
def handle_m0012 arg
|
148
|
+
@node.verify_security_code 2, arg['securityCode']
|
149
|
+
end
|
150
|
+
|
151
|
+
def handle_m0013 arg
|
152
|
+
@node.verify_security_code 2, arg['securityCode']
|
153
|
+
end
|
154
|
+
|
155
|
+
def handle_m0014 arg
|
156
|
+
@node.verify_security_code 2, arg['securityCode']
|
157
|
+
end
|
158
|
+
|
159
|
+
def handle_m0015 arg
|
160
|
+
@node.verify_security_code 2, arg['securityCode']
|
161
|
+
end
|
162
|
+
|
163
|
+
def handle_m0016 arg
|
164
|
+
@node.verify_security_code 2, arg['securityCode']
|
165
|
+
end
|
166
|
+
|
167
|
+
def handle_m0017 arg
|
168
|
+
@node.verify_security_code 2, arg['securityCode']
|
169
|
+
end
|
170
|
+
|
171
|
+
def handle_m0018 arg
|
172
|
+
@node.verify_security_code 2, arg['securityCode']
|
173
|
+
end
|
174
|
+
|
175
|
+
def handle_m0019 arg
|
176
|
+
@node.verify_security_code 2, arg['securityCode']
|
177
|
+
end
|
178
|
+
|
179
|
+
def handle_m0020 arg
|
180
|
+
@node.verify_security_code 2, arg['securityCode']
|
181
|
+
end
|
182
|
+
|
183
|
+
def handle_m0021 arg
|
184
|
+
@node.verify_security_code 2, arg['securityCode']
|
185
|
+
end
|
186
|
+
|
187
|
+
def handle_m0103 arg
|
188
|
+
level = {'Level1'=>1,'Level2'=>2}[arg['status']]
|
189
|
+
@node.change_security_code level, arg['oldSecurityCode'], arg['newSecurityCode']
|
190
|
+
end
|
191
|
+
|
192
|
+
def handle_m0104 arg
|
193
|
+
@node.verify_security_code 1, arg['securityCode']
|
142
194
|
end
|
143
195
|
|
144
196
|
def set_input i, value
|
@@ -146,11 +198,6 @@ module RSMP
|
|
146
198
|
@inputs[i] = (arg['value'] ? '1' : '0')
|
147
199
|
end
|
148
200
|
|
149
|
-
def handle_m0007 arg
|
150
|
-
@node.verify_security_code arg['securityCode']
|
151
|
-
set_fixed_time_control arg['status']
|
152
|
-
end
|
153
|
-
|
154
201
|
def set_fixed_time_control status
|
155
202
|
@fixed_time_control = status
|
156
203
|
end
|
@@ -182,8 +229,8 @@ module RSMP
|
|
182
229
|
'S0008', 'S0009', 'S0010', 'S0011', 'S0012', 'S0013', 'S0014',
|
183
230
|
'S0015', 'S0016', 'S0017', 'S0018', 'S0019', 'S0020', 'S0021',
|
184
231
|
'S0022', 'S0023', 'S0024', 'S0026', 'S0027', 'S0028',
|
185
|
-
'S0029',
|
186
|
-
'S0091', 'S0092', 'S0095', 'S0096',
|
232
|
+
'S0029', 'S0030', 'S0031',
|
233
|
+
'S0091', 'S0092', 'S0095', 'S0096', 'S0097',
|
187
234
|
'S0205', 'S0206', 'S0207', 'S0208'
|
188
235
|
return send("handle_#{code.downcase}", code, name)
|
189
236
|
else
|
@@ -194,7 +241,7 @@ module RSMP
|
|
194
241
|
def handle_s0001 status_code, status_name=nil
|
195
242
|
case status_name
|
196
243
|
when 'signalgroupstatus'
|
197
|
-
|
244
|
+
RSMP::Tlc.make_status format_signal_group_status
|
198
245
|
when 'cyclecounter'
|
199
246
|
RSMP::Tlc.make_status @pos.to_s
|
200
247
|
when 'basecyclecounter'
|
@@ -415,6 +462,20 @@ module RSMP
|
|
415
462
|
end
|
416
463
|
end
|
417
464
|
|
465
|
+
def handle_s0030 status_code, status_name=nil
|
466
|
+
case status_name
|
467
|
+
when 'status'
|
468
|
+
RSMP::Tlc.make_status ''
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def handle_s0031 status_code, status_name=nil
|
473
|
+
case status_name
|
474
|
+
when 'status'
|
475
|
+
RSMP::Tlc.make_status ''
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
418
479
|
def handle_s0091 status_code, status_name=nil
|
419
480
|
case status_name
|
420
481
|
when 'user'
|
@@ -457,6 +518,15 @@ module RSMP
|
|
457
518
|
end
|
458
519
|
end
|
459
520
|
|
521
|
+
def handle_s0097 status_code, status_name=nil
|
522
|
+
case status_name
|
523
|
+
when 'version'
|
524
|
+
RSMP::Tlc.make_status '1'
|
525
|
+
when 'hash'
|
526
|
+
RSMP::Tlc.make_status '1'
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
460
530
|
def handle_s0205 status_code, status_name=nil
|
461
531
|
case status_name
|
462
532
|
when 'start'
|
@@ -532,6 +602,31 @@ module RSMP
|
|
532
602
|
@state = get_state pos
|
533
603
|
end
|
534
604
|
|
605
|
+
def handle_command command_code, arg
|
606
|
+
case command_code
|
607
|
+
when 'M0010', 'M0011'
|
608
|
+
return send("handle_#{command_code.downcase}", arg)
|
609
|
+
else
|
610
|
+
raise UnknownCommand.new "Unknown command #{command_code}"
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
# Start of signal group. Orders a signal group to green
|
615
|
+
def handle_m0010 arg
|
616
|
+
@node.verify_security_code 2, arg['securityCode']
|
617
|
+
if RSMP::Tlc.from_rsmp_bool arg['status']
|
618
|
+
log "Start signal group #{c_id}, go to green", level: :info
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
# Stop of signal group. Orders a signal group to red
|
623
|
+
def handle_m0011 arg
|
624
|
+
@node.verify_security_code 2, arg['securityCode']
|
625
|
+
if RSMP::Tlc.from_rsmp_bool arg['status']
|
626
|
+
log "Stop signal group #{c_id}, go to red", level: :info
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
535
630
|
def get_status code, name=nil
|
536
631
|
case code
|
537
632
|
when 'S0025'
|
@@ -643,7 +738,7 @@ module RSMP
|
|
643
738
|
end
|
644
739
|
|
645
740
|
def handle_m0008 arg
|
646
|
-
@node.verify_security_code arg['securityCode']
|
741
|
+
@node.verify_security_code 2, arg['securityCode']
|
647
742
|
force_detector_logic arg['status']=='True', arg['value']='True'
|
648
743
|
arg
|
649
744
|
end
|
@@ -658,9 +753,9 @@ module RSMP
|
|
658
753
|
class Tlc < Site
|
659
754
|
def initialize options={}
|
660
755
|
super options
|
661
|
-
|
662
756
|
@sxl = 'traffic_light_controller'
|
663
|
-
|
757
|
+
@security_codes = options[:site_settings]['security_codes']
|
758
|
+
@interval = options[:site_settings]['interval'] || 1
|
664
759
|
unless @main
|
665
760
|
raise ConfigurationError.new "TLC must have a main component"
|
666
761
|
end
|
@@ -687,31 +782,39 @@ module RSMP
|
|
687
782
|
end
|
688
783
|
|
689
784
|
def start_timer
|
690
|
-
|
691
|
-
interval
|
692
|
-
log "Starting #{name} with interval #{interval} seconds", level: :debug
|
785
|
+
task_name = "tlc timer"
|
786
|
+
log "Starting #{task_name} with interval #{@interval} seconds", level: :debug
|
693
787
|
|
694
788
|
@timer = @task.async do |task|
|
695
|
-
task.annotate
|
789
|
+
task.annotate task_name
|
696
790
|
next_time = Time.now.to_f
|
697
791
|
loop do
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
792
|
+
begin
|
793
|
+
now = RSMP.now_object
|
794
|
+
timer(now)
|
795
|
+
rescue EOFError => e
|
796
|
+
log "TLC timer: Connection closed: #{e}", level: :warning
|
797
|
+
rescue IOError => e
|
798
|
+
log "TLC timer: IOError", level: :warning
|
799
|
+
rescue Errno::ECONNRESET
|
800
|
+
log "TLC timer: Connection reset by peer", level: :warning
|
801
|
+
rescue Errno::EPIPE => e
|
802
|
+
log "TLC timer: Broken pipe", level: :warning
|
803
|
+
rescue StandardError => e
|
804
|
+
log "TLC timer: #{e}", level: :debug
|
805
|
+
ensure
|
806
|
+
# adjust sleep duration to avoid drift. so wake up always happens on the
|
807
|
+
# same fractional second.
|
808
|
+
# note that Time.now is not monotonic. If the clock si changed,
|
809
|
+
# either manaully or via NTP, the sleep interval might jump.
|
810
|
+
# an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
|
811
|
+
# to get the current time. this ensures a constant interval, but
|
812
|
+
# if the clock is changed, the wake up would then happen on a different
|
813
|
+
# fractional second
|
814
|
+
next_time += @interval
|
815
|
+
duration = next_time - Time.now.to_f
|
816
|
+
task.sleep duration
|
817
|
+
end
|
715
818
|
end
|
716
819
|
end
|
717
820
|
end
|
@@ -721,7 +824,16 @@ module RSMP
|
|
721
824
|
@main.timer now
|
722
825
|
end
|
723
826
|
|
724
|
-
def verify_security_code code
|
827
|
+
def verify_security_code level, code
|
828
|
+
raise ArgumentError.new("Level must be 1-2, got #{level}") unless (1..2).include?(level)
|
829
|
+
if @security_codes[level] != code
|
830
|
+
raise MessageRejected.new("Wrong security code for level #{level}")
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
def change_security_code level, old_code, new_code
|
835
|
+
verify_security_code level, old_code
|
836
|
+
@security_codes[level] = new_code
|
725
837
|
end
|
726
838
|
|
727
839
|
def self.to_rmsp_bool bool
|
@@ -745,5 +857,13 @@ module RSMP
|
|
745
857
|
end
|
746
858
|
end
|
747
859
|
|
860
|
+
def do_deferred item
|
861
|
+
case item
|
862
|
+
when :restart
|
863
|
+
log "Restarting TLC", level: :info
|
864
|
+
restart
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
748
868
|
end
|
749
869
|
end
|