rsmp 0.1.12 → 0.1.27

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
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 parent_task, options={}
24
+ matching_alarm = nil
25
+ message = collect(parent_task,options.merge(type: "Alarm", with_message: true, num: 1)) do |message|
26
+ # TODO check components
27
+ matching_alarm = nil
28
+ alarm = 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: 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
+ messages = []
53
+ collect(parent_task,options.merge({
54
+ type: ['CommandResponse','MessageNotAck'],
55
+ num: 1
56
+ })) do |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
+ add = false
72
+ # look through querues
73
+ want.each_with_index do |query,i|
74
+ # look through items in message
75
+ message.attributes['rvs'].each do |input|
76
+ matching = command_match? query, input
77
+ if matching == true
78
+ result[query] = input
79
+ add = true
80
+ elsif matching == false
81
+ result.delete query
82
+ end
83
+ end
84
+ end
85
+ messages << message if add
86
+ result.size == want.size # any queries left to match?
87
+ end
88
+ end
89
+ return result, messages
90
+ rescue Async::TimeoutError
91
+ raise RSMP::TimeoutError.new "Did not receive correct command response to #{m_id} within #{options[:timeout]}s"
92
+ end
93
+
94
+ def collect_status_updates_or_responses task, type, options, m_id
95
+ want = options[:status_list].clone
96
+ result = {}
97
+ messages = []
98
+ # wait for a status update
99
+ collect(task,options.merge({
100
+ type: [type,'MessageNotAck'],
101
+ num: 1
102
+ })) do |message|
103
+ if message.is_a?(MessageNotAck)
104
+ if message.attribute('oMId') == m_id
105
+ # set result to an exception, but don't raise it.
106
+ # this will be returned by the task and stored as the task result
107
+ # when the parent task call wait() on the task, the exception
108
+ # will be raised in the parent task, and caught by rspec.
109
+ # rspec will then show the error and record the test as failed
110
+ m_id_short = RSMP::Message.shorten_m_id m_id, 8
111
+ result = RSMP::MessageRejected.new "Status request #{m_id_short} was rejected: #{message.attribute('rea')}"
112
+ next true # done, no more messages wanted
113
+ end
114
+ false
115
+ else
116
+ found = []
117
+ add = false
118
+ # look through querues
119
+ want.each_with_index do |query,i|
120
+ # look through status items in message
121
+ message.attributes['sS'].each do |input|
122
+ matching = status_match? query, input
123
+ if matching == true
124
+ result[query] = input
125
+ add = true
126
+ elsif matching == false
127
+ result.delete query
128
+ end
129
+ end
130
+ end
131
+ messages << message if add
132
+ result.size == want.size # any queries left to match?
133
+ end
134
+ end
135
+ return result, messages
136
+ rescue Async::TimeoutError
137
+ type_str = {'StatusUpdate'=>'update', 'StatusResponse'=>'response'}[type]
138
+ raise RSMP::TimeoutError.new "Did not received correct status #{type_str} in reply to #{m_id} within #{options[:timeout]}s"
139
+ end
140
+
141
+ def status_match? query, item
142
+ return nil if query['sCI'] && query['sCI'] != item['sCI']
143
+ return nil if query['n'] && query['n'] != item['n']
144
+ return false if query['q'] && query['q'] != item['q']
145
+ if query['s'].is_a? Regexp
146
+ return false if query['s'] && item['s'] !~ query['s']
147
+ else
148
+ return false if query['s'] && item['s'] != query['s']
149
+ end
150
+ true
151
+ end
152
+
153
+ def command_match? query, item
154
+ return nil if query['cCI'] && query['cCI'] != item['cCI']
155
+ return nil if query['n'] && query['n'] != item['n']
156
+ if query['v'].is_a? Regexp
157
+ return false if query['v'] && item['v'] !~ query['v']
158
+ else
159
+ return false if query['v'] && item['v'] != query['v']
160
+ end
161
+ true
162
+ end
163
+
164
+ def send_while_collecting parent_task, send_block, &collect_block
165
+ m_id = RSMP::Message.make_m_id # make message id so we can start waiting for it
166
+
167
+ # wait for command responses in an async task
168
+ task = parent_task.async do |task|
169
+ collect_block.call task, m_id
170
+ rescue StandardError => e
171
+ notify_error e, level: :internal
172
+ end
173
+
174
+ # call block, it should send command request using the given m_id
175
+ send_block.call m_id
176
+
177
+ # wait for the response and return it, raise exception if NotAck received, it it timed out
178
+ task.wait
179
+ end
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
+
205
+ end
206
+ end
@@ -20,7 +20,7 @@ module RSMP
20
20
  def handle_supervisor_settings options
21
21
  @supervisor_settings = {
22
22
  'port' => 12111,
23
- 'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4'],
23
+ 'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4','3.1.5'],
24
24
  'timer_interval' => 0.1,
25
25
  'watchdog_interval' => 1,
26
26
  'watchdog_timeout' => 2,
@@ -32,7 +32,9 @@ module RSMP
32
32
  'site_ready_timeout' => 1,
33
33
  'stop_after_first_session' => false,
34
34
  'sites' => {
35
- :any => {}
35
+ :any => {
36
+ 'sxl' => 'tlc'
37
+ }
36
38
  }
37
39
  }
38
40
 
@@ -47,17 +49,35 @@ module RSMP
47
49
  check_required_settings @supervisor_settings, required
48
50
 
49
51
  @rsmp_versions = @supervisor_settings["rsmp_versions"]
52
+
53
+ check_site_sxl_types
54
+ end
55
+
56
+ def check_site_sxl_types
57
+ @supervisor_settings['sites'].each do |ip,settings|
58
+ unless settings
59
+ raise RSMP::ConfigurationError.new("Configuration for site '#{ip}' is empty")
60
+ end
61
+ sxl = settings['sxl']
62
+ sxl = 'tlc' unless sxl # temporary fix until configs are updated
63
+ unless sxl
64
+ raise RSMP::ConfigurationError.new("Configuration error for site '#{ip}': No SXL specified")
65
+ end
66
+ RSMP::Schemer.find_schemas! sxl if sxl
67
+ rescue RSMP::Schemer::UnknownSchemaError => e
68
+ raise RSMP::ConfigurationError.new("Configuration error for site '#{ip}': #{e}")
69
+ end
50
70
  end
51
71
 
52
72
  def start_action
53
73
  @endpoint = Async::IO::Endpoint.tcp('0.0.0.0', @supervisor_settings["port"])
54
- @endpoint.accept do |socket|
74
+ @endpoint.accept do |socket| # creates async tasks
55
75
  handle_connection(socket)
76
+ rescue StandardError => e
77
+ notify_error e, level: :internal
56
78
  end
57
- rescue SystemCallError => e # all ERRNO errors
58
- log "Exception: #{e.to_s}", level: :error
59
79
  rescue StandardError => e
60
- log ["Exception: #{e.inspect}",e.backtrace].flatten.join("\n"), level: :error
80
+ notify_error e, level: :internal
61
81
  end
62
82
 
63
83
  def stop
@@ -74,16 +94,17 @@ module RSMP
74
94
  remote_hostname = socket.remote_address.ip_address
75
95
  remote_ip = socket.remote_address.ip_address
76
96
 
77
- info = {ip:remote_ip, port:remote_port, hostname:remote_hostname, now:RSMP.now_string()}
97
+ info = {ip:remote_ip, port:remote_port, hostname:remote_hostname, now:Clock.now}
78
98
  if accept? socket, info
79
99
  connect socket, info
80
100
  else
81
101
  reject socket, info
82
102
  end
83
- rescue SystemCallError => e # all ERRNO errors
84
- log "Exception: #{e.to_s}", level: :error
103
+ rescue ConnectionError => e
104
+ log "Rejected connection from #{remote_ip}, #{e.to_s}", level: :info
85
105
  rescue StandardError => e
86
- log "Exception: #{e}", exception: e, level: :error
106
+ log "Connection: #{e.to_s}", exception: e, level: :error
107
+ notify_error e, level: :internal
87
108
  ensure
88
109
  close socket, info
89
110
  end
@@ -91,14 +112,14 @@ module RSMP
91
112
  def starting
92
113
  log "Starting supervisor on port #{@supervisor_settings["port"]}",
93
114
  level: :info,
94
- timestamp: RSMP.now_object
115
+ timestamp: @clock.now
95
116
  end
96
117
 
97
118
  def accept? socket, info
98
119
  true
99
120
  end
100
121
 
101
- def build_connector settings
122
+ def build_proxy settings
102
123
  SiteProxy.new settings
103
124
  end
104
125
 
@@ -115,20 +136,25 @@ module RSMP
115
136
  ip: info[:ip],
116
137
  port: info[:port],
117
138
  level: :info,
118
- timestamp: RSMP.now_object
139
+ timestamp: Clock.now
119
140
 
120
- proxy = build_connector({
141
+ settings = ip_to_site_settings info[:ip]
142
+ raise ConnectionError.new('unknown ip not allowed') unless settings
143
+
144
+ proxy = build_proxy({
121
145
  supervisor: self,
146
+ ip: info[:ip],
147
+ port: info[:port],
122
148
  task: @task,
123
- settings: @supervisor_settings[:sites],
149
+ settings: settings,
124
150
  socket: socket,
125
151
  info: info,
126
152
  logger: @logger,
127
153
  archive: @archive
128
154
  })
129
155
  @proxies.push proxy
130
-
131
156
  proxy.run # will run until the site disconnects
157
+ ensure
132
158
  @proxies.delete proxy
133
159
  site_ids_changed
134
160
 
@@ -145,9 +171,9 @@ module RSMP
145
171
 
146
172
  def close socket, info
147
173
  if info
148
- log "Connection to #{format_ip_and_port(info)} closed", ip: info[:ip], level: :info, timestamp: RSMP.now_object
174
+ log "Connection to #{format_ip_and_port(info)} closed", ip: info[:ip], level: :info, timestamp: Clock.now
149
175
  else
150
- log "Connection closed", level: :info, timestamp: RSMP.now_object
176
+ log "Connection closed", level: :info, timestamp: Clock.now
151
177
  end
152
178
 
153
179
  socket.close
@@ -167,15 +193,15 @@ module RSMP
167
193
  def wait_for_site site_id, timeout
168
194
  site = find_site site_id
169
195
  return site if site
170
- RSMP::Wait.wait_for(@task,@site_id_condition,timeout) { find_site site_id }
196
+ wait_for(@site_id_condition,timeout) { find_site site_id }
171
197
  rescue Async::TimeoutError
172
- nil
198
+ raise RSMP::TimeoutError.new "Site '#{site_id}'' did not connect within #{timeout}s"
173
199
  end
174
200
 
175
201
  def wait_for_site_disconnect site_id, timeout
176
- RSMP::Wait.wait_for(@task,@site_id_condition,timeout) { true unless find_site site_id }
202
+ wait_for(@site_id_condition,timeout) { true unless find_site site_id }
177
203
  rescue Async::TimeoutError
178
- false
204
+ raise RSMP::TimeoutError.new "Site '#{site_id}'' did not disconnect within #{timeout}s"
179
205
  end
180
206
 
181
207
  def check_site_id site_id
@@ -184,7 +210,7 @@ module RSMP
184
210
  end
185
211
 
186
212
  def check_site_already_connected site_id
187
- raise FatalError.new "Site #{site_id} already connected" if find_site(site_id)
213
+ raise FatalError.new "Site '#{site_id}'' already connected" if find_site(site_id)
188
214
  end
189
215
 
190
216
  def find_allowed_site_setting site_id
@@ -197,6 +223,10 @@ module RSMP
197
223
  raise FatalError.new "site id #{site_id} rejected"
198
224
  end
199
225
 
226
+ def ip_to_site_settings ip
227
+ @supervisor_settings['sites'][ip] || @supervisor_settings['sites'][:any]
228
+ end
229
+
200
230
  def aggregated_status_changed site_proxy, component
201
231
  end
202
232
 
@@ -14,7 +14,6 @@ module RSMP
14
14
  @ip = options[:ip]
15
15
  @port = options[:port]
16
16
  @status_subscriptions = {}
17
- @status_subscriptions_mutex = Mutex.new
18
17
  @sxl = @site_settings['sxl']
19
18
  @synthetic_id = Supervisor.build_id_from_ip_port @ip, @port
20
19
  end
@@ -28,6 +27,7 @@ module RSMP
28
27
  super
29
28
  connect
30
29
  @logger.unmute @ip, @port
30
+ log "Connected to superviser at #{@ip}:#{@port}", level: :info
31
31
  start_reader
32
32
  send_version @site_settings['site_id'], @site_settings["rsmp_versions"]
33
33
  rescue Errno::ECONNREFUSED
@@ -38,17 +38,23 @@ module RSMP
38
38
  end
39
39
  end
40
40
 
41
+ def stop
42
+ log "Closing connection to supervisor", level: :info
43
+ super
44
+ @last_status_sent = nil
45
+ end
46
+
41
47
  def connect
42
48
  return if @socket
43
49
  @endpoint = Async::IO::Endpoint.tcp(@ip, @port)
44
50
  @socket = @endpoint.connect
45
51
  @stream = Async::IO::Stream.new(@socket)
46
- @protocol = Async::IO::Protocol::Line.new(@stream,RSMP::WRAPPING_DELIMITER) # rsmp messages are json terminated with a form-feed
52
+ @protocol = Async::IO::Protocol::Line.new(@stream,WRAPPING_DELIMITER) # rsmp messages are json terminated with a form-feed
47
53
  end
48
54
 
49
55
  def connection_complete
50
56
  super
51
- log "Connection to supervisor established", level: :info
57
+ log "Connection to supervisor established, using core #{@rsmp_version}, #{sxl} #{sxl_version}", level: :info
52
58
  start_watchdog
53
59
  end
54
60
 
@@ -59,6 +65,8 @@ module RSMP
59
65
  when StatusUpdate
60
66
  when AggregatedStatus
61
67
  will_not_handle message
68
+ when AggregatedStatusRequest
69
+ process_aggregated_status_request message
62
70
  when CommandRequest
63
71
  process_command_request message
64
72
  when CommandResponse
@@ -72,6 +80,13 @@ module RSMP
72
80
  else
73
81
  super message
74
82
  end
83
+ rescue UnknownComponent, UnknownCommand, UnknownStatus,
84
+ MessageRejected, MissingAttribute => e
85
+ dont_acknowledge message, '', e.to_s
86
+ end
87
+
88
+ def process_deferred
89
+ site.process_deferred
75
90
  end
76
91
 
77
92
  def acknowledged_first_ingoing message
@@ -110,9 +125,9 @@ module RSMP
110
125
 
111
126
  def send_aggregated_status component
112
127
  message = AggregatedStatus.new({
113
- "aSTS" => RSMP.now_string,
128
+ "aSTS" => clock.to_s,
114
129
  "cId" => component.c_id,
115
- "fP" => nil,
130
+ "fP" => 'NormalControl',
116
131
  "fS" => nil,
117
132
  "se" => component.aggregated_status_bools
118
133
  })
@@ -135,48 +150,66 @@ module RSMP
135
150
  acknowledge message
136
151
  end
137
152
 
153
+ # reorganize rmsp command request arg attribute:
154
+ # [{"cCI":"M0002","cO":"setPlan","n":"status","v":"True"},{"cCI":"M0002","cO":"setPlan","n":"securityCode","v":"5678"},{"cCI":"M0002","cO":"setPlan","n":"timeplan","v":"3"}]
155
+ # into the simpler, but equivalent:
156
+ # {"M0002"=>{"status"=>"True", "securityCode"=>"5678", "timeplan"=>"3"}}
157
+ def simplify_command_requests arg
158
+ sorted = {}
159
+ arg.each do |item|
160
+ sorted[item['cCI']] ||= {}
161
+ sorted[item['cCI']][item['n']] = item['v']
162
+ end
163
+ sorted
164
+ end
165
+
166
+ def process_aggregated_status_request message
167
+ log "Received #{message.type}", message: message, level: :log
168
+ component_id = message.attributes["cId"]
169
+ component = @site.find_component component_id
170
+ acknowledge message
171
+ send_aggregated_status component
172
+ end
173
+
138
174
  def process_command_request message
139
175
  log "Received #{message.type}", message: message, level: :log
140
- rvs = []
141
- message.attributes["arg"].each do |arg|
142
- unless arg['cCI'] && arg['n'] && arg['v']
143
- dont_acknowledge message, '', 'bad arguments'
144
- return
145
- end
146
- rvs << { "cCI" => arg["cCI"],
147
- "n" => arg["n"],
148
- "v" => arg["v"],
149
- "age" => "recent" }
176
+ component_id = message.attributes["cId"]
177
+ component = @site.find_component component_id
178
+ commands = simplify_command_requests message.attributes["arg"]
179
+ commands.each_pair do |command_code,arg|
180
+ component.handle_command command_code,arg
150
181
  end
151
182
 
183
+ rvs = message.attributes["arg"].map do |item|
184
+ item = item.dup.merge('age'=>'recent')
185
+ item.delete 'cO'
186
+ item
187
+ end
152
188
  response = CommandResponse.new({
153
- "cId"=>message.attributes["cId"],
154
- "cTS"=>RSMP.now_string,
189
+ "cId"=>component_id,
190
+ "cTS"=>clock.to_s,
155
191
  "rvs"=>rvs
156
192
  })
157
193
  acknowledge message
158
194
  send_message response
159
195
  end
160
196
 
161
- def process_status_request message
197
+ def process_status_request message, options={}
162
198
  component_id = message.attributes["cId"]
163
199
  component = @site.find_component component_id
164
-
165
200
  log "Received #{message.type}", message: message, level: :log
166
- sS = message.attributes["sS"].clone.map do |request|
167
- request["s"] = rand(100).to_s
168
- request["q"] = "recent"
169
- request
201
+ sS = message.attributes["sS"].map do |arg|
202
+ value, quality = component.get_status arg['sCI'], arg['n']
203
+ { "s" => value.to_s, "q" => quality.to_s }.merge arg
170
204
  end
171
205
  response = StatusResponse.new({
172
206
  "cId"=>component_id,
173
- "sTs"=>RSMP.now_string,
174
- "sS"=>sS
207
+ "sTs"=>clock.to_s,
208
+ "sS"=>sS,
209
+ "mId" => options[:m_id]
175
210
  })
176
211
  acknowledge message
177
212
  send_message response
178
- rescue UnknownComponent => e
179
- dont_acknowledge message, '', e.to_s
180
213
  end
181
214
 
182
215
  def process_status_subcribe message
@@ -198,10 +231,11 @@ module RSMP
198
231
  update_list[component] ||= {}
199
232
 
200
233
  subs = @status_subscriptions[component]
234
+ now = Time.now # internal timestamp
201
235
 
202
236
  message.attributes["sS"].each do |arg|
203
237
  sCI = arg["sCI"]
204
- subcription = {interval: arg["uRt"].to_i, last_sent_at: nil}
238
+ subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
205
239
  subs[sCI] ||= {}
206
240
  subs[sCI][arg["n"]] = subcription
207
241
 
@@ -235,18 +269,40 @@ module RSMP
235
269
  status_update_timer now if ready?
236
270
  end
237
271
 
272
+ def fetch_last_sent_status component, code, name
273
+ if @last_status_sent && @last_status_sent[component] && @last_status_sent[component][code]
274
+ @last_status_sent[component][code][name]
275
+ else
276
+ nil
277
+ end
278
+ end
279
+
280
+ def store_last_sent_status component, code, name, value
281
+ @last_status_sent ||= {}
282
+ @last_status_sent[component] ||= {}
283
+ @last_status_sent[component][code] ||= {}
284
+ @last_status_sent[component][code][name] = value
285
+ end
286
+
238
287
  def status_update_timer now
239
288
  update_list = {}
240
289
  # go through subscriptons and build a similarly organized list,
241
290
  # that only contains what should be send
242
291
 
243
292
  @status_subscriptions.each_pair do |component,by_code|
293
+ component_object = @site.find_component component
244
294
  by_code.each_pair do |code,by_name|
245
295
  by_name.each_pair do |name,subscription|
296
+ current = nil
246
297
  if subscription[:interval] == 0
247
298
  # send as soon as the data changes
248
- if rand(100) >= 90
299
+ if component_object
300
+ current, age = *(component_object.get_status code, name)
301
+ end
302
+ last_sent = fetch_last_sent_status component, code, name
303
+ if current != last_sent
249
304
  should_send = true
305
+ store_last_sent_status component, code, name, current
250
306
  end
251
307
  else
252
308
  # send at regular intervals
@@ -257,31 +313,35 @@ module RSMP
257
313
  if should_send
258
314
  subscription[:last_sent_at] = now
259
315
  update_list[component] ||= {}
260
- update_list[component][code] ||= []
261
- update_list[component][code] << name
316
+ update_list[component][code] ||= {}
317
+ update_list[component][code][name] = current
262
318
  end
263
319
  end
264
320
  end
265
321
  end
266
322
  send_status_updates update_list
267
- rescue StandardError => e
268
- log ["Status update exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
269
323
  end
270
324
 
271
325
  def send_status_updates update_list
272
- now = RSMP.now_string
273
- update_list.each_pair do |component,by_code|
326
+ now = clock.to_s
327
+ update_list.each_pair do |component_id,by_code|
328
+ component = @site.find_component component_id
274
329
  sS = []
275
330
  by_code.each_pair do |code,names|
276
- names.each do |name|
331
+ names.map do |status_name,value|
332
+ if value
333
+ quality = 'recent'
334
+ else
335
+ value,quality = component.get_status code, status_name
336
+ end
277
337
  sS << { "sCI" => code,
278
- "n" => name,
279
- "s" => rand(100).to_s,
280
- "q" => "recent" }
338
+ "n" => status_name,
339
+ "s" => value.to_s,
340
+ "q" => quality }
281
341
  end
282
342
  end
283
343
  update = StatusUpdate.new({
284
- "cId"=>component,
344
+ "cId"=>component_id,
285
345
  "sTs"=>now,
286
346
  "sS"=>sS
287
347
  })
@@ -291,7 +351,7 @@ module RSMP
291
351
 
292
352
  def send_alarm
293
353
  message = Alarm.new({
294
- "aSTS"=>RSMP.now_string,
354
+ "aSTS"=>clock.to_s,
295
355
  "fP"=>nil,
296
356
  "fS"=>nil,
297
357
  "se"=>@site.aggregated_status_bools