rsmp 0.37.0 → 0.38.0
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/.devcontainer/devcontainer.json +22 -0
- data/.github/workflows/rubocop.yaml +17 -0
- data/.gitignore +5 -6
- data/.rubocop.yml +80 -0
- data/Gemfile +13 -1
- data/Gemfile.lock +34 -1
- data/Rakefile +3 -3
- data/lib/rsmp/cli.rb +147 -124
- data/lib/rsmp/collect/ack_collector.rb +8 -7
- data/lib/rsmp/collect/aggregated_status_collector.rb +4 -4
- data/lib/rsmp/collect/alarm_collector.rb +31 -23
- data/lib/rsmp/collect/alarm_matcher.rb +3 -3
- data/lib/rsmp/collect/collector/logging.rb +17 -0
- data/lib/rsmp/collect/collector/reporting.rb +44 -0
- data/lib/rsmp/collect/collector/status.rb +34 -0
- data/lib/rsmp/collect/collector.rb +69 -150
- data/lib/rsmp/collect/command_matcher.rb +19 -6
- data/lib/rsmp/collect/command_response_collector.rb +7 -7
- data/lib/rsmp/collect/distributor.rb +14 -11
- data/lib/rsmp/collect/filter.rb +31 -15
- data/lib/rsmp/collect/matcher.rb +7 -11
- data/lib/rsmp/collect/queue.rb +4 -4
- data/lib/rsmp/collect/receiver.rb +10 -12
- data/lib/rsmp/collect/state_collector.rb +116 -77
- data/lib/rsmp/collect/status_collector.rb +6 -6
- data/lib/rsmp/collect/status_matcher.rb +17 -7
- data/lib/rsmp/{alarm_state.rb → component/alarm_state.rb} +76 -37
- data/lib/rsmp/{component.rb → component/component.rb} +15 -15
- data/lib/rsmp/component/component_base.rb +89 -0
- data/lib/rsmp/component/component_proxy.rb +75 -0
- data/lib/rsmp/component/components.rb +63 -0
- data/lib/rsmp/convert/export/json_schema.rb +116 -110
- data/lib/rsmp/convert/import/yaml.rb +21 -18
- data/lib/rsmp/{rsmp.rb → helpers/clock.rb} +5 -6
- data/lib/rsmp/{deep_merge.rb → helpers/deep_merge.rb} +2 -1
- data/lib/rsmp/helpers/error.rb +71 -0
- data/lib/rsmp/{inspect.rb → helpers/inspect.rb} +6 -10
- data/lib/rsmp/log/archive.rb +98 -0
- data/lib/rsmp/log/colorization.rb +41 -0
- data/lib/rsmp/log/filtering.rb +54 -0
- data/lib/rsmp/log/logger.rb +206 -0
- data/lib/rsmp/{logging.rb → log/logging.rb} +5 -7
- data/lib/rsmp/message.rb +159 -148
- data/lib/rsmp/{node.rb → node/node.rb} +19 -17
- data/lib/rsmp/{protocol.rb → node/protocol.rb} +5 -3
- data/lib/rsmp/node/site/site.rb +195 -0
- data/lib/rsmp/node/supervisor/modules/configuration.rb +59 -0
- data/lib/rsmp/node/supervisor/modules/connection.rb +140 -0
- data/lib/rsmp/node/supervisor/modules/sites.rb +64 -0
- data/lib/rsmp/node/supervisor/supervisor.rb +72 -0
- data/lib/rsmp/{task.rb → node/task.rb} +12 -14
- data/lib/rsmp/proxy/modules/acknowledgements.rb +144 -0
- data/lib/rsmp/proxy/modules/receive.rb +119 -0
- data/lib/rsmp/proxy/modules/send.rb +76 -0
- data/lib/rsmp/proxy/modules/state.rb +25 -0
- data/lib/rsmp/proxy/modules/tasks.rb +105 -0
- data/lib/rsmp/proxy/modules/versions.rb +69 -0
- data/lib/rsmp/proxy/modules/watchdogs.rb +66 -0
- data/lib/rsmp/proxy/proxy.rb +199 -0
- data/lib/rsmp/proxy/site/modules/aggregated_status.rb +52 -0
- data/lib/rsmp/proxy/site/modules/alarms.rb +27 -0
- data/lib/rsmp/proxy/site/modules/commands.rb +31 -0
- data/lib/rsmp/proxy/site/modules/status.rb +110 -0
- data/lib/rsmp/proxy/site/site_proxy.rb +205 -0
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +47 -0
- data/lib/rsmp/proxy/supervisor/modules/alarms.rb +73 -0
- data/lib/rsmp/proxy/supervisor/modules/commands.rb +53 -0
- data/lib/rsmp/proxy/supervisor/modules/status.rb +204 -0
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +178 -0
- data/lib/rsmp/tlc/detector_logic.rb +18 -34
- data/lib/rsmp/tlc/input_states.rb +126 -0
- data/lib/rsmp/tlc/modules/detector_logics.rb +50 -0
- data/lib/rsmp/tlc/modules/display.rb +78 -0
- data/lib/rsmp/tlc/modules/helpers.rb +41 -0
- data/lib/rsmp/tlc/modules/inputs.rb +173 -0
- data/lib/rsmp/tlc/modules/modes.rb +253 -0
- data/lib/rsmp/tlc/modules/outputs.rb +30 -0
- data/lib/rsmp/tlc/modules/plans.rb +218 -0
- data/lib/rsmp/tlc/modules/signal_groups.rb +109 -0
- data/lib/rsmp/tlc/modules/startup_sequence.rb +22 -0
- data/lib/rsmp/tlc/modules/system.rb +140 -0
- data/lib/rsmp/tlc/modules/traffic_data.rb +49 -0
- data/lib/rsmp/tlc/signal_group.rb +37 -41
- data/lib/rsmp/tlc/signal_plan.rb +14 -11
- data/lib/rsmp/tlc/signal_priority.rb +39 -35
- data/lib/rsmp/tlc/startup_sequence.rb +59 -0
- data/lib/rsmp/tlc/traffic_controller.rb +38 -1010
- data/lib/rsmp/tlc/traffic_controller_site.rb +58 -57
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +82 -48
- data/rsmp.gemspec +24 -31
- metadata +79 -139
- data/lib/rsmp/archive.rb +0 -76
- data/lib/rsmp/collect/message_matchers.rb +0 -0
- data/lib/rsmp/component_base.rb +0 -87
- data/lib/rsmp/component_proxy.rb +0 -57
- data/lib/rsmp/components.rb +0 -65
- data/lib/rsmp/error.rb +0 -71
- data/lib/rsmp/logger.rb +0 -216
- data/lib/rsmp/proxy.rb +0 -693
- data/lib/rsmp/site.rb +0 -188
- data/lib/rsmp/site_proxy.rb +0 -389
- data/lib/rsmp/supervisor.rb +0 -302
- data/lib/rsmp/supervisor_proxy.rb +0 -510
- data/lib/rsmp/tlc/inputs.rb +0 -134
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
# Handles a site connection to a remote supervisor
|
|
2
|
-
|
|
3
|
-
require 'digest'
|
|
4
|
-
|
|
5
|
-
module RSMP
|
|
6
|
-
class SupervisorProxy < Proxy
|
|
7
|
-
|
|
8
|
-
attr_reader :supervisor_id, :site
|
|
9
|
-
|
|
10
|
-
def initialize options
|
|
11
|
-
super options.merge(node:options[:site])
|
|
12
|
-
@site = options[:site]
|
|
13
|
-
@site_settings = @site.site_settings.clone
|
|
14
|
-
@ip = options[:ip]
|
|
15
|
-
@port = options[:port]
|
|
16
|
-
@status_subscriptions = {}
|
|
17
|
-
@sxl = @site_settings['sxl']
|
|
18
|
-
@synthetic_id = Supervisor.build_id_from_ip_port @ip, @port
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# handle communication
|
|
22
|
-
# if disconnected, then try to reconnect
|
|
23
|
-
def run
|
|
24
|
-
loop do
|
|
25
|
-
connect
|
|
26
|
-
start_reader
|
|
27
|
-
start_handshake
|
|
28
|
-
wait_for_reader # run until disconnected
|
|
29
|
-
break if reconnect_delay == false
|
|
30
|
-
rescue Restart
|
|
31
|
-
@logger.mute @ip, @port
|
|
32
|
-
raise
|
|
33
|
-
rescue RSMP::ConnectionError => e
|
|
34
|
-
log e, level: :error
|
|
35
|
-
break if reconnect_delay == false
|
|
36
|
-
rescue StandardError => e
|
|
37
|
-
distribute_error e, level: :internal
|
|
38
|
-
break if reconnect_delay == false
|
|
39
|
-
ensure
|
|
40
|
-
close
|
|
41
|
-
stop_subtasks
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def start_handshake
|
|
46
|
-
send_version @site_settings['site_id'], core_versions
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# connect to the supervisor and initiate handshake supervisor
|
|
50
|
-
def connect
|
|
51
|
-
log "Connecting to supervisor at #{@ip}:#{@port}", level: :info
|
|
52
|
-
set_state :connecting
|
|
53
|
-
connect_tcp
|
|
54
|
-
@logger.unmute @ip, @port
|
|
55
|
-
log "Connected to supervisor at #{@ip}:#{@port}", level: :info
|
|
56
|
-
rescue SystemCallError => e
|
|
57
|
-
raise ConnectionError.new "Could not connect to supervisor at #{@ip}:#{@port}: Errno #{e.errno} #{e}"
|
|
58
|
-
rescue StandardError => e
|
|
59
|
-
raise ConnectionError.new "Error while connecting to supervisor at #{@ip}:#{@port}: #{e}"
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def stop_task
|
|
63
|
-
super
|
|
64
|
-
@last_status_sent = nil
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def connect_tcp
|
|
68
|
-
@endpoint = IO::Endpoint.tcp(@ip, @port)
|
|
69
|
-
|
|
70
|
-
# this timeout is a workaround for connect hanging on windows if the other side is not present yet
|
|
71
|
-
timeout = @site_settings.dig('timeouts','connect') || 1.1
|
|
72
|
-
task.with_timeout timeout do
|
|
73
|
-
@socket = @endpoint.connect
|
|
74
|
-
end
|
|
75
|
-
delay = @site_settings.dig('intervals','after_connect')
|
|
76
|
-
task.sleep delay if delay
|
|
77
|
-
|
|
78
|
-
@stream = IO::Stream::Buffered.new(@socket)
|
|
79
|
-
@protocol = RSMP::Protocol.new(@stream) # rsmp messages are json terminated with a form-feed
|
|
80
|
-
set_state :connected
|
|
81
|
-
rescue Errno::ECONNREFUSED => e # rescue to avoid log output
|
|
82
|
-
log "Connection refused", level: :warning
|
|
83
|
-
raise e
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def handshake_complete
|
|
87
|
-
sanitized_sxl_version = RSMP::Schema.sanitize_version(sxl_version)
|
|
88
|
-
log "Connection to supervisor established, using core #{@core_version}, #{sxl} #{sanitized_sxl_version}", level: :info
|
|
89
|
-
set_state :ready
|
|
90
|
-
start_watchdog
|
|
91
|
-
if @site_settings['send_after_connect']
|
|
92
|
-
send_all_aggregated_status
|
|
93
|
-
send_active_alarms
|
|
94
|
-
end
|
|
95
|
-
super
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def process_message message
|
|
99
|
-
case message
|
|
100
|
-
when StatusResponse, StatusUpdate, AggregatedStatus, AlarmIssue
|
|
101
|
-
will_not_handle message
|
|
102
|
-
when AggregatedStatusRequest
|
|
103
|
-
process_aggregated_status_request message
|
|
104
|
-
when CommandRequest
|
|
105
|
-
process_command_request message
|
|
106
|
-
when CommandResponse
|
|
107
|
-
process_command_response message
|
|
108
|
-
when StatusRequest
|
|
109
|
-
process_status_request message
|
|
110
|
-
when StatusSubscribe
|
|
111
|
-
process_status_subcribe message
|
|
112
|
-
when StatusUnsubscribe
|
|
113
|
-
process_status_unsubcribe message
|
|
114
|
-
when Alarm, AlarmAcknowledged, AlarmSuspend, AlarmResume, AlarmRequest
|
|
115
|
-
process_alarm message
|
|
116
|
-
else
|
|
117
|
-
super message
|
|
118
|
-
end
|
|
119
|
-
rescue UnknownComponent, UnknownCommand, UnknownStatus,
|
|
120
|
-
MessageRejected, MissingAttribute => e
|
|
121
|
-
dont_acknowledge message, '', e.to_s
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def acknowledged_first_ingoing message
|
|
125
|
-
case message.type
|
|
126
|
-
when "Watchdog"
|
|
127
|
-
handshake_complete
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def send_all_aggregated_status
|
|
132
|
-
@site.components.each_pair do |c_id,component|
|
|
133
|
-
if component.grouped
|
|
134
|
-
send_aggregated_status component
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def send_active_alarms
|
|
140
|
-
@site.components.each_pair do |c_id,component|
|
|
141
|
-
component.alarms.each_pair do |alarm_code, alarm_state|
|
|
142
|
-
if alarm_state.active
|
|
143
|
-
alarm = AlarmIssue.new( alarm_state.to_hash.merge('aSp' => 'Issue') )
|
|
144
|
-
send_message alarm
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def reconnect_delay
|
|
151
|
-
return false if @site_settings['intervals']['reconnect'] == :no
|
|
152
|
-
interval = @site_settings['intervals']['reconnect']
|
|
153
|
-
log "Will try to reconnect again every #{interval} seconds...", level: :info
|
|
154
|
-
@logger.mute @ip, @port
|
|
155
|
-
@task.sleep interval
|
|
156
|
-
true
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def version_accepted message
|
|
160
|
-
log "Received Version message, using RSMP #{@core_version}", message: message, level: :log
|
|
161
|
-
start_timer
|
|
162
|
-
acknowledge message
|
|
163
|
-
@version_determined = true
|
|
164
|
-
send_watchdog
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def send_aggregated_status component, options={}
|
|
168
|
-
m_id = options[:m_id] || RSMP::Message.make_m_id
|
|
169
|
-
|
|
170
|
-
# For core <=3.1.2, se items must be send as strings
|
|
171
|
-
# For core > 3.1.2, se items must be send as booleans
|
|
172
|
-
if Proxy::version_meets_requirement?(core_version,"<=3.1.2")
|
|
173
|
-
se = component.aggregated_status_bools.map {|bool| bool ? "true" : "false"}
|
|
174
|
-
else
|
|
175
|
-
se = component.aggregated_status_bools
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
message = AggregatedStatus.new({
|
|
179
|
-
"aSTS" => clock.to_s,
|
|
180
|
-
"cId" => component.c_id,
|
|
181
|
-
"fP" => nil,
|
|
182
|
-
"fS" => nil,
|
|
183
|
-
"se" => se,
|
|
184
|
-
"mId" => m_id,
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
set_nts_message_attributes message
|
|
189
|
-
send_and_optionally_collect message, options do |collect_options|
|
|
190
|
-
Collector.new self, collect_options.merge(task:@task, type: 'MessageAck')
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def send_alarm component, alarm, options={}
|
|
195
|
-
send_and_optionally_collect alarm, options do |collect_options|
|
|
196
|
-
Collector.new self, collect_options.merge(task:@task, type: 'MessageAck')
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def process_aggregated_status message
|
|
201
|
-
se = message.attribute("se")
|
|
202
|
-
validate_aggregated_status(message,se)
|
|
203
|
-
on = set_aggregated_status se
|
|
204
|
-
log "Received #{message.type} status [#{on.join(', ')}]", message: message, level: :log
|
|
205
|
-
acknowledge message
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def process_alarm message
|
|
209
|
-
case message
|
|
210
|
-
when AlarmAcknowledge
|
|
211
|
-
handle_alarm_acknowledge message
|
|
212
|
-
when AlarmSuspend
|
|
213
|
-
handle_alarm_suspend message
|
|
214
|
-
when AlarmResume
|
|
215
|
-
handle_alarm_resume message
|
|
216
|
-
when AlarmRequest
|
|
217
|
-
handle_alarm_request message
|
|
218
|
-
else
|
|
219
|
-
dont_acknowledge message, "Invalid alarm message type"
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# handle incoming alarm acknowledge
|
|
224
|
-
def handle_alarm_acknowledge message
|
|
225
|
-
component_id = message.attributes["cId"]
|
|
226
|
-
component = @site.find_component component_id
|
|
227
|
-
alarm_code = message.attribute("aCId")
|
|
228
|
-
log "Received #{message.type} #{alarm_code} acknowledgement", message: message, level: :log
|
|
229
|
-
acknowledge message
|
|
230
|
-
component.acknowledge_alarm alarm_code
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# handle incoming alarm suspend
|
|
234
|
-
def handle_alarm_suspend message
|
|
235
|
-
component_id = message.attributes["cId"]
|
|
236
|
-
component = @site.find_component component_id
|
|
237
|
-
alarm_code = message.attribute("aCId")
|
|
238
|
-
log "Received #{message.type} #{alarm_code} suspend", message: message, level: :log
|
|
239
|
-
acknowledge message
|
|
240
|
-
component.suspend_alarm alarm_code
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
# handle incoming alarm resume
|
|
244
|
-
def handle_alarm_resume message
|
|
245
|
-
component_id = message.attributes["cId"]
|
|
246
|
-
component = @site.find_component component_id
|
|
247
|
-
alarm_code = message.attribute("aCId")
|
|
248
|
-
log "Received #{message.type} #{alarm_code} resume", message: message, level: :log
|
|
249
|
-
acknowledge message
|
|
250
|
-
component.resume_alarm alarm_code
|
|
251
|
-
end
|
|
252
|
-
# reorganize rmsp command request arg attribute:
|
|
253
|
-
# [{"cCI":"M0002","cO":"setPlan","n":"status","v":"True"},{"cCI":"M0002","cO":"setPlan","n":"securityCode","v":"5678"},{"cCI":"M0002","cO":"setPlan","n":"timeplan","v":"3"}]
|
|
254
|
-
# into the simpler, but equivalent:
|
|
255
|
-
# {"M0002"=>{"status"=>"True", "securityCode"=>"5678", "timeplan"=>"3"}}
|
|
256
|
-
def simplify_command_requests arg
|
|
257
|
-
sorted = {}
|
|
258
|
-
arg.each do |item|
|
|
259
|
-
sorted[item['cCI']] ||= {}
|
|
260
|
-
sorted[item['cCI']][item['n']] = item['v']
|
|
261
|
-
end
|
|
262
|
-
sorted
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
def process_aggregated_status_request message
|
|
266
|
-
log "Received #{message.type}", message: message, level: :log
|
|
267
|
-
component_id = message.attributes["cId"]
|
|
268
|
-
component = @site.find_component component_id
|
|
269
|
-
acknowledge message
|
|
270
|
-
send_aggregated_status component
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
def process_command_request message
|
|
274
|
-
component_id = message.attributes["cId"]
|
|
275
|
-
|
|
276
|
-
rvs = message.attributes["arg"].map do |item|
|
|
277
|
-
item = item.dup.merge('age'=>'recent')
|
|
278
|
-
item.delete 'cO'
|
|
279
|
-
item
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
begin
|
|
283
|
-
component = @site.find_component component_id
|
|
284
|
-
commands = simplify_command_requests message.attributes["arg"]
|
|
285
|
-
commands.each_pair do |command_code,arg|
|
|
286
|
-
component.handle_command command_code,arg
|
|
287
|
-
end
|
|
288
|
-
log "Received #{message.type}", message: message, level: :log
|
|
289
|
-
rescue UnknownComponent
|
|
290
|
-
log "Received #{message.type} with unknown component id '#{component_id}' and cannot infer type", message: message, level: :warning
|
|
291
|
-
# If the component is unknown, we must set age=undefined for all items,
|
|
292
|
-
# while still acknowledge the message.
|
|
293
|
-
# See https://github.com/rsmp-nordic/rsmp_validator/issues/271
|
|
294
|
-
rvs.map do |item|
|
|
295
|
-
item['age'] = 'undefined'
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
response = CommandResponse.new({
|
|
300
|
-
"cId"=>component_id,
|
|
301
|
-
"cTS"=>clock.to_s,
|
|
302
|
-
"rvs"=>rvs
|
|
303
|
-
})
|
|
304
|
-
set_nts_message_attributes response
|
|
305
|
-
acknowledge message
|
|
306
|
-
send_message response
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def rsmpify_value v, q
|
|
310
|
-
if v.is_a?(Array) || v.is_a?(Set)
|
|
311
|
-
v
|
|
312
|
-
elsif ['undefined','unknown'].include?(q.to_s)
|
|
313
|
-
nil
|
|
314
|
-
else
|
|
315
|
-
v.to_s
|
|
316
|
-
end
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def process_status_request message, options={}
|
|
320
|
-
sS = []
|
|
321
|
-
begin
|
|
322
|
-
component_id = message.attributes["cId"]
|
|
323
|
-
component = @site.find_component component_id
|
|
324
|
-
sS = message.attributes["sS"].map do |arg|
|
|
325
|
-
value, quality = component.get_status arg['sCI'], arg['n'], {sxl_version: sxl_version}
|
|
326
|
-
{ "s" => rsmpify_value(value, quality), "q" => quality.to_s }.merge arg
|
|
327
|
-
end
|
|
328
|
-
log "Received #{message.type}", message: message, level: :log
|
|
329
|
-
|
|
330
|
-
rescue UnknownComponent
|
|
331
|
-
log "Received #{message.type} with unknown component id '#{component_id}' and cannot infer type", message: message, level: :warning
|
|
332
|
-
# If the component is unknown, we must set q=undefined and s=nil for all items,
|
|
333
|
-
# while still acknowledge the message.
|
|
334
|
-
sS = message.attributes["sS"].map do |arg|
|
|
335
|
-
arg.dup.merge('q'=>'undefined','s'=>nil)
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
response = StatusResponse.new({
|
|
340
|
-
"cId"=>component_id,
|
|
341
|
-
"sTs"=>clock.to_s,
|
|
342
|
-
"sS"=>sS,
|
|
343
|
-
"mId" => options[:m_id]
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
set_nts_message_attributes response
|
|
347
|
-
acknowledge message
|
|
348
|
-
send_message response
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def process_status_subcribe message
|
|
352
|
-
log "Received #{message.type}", message: message, level: :log
|
|
353
|
-
|
|
354
|
-
# @status_subscriptions is organized by component/code/name, for example:
|
|
355
|
-
#
|
|
356
|
-
# {"AA+BBCCC=DDDEE002"=>{"S001"=>["number"]}}
|
|
357
|
-
#
|
|
358
|
-
# This is done to make it easy to send a single status update
|
|
359
|
-
# for each component, containing all the requested statuses
|
|
360
|
-
|
|
361
|
-
update_list = {}
|
|
362
|
-
component_id = message.attributes["cId"]
|
|
363
|
-
@status_subscriptions[component_id] ||= {}
|
|
364
|
-
update_list[component_id] ||= {}
|
|
365
|
-
now = Time.now # internal timestamp
|
|
366
|
-
subs = @status_subscriptions[component_id]
|
|
367
|
-
|
|
368
|
-
message.attributes["sS"].each do |arg|
|
|
369
|
-
sCI = arg["sCI"]
|
|
370
|
-
subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
|
|
371
|
-
subs[sCI] ||= {}
|
|
372
|
-
subs[sCI][arg["n"]] = subcription
|
|
373
|
-
update_list[component_id][sCI] ||= []
|
|
374
|
-
update_list[component_id][sCI] << arg["n"]
|
|
375
|
-
end
|
|
376
|
-
acknowledge message
|
|
377
|
-
send_status_updates update_list # send status after subscribing is accepted
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
def get_status_subscribe_interval component_id, sCI, n
|
|
381
|
-
@status_subscriptions.dig component_id, sCI, n
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def process_status_unsubcribe message
|
|
385
|
-
log "Received #{message.type}", message: message, level: :log
|
|
386
|
-
component = message.attributes["cId"]
|
|
387
|
-
|
|
388
|
-
subs = @status_subscriptions[component]
|
|
389
|
-
if subs
|
|
390
|
-
message.attributes["sS"].each do |arg|
|
|
391
|
-
sCI = arg["sCI"]
|
|
392
|
-
if subs[sCI]
|
|
393
|
-
subs[sCI].delete arg["n"]
|
|
394
|
-
subs.delete(sCI) if subs[sCI].empty?
|
|
395
|
-
end
|
|
396
|
-
end
|
|
397
|
-
@status_subscriptions.delete(component) if subs.empty?
|
|
398
|
-
end
|
|
399
|
-
acknowledge message
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
def timer now
|
|
403
|
-
super
|
|
404
|
-
status_update_timer now if ready?
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
def fetch_last_sent_status component, code, name
|
|
408
|
-
@last_status_sent.dig component, code, name if @last_status_sent
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
def store_last_sent_status message
|
|
412
|
-
component_id = message.attribute('cId')
|
|
413
|
-
@last_status_sent ||= {}
|
|
414
|
-
@last_status_sent[component_id] ||= {}
|
|
415
|
-
message.attribute('sS').each do |item|
|
|
416
|
-
sCI, n, s = item['sCI'], item['n'], item['s']
|
|
417
|
-
@last_status_sent[component_id][sCI] ||= {}
|
|
418
|
-
@last_status_sent[component_id][sCI][n] = s
|
|
419
|
-
end
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
def status_update_timer now
|
|
423
|
-
update_list = {}
|
|
424
|
-
# go through subscriptons and build a similarly organized list,
|
|
425
|
-
# that only contains what should be send
|
|
426
|
-
|
|
427
|
-
@status_subscriptions.each_pair do |component,by_code|
|
|
428
|
-
component_object = @site.find_component component
|
|
429
|
-
by_code.each_pair do |code,by_name|
|
|
430
|
-
by_name.each_pair do |name,subscription|
|
|
431
|
-
current = nil
|
|
432
|
-
should_send = false
|
|
433
|
-
if subscription[:interval] == 0
|
|
434
|
-
# send as soon as the data changes
|
|
435
|
-
if component_object
|
|
436
|
-
current, quality = *(component_object.get_status code, name)
|
|
437
|
-
current = rsmpify_value(current,quality)
|
|
438
|
-
end
|
|
439
|
-
last_sent = fetch_last_sent_status component, code, name
|
|
440
|
-
if current != last_sent
|
|
441
|
-
should_send = true
|
|
442
|
-
end
|
|
443
|
-
else
|
|
444
|
-
# send at regular intervals
|
|
445
|
-
if subscription[:last_sent_at] == nil || (now - subscription[:last_sent_at]) >= subscription[:interval]
|
|
446
|
-
should_send = true
|
|
447
|
-
end
|
|
448
|
-
end
|
|
449
|
-
if should_send
|
|
450
|
-
subscription[:last_sent_at] = now
|
|
451
|
-
update_list[component] ||= {}
|
|
452
|
-
update_list[component][code] ||= {}
|
|
453
|
-
update_list[component][code][name] = current
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
send_status_updates update_list
|
|
459
|
-
end
|
|
460
|
-
|
|
461
|
-
def send_status_updates update_list
|
|
462
|
-
now = clock.to_s
|
|
463
|
-
update_list.each_pair do |component_id,by_code|
|
|
464
|
-
component = @site.find_component component_id
|
|
465
|
-
sS = []
|
|
466
|
-
by_code.each_pair do |code,names|
|
|
467
|
-
names.map do |status_name,value|
|
|
468
|
-
if value
|
|
469
|
-
quality = 'recent'
|
|
470
|
-
else
|
|
471
|
-
value,quality = component.get_status code, status_name
|
|
472
|
-
end
|
|
473
|
-
sS << { "sCI" => code,
|
|
474
|
-
"n" => status_name,
|
|
475
|
-
"s" => rsmpify_value(value, quality),
|
|
476
|
-
"q" => quality }
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
update = StatusUpdate.new({
|
|
480
|
-
"cId"=>component_id,
|
|
481
|
-
"sTs"=>now,
|
|
482
|
-
"sS"=>sS
|
|
483
|
-
})
|
|
484
|
-
set_nts_message_attributes update
|
|
485
|
-
send_message update
|
|
486
|
-
store_last_sent_status update
|
|
487
|
-
component.status_updates_sent
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
def sxl_version
|
|
492
|
-
@site_settings['sxl_version'].to_s
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
def process_version message
|
|
496
|
-
return extraneous_version message if @version_determined
|
|
497
|
-
check_core_version message
|
|
498
|
-
check_sxl_version message
|
|
499
|
-
@site_id = Supervisor.build_id_from_ip_port @ip, @port
|
|
500
|
-
version_accepted message
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
def check_sxl_version message
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
def main
|
|
507
|
-
@site.main
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
end
|
data/lib/rsmp/tlc/inputs.rb
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
module RSMP
|
|
2
|
-
module TLC
|
|
3
|
-
# class that maintains the state of TLC inputs
|
|
4
|
-
# indexing is 1-based since that's how the RSMP messages are specified
|
|
5
|
-
class Inputs
|
|
6
|
-
attr_reader :size
|
|
7
|
-
|
|
8
|
-
def initialize size
|
|
9
|
-
@size = size
|
|
10
|
-
reset
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def reset
|
|
14
|
-
string_size = @size+1
|
|
15
|
-
@value = '0'*string_size
|
|
16
|
-
@forced = '0'*string_size
|
|
17
|
-
@forced_value = '0'*string_size
|
|
18
|
-
@actual = '0'*string_size
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def set input, value
|
|
22
|
-
check_input input
|
|
23
|
-
report_change(input) do
|
|
24
|
-
@value[input] = to_digit value
|
|
25
|
-
update_actual input
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def set_forcing input, force=true, forced_value=true
|
|
30
|
-
check_input input
|
|
31
|
-
report_change(input) do
|
|
32
|
-
@forced[input] = to_digit force
|
|
33
|
-
@forced_value[input] = to_digit forced_value
|
|
34
|
-
update_actual input
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def force input, forced_value=true
|
|
39
|
-
report_change(input) do
|
|
40
|
-
set_forcing input, true, forced_value
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def release input
|
|
45
|
-
report_change(input) do
|
|
46
|
-
set_forcing input, false, false
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def value input
|
|
51
|
-
check_input input
|
|
52
|
-
from_digit @value[input]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def forced? input
|
|
56
|
-
check_input input
|
|
57
|
-
from_digit @forced[input]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def forced_value input
|
|
61
|
-
check_input input
|
|
62
|
-
from_digit @forced_value[input]
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def actual input
|
|
66
|
-
check_input input
|
|
67
|
-
from_digit @actual[input]
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def report input
|
|
71
|
-
{
|
|
72
|
-
value: value(input),
|
|
73
|
-
forced: forced?(input),
|
|
74
|
-
forced_value: forced_value(input),
|
|
75
|
-
actual:actual(input)
|
|
76
|
-
}
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def value_string
|
|
80
|
-
@value[1..-1]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def value_string
|
|
84
|
-
@value[1..-1]
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def forced_string
|
|
88
|
-
@forced[1..-1]
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def forced_value_string
|
|
92
|
-
@forced[1..-1]
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def actual_string
|
|
96
|
-
@actual[1..-1]
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
protected
|
|
100
|
-
|
|
101
|
-
def check_input input
|
|
102
|
-
raise ArgumentError.new("Input index #{input} must be in the range 1-#{@size}") if input<1 || input>@size
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def from_digit input
|
|
106
|
-
input == '1'
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def to_digit input
|
|
110
|
-
input ? '1' : '0'
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def update_actual input
|
|
114
|
-
if from_digit @forced[input]
|
|
115
|
-
@actual[input] = @forced_value[input]
|
|
116
|
-
else
|
|
117
|
-
@actual[input] = @value[input]
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def report_change input, &block
|
|
122
|
-
before = @actual[input]
|
|
123
|
-
yield
|
|
124
|
-
if @actual[input] != before
|
|
125
|
-
from_digit @actual[input]
|
|
126
|
-
else
|
|
127
|
-
nil
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
|