rsmp 0.51.3 → 0.51.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/documentation/configuration.md +12 -0
- data/lib/rsmp/options/options.rb +8 -4
- data/lib/rsmp/options/schemas/required_settings.json +30 -0
- data/lib/rsmp/options/schemas/required_supervisor_settings.json +12 -0
- data/lib/rsmp/options/site_options.rb +6 -1
- data/lib/rsmp/options/supervisor_options.rb +9 -0
- data/lib/rsmp/proxy/modules/acknowledgements.rb +2 -0
- data/lib/rsmp/proxy/site/modules/status.rb +45 -9
- data/lib/rsmp/tlc/modules/inputs.rb +2 -0
- data/lib/rsmp/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 653475280335ad2b867657cada46ef95d9cb61808cb12f3302762b0e2bcda790
|
|
4
|
+
data.tar.gz: 2d8b6cfe3c5b46623329cb1dbcb1a01544dd293ab93f5398b650fea8121d31fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82018c56d70a3cfa59db7d60b30c172f324765a01be8d650906d64c92109d68c52d80f75efdf388fd8332a9fa5028bc0ee1bed51e4293a1ffa6c5f441bfec8db
|
|
7
|
+
data.tar.gz: a1b83604f3b516da1dc0e8a3071cddd71cd4cadbc78f22406fe5ea0f73d21c06663ef1739de1666818d88ea85445cc6cc045ee024987fe2120a4ddc6e179b539
|
data/CHANGELOG.md
CHANGED
|
@@ -719,3 +719,13 @@ Initial release.
|
|
|
719
719
|
|
|
720
720
|
## 0.51.1
|
|
721
721
|
- cleanup semantic versioning handling of version strings and use canonical schema versions internally
|
|
722
|
+
|
|
723
|
+
## 0.51.2
|
|
724
|
+
- bumb patch version for test purposes, no changes othewise
|
|
725
|
+
|
|
726
|
+
## 0.51.3
|
|
727
|
+
- ensure socket shutdown when stopping node
|
|
728
|
+
|
|
729
|
+
## 0.51.4
|
|
730
|
+
- don't update subscription list until unsubscribe is acknowledged
|
|
731
|
+
|
data/Gemfile.lock
CHANGED
|
@@ -16,6 +16,18 @@ RSMP uses option classes to handle configuration for sites and supervisors. Thes
|
|
|
16
16
|
|
|
17
17
|
Each class applies defaults and validates the configuration against a JSON Schema located in `lib/rsmp/options/schemas/`.
|
|
18
18
|
|
|
19
|
+
Field types and unknown keys are checked before defaults are applied. The `required_settings.json`
|
|
20
|
+
and `required_supervisor_settings.json` schemas then check required settings after defaults and
|
|
21
|
+
supervisor site inheritance are resolved:
|
|
22
|
+
|
|
23
|
+
- Every configured signal plan must have a `cycle_time`.
|
|
24
|
+
- Configured components must include exactly one `main` component. Sites supply a default main
|
|
25
|
+
component when omitted; supervisor site settings can inherit it from `default`.
|
|
26
|
+
- A site in `server` role must have a `port`, either explicitly or from its first supervisor endpoint.
|
|
27
|
+
With `supervisors: []`, the port must be explicit.
|
|
28
|
+
|
|
29
|
+
`inputs.programming: {}` is allowed and means no programmed input actions.
|
|
30
|
+
|
|
19
31
|
## Version strings
|
|
20
32
|
|
|
21
33
|
Core 3.3.0 and later use exact `MAJOR.MINOR.PATCH` version strings for both Core and SXL negotiation.
|
data/lib/rsmp/options/options.rb
CHANGED
|
@@ -34,7 +34,9 @@ module RSMP
|
|
|
34
34
|
validate_log_settings! if validate
|
|
35
35
|
config = normalize_config(options || {})
|
|
36
36
|
validate!(config) if validate
|
|
37
|
-
|
|
37
|
+
config = apply_defaults(config)
|
|
38
|
+
validate_effective!(config) if validate
|
|
39
|
+
@data = normalize(config)
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
def defaults = {}
|
|
@@ -56,10 +58,10 @@ module RSMP
|
|
|
56
58
|
raise RSMP::ConfigurationError, "Invalid log configuration#{source_suffix}:\n#{message}"
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
def validate!(data = @data)
|
|
60
|
-
return unless
|
|
61
|
+
def validate!(data = @data, path: schema_path)
|
|
62
|
+
return unless path && File.exist?(path)
|
|
61
63
|
|
|
62
|
-
schemer = JSONSchemer.schema(Pathname.new(
|
|
64
|
+
schemer = JSONSchemer.schema(Pathname.new(path))
|
|
63
65
|
errors = schemer.validate(data).to_a
|
|
64
66
|
return if errors.empty?
|
|
65
67
|
|
|
@@ -86,6 +88,8 @@ module RSMP
|
|
|
86
88
|
|
|
87
89
|
private
|
|
88
90
|
|
|
91
|
+
def validate_effective!(_config); end
|
|
92
|
+
|
|
89
93
|
def apply_defaults(options)
|
|
90
94
|
defaults.deep_merge(options)
|
|
91
95
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$comment": "Validate after applying defaults and inherited site settings; structural validation uses the other config schemas.",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"components": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": ["main"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"main": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"minProperties": 1,
|
|
13
|
+
"maxProperties": 1
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"signal_plans": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"additionalProperties": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"required": ["cycle_time"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"if": {
|
|
26
|
+
"required": ["connection_role"],
|
|
27
|
+
"properties": { "connection_role": { "const": "server" } }
|
|
28
|
+
},
|
|
29
|
+
"then": { "required": ["port"] }
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$comment": "Validate each site after merging supervisor defaults, without changing the stored overrides.",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"default": { "$ref": "required_settings.json" },
|
|
7
|
+
"sites": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": { "$ref": "required_settings.json" }
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -23,6 +23,10 @@ module RSMP
|
|
|
23
23
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
|
+
def validate_effective!(config)
|
|
27
|
+
validate!(config, path: File.join(SCHEMAS_PATH, 'required_settings.json'))
|
|
28
|
+
end
|
|
29
|
+
|
|
26
30
|
def default_message_buffer
|
|
27
31
|
{
|
|
28
32
|
'max_messages' => 10_000,
|
|
@@ -54,7 +58,8 @@ module RSMP
|
|
|
54
58
|
defaults = defaults()
|
|
55
59
|
defaults['components']['main'] = options['components']['main'] if options.dig('components', 'main')
|
|
56
60
|
data = defaults.deep_merge(options)
|
|
57
|
-
|
|
61
|
+
port = data.dig('supervisors', 0, 'port')
|
|
62
|
+
data['port'] ||= port if port
|
|
58
63
|
data
|
|
59
64
|
end
|
|
60
65
|
end
|
|
@@ -28,6 +28,15 @@ module RSMP
|
|
|
28
28
|
def schema_file
|
|
29
29
|
'supervisor.json'
|
|
30
30
|
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def validate_effective!(config)
|
|
35
|
+
base = config['default'] || {}
|
|
36
|
+
sites = (config['sites'] || {}).transform_values { |settings| base.deep_merge(settings) }
|
|
37
|
+
effective = config.merge('sites' => sites)
|
|
38
|
+
validate!(effective, path: File.join(SCHEMAS_PATH, 'required_supervisor_settings.json'))
|
|
39
|
+
end
|
|
31
40
|
end
|
|
32
41
|
end
|
|
33
42
|
end
|
|
@@ -3,6 +3,16 @@ module RSMP
|
|
|
3
3
|
module Modules
|
|
4
4
|
# Handles status requests, responses, subscriptions and updates
|
|
5
5
|
module Status
|
|
6
|
+
def clear
|
|
7
|
+
super
|
|
8
|
+
@pending_status_unsubscriptions = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def process_not_ack(message)
|
|
12
|
+
@pending_status_unsubscriptions.delete(message.attribute('oMId'))
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
|
|
6
16
|
# Build and send a StatusRequest. Returns Result<StatusRequest>.
|
|
7
17
|
def request_status(status_list, component: nil, m_id: nil, validate: true)
|
|
8
18
|
readiness = validate_ready 'request status'
|
|
@@ -75,9 +85,9 @@ module RSMP
|
|
|
75
85
|
subscribe_list.each do |item|
|
|
76
86
|
code = item['sCI']
|
|
77
87
|
name = item['n']
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
ensure_subscription_path(component_id, code, name)
|
|
89
|
+
# Replace the record so an older unsubscribe ACK cannot remove it.
|
|
90
|
+
@status_subscriptions[component_id][code][name] = item.slice('uRt', 'sOc')
|
|
81
91
|
end
|
|
82
92
|
end
|
|
83
93
|
|
|
@@ -147,20 +157,46 @@ module RSMP
|
|
|
147
157
|
def unsubscribe_to_status(status_list, component: nil, validate: nil)
|
|
148
158
|
component ||= main.c_id
|
|
149
159
|
|
|
150
|
-
status_list.each do |item|
|
|
151
|
-
remove_subscription_item(component, item['sCI'], item['n'])
|
|
152
|
-
end
|
|
153
|
-
|
|
154
160
|
# Local subscription state must be cleaned up even when the peer has
|
|
155
161
|
# already disconnected. In that case there is nothing left to send.
|
|
156
|
-
|
|
162
|
+
unless ready?
|
|
163
|
+
status_list.each do |item|
|
|
164
|
+
remove_subscription_item(component, item['sCI'], item['n'])
|
|
165
|
+
end
|
|
166
|
+
return Result.success(nil)
|
|
167
|
+
end
|
|
157
168
|
|
|
158
169
|
message = RSMP::StatusUnsubscribe.new({
|
|
159
170
|
'cId' => component,
|
|
160
171
|
'sS' => status_list
|
|
161
172
|
})
|
|
162
173
|
apply_nts_message_attributes message
|
|
163
|
-
|
|
174
|
+
track_status_unsubscribe(message)
|
|
175
|
+
delivery = send_message(message, validate: validate)
|
|
176
|
+
delivery.map(&:message)
|
|
177
|
+
ensure
|
|
178
|
+
@pending_status_unsubscriptions.delete(message.m_id) if message && !delivery&.success?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def track_status_unsubscribe(message)
|
|
182
|
+
component = message.attribute('cId')
|
|
183
|
+
@pending_status_unsubscriptions[message.m_id] = message.attribute('sS').map do |item|
|
|
184
|
+
code = item['sCI']
|
|
185
|
+
name = item['n']
|
|
186
|
+
[code, name, @status_subscriptions.dig(component, code, name)]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def status_unsubscribe_acknowledged(original)
|
|
191
|
+
component = original.attribute('cId')
|
|
192
|
+
subscriptions = @pending_status_unsubscriptions.delete(original.m_id)
|
|
193
|
+
return unless subscriptions
|
|
194
|
+
|
|
195
|
+
subscriptions.each do |code, name, subscription|
|
|
196
|
+
next unless @status_subscriptions.dig(component, code, name).equal?(subscription)
|
|
197
|
+
|
|
198
|
+
remove_subscription_item(component, code, name)
|
|
199
|
+
end
|
|
164
200
|
end
|
|
165
201
|
|
|
166
202
|
def unsubscribe_to_status!(...)
|
data/lib/rsmp/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rsmp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.51.
|
|
4
|
+
version: 0.51.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emil Tin
|
|
@@ -218,6 +218,8 @@ files:
|
|
|
218
218
|
- lib/rsmp/options/options.rb
|
|
219
219
|
- lib/rsmp/options/schemas/config_common.json
|
|
220
220
|
- lib/rsmp/options/schemas/log.json
|
|
221
|
+
- lib/rsmp/options/schemas/required_settings.json
|
|
222
|
+
- lib/rsmp/options/schemas/required_supervisor_settings.json
|
|
221
223
|
- lib/rsmp/options/schemas/site.json
|
|
222
224
|
- lib/rsmp/options/schemas/supervisor.json
|
|
223
225
|
- lib/rsmp/options/schemas/supervisor_site.json
|