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
data/lib/rsmp/proxy.rb
DELETED
|
@@ -1,693 +0,0 @@
|
|
|
1
|
-
# A connection to a remote site or supervisor.
|
|
2
|
-
# Uses the Task module to handle asyncronous work, but adds
|
|
3
|
-
# the concept of a connection that can be connected or disconnected.
|
|
4
|
-
|
|
5
|
-
require 'rubygems'
|
|
6
|
-
|
|
7
|
-
module RSMP
|
|
8
|
-
class Proxy
|
|
9
|
-
WRAPPING_DELIMITER = "\f"
|
|
10
|
-
|
|
11
|
-
include Logging
|
|
12
|
-
include Distributor
|
|
13
|
-
include Inspect
|
|
14
|
-
include Task
|
|
15
|
-
|
|
16
|
-
attr_reader :state, :archive, :connection_info, :sxl, :collector, :ip, :port, :node, :core_version
|
|
17
|
-
def initialize options
|
|
18
|
-
@node = options[:node]
|
|
19
|
-
initialize_logging options
|
|
20
|
-
initialize_distributor
|
|
21
|
-
initialize_task
|
|
22
|
-
setup options
|
|
23
|
-
clear
|
|
24
|
-
@state = :disconnected
|
|
25
|
-
@state_condition = Async::Notification.new
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def now
|
|
30
|
-
node.now
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def disconnect
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# wait for the reader task to complete,
|
|
38
|
-
# which is not expected to happen before the connection is closed
|
|
39
|
-
def wait_for_reader
|
|
40
|
-
@reader.wait if @reader
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# close connection, but keep our main task running so we can reconnect
|
|
44
|
-
def close
|
|
45
|
-
log "Closing connection", level: :warning
|
|
46
|
-
close_stream
|
|
47
|
-
close_socket
|
|
48
|
-
stop_reader
|
|
49
|
-
set_state :disconnected
|
|
50
|
-
distribute_error DisconnectError.new("Connection was closed")
|
|
51
|
-
|
|
52
|
-
# stop timer
|
|
53
|
-
# as we're running inside the timer, code after stop_timer() will not be called,
|
|
54
|
-
# unless it's in the ensure block
|
|
55
|
-
stop_timer
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def stop_subtasks
|
|
59
|
-
stop_timer
|
|
60
|
-
stop_reader
|
|
61
|
-
clear
|
|
62
|
-
super
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def stop_timer
|
|
66
|
-
@timer.stop if @timer
|
|
67
|
-
ensure
|
|
68
|
-
@timer = nil
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def stop_reader
|
|
72
|
-
@reader.stop if @reader
|
|
73
|
-
ensure
|
|
74
|
-
@reader = nil
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def close_stream
|
|
78
|
-
@stream.close if @stream
|
|
79
|
-
ensure
|
|
80
|
-
@stream = nil
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def close_socket
|
|
84
|
-
@socket.close if @socket
|
|
85
|
-
ensure
|
|
86
|
-
@socket = nil
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def stop_task
|
|
90
|
-
close
|
|
91
|
-
super
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# change our state
|
|
95
|
-
def set_state state
|
|
96
|
-
return if state == @state
|
|
97
|
-
@state = state
|
|
98
|
-
state_changed
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# the state changed
|
|
102
|
-
# override to to things like notifications
|
|
103
|
-
def state_changed
|
|
104
|
-
@state_condition.signal @state
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# revive after a reconnect
|
|
108
|
-
def revive options
|
|
109
|
-
setup options
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def setup options
|
|
113
|
-
@settings = options[:settings]
|
|
114
|
-
@socket = options[:socket]
|
|
115
|
-
@stream = options[:stream]
|
|
116
|
-
@protocol = options[:protocol]
|
|
117
|
-
@ip = options[:ip]
|
|
118
|
-
@port = options[:port]
|
|
119
|
-
@connection_info = options[:info]
|
|
120
|
-
@sxl = nil
|
|
121
|
-
@site_settings = nil # can't pick until we know the site id
|
|
122
|
-
if options[:collect]
|
|
123
|
-
@collector = RSMP::Collector.new self, options[:collect]
|
|
124
|
-
@collector.start
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def inspect
|
|
129
|
-
"#<#{self.class.name}:#{self.object_id}, #{inspector(
|
|
130
|
-
:@acknowledgements,:@settings,:@site_settings
|
|
131
|
-
)}>"
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def clock
|
|
135
|
-
@node.clock
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def ready?
|
|
139
|
-
@state == :ready
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def connected?
|
|
143
|
-
@state == :connected || @state == :ready
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def disconnected?
|
|
147
|
-
@state == :disconnected
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def clear
|
|
151
|
-
@awaiting_acknowledgement = {}
|
|
152
|
-
@latest_watchdog_received = nil
|
|
153
|
-
@watchdog_started = false
|
|
154
|
-
@version_determined = false
|
|
155
|
-
@ingoing_acknowledged = {}
|
|
156
|
-
@outgoing_acknowledged = {}
|
|
157
|
-
@latest_watchdog_send_at = nil
|
|
158
|
-
|
|
159
|
-
@acknowledgements = {}
|
|
160
|
-
@acknowledgement_condition = Async::Notification.new
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# run an async task that reads from @socket
|
|
164
|
-
def start_reader
|
|
165
|
-
@reader = @task.async do |task|
|
|
166
|
-
task.annotate "reader"
|
|
167
|
-
run_reader
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def run_reader
|
|
172
|
-
@stream ||= IO::Stream::Buffered.new(@socket)
|
|
173
|
-
@protocol ||= RSMP::Protocol.new(@stream) # rsmp messages are json terminated with a form-feed
|
|
174
|
-
loop do
|
|
175
|
-
read_line
|
|
176
|
-
end
|
|
177
|
-
rescue Restart
|
|
178
|
-
log "Closing connection", level: :warning
|
|
179
|
-
raise
|
|
180
|
-
rescue EOFError, Async::Stop
|
|
181
|
-
log "Connection closed", level: :warning
|
|
182
|
-
rescue IOError => e
|
|
183
|
-
log "IOError: #{e}", level: :warning
|
|
184
|
-
rescue Errno::ECONNRESET
|
|
185
|
-
log "Connection reset by peer", level: :warning
|
|
186
|
-
rescue Errno::EPIPE
|
|
187
|
-
log "Broken pipe", level: :warning
|
|
188
|
-
rescue StandardError => e
|
|
189
|
-
distribute_error e, level: :internal
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def read_line
|
|
193
|
-
json = @protocol.read_line
|
|
194
|
-
beginning = Time.now
|
|
195
|
-
message = process_packet json
|
|
196
|
-
duration = Time.now - beginning
|
|
197
|
-
ms = (duration*1000).round(4)
|
|
198
|
-
if duration > 0
|
|
199
|
-
per_second = (1.0 / duration).round
|
|
200
|
-
else
|
|
201
|
-
per_second = Float::INFINITY
|
|
202
|
-
end
|
|
203
|
-
if message
|
|
204
|
-
type = message.type
|
|
205
|
-
m_id = Logger.shorten_message_id(message.m_id)
|
|
206
|
-
else
|
|
207
|
-
type = 'Unknown'
|
|
208
|
-
m_id = nil
|
|
209
|
-
end
|
|
210
|
-
str = [type,m_id,"processed in #{ms}ms, #{per_second}req/s"].compact.join(' ')
|
|
211
|
-
log str, level: :statistics
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def receive_error e, options={}
|
|
215
|
-
@node.receive_error e, options
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def start_watchdog
|
|
219
|
-
log "Starting watchdog with interval #{@site_settings['intervals']['watchdog']} seconds", level: :debug
|
|
220
|
-
@watchdog_started = true
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def stop_watchdog
|
|
224
|
-
log "Stopping watchdog", level: :debug
|
|
225
|
-
@watchdog_started = false
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def with_watchdog_disabled
|
|
229
|
-
was = @watchdog_started
|
|
230
|
-
stop_watchdog if was
|
|
231
|
-
yield
|
|
232
|
-
ensure
|
|
233
|
-
start_watchdog if was
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def start_timer
|
|
238
|
-
return if @timer
|
|
239
|
-
name = "timer"
|
|
240
|
-
interval = @site_settings['intervals']['timer'] || 1
|
|
241
|
-
log "Starting #{name} with interval #{interval} seconds", level: :debug
|
|
242
|
-
@latest_watchdog_received = Clock.now
|
|
243
|
-
@timer = @task.async do |task|
|
|
244
|
-
task.annotate "timer"
|
|
245
|
-
run_timer task, interval
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def run_timer task, interval
|
|
250
|
-
next_time = Time.now.to_f
|
|
251
|
-
loop do
|
|
252
|
-
begin
|
|
253
|
-
now = Clock.now
|
|
254
|
-
timer(now)
|
|
255
|
-
rescue RSMP::Schema::Error => e
|
|
256
|
-
log "Timer: Schema error: #{e}", level: :warning
|
|
257
|
-
rescue EOFError => e
|
|
258
|
-
log "Timer: Connection closed: #{e}", level: :warning
|
|
259
|
-
rescue IOError => e
|
|
260
|
-
log "Timer: IOError", level: :warning
|
|
261
|
-
rescue Errno::ECONNRESET
|
|
262
|
-
log "Timer: Connection reset by peer", level: :warning
|
|
263
|
-
rescue Errno::EPIPE => e
|
|
264
|
-
log "Timer: Broken pipe", level: :warning
|
|
265
|
-
rescue StandardError => e
|
|
266
|
-
distribute_error e, level: :internal
|
|
267
|
-
end
|
|
268
|
-
ensure
|
|
269
|
-
next_time += interval
|
|
270
|
-
duration = next_time - Time.now.to_f
|
|
271
|
-
task.sleep duration
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def timer now
|
|
276
|
-
watchdog_send_timer now
|
|
277
|
-
check_ack_timeout now
|
|
278
|
-
check_watchdog_timeout now
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def watchdog_send_timer now
|
|
282
|
-
return unless @watchdog_started
|
|
283
|
-
return if @site_settings['intervals']['watchdog'] == :never
|
|
284
|
-
if @latest_watchdog_send_at == nil
|
|
285
|
-
send_watchdog now
|
|
286
|
-
else
|
|
287
|
-
# we add half the timer interval to pick the timer
|
|
288
|
-
# event closes to the wanted wathcdog interval
|
|
289
|
-
diff = now - @latest_watchdog_send_at
|
|
290
|
-
if (diff + 0.5*@site_settings['intervals']['timer']) >= (@site_settings['intervals']['watchdog'])
|
|
291
|
-
send_watchdog now
|
|
292
|
-
end
|
|
293
|
-
end
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
def send_watchdog now=Clock.now
|
|
297
|
-
message = Watchdog.new( {"wTs" => clock.to_s})
|
|
298
|
-
send_message message
|
|
299
|
-
@latest_watchdog_send_at = now
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
def check_ack_timeout now
|
|
303
|
-
timeout = @site_settings['timeouts']['acknowledgement']
|
|
304
|
-
# hash cannot be modify during iteration, so clone it
|
|
305
|
-
@awaiting_acknowledgement.clone.each_pair do |m_id, message|
|
|
306
|
-
latest = message.timestamp + timeout
|
|
307
|
-
if now > latest
|
|
308
|
-
str = "No acknowledgements for #{message.type} #{message.m_id_short} within #{timeout} seconds"
|
|
309
|
-
log str, level: :error
|
|
310
|
-
begin
|
|
311
|
-
close
|
|
312
|
-
ensure
|
|
313
|
-
distribute_error MissingAcknowledgment.new(str)
|
|
314
|
-
end
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def check_watchdog_timeout now
|
|
320
|
-
timeout = @site_settings['timeouts']['watchdog']
|
|
321
|
-
latest = @latest_watchdog_received + timeout
|
|
322
|
-
left = latest - now
|
|
323
|
-
if left < 0
|
|
324
|
-
str = "No Watchdog received within #{timeout} seconds"
|
|
325
|
-
log str, level: :warning
|
|
326
|
-
distribute MissingWatchdog.new(str)
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
def log str, options={}
|
|
331
|
-
super str, options.merge(ip: @ip, port: @port, site_id: @site_id)
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
def get_schemas
|
|
335
|
-
schemas = { core: RSMP::Schema.latest_core_version } # use latest core
|
|
336
|
-
schemas[:core] = core_version if core_version
|
|
337
|
-
schemas[sxl] = RSMP::Schema.sanitize_version(sxl_version.to_s) if sxl && sxl_version
|
|
338
|
-
schemas
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
def send_message message, reason=nil, validate: true, force: false
|
|
342
|
-
raise NotReady unless connected? unless force
|
|
343
|
-
raise IOError unless @protocol
|
|
344
|
-
message.direction = :out
|
|
345
|
-
message.generate_json
|
|
346
|
-
message.validate get_schemas unless validate==false
|
|
347
|
-
@protocol.write_lines message.json
|
|
348
|
-
expect_acknowledgement message
|
|
349
|
-
distribute message
|
|
350
|
-
log_send message, reason
|
|
351
|
-
rescue EOFError, IOError
|
|
352
|
-
buffer_message message
|
|
353
|
-
rescue SchemaError, RSMP::Schema::Error => e
|
|
354
|
-
schemas_string = e.schemas.map {|schema| "#{schema.first}: #{schema.last}"}.join(", ")
|
|
355
|
-
str = "Could not send #{message.type} because schema validation failed (#{schemas_string}): #{e.message}"
|
|
356
|
-
log str, message: message, level: :error
|
|
357
|
-
distribute_error e.exception("#{str} #{message.json}")
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
def buffer_message message
|
|
361
|
-
# TODO
|
|
362
|
-
#log "Cannot send #{message.type} because the connection is closed.", message: message, level: :error
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
def log_send message, reason=nil
|
|
366
|
-
if reason
|
|
367
|
-
str = "Sent #{message.type} #{reason}"
|
|
368
|
-
else
|
|
369
|
-
str = "Sent #{message.type}"
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
if message.type == "MessageNotAck"
|
|
373
|
-
log str, message: message, level: :warning
|
|
374
|
-
else
|
|
375
|
-
log str, message: message, level: :log
|
|
376
|
-
end
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
def should_validate_ingoing_message? message
|
|
380
|
-
return true unless @site_settings
|
|
381
|
-
skip = @site_settings.dig('skip_validation')
|
|
382
|
-
return true unless skip
|
|
383
|
-
klass = message.class.name.split('::').last
|
|
384
|
-
!skip.include?(klass)
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
def process_deferred
|
|
388
|
-
@node.process_deferred
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
def verify_sequence message
|
|
392
|
-
expect_version_message(message) unless @version_determined
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
def process_packet json
|
|
396
|
-
attributes = Message.parse_attributes json
|
|
397
|
-
message = Message.build attributes, json
|
|
398
|
-
message.validate(get_schemas) if should_validate_ingoing_message?(message)
|
|
399
|
-
verify_sequence message
|
|
400
|
-
with_deferred_distribution do
|
|
401
|
-
distribute message
|
|
402
|
-
process_message message
|
|
403
|
-
end
|
|
404
|
-
process_deferred
|
|
405
|
-
message
|
|
406
|
-
rescue InvalidPacket => e
|
|
407
|
-
str = "Received invalid package, must be valid JSON but got #{json.size} bytes: #{e.message}"
|
|
408
|
-
distribute_error e.exception(str)
|
|
409
|
-
log str, level: :warning
|
|
410
|
-
nil
|
|
411
|
-
rescue MalformedMessage => e
|
|
412
|
-
str = "Received malformed message, #{e.message}"
|
|
413
|
-
distribute_error e.exception(str)
|
|
414
|
-
log str, message: Malformed.new(attributes), level: :warning
|
|
415
|
-
# cannot send NotAcknowledged for a malformed message since we can't read it, just ignore it
|
|
416
|
-
nil
|
|
417
|
-
rescue SchemaError, RSMP::Schema::Error => e
|
|
418
|
-
schemas_string = e.schemas.map {|schema| "#{schema.first}: #{schema.last}"}.join(", ")
|
|
419
|
-
reason = "schema errors (#{schemas_string}): #{e.message}"
|
|
420
|
-
str = "Received invalid #{message.type}"
|
|
421
|
-
distribute_error e.exception(str), message: message
|
|
422
|
-
dont_acknowledge message, str, reason
|
|
423
|
-
message
|
|
424
|
-
rescue InvalidMessage => e
|
|
425
|
-
reason = "#{e.message}"
|
|
426
|
-
str = "Received invalid #{message.type},"
|
|
427
|
-
distribute_error e.exception("#{str} #{message.json}"), message: message
|
|
428
|
-
dont_acknowledge message, str, reason
|
|
429
|
-
message
|
|
430
|
-
rescue FatalError => e
|
|
431
|
-
reason = e.message
|
|
432
|
-
str = "Rejected #{message.type},"
|
|
433
|
-
distribute_error e.exception(str), message: message
|
|
434
|
-
dont_acknowledge message, str, reason
|
|
435
|
-
close
|
|
436
|
-
message
|
|
437
|
-
ensure
|
|
438
|
-
@node.clear_deferred
|
|
439
|
-
end
|
|
440
|
-
|
|
441
|
-
def process_message message
|
|
442
|
-
case message
|
|
443
|
-
when MessageAck
|
|
444
|
-
process_ack message
|
|
445
|
-
when MessageNotAck
|
|
446
|
-
process_not_ack message
|
|
447
|
-
when Version
|
|
448
|
-
process_version message
|
|
449
|
-
when Watchdog
|
|
450
|
-
process_watchdog message
|
|
451
|
-
else
|
|
452
|
-
dont_acknowledge message, "Received", "unknown message (#{message.type})"
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
def will_not_handle message
|
|
457
|
-
reason = "since we're a #{self.class.name.downcase}" unless reason
|
|
458
|
-
log "Ignoring #{message.type}, #{reason}", message: message, level: :warning
|
|
459
|
-
dont_acknowledge message, nil, reason
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
def expect_acknowledgement message
|
|
463
|
-
unless message.is_a?(MessageAck) || message.is_a?(MessageNotAck)
|
|
464
|
-
@awaiting_acknowledgement[message.m_id] = message
|
|
465
|
-
end
|
|
466
|
-
end
|
|
467
|
-
|
|
468
|
-
def dont_expect_acknowledgement message
|
|
469
|
-
@awaiting_acknowledgement.delete message.attribute("oMId")
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
def extraneous_version message
|
|
473
|
-
dont_acknowledge message, "Received", "extraneous Version message"
|
|
474
|
-
end
|
|
475
|
-
|
|
476
|
-
def core_versions
|
|
477
|
-
version = @site_settings["core_version"]
|
|
478
|
-
if version == 'latest'
|
|
479
|
-
[RSMP::Schema.latest_core_version]
|
|
480
|
-
elsif version
|
|
481
|
-
[version]
|
|
482
|
-
else
|
|
483
|
-
RSMP::Schema.core_versions
|
|
484
|
-
end
|
|
485
|
-
end
|
|
486
|
-
|
|
487
|
-
def check_core_version message
|
|
488
|
-
versions = core_versions
|
|
489
|
-
# find versions that both we and the client support
|
|
490
|
-
candidates = message.versions & versions
|
|
491
|
-
if candidates.any?
|
|
492
|
-
@core_version = candidates.sort_by { |v| Gem::Version.new(v) }.last # pick latest version
|
|
493
|
-
else
|
|
494
|
-
reason = "RSMP versions [#{message.versions.join(', ')}] requested, but only [#{versions.join(', ')}] supported."
|
|
495
|
-
dont_acknowledge message, "Version message rejected", reason, force: true
|
|
496
|
-
raise HandshakeError.new reason
|
|
497
|
-
end
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def process_version message
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
def acknowledge original
|
|
504
|
-
raise InvalidArgument unless original
|
|
505
|
-
ack = MessageAck.build_from(original)
|
|
506
|
-
ack.original = original.clone
|
|
507
|
-
send_message ack, "for #{ack.original.type} #{original.m_id_short}"
|
|
508
|
-
check_ingoing_acknowledged original
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def dont_acknowledge original, prefix=nil, reason=nil, force: true
|
|
512
|
-
raise InvalidArgument unless original
|
|
513
|
-
str = [prefix,reason].join(' ')
|
|
514
|
-
log str, message: original, level: :warning if reason
|
|
515
|
-
message = MessageNotAck.new({
|
|
516
|
-
"oMId" => original.m_id,
|
|
517
|
-
"rea" => reason || "Unknown reason"
|
|
518
|
-
})
|
|
519
|
-
message.original = original.clone
|
|
520
|
-
send_message message, "for #{original.type} #{original.m_id_short}", force: force
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
def wait_for_state state, timeout:
|
|
524
|
-
states = [state].flatten
|
|
525
|
-
return if states.include?(@state)
|
|
526
|
-
wait_for_condition(@state_condition,timeout: timeout) do
|
|
527
|
-
states.include?(@state)
|
|
528
|
-
end
|
|
529
|
-
@state
|
|
530
|
-
rescue RSMP::TimeoutError
|
|
531
|
-
raise RSMP::TimeoutError.new "Did not reach state #{state} within #{timeout}s"
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
def send_version site_id, core_versions
|
|
535
|
-
if core_versions=='latest'
|
|
536
|
-
versions = [RSMP::Schema.latest_core_version]
|
|
537
|
-
elsif core_versions=='all'
|
|
538
|
-
versions = RSMP::Schema.core_versions
|
|
539
|
-
else
|
|
540
|
-
versions = [core_versions].flatten
|
|
541
|
-
end
|
|
542
|
-
versions_array = versions.map {|v| {"vers" => v} }
|
|
543
|
-
|
|
544
|
-
site_id_array = [site_id].flatten.map {|id| {"sId" => id} }
|
|
545
|
-
|
|
546
|
-
version_response = Version.new({
|
|
547
|
-
"RSMP"=>versions_array,
|
|
548
|
-
"siteId"=>site_id_array,
|
|
549
|
-
"SXL"=>sxl_version.to_s
|
|
550
|
-
})
|
|
551
|
-
send_message version_response
|
|
552
|
-
end
|
|
553
|
-
|
|
554
|
-
def find_original_for_message message
|
|
555
|
-
@awaiting_acknowledgement[ message.attribute("oMId") ]
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
# TODO this might be better handled by a proper event machine using e.g. the EventMachine gem
|
|
559
|
-
def check_outgoing_acknowledged message
|
|
560
|
-
unless @outgoing_acknowledged[message.type]
|
|
561
|
-
@outgoing_acknowledged[message.type] = true
|
|
562
|
-
acknowledged_first_outgoing message
|
|
563
|
-
end
|
|
564
|
-
end
|
|
565
|
-
|
|
566
|
-
def check_ingoing_acknowledged message
|
|
567
|
-
unless @ingoing_acknowledged[message.type]
|
|
568
|
-
@ingoing_acknowledged[message.type] = true
|
|
569
|
-
acknowledged_first_ingoing message
|
|
570
|
-
end
|
|
571
|
-
end
|
|
572
|
-
|
|
573
|
-
def acknowledged_first_outgoing message
|
|
574
|
-
end
|
|
575
|
-
|
|
576
|
-
def acknowledged_first_ingoing message
|
|
577
|
-
end
|
|
578
|
-
|
|
579
|
-
def process_ack message
|
|
580
|
-
original = find_original_for_message message
|
|
581
|
-
if original
|
|
582
|
-
dont_expect_acknowledgement message
|
|
583
|
-
message.original = original
|
|
584
|
-
log_acknowledgement_for_original message, original
|
|
585
|
-
|
|
586
|
-
case original.type
|
|
587
|
-
when "Version"
|
|
588
|
-
version_acknowledged
|
|
589
|
-
when "StatusSubscribe"
|
|
590
|
-
status_subscribe_acknowledged original
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
check_outgoing_acknowledged original
|
|
594
|
-
|
|
595
|
-
@acknowledgements[ original.m_id ] = message
|
|
596
|
-
@acknowledgement_condition.signal message
|
|
597
|
-
else
|
|
598
|
-
log_acknowledgement_for_unknown message
|
|
599
|
-
end
|
|
600
|
-
end
|
|
601
|
-
|
|
602
|
-
def process_not_ack message
|
|
603
|
-
original = find_original_for_message message
|
|
604
|
-
if original
|
|
605
|
-
dont_expect_acknowledgement message
|
|
606
|
-
message.original = original
|
|
607
|
-
log_acknowledgement_for_original message, original
|
|
608
|
-
@acknowledgements[ original.m_id ] = message
|
|
609
|
-
@acknowledgement_condition.signal message
|
|
610
|
-
else
|
|
611
|
-
log_acknowledgement_for_unknown message
|
|
612
|
-
end
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
def log_acknowledgement_for_original message, original
|
|
616
|
-
str = "Received #{message.type} for #{original.type} #{message.attribute("oMId")[0..3]}"
|
|
617
|
-
if message.type == 'MessageNotAck'
|
|
618
|
-
reason = message.attributes["rea"]
|
|
619
|
-
str = "#{str}: #{reason}" if reason
|
|
620
|
-
log str, message: message, level: :warning
|
|
621
|
-
else
|
|
622
|
-
log str, message: message, level: :log
|
|
623
|
-
end
|
|
624
|
-
end
|
|
625
|
-
|
|
626
|
-
def log_acknowledgement_for_unknown message
|
|
627
|
-
log "Received #{message.type} for unknown message #{message.attribute("oMId")[0..3]}", message: message, level: :warning
|
|
628
|
-
end
|
|
629
|
-
|
|
630
|
-
def process_watchdog message
|
|
631
|
-
log "Received #{message.type}", message: message, level: :log
|
|
632
|
-
@latest_watchdog_received = Clock.now
|
|
633
|
-
acknowledge message
|
|
634
|
-
end
|
|
635
|
-
|
|
636
|
-
def expect_version_message message
|
|
637
|
-
unless message.is_a?(Version) || message.is_a?(MessageAck) || message.is_a?(MessageNotAck)
|
|
638
|
-
raise HandshakeError.new "Version must be received first"
|
|
639
|
-
end
|
|
640
|
-
end
|
|
641
|
-
|
|
642
|
-
def handshake_complete
|
|
643
|
-
set_state :ready
|
|
644
|
-
end
|
|
645
|
-
|
|
646
|
-
def version_acknowledged
|
|
647
|
-
end
|
|
648
|
-
|
|
649
|
-
def author
|
|
650
|
-
@node.site_id
|
|
651
|
-
end
|
|
652
|
-
|
|
653
|
-
def send_and_optionally_collect message, options, &block
|
|
654
|
-
collect_options = options[:collect] || options[:collect!]
|
|
655
|
-
if collect_options
|
|
656
|
-
task = @task.async do |task|
|
|
657
|
-
task.annotate 'send_and_optionally_collect'
|
|
658
|
-
collector = yield collect_options # call block to create collector
|
|
659
|
-
collector.collect
|
|
660
|
-
collector.ok! if options[:collect!] # raise any errors if the bang version was specified
|
|
661
|
-
collector
|
|
662
|
-
end
|
|
663
|
-
|
|
664
|
-
send_message message, validate: options[:validate]
|
|
665
|
-
{ sent: message, collector: task.wait }
|
|
666
|
-
else
|
|
667
|
-
send_message message, validate: options[:validate]
|
|
668
|
-
return { sent: message }
|
|
669
|
-
end
|
|
670
|
-
end
|
|
671
|
-
|
|
672
|
-
def set_nts_message_attributes message
|
|
673
|
-
message.attributes['ntsOId'] = (main && main.ntsOId) ? main.ntsOId : ''
|
|
674
|
-
message.attributes['xNId'] = (main && main.xNId) ? main.xNId : ''
|
|
675
|
-
end
|
|
676
|
-
|
|
677
|
-
# Use Gem class to check version requirement
|
|
678
|
-
# Requirement must be a string like '1.1', '>=1.0.3' or '<2.1.4',
|
|
679
|
-
# or list of strings, like ['<=1.4','<1.5']
|
|
680
|
-
def self.version_meets_requirement? version, requirement
|
|
681
|
-
Gem::Requirement.new(requirement).satisfied_by?(Gem::Version.new(version))
|
|
682
|
-
end
|
|
683
|
-
|
|
684
|
-
def status_subscribe_acknowledged original
|
|
685
|
-
component = find_component original.attribute('cId')
|
|
686
|
-
return unless component
|
|
687
|
-
short = Message.shorten_m_id original.m_id
|
|
688
|
-
subscribe_list = original.attributes['sS']
|
|
689
|
-
log "StatusSubscribe #{short} acknowledged, allowing repeated status values for #{subscribe_list}", level: :info
|
|
690
|
-
component.allow_repeat_updates subscribe_list
|
|
691
|
-
end
|
|
692
|
-
end
|
|
693
|
-
end
|