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.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +11 -11
  3. data/README.md +19 -3
  4. data/Rakefile +2 -2
  5. data/config/supervisor.yaml +2 -1
  6. data/config/tlc.yaml +2 -2
  7. data/lib/rsmp/cli.rb +29 -5
  8. data/lib/rsmp/component/component.rb +0 -4
  9. data/lib/rsmp/component/component_base.rb +15 -2
  10. data/lib/rsmp/component/component_proxy.rb +1 -1
  11. data/lib/rsmp/component/components.rb +22 -1
  12. data/lib/rsmp/convert/export/json_schema/outputs.rb +1 -0
  13. data/lib/rsmp/convert/export/json_schema/values.rb +6 -4
  14. data/lib/rsmp/convert/export/json_schema.rb +7 -3
  15. data/lib/rsmp/helpers/deep_merge.rb +2 -2
  16. data/lib/rsmp/message.rb +32 -0
  17. data/lib/rsmp/node/site/site.rb +34 -10
  18. data/lib/rsmp/node/supervisor/modules/configuration.rb +32 -5
  19. data/lib/rsmp/node/supervisor/modules/connection.rb +0 -2
  20. data/lib/rsmp/node/supervisor/supervisor.rb +0 -7
  21. data/lib/rsmp/options/options.rb +55 -6
  22. data/lib/rsmp/options/schemas/site.json +6 -3
  23. data/lib/rsmp/options/schemas/supervisor.json +5 -2
  24. data/lib/rsmp/options/schemas/supervisor_site.json +5 -2
  25. data/lib/rsmp/options/site_options.rb +3 -2
  26. data/lib/rsmp/options/supervisor_options.rb +3 -1
  27. data/lib/rsmp/proxy/modules/acknowledgements.rb +2 -0
  28. data/lib/rsmp/proxy/modules/receive.rb +5 -2
  29. data/lib/rsmp/proxy/modules/state.rb +1 -0
  30. data/lib/rsmp/proxy/modules/versions.rb +90 -15
  31. data/lib/rsmp/proxy/proxy.rb +52 -3
  32. data/lib/rsmp/proxy/site/modules/status.rb +5 -3
  33. data/lib/rsmp/proxy/site/site_proxy.rb +68 -35
  34. data/lib/rsmp/proxy/site/sxl_selection.rb +54 -0
  35. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +54 -18
  36. data/lib/rsmp/schema/message_resolution.rb +104 -0
  37. data/lib/rsmp/schema.rb +104 -22
  38. data/lib/rsmp/schema_error.rb +7 -1
  39. data/lib/rsmp/sxl/interface.rb +48 -0
  40. data/lib/rsmp/sxl/registry.rb +55 -0
  41. data/lib/rsmp/sxl/site_interface.rb +10 -0
  42. data/lib/rsmp/sxl/supervisor_interface.rb +21 -0
  43. data/lib/rsmp/tlc/detector_logic.rb +2 -2
  44. data/lib/rsmp/tlc/signal_group.rb +2 -2
  45. data/lib/rsmp/tlc/site_interface.rb +10 -0
  46. data/lib/rsmp/tlc/{traffic_controller_proxy.rb → supervisor_interface.rb} +19 -34
  47. data/lib/rsmp/tlc/traffic_controller.rb +10 -2
  48. data/lib/rsmp/tlc/traffic_controller_site.rb +4 -2
  49. data/lib/rsmp/tlc.rb +10 -0
  50. data/lib/rsmp/version.rb +1 -1
  51. data/lib/rsmp.rb +8 -1
  52. data/rsmp.gemspec +5 -5
  53. data/schemas/core/3.3.0/aggregated_status.json +25 -0
  54. data/schemas/core/3.3.0/aggregated_status_request.json +9 -0
  55. data/schemas/core/3.3.0/alarm.json +71 -0
  56. data/schemas/core/3.3.0/alarm_acknowledge.json +11 -0
  57. data/schemas/core/3.3.0/alarm_issue.json +44 -0
  58. data/schemas/core/3.3.0/alarm_request.json +3 -0
  59. data/schemas/core/3.3.0/alarm_suspend_resume.json +3 -0
  60. data/schemas/core/3.3.0/alarm_suspended_resumed.json +44 -0
  61. data/schemas/core/3.3.0/command_request.json +24 -0
  62. data/schemas/core/3.3.0/command_response.json +35 -0
  63. data/schemas/core/3.3.0/component_list.json +24 -0
  64. data/schemas/core/3.3.0/core.json +40 -0
  65. data/schemas/core/3.3.0/definitions.json +133 -0
  66. data/schemas/core/3.3.0/message_ack.json +11 -0
  67. data/schemas/core/3.3.0/message_not_ack.json +15 -0
  68. data/schemas/core/3.3.0/rsmp.json +142 -0
  69. data/schemas/core/3.3.0/status.json +21 -0
  70. data/schemas/core/3.3.0/status_request.json +5 -0
  71. data/schemas/core/3.3.0/status_response.json +41 -0
  72. data/schemas/core/3.3.0/status_subscribe.json +31 -0
  73. data/schemas/core/3.3.0/status_unsubscribe.json +5 -0
  74. data/schemas/core/3.3.0/status_update.json +41 -0
  75. data/schemas/core/3.3.0/version.json +144 -0
  76. data/schemas/core/3.3.0/watchdog.json +9 -0
  77. data/schemas/tlc/1.3.0/alarms/A0001.json +4 -0
  78. data/schemas/tlc/1.3.0/alarms/A0002.json +4 -0
  79. data/schemas/tlc/1.3.0/alarms/A0003.json +4 -0
  80. data/schemas/tlc/1.3.0/alarms/A0004.json +4 -0
  81. data/schemas/tlc/1.3.0/alarms/A0005.json +4 -0
  82. data/schemas/tlc/1.3.0/alarms/A0006.json +4 -0
  83. data/schemas/tlc/1.3.0/alarms/A0007.json +34 -0
  84. data/schemas/tlc/1.3.0/alarms/A0008.json +30 -0
  85. data/schemas/tlc/1.3.0/alarms/A0009.json +4 -0
  86. data/schemas/tlc/1.3.0/alarms/A0010.json +4 -0
  87. data/schemas/tlc/1.3.0/alarms/A0101.json +4 -0
  88. data/schemas/tlc/1.3.0/alarms/A0201.json +35 -0
  89. data/schemas/tlc/1.3.0/alarms/A0202.json +35 -0
  90. data/schemas/tlc/1.3.0/alarms/A0301.json +92 -0
  91. data/schemas/tlc/1.3.0/alarms/A0302.json +115 -0
  92. data/schemas/tlc/1.3.0/alarms/A0303.json +92 -0
  93. data/schemas/tlc/1.3.0/alarms/A0304.json +115 -0
  94. data/schemas/tlc/1.3.0/alarms/alarms.json +287 -0
  95. data/schemas/tlc/1.3.0/commands/M0001.json +92 -0
  96. data/schemas/tlc/1.3.0/commands/M0002.json +69 -0
  97. data/schemas/tlc/1.3.0/commands/M0003.json +69 -0
  98. data/schemas/tlc/1.3.0/commands/M0004.json +51 -0
  99. data/schemas/tlc/1.3.0/commands/M0005.json +69 -0
  100. data/schemas/tlc/1.3.0/commands/M0006.json +69 -0
  101. data/schemas/tlc/1.3.0/commands/M0007.json +51 -0
  102. data/schemas/tlc/1.3.0/commands/M0008.json +87 -0
  103. data/schemas/tlc/1.3.0/commands/M0010.json +51 -0
  104. data/schemas/tlc/1.3.0/commands/M0011.json +51 -0
  105. data/schemas/tlc/1.3.0/commands/M0012.json +51 -0
  106. data/schemas/tlc/1.3.0/commands/M0013.json +51 -0
  107. data/schemas/tlc/1.3.0/commands/M0014.json +69 -0
  108. data/schemas/tlc/1.3.0/commands/M0015.json +69 -0
  109. data/schemas/tlc/1.3.0/commands/M0016.json +51 -0
  110. data/schemas/tlc/1.3.0/commands/M0017.json +51 -0
  111. data/schemas/tlc/1.3.0/commands/M0018.json +69 -0
  112. data/schemas/tlc/1.3.0/commands/M0019.json +87 -0
  113. data/schemas/tlc/1.3.0/commands/M0020.json +87 -0
  114. data/schemas/tlc/1.3.0/commands/M0021.json +51 -0
  115. data/schemas/tlc/1.3.0/commands/M0022.json +249 -0
  116. data/schemas/tlc/1.3.0/commands/M0023.json +51 -0
  117. data/schemas/tlc/1.3.0/commands/M0024.json +33 -0
  118. data/schemas/tlc/1.3.0/commands/M0103.json +72 -0
  119. data/schemas/tlc/1.3.0/commands/M0104.json +141 -0
  120. data/schemas/tlc/1.3.0/commands/command_requests.json +8 -0
  121. data/schemas/tlc/1.3.0/commands/command_responses.json +8 -0
  122. data/schemas/tlc/1.3.0/commands/commands.json +415 -0
  123. data/schemas/tlc/1.3.0/defs/definitions.json +72 -0
  124. data/schemas/tlc/1.3.0/defs/guards.json +24 -0
  125. data/schemas/tlc/1.3.0/rsmp.json +74 -0
  126. data/schemas/tlc/1.3.0/statuses/S0001.json +109 -0
  127. data/schemas/tlc/1.3.0/statuses/S0002.json +36 -0
  128. data/schemas/tlc/1.3.0/statuses/S0003.json +36 -0
  129. data/schemas/tlc/1.3.0/statuses/S0004.json +36 -0
  130. data/schemas/tlc/1.3.0/statuses/S0005.json +72 -0
  131. data/schemas/tlc/1.3.0/statuses/S0006.json +54 -0
  132. data/schemas/tlc/1.3.0/statuses/S0007.json +73 -0
  133. data/schemas/tlc/1.3.0/statuses/S0008.json +73 -0
  134. data/schemas/tlc/1.3.0/statuses/S0009.json +73 -0
  135. data/schemas/tlc/1.3.0/statuses/S0010.json +73 -0
  136. data/schemas/tlc/1.3.0/statuses/S0011.json +73 -0
  137. data/schemas/tlc/1.3.0/statuses/S0012.json +73 -0
  138. data/schemas/tlc/1.3.0/statuses/S0013.json +54 -0
  139. data/schemas/tlc/1.3.0/statuses/S0014.json +55 -0
  140. data/schemas/tlc/1.3.0/statuses/S0015.json +55 -0
  141. data/schemas/tlc/1.3.0/statuses/S0016.json +36 -0
  142. data/schemas/tlc/1.3.0/statuses/S0017.json +36 -0
  143. data/schemas/tlc/1.3.0/statuses/S0018.json +61 -0
  144. data/schemas/tlc/1.3.0/statuses/S0019.json +36 -0
  145. data/schemas/tlc/1.3.0/statuses/S0020.json +54 -0
  146. data/schemas/tlc/1.3.0/statuses/S0021.json +37 -0
  147. data/schemas/tlc/1.3.0/statuses/S0022.json +36 -0
  148. data/schemas/tlc/1.3.0/statuses/S0023.json +37 -0
  149. data/schemas/tlc/1.3.0/statuses/S0024.json +36 -0
  150. data/schemas/tlc/1.3.0/statuses/S0025.json +162 -0
  151. data/schemas/tlc/1.3.0/statuses/S0026.json +36 -0
  152. data/schemas/tlc/1.3.0/statuses/S0027.json +36 -0
  153. data/schemas/tlc/1.3.0/statuses/S0028.json +36 -0
  154. data/schemas/tlc/1.3.0/statuses/S0029.json +36 -0
  155. data/schemas/tlc/1.3.0/statuses/S0030.json +36 -0
  156. data/schemas/tlc/1.3.0/statuses/S0031.json +36 -0
  157. data/schemas/tlc/1.3.0/statuses/S0032.json +73 -0
  158. data/schemas/tlc/1.3.0/statuses/S0033.json +77 -0
  159. data/schemas/tlc/1.3.0/statuses/S0034.json +36 -0
  160. data/schemas/tlc/1.3.0/statuses/S0035.json +49 -0
  161. data/schemas/tlc/1.3.0/statuses/S0091.json +40 -0
  162. data/schemas/tlc/1.3.0/statuses/S0092.json +40 -0
  163. data/schemas/tlc/1.3.0/statuses/S0095.json +36 -0
  164. data/schemas/tlc/1.3.0/statuses/S0096.json +126 -0
  165. data/schemas/tlc/1.3.0/statuses/S0097.json +54 -0
  166. data/schemas/tlc/1.3.0/statuses/S0098.json +72 -0
  167. data/schemas/tlc/1.3.0/statuses/S0201.json +54 -0
  168. data/schemas/tlc/1.3.0/statuses/S0202.json +54 -0
  169. data/schemas/tlc/1.3.0/statuses/S0203.json +54 -0
  170. data/schemas/tlc/1.3.0/statuses/S0204.json +198 -0
  171. data/schemas/tlc/1.3.0/statuses/S0205.json +54 -0
  172. data/schemas/tlc/1.3.0/statuses/S0206.json +54 -0
  173. data/schemas/tlc/1.3.0/statuses/S0207.json +54 -0
  174. data/schemas/tlc/1.3.0/statuses/S0208.json +198 -0
  175. data/schemas/tlc/1.3.0/statuses/statuses.json +787 -0
  176. data/schemas/tlc/1.3.0/sxl.yaml +2296 -0
  177. metadata +144 -12
@@ -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
- normalized = normalize(options || {})
29
- @data = apply_defaults(normalized)
30
- validate! if validate
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(@data).to_a
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
- memo[key.to_s] = normalize(val)
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
- "sxl": { "type": "string" },
21
- "sxl_version": { "type": "string" },
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
- "sxl": { "type": "string" },
19
- "sxl_version": { "type": "string" },
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
- "sxl": { "type": "string" },
7
- "sxl_version": { "type": "string" },
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
- 'sxl' => 'tlc',
12
- 'sxl_version' => RSMP::Schema.latest_version(:tlc),
11
+ 'sxls' => {
12
+ 'tlc' => RSMP::Schema.latest_version(:tlc)
13
+ },
13
14
  'intervals' => {
14
15
  'timer' => 0.1,
15
16
  'watchdog' => 1,
@@ -7,7 +7,9 @@ module RSMP
7
7
  'port' => 12_111,
8
8
  'ips' => 'all',
9
9
  'default' => {
10
- 'sxl' => 'tlc',
10
+ 'sxls' => {
11
+ 'tlc' => RSMP::Schema.latest_version(:tlc)
12
+ },
11
13
  'intervals' => {
12
14
  'timer' => 1,
13
15
  'watchdog' => 1
@@ -86,6 +86,8 @@ module RSMP
86
86
  case original.type
87
87
  when 'Version'
88
88
  version_acknowledged
89
+ when 'ComponentList'
90
+ component_list_acknowledged
89
91
  when 'StatusSubscribe'
90
92
  status_subscribe_acknowledged original
91
93
  end
@@ -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
- schemas_string = error.schemas.map { |schema| "#{schema.first}: #{schema.last}" }.join(', ')
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)
@@ -17,6 +17,7 @@ module RSMP
17
17
  end
18
18
 
19
19
  def handshake_complete
20
+ build_sxl_interfaces
20
21
  self.state = :ready
21
22
  end
22
23
  end
@@ -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
- versions = if core_versions == 'latest'
40
- [RSMP::Schema.latest_core_version]
41
- elsif core_versions == 'all'
42
- RSMP::Schema.core_versions
43
- else
44
- [core_versions].flatten
45
- end
46
- versions_array = versions.map { |v| { 'vers' => v } }
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
- site_id_array = [site_id].flatten.map { |id| { 'sId' => id } }
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
- version_response = Version.new({
51
- 'RSMP' => versions_array,
52
- 'siteId' => site_id_array,
53
- 'SXL' => sxl_version.to_s
54
- })
55
- send_message version_response
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
@@ -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, :sxl, :collector, :ip, :port, :node, :core_version
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
- @sxl = nil
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
- schemas[sxl] = RSMP::Schema.sanitize_version(sxl_version.to_s) if sxl && sxl_version
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 = RSMP::Schema.status_catalogue(@sxl, RSMP::Schema.sanitize_version(sxl_version))
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
- log "Connection to site #{@site_id} established, using core #{@core_version}, #{@sxl} #{@site_sxl_version}",
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
- process_alarm message
62
- when CommandResponse
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
- send_version @site_id, core_versions
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
- handshake_complete
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
- @sxl = @site_settings['sxl']
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