rsmp 0.43.2 → 0.45.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/Gemfile.lock +11 -11
- data/README.md +19 -3
- data/Rakefile +2 -2
- data/config/supervisor.yaml +2 -1
- data/config/tlc.yaml +2 -2
- data/lib/rsmp/cli.rb +29 -5
- data/lib/rsmp/component/component.rb +0 -4
- data/lib/rsmp/component/component_base.rb +15 -2
- data/lib/rsmp/component/component_proxy.rb +1 -1
- data/lib/rsmp/component/components.rb +22 -1
- data/lib/rsmp/convert/export/json_schema/outputs.rb +1 -0
- data/lib/rsmp/convert/export/json_schema/values.rb +6 -4
- data/lib/rsmp/convert/export/json_schema.rb +7 -3
- data/lib/rsmp/helpers/deep_merge.rb +2 -2
- data/lib/rsmp/message.rb +32 -0
- data/lib/rsmp/node/site/site.rb +34 -10
- data/lib/rsmp/node/supervisor/modules/configuration.rb +32 -5
- data/lib/rsmp/node/supervisor/modules/connection.rb +0 -2
- data/lib/rsmp/node/supervisor/supervisor.rb +0 -7
- data/lib/rsmp/options/options.rb +55 -6
- data/lib/rsmp/options/schemas/site.json +6 -3
- data/lib/rsmp/options/schemas/supervisor.json +5 -2
- data/lib/rsmp/options/schemas/supervisor_site.json +5 -2
- data/lib/rsmp/options/site_options.rb +3 -2
- data/lib/rsmp/options/supervisor_options.rb +3 -1
- data/lib/rsmp/proxy/modules/acknowledgements.rb +2 -0
- data/lib/rsmp/proxy/modules/receive.rb +5 -2
- data/lib/rsmp/proxy/modules/state.rb +1 -0
- data/lib/rsmp/proxy/modules/versions.rb +90 -15
- data/lib/rsmp/proxy/proxy.rb +52 -3
- data/lib/rsmp/proxy/site/modules/status.rb +5 -3
- data/lib/rsmp/proxy/site/site_proxy.rb +68 -35
- data/lib/rsmp/proxy/site/sxl_selection.rb +54 -0
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +54 -18
- data/lib/rsmp/schema/message_resolution.rb +104 -0
- data/lib/rsmp/schema.rb +104 -22
- data/lib/rsmp/schema_error.rb +7 -1
- data/lib/rsmp/sxl/interface.rb +48 -0
- data/lib/rsmp/sxl/registry.rb +55 -0
- data/lib/rsmp/sxl/site_interface.rb +10 -0
- data/lib/rsmp/sxl/supervisor_interface.rb +21 -0
- data/lib/rsmp/tlc/detector_logic.rb +2 -2
- data/lib/rsmp/tlc/signal_group.rb +2 -2
- data/lib/rsmp/tlc/site_interface.rb +10 -0
- data/lib/rsmp/tlc/{traffic_controller_proxy.rb → supervisor_interface.rb} +19 -34
- data/lib/rsmp/tlc/traffic_controller.rb +10 -2
- data/lib/rsmp/tlc/traffic_controller_site.rb +4 -2
- data/lib/rsmp/tlc.rb +10 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +8 -1
- data/rsmp.gemspec +5 -5
- data/schemas/core/3.3.0/aggregated_status.json +25 -0
- data/schemas/core/3.3.0/aggregated_status_request.json +9 -0
- data/schemas/core/3.3.0/alarm.json +71 -0
- data/schemas/core/3.3.0/alarm_acknowledge.json +11 -0
- data/schemas/core/3.3.0/alarm_issue.json +44 -0
- data/schemas/core/3.3.0/alarm_request.json +3 -0
- data/schemas/core/3.3.0/alarm_suspend_resume.json +3 -0
- data/schemas/core/3.3.0/alarm_suspended_resumed.json +44 -0
- data/schemas/core/3.3.0/command_request.json +24 -0
- data/schemas/core/3.3.0/command_response.json +35 -0
- data/schemas/core/3.3.0/component_list.json +24 -0
- data/schemas/core/3.3.0/core.json +40 -0
- data/schemas/core/3.3.0/definitions.json +133 -0
- data/schemas/core/3.3.0/message_ack.json +11 -0
- data/schemas/core/3.3.0/message_not_ack.json +15 -0
- data/schemas/core/3.3.0/rsmp.json +142 -0
- data/schemas/core/3.3.0/status.json +21 -0
- data/schemas/core/3.3.0/status_request.json +5 -0
- data/schemas/core/3.3.0/status_response.json +41 -0
- data/schemas/core/3.3.0/status_subscribe.json +31 -0
- data/schemas/core/3.3.0/status_unsubscribe.json +5 -0
- data/schemas/core/3.3.0/status_update.json +41 -0
- data/schemas/core/3.3.0/version.json +144 -0
- data/schemas/core/3.3.0/watchdog.json +9 -0
- data/schemas/tlc/1.3.0/alarms/A0001.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0002.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0003.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0004.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0005.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0006.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0007.json +34 -0
- data/schemas/tlc/1.3.0/alarms/A0008.json +30 -0
- data/schemas/tlc/1.3.0/alarms/A0009.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0010.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0101.json +4 -0
- data/schemas/tlc/1.3.0/alarms/A0201.json +35 -0
- data/schemas/tlc/1.3.0/alarms/A0202.json +35 -0
- data/schemas/tlc/1.3.0/alarms/A0301.json +92 -0
- data/schemas/tlc/1.3.0/alarms/A0302.json +115 -0
- data/schemas/tlc/1.3.0/alarms/A0303.json +92 -0
- data/schemas/tlc/1.3.0/alarms/A0304.json +115 -0
- data/schemas/tlc/1.3.0/alarms/alarms.json +287 -0
- data/schemas/tlc/1.3.0/commands/M0001.json +92 -0
- data/schemas/tlc/1.3.0/commands/M0002.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0003.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0004.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0005.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0006.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0007.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0008.json +87 -0
- data/schemas/tlc/1.3.0/commands/M0010.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0011.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0012.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0013.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0014.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0015.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0016.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0017.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0018.json +69 -0
- data/schemas/tlc/1.3.0/commands/M0019.json +87 -0
- data/schemas/tlc/1.3.0/commands/M0020.json +87 -0
- data/schemas/tlc/1.3.0/commands/M0021.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0022.json +249 -0
- data/schemas/tlc/1.3.0/commands/M0023.json +51 -0
- data/schemas/tlc/1.3.0/commands/M0024.json +33 -0
- data/schemas/tlc/1.3.0/commands/M0103.json +72 -0
- data/schemas/tlc/1.3.0/commands/M0104.json +141 -0
- data/schemas/tlc/1.3.0/commands/command_requests.json +8 -0
- data/schemas/tlc/1.3.0/commands/command_responses.json +8 -0
- data/schemas/tlc/1.3.0/commands/commands.json +415 -0
- data/schemas/tlc/1.3.0/defs/definitions.json +72 -0
- data/schemas/tlc/1.3.0/defs/guards.json +24 -0
- data/schemas/tlc/1.3.0/rsmp.json +74 -0
- data/schemas/tlc/1.3.0/statuses/S0001.json +109 -0
- data/schemas/tlc/1.3.0/statuses/S0002.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0003.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0004.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0005.json +72 -0
- data/schemas/tlc/1.3.0/statuses/S0006.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0007.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0008.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0009.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0010.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0011.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0012.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0013.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0014.json +55 -0
- data/schemas/tlc/1.3.0/statuses/S0015.json +55 -0
- data/schemas/tlc/1.3.0/statuses/S0016.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0017.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0018.json +61 -0
- data/schemas/tlc/1.3.0/statuses/S0019.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0020.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0021.json +37 -0
- data/schemas/tlc/1.3.0/statuses/S0022.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0023.json +37 -0
- data/schemas/tlc/1.3.0/statuses/S0024.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0025.json +162 -0
- data/schemas/tlc/1.3.0/statuses/S0026.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0027.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0028.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0029.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0030.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0031.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0032.json +73 -0
- data/schemas/tlc/1.3.0/statuses/S0033.json +77 -0
- data/schemas/tlc/1.3.0/statuses/S0034.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0035.json +49 -0
- data/schemas/tlc/1.3.0/statuses/S0091.json +40 -0
- data/schemas/tlc/1.3.0/statuses/S0092.json +40 -0
- data/schemas/tlc/1.3.0/statuses/S0095.json +36 -0
- data/schemas/tlc/1.3.0/statuses/S0096.json +126 -0
- data/schemas/tlc/1.3.0/statuses/S0097.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0098.json +72 -0
- data/schemas/tlc/1.3.0/statuses/S0201.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0202.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0203.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0204.json +198 -0
- data/schemas/tlc/1.3.0/statuses/S0205.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0206.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0207.json +54 -0
- data/schemas/tlc/1.3.0/statuses/S0208.json +198 -0
- data/schemas/tlc/1.3.0/statuses/statuses.json +787 -0
- data/schemas/tlc/1.3.0/sxl.yaml +2296 -0
- metadata +144 -12
data/lib/rsmp/options/options.rb
CHANGED
|
@@ -25,9 +25,9 @@ module RSMP
|
|
|
25
25
|
options = extra if options.nil? && extra.any?
|
|
26
26
|
@source = source
|
|
27
27
|
@log_settings = normalize(log_settings || {})
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
config = normalize_config(options || {})
|
|
29
|
+
validate!(config) if validate
|
|
30
|
+
@data = normalize(apply_defaults(config))
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def defaults
|
|
@@ -44,11 +44,11 @@ module RSMP
|
|
|
44
44
|
File.join(SCHEMAS_PATH, schema_file)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
def validate!
|
|
47
|
+
def validate!(data = @data)
|
|
48
48
|
return unless schema_path && File.exist?(schema_path)
|
|
49
49
|
|
|
50
50
|
schemer = JSONSchemer.schema(Pathname.new(schema_path))
|
|
51
|
-
errors = schemer.validate(
|
|
51
|
+
errors = schemer.validate(data).to_a
|
|
52
52
|
return if errors.empty?
|
|
53
53
|
|
|
54
54
|
message = errors.map { |error| format_error(error) }.join("\n")
|
|
@@ -82,7 +82,8 @@ module RSMP
|
|
|
82
82
|
case value
|
|
83
83
|
when Hash
|
|
84
84
|
value.each_with_object({}) do |(key, val), memo|
|
|
85
|
-
|
|
85
|
+
normalized_key = key.to_s
|
|
86
|
+
memo[normalized_key] = normalized_key == 'sxls' ? normalize_sxls(val) : normalize(val)
|
|
86
87
|
end
|
|
87
88
|
when Array
|
|
88
89
|
value.map { |item| normalize(item) }
|
|
@@ -91,6 +92,54 @@ module RSMP
|
|
|
91
92
|
end
|
|
92
93
|
end
|
|
93
94
|
|
|
95
|
+
def normalize_sxls(value)
|
|
96
|
+
case value
|
|
97
|
+
when Hash
|
|
98
|
+
value.map do |name, details|
|
|
99
|
+
normalize_sxl_item(name.to_s, details)
|
|
100
|
+
end
|
|
101
|
+
when Array
|
|
102
|
+
value.map { |item| normalize(item) }
|
|
103
|
+
else
|
|
104
|
+
value
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def normalize_config(value)
|
|
109
|
+
case value
|
|
110
|
+
when Hash
|
|
111
|
+
value.each_with_object({}) do |(key, val), memo|
|
|
112
|
+
normalized_key = key.to_s
|
|
113
|
+
memo[normalized_key] = normalized_key == 'sxls' ? normalize_config_sxls(val) : normalize_config(val)
|
|
114
|
+
end
|
|
115
|
+
when Array
|
|
116
|
+
value.map { |item| normalize_config(item) }
|
|
117
|
+
else
|
|
118
|
+
value
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def normalize_config_sxls(value)
|
|
123
|
+
raise RSMP::ConfigurationError, 'sxls must be a hash of SXL names to versions' unless value.is_a?(Hash)
|
|
124
|
+
|
|
125
|
+
value.each_with_object({}) do |(name, details), memo|
|
|
126
|
+
raise RSMP::ConfigurationError, "sxls/#{name} must be a version string" if details.is_a?(Hash)
|
|
127
|
+
|
|
128
|
+
memo[name.to_s] = details.to_s
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def normalize_sxl_item(name, details)
|
|
133
|
+
case details
|
|
134
|
+
when Hash
|
|
135
|
+
raise RSMP::ConfigurationError, "sxls/#{name} must be a version string"
|
|
136
|
+
when nil
|
|
137
|
+
{ 'name' => name }
|
|
138
|
+
else
|
|
139
|
+
{ 'name' => name, 'version' => details.to_s }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
94
143
|
def format_error(error)
|
|
95
144
|
pointer = error_pointer(error)
|
|
96
145
|
details = error_details(error)
|
|
@@ -17,8 +17,11 @@
|
|
|
17
17
|
"additionalProperties": true
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
20
|
+
"sxls": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"propertyNames": { "not": { "const": "core" } },
|
|
23
|
+
"additionalProperties": { "type": "string" }
|
|
24
|
+
},
|
|
22
25
|
"core_version": { "type": "string" },
|
|
23
26
|
"intervals": {
|
|
24
27
|
"type": "object",
|
|
@@ -46,4 +49,4 @@
|
|
|
46
49
|
"live_output": { "type": ["string", "null"] }
|
|
47
50
|
},
|
|
48
51
|
"additionalProperties": true
|
|
49
|
-
}
|
|
52
|
+
}
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
"default": {
|
|
16
16
|
"type": "object",
|
|
17
17
|
"properties": {
|
|
18
|
-
"
|
|
19
|
-
|
|
18
|
+
"sxls": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"propertyNames": { "not": { "const": "core" } },
|
|
21
|
+
"additionalProperties": { "type": "string" }
|
|
22
|
+
},
|
|
20
23
|
"core_version": { "type": "string" },
|
|
21
24
|
"intervals": {
|
|
22
25
|
"type": "object",
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
"$id": "supervisor_site.json",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
6
|
-
"
|
|
7
|
-
|
|
6
|
+
"sxls": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"propertyNames": { "not": { "const": "core" } },
|
|
9
|
+
"additionalProperties": { "type": "string" }
|
|
10
|
+
},
|
|
8
11
|
"type": { "type": "string" },
|
|
9
12
|
"components": { "type": "object" },
|
|
10
13
|
"supervisors": {
|
|
@@ -8,8 +8,9 @@ module RSMP
|
|
|
8
8
|
'supervisors' => [
|
|
9
9
|
{ 'ip' => '127.0.0.1', 'port' => 12_111 }
|
|
10
10
|
],
|
|
11
|
-
'
|
|
12
|
-
|
|
11
|
+
'sxls' => {
|
|
12
|
+
'tlc' => RSMP::Schema.latest_version(:tlc)
|
|
13
|
+
},
|
|
13
14
|
'intervals' => {
|
|
14
15
|
'timer' => 0.1,
|
|
15
16
|
'watchdog' => 1,
|
|
@@ -5,6 +5,8 @@ module RSMP
|
|
|
5
5
|
# Handles receiving and processing incoming messages
|
|
6
6
|
module Receive
|
|
7
7
|
def should_validate_ingoing_message?(message)
|
|
8
|
+
return false if message.is_a?(Version) && !@version_determined
|
|
9
|
+
|
|
8
10
|
return true unless @site_settings
|
|
9
11
|
|
|
10
12
|
skip = @site_settings['skip_validation']
|
|
@@ -37,7 +39,8 @@ module RSMP
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
def handle_schema_error(message, error)
|
|
40
|
-
|
|
42
|
+
failed_schemas = error.respond_to?(:schemas) && error.schemas ? error.schemas : schemas
|
|
43
|
+
schemas_string = failed_schemas.map { |schema| "#{schema.first}: #{schema.last}" }.join(', ')
|
|
41
44
|
reason = "schema errors (#{schemas_string}): #{error.message}"
|
|
42
45
|
str = "Received invalid #{message.type}"
|
|
43
46
|
distribute_error error.exception(str), message: message
|
|
@@ -79,7 +82,7 @@ module RSMP
|
|
|
79
82
|
handle_malformed_message(attributes, e)
|
|
80
83
|
rescue SchemaError, RSMP::Schema::Error => e
|
|
81
84
|
handle_schema_error(message, e)
|
|
82
|
-
rescue InvalidMessage => e
|
|
85
|
+
rescue InvalidMessage, MessageRejected => e
|
|
83
86
|
handle_invalid_message(message, e)
|
|
84
87
|
rescue FatalError => e
|
|
85
88
|
handle_fatal_error(message, e)
|
|
@@ -15,6 +15,27 @@ module RSMP
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def core_3_3?
|
|
19
|
+
core_version && version_meets_requirement?(core_version, '>=3.3.0')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def configured_sxls
|
|
23
|
+
(@site_settings['sxls'] || []).map { |item| item.transform_keys(&:to_s) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def primary_configured_sxl
|
|
27
|
+
configured_sxls.first
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def sxl_request_items
|
|
31
|
+
configured_sxls.map do |sxl|
|
|
32
|
+
item = { 'name' => sxl['name'], 'version' => sxl['version'].to_s }
|
|
33
|
+
prefix = RSMP::Schema.sxl_prefix(sxl['name'], sxl['version'], lenient: true)
|
|
34
|
+
item['prefix'] = prefix if prefix
|
|
35
|
+
item
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
18
39
|
def check_core_version(message)
|
|
19
40
|
versions = core_versions
|
|
20
41
|
# find versions that both we and the client support
|
|
@@ -36,33 +57,87 @@ module RSMP
|
|
|
36
57
|
end
|
|
37
58
|
|
|
38
59
|
def send_version(site_id, core_versions)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
send_version_message(site_id, core_versions, step: nil)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def send_version_request(site_id, core_versions)
|
|
64
|
+
send_version_message(site_id, core_versions, step: 'Request')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def send_version_response(site_id, core_versions)
|
|
68
|
+
if core_3_3?
|
|
69
|
+
send_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
|
+
else
|
|
77
|
+
send_version_message(site_id, core_versions, step: nil)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def send_version_message(site_id, core_versions, step:)
|
|
82
|
+
attributes = version_message_attributes(site_id, core_versions)
|
|
83
|
+
attributes.merge!(version_request_attributes) if step == 'Request'
|
|
84
|
+
send_message Version.new(attributes), validate: false
|
|
85
|
+
end
|
|
47
86
|
|
|
48
|
-
|
|
87
|
+
def version_message_attributes(site_id, core_versions)
|
|
88
|
+
primary = primary_configured_sxl
|
|
89
|
+
attributes = {
|
|
90
|
+
'RSMP' => version_items(core_versions),
|
|
91
|
+
'siteId' => site_id_items(site_id)
|
|
92
|
+
}
|
|
93
|
+
attributes['SXL'] = primary['version'].to_s if primary
|
|
94
|
+
attributes
|
|
95
|
+
end
|
|
49
96
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
97
|
+
def version_items(core_versions)
|
|
98
|
+
normalized_core_versions(core_versions).map { |version| { 'vers' => version } }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def normalized_core_versions(core_versions)
|
|
102
|
+
case core_versions
|
|
103
|
+
when 'latest'
|
|
104
|
+
[RSMP::Schema.latest_core_version]
|
|
105
|
+
when 'all'
|
|
106
|
+
RSMP::Schema.core_versions
|
|
107
|
+
else
|
|
108
|
+
[core_versions].flatten
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def site_id_items(site_id)
|
|
113
|
+
[site_id].flatten.map { |id| { 'sId' => id } }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def version_request_attributes
|
|
117
|
+
{
|
|
118
|
+
'step' => 'Request',
|
|
119
|
+
'SXLS' => sxl_request_items
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def version_response_sxls
|
|
124
|
+
accepted_sxls + rejected_sxls
|
|
56
125
|
end
|
|
57
126
|
|
|
58
127
|
def version_acknowledged; end
|
|
59
128
|
|
|
129
|
+
def component_list_acknowledged; end
|
|
130
|
+
|
|
60
131
|
# Use Gem class to check version requirement
|
|
61
132
|
# Requirement must be a string like '1.1', '>=1.0.3' or '<2.1.4',
|
|
62
133
|
# or list of strings, like ['<=1.4','<1.5']
|
|
63
134
|
def self.version_meets_requirement?(version, requirement)
|
|
64
135
|
Gem::Requirement.new(requirement).satisfied_by?(Gem::Version.new(version))
|
|
65
136
|
end
|
|
137
|
+
|
|
138
|
+
def version_meets_requirement?(version, requirement)
|
|
139
|
+
RSMP::Proxy::Modules::Versions.version_meets_requirement?(version, requirement)
|
|
140
|
+
end
|
|
66
141
|
end
|
|
67
142
|
end
|
|
68
143
|
end
|
data/lib/rsmp/proxy/proxy.rb
CHANGED
|
@@ -17,7 +17,9 @@ module RSMP
|
|
|
17
17
|
include Modules::Versions
|
|
18
18
|
include Modules::Tasks
|
|
19
19
|
|
|
20
|
-
attr_reader :state, :archive, :connection_info, :
|
|
20
|
+
attr_reader :state, :archive, :connection_info, :sxls, :accepted_sxls, :rejected_sxls,
|
|
21
|
+
:collector, :ip, :port, :node, :core_version, :sxl_interfaces,
|
|
22
|
+
:site_settings
|
|
21
23
|
|
|
22
24
|
def initialize(options)
|
|
23
25
|
@node = options[:node]
|
|
@@ -136,6 +138,9 @@ module RSMP
|
|
|
136
138
|
@ingoing_acknowledged = {}
|
|
137
139
|
@outgoing_acknowledged = {}
|
|
138
140
|
@latest_watchdog_send_at = nil
|
|
141
|
+
@component_list_received = false
|
|
142
|
+
@outgoing_watchdog_acknowledged = false
|
|
143
|
+
@sxl_interfaces = {}
|
|
139
144
|
|
|
140
145
|
@acknowledgements = {}
|
|
141
146
|
@acknowledgement_condition = Async::Notification.new
|
|
@@ -154,7 +159,10 @@ module RSMP
|
|
|
154
159
|
@ip = options[:ip]
|
|
155
160
|
@port = options[:port]
|
|
156
161
|
@connection_info = options[:info]
|
|
157
|
-
@
|
|
162
|
+
@sxls = []
|
|
163
|
+
@accepted_sxls = []
|
|
164
|
+
@rejected_sxls = []
|
|
165
|
+
@receive_alarms = true
|
|
158
166
|
@site_settings = nil # can't pick until we know the site id
|
|
159
167
|
return unless options[:collect]
|
|
160
168
|
|
|
@@ -177,14 +185,55 @@ module RSMP
|
|
|
177
185
|
def schemas
|
|
178
186
|
schemas = { core: RSMP::Schema.latest_core_version } # use latest core
|
|
179
187
|
schemas[:core] = core_version if core_version
|
|
180
|
-
|
|
188
|
+
accepted_sxls.each do |sxl|
|
|
189
|
+
schemas[sxl['name'].to_sym] = RSMP::Schema.sanitize_version(sxl['version'].to_s)
|
|
190
|
+
end
|
|
181
191
|
schemas
|
|
182
192
|
end
|
|
183
193
|
|
|
194
|
+
def receive_alarms?
|
|
195
|
+
@receive_alarms != false
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def primary_sxl
|
|
199
|
+
accepted_sxls.first || sxls.first
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def sxl
|
|
203
|
+
primary_sxl && primary_sxl['name']
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def sxl_version
|
|
207
|
+
primary_sxl && primary_sxl['version']
|
|
208
|
+
end
|
|
209
|
+
|
|
184
210
|
def author
|
|
185
211
|
@node.site_id
|
|
186
212
|
end
|
|
187
213
|
|
|
214
|
+
def build_sxl_interfaces
|
|
215
|
+
@sxl_interfaces = accepted_sxls.to_h do |sxl|
|
|
216
|
+
[sxl['name'], RSMP::SXL::Registry.build_for(self, sxl)]
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def sxl_interface(name)
|
|
221
|
+
sxl_interfaces.fetch(name.to_s) do
|
|
222
|
+
raise RSMP::Schema::UnknownSchemaTypeError, "SXL #{name} is not accepted on this connection"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def tlc
|
|
227
|
+
sxl_interface 'tlc'
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def sxl_interface_for(message)
|
|
231
|
+
resolved = RSMP::Schema.resolve_sxl(message.attributes, schemas: schemas)
|
|
232
|
+
return unless resolved
|
|
233
|
+
|
|
234
|
+
sxl_interface resolved.first
|
|
235
|
+
end
|
|
236
|
+
|
|
188
237
|
# Use Gem class to check version requirement
|
|
189
238
|
# Requirement must be a string like '1.1', '>=1.0.3' or '<2.1.4',
|
|
190
239
|
# or list of strings, like ['<=1.4','<1.5']
|
|
@@ -125,14 +125,13 @@ module RSMP
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def unsubscribe_to_status(status_list, component: nil, validate: nil)
|
|
128
|
+
validate_ready 'unsubscribe to status'
|
|
128
129
|
component ||= main.c_id
|
|
129
130
|
|
|
130
131
|
status_list.each do |item|
|
|
131
132
|
remove_subscription_item(component, item['sCI'], item['n'])
|
|
132
133
|
end
|
|
133
134
|
|
|
134
|
-
return unless ready? # if the connection is don't we skip sending
|
|
135
|
-
|
|
136
135
|
message = RSMP::StatusUnsubscribe.new({
|
|
137
136
|
'cId' => component,
|
|
138
137
|
'sS' => status_list
|
|
@@ -145,7 +144,10 @@ module RSMP
|
|
|
145
144
|
# unsubscribes to all statuses (with all attributes) defined in the used SXL
|
|
146
145
|
def unsubscribe_from_all(component: nil)
|
|
147
146
|
component ||= main.c_id
|
|
148
|
-
catalogue =
|
|
147
|
+
catalogue = accepted_sxls.each_with_object({}) do |sxl, memo|
|
|
148
|
+
version = RSMP::Schema.sanitize_version(sxl['version'].to_s)
|
|
149
|
+
memo.merge! RSMP::Schema.status_catalogue(sxl['name'], version)
|
|
150
|
+
end
|
|
149
151
|
status_list = catalogue.flat_map do |status_code_id, names|
|
|
150
152
|
names.map { |name| { 'sCI' => status_code_id.to_s, 'n' => name.to_s } }
|
|
151
153
|
end
|
|
@@ -6,6 +6,7 @@ module RSMP
|
|
|
6
6
|
include Modules::AggregatedStatus
|
|
7
7
|
include Modules::Alarms
|
|
8
8
|
include Modules::Commands
|
|
9
|
+
include SiteSxlSelection
|
|
9
10
|
|
|
10
11
|
attr_reader :supervisor, :site_id
|
|
11
12
|
|
|
@@ -44,7 +45,8 @@ module RSMP
|
|
|
44
45
|
|
|
45
46
|
def handshake_complete
|
|
46
47
|
super
|
|
47
|
-
|
|
48
|
+
sxl_summary = accepted_sxls.map { |item| "#{item['name']} #{item['version']}" }.join(', ')
|
|
49
|
+
log "Connection to site #{@site_id} established, using core #{@core_version}, SXLs [#{sxl_summary}]",
|
|
48
50
|
level: :info
|
|
49
51
|
start_watchdog
|
|
50
52
|
end
|
|
@@ -55,16 +57,13 @@ module RSMP
|
|
|
55
57
|
case message
|
|
56
58
|
when StatusUnsubscribe, AggregatedStatusRequest
|
|
57
59
|
will_not_handle message
|
|
60
|
+
when ComponentList
|
|
61
|
+
process_component_list message
|
|
58
62
|
when AggregatedStatus
|
|
59
63
|
process_aggregated_status message
|
|
60
|
-
when AlarmIssue, AlarmSuspended, AlarmResumed, AlarmAcknowledged
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
process_command_response message
|
|
64
|
-
when StatusResponse
|
|
65
|
-
process_status_response message
|
|
66
|
-
when StatusUpdate
|
|
67
|
-
process_status_update message
|
|
64
|
+
when AlarmIssue, AlarmSuspended, AlarmResumed, AlarmAcknowledged,
|
|
65
|
+
CommandResponse, StatusResponse, StatusUpdate
|
|
66
|
+
handle_interface_message message
|
|
68
67
|
else
|
|
69
68
|
super
|
|
70
69
|
end
|
|
@@ -78,11 +77,32 @@ module RSMP
|
|
|
78
77
|
message.is_a?(CommandRequest) || message.is_a?(StatusRequest) || message.is_a?(StatusSubscribe)
|
|
79
78
|
end
|
|
80
79
|
|
|
80
|
+
def handle_interface_message(message)
|
|
81
|
+
interface = sxl_interface_for message
|
|
82
|
+
interface.validate_message! message
|
|
83
|
+
record_interface_message message
|
|
84
|
+
interface.process_message message
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def record_interface_message(message)
|
|
88
|
+
case message
|
|
89
|
+
when AlarmIssue, AlarmSuspended, AlarmResumed, AlarmAcknowledged
|
|
90
|
+
process_alarm message
|
|
91
|
+
when CommandResponse
|
|
92
|
+
process_command_response message
|
|
93
|
+
when StatusResponse
|
|
94
|
+
process_status_response message
|
|
95
|
+
when StatusUpdate
|
|
96
|
+
process_status_update message
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
81
100
|
def version_accepted(message)
|
|
82
101
|
log "Received Version message for site #{@site_id}", message: message, level: :log
|
|
83
102
|
start_timer
|
|
84
103
|
acknowledge message
|
|
85
|
-
|
|
104
|
+
response_id = core_3_3? ? (@supervisor.site_id || @site_id) : @site_id
|
|
105
|
+
send_version_response response_id, core_versions
|
|
86
106
|
@version_determined = true
|
|
87
107
|
end
|
|
88
108
|
|
|
@@ -96,7 +116,12 @@ module RSMP
|
|
|
96
116
|
def acknowledged_first_outgoing(message)
|
|
97
117
|
case message.type
|
|
98
118
|
when 'Watchdog'
|
|
99
|
-
|
|
119
|
+
if core_3_3?
|
|
120
|
+
@outgoing_watchdog_acknowledged = true
|
|
121
|
+
handshake_complete if @component_list_received
|
|
122
|
+
else
|
|
123
|
+
handshake_complete
|
|
124
|
+
end
|
|
100
125
|
end
|
|
101
126
|
end
|
|
102
127
|
|
|
@@ -106,6 +131,33 @@ module RSMP
|
|
|
106
131
|
|
|
107
132
|
def version_acknowledged; end
|
|
108
133
|
|
|
134
|
+
def process_component_list(message)
|
|
135
|
+
log "Received #{message.type}", message: message, level: :log
|
|
136
|
+
rebuild_components_from_list message.attributes['components']
|
|
137
|
+
build_sxl_interfaces
|
|
138
|
+
acknowledge message
|
|
139
|
+
@component_list_received = true
|
|
140
|
+
handshake_complete if @outgoing_watchdog_acknowledged
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def rebuild_components_from_list(items)
|
|
144
|
+
main_id = @site_settings.dig('components', 'main')&.keys&.first
|
|
145
|
+
@components = {}
|
|
146
|
+
@main = nil
|
|
147
|
+
items.each do |item|
|
|
148
|
+
grouped = item['id'] == main_id
|
|
149
|
+
component = ComponentProxy.new(
|
|
150
|
+
id: item['id'],
|
|
151
|
+
node: self,
|
|
152
|
+
type: item['type'],
|
|
153
|
+
name: item['name'],
|
|
154
|
+
grouped: grouped
|
|
155
|
+
)
|
|
156
|
+
@components[component.c_id] = component
|
|
157
|
+
@main = component if grouped
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
109
161
|
def site_ids_changed
|
|
110
162
|
@supervisor.site_ids_changed
|
|
111
163
|
end
|
|
@@ -114,27 +166,6 @@ module RSMP
|
|
|
114
166
|
@settings['intervals']['watchdog'] = interval
|
|
115
167
|
end
|
|
116
168
|
|
|
117
|
-
def check_sxl_version(message)
|
|
118
|
-
# check that we have a schema for specified sxl type and version
|
|
119
|
-
# note that the type comes from the site config, while the version
|
|
120
|
-
# comes from the Version message send by the site
|
|
121
|
-
type = @site_settings['sxl']
|
|
122
|
-
sanitized_version = RSMP::Schema.sanitize_version(message.attribute('SXL'))
|
|
123
|
-
RSMP::Schema.find_schema! type, sanitized_version
|
|
124
|
-
|
|
125
|
-
# store raw sxl version from site (may be 2-part like "1.2"), so we echo it back unchanged
|
|
126
|
-
# TODO should check agaist site settings
|
|
127
|
-
@site_sxl_version = message.attribute('SXL')
|
|
128
|
-
rescue RSMP::Schema::UnknownSchemaError => e
|
|
129
|
-
dont_acknowledge message, "Rejected #{message.type} message,", e.to_s
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def sxl_version
|
|
133
|
-
# a supervisor does not maintain it's own sxl version
|
|
134
|
-
# instead we use what the site requests
|
|
135
|
-
@site_sxl_version
|
|
136
|
-
end
|
|
137
|
-
|
|
138
169
|
def process_version(message)
|
|
139
170
|
return extraneous_version message if @version_determined
|
|
140
171
|
|
|
@@ -173,7 +204,9 @@ module RSMP
|
|
|
173
204
|
def setup_site_settings
|
|
174
205
|
@site_settings = find_site_settings @site_id
|
|
175
206
|
if @site_settings
|
|
176
|
-
@
|
|
207
|
+
@sxls = configured_sxls
|
|
208
|
+
@accepted_sxls = @sxls.dup
|
|
209
|
+
build_sxl_interfaces
|
|
177
210
|
setup_components @site_settings['components']
|
|
178
211
|
else
|
|
179
212
|
dont_acknowledge message, 'Rejected', "No config found for site #{@site_id}"
|
|
@@ -188,10 +221,10 @@ module RSMP
|
|
|
188
221
|
def build_component(id:, type:, settings: {})
|
|
189
222
|
settings ||= {}
|
|
190
223
|
if type == 'main'
|
|
191
|
-
ComponentProxy.new id: id, node: self, grouped: true,
|
|
224
|
+
ComponentProxy.new id: id, node: self, type: type, name: settings['name'], grouped: true,
|
|
192
225
|
ntsoid: settings['ntsOId'], xnid: settings['xNId']
|
|
193
226
|
else
|
|
194
|
-
ComponentProxy.new id: id, node: self, grouped: false
|
|
227
|
+
ComponentProxy.new id: id, node: self, type: type, name: settings['name'], grouped: false
|
|
195
228
|
end
|
|
196
229
|
end
|
|
197
230
|
|