rsmp 0.43.2 → 0.45.1
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/.github/workflows/rubocop.yaml +1 -1
- data/.github/workflows/sus.yaml +1 -1
- data/.gitignore +0 -1
- data/CHANGELOG.md +12 -1
- data/Gemfile +0 -2
- data/Gemfile.lock +10 -88
- data/README.md +30 -35
- data/Rakefile +2 -2
- data/config/supervisor.yaml +2 -1
- data/config/tlc.yaml +2 -2
- data/documentation/configuration.md +12 -11
- data/documentation/message_distribution.md +1 -2
- data/documentation/tasks.md +1 -2
- data/exe/rsmp +1 -2
- data/lib/rsmp/cli.rb +62 -8
- 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 +19 -0
- data/lib/rsmp/convert/export/json_schema/values.rb +7 -5
- data/lib/rsmp/convert/export/json_schema.rb +15 -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/core_sxl_resolution.rb +69 -0
- data/lib/rsmp/schema/message_resolution.rb +104 -0
- data/lib/rsmp/schema/validation.rb +57 -0
- data/lib/rsmp/schema.rb +87 -32
- 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.0.10/rsmp.json +2 -1
- data/schemas/tlc/1.0.10/sxl.yaml +1 -0
- data/schemas/tlc/1.0.10/sxl_index.json +356 -0
- data/schemas/tlc/1.0.13/rsmp.json +2 -1
- data/schemas/tlc/1.0.13/sxl.yaml +1 -0
- data/schemas/tlc/1.0.13/sxl_index.json +436 -0
- data/schemas/tlc/1.0.14/rsmp.json +2 -1
- data/schemas/tlc/1.0.14/sxl.yaml +1 -0
- data/schemas/tlc/1.0.14/sxl_index.json +468 -0
- data/schemas/tlc/1.0.15/rsmp.json +2 -1
- data/schemas/tlc/1.0.15/sxl.yaml +1 -0
- data/schemas/tlc/1.0.15/sxl_index.json +508 -0
- data/schemas/tlc/1.0.7/rsmp.json +2 -1
- data/schemas/tlc/1.0.7/sxl.yaml +1 -0
- data/schemas/tlc/1.0.7/sxl_index.json +356 -0
- data/schemas/tlc/1.0.8/rsmp.json +2 -1
- data/schemas/tlc/1.0.8/sxl.yaml +1 -0
- data/schemas/tlc/1.0.8/sxl_index.json +356 -0
- data/schemas/tlc/1.0.9/rsmp.json +2 -1
- data/schemas/tlc/1.0.9/sxl.yaml +1 -0
- data/schemas/tlc/1.0.9/sxl_index.json +356 -0
- data/schemas/tlc/1.1.0/rsmp.json +2 -1
- data/schemas/tlc/1.1.0/sxl.yaml +1 -0
- data/schemas/tlc/1.1.0/sxl_index.json +572 -0
- data/schemas/tlc/1.2.0/rsmp.json +2 -1
- data/schemas/tlc/1.2.0/sxl.yaml +1 -0
- data/schemas/tlc/1.2.0/sxl_index.json +571 -0
- data/schemas/tlc/1.2.1/rsmp.json +2 -1
- data/schemas/tlc/1.2.1/sxl.yaml +1 -0
- data/schemas/tlc/1.2.1/sxl_index.json +571 -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 +133 -0
- data/schemas/tlc/1.3.0/defs/guards.json +24 -0
- data/schemas/tlc/1.3.0/rsmp.json +75 -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 +37 -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 +2297 -0
- data/schemas/tlc/1.3.0/sxl_index.json +578 -0
- metadata +157 -15
- data/.github/copilot-instructions.md +0 -33
- data/.rspec +0 -1
- data/cucumber.yml +0 -1
|
@@ -19,7 +19,12 @@ module RSMP
|
|
|
19
19
|
|
|
20
20
|
components_by_type.each_pair do |id, component_settings|
|
|
21
21
|
component_settings ||= {}
|
|
22
|
-
|
|
22
|
+
component = build_component(id: id, type: type, settings: component_settings)
|
|
23
|
+
component.update_metadata(
|
|
24
|
+
type: component_settings['type'] || type,
|
|
25
|
+
name: component_settings['name'] || id
|
|
26
|
+
)
|
|
27
|
+
@components[id] = component
|
|
23
28
|
@main = @components[id] if type == 'main'
|
|
24
29
|
end
|
|
25
30
|
end
|
|
@@ -36,6 +41,22 @@ module RSMP
|
|
|
36
41
|
@components[component.c_id] = component
|
|
37
42
|
end
|
|
38
43
|
|
|
44
|
+
def natural_sort_key(value)
|
|
45
|
+
value.to_s.split(/(\d+)/).map do |part|
|
|
46
|
+
part.match?(/\A\d+\z/) ? part.to_i : part
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def component_list
|
|
51
|
+
@components.values.sort_by { |component| natural_sort_key(component.c_id) }.map do |component|
|
|
52
|
+
{
|
|
53
|
+
'id' => component.c_id,
|
|
54
|
+
'type' => component.component_type || 'component',
|
|
55
|
+
'name' => component.name || component.c_id
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
39
60
|
def infer_component_type(component_id)
|
|
40
61
|
raise UnknownComponent, "Component #{component_id} mising and cannot infer type"
|
|
41
62
|
end
|
|
@@ -103,6 +103,23 @@ module RSMP
|
|
|
103
103
|
out["commands/#{key}.json"] = output_json json
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
+
def self.output_sxl_index(out, sxl)
|
|
107
|
+
out['sxl_index.json'] = output_json({
|
|
108
|
+
'meta' => sxl[:meta],
|
|
109
|
+
'statuses' => index_items(sxl[:statuses]),
|
|
110
|
+
'commands' => index_items(sxl[:commands]),
|
|
111
|
+
'alarms' => index_items(sxl[:alarms])
|
|
112
|
+
})
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.index_items(items)
|
|
116
|
+
items.keys.sort.to_h do |key|
|
|
117
|
+
[key, {
|
|
118
|
+
'arguments' => (items[key]['arguments'] || {}).keys.sort
|
|
119
|
+
}]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
106
123
|
# output the json schema root
|
|
107
124
|
def self.output_root(out, meta)
|
|
108
125
|
json = {
|
|
@@ -112,6 +129,8 @@ module RSMP
|
|
|
112
129
|
'version' => meta['version'],
|
|
113
130
|
'allOf' => root_type_rules
|
|
114
131
|
}
|
|
132
|
+
json['prefix'] = meta['prefix'] if meta['prefix']
|
|
133
|
+
json['minimum_core_version'] = meta['minimum_core_version'] if meta['minimum_core_version']
|
|
115
134
|
out['rsmp.json'] = output_json json
|
|
116
135
|
end
|
|
117
136
|
|
|
@@ -7,7 +7,7 @@ module RSMP
|
|
|
7
7
|
def self.build_value(item)
|
|
8
8
|
out = {}
|
|
9
9
|
out['description'] = item['description'] if item['description']
|
|
10
|
-
if item['type'] =~ /_list
|
|
10
|
+
if item['type'] =~ /_list(_as_string)?$/
|
|
11
11
|
handle_string_list item, out
|
|
12
12
|
else
|
|
13
13
|
handle_types item, out
|
|
@@ -59,11 +59,13 @@ module RSMP
|
|
|
59
59
|
# convert a yaml item with list: true to json schema
|
|
60
60
|
def self.handle_string_list(item, out)
|
|
61
61
|
case item['type']
|
|
62
|
-
when 'boolean_list'
|
|
62
|
+
when 'boolean_list', 'boolean_list_as_string'
|
|
63
63
|
out['$ref'] = '../defs/definitions.json#/boolean_list'
|
|
64
|
-
when 'integer_list'
|
|
64
|
+
when 'integer_list', 'integer_list_as_string'
|
|
65
65
|
out['$ref'] = '../defs/definitions.json#/integer_list'
|
|
66
|
-
when '
|
|
66
|
+
when 'number_list', 'number_list_as_string'
|
|
67
|
+
out['$ref'] = '../defs/definitions.json#/number_list'
|
|
68
|
+
when 'string_list', 'string_list_as_string'
|
|
67
69
|
out['$ref'] = '../defs/definitions.json#/string_list'
|
|
68
70
|
else
|
|
69
71
|
raise "Error: List of #{item['type']} is not supported: #{item.inspect}"
|
|
@@ -74,7 +76,7 @@ module RSMP
|
|
|
74
76
|
out['pattern'] = /(?-mix:^(#{value_list})(?:,(#{value_list}))*$)/
|
|
75
77
|
end
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
handle_pattern item, out
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
# convert yaml values to json schema enum
|
|
@@ -26,8 +26,18 @@ module RSMP
|
|
|
26
26
|
JSON.generate(item, JSON_OPTIONS)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
def self.minimum_core_version(sxl)
|
|
30
|
+
sxl.dig(:meta, 'minimum_core_version') || RSMP::Schema.latest_core_version
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Path to definitions.json for the fallback bundled core schema version
|
|
34
|
+
def self.definitions_source(sxl)
|
|
35
|
+
version = minimum_core_version(sxl)
|
|
36
|
+
path = File.expand_path("../../../../schemas/core/#{version}/definitions.json", __dir__)
|
|
37
|
+
raise "Missing core definitions for RSMP #{version}" unless File.exist?(path)
|
|
38
|
+
|
|
39
|
+
path
|
|
40
|
+
end
|
|
31
41
|
|
|
32
42
|
# generate the json schema from a string containing yaml
|
|
33
43
|
def self.generate(sxl)
|
|
@@ -36,6 +46,7 @@ module RSMP
|
|
|
36
46
|
output_alarms out, sxl[:alarms]
|
|
37
47
|
output_statuses out, sxl[:statuses]
|
|
38
48
|
output_commands out, sxl[:commands]
|
|
49
|
+
output_sxl_index out, sxl
|
|
39
50
|
out
|
|
40
51
|
end
|
|
41
52
|
|
|
@@ -50,7 +61,8 @@ module RSMP
|
|
|
50
61
|
# Copy definitions.json so each version folder is self-contained
|
|
51
62
|
defs_dest = File.join(folder, 'defs', 'definitions.json')
|
|
52
63
|
FileUtils.mkdir_p File.dirname(defs_dest)
|
|
53
|
-
|
|
64
|
+
source = definitions_source(sxl)
|
|
65
|
+
FileUtils.cp source, defs_dest
|
|
54
66
|
end
|
|
55
67
|
end
|
|
56
68
|
end
|
|
@@ -3,8 +3,8 @@ class Hash
|
|
|
3
3
|
def deep_merge(other_hash)
|
|
4
4
|
return self unless other_hash
|
|
5
5
|
|
|
6
|
-
merge(other_hash) do |
|
|
7
|
-
if old.is_a?(Hash) && fresh.is_a?(Hash)
|
|
6
|
+
merge(other_hash) do |key, old, fresh|
|
|
7
|
+
if key != 'sxls' && old.is_a?(Hash) && fresh.is_a?(Hash)
|
|
8
8
|
old.deep_merge(fresh)
|
|
9
9
|
else
|
|
10
10
|
fresh
|
data/lib/rsmp/message.rb
CHANGED
|
@@ -30,6 +30,7 @@ module RSMP
|
|
|
30
30
|
'MessageAck' => MessageAck,
|
|
31
31
|
'MessageNotAck' => MessageNotAck,
|
|
32
32
|
'Version' => Version,
|
|
33
|
+
'ComponentList' => ComponentList,
|
|
33
34
|
'AggregatedStatus' => AggregatedStatus,
|
|
34
35
|
'AggregatedStatusRequest' => AggregatedStatusRequest,
|
|
35
36
|
'Watchdog' => Watchdog,
|
|
@@ -216,6 +217,37 @@ module RSMP
|
|
|
216
217
|
def versions
|
|
217
218
|
attribute('RSMP').map { |item| item['vers'] }
|
|
218
219
|
end
|
|
220
|
+
|
|
221
|
+
def step
|
|
222
|
+
@attributes['step']
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def request?
|
|
226
|
+
step == 'Request'
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def response?
|
|
230
|
+
step == 'Response'
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def sxls
|
|
234
|
+
(@attributes['SXLS'] || []).map do |item|
|
|
235
|
+
item.transform_keys(&:to_s)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def site_ids
|
|
240
|
+
attribute('siteId').map { |item| item['sId'] }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# ComponentList message, lists site components and their component types.
|
|
245
|
+
class ComponentList < Message
|
|
246
|
+
def initialize(attributes = {})
|
|
247
|
+
super({
|
|
248
|
+
'type' => 'ComponentList'
|
|
249
|
+
}.merge attributes)
|
|
250
|
+
end
|
|
219
251
|
end
|
|
220
252
|
|
|
221
253
|
# Unknown message type wrapper.
|
data/lib/rsmp/node/site/site.rb
CHANGED
|
@@ -19,8 +19,16 @@ module RSMP
|
|
|
19
19
|
build_proxies
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def sxls
|
|
23
|
+
@site_settings['sxls']
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def primary_sxl
|
|
27
|
+
sxls.first
|
|
28
|
+
end
|
|
29
|
+
|
|
22
30
|
def sxl_version
|
|
23
|
-
|
|
31
|
+
primary_sxl && primary_sxl['version']
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
def site_id
|
|
@@ -30,18 +38,34 @@ module RSMP
|
|
|
30
38
|
def handle_site_settings(options = {})
|
|
31
39
|
options_class = self.class.options_class
|
|
32
40
|
settings = options[:site_settings] || {}
|
|
41
|
+
settings = denormalize_sxls(settings)
|
|
33
42
|
@site_options = options_class.new(settings)
|
|
34
43
|
@site_settings = @site_options.to_h
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
check_sxls
|
|
37
46
|
check_core_versions
|
|
38
47
|
setup_components @site_settings['components']
|
|
39
48
|
end
|
|
40
49
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
def denormalize_sxls(settings)
|
|
51
|
+
sxls = settings['sxls']
|
|
52
|
+
return settings unless sxls.is_a?(Array)
|
|
53
|
+
|
|
54
|
+
settings.merge(
|
|
55
|
+
'sxls' => sxls.to_h { |sxl| [sxl['name'], sxl['version']] }
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def check_sxls
|
|
60
|
+
raise RSMP::ConfigurationError, 'No SXLs specified' unless sxls
|
|
61
|
+
|
|
62
|
+
sxls.each do |sxl|
|
|
63
|
+
name = sxl['name']
|
|
64
|
+
version = sxl['version'].to_s
|
|
65
|
+
raise RSMP::ConfigurationError, 'SXL name cannot be core' if name.to_s == 'core'
|
|
66
|
+
|
|
67
|
+
RSMP::Schema.find_schema! name, version, lenient: true
|
|
68
|
+
end
|
|
45
69
|
end
|
|
46
70
|
|
|
47
71
|
def check_core_versions
|
|
@@ -60,7 +84,7 @@ module RSMP
|
|
|
60
84
|
|
|
61
85
|
def log_site_starting
|
|
62
86
|
log "Starting #{site_type_name} #{@site_settings['site_id']}", level: :info, timestamp: @clock.now
|
|
63
|
-
sxl = "Using #{
|
|
87
|
+
sxl = "Using SXLs #{sxls.map { |item| "#{item['name']} #{item['version']}" }.join(', ')}"
|
|
64
88
|
version = @site_settings['core_version']
|
|
65
89
|
core = if version
|
|
66
90
|
"accepting only core version #{version}"
|
|
@@ -113,7 +137,7 @@ module RSMP
|
|
|
113
137
|
|
|
114
138
|
def send_alarm(alarm)
|
|
115
139
|
@proxies.each do |proxy|
|
|
116
|
-
proxy.send_message alarm if proxy.ready?
|
|
140
|
+
proxy.send_message alarm if proxy.ready? && proxy.receive_alarms?
|
|
117
141
|
end
|
|
118
142
|
end
|
|
119
143
|
|
|
@@ -158,10 +182,10 @@ module RSMP
|
|
|
158
182
|
def build_component(id:, type:, settings:)
|
|
159
183
|
settings ||= {}
|
|
160
184
|
if type == 'main'
|
|
161
|
-
Component.new id: id, node: self, grouped: true,
|
|
185
|
+
Component.new id: id, node: self, type: type, name: settings['name'], grouped: true,
|
|
162
186
|
ntsoid: settings['ntsOId'], xnid: settings['xNId']
|
|
163
187
|
else
|
|
164
|
-
Component.new id: id, node: self, grouped: false
|
|
188
|
+
Component.new id: id, node: self, type: type, name: settings['name'], grouped: false
|
|
165
189
|
end
|
|
166
190
|
end
|
|
167
191
|
end
|
|
@@ -4,22 +4,49 @@ module RSMP
|
|
|
4
4
|
# Handles supervisor configuration and site settings
|
|
5
5
|
module Configuration
|
|
6
6
|
def handle_supervisor_settings(supervisor_settings)
|
|
7
|
+
supervisor_settings = denormalize_supervisor_sxls(supervisor_settings || {})
|
|
7
8
|
options = RSMP::Supervisor::Options.new(supervisor_settings || {})
|
|
8
9
|
@supervisor_settings = options.to_h
|
|
9
10
|
@core_version = @supervisor_settings.dig('default', 'core_version')
|
|
10
|
-
|
|
11
|
+
check_site_sxls
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
def
|
|
14
|
+
def denormalize_supervisor_sxls(settings)
|
|
15
|
+
settings = settings.merge('default' => denormalize_site_sxls(settings['default'])) if settings['default']
|
|
16
|
+
return settings unless settings['sites']
|
|
17
|
+
|
|
18
|
+
settings.merge(
|
|
19
|
+
'sites' => settings['sites'].transform_values { |site_settings| denormalize_site_sxls(site_settings) }
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def denormalize_site_sxls(settings)
|
|
24
|
+
sxls = settings['sxls']
|
|
25
|
+
return settings unless sxls.is_a?(Array)
|
|
26
|
+
|
|
27
|
+
settings.merge(
|
|
28
|
+
'sxls' => sxls.to_h { |sxl| [sxl['name'], sxl['version']] }
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def check_site_sxls
|
|
14
33
|
sites = @supervisor_settings['sites'].clone || {}
|
|
15
34
|
sites['default'] = @supervisor_settings['default']
|
|
16
35
|
sites.each do |site_id, settings|
|
|
17
36
|
raise RSMP::ConfigurationError, "Configuration for site '#{site_id}' is empty" unless settings
|
|
18
37
|
|
|
19
|
-
|
|
20
|
-
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': No
|
|
38
|
+
sxls = settings['sxls']
|
|
39
|
+
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': No SXLs specified" unless sxls
|
|
40
|
+
|
|
41
|
+
sxls.each do |sxl|
|
|
42
|
+
name = sxl['name']
|
|
43
|
+
if name.to_s == 'core'
|
|
44
|
+
raise RSMP::ConfigurationError,
|
|
45
|
+
"Configuration error for site '#{site_id}': SXL name cannot be core"
|
|
46
|
+
end
|
|
21
47
|
|
|
22
|
-
|
|
48
|
+
RSMP::Schema.find_schema! name, sxl['version'], lenient: true
|
|
49
|
+
end
|
|
23
50
|
rescue RSMP::Schema::UnknownSchemaError => e
|
|
24
51
|
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': #{e}"
|
|
25
52
|
end
|
|
@@ -128,8 +128,6 @@ module RSMP
|
|
|
128
128
|
stop if @supervisor_settings['one_shot']
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
# Proxy type is now derived from `site_settings['sxl']` in Supervisor#build_proxy.
|
|
132
|
-
|
|
133
131
|
def reject_connection(_socket, info)
|
|
134
132
|
log 'Site rejected', ip: info[:ip], level: :info
|
|
135
133
|
end
|
|
@@ -59,13 +59,6 @@ module RSMP
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def build_proxy(settings)
|
|
62
|
-
# Determine proxy type from site settings (SXL). Fall back to supervisor
|
|
63
|
-
# default settings when site-specific settings are not present.
|
|
64
|
-
site_settings = settings[:site_settings] || @supervisor_settings['default']
|
|
65
|
-
sxl_type = site_settings && site_settings['sxl']
|
|
66
|
-
|
|
67
|
-
return RSMP::TLC::TrafficControllerProxy.new(settings) if sxl_type == 'tlc'
|
|
68
|
-
|
|
69
62
|
SiteProxy.new settings
|
|
70
63
|
end
|
|
71
64
|
|
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)
|