rsmp 0.1.19 → 0.1.21
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/Gemfile.lock +10 -10
- data/documentation/{classes.md → classes_and_modules.md} +25 -22
- data/documentation/message_distribution.md +23 -0
- data/lib/rsmp.rb +9 -5
- data/lib/rsmp/archive.rb +5 -13
- data/lib/rsmp/{probe.rb → collector.rb} +31 -42
- data/lib/rsmp/{site_base.rb → components.rb} +2 -2
- data/lib/rsmp/listener.rb +33 -0
- data/lib/rsmp/logger.rb +3 -3
- data/lib/rsmp/{base.rb → logging.rb} +2 -2
- data/lib/rsmp/node.rb +4 -6
- data/lib/rsmp/notifier.rb +24 -0
- data/lib/rsmp/proxy.rb +25 -5
- data/lib/rsmp/site.rb +2 -2
- data/lib/rsmp/site_proxy.rb +66 -84
- data/lib/rsmp/site_proxy_wait.rb +181 -0
- data/lib/rsmp/supervisor.rb +2 -2
- data/lib/rsmp/supervisor_proxy.rb +0 -1
- data/lib/rsmp/tlc.rb +20 -4
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp/wait.rb +4 -151
- data/rsmp.gemspec +4 -4
- metadata +22 -19
- data/lib/rsmp/probe_collection.rb +0 -28
@@ -0,0 +1,24 @@
|
|
1
|
+
# Distributes messages to listeners
|
2
|
+
|
3
|
+
module RSMP
|
4
|
+
module Notifier
|
5
|
+
|
6
|
+
def initialize_distributor
|
7
|
+
@listeners = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_listener listener
|
11
|
+
raise ArgumentError unless listener
|
12
|
+
@listeners << listener unless @listeners.include? listener
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_listener listener
|
16
|
+
raise ArgumentError unless listener
|
17
|
+
@listeners.delete listener
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify item
|
21
|
+
@listeners.each { |listener| listener.notify item }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/rsmp/proxy.rb
CHANGED
@@ -1,22 +1,40 @@
|
|
1
|
-
#
|
1
|
+
# Logging class for a connection to a remote site or supervisor.
|
2
2
|
|
3
3
|
module RSMP
|
4
|
-
class Proxy
|
4
|
+
class Proxy
|
5
|
+
include Logging
|
5
6
|
include Wait
|
7
|
+
include Notifier
|
6
8
|
|
7
|
-
attr_reader :state, :archive, :connection_info, :sxl, :task
|
9
|
+
attr_reader :state, :archive, :connection_info, :sxl, :task, :collector
|
8
10
|
|
9
11
|
def initialize options
|
10
|
-
|
12
|
+
initialize_logging options
|
11
13
|
@settings = options[:settings]
|
12
14
|
@task = options[:task]
|
13
15
|
@socket = options[:socket]
|
14
16
|
@ip = options[:ip]
|
15
17
|
@connection_info = options[:info]
|
16
18
|
@sxl = nil
|
19
|
+
initialize_distributor
|
20
|
+
|
21
|
+
prepare_collection options[:settings]['collect']
|
22
|
+
|
17
23
|
clear
|
18
24
|
end
|
19
25
|
|
26
|
+
def prepare_collection num
|
27
|
+
if num
|
28
|
+
@collector = RSMP::Collector.new self, num: num, ingoing: true, outgoing: true
|
29
|
+
add_listener @collector
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def collect task, options, &block
|
34
|
+
probe = RSMP::Collector.new self, options
|
35
|
+
probe.collect task, &block
|
36
|
+
end
|
37
|
+
|
20
38
|
def run
|
21
39
|
start
|
22
40
|
@reader.wait if @reader
|
@@ -213,6 +231,7 @@ module RSMP
|
|
213
231
|
message.direction = :out
|
214
232
|
expect_acknowledgement message
|
215
233
|
@protocol.write_lines message.json
|
234
|
+
notify message: message
|
216
235
|
log_send message, reason
|
217
236
|
rescue EOFError, IOError
|
218
237
|
buffer_message message
|
@@ -243,6 +262,7 @@ module RSMP
|
|
243
262
|
attributes = Message.parse_attributes json
|
244
263
|
message = Message.build attributes, json
|
245
264
|
message.validate sxl
|
265
|
+
notify message: message
|
246
266
|
expect_version_message(message) unless @version_determined
|
247
267
|
process_message message
|
248
268
|
process_deferred
|
@@ -342,7 +362,7 @@ module RSMP
|
|
342
362
|
def wait_for_state state, timeout
|
343
363
|
states = [state].flatten
|
344
364
|
return if states.include?(@state)
|
345
|
-
wait_for(@state_condition,timeout) do
|
365
|
+
wait_for(@state_condition,timeout) do
|
346
366
|
states.include?(@state)
|
347
367
|
end
|
348
368
|
@state
|
data/lib/rsmp/site.rb
CHANGED
@@ -4,12 +4,12 @@
|
|
4
4
|
|
5
5
|
module RSMP
|
6
6
|
class Site < Node
|
7
|
-
include
|
7
|
+
include Components
|
8
8
|
|
9
9
|
attr_reader :rsmp_versions, :site_settings, :logger, :proxies
|
10
10
|
|
11
11
|
def initialize options={}
|
12
|
-
|
12
|
+
initialize_components
|
13
13
|
handle_site_settings options
|
14
14
|
super options
|
15
15
|
@proxies = []
|
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
module RSMP
|
4
4
|
class SiteProxy < Proxy
|
5
|
-
include
|
5
|
+
include Components
|
6
|
+
include SiteProxyWait
|
6
7
|
|
7
8
|
attr_reader :supervisor, :site_id
|
8
9
|
|
9
10
|
def initialize options
|
10
11
|
super options
|
11
|
-
|
12
|
+
initialize_components
|
12
13
|
@supervisor = options[:supervisor]
|
13
14
|
@settings = @supervisor.supervisor_settings.clone
|
14
15
|
@site_id = nil
|
@@ -55,6 +56,11 @@ module RSMP
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
59
|
+
def process_command_response message
|
60
|
+
log "Received #{message.type}", message: message, level: :log
|
61
|
+
acknowledge message
|
62
|
+
end
|
63
|
+
|
58
64
|
def process_deferred
|
59
65
|
supervisor.process_deferred
|
60
66
|
end
|
@@ -132,38 +138,33 @@ module RSMP
|
|
132
138
|
@supervisor.site_ids_changed
|
133
139
|
end
|
134
140
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
-
end
|
141
|
+
def request_status component, status_list, options={}
|
142
|
+
raise NotReady unless ready?
|
143
|
+
m_id = options[:m_id] || RSMP::Message.make_m_id
|
140
144
|
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
145
|
+
# additional items can be used when verifying the response,
|
146
|
+
# but must to remove from the request
|
147
|
+
request_list = status_list.map { |item| item.slice('sCI','n') }
|
155
148
|
|
156
|
-
def request_status options
|
157
|
-
raise NotReady unless ready?
|
158
149
|
message = RSMP::StatusRequest.new({
|
159
150
|
"ntsOId" => '',
|
160
151
|
"xNId" => '',
|
161
|
-
"cId" =>
|
162
|
-
"sS" =>
|
163
|
-
"mId" =>
|
152
|
+
"cId" => component,
|
153
|
+
"sS" => request_list,
|
154
|
+
"mId" => m_id
|
164
155
|
})
|
165
|
-
|
166
|
-
|
156
|
+
if options[:collect]
|
157
|
+
result = nil
|
158
|
+
task = @task.async do |task|
|
159
|
+
collect_options = options[:collect].merge status_list: status_list
|
160
|
+
collect_status_responses task, collect_options, m_id
|
161
|
+
end
|
162
|
+
send_message message
|
163
|
+
return message, task.wait
|
164
|
+
else
|
165
|
+
send_message message
|
166
|
+
message
|
167
|
+
end
|
167
168
|
end
|
168
169
|
|
169
170
|
def process_status_response message
|
@@ -173,15 +174,31 @@ module RSMP
|
|
173
174
|
|
174
175
|
def subscribe_to_status component, status_list, options={}
|
175
176
|
raise NotReady unless ready?
|
177
|
+
m_id = options[:m_id] || RSMP::Message.make_m_id
|
178
|
+
|
179
|
+
# additional items can be used when verifying the response,
|
180
|
+
# but must to remove from the subscribe message
|
181
|
+
subscribe_list = status_list.map { |item| item.slice('sCI','n','uRt') }
|
182
|
+
|
176
183
|
message = RSMP::StatusSubscribe.new({
|
177
184
|
"ntsOId" => '',
|
178
185
|
"xNId" => '',
|
179
186
|
"cId" => component,
|
180
|
-
"sS" =>
|
181
|
-
'mId'=>
|
187
|
+
"sS" => subscribe_list,
|
188
|
+
'mId' => m_id
|
182
189
|
})
|
183
|
-
|
184
|
-
|
190
|
+
if options[:collect]
|
191
|
+
result = nil
|
192
|
+
task = @task.async do |task|
|
193
|
+
collect_options = options[:collect].merge status_list: status_list
|
194
|
+
collect_status_updates task, collect_options, m_id
|
195
|
+
end
|
196
|
+
send_message message
|
197
|
+
return message, task.wait
|
198
|
+
else
|
199
|
+
send_message message
|
200
|
+
message
|
201
|
+
end
|
185
202
|
end
|
186
203
|
|
187
204
|
def unsubscribe_to_status component, status_list
|
@@ -190,7 +207,7 @@ module RSMP
|
|
190
207
|
"ntsOId" => '',
|
191
208
|
"xNId" => '',
|
192
209
|
"cId" => component,
|
193
|
-
"sS" =>
|
210
|
+
"sS" => status_list
|
194
211
|
})
|
195
212
|
send_message message
|
196
213
|
message
|
@@ -201,36 +218,6 @@ module RSMP
|
|
201
218
|
acknowledge message
|
202
219
|
end
|
203
220
|
|
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]
|
212
|
-
end
|
213
|
-
true
|
214
|
-
end
|
215
|
-
|
216
|
-
def wait_for_alarm options={}
|
217
|
-
raise ArgumentError.new("component argument is missing") unless options[:component]
|
218
|
-
matching_alarm = nil
|
219
|
-
item = @archive.capture(@task,options.merge(type: "Alarm", with_message: true, num: 1)) do |item|
|
220
|
-
# TODO check components
|
221
|
-
matching_alarm = nil
|
222
|
-
alarm = item[:message]
|
223
|
-
next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
|
224
|
-
next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
|
225
|
-
next if options[:aS] && options[:aS] != alarm.attribute("aS")
|
226
|
-
matching_alarm = alarm
|
227
|
-
break
|
228
|
-
end
|
229
|
-
if item
|
230
|
-
{ message: item[:message], status: matching_alarm }
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
221
|
def send_alarm_acknowledgement component, alarm_code
|
235
222
|
message = RSMP::AlarmAcknowledged.new({
|
236
223
|
"ntsOId" => '',
|
@@ -245,33 +232,28 @@ module RSMP
|
|
245
232
|
message
|
246
233
|
end
|
247
234
|
|
248
|
-
def
|
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
|
260
|
-
end
|
261
|
-
item[:message] if item
|
262
|
-
end
|
263
|
-
|
264
|
-
def send_command component, args, options={}
|
235
|
+
def send_command component, command_list, options={}
|
265
236
|
raise NotReady unless ready?
|
237
|
+
m_id = options[:m_id] || RSMP::Message.make_m_id
|
266
238
|
message = RSMP::CommandRequest.new({
|
267
239
|
"ntsOId" => '',
|
268
240
|
"xNId" => '',
|
269
241
|
"cId" => component,
|
270
|
-
"arg" =>
|
271
|
-
"mId" =>
|
242
|
+
"arg" => command_list,
|
243
|
+
"mId" => m_id
|
272
244
|
})
|
273
|
-
|
274
|
-
|
245
|
+
if options[:collect]
|
246
|
+
result = nil
|
247
|
+
task = @task.async do |task|
|
248
|
+
collect_options = options[:collect].merge command_list: command_list
|
249
|
+
collect_command_responses task, collect_options, m_id
|
250
|
+
end
|
251
|
+
send_message message
|
252
|
+
return message, task.wait
|
253
|
+
else
|
254
|
+
send_message message
|
255
|
+
message
|
256
|
+
end
|
275
257
|
end
|
276
258
|
|
277
259
|
def set_watchdog_interval interval
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# waiting for various types of messages and reponses from remote sites
|
2
|
+
module RSMP
|
3
|
+
module SiteProxyWait
|
4
|
+
|
5
|
+
def wait_for_status_updates parent_task, options={}, &send_block
|
6
|
+
send_while_collecting parent_task, send_block do |task, m_id|
|
7
|
+
collect_status_updates_or_responses task, 'StatusUpdate', options, m_id
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def wait_for_status_responses parent_task, options={}, &send_block
|
12
|
+
send_while_collecting parent_task, send_block do |task, m_id|
|
13
|
+
collect_status_updates_or_responses task, 'StatusResponse', options, m_id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def wait_for_command_responses parent_task, options={}, &send_block
|
18
|
+
send_while_collecting parent_task, send_block do |task, m_id|
|
19
|
+
collect_command_responses task, options, m_id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def wait_for_alarm options={}
|
24
|
+
matching_alarm = nil
|
25
|
+
item = collect(@task,options.merge(type: "Alarm", with_message: true, num: 1)) do |item|
|
26
|
+
# TODO check components
|
27
|
+
matching_alarm = nil
|
28
|
+
alarm = item[:message]
|
29
|
+
next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
|
30
|
+
next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
|
31
|
+
next if options[:aS] && options[:aS] != alarm.attribute("aS")
|
32
|
+
matching_alarm = alarm
|
33
|
+
break
|
34
|
+
end
|
35
|
+
if item
|
36
|
+
{ message: item[:message], status: matching_alarm }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def collect_status_updates task, options, m_id
|
41
|
+
collect_status_updates_or_responses task, 'StatusUpdate', options, m_id
|
42
|
+
end
|
43
|
+
|
44
|
+
def collect_status_responses task, options, m_id
|
45
|
+
collect_status_updates_or_responses task, 'StatusResponse', options, m_id
|
46
|
+
end
|
47
|
+
|
48
|
+
def collect_command_responses parent_task, options, m_id
|
49
|
+
task.annotate "wait for command response"
|
50
|
+
want = options[:command_list].clone
|
51
|
+
result = {}
|
52
|
+
item = collect(parent_task,options.merge({
|
53
|
+
type: ['CommandResponse','MessageNotAck'],
|
54
|
+
num: 1
|
55
|
+
})) do |item|
|
56
|
+
message = item[:message]
|
57
|
+
if message.is_a?(MessageNotAck)
|
58
|
+
if message.attribute('oMId') == m_id
|
59
|
+
# set result to an exception, but don't raise it.
|
60
|
+
# this will be returned by the task and stored as the task result
|
61
|
+
# when the parent task call wait() on the task, the exception
|
62
|
+
# will be raised in the parent task, and caught by rspec.
|
63
|
+
# rspec will then show the error and record the test as failed
|
64
|
+
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
65
|
+
result = RSMP::MessageRejected.new "Command request #{m_id_short} was rejected: #{message.attribute('rea')}"
|
66
|
+
next true # done, no more messages wanted
|
67
|
+
else
|
68
|
+
false
|
69
|
+
end
|
70
|
+
else
|
71
|
+
found = []
|
72
|
+
# look through querues
|
73
|
+
want.each_with_index do |query,i|
|
74
|
+
# look through items in message
|
75
|
+
item[:message].attributes['rvs'].each do |input|
|
76
|
+
ok = command_match? query, input
|
77
|
+
if ok
|
78
|
+
result[query] = input
|
79
|
+
found << i # record which queries where matched succesfully
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
# remove queries that where matched
|
84
|
+
found.sort.reverse.each do |i|
|
85
|
+
want.delete_at i
|
86
|
+
end
|
87
|
+
want.empty? # any queries left to match?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
result
|
91
|
+
rescue Async::TimeoutError
|
92
|
+
raise RSMP::TimeoutError.new "Did not receive command response to #{m_id} within #{options[:timeout]}s"
|
93
|
+
end
|
94
|
+
|
95
|
+
def collect_status_updates_or_responses task, type, options, m_id
|
96
|
+
want = options[:status_list]
|
97
|
+
result = {}
|
98
|
+
# wait for a status update
|
99
|
+
item = collect(task,options.merge({
|
100
|
+
type: [type,'MessageNotAck'],
|
101
|
+
num: 1
|
102
|
+
})) do |item|
|
103
|
+
message = item[:message]
|
104
|
+
if message.is_a?(MessageNotAck)
|
105
|
+
if message.attribute('oMId') == m_id
|
106
|
+
# set result to an exception, but don't raise it.
|
107
|
+
# this will be returned by the task and stored as the task result
|
108
|
+
# when the parent task call wait() on the task, the exception
|
109
|
+
# will be raised in the parent task, and caught by rspec.
|
110
|
+
# rspec will then show the error and record the test as failed
|
111
|
+
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
112
|
+
result = RSMP::MessageRejected.new "Status request #{m_id_short} was rejected: #{message.attribute('rea')}"
|
113
|
+
next true # done, no more messages wanted
|
114
|
+
end
|
115
|
+
false
|
116
|
+
else
|
117
|
+
found = []
|
118
|
+
# look through querues
|
119
|
+
want.each_with_index do |query,i|
|
120
|
+
# look through status items in message
|
121
|
+
item[:message].attributes['sS'].each do |input|
|
122
|
+
ok = status_match? query, input
|
123
|
+
if ok
|
124
|
+
result[query] = input
|
125
|
+
found << i # record which queries where matched succesfully
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
# remove queries that where matched
|
130
|
+
found.sort.reverse.each do |i|
|
131
|
+
want.delete_at i
|
132
|
+
end
|
133
|
+
want.empty? # any queries left to match?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
result
|
137
|
+
rescue Async::TimeoutError
|
138
|
+
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"
|
140
|
+
end
|
141
|
+
|
142
|
+
def status_match? query, item
|
143
|
+
return false if query['sCI'] && query['sCI'] != item['sCI']
|
144
|
+
return false if query['n'] && query['n'] != item['n']
|
145
|
+
return false if query['q'] && query['q'] != item['q']
|
146
|
+
if query['s'].is_a? Regexp
|
147
|
+
return false if query['s'] && item['s'] !~ query['s']
|
148
|
+
else
|
149
|
+
return false if query['s'] && item['s'] != query['s']
|
150
|
+
end
|
151
|
+
true
|
152
|
+
end
|
153
|
+
|
154
|
+
def command_match? query, item
|
155
|
+
return false if query['cCI'] && query['cCI'] != item['cCI']
|
156
|
+
return false if query['n'] && query['n'] != item['n']
|
157
|
+
if query['v'].is_a? Regexp
|
158
|
+
return false if query['v'] && item['v'] !~ query['v']
|
159
|
+
else
|
160
|
+
return false if query['v'] && item['v'] != query['v']
|
161
|
+
end
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
165
|
+
def send_while_collecting parent_task, send_block, &collect_block
|
166
|
+
m_id = RSMP::Message.make_m_id # make message id so we can start waiting for it
|
167
|
+
|
168
|
+
# wait for command responses in an async task
|
169
|
+
task = parent_task.async do |task|
|
170
|
+
collect_block.call task, m_id
|
171
|
+
end
|
172
|
+
|
173
|
+
# call block, it should send command request using the given m_id
|
174
|
+
send_block.call m_id
|
175
|
+
|
176
|
+
# wait for the response and return it, raise exception if NotAck received, it it timed out
|
177
|
+
task.wait
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|