rsmp 0.1.12 → 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.
@@ -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