rsmp 0.48.4 → 0.51.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/CHANGELOG.md +6 -0
- data/Gemfile.lock +19 -20
- data/README.md +27 -3
- data/Rakefile +3 -1
- data/documentation/cli.md +79 -4
- data/documentation/collecting_message.md +90 -170
- data/documentation/message_distribution.md +17 -3
- data/documentation/tasks.md +65 -117
- data/lib/rsmp/cli.rb +93 -5
- data/lib/rsmp/collect/collector.rb +117 -134
- data/lib/rsmp/collect/distributor.rb +16 -3
- data/lib/rsmp/collect/queue.rb +14 -6
- data/lib/rsmp/collect/receiver.rb +1 -1
- data/lib/rsmp/collect/state_collector.rb +11 -2
- data/lib/rsmp/collection.rb +12 -0
- data/lib/rsmp/completion.rb +49 -0
- data/lib/rsmp/component/component_base.rb +3 -3
- data/lib/rsmp/convert/export/json_schema/index.rb +8 -3
- data/lib/rsmp/convert/import/yaml.rb +12 -8
- data/lib/rsmp/delivery.rb +18 -0
- data/lib/rsmp/event.rb +19 -0
- data/lib/rsmp/event_source.rb +36 -0
- data/lib/rsmp/exchange.rb +10 -0
- data/lib/rsmp/failure.rb +23 -0
- data/lib/rsmp/helpers/error.rb +14 -23
- data/lib/rsmp/message.rb +9 -7
- data/lib/rsmp/node/node.rb +3 -21
- data/lib/rsmp/node/site/connections.rb +4 -9
- data/lib/rsmp/node/site/site.rb +31 -16
- data/lib/rsmp/node/supervisor/modules/connection.rb +19 -5
- data/lib/rsmp/node/supervisor/modules/sites.rb +16 -5
- data/lib/rsmp/node/supervisor/supervisor.rb +13 -14
- data/lib/rsmp/node/task.rb +59 -53
- data/lib/rsmp/proxy/modules/acknowledgements.rb +22 -9
- data/lib/rsmp/proxy/modules/lifecycle.rb +134 -0
- data/lib/rsmp/proxy/modules/receive.rb +109 -66
- data/lib/rsmp/proxy/modules/send.rb +109 -51
- data/lib/rsmp/proxy/modules/state.rb +15 -5
- data/lib/rsmp/proxy/modules/tasks.rb +96 -70
- data/lib/rsmp/proxy/modules/versions.rb +8 -8
- data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
- data/lib/rsmp/proxy/proxy.rb +4 -66
- data/lib/rsmp/proxy/site/connection.rb +27 -22
- data/lib/rsmp/proxy/site/modules/aggregated_status.rb +18 -8
- data/lib/rsmp/proxy/site/modules/alarms.rb +45 -38
- data/lib/rsmp/proxy/site/modules/commands.rb +18 -8
- data/lib/rsmp/proxy/site/modules/status.rb +44 -19
- data/lib/rsmp/proxy/site/site_proxy.rb +12 -10
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/alarms.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/commands.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +8 -9
- data/lib/rsmp/proxy/supervisor/modules/status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +1 -1
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +34 -30
- data/lib/rsmp/result.rb +95 -0
- data/lib/rsmp/schema/validation.rb +5 -3
- data/lib/rsmp/sxl/interface.rb +8 -1
- data/lib/rsmp/sxl/processing/catalogue.rb +61 -0
- data/lib/rsmp/sxl/processing/conflict_checker.rb +30 -0
- data/lib/rsmp/sxl/processing/dependency_graph.rb +37 -0
- data/lib/rsmp/sxl/processing/document.rb +158 -0
- data/lib/rsmp/sxl/processing/error.rb +8 -0
- data/lib/rsmp/sxl/processing/manifest.rb +144 -0
- data/lib/rsmp/sxl/processing/natural_sort.rb +22 -0
- data/lib/rsmp/sxl/processing/resolver.rb +94 -0
- data/lib/rsmp/sxl/processing/version_requirement.rb +86 -0
- data/lib/rsmp/sxl/processing.rb +18 -0
- data/lib/rsmp/sxl/supervisor_interface.rb +8 -1
- data/lib/rsmp/termination.rb +10 -0
- data/lib/rsmp/tlc/modules/plans.rb +0 -2
- data/lib/rsmp/tlc/proxy/control.rb +50 -16
- data/lib/rsmp/tlc/proxy/detectors.rb +15 -4
- data/lib/rsmp/tlc/proxy/io.rb +46 -31
- data/lib/rsmp/tlc/proxy/plans.rb +36 -19
- data/lib/rsmp/tlc/proxy/plans_bang.rb +44 -0
- data/lib/rsmp/tlc/proxy/status.rb +47 -23
- data/lib/rsmp/tlc/proxy/system.rb +16 -4
- data/lib/rsmp/tlc/supervisor_interface.rb +7 -1
- data/lib/rsmp/tlc/traffic_controller_site.rb +6 -9
- data/lib/rsmp/validation.rb +22 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +19 -4
- data/rsmp.gemspec +1 -1
- metadata +25 -4
- data/lib/rsmp/collect/collector/status.rb +0 -34
|
@@ -1,103 +1,129 @@
|
|
|
1
1
|
module RSMP
|
|
2
2
|
class Proxy
|
|
3
3
|
module Modules
|
|
4
|
-
# Reader and timer
|
|
5
|
-
# Handles async tasks for reading from socket and running periodic timers
|
|
4
|
+
# Reader and timer tasks are siblings in one supervised session barrier.
|
|
6
5
|
module Tasks
|
|
7
|
-
|
|
6
|
+
def session_active?(id = @session_id)
|
|
7
|
+
id == @session_id && !@session_closed
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def session_tasks
|
|
11
|
+
@session_tasks ||= Async::Barrier.new(parent: @task)
|
|
12
|
+
end
|
|
13
|
+
|
|
8
14
|
def start_reader
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
id = @session_id
|
|
16
|
+
@reader = session_tasks.async do |task|
|
|
17
|
+
task.annotate "reader session #{id}"
|
|
18
|
+
run_reader(id)
|
|
12
19
|
end
|
|
13
20
|
end
|
|
14
21
|
|
|
15
|
-
def run_reader
|
|
22
|
+
def run_reader(id)
|
|
16
23
|
@stream ||= IO::Stream::Buffered.new(@socket)
|
|
17
|
-
@protocol ||= RSMP::Protocol.new(@stream)
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
@protocol ||= RSMP::Protocol.new(@stream)
|
|
25
|
+
while session_active?(id)
|
|
26
|
+
line = read_protocol_line
|
|
27
|
+
return line if line.failure?
|
|
28
|
+
|
|
29
|
+
process_received_line(line.value)
|
|
20
30
|
end
|
|
21
|
-
|
|
22
|
-
log 'Closing connection', level: :warning
|
|
23
|
-
raise
|
|
24
|
-
rescue EOFError, Async::Stop
|
|
25
|
-
log 'Connection closed', level: :warning
|
|
26
|
-
rescue IOError => e
|
|
27
|
-
log "IOError: #{e}", level: :debug
|
|
28
|
-
rescue Errno::ECONNRESET
|
|
29
|
-
log 'Connection reset by peer', level: :warning
|
|
30
|
-
rescue Errno::EPIPE
|
|
31
|
-
log 'Broken pipe', level: :warning
|
|
32
|
-
rescue StandardError => e
|
|
33
|
-
distribute_error e, level: :internal
|
|
31
|
+
Result.success(:closed)
|
|
34
32
|
end
|
|
35
33
|
|
|
36
|
-
def
|
|
34
|
+
def read_protocol_line
|
|
37
35
|
json = @protocol.read_line
|
|
36
|
+
raise EOFError, 'Connection closed by peer' unless json
|
|
37
|
+
|
|
38
|
+
Result.success(json)
|
|
39
|
+
rescue EOFError => e
|
|
40
|
+
connection_read_failure(:peer_closed, 'Connection closed by peer', e)
|
|
41
|
+
rescue IOError, Errno::ECONNRESET, Errno::EPIPE => e
|
|
42
|
+
connection_read_failure(:transport_failure, e.message, e)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def connection_read_failure(reason, text, error)
|
|
46
|
+
log(text, level: :warning)
|
|
47
|
+
Result.failure(
|
|
48
|
+
:disconnected,
|
|
49
|
+
message: text,
|
|
50
|
+
source: reason == :peer_closed ? :peer : :transport,
|
|
51
|
+
context: { reason: reason, session_id: @session_id },
|
|
52
|
+
cause: error
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Wait for the reader while observing every sibling. A failed timer task
|
|
57
|
+
# raises here with its original exception and backtrace.
|
|
58
|
+
def wait_for_session
|
|
59
|
+
reader = @reader
|
|
60
|
+
observed = session_tasks.wait do |finished|
|
|
61
|
+
result = finished.wait
|
|
62
|
+
break result if finished.equal?(reader)
|
|
63
|
+
break result if result.is_a?(Result::Failure)
|
|
64
|
+
next if finished.cancelled? || !session_active?
|
|
65
|
+
|
|
66
|
+
raise "#{finished.annotation} ended while its connection session was active"
|
|
67
|
+
end
|
|
68
|
+
observed.is_a?(Result::Success) || observed.is_a?(Result::Failure) ? observed : Result.success(:closed)
|
|
69
|
+
ensure
|
|
70
|
+
@session_tasks.cancel if @session_tasks && !@session_tasks.empty?
|
|
71
|
+
@session_tasks = nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def process_received_line(json)
|
|
38
75
|
beginning = Time.now
|
|
39
|
-
|
|
76
|
+
result = process_packet(json)
|
|
77
|
+
log_processing_statistics(result, beginning)
|
|
78
|
+
result
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def log_processing_statistics(result, beginning)
|
|
82
|
+
message = result.success? ? result.value : result.failure.context[:message]
|
|
40
83
|
duration = Time.now - beginning
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
else
|
|
51
|
-
type = 'Unknown'
|
|
52
|
-
m_id = nil
|
|
53
|
-
end
|
|
54
|
-
str = [type, m_id, "processed in #{ms}ms, #{per_second}req/s"].compact.join(' ')
|
|
55
|
-
log str, level: :statistics
|
|
84
|
+
type = message.respond_to?(:type) ? message.type : 'Unknown'
|
|
85
|
+
m_id = message.respond_to?(:m_id) ? Logger.shorten_message_id(message.m_id) : nil
|
|
86
|
+
log([type, m_id, processing_speed(duration)].compact.join(' '), level: :statistics)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def processing_speed(duration)
|
|
90
|
+
milliseconds = (duration * 1000).round(4)
|
|
91
|
+
per_second = duration.positive? ? (1.0 / duration).round : Float::INFINITY
|
|
92
|
+
"processed in #{milliseconds}ms, #{per_second}req/s"
|
|
56
93
|
end
|
|
57
94
|
|
|
58
95
|
def start_timer
|
|
59
|
-
return if @timer
|
|
96
|
+
return if @timer&.running?
|
|
60
97
|
|
|
61
|
-
|
|
98
|
+
id = @session_id
|
|
62
99
|
interval = @site_settings['intervals']['timer'] || 1
|
|
63
|
-
log "Starting
|
|
100
|
+
log "Starting timer with interval #{interval} seconds", level: :debug
|
|
64
101
|
@latest_watchdog_received = Clock.now
|
|
65
|
-
@timer =
|
|
66
|
-
task.annotate
|
|
67
|
-
run_timer
|
|
102
|
+
@timer = session_tasks.async do |task|
|
|
103
|
+
task.annotate "timer session #{id}"
|
|
104
|
+
run_timer(task, interval, id)
|
|
68
105
|
end
|
|
69
106
|
end
|
|
70
107
|
|
|
71
|
-
def run_timer(task, interval)
|
|
108
|
+
def run_timer(task, interval, id)
|
|
72
109
|
next_time = Time.now.to_f
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
rescue RSMP::Schema::Error => e
|
|
78
|
-
log "Timer: Schema error: #{e}", level: :warning
|
|
79
|
-
rescue EOFError => e
|
|
80
|
-
log "Timer: Connection closed: #{e}", level: :warning
|
|
81
|
-
rescue IOError
|
|
82
|
-
log 'Timer: IOError', level: :warning
|
|
83
|
-
rescue Errno::ECONNRESET
|
|
84
|
-
log 'Timer: Connection reset by peer', level: :warning
|
|
85
|
-
rescue Errno::EPIPE
|
|
86
|
-
log 'Timer: Broken pipe', level: :warning
|
|
87
|
-
rescue StandardError => e
|
|
88
|
-
distribute_error e, level: :internal
|
|
89
|
-
end
|
|
90
|
-
ensure
|
|
110
|
+
while session_active?(id)
|
|
111
|
+
result = timer(Clock.now)
|
|
112
|
+
return result if result.failure?
|
|
113
|
+
|
|
91
114
|
next_time += interval
|
|
92
|
-
|
|
93
|
-
task.sleep duration
|
|
115
|
+
task.sleep([next_time - Time.now.to_f, 0].max) if session_active?(id)
|
|
94
116
|
end
|
|
117
|
+
Result.success(:closed)
|
|
95
118
|
end
|
|
96
119
|
|
|
97
120
|
def timer(now)
|
|
98
|
-
watchdog_send_timer
|
|
99
|
-
check_ack_timeout
|
|
100
|
-
|
|
121
|
+
watchdog_send_timer(now)
|
|
122
|
+
acknowledgement = check_ack_timeout(now)
|
|
123
|
+
return acknowledgement if acknowledgement.failure?
|
|
124
|
+
|
|
125
|
+
check_watchdog_timeout(now)
|
|
126
|
+
Result.success
|
|
101
127
|
end
|
|
102
128
|
end
|
|
103
129
|
end
|
|
@@ -66,13 +66,13 @@ module RSMP
|
|
|
66
66
|
|
|
67
67
|
def send_version_response(site_id, core_versions)
|
|
68
68
|
if core_3_3?
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
send_generated_message Version.new({
|
|
70
|
+
'step' => 'Response',
|
|
71
|
+
'RSMP' => [{ 'vers' => core_version }],
|
|
72
|
+
'supervisorId' => site_id,
|
|
73
|
+
'SXLS' => version_response_sxls,
|
|
74
|
+
'receiveAlarms' => @site_settings['receive_alarms'] != false
|
|
75
|
+
}), validate: false
|
|
76
76
|
else
|
|
77
77
|
send_version_message(site_id, core_versions, step: nil)
|
|
78
78
|
end
|
|
@@ -81,7 +81,7 @@ module RSMP
|
|
|
81
81
|
def send_version_message(site_id, core_versions, step:)
|
|
82
82
|
attributes = version_message_attributes(site_id, core_versions)
|
|
83
83
|
attributes.merge!(version_request_attributes) if step == 'Request'
|
|
84
|
-
|
|
84
|
+
send_generated_message Version.new(attributes), validate: false
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def version_message_attributes(site_id, core_versions)
|
|
@@ -40,7 +40,7 @@ module RSMP
|
|
|
40
40
|
|
|
41
41
|
def send_watchdog(now = Clock.now)
|
|
42
42
|
message = RSMP::Watchdog.new({ 'wTs' => clock.to_s })
|
|
43
|
-
|
|
43
|
+
send_generated_message message
|
|
44
44
|
@latest_watchdog_send_at = now
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -52,7 +52,20 @@ module RSMP
|
|
|
52
52
|
|
|
53
53
|
str = "No Watchdog received within #{timeout} seconds"
|
|
54
54
|
log str, level: :warning
|
|
55
|
-
|
|
55
|
+
failure = Failure.new(
|
|
56
|
+
code: :missing_watchdog,
|
|
57
|
+
message: str,
|
|
58
|
+
source: :peer,
|
|
59
|
+
context: { session_id: session_id }
|
|
60
|
+
)
|
|
61
|
+
distribute_event(
|
|
62
|
+
Event.new(
|
|
63
|
+
type: :missing_watchdog,
|
|
64
|
+
source: self,
|
|
65
|
+
session_id: session_id,
|
|
66
|
+
failure: failure
|
|
67
|
+
)
|
|
68
|
+
)
|
|
56
69
|
end
|
|
57
70
|
|
|
58
71
|
def process_watchdog(message)
|
data/lib/rsmp/proxy/proxy.rb
CHANGED
|
@@ -16,10 +16,11 @@ module RSMP
|
|
|
16
16
|
include Modules::Receive
|
|
17
17
|
include Modules::Versions
|
|
18
18
|
include Modules::Tasks
|
|
19
|
+
include Modules::Lifecycle
|
|
19
20
|
|
|
20
21
|
attr_reader :state, :archive, :connection_info, :sxls, :accepted_sxls, :rejected_sxls,
|
|
21
22
|
:collector, :ip, :port, :node, :core_version, :sxl_interfaces,
|
|
22
|
-
:site_settings
|
|
23
|
+
:site_settings, :session_id
|
|
23
24
|
|
|
24
25
|
def initialize(options)
|
|
25
26
|
@node = options[:node]
|
|
@@ -31,6 +32,8 @@ module RSMP
|
|
|
31
32
|
clear
|
|
32
33
|
@state = :disconnected
|
|
33
34
|
@state_condition = Async::Notification.new
|
|
35
|
+
@session_id = 0
|
|
36
|
+
@session_closed = true
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def inspect
|
|
@@ -41,67 +44,6 @@ module RSMP
|
|
|
41
44
|
node.now
|
|
42
45
|
end
|
|
43
46
|
|
|
44
|
-
# Connection lifecycle methods
|
|
45
|
-
|
|
46
|
-
def disconnect; end
|
|
47
|
-
|
|
48
|
-
# wait for the reader task to complete,
|
|
49
|
-
# which is not expected to happen before the connection is closed
|
|
50
|
-
def wait_for_reader
|
|
51
|
-
@reader&.wait
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# close connection, but keep our main task running so we can reconnect
|
|
55
|
-
def close
|
|
56
|
-
log 'Closing connection', level: :warning
|
|
57
|
-
close_stream
|
|
58
|
-
close_socket
|
|
59
|
-
stop_reader
|
|
60
|
-
self.state = :disconnected
|
|
61
|
-
distribute_error DisconnectError.new('Connection was closed')
|
|
62
|
-
|
|
63
|
-
# stop timer
|
|
64
|
-
# as we're running inside the timer, code after stop_timer() will not be called,
|
|
65
|
-
# unless it's in the ensure block
|
|
66
|
-
stop_timer
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def stop_subtasks
|
|
70
|
-
stop_timer
|
|
71
|
-
stop_reader
|
|
72
|
-
clear
|
|
73
|
-
super
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def stop_timer
|
|
77
|
-
@timer&.stop
|
|
78
|
-
ensure
|
|
79
|
-
@timer = nil
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def stop_reader
|
|
83
|
-
@reader&.stop
|
|
84
|
-
ensure
|
|
85
|
-
@reader = nil
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def close_stream
|
|
89
|
-
@stream&.close
|
|
90
|
-
ensure
|
|
91
|
-
@stream = nil
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def close_socket
|
|
95
|
-
@socket&.close
|
|
96
|
-
ensure
|
|
97
|
-
@socket = nil
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def stop_task
|
|
101
|
-
close
|
|
102
|
-
super
|
|
103
|
-
end
|
|
104
|
-
|
|
105
47
|
# State management methods
|
|
106
48
|
|
|
107
49
|
def ready?
|
|
@@ -174,10 +116,6 @@ module RSMP
|
|
|
174
116
|
@node.clock
|
|
175
117
|
end
|
|
176
118
|
|
|
177
|
-
def receive_error(error, options = {})
|
|
178
|
-
@node.receive_error error, options
|
|
179
|
-
end
|
|
180
|
-
|
|
181
119
|
def log(str, options = {})
|
|
182
120
|
super(str, options.merge(ip: @ip, port: @port, site_id: @site_id))
|
|
183
121
|
end
|
|
@@ -12,50 +12,46 @@ module RSMP
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def run_accepted_connection
|
|
15
|
+
begin_session
|
|
15
16
|
self.state = :connected
|
|
16
17
|
start_reader
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
log e, level: :error
|
|
20
|
-
rescue StandardError => e
|
|
21
|
-
distribute_error e, level: :internal
|
|
18
|
+
ended = wait_for_reader
|
|
19
|
+
close_from_result(ended)
|
|
22
20
|
ensure
|
|
21
|
+
close(reason: :internal_failure) if $ERROR_INFO
|
|
23
22
|
close
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def run_outbound_connection
|
|
27
26
|
loop do
|
|
28
27
|
setup_site_settings
|
|
29
|
-
connect
|
|
28
|
+
connected = connect
|
|
29
|
+
unless connected.success?
|
|
30
|
+
publish_connection_attempt_failure(connected.failure)
|
|
31
|
+
break unless reconnect_delay?
|
|
32
|
+
|
|
33
|
+
next
|
|
34
|
+
end
|
|
30
35
|
start_reader
|
|
31
|
-
wait_for_reader
|
|
32
|
-
break unless reconnect_delay?
|
|
33
|
-
rescue Restart
|
|
34
|
-
@logger.mute @ip, @port
|
|
35
|
-
raise
|
|
36
|
-
rescue RSMP::ConnectionError => e
|
|
37
|
-
log e, level: :error
|
|
38
|
-
break unless reconnect_delay?
|
|
39
|
-
rescue StandardError => e
|
|
40
|
-
distribute_error e, level: :internal
|
|
36
|
+
close_from_result(wait_for_reader)
|
|
41
37
|
break unless reconnect_delay?
|
|
42
38
|
ensure
|
|
39
|
+
close(reason: :internal_failure) if $ERROR_INFO
|
|
43
40
|
close
|
|
44
|
-
stop_subtasks
|
|
45
41
|
end
|
|
46
42
|
end
|
|
47
43
|
|
|
48
44
|
def connect
|
|
49
45
|
log "Connecting to site #{@site_id} at #{@ip}:#{@port}", level: :info
|
|
46
|
+
begin_session
|
|
50
47
|
self.state = :connecting
|
|
51
|
-
open_socket
|
|
48
|
+
opened = open_socket
|
|
49
|
+
return opened if opened.failure?
|
|
50
|
+
|
|
52
51
|
self.state = :connected
|
|
53
52
|
@logger.unmute @ip, @port
|
|
54
53
|
log "Connected to site #{@site_id} at #{@ip}:#{@port}", level: :info
|
|
55
|
-
|
|
56
|
-
raise ConnectionError, "Could not connect to site #{@site_id} at #{@ip}:#{@port}: Errno #{e.errno} #{e}"
|
|
57
|
-
rescue StandardError => e
|
|
58
|
-
raise ConnectionError, "Error while connecting to site #{@site_id} at #{@ip}:#{@port}: #{e}"
|
|
54
|
+
Result.success(self)
|
|
59
55
|
end
|
|
60
56
|
|
|
61
57
|
def open_socket
|
|
@@ -64,6 +60,15 @@ module RSMP
|
|
|
64
60
|
task.with_timeout(timeout) { @socket = endpoint.connect }
|
|
65
61
|
@stream = IO::Stream::Buffered.new(@socket)
|
|
66
62
|
@protocol = RSMP::Protocol.new(@stream)
|
|
63
|
+
Result.success(self)
|
|
64
|
+
rescue SystemCallError, SocketError, IOError, Async::TimeoutError => e
|
|
65
|
+
Result.failure(
|
|
66
|
+
:connection_failed,
|
|
67
|
+
message: "Could not connect to site #{@site_id} at #{@ip}:#{@port}: #{e.message}",
|
|
68
|
+
source: :transport,
|
|
69
|
+
context: { ip: @ip, port: @port, session_id: @session_id },
|
|
70
|
+
cause: e
|
|
71
|
+
)
|
|
67
72
|
end
|
|
68
73
|
|
|
69
74
|
def reconnect_delay?
|
|
@@ -3,23 +3,29 @@ module RSMP
|
|
|
3
3
|
module Modules
|
|
4
4
|
# Handles aggregated status requests and responses
|
|
5
5
|
module AggregatedStatus
|
|
6
|
-
# Build and send an AggregatedStatusRequest. Returns
|
|
6
|
+
# Build and send an AggregatedStatusRequest. Returns Result<AggregatedStatusRequest>.
|
|
7
7
|
def request_aggregated_status(component, m_id: nil, validate: true)
|
|
8
|
-
validate_ready 'request aggregated status'
|
|
8
|
+
readiness = validate_ready 'request aggregated status'
|
|
9
|
+
return readiness if readiness.failure?
|
|
10
|
+
|
|
9
11
|
m_id ||= RSMP::Message.make_m_id
|
|
10
12
|
message = RSMP::AggregatedStatusRequest.new({
|
|
11
13
|
'cId' => component,
|
|
12
14
|
'mId' => m_id
|
|
13
15
|
})
|
|
14
16
|
apply_nts_message_attributes message
|
|
15
|
-
send_message
|
|
16
|
-
|
|
17
|
+
send_message(message, validate: validate).map(&:message)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def request_aggregated_status!(...)
|
|
21
|
+
request_aggregated_status(...).value!
|
|
17
22
|
end
|
|
18
23
|
|
|
19
|
-
# Build, send an AggregatedStatusRequest and collect the response. Returns
|
|
20
|
-
# Call .ok! on the result to raise on NotAck or timeout.
|
|
24
|
+
# Build, send an AggregatedStatusRequest and collect the response. Returns Result<Exchange>.
|
|
21
25
|
def request_aggregated_status_and_collect(component, within:, m_id: nil, validate: true)
|
|
22
|
-
validate_ready 'request aggregated status'
|
|
26
|
+
readiness = validate_ready 'request aggregated status'
|
|
27
|
+
return readiness if readiness.failure?
|
|
28
|
+
|
|
23
29
|
m_id ||= RSMP::Message.make_m_id
|
|
24
30
|
message = RSMP::AggregatedStatusRequest.new({
|
|
25
31
|
'cId' => component,
|
|
@@ -27,7 +33,11 @@ module RSMP
|
|
|
27
33
|
})
|
|
28
34
|
apply_nts_message_attributes message
|
|
29
35
|
collector = AggregatedStatusCollector.new(self, timeout: within, m_id: m_id, num: 1)
|
|
30
|
-
send_message_and_collect(message, collector, validate: validate)
|
|
36
|
+
send_message_and_collect(message, collector, validate: validate)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def request_aggregated_status_and_collect!(...)
|
|
40
|
+
request_aggregated_status_and_collect(...).value!
|
|
31
41
|
end
|
|
32
42
|
|
|
33
43
|
def validate_aggregated_status(message, status_elements)
|
|
@@ -18,67 +18,74 @@ module RSMP
|
|
|
18
18
|
'cId' => component,
|
|
19
19
|
'aCId' => alarm_code
|
|
20
20
|
})
|
|
21
|
-
send_message
|
|
22
|
-
|
|
21
|
+
send_message(message, validate: options[:validate]).map(&:message)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def send_alarm_acknowledgement!(...)
|
|
25
|
+
send_alarm_acknowledgement(...).value!
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
# Send an AlarmSuspend message and optionally collect the confirming response.
|
|
26
|
-
#
|
|
27
|
-
def suspend_alarm(
|
|
29
|
+
# Returns Result<Exchange> when collecting and Result<AlarmSuspend> otherwise.
|
|
30
|
+
def suspend_alarm(c_id:, a_c_id:, collect: false)
|
|
28
31
|
message = RSMP::AlarmSuspend.new(
|
|
29
32
|
'mId' => RSMP::Message.make_m_id,
|
|
30
33
|
'cId' => c_id,
|
|
31
34
|
'aCId' => a_c_id
|
|
32
35
|
)
|
|
33
36
|
if collect
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
[message, collect_task.wait.first]
|
|
37
|
+
collector = RSMP::AlarmCollector.new(
|
|
38
|
+
self,
|
|
39
|
+
m_id: message.m_id,
|
|
40
|
+
num: 1,
|
|
41
|
+
matcher: {
|
|
42
|
+
'cId' => c_id,
|
|
43
|
+
'aCId' => a_c_id,
|
|
44
|
+
'aSp' => 'Suspend',
|
|
45
|
+
'sS' => /^Suspended/i
|
|
46
|
+
},
|
|
47
|
+
timeout: node.supervisor_settings.dig('default', 'timeouts', 'alarm')
|
|
48
|
+
)
|
|
49
|
+
send_message_and_collect(message, collector)
|
|
48
50
|
else
|
|
49
|
-
send_message
|
|
50
|
-
message
|
|
51
|
+
send_message(message).map(&:message)
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
def suspend_alarm!(...)
|
|
56
|
+
suspend_alarm(...).value!
|
|
57
|
+
end
|
|
58
|
+
|
|
54
59
|
# Send an AlarmResume message and optionally collect the confirming response.
|
|
55
|
-
#
|
|
56
|
-
def resume_alarm(
|
|
60
|
+
# Returns Result<Exchange> when collecting and Result<AlarmResume> otherwise.
|
|
61
|
+
def resume_alarm(c_id:, a_c_id:, collect: false)
|
|
57
62
|
message = RSMP::AlarmResume.new(
|
|
58
63
|
'mId' => RSMP::Message.make_m_id,
|
|
59
64
|
'cId' => c_id,
|
|
60
65
|
'aCId' => a_c_id
|
|
61
66
|
)
|
|
62
67
|
if collect
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[message, collect_task.wait.first]
|
|
68
|
+
collector = RSMP::AlarmCollector.new(
|
|
69
|
+
self,
|
|
70
|
+
m_id: message.m_id,
|
|
71
|
+
num: 1,
|
|
72
|
+
matcher: {
|
|
73
|
+
'cId' => c_id,
|
|
74
|
+
'aCId' => a_c_id,
|
|
75
|
+
'aSp' => 'Suspend',
|
|
76
|
+
'sS' => /^notSuspended/i
|
|
77
|
+
},
|
|
78
|
+
timeout: node.supervisor_settings.dig('default', 'timeouts', 'alarm')
|
|
79
|
+
)
|
|
80
|
+
send_message_and_collect(message, collector)
|
|
77
81
|
else
|
|
78
|
-
send_message
|
|
79
|
-
message
|
|
82
|
+
send_message(message).map(&:message)
|
|
80
83
|
end
|
|
81
84
|
end
|
|
85
|
+
|
|
86
|
+
def resume_alarm!(...)
|
|
87
|
+
resume_alarm(...).value!
|
|
88
|
+
end
|
|
82
89
|
end
|
|
83
90
|
end
|
|
84
91
|
end
|
|
@@ -3,9 +3,11 @@ module RSMP
|
|
|
3
3
|
module Modules
|
|
4
4
|
# Handles command requests and responses
|
|
5
5
|
module Commands
|
|
6
|
-
# Build and send a CommandRequest. Returns
|
|
6
|
+
# Build and send a CommandRequest. Returns Result<CommandRequest>.
|
|
7
7
|
def send_command(command_list, component: nil, validate: true)
|
|
8
|
-
validate_ready 'send command'
|
|
8
|
+
readiness = validate_ready 'send command'
|
|
9
|
+
return readiness if readiness.failure?
|
|
10
|
+
|
|
9
11
|
component ||= main.c_id
|
|
10
12
|
m_id = RSMP::Message.make_m_id
|
|
11
13
|
message = RSMP::CommandRequest.new({
|
|
@@ -14,14 +16,18 @@ module RSMP
|
|
|
14
16
|
'mId' => m_id
|
|
15
17
|
})
|
|
16
18
|
apply_nts_message_attributes message
|
|
17
|
-
send_message
|
|
18
|
-
|
|
19
|
+
send_message(message, validate: validate).map(&:message)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def send_command!(...)
|
|
23
|
+
send_command(...).value!
|
|
19
24
|
end
|
|
20
25
|
|
|
21
|
-
# Build, send a CommandRequest and collect
|
|
22
|
-
# Raises on NotAck or timeout if ok! is called on the result.
|
|
26
|
+
# Build, send a CommandRequest and collect its responses. Returns Result<Exchange>.
|
|
23
27
|
def send_command_and_collect(command_list, within:, component: nil, validate: true)
|
|
24
|
-
validate_ready 'send command'
|
|
28
|
+
readiness = validate_ready 'send command'
|
|
29
|
+
return readiness if readiness.failure?
|
|
30
|
+
|
|
25
31
|
component ||= main.c_id
|
|
26
32
|
m_id = RSMP::Message.make_m_id
|
|
27
33
|
collector_list = command_collector_list(command_list)
|
|
@@ -32,7 +38,11 @@ module RSMP
|
|
|
32
38
|
})
|
|
33
39
|
apply_nts_message_attributes message
|
|
34
40
|
collector = CommandResponseCollector.new(self, collector_list, timeout: within, initiator: message)
|
|
35
|
-
send_message_and_collect(message, collector, validate: validate)
|
|
41
|
+
send_message_and_collect(message, collector, validate: validate)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def send_command_and_collect!(...)
|
|
45
|
+
send_command_and_collect(...).value!
|
|
36
46
|
end
|
|
37
47
|
|
|
38
48
|
def command_collector_list(command_list)
|