rsmp 0.1.21 → 0.1.27
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/.gitmodules +0 -4
- data/Gemfile.lock +13 -11
- data/config/supervisor.yaml +12 -6
- data/config/tlc.yaml +8 -0
- data/documentation/message_distribution.md +8 -8
- data/lib/rsmp.rb +4 -0
- data/lib/rsmp/archive.rb +7 -4
- data/lib/rsmp/cli.rb +131 -102
- data/lib/rsmp/collector.rb +42 -30
- data/lib/rsmp/component.rb +2 -0
- data/lib/rsmp/components.rb +1 -1
- data/lib/rsmp/convert/export/json_schema.rb +204 -0
- data/lib/rsmp/convert/import/yaml.rb +38 -0
- data/lib/rsmp/inspect.rb +46 -0
- data/lib/rsmp/listener.rb +4 -14
- data/lib/rsmp/logger.rb +3 -1
- data/lib/rsmp/logging.rb +1 -1
- data/lib/rsmp/message.rb +22 -31
- data/lib/rsmp/node.rb +11 -1
- data/lib/rsmp/notifier.rb +7 -2
- data/lib/rsmp/proxy.rb +69 -28
- data/lib/rsmp/rsmp.rb +34 -23
- data/lib/rsmp/site.rb +16 -8
- data/lib/rsmp/site_proxy.rb +86 -20
- data/lib/rsmp/site_proxy_wait.rb +63 -38
- data/lib/rsmp/supervisor.rb +47 -18
- data/lib/rsmp/supervisor_proxy.rb +18 -10
- data/lib/rsmp/tlc.rb +55 -33
- data/lib/rsmp/version.rb +1 -1
- data/rsmp.gemspec +3 -14
- metadata +12 -112
data/lib/rsmp/site.rb
CHANGED
@@ -26,9 +26,9 @@ module RSMP
|
|
26
26
|
'supervisors' => [
|
27
27
|
{ 'ip' => '127.0.0.1', 'port' => 12111 }
|
28
28
|
],
|
29
|
-
'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4'],
|
30
|
-
'sxl' => '
|
31
|
-
'sxl_version' => '1.0.
|
29
|
+
'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4','3.1.5'],
|
30
|
+
'sxl' => 'tlc',
|
31
|
+
'sxl_version' => '1.0.15',
|
32
32
|
'timer_interval' => 0.1,
|
33
33
|
'watchdog_interval' => 1,
|
34
34
|
'watchdog_timeout' => 2,
|
@@ -54,9 +54,17 @@ module RSMP
|
|
54
54
|
:acknowledgement_timeout,:command_response_timeout]
|
55
55
|
check_required_settings @site_settings, required
|
56
56
|
|
57
|
+
check_sxl_version
|
58
|
+
|
57
59
|
setup_components @site_settings['components']
|
58
60
|
end
|
59
61
|
|
62
|
+
def check_sxl_version
|
63
|
+
sxl = @site_settings['sxl']
|
64
|
+
version = @site_settings['sxl_version']
|
65
|
+
RSMP::Schemer::find_schema! sxl, version
|
66
|
+
end
|
67
|
+
|
60
68
|
def reconnect
|
61
69
|
@sleep_condition.signal
|
62
70
|
end
|
@@ -64,8 +72,10 @@ module RSMP
|
|
64
72
|
def start_action
|
65
73
|
@site_settings["supervisors"].each do |supervisor_settings|
|
66
74
|
@task.async do |task|
|
67
|
-
task.annotate "
|
75
|
+
task.annotate "site proxy"
|
68
76
|
connect_to_supervisor task, supervisor_settings
|
77
|
+
rescue StandardError => e
|
78
|
+
notify_error e, level: :internal
|
69
79
|
end
|
70
80
|
end
|
71
81
|
end
|
@@ -101,10 +111,8 @@ module RSMP
|
|
101
111
|
proxy.run # run until disconnected
|
102
112
|
rescue IOError => e
|
103
113
|
log "Stream error: #{e}", level: :warning
|
104
|
-
rescue SystemCallError => e # all ERRNO errors
|
105
|
-
log "Reader exception: #{e.to_s}", level: :error
|
106
114
|
rescue StandardError => e
|
107
|
-
|
115
|
+
notify_error e, level: :internal
|
108
116
|
ensure
|
109
117
|
begin
|
110
118
|
if @site_settings["reconnect_interval"] != :no
|
@@ -135,7 +143,7 @@ module RSMP
|
|
135
143
|
def starting
|
136
144
|
log "Starting site #{@site_settings["site_id"]}",
|
137
145
|
level: :info,
|
138
|
-
timestamp:
|
146
|
+
timestamp: @clock.now
|
139
147
|
end
|
140
148
|
|
141
149
|
def alarm
|
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -15,6 +15,11 @@ module RSMP
|
|
15
15
|
@site_id = nil
|
16
16
|
end
|
17
17
|
|
18
|
+
def inspect
|
19
|
+
"#<#{self.class.name}:#{self.object_id}, #{inspector(
|
20
|
+
:@acknowledgements,:@settings,:@site_settings,:@components
|
21
|
+
)}>"
|
22
|
+
end
|
18
23
|
def node
|
19
24
|
supervisor
|
20
25
|
end
|
@@ -31,7 +36,7 @@ module RSMP
|
|
31
36
|
|
32
37
|
def connection_complete
|
33
38
|
super
|
34
|
-
log "Connection to site #{@site_id} established", level: :info
|
39
|
+
log "Connection to site #{@site_id} established, using core #{@rsmp_version}, sxl #{@sxl} #{@site_sxl_version}", level: :info
|
35
40
|
end
|
36
41
|
|
37
42
|
def process_message message
|
@@ -43,6 +48,8 @@ module RSMP
|
|
43
48
|
will_not_handle message
|
44
49
|
when AggregatedStatus
|
45
50
|
process_aggregated_status message
|
51
|
+
when AggregatedStatusRequest
|
52
|
+
will_not_handle message
|
46
53
|
when Alarm
|
47
54
|
process_alarm message
|
48
55
|
when CommandResponse
|
@@ -66,18 +73,45 @@ module RSMP
|
|
66
73
|
end
|
67
74
|
|
68
75
|
def version_accepted message
|
69
|
-
|
76
|
+
if @settings['sites']
|
77
|
+
@site_settings = @settings['sites'][@site_id]
|
78
|
+
@site_settings = @settings['sites'][:any] unless @site_settings
|
79
|
+
if @site_settings
|
80
|
+
@sxl = @site_settings['sxl']
|
81
|
+
setup_components @site_settings['components']
|
82
|
+
else
|
83
|
+
dont_acknowledge message, 'Rejected', "No config found for site #{@site_id}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
log "Received Version message for site #{@site_id}", message: message, level: :log
|
70
88
|
start_timer
|
71
89
|
acknowledge message
|
72
90
|
send_version @site_id, @settings['rsmp_versions']
|
73
91
|
@version_determined = true
|
74
92
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
93
|
+
end
|
94
|
+
|
95
|
+
def request_aggregated_status component, options={}
|
96
|
+
raise NotReady unless ready?
|
97
|
+
m_id = options[:m_id] || RSMP::Message.make_m_id
|
98
|
+
|
99
|
+
message = RSMP::AggregatedStatusRequest.new({
|
100
|
+
"ntsOId" => '',
|
101
|
+
"xNId" => '',
|
102
|
+
"cId" => component,
|
103
|
+
"mId" => m_id
|
104
|
+
})
|
105
|
+
if options[:collect]
|
106
|
+
result = nil
|
107
|
+
task = @task.async do |task|
|
108
|
+
wait_for_aggregated_status task, options[:collect]
|
80
109
|
end
|
110
|
+
send_message message, validate: options[:validate]
|
111
|
+
return message, task.wait
|
112
|
+
else
|
113
|
+
send_message message, validate: options[:validate]
|
114
|
+
message
|
81
115
|
end
|
82
116
|
end
|
83
117
|
|
@@ -159,10 +193,16 @@ module RSMP
|
|
159
193
|
collect_options = options[:collect].merge status_list: status_list
|
160
194
|
collect_status_responses task, collect_options, m_id
|
161
195
|
end
|
162
|
-
send_message message
|
163
|
-
|
196
|
+
send_message message, validate: options[:validate]
|
197
|
+
|
198
|
+
# task.wait return the result of the task. if the task raised an exception
|
199
|
+
# it will be reraised. but that mechanish does not work if multiple values
|
200
|
+
# are returned. so manually raise if first element is an exception
|
201
|
+
result = task.wait
|
202
|
+
raise result.first if result.first.is_a? Exception
|
203
|
+
return message, *result
|
164
204
|
else
|
165
|
-
send_message message
|
205
|
+
send_message message, validate: options[:validate]
|
166
206
|
message
|
167
207
|
end
|
168
208
|
end
|
@@ -193,15 +233,21 @@ module RSMP
|
|
193
233
|
collect_options = options[:collect].merge status_list: status_list
|
194
234
|
collect_status_updates task, collect_options, m_id
|
195
235
|
end
|
196
|
-
send_message message
|
197
|
-
|
236
|
+
send_message message, validate: options[:validate]
|
237
|
+
|
238
|
+
# task.wait return the result of the task. if the task raised an exception
|
239
|
+
# it will be reraised. but that mechanish does not work if multiple values
|
240
|
+
# are returned. so manually raise if first element is an exception
|
241
|
+
result = task.wait
|
242
|
+
raise result.first if result.first.is_a? Exception
|
243
|
+
return message, *result
|
198
244
|
else
|
199
|
-
send_message message
|
245
|
+
send_message message, validate: options[:validate]
|
200
246
|
message
|
201
247
|
end
|
202
248
|
end
|
203
249
|
|
204
|
-
def unsubscribe_to_status component, status_list
|
250
|
+
def unsubscribe_to_status component, status_list, options={}
|
205
251
|
raise NotReady unless ready?
|
206
252
|
message = RSMP::StatusUnsubscribe.new({
|
207
253
|
"ntsOId" => '',
|
@@ -209,7 +255,7 @@ module RSMP
|
|
209
255
|
"cId" => component,
|
210
256
|
"sS" => status_list
|
211
257
|
})
|
212
|
-
send_message message
|
258
|
+
send_message message, validate: options[:validate]
|
213
259
|
message
|
214
260
|
end
|
215
261
|
|
@@ -218,7 +264,7 @@ module RSMP
|
|
218
264
|
acknowledge message
|
219
265
|
end
|
220
266
|
|
221
|
-
def send_alarm_acknowledgement component, alarm_code
|
267
|
+
def send_alarm_acknowledgement component, alarm_code, options={}
|
222
268
|
message = RSMP::AlarmAcknowledged.new({
|
223
269
|
"ntsOId" => '',
|
224
270
|
"xNId" => '',
|
@@ -228,7 +274,7 @@ module RSMP
|
|
228
274
|
"xNACId" => '',
|
229
275
|
"aSp" => 'Acknowledge'
|
230
276
|
})
|
231
|
-
send_message message
|
277
|
+
send_message message, validate: options[:validate]
|
232
278
|
message
|
233
279
|
end
|
234
280
|
|
@@ -248,10 +294,16 @@ module RSMP
|
|
248
294
|
collect_options = options[:collect].merge command_list: command_list
|
249
295
|
collect_command_responses task, collect_options, m_id
|
250
296
|
end
|
251
|
-
send_message message
|
252
|
-
|
297
|
+
send_message message, validate: options[:validate]
|
298
|
+
|
299
|
+
# task.wait return the result of the task. if the task raised an exception
|
300
|
+
# it will be reraised. but that mechanish does not work if multiple values
|
301
|
+
# are returned. so manually raise if first element is an exception
|
302
|
+
result = task.wait
|
303
|
+
raise result.first if result.first.is_a? Exception
|
304
|
+
return message, *result
|
253
305
|
else
|
254
|
-
send_message message
|
306
|
+
send_message message, validate: options[:validate]
|
255
307
|
message
|
256
308
|
end
|
257
309
|
end
|
@@ -261,6 +313,14 @@ module RSMP
|
|
261
313
|
end
|
262
314
|
|
263
315
|
def check_sxl_version message
|
316
|
+
|
317
|
+
# check that we have a schema for specified sxl type and version
|
318
|
+
# note that the type comes from the site config, while the version
|
319
|
+
# comes from the Version message send by the site
|
320
|
+
type = 'tlc'
|
321
|
+
version = message.attribute 'SXL'
|
322
|
+
RSMP::Schemer::find_schema! type, version
|
323
|
+
|
264
324
|
# store sxl version requested by site
|
265
325
|
# TODO should check agaist site settings
|
266
326
|
@site_sxl_version = message.attribute 'SXL'
|
@@ -278,6 +338,8 @@ module RSMP
|
|
278
338
|
check_rsmp_version message
|
279
339
|
check_sxl_version message
|
280
340
|
version_accepted message
|
341
|
+
rescue RSMP::Schemer::UnknownSchemaError => e
|
342
|
+
dont_acknowledge message, "Rejected #{message.type} message,", "#{e}"
|
281
343
|
end
|
282
344
|
|
283
345
|
def check_site_ids message
|
@@ -288,5 +350,9 @@ module RSMP
|
|
288
350
|
site_ids_changed
|
289
351
|
end
|
290
352
|
|
353
|
+
def notify_error e, options={}
|
354
|
+
@supervisor.notify_error e, options if @supervisor
|
355
|
+
end
|
356
|
+
|
291
357
|
end
|
292
358
|
end
|
data/lib/rsmp/site_proxy_wait.rb
CHANGED
@@ -20,12 +20,12 @@ module RSMP
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def wait_for_alarm options={}
|
23
|
+
def wait_for_alarm parent_task, options={}
|
24
24
|
matching_alarm = nil
|
25
|
-
|
25
|
+
message = collect(parent_task,options.merge(type: "Alarm", with_message: true, num: 1)) do |message|
|
26
26
|
# TODO check components
|
27
27
|
matching_alarm = nil
|
28
|
-
alarm =
|
28
|
+
alarm = message
|
29
29
|
next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
|
30
30
|
next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
|
31
31
|
next if options[:aS] && options[:aS] != alarm.attribute("aS")
|
@@ -33,7 +33,7 @@ module RSMP
|
|
33
33
|
break
|
34
34
|
end
|
35
35
|
if item
|
36
|
-
{ message:
|
36
|
+
{ message: message, status: matching_alarm }
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -49,11 +49,11 @@ module RSMP
|
|
49
49
|
task.annotate "wait for command response"
|
50
50
|
want = options[:command_list].clone
|
51
51
|
result = {}
|
52
|
-
|
52
|
+
messages = []
|
53
|
+
collect(parent_task,options.merge({
|
53
54
|
type: ['CommandResponse','MessageNotAck'],
|
54
55
|
num: 1
|
55
|
-
})) do |
|
56
|
-
message = item[:message]
|
56
|
+
})) do |message|
|
57
57
|
if message.is_a?(MessageNotAck)
|
58
58
|
if message.attribute('oMId') == m_id
|
59
59
|
# set result to an exception, but don't raise it.
|
@@ -68,39 +68,38 @@ module RSMP
|
|
68
68
|
false
|
69
69
|
end
|
70
70
|
else
|
71
|
-
|
71
|
+
add = false
|
72
72
|
# look through querues
|
73
73
|
want.each_with_index do |query,i|
|
74
74
|
# look through items in message
|
75
|
-
|
76
|
-
|
77
|
-
if
|
75
|
+
message.attributes['rvs'].each do |input|
|
76
|
+
matching = command_match? query, input
|
77
|
+
if matching == true
|
78
78
|
result[query] = input
|
79
|
-
|
79
|
+
add = true
|
80
|
+
elsif matching == false
|
81
|
+
result.delete query
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
83
|
-
|
84
|
-
|
85
|
-
want.delete_at i
|
86
|
-
end
|
87
|
-
want.empty? # any queries left to match?
|
85
|
+
messages << message if add
|
86
|
+
result.size == want.size # any queries left to match?
|
88
87
|
end
|
89
88
|
end
|
90
|
-
result
|
89
|
+
return result, messages
|
91
90
|
rescue Async::TimeoutError
|
92
|
-
raise RSMP::TimeoutError.new "Did not receive command response to #{m_id} within #{options[:timeout]}s"
|
91
|
+
raise RSMP::TimeoutError.new "Did not receive correct command response to #{m_id} within #{options[:timeout]}s"
|
93
92
|
end
|
94
93
|
|
95
94
|
def collect_status_updates_or_responses task, type, options, m_id
|
96
|
-
want = options[:status_list]
|
95
|
+
want = options[:status_list].clone
|
97
96
|
result = {}
|
97
|
+
messages = []
|
98
98
|
# wait for a status update
|
99
|
-
|
99
|
+
collect(task,options.merge({
|
100
100
|
type: [type,'MessageNotAck'],
|
101
101
|
num: 1
|
102
|
-
})) do |
|
103
|
-
message = item[:message]
|
102
|
+
})) do |message|
|
104
103
|
if message.is_a?(MessageNotAck)
|
105
104
|
if message.attribute('oMId') == m_id
|
106
105
|
# set result to an exception, but don't raise it.
|
@@ -115,33 +114,33 @@ module RSMP
|
|
115
114
|
false
|
116
115
|
else
|
117
116
|
found = []
|
117
|
+
add = false
|
118
118
|
# look through querues
|
119
119
|
want.each_with_index do |query,i|
|
120
120
|
# look through status items in message
|
121
|
-
|
122
|
-
|
123
|
-
if
|
121
|
+
message.attributes['sS'].each do |input|
|
122
|
+
matching = status_match? query, input
|
123
|
+
if matching == true
|
124
124
|
result[query] = input
|
125
|
-
|
125
|
+
add = true
|
126
|
+
elsif matching == false
|
127
|
+
result.delete query
|
126
128
|
end
|
127
129
|
end
|
128
130
|
end
|
129
|
-
|
130
|
-
|
131
|
-
want.delete_at i
|
132
|
-
end
|
133
|
-
want.empty? # any queries left to match?
|
131
|
+
messages << message if add
|
132
|
+
result.size == want.size # any queries left to match?
|
134
133
|
end
|
135
134
|
end
|
136
|
-
result
|
135
|
+
return result, messages
|
137
136
|
rescue Async::TimeoutError
|
138
137
|
type_str = {'StatusUpdate'=>'update', 'StatusResponse'=>'response'}[type]
|
139
|
-
raise RSMP::TimeoutError.new "Did not received status #{type_str} in reply to #{m_id} within #{options[:timeout]}s"
|
138
|
+
raise RSMP::TimeoutError.new "Did not received correct status #{type_str} in reply to #{m_id} within #{options[:timeout]}s"
|
140
139
|
end
|
141
140
|
|
142
141
|
def status_match? query, item
|
143
|
-
return
|
144
|
-
return
|
142
|
+
return nil if query['sCI'] && query['sCI'] != item['sCI']
|
143
|
+
return nil if query['n'] && query['n'] != item['n']
|
145
144
|
return false if query['q'] && query['q'] != item['q']
|
146
145
|
if query['s'].is_a? Regexp
|
147
146
|
return false if query['s'] && item['s'] !~ query['s']
|
@@ -152,8 +151,8 @@ module RSMP
|
|
152
151
|
end
|
153
152
|
|
154
153
|
def command_match? query, item
|
155
|
-
return
|
156
|
-
return
|
154
|
+
return nil if query['cCI'] && query['cCI'] != item['cCI']
|
155
|
+
return nil if query['n'] && query['n'] != item['n']
|
157
156
|
if query['v'].is_a? Regexp
|
158
157
|
return false if query['v'] && item['v'] !~ query['v']
|
159
158
|
else
|
@@ -168,6 +167,8 @@ module RSMP
|
|
168
167
|
# wait for command responses in an async task
|
169
168
|
task = parent_task.async do |task|
|
170
169
|
collect_block.call task, m_id
|
170
|
+
rescue StandardError => e
|
171
|
+
notify_error e, level: :internal
|
171
172
|
end
|
172
173
|
|
173
174
|
# call block, it should send command request using the given m_id
|
@@ -177,5 +178,29 @@ module RSMP
|
|
177
178
|
task.wait
|
178
179
|
end
|
179
180
|
|
181
|
+
def wait_for_aggregated_status parent_task, options={}
|
182
|
+
collect(parent_task,options.merge({
|
183
|
+
type: ['AggregatedStatus','MessageNotAck'],
|
184
|
+
num: 1
|
185
|
+
})) do |message|
|
186
|
+
if message.is_a?(MessageNotAck)
|
187
|
+
if message.attribute('oMId') == m_id
|
188
|
+
# set result to an exception, but don't raise it.
|
189
|
+
# this will be returned by the task and stored as the task result
|
190
|
+
# when the parent task call wait() on the task, the exception
|
191
|
+
# will be raised in the parent task, and caught by rspec.
|
192
|
+
# rspec will then show the error and record the test as failed
|
193
|
+
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
194
|
+
result = RSMP::MessageRejected.new "Aggregated status request #{m_id_short} was rejected: #{message.attribute('rea')}"
|
195
|
+
next true # done, no more messages wanted
|
196
|
+
else
|
197
|
+
false
|
198
|
+
end
|
199
|
+
else
|
200
|
+
true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
180
205
|
end
|
181
206
|
end
|