rsmp 0.1.21 → 0.1.27

Sign up to get free protection for your applications and to get access to all the features.
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' => 'traffic_light_controller',
31
- 'sxl_version' => '1.0.7',
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 "site_proxy"
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
- log ["Reader exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
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: RSMP.now_object
146
+ timestamp: @clock.now
139
147
  end
140
148
 
141
149
  def alarm
@@ -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
- log "Received Version message for site #{@site_id} using RSMP #{@rsmp_version}", message: message, level: :log
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
- if @settings['sites']
76
- @site_settings = @settings['sites'][@site_id]
77
- @site_settings =@settings['sites'][:any] unless @site_settings
78
- if @site_settings
79
- setup_components @site_settings['components']
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
- return message, task.wait
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
- return message, task.wait
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
- return message, task.wait
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
@@ -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
- item = collect(@task,options.merge(type: "Alarm", with_message: true, num: 1)) do |item|
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 = item[:message]
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: item[:message], status: matching_alarm }
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
- item = collect(parent_task,options.merge({
52
+ messages = []
53
+ collect(parent_task,options.merge({
53
54
  type: ['CommandResponse','MessageNotAck'],
54
55
  num: 1
55
- })) do |item|
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
- found = []
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
- item[:message].attributes['rvs'].each do |input|
76
- ok = command_match? query, input
77
- if ok
75
+ message.attributes['rvs'].each do |input|
76
+ matching = command_match? query, input
77
+ if matching == true
78
78
  result[query] = input
79
- found << i # record which queries where matched succesfully
79
+ add = true
80
+ elsif matching == false
81
+ result.delete query
80
82
  end
81
83
  end
82
84
  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?
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
- item = collect(task,options.merge({
99
+ collect(task,options.merge({
100
100
  type: [type,'MessageNotAck'],
101
101
  num: 1
102
- })) do |item|
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
- item[:message].attributes['sS'].each do |input|
122
- ok = status_match? query, input
123
- if ok
121
+ message.attributes['sS'].each do |input|
122
+ matching = status_match? query, input
123
+ if matching == true
124
124
  result[query] = input
125
- found << i # record which queries where matched succesfully
125
+ add = true
126
+ elsif matching == false
127
+ result.delete query
126
128
  end
127
129
  end
128
130
  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?
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 false if query['sCI'] && query['sCI'] != item['sCI']
144
- return false if query['n'] && query['n'] != item['n']
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 false if query['cCI'] && query['cCI'] != item['cCI']
156
- return false if query['n'] && query['n'] != item['n']
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