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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
class SupervisorProxy < Proxy
|
|
3
|
+
module Modules
|
|
4
|
+
# Alarm handling
|
|
5
|
+
module Alarms
|
|
6
|
+
def send_alarm(_component, alarm, options = {})
|
|
7
|
+
send_and_optionally_collect alarm, options do |collect_options|
|
|
8
|
+
Collector.new self, collect_options.merge(task: @task, type: 'MessageAck')
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def send_active_alarms
|
|
13
|
+
@site.components.each_pair do |_c_id, component|
|
|
14
|
+
component.alarms.each_pair do |_alarm_code, alarm_state|
|
|
15
|
+
if alarm_state.active
|
|
16
|
+
alarm = AlarmIssue.new(alarm_state.to_hash.merge('aSp' => 'Issue'))
|
|
17
|
+
send_message alarm
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def process_alarm(message)
|
|
24
|
+
case message
|
|
25
|
+
when AlarmAcknowledge
|
|
26
|
+
handle_alarm_acknowledge message
|
|
27
|
+
when AlarmSuspend
|
|
28
|
+
handle_alarm_suspend message
|
|
29
|
+
when AlarmResume
|
|
30
|
+
handle_alarm_resume message
|
|
31
|
+
when AlarmRequest
|
|
32
|
+
handle_alarm_request message
|
|
33
|
+
else
|
|
34
|
+
dont_acknowledge message, 'Invalid alarm message type'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def handle_alarm_acknowledge(message)
|
|
39
|
+
component_id = message.attributes['cId']
|
|
40
|
+
component = @site.find_component component_id
|
|
41
|
+
alarm_code = message.attribute('aCId')
|
|
42
|
+
log "Received #{message.type} #{alarm_code} acknowledgement", message: message, level: :log
|
|
43
|
+
acknowledge message
|
|
44
|
+
component.acknowledge_alarm alarm_code
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def handle_alarm_suspend(message)
|
|
48
|
+
component_id = message.attributes['cId']
|
|
49
|
+
component = @site.find_component component_id
|
|
50
|
+
alarm_code = message.attribute('aCId')
|
|
51
|
+
log "Received #{message.type} #{alarm_code} suspend", message: message, level: :log
|
|
52
|
+
acknowledge message
|
|
53
|
+
component.suspend_alarm alarm_code
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_alarm_resume(message)
|
|
57
|
+
component_id = message.attributes['cId']
|
|
58
|
+
component = @site.find_component component_id
|
|
59
|
+
alarm_code = message.attribute('aCId')
|
|
60
|
+
log "Received #{message.type} #{alarm_code} resume", message: message, level: :log
|
|
61
|
+
acknowledge message
|
|
62
|
+
component.resume_alarm alarm_code
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def handle_alarm_request(message)
|
|
66
|
+
log "Received #{message.type}", message: message, level: :log
|
|
67
|
+
acknowledge message
|
|
68
|
+
send_active_alarms
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
class SupervisorProxy < Proxy
|
|
3
|
+
module Modules
|
|
4
|
+
# Command request handling
|
|
5
|
+
module Commands
|
|
6
|
+
def simplify_command_requests(arg)
|
|
7
|
+
sorted = {}
|
|
8
|
+
arg.each do |item|
|
|
9
|
+
sorted[item['cCI']] ||= {}
|
|
10
|
+
sorted[item['cCI']][item['n']] = item['v']
|
|
11
|
+
end
|
|
12
|
+
sorted
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def build_command_rvs(args)
|
|
16
|
+
args.map do |item|
|
|
17
|
+
item = item.dup.merge('age' => 'recent')
|
|
18
|
+
item.delete 'cO'
|
|
19
|
+
item
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def execute_commands(message, component_id, rvs)
|
|
24
|
+
component = @site.find_component component_id
|
|
25
|
+
commands = simplify_command_requests message.attributes['arg']
|
|
26
|
+
commands.each_pair do |command_code, arg|
|
|
27
|
+
component.handle_command command_code, arg
|
|
28
|
+
end
|
|
29
|
+
log "Received #{message.type}", message: message, level: :log
|
|
30
|
+
rescue UnknownComponent
|
|
31
|
+
log "Received #{message.type} with unknown component id '#{component_id}' and cannot infer type",
|
|
32
|
+
message: message, level: :warning
|
|
33
|
+
rvs.map { |item| item['age'] = 'undefined' }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def process_command_request(message)
|
|
37
|
+
component_id = message.attributes['cId']
|
|
38
|
+
rvs = build_command_rvs(message.attributes['arg'])
|
|
39
|
+
execute_commands(message, component_id, rvs)
|
|
40
|
+
|
|
41
|
+
response = CommandResponse.new({
|
|
42
|
+
'cId' => component_id,
|
|
43
|
+
'cTS' => clock.to_s,
|
|
44
|
+
'rvs' => rvs
|
|
45
|
+
})
|
|
46
|
+
apply_nts_message_attributes response
|
|
47
|
+
acknowledge message
|
|
48
|
+
send_message response
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
class SupervisorProxy < Proxy
|
|
3
|
+
module Modules
|
|
4
|
+
# Status request and subscription handling
|
|
5
|
+
module Status
|
|
6
|
+
def rsmpify_value(value, quality)
|
|
7
|
+
if value.is_a?(Array) || value.is_a?(Set)
|
|
8
|
+
value
|
|
9
|
+
elsif %w[undefined unknown].include?(quality.to_s)
|
|
10
|
+
nil
|
|
11
|
+
else
|
|
12
|
+
value.to_s
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fetch_status_values(component, args)
|
|
17
|
+
args.map do |arg|
|
|
18
|
+
value, quality = component.get_status arg['sCI'], arg['n'], { sxl_version: sxl_version }
|
|
19
|
+
{ 's' => rsmpify_value(value, quality), 'q' => quality.to_s }.merge arg
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def build_undefined_statuses(args)
|
|
24
|
+
args.map { |arg| arg.dup.merge('q' => 'undefined', 's' => nil) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def process_status_request(message, options = {})
|
|
28
|
+
component_id = message.attributes['cId']
|
|
29
|
+
args = message.attributes['sS']
|
|
30
|
+
|
|
31
|
+
begin
|
|
32
|
+
component = @site.find_component component_id
|
|
33
|
+
ss = fetch_status_values(component, args)
|
|
34
|
+
log "Received #{message.type}", message: message, level: :log
|
|
35
|
+
rescue UnknownComponent
|
|
36
|
+
log "Received #{message.type} with unknown component id '#{component_id}' and cannot infer type",
|
|
37
|
+
message: message, level: :warning
|
|
38
|
+
ss = build_undefined_statuses(args)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
response = StatusResponse.new({
|
|
42
|
+
'cId' => component_id,
|
|
43
|
+
'sTs' => clock.to_s,
|
|
44
|
+
'sS' => ss,
|
|
45
|
+
'mId' => options[:m_id]
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
apply_nts_message_attributes response
|
|
49
|
+
acknowledge message
|
|
50
|
+
send_message response
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def add_status_subscription(component_id, subs, update_list, arg, now)
|
|
54
|
+
sci = arg['sCI']
|
|
55
|
+
name = arg['n']
|
|
56
|
+
subcription = { interval: arg['uRt'].to_i, last_sent_at: now }
|
|
57
|
+
subs[sci] ||= {}
|
|
58
|
+
subs[sci][name] = subcription
|
|
59
|
+
update_list[component_id][sci] ||= []
|
|
60
|
+
update_list[component_id][sci] << name
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def process_status_subcribe(message)
|
|
64
|
+
log "Received #{message.type}", message: message, level: :log
|
|
65
|
+
|
|
66
|
+
update_list = {}
|
|
67
|
+
component_id = message.attributes['cId']
|
|
68
|
+
@status_subscriptions[component_id] ||= {}
|
|
69
|
+
update_list[component_id] ||= {}
|
|
70
|
+
now = Time.now
|
|
71
|
+
subs = @status_subscriptions[component_id]
|
|
72
|
+
|
|
73
|
+
message.attributes['sS'].each do |arg|
|
|
74
|
+
add_status_subscription(component_id, subs, update_list, arg, now)
|
|
75
|
+
end
|
|
76
|
+
acknowledge message
|
|
77
|
+
send_status_updates update_list
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def get_status_subscribe_interval(component_id, sci, name)
|
|
81
|
+
@status_subscriptions.dig component_id, sci, name
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def remove_status_subscription(subs, arg)
|
|
85
|
+
sci = arg['sCI']
|
|
86
|
+
return unless subs[sci]
|
|
87
|
+
|
|
88
|
+
subs[sci].delete arg['n']
|
|
89
|
+
subs.delete(sci) if subs[sci].empty?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def process_status_unsubcribe(message)
|
|
93
|
+
log "Received #{message.type}", message: message, level: :log
|
|
94
|
+
component = message.attributes['cId']
|
|
95
|
+
|
|
96
|
+
subs = @status_subscriptions[component]
|
|
97
|
+
if subs
|
|
98
|
+
message.attributes['sS'].each { |arg| remove_status_subscription(subs, arg) }
|
|
99
|
+
@status_subscriptions.delete(component) if subs.empty?
|
|
100
|
+
end
|
|
101
|
+
acknowledge message
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def fetch_last_sent_status(component, code, name)
|
|
105
|
+
@last_status_sent&.dig component, code, name
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def store_last_sent_status(message)
|
|
109
|
+
component_id = message.attribute('cId')
|
|
110
|
+
@last_status_sent ||= {}
|
|
111
|
+
@last_status_sent[component_id] ||= {}
|
|
112
|
+
message.attribute('sS').each do |item|
|
|
113
|
+
sci = item['sCI']
|
|
114
|
+
n = item['n']
|
|
115
|
+
s = item['s']
|
|
116
|
+
@last_status_sent[component_id][sci] ||= {}
|
|
117
|
+
@last_status_sent[component_id][sci][n] = s
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def check_on_change_update(subscription, component, code, name)
|
|
122
|
+
return [nil, false] unless subscription[:interval].zero?
|
|
123
|
+
|
|
124
|
+
current = nil
|
|
125
|
+
if component
|
|
126
|
+
current, quality = *(component.get_status code, name)
|
|
127
|
+
current = rsmpify_value(current, quality)
|
|
128
|
+
end
|
|
129
|
+
last_sent = fetch_last_sent_status component.c_id, code, name
|
|
130
|
+
[current, current != last_sent]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def interval_update_due?(subscription, now)
|
|
134
|
+
return true if subscription[:last_sent_at].nil?
|
|
135
|
+
|
|
136
|
+
(now - subscription[:last_sent_at]) >= subscription[:interval]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def check_status_subscription(subscription, component, code, name, now)
|
|
140
|
+
current, should_send = check_on_change_update(subscription, component, code, name)
|
|
141
|
+
should_send ||= interval_update_due?(subscription, now)
|
|
142
|
+
return [nil, false] unless should_send
|
|
143
|
+
|
|
144
|
+
subscription[:last_sent_at] = now
|
|
145
|
+
[current, true]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def status_update_timer(now)
|
|
149
|
+
update_list = {}
|
|
150
|
+
|
|
151
|
+
@status_subscriptions.each_pair do |component_id, by_code|
|
|
152
|
+
component = @site.find_component component_id
|
|
153
|
+
by_code.each_pair do |code, by_name|
|
|
154
|
+
by_name.each_pair do |name, subscription|
|
|
155
|
+
current, should_send = check_status_subscription(subscription, component, code, name, now)
|
|
156
|
+
next unless should_send
|
|
157
|
+
|
|
158
|
+
update_list[component_id] ||= {}
|
|
159
|
+
update_list[component_id][code] ||= {}
|
|
160
|
+
update_list[component_id][code][name] = current
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
send_status_updates update_list
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def build_status_list(component, by_code)
|
|
168
|
+
ss = []
|
|
169
|
+
by_code.each_pair do |code, names|
|
|
170
|
+
names.map do |status_name, value|
|
|
171
|
+
if value
|
|
172
|
+
quality = 'recent'
|
|
173
|
+
else
|
|
174
|
+
value, quality = component.get_status code, status_name
|
|
175
|
+
end
|
|
176
|
+
ss << { 'sCI' => code,
|
|
177
|
+
'n' => status_name,
|
|
178
|
+
's' => rsmpify_value(value, quality),
|
|
179
|
+
'q' => quality }
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
ss
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def send_status_updates(update_list)
|
|
186
|
+
now = clock.to_s
|
|
187
|
+
update_list.each_pair do |component_id, by_code|
|
|
188
|
+
component = @site.find_component component_id
|
|
189
|
+
ss = build_status_list(component, by_code)
|
|
190
|
+
update = StatusUpdate.new({
|
|
191
|
+
'cId' => component_id,
|
|
192
|
+
'sTs' => now,
|
|
193
|
+
'sS' => ss
|
|
194
|
+
})
|
|
195
|
+
apply_nts_message_attributes update
|
|
196
|
+
send_message update
|
|
197
|
+
store_last_sent_status update
|
|
198
|
+
component.status_updates_sent
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Handles a site connection to a remote supervisor
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
|
|
5
|
+
module RSMP
|
|
6
|
+
class SupervisorProxy < Proxy
|
|
7
|
+
include Modules::Status
|
|
8
|
+
include Modules::Commands
|
|
9
|
+
include Modules::Alarms
|
|
10
|
+
include Modules::AggregatedStatus
|
|
11
|
+
|
|
12
|
+
attr_reader :supervisor_id, :site
|
|
13
|
+
|
|
14
|
+
def initialize(options)
|
|
15
|
+
super(options.merge(node: options[:site]))
|
|
16
|
+
@site = options[:site]
|
|
17
|
+
@site_settings = @site.site_settings.clone
|
|
18
|
+
@ip = options[:ip]
|
|
19
|
+
@port = options[:port]
|
|
20
|
+
@status_subscriptions = {}
|
|
21
|
+
@sxl = @site_settings['sxl']
|
|
22
|
+
@synthetic_id = Supervisor.build_id_from_ip_port @ip, @port
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# handle communication
|
|
26
|
+
# if disconnected, then try to reconnect
|
|
27
|
+
def run
|
|
28
|
+
loop do
|
|
29
|
+
connect
|
|
30
|
+
start_reader
|
|
31
|
+
start_handshake
|
|
32
|
+
wait_for_reader # run until disconnected
|
|
33
|
+
break unless reconnect_delay?
|
|
34
|
+
rescue Restart
|
|
35
|
+
@logger.mute @ip, @port
|
|
36
|
+
raise
|
|
37
|
+
rescue RSMP::ConnectionError => e
|
|
38
|
+
log e, level: :error
|
|
39
|
+
break unless reconnect_delay?
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
distribute_error e, level: :internal
|
|
42
|
+
break unless reconnect_delay?
|
|
43
|
+
ensure
|
|
44
|
+
close
|
|
45
|
+
stop_subtasks
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def start_handshake
|
|
50
|
+
send_version @site_settings['site_id'], core_versions
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# connect to the supervisor and initiate handshake supervisor
|
|
54
|
+
def connect
|
|
55
|
+
log "Connecting to supervisor at #{@ip}:#{@port}", level: :info
|
|
56
|
+
self.state = :connecting
|
|
57
|
+
connect_tcp
|
|
58
|
+
@logger.unmute @ip, @port
|
|
59
|
+
log "Connected to supervisor at #{@ip}:#{@port}", level: :info
|
|
60
|
+
rescue SystemCallError => e
|
|
61
|
+
raise ConnectionError, "Could not connect to supervisor at #{@ip}:#{@port}: Errno #{e.errno} #{e}"
|
|
62
|
+
rescue StandardError => e
|
|
63
|
+
raise ConnectionError, "Error while connecting to supervisor at #{@ip}:#{@port}: #{e}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def stop_task
|
|
67
|
+
super
|
|
68
|
+
@last_status_sent = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def connect_tcp
|
|
72
|
+
@endpoint = IO::Endpoint.tcp(@ip, @port)
|
|
73
|
+
|
|
74
|
+
# this timeout is a workaround for connect hanging on windows if the other side is not present yet
|
|
75
|
+
timeout = @site_settings.dig('timeouts', 'connect') || 1.1
|
|
76
|
+
task.with_timeout timeout do
|
|
77
|
+
@socket = @endpoint.connect
|
|
78
|
+
end
|
|
79
|
+
delay = @site_settings.dig('intervals', 'after_connect')
|
|
80
|
+
task.sleep delay if delay
|
|
81
|
+
|
|
82
|
+
@stream = IO::Stream::Buffered.new(@socket)
|
|
83
|
+
@protocol = RSMP::Protocol.new(@stream) # rsmp messages are json terminated with a form-feed
|
|
84
|
+
self.state = :connected
|
|
85
|
+
rescue Errno::ECONNREFUSED => e # rescue to avoid log output
|
|
86
|
+
log 'Connection refused', level: :warning
|
|
87
|
+
raise e
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def handshake_complete
|
|
91
|
+
sanitized_sxl_version = RSMP::Schema.sanitize_version(sxl_version)
|
|
92
|
+
log "Connection to supervisor established, using core #{@core_version}, #{sxl} #{sanitized_sxl_version}",
|
|
93
|
+
level: :info
|
|
94
|
+
self.state = :ready
|
|
95
|
+
start_watchdog
|
|
96
|
+
if @site_settings['send_after_connect']
|
|
97
|
+
send_all_aggregated_status
|
|
98
|
+
send_active_alarms
|
|
99
|
+
end
|
|
100
|
+
super
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def process_message(message)
|
|
104
|
+
case message
|
|
105
|
+
when StatusResponse, StatusUpdate, AggregatedStatus, AlarmIssue
|
|
106
|
+
will_not_handle message
|
|
107
|
+
when AggregatedStatusRequest
|
|
108
|
+
process_aggregated_status_request message
|
|
109
|
+
when CommandRequest
|
|
110
|
+
process_command_request message
|
|
111
|
+
when CommandResponse
|
|
112
|
+
process_command_response message
|
|
113
|
+
when StatusRequest
|
|
114
|
+
process_status_request message
|
|
115
|
+
when StatusSubscribe
|
|
116
|
+
process_status_subcribe message
|
|
117
|
+
when StatusUnsubscribe
|
|
118
|
+
process_status_unsubcribe message
|
|
119
|
+
when Alarm, AlarmAcknowledged, AlarmSuspend, AlarmResume, AlarmRequest
|
|
120
|
+
process_alarm message
|
|
121
|
+
else
|
|
122
|
+
super
|
|
123
|
+
end
|
|
124
|
+
rescue UnknownComponent, UnknownCommand, UnknownStatus,
|
|
125
|
+
MessageRejected, MissingAttribute => e
|
|
126
|
+
dont_acknowledge message, '', e.to_s
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def acknowledged_first_ingoing(message)
|
|
130
|
+
case message.type
|
|
131
|
+
when 'Watchdog'
|
|
132
|
+
handshake_complete
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def reconnect_delay?
|
|
137
|
+
return false if @site_settings['intervals']['reconnect'] == :no
|
|
138
|
+
|
|
139
|
+
interval = @site_settings['intervals']['reconnect']
|
|
140
|
+
log "Will try to reconnect again every #{interval} seconds...", level: :info
|
|
141
|
+
@logger.mute @ip, @port
|
|
142
|
+
@task.sleep interval
|
|
143
|
+
true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def version_accepted(message)
|
|
147
|
+
log "Received Version message, using RSMP #{@core_version}", message: message, level: :log
|
|
148
|
+
start_timer
|
|
149
|
+
acknowledge message
|
|
150
|
+
@version_determined = true
|
|
151
|
+
send_watchdog
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def timer(now)
|
|
155
|
+
super
|
|
156
|
+
status_update_timer now if ready?
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def sxl_version
|
|
160
|
+
@site_settings['sxl_version'].to_s
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def process_version(message)
|
|
164
|
+
return extraneous_version message if @version_determined
|
|
165
|
+
|
|
166
|
+
check_core_version message
|
|
167
|
+
check_sxl_version message
|
|
168
|
+
@site_id = Supervisor.build_id_from_ip_port @ip, @port
|
|
169
|
+
version_accepted message
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def check_sxl_version(message); end
|
|
173
|
+
|
|
174
|
+
def main
|
|
175
|
+
@site.main
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -3,22 +3,22 @@ module RSMP
|
|
|
3
3
|
class DetectorLogic < Component
|
|
4
4
|
attr_reader :forced, :value
|
|
5
5
|
|
|
6
|
-
def initialize
|
|
7
|
-
super
|
|
6
|
+
def initialize(node:, id:)
|
|
7
|
+
super(node: node, id: id, grouped: false)
|
|
8
8
|
@forced = 0
|
|
9
9
|
@value = 0
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def get_status
|
|
12
|
+
def get_status(code, name = nil, _options = {})
|
|
13
13
|
case code
|
|
14
14
|
when 'S0201', 'S0202', 'S0203', 'S0204'
|
|
15
|
-
|
|
15
|
+
send("handle_#{code.downcase}", code, name)
|
|
16
16
|
else
|
|
17
|
-
raise InvalidMessage
|
|
17
|
+
raise InvalidMessage, "unknown status code #{code}"
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def handle_s0201
|
|
21
|
+
def handle_s0201(_status_code, status_name = nil, _options = {})
|
|
22
22
|
case status_name
|
|
23
23
|
when 'starttime'
|
|
24
24
|
TrafficControllerSite.make_status @node.clock.to_s
|
|
@@ -27,7 +27,7 @@ module RSMP
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def handle_s0202
|
|
30
|
+
def handle_s0202(_status_code, status_name = nil, _options = {})
|
|
31
31
|
case status_name
|
|
32
32
|
when 'starttime'
|
|
33
33
|
TrafficControllerSite.make_status @node.clock.to_s
|
|
@@ -36,7 +36,7 @@ module RSMP
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def handle_s0203
|
|
39
|
+
def handle_s0203(_status_code, status_name = nil, _options = {})
|
|
40
40
|
case status_name
|
|
41
41
|
when 'starttime'
|
|
42
42
|
TrafficControllerSite.make_status @node.clock.to_s
|
|
@@ -45,57 +45,41 @@ module RSMP
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def handle_s0204
|
|
48
|
+
def handle_s0204(_status_code, status_name = nil, _options = {})
|
|
49
49
|
case status_name
|
|
50
50
|
when 'starttime'
|
|
51
51
|
TrafficControllerSite.make_status @node.clock.to_s
|
|
52
|
-
when 'P'
|
|
53
|
-
TrafficControllerSite.make_status 0
|
|
54
|
-
when 'PS'
|
|
55
|
-
TrafficControllerSite.make_status 0
|
|
56
|
-
when 'L'
|
|
57
|
-
TrafficControllerSite.make_status 0
|
|
58
|
-
when 'LS'
|
|
59
|
-
TrafficControllerSite.make_status 0
|
|
60
|
-
when 'B'
|
|
61
|
-
TrafficControllerSite.make_status 0
|
|
62
|
-
when 'SP'
|
|
63
|
-
TrafficControllerSite.make_status 0
|
|
64
|
-
when 'MC'
|
|
65
|
-
TrafficControllerSite.make_status 0
|
|
66
|
-
when 'C'
|
|
67
|
-
TrafficControllerSite.make_status 0
|
|
68
|
-
when 'F'
|
|
52
|
+
when 'P', 'PS', 'L', 'LS', 'B', 'SP', 'MC', 'C', 'F'
|
|
69
53
|
TrafficControllerSite.make_status 0
|
|
70
54
|
end
|
|
71
55
|
end
|
|
72
56
|
|
|
73
|
-
def handle_command
|
|
57
|
+
def handle_command(command_code, arg, _options = {})
|
|
74
58
|
case command_code
|
|
75
59
|
when 'M0008'
|
|
76
60
|
handle_m0008 arg
|
|
77
61
|
else
|
|
78
|
-
raise UnknownCommand
|
|
62
|
+
raise UnknownCommand, "Unknown command #{command_code}"
|
|
79
63
|
end
|
|
80
64
|
end
|
|
81
65
|
|
|
82
|
-
def handle_m0008
|
|
66
|
+
def handle_m0008(arg, _options = {})
|
|
83
67
|
@node.verify_security_code 2, arg['securityCode']
|
|
84
|
-
status = arg['status']=='True'
|
|
85
|
-
mode = arg['mode']=='True'
|
|
68
|
+
status = arg['status'] == 'True'
|
|
69
|
+
mode = arg['mode'] == 'True'
|
|
86
70
|
force_detector_logic status, mode
|
|
87
71
|
arg
|
|
88
72
|
end
|
|
89
73
|
|
|
90
|
-
def force_detector_logic
|
|
74
|
+
def force_detector_logic(forced, value)
|
|
91
75
|
@forced = forced
|
|
92
76
|
@value = value
|
|
93
77
|
if @forced
|
|
94
78
|
log "Forcing to #{value}", level: :info
|
|
95
79
|
else
|
|
96
|
-
log
|
|
80
|
+
log 'Releasing', level: :info
|
|
97
81
|
end
|
|
98
82
|
end
|
|
99
83
|
end
|
|
100
84
|
end
|
|
101
|
-
end
|
|
85
|
+
end
|