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