rsmp 0.45.2 → 0.47.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/CHANGELOG.md +19 -0
- data/Gemfile.lock +1 -1
- data/documentation/configuration.md +26 -0
- data/lib/rsmp/cli.rb +27 -0
- data/lib/rsmp/config.rb +63 -0
- data/lib/rsmp/node/site/site.rb +45 -6
- data/lib/rsmp/options/options.rb +21 -9
- data/lib/rsmp/options/schemas/config_common.json +129 -0
- data/lib/rsmp/options/schemas/log.json +71 -0
- data/lib/rsmp/options/schemas/site.json +9 -31
- data/lib/rsmp/options/schemas/supervisor.json +3 -29
- data/lib/rsmp/options/schemas/supervisor_site.json +19 -31
- data/lib/rsmp/options/schemas/traffic_controller_site.json +1 -2
- data/lib/rsmp/options/site_options.rb +8 -0
- data/lib/rsmp/proxy/modules/send.rb +15 -7
- data/lib/rsmp/proxy/site/modules/status.rb +2 -1
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +139 -0
- data/lib/rsmp/proxy/supervisor/modules/status.rb +15 -0
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +9 -6
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +2 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c206e0c53de4f523b03832b8821b698ea0f0440c11cdb51f5685382f4bdaebf5
|
|
4
|
+
data.tar.gz: 2b35690dbe819d58cf76fe82faf1baec3392e6ecf54fbcd71b0e15c4fd7b7887
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4e7c84bc1a7c35f5f58108db5803cc8f0a9abe58ab9edc83a09d778d62479bcc1e0310eb2af9c0e9fe1cad75bf187303ba0d8759ff8b793dd15ae1abe118bcfe
|
|
7
|
+
data.tar.gz: 562f88737283e91ab232c68c70344c9f668e5d08b0aa488b24ac21af57d07c30936b297c647aa00bedc7dae585faf8ed6be3593849c3f63721b1974b809b1696
|
data/CHANGELOG.md
CHANGED
|
@@ -670,3 +670,22 @@ Initial release.
|
|
|
670
670
|
- fix issues with config normalization
|
|
671
671
|
- make collector cancellation raise pending errors
|
|
672
672
|
- replace Cucumber/Aruba CLI tests with sus tests that call the Thor CLI directly
|
|
673
|
+
|
|
674
|
+
## 0.45.1
|
|
675
|
+
- fix CLI execution by using relative requires
|
|
676
|
+
- update schema generation and generated schemas
|
|
677
|
+
- support patterns in list item types
|
|
678
|
+
- resolve SXL schema core definitions dynamically
|
|
679
|
+
- add CLI tests and split message tests
|
|
680
|
+
|
|
681
|
+
## 0.45.2
|
|
682
|
+
- update gems
|
|
683
|
+
- add AGENTS.md
|
|
684
|
+
- replace em dashes to avoid UTF issues
|
|
685
|
+
|
|
686
|
+
## 0.46.0
|
|
687
|
+
- add outgoing message buffering for sites
|
|
688
|
+
|
|
689
|
+
## 0.47.0
|
|
690
|
+
- add cli command for checking configs
|
|
691
|
+
- tighten config schemas
|
data/Gemfile.lock
CHANGED
|
@@ -46,10 +46,36 @@ sxls:
|
|
|
46
46
|
components:
|
|
47
47
|
main:
|
|
48
48
|
TC:
|
|
49
|
+
message_buffer:
|
|
50
|
+
max_messages: 10000
|
|
51
|
+
statuses: true
|
|
49
52
|
log:
|
|
50
53
|
json: true
|
|
51
54
|
```
|
|
52
55
|
|
|
56
|
+
## Message Buffer
|
|
57
|
+
|
|
58
|
+
Sites buffer outgoing alarm and aggregated status messages while a supervisor connection is down. Status updates are buffered according to `message_buffer.statuses`, which defaults to `true`.
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
message_buffer:
|
|
62
|
+
max_messages: 10000
|
|
63
|
+
statuses: true
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`statuses: true` buffers all subscribed status updates during communication disruption. To buffer only selected statuses, provide selectors:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
message_buffer:
|
|
70
|
+
statuses:
|
|
71
|
+
- sCI: S0001
|
|
72
|
+
n: signalgroupstatus
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use `statuses: false` or an empty list to avoid buffering status updates. Subscriptions for statuses that are not buffered are removed when the connection is lost.
|
|
76
|
+
|
|
77
|
+
The current implementation uses an in-memory buffer per supervisor connection. Buffered messages survive reconnects while the process keeps running, but are lost if the process exits, crashes, or the host loses power. The RSMP core specification requires the outgoing communication buffer to survive communication failure and power outage, so this is not yet a complete persistent buffer implementation.
|
|
78
|
+
|
|
53
79
|
## Example: supervisor YAML
|
|
54
80
|
|
|
55
81
|
```yaml
|
data/lib/rsmp/cli.rb
CHANGED
|
@@ -20,6 +20,32 @@ module RSMP
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# CLI subcommands for RSMP configuration validation.
|
|
24
|
+
class ConfigCLI < Thor
|
|
25
|
+
namespace :config
|
|
26
|
+
desc 'check PATH...', 'Validate RSMP site or supervisor config files'
|
|
27
|
+
method_option :type, type: :string, aliases: '-t', default: 'auto',
|
|
28
|
+
enum: ['auto'] + RSMP::Config.types,
|
|
29
|
+
banner: 'Config type: auto, site, tlc or supervisor'
|
|
30
|
+
def check(*paths)
|
|
31
|
+
if paths.empty?
|
|
32
|
+
puts 'Error: config check requires at least one path'
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
valid = true
|
|
37
|
+
paths.each do |path|
|
|
38
|
+
RSMP::Config.load_file(path, type: options[:type])
|
|
39
|
+
puts 'OK'
|
|
40
|
+
rescue RSMP::ConfigurationError => e
|
|
41
|
+
valid = false
|
|
42
|
+
puts "Error: #{e.message}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
exit 1 unless valid
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
23
49
|
# CLI commands for running RSMP site and supervisor.
|
|
24
50
|
class CLI < Thor
|
|
25
51
|
desc 'version', 'Show version'
|
|
@@ -69,6 +95,7 @@ module RSMP
|
|
|
69
95
|
end
|
|
70
96
|
|
|
71
97
|
register SchemaCLI, 'schema', 'schema COMMAND', 'SXL schema commands'
|
|
98
|
+
register ConfigCLI, 'config', 'config COMMAND', 'Configuration commands'
|
|
72
99
|
|
|
73
100
|
no_commands do
|
|
74
101
|
def load_site_configuration
|
data/lib/rsmp/config.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
# Validation helpers for RSMP runtime configuration hashes and files.
|
|
3
|
+
module Config
|
|
4
|
+
class << self
|
|
5
|
+
def validate(settings = {}, type:, source: nil, log_settings: nil)
|
|
6
|
+
options_class_for(type).new(settings, source: source, log_settings: log_settings)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def load_file(path, type:)
|
|
10
|
+
raise RSMP::ConfigurationError, 'not found' unless File.exist?(path)
|
|
11
|
+
raise RSMP::ConfigurationError, 'is not a file' unless File.file?(path)
|
|
12
|
+
raise RSMP::ConfigurationError, 'must be a YAML file (.yml or .yaml)' unless yaml_file?(path)
|
|
13
|
+
|
|
14
|
+
raw = YAML.load_file(path)
|
|
15
|
+
raise RSMP::ConfigurationError, "Config #{path} must be a hash" unless raw.is_a?(Hash) || raw.nil?
|
|
16
|
+
|
|
17
|
+
raw ||= {}
|
|
18
|
+
settings = raw.dup
|
|
19
|
+
log_settings = settings.delete('log') || {}
|
|
20
|
+
validate(settings, type: resolve_type(type, settings), log_settings: log_settings)
|
|
21
|
+
rescue Psych::SyntaxError => e
|
|
22
|
+
raise RSMP::ConfigurationError, "Cannot read config file #{path}: #{e}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def types
|
|
26
|
+
%w[site supervisor tlc]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def yaml_file?(path)
|
|
32
|
+
%w[.yml .yaml].include?(File.extname(path).downcase)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def resolve_type(type, settings)
|
|
36
|
+
type = type.to_s
|
|
37
|
+
return infer_type(settings) if type == 'auto'
|
|
38
|
+
|
|
39
|
+
type
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def infer_type(settings)
|
|
43
|
+
return 'supervisor' if settings.key?('sites')
|
|
44
|
+
return 'site' if settings.key?('supervisors')
|
|
45
|
+
|
|
46
|
+
raise RSMP::ConfigurationError, 'Cannot infer config type; use --type site, --type tlc or --type supervisor'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def options_class_for(type)
|
|
50
|
+
case type.to_s
|
|
51
|
+
when 'site'
|
|
52
|
+
RSMP::Site::Options
|
|
53
|
+
when 'supervisor'
|
|
54
|
+
RSMP::Supervisor::Options
|
|
55
|
+
when 'tlc'
|
|
56
|
+
RSMP::TLC::TrafficControllerSite::Options
|
|
57
|
+
else
|
|
58
|
+
raise RSMP::ConfigurationError, "Unknown config type #{type.inspect}, expected one of #{types.join(', ')}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/rsmp/node/site/site.rb
CHANGED
|
@@ -96,10 +96,9 @@ module RSMP
|
|
|
96
96
|
|
|
97
97
|
def run
|
|
98
98
|
log_site_starting
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
end
|
|
99
|
+
start_status_timer
|
|
100
|
+
@proxies.each(&:start)
|
|
101
|
+
@proxies.each(&:wait)
|
|
103
102
|
end
|
|
104
103
|
|
|
105
104
|
def build_proxies
|
|
@@ -119,7 +118,7 @@ module RSMP
|
|
|
119
118
|
|
|
120
119
|
def aggregated_status_changed(component, _options = {})
|
|
121
120
|
@proxies.each do |proxy|
|
|
122
|
-
proxy.send_aggregated_status component
|
|
121
|
+
proxy.send_aggregated_status component
|
|
123
122
|
end
|
|
124
123
|
end
|
|
125
124
|
|
|
@@ -137,10 +136,50 @@ module RSMP
|
|
|
137
136
|
|
|
138
137
|
def send_alarm(alarm)
|
|
139
138
|
@proxies.each do |proxy|
|
|
140
|
-
proxy.send_message alarm if proxy.
|
|
139
|
+
proxy.send_message alarm if proxy.receive_alarms?
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def start_status_timer
|
|
144
|
+
return if @status_timer
|
|
145
|
+
|
|
146
|
+
interval = @site_settings['intervals']['timer'] || 1
|
|
147
|
+
log "Starting site status timer with interval #{interval} seconds", level: :debug
|
|
148
|
+
@status_timer = @task.async do |task|
|
|
149
|
+
task.annotate 'site status timer'
|
|
150
|
+
run_status_timer task, interval
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def run_status_timer(task, interval)
|
|
155
|
+
next_time = Time.now.to_f
|
|
156
|
+
loop do
|
|
157
|
+
now = Clock.now
|
|
158
|
+
tick_status_subscriptions now
|
|
159
|
+
rescue StandardError => e
|
|
160
|
+
distribute_error e, level: :internal
|
|
161
|
+
ensure
|
|
162
|
+
next_time += interval
|
|
163
|
+
duration = next_time - Time.now.to_f
|
|
164
|
+
task.sleep duration
|
|
141
165
|
end
|
|
142
166
|
end
|
|
143
167
|
|
|
168
|
+
def tick_status_subscriptions(now)
|
|
169
|
+
@proxies.each { |proxy| proxy.status_update_timer now }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def stop_status_timer
|
|
173
|
+
@status_timer&.stop
|
|
174
|
+
ensure
|
|
175
|
+
@status_timer = nil
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def stop_subtasks
|
|
179
|
+
stop_status_timer
|
|
180
|
+
super
|
|
181
|
+
end
|
|
182
|
+
|
|
144
183
|
def connect_to_supervisor(_task, supervisor_settings)
|
|
145
184
|
proxy = build_proxy({
|
|
146
185
|
site: self,
|
data/lib/rsmp/options/options.rb
CHANGED
|
@@ -10,6 +10,8 @@ module RSMP
|
|
|
10
10
|
|
|
11
11
|
def self.load_file(path, validate: true)
|
|
12
12
|
raise RSMP::ConfigurationError, "Config #{path} not found" unless File.exist?(path)
|
|
13
|
+
raise RSMP::ConfigurationError, "Config #{path} is not a file" unless File.file?(path)
|
|
14
|
+
raise RSMP::ConfigurationError, "Config #{path} must be a YAML file (.yml or .yaml)" unless yaml_file?(path)
|
|
13
15
|
|
|
14
16
|
raw = YAML.load_file(path)
|
|
15
17
|
raise RSMP::ConfigurationError, "Config #{path} must be a hash" unless raw.is_a?(Hash) || raw.nil?
|
|
@@ -21,27 +23,37 @@ module RSMP
|
|
|
21
23
|
raise RSMP::ConfigurationError, "Cannot read config file #{path}: #{e}"
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
def self.yaml_file?(path)
|
|
27
|
+
%w[.yml .yaml].include?(File.extname(path).downcase)
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
def initialize(options = nil, source: nil, log_settings: nil, validate: true, **extra)
|
|
25
31
|
options = extra if options.nil? && extra.any?
|
|
26
32
|
@source = source
|
|
27
33
|
@log_settings = normalize(log_settings || {})
|
|
34
|
+
validate_log_settings! if validate
|
|
28
35
|
config = normalize_config(options || {})
|
|
29
36
|
validate!(config) if validate
|
|
30
37
|
@data = normalize(apply_defaults(config))
|
|
31
38
|
end
|
|
32
39
|
|
|
33
|
-
def defaults
|
|
34
|
-
{}
|
|
35
|
-
end
|
|
40
|
+
def defaults = {}
|
|
36
41
|
|
|
37
|
-
def schema_file
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
def schema_file = nil
|
|
43
|
+
|
|
44
|
+
def schema_path = schema_file && File.join(SCHEMAS_PATH, schema_file)
|
|
40
45
|
|
|
41
|
-
def
|
|
42
|
-
return unless schema_file
|
|
46
|
+
def log_schema_path = File.join(SCHEMAS_PATH, 'log.json')
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
def validate_log_settings!
|
|
49
|
+
return unless File.exist?(log_schema_path)
|
|
50
|
+
|
|
51
|
+
schemer = JSONSchemer.schema(Pathname.new(log_schema_path))
|
|
52
|
+
errors = schemer.validate(@log_settings).to_a
|
|
53
|
+
return if errors.empty?
|
|
54
|
+
|
|
55
|
+
message = errors.map { |error| format_error(error) }.join("\n")
|
|
56
|
+
raise RSMP::ConfigurationError, "Invalid log configuration#{source_suffix}:\n#{message}"
|
|
45
57
|
end
|
|
46
58
|
|
|
47
59
|
def validate!(data = @data)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "config_common.json",
|
|
4
|
+
"$defs": {
|
|
5
|
+
"endpoint": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"ip": { "type": "string" },
|
|
9
|
+
"port": { "type": ["integer", "string"] }
|
|
10
|
+
},
|
|
11
|
+
"required": ["ip", "port"],
|
|
12
|
+
"additionalProperties": false
|
|
13
|
+
},
|
|
14
|
+
"intervals": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"timer": { "type": "number" },
|
|
18
|
+
"watchdog": { "type": "number" },
|
|
19
|
+
"reconnect": { "type": "number" },
|
|
20
|
+
"after_connect": { "type": "number" }
|
|
21
|
+
},
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
},
|
|
24
|
+
"timeouts": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"connect": { "type": "number" },
|
|
28
|
+
"ready": { "type": "number" },
|
|
29
|
+
"watchdog": { "type": "number" },
|
|
30
|
+
"acknowledgement": { "type": "number" },
|
|
31
|
+
"command": { "type": "number" },
|
|
32
|
+
"command_timeout": { "type": "number" },
|
|
33
|
+
"status_response": { "type": "number" }
|
|
34
|
+
},
|
|
35
|
+
"additionalProperties": false
|
|
36
|
+
},
|
|
37
|
+
"component_settings": {
|
|
38
|
+
"type": ["object", "null"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"name": { "type": "string" },
|
|
41
|
+
"ntsOId": { "type": "string" },
|
|
42
|
+
"xNId": { "type": "string" },
|
|
43
|
+
"type": { "type": "string" }
|
|
44
|
+
},
|
|
45
|
+
"additionalProperties": false
|
|
46
|
+
},
|
|
47
|
+
"components": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": {
|
|
50
|
+
"type": ["object", "null"],
|
|
51
|
+
"additionalProperties": { "$ref": "#/$defs/component_settings" }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"security_codes": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"additionalProperties": { "type": "string" }
|
|
57
|
+
},
|
|
58
|
+
"signal_plans": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"additionalProperties": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": {
|
|
63
|
+
"cycle_time": { "type": "number" },
|
|
64
|
+
"states": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": { "type": "string" }
|
|
67
|
+
},
|
|
68
|
+
"dynamic_bands": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": { "type": "integer" }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"input_programming_action": {
|
|
77
|
+
"type": ["object", "null"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"raise_alarm": { "type": "string" },
|
|
80
|
+
"component": { "type": "string" }
|
|
81
|
+
},
|
|
82
|
+
"additionalProperties": false
|
|
83
|
+
},
|
|
84
|
+
"inputs": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"properties": {
|
|
87
|
+
"total": { "type": "integer", "minimum": 1 },
|
|
88
|
+
"programming": {
|
|
89
|
+
"oneOf": [
|
|
90
|
+
{
|
|
91
|
+
"type": "object",
|
|
92
|
+
"additionalProperties": { "$ref": "#/$defs/input_programming_action" }
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": { "$ref": "#/$defs/input_programming_action" }
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"additionalProperties": false
|
|
102
|
+
},
|
|
103
|
+
"message_buffer": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"properties": {
|
|
106
|
+
"max_messages": { "type": "integer", "minimum": 1 },
|
|
107
|
+
"statuses": {
|
|
108
|
+
"oneOf": [
|
|
109
|
+
{ "type": "boolean" },
|
|
110
|
+
{
|
|
111
|
+
"type": "array",
|
|
112
|
+
"items": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"properties": {
|
|
115
|
+
"cId": { "type": "string" },
|
|
116
|
+
"sCI": { "type": "string" },
|
|
117
|
+
"n": { "type": "string" }
|
|
118
|
+
},
|
|
119
|
+
"required": ["sCI"],
|
|
120
|
+
"additionalProperties": false
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"additionalProperties": false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "log.json",
|
|
4
|
+
"$defs": {
|
|
5
|
+
"display_field": {
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{ "type": "boolean" },
|
|
8
|
+
{ "type": "integer", "minimum": 0 },
|
|
9
|
+
{ "type": "string" }
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"message_filter": { "type": "boolean" },
|
|
13
|
+
"style_item": {
|
|
14
|
+
"oneOf": [
|
|
15
|
+
{ "type": "string" },
|
|
16
|
+
{
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"color": { "type": "string" },
|
|
20
|
+
"mode": { "type": "string" }
|
|
21
|
+
},
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"style": {
|
|
27
|
+
"oneOf": [
|
|
28
|
+
{ "type": "boolean" },
|
|
29
|
+
{ "type": "string" },
|
|
30
|
+
{
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": { "$ref": "#/$defs/style_item" }
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"type": "object",
|
|
38
|
+
"properties": {
|
|
39
|
+
"active": { "type": "boolean" },
|
|
40
|
+
"path": { "type": ["string", "null"] },
|
|
41
|
+
"stream": {},
|
|
42
|
+
"style": { "$ref": "#/$defs/style" },
|
|
43
|
+
"debug": { "type": "boolean" },
|
|
44
|
+
"info": { "type": "boolean" },
|
|
45
|
+
"statistics": { "type": "boolean" },
|
|
46
|
+
"hide_ip_and_port": { "type": "boolean" },
|
|
47
|
+
"acknowledgements": { "type": "boolean" },
|
|
48
|
+
"watchdogs": { "$ref": "#/$defs/message_filter" },
|
|
49
|
+
"alarms": { "$ref": "#/$defs/message_filter" },
|
|
50
|
+
"versions": { "$ref": "#/$defs/message_filter" },
|
|
51
|
+
"statuses": { "$ref": "#/$defs/message_filter" },
|
|
52
|
+
"commands": { "$ref": "#/$defs/message_filter" },
|
|
53
|
+
"aggregated_status": { "$ref": "#/$defs/message_filter" },
|
|
54
|
+
"tabs": { "type": ["string", "boolean", "null"] },
|
|
55
|
+
"prefix": { "$ref": "#/$defs/display_field" },
|
|
56
|
+
"index": { "$ref": "#/$defs/display_field" },
|
|
57
|
+
"author": { "$ref": "#/$defs/display_field" },
|
|
58
|
+
"timestamp": { "$ref": "#/$defs/display_field" },
|
|
59
|
+
"ip": { "$ref": "#/$defs/display_field" },
|
|
60
|
+
"port": { "$ref": "#/$defs/display_field" },
|
|
61
|
+
"site_id": { "$ref": "#/$defs/display_field" },
|
|
62
|
+
"component": { "$ref": "#/$defs/display_field" },
|
|
63
|
+
"direction": { "$ref": "#/$defs/display_field" },
|
|
64
|
+
"level": { "$ref": "#/$defs/display_field" },
|
|
65
|
+
"id": { "$ref": "#/$defs/display_field" },
|
|
66
|
+
"text": { "$ref": "#/$defs/display_field" },
|
|
67
|
+
"json": { "$ref": "#/$defs/display_field" },
|
|
68
|
+
"exception": { "$ref": "#/$defs/display_field" }
|
|
69
|
+
},
|
|
70
|
+
"additionalProperties": false
|
|
71
|
+
}
|
|
@@ -7,15 +7,7 @@
|
|
|
7
7
|
"type": { "type": "string" },
|
|
8
8
|
"supervisors": {
|
|
9
9
|
"type": "array",
|
|
10
|
-
"items": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"properties": {
|
|
13
|
-
"ip": { "type": "string" },
|
|
14
|
-
"port": { "type": ["integer", "string"] }
|
|
15
|
-
},
|
|
16
|
-
"required": ["ip", "port"],
|
|
17
|
-
"additionalProperties": true
|
|
18
|
-
}
|
|
10
|
+
"items": { "$ref": "config_common.json#/$defs/endpoint" }
|
|
19
11
|
},
|
|
20
12
|
"sxls": {
|
|
21
13
|
"type": "object",
|
|
@@ -23,30 +15,16 @@
|
|
|
23
15
|
"additionalProperties": { "type": "string" }
|
|
24
16
|
},
|
|
25
17
|
"core_version": { "type": "string" },
|
|
26
|
-
"intervals": {
|
|
27
|
-
|
|
28
|
-
"properties": {
|
|
29
|
-
"timer": { "type": "number" },
|
|
30
|
-
"watchdog": { "type": "number" },
|
|
31
|
-
"reconnect": { "type": "number" }
|
|
32
|
-
},
|
|
33
|
-
"additionalProperties": true
|
|
34
|
-
},
|
|
35
|
-
"timeouts": {
|
|
36
|
-
"type": "object",
|
|
37
|
-
"properties": {
|
|
38
|
-
"watchdog": { "type": "number" },
|
|
39
|
-
"acknowledgement": { "type": "number" }
|
|
40
|
-
},
|
|
41
|
-
"additionalProperties": true
|
|
42
|
-
},
|
|
18
|
+
"intervals": { "$ref": "config_common.json#/$defs/intervals" },
|
|
19
|
+
"timeouts": { "$ref": "config_common.json#/$defs/timeouts" },
|
|
43
20
|
"send_after_connect": { "type": "boolean" },
|
|
44
|
-
"
|
|
45
|
-
"
|
|
21
|
+
"message_buffer": { "$ref": "config_common.json#/$defs/message_buffer" },
|
|
22
|
+
"components": { "$ref": "config_common.json#/$defs/components" },
|
|
23
|
+
"security_codes": { "$ref": "config_common.json#/$defs/security_codes" },
|
|
46
24
|
"startup_sequence": { "type": "string" },
|
|
47
|
-
"signal_plans": { "
|
|
48
|
-
"inputs": { "
|
|
25
|
+
"signal_plans": { "$ref": "config_common.json#/$defs/signal_plans" },
|
|
26
|
+
"inputs": { "$ref": "config_common.json#/$defs/inputs" },
|
|
49
27
|
"live_output": { "type": ["string", "null"] }
|
|
50
28
|
},
|
|
51
|
-
"additionalProperties":
|
|
29
|
+
"additionalProperties": false
|
|
52
30
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"properties": {
|
|
5
5
|
"port": { "type": ["integer", "string"] },
|
|
6
6
|
"ip": { "type": "string" },
|
|
7
|
+
"proxy_type": { "type": "string" },
|
|
7
8
|
"ips": {
|
|
8
9
|
"oneOf": [
|
|
9
10
|
{ "type": "string" },
|
|
@@ -12,38 +13,11 @@
|
|
|
12
13
|
},
|
|
13
14
|
"site_id": { "type": "string" },
|
|
14
15
|
"max_sites": { "type": "integer" },
|
|
15
|
-
"default": {
|
|
16
|
-
"type": "object",
|
|
17
|
-
"properties": {
|
|
18
|
-
"sxls": {
|
|
19
|
-
"type": "object",
|
|
20
|
-
"propertyNames": { "not": { "const": "core" } },
|
|
21
|
-
"additionalProperties": { "type": "string" }
|
|
22
|
-
},
|
|
23
|
-
"core_version": { "type": "string" },
|
|
24
|
-
"intervals": {
|
|
25
|
-
"type": "object",
|
|
26
|
-
"properties": {
|
|
27
|
-
"timer": { "type": "number" },
|
|
28
|
-
"watchdog": { "type": "number" }
|
|
29
|
-
},
|
|
30
|
-
"additionalProperties": true
|
|
31
|
-
},
|
|
32
|
-
"timeouts": {
|
|
33
|
-
"type": "object",
|
|
34
|
-
"properties": {
|
|
35
|
-
"watchdog": { "type": "number" },
|
|
36
|
-
"acknowledgement": { "type": "number" }
|
|
37
|
-
},
|
|
38
|
-
"additionalProperties": true
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
"additionalProperties": true
|
|
42
|
-
},
|
|
16
|
+
"default": { "$ref": "supervisor_site.json" },
|
|
43
17
|
"sites": {
|
|
44
18
|
"type": "object",
|
|
45
19
|
"additionalProperties": { "$ref": "supervisor_site.json" }
|
|
46
20
|
}
|
|
47
21
|
},
|
|
48
|
-
"additionalProperties":
|
|
22
|
+
"additionalProperties": false
|
|
49
23
|
}
|
|
@@ -8,42 +8,30 @@
|
|
|
8
8
|
"propertyNames": { "not": { "const": "core" } },
|
|
9
9
|
"additionalProperties": { "type": "string" }
|
|
10
10
|
},
|
|
11
|
+
"core_version": { "type": "string" },
|
|
12
|
+
"rsmp_versions": {
|
|
13
|
+
"oneOf": [
|
|
14
|
+
{ "type": "string" },
|
|
15
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
16
|
+
]
|
|
17
|
+
},
|
|
11
18
|
"type": { "type": "string" },
|
|
12
|
-
"components": { "
|
|
19
|
+
"components": { "$ref": "config_common.json#/$defs/components" },
|
|
13
20
|
"supervisors": {
|
|
14
21
|
"type": "array",
|
|
15
|
-
"items": {
|
|
16
|
-
"type": "object",
|
|
17
|
-
"properties": {
|
|
18
|
-
"ip": { "type": "string" },
|
|
19
|
-
"port": { "type": ["integer", "string"] }
|
|
20
|
-
},
|
|
21
|
-
"required": ["ip", "port"],
|
|
22
|
-
"additionalProperties": true
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"intervals": {
|
|
26
|
-
"type": "object",
|
|
27
|
-
"properties": {
|
|
28
|
-
"timer": { "type": "number" },
|
|
29
|
-
"watchdog": { "type": "number" },
|
|
30
|
-
"reconnect": { "type": "number" },
|
|
31
|
-
"after_connect": { "type": "number" }
|
|
32
|
-
},
|
|
33
|
-
"additionalProperties": true
|
|
34
|
-
},
|
|
35
|
-
"timeouts": {
|
|
36
|
-
"type": "object",
|
|
37
|
-
"properties": {
|
|
38
|
-
"connect": { "type": "number" },
|
|
39
|
-
"watchdog": { "type": "number" },
|
|
40
|
-
"acknowledgement": { "type": "number" }
|
|
41
|
-
},
|
|
42
|
-
"additionalProperties": true
|
|
22
|
+
"items": { "$ref": "config_common.json#/$defs/endpoint" }
|
|
43
23
|
},
|
|
24
|
+
"intervals": { "$ref": "config_common.json#/$defs/intervals" },
|
|
25
|
+
"timeouts": { "$ref": "config_common.json#/$defs/timeouts" },
|
|
44
26
|
"send_after_connect": { "type": "boolean" },
|
|
27
|
+
"receive_alarms": { "type": "boolean" },
|
|
28
|
+
"message_buffer": { "$ref": "config_common.json#/$defs/message_buffer" },
|
|
45
29
|
"skip_validation": { "type": "array", "items": { "type": "string" } },
|
|
46
|
-
"security_codes": { "
|
|
30
|
+
"security_codes": { "$ref": "config_common.json#/$defs/security_codes" },
|
|
31
|
+
"startup_sequence": { "type": "string" },
|
|
32
|
+
"signal_plans": { "$ref": "config_common.json#/$defs/signal_plans" },
|
|
33
|
+
"inputs": { "$ref": "config_common.json#/$defs/inputs" },
|
|
34
|
+
"live_output": { "type": ["string", "null"] }
|
|
47
35
|
},
|
|
48
|
-
"additionalProperties":
|
|
36
|
+
"additionalProperties": false
|
|
49
37
|
}
|
|
@@ -21,6 +21,7 @@ module RSMP
|
|
|
21
21
|
'acknowledgement' => 2
|
|
22
22
|
},
|
|
23
23
|
'send_after_connect' => true,
|
|
24
|
+
'message_buffer' => default_message_buffer,
|
|
24
25
|
'components' => {
|
|
25
26
|
'main' => {
|
|
26
27
|
'C1' => {}
|
|
@@ -35,6 +36,13 @@ module RSMP
|
|
|
35
36
|
|
|
36
37
|
private
|
|
37
38
|
|
|
39
|
+
def default_message_buffer
|
|
40
|
+
{
|
|
41
|
+
'max_messages' => 10_000,
|
|
42
|
+
'statuses' => true
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
38
46
|
def apply_defaults(options)
|
|
39
47
|
defaults = defaults()
|
|
40
48
|
defaults['components']['main'] = options['components']['main'] if options.dig('components', 'main')
|
|
@@ -11,8 +11,13 @@ module RSMP
|
|
|
11
11
|
distribute_error error.exception("#{str} #{message.json}")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def send_message(message, reason = nil, validate: true, force: false)
|
|
15
|
-
|
|
14
|
+
def send_message(message, reason = nil, validate: true, force: false, buffer: true)
|
|
15
|
+
unless force || connected?
|
|
16
|
+
error = NotReady.new
|
|
17
|
+
raise error unless buffer
|
|
18
|
+
|
|
19
|
+
return buffer_message(message, error)
|
|
20
|
+
end
|
|
16
21
|
raise IOError unless @protocol
|
|
17
22
|
|
|
18
23
|
message.direction = :out
|
|
@@ -22,15 +27,18 @@ module RSMP
|
|
|
22
27
|
expect_acknowledgement message
|
|
23
28
|
distribute message
|
|
24
29
|
log_send message, reason
|
|
25
|
-
rescue IOError
|
|
26
|
-
|
|
30
|
+
rescue NotReady, IOError => e
|
|
31
|
+
raise e unless buffer
|
|
32
|
+
|
|
33
|
+
buffer_message message, e
|
|
27
34
|
rescue SchemaError, RSMP::Schema::Error => e
|
|
28
35
|
handle_send_schema_error(message, e)
|
|
29
36
|
end
|
|
30
37
|
|
|
31
|
-
def buffer_message(message)
|
|
32
|
-
#
|
|
33
|
-
|
|
38
|
+
def buffer_message(message, error = nil)
|
|
39
|
+
str = "Cannot send #{message.type} because the connection is closed."
|
|
40
|
+
log str, message: message, level: :error
|
|
41
|
+
raise error if error
|
|
34
42
|
end
|
|
35
43
|
|
|
36
44
|
def log_send(message, reason = nil)
|
|
@@ -125,13 +125,14 @@ 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'
|
|
129
128
|
component ||= main.c_id
|
|
130
129
|
|
|
131
130
|
status_list.each do |item|
|
|
132
131
|
remove_subscription_item(component, item['sCI'], item['n'])
|
|
133
132
|
end
|
|
134
133
|
|
|
134
|
+
return unless ready? # if the connection is don't we skip sending
|
|
135
|
+
|
|
135
136
|
message = RSMP::StatusUnsubscribe.new({
|
|
136
137
|
'cId' => component,
|
|
137
138
|
'sS' => status_list
|
|
@@ -13,7 +13,7 @@ module RSMP
|
|
|
13
13
|
def send_aggregated_status(component, m_id: nil)
|
|
14
14
|
m_id ||= RSMP::Message.make_m_id
|
|
15
15
|
|
|
16
|
-
se = if Proxy.version_meets_requirement?(core_version, '<=3.1.2')
|
|
16
|
+
se = if core_version && Proxy.version_meets_requirement?(core_version, '<=3.1.2')
|
|
17
17
|
component.aggregated_status_bools.map { |bool| bool ? 'true' : 'false' }
|
|
18
18
|
else
|
|
19
19
|
component.aggregated_status_bools
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
class SupervisorProxy < Proxy
|
|
3
|
+
module Modules
|
|
4
|
+
# In-memory outgoing communication buffer for site-originated messages.
|
|
5
|
+
module MessageBuffer
|
|
6
|
+
def message_buffer_settings
|
|
7
|
+
@site_settings['message_buffer'] || {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def message_buffer_max_messages
|
|
11
|
+
message_buffer_settings['max_messages'] || 10_000
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def status_buffer_selectors
|
|
15
|
+
return message_buffer_settings['statuses'] if message_buffer_settings.key? 'statuses'
|
|
16
|
+
|
|
17
|
+
[]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def status_buffer_selector?(component_id, status)
|
|
21
|
+
selectors = status_buffer_selectors
|
|
22
|
+
return true if selectors == true
|
|
23
|
+
return false unless selectors.is_a?(Array)
|
|
24
|
+
|
|
25
|
+
selectors.any? { |selector| status_buffer_selector_matches?(selector, component_id, status) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def status_buffer_selector_matches?(selector, component_id, status)
|
|
29
|
+
selector = selector.transform_keys(&:to_s)
|
|
30
|
+
component_matches = !selector['cId'] || selector['cId'] == component_id
|
|
31
|
+
code_matches = !selector['sCI'] || selector['sCI'] == status['sCI']
|
|
32
|
+
name_matches = !selector['n'] || selector['n'] == status['n']
|
|
33
|
+
component_matches && code_matches && name_matches
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def clone_message(message, attributes = message.attributes)
|
|
37
|
+
message.class.new(JSON.parse(JSON.generate(attributes)))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def site_originated_buffer_candidate?(message)
|
|
41
|
+
message.is_a?(RSMP::AggregatedStatus) ||
|
|
42
|
+
message.is_a?(RSMP::AlarmIssue) ||
|
|
43
|
+
message.is_a?(RSMP::AlarmSuspended) ||
|
|
44
|
+
message.is_a?(RSMP::AlarmResumed) ||
|
|
45
|
+
message.is_a?(RSMP::AlarmAcknowledged) ||
|
|
46
|
+
message.is_a?(RSMP::StatusUpdate)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def prepare_status_update_for_buffer(message, core_version:, for_send:)
|
|
50
|
+
attributes = JSON.parse(JSON.generate(message.attributes))
|
|
51
|
+
component_id = attributes['cId']
|
|
52
|
+
attributes['sS'] = attributes['sS'].select { |status| status_buffer_selector?(component_id, status) }
|
|
53
|
+
return if attributes['sS'].empty?
|
|
54
|
+
|
|
55
|
+
if for_send && core_version && version_meets_requirement?(core_version, '>=3.2.0')
|
|
56
|
+
attributes['sS'].each { |status| status['q'] = 'old' }
|
|
57
|
+
end
|
|
58
|
+
clone_message message, attributes
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def normalize_aggregated_status_buffer(states, core_version)
|
|
62
|
+
if version_meets_requirement?(core_version, '<=3.1.2')
|
|
63
|
+
states.map { |item| item == true || item.to_s == 'true' ? 'true' : 'false' }
|
|
64
|
+
else
|
|
65
|
+
states.map { |item| item == true || item.to_s == 'true' }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def prepare_aggregated_status_for_buffer(message, core_version:, for_send:)
|
|
70
|
+
attributes = JSON.parse(JSON.generate(message.attributes))
|
|
71
|
+
if for_send && core_version && attributes['se']
|
|
72
|
+
attributes['se'] = normalize_aggregated_status_buffer(attributes['se'], core_version)
|
|
73
|
+
end
|
|
74
|
+
clone_message message, attributes
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def prepare_message_for_buffer(message, core_version: @core_version, for_send: false)
|
|
78
|
+
return unless site_originated_buffer_candidate? message
|
|
79
|
+
return false if message.is_a?(RSMP::StatusUpdate) && status_buffer_selectors == false
|
|
80
|
+
return false if message.is_a?(RSMP::Alarm) && !receive_alarms?
|
|
81
|
+
|
|
82
|
+
if message.is_a? RSMP::AggregatedStatus
|
|
83
|
+
prepare_aggregated_status_for_buffer message, core_version: core_version, for_send: for_send
|
|
84
|
+
elsif message.is_a? RSMP::StatusUpdate
|
|
85
|
+
prepare_status_update_for_buffer message, core_version: core_version, for_send: for_send
|
|
86
|
+
else
|
|
87
|
+
clone_message message
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def buffer_message(message, error = nil)
|
|
92
|
+
prepared = prepare_message_for_buffer message, core_version: @core_version
|
|
93
|
+
if prepared
|
|
94
|
+
enqueue_buffered_message prepared
|
|
95
|
+
elsif site_originated_buffer_candidate? message
|
|
96
|
+
log "Discarded #{message.type}; it is not configured for buffering", message: message, level: :warning
|
|
97
|
+
else
|
|
98
|
+
super
|
|
99
|
+
end
|
|
100
|
+
rescue NotReady, IOError
|
|
101
|
+
raise error if error
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def enqueue_buffered_message(message)
|
|
105
|
+
while @message_buffer.size >= message_buffer_max_messages
|
|
106
|
+
dropped = @message_buffer.shift
|
|
107
|
+
log "Dropped buffered #{dropped.type}; message buffer is full", message: dropped, level: :warning
|
|
108
|
+
end
|
|
109
|
+
@message_buffer << message
|
|
110
|
+
log "Buffered #{message.type}; #{message_buffer.size} message(s) queued", message: message, level: :warning
|
|
111
|
+
message
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def flush_message_buffer
|
|
115
|
+
return if @message_buffer.empty?
|
|
116
|
+
|
|
117
|
+
queued = @message_buffer
|
|
118
|
+
@message_buffer = []
|
|
119
|
+
log "Sending #{queued.size} buffered message(s)", level: :info
|
|
120
|
+
queued.each_with_index do |message, index|
|
|
121
|
+
break unless flush_buffered_message(message, queued, index)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def flush_buffered_message(message, queued, index)
|
|
126
|
+
prepared = prepare_message_for_buffer message, core_version: @core_version, for_send: true
|
|
127
|
+
return true unless prepared
|
|
128
|
+
|
|
129
|
+
send_message prepared, 'from buffer', buffer: false
|
|
130
|
+
true
|
|
131
|
+
rescue NotReady, IOError
|
|
132
|
+
@message_buffer = queued[index..] + @message_buffer
|
|
133
|
+
log "Stopped sending buffered messages; #{message_buffer.size} message(s) remain queued", level: :warning
|
|
134
|
+
false
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -101,6 +101,21 @@ module RSMP
|
|
|
101
101
|
acknowledge message
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
def prune_unbuffered_status_subscriptions
|
|
105
|
+
@status_subscriptions.each_key.to_a.each do |component_id|
|
|
106
|
+
by_code = @status_subscriptions[component_id]
|
|
107
|
+
by_code.each_key.to_a.each do |code|
|
|
108
|
+
by_name = by_code[code]
|
|
109
|
+
by_name.delete_if do |name, _subscription|
|
|
110
|
+
status = { 'sCI' => code, 'n' => name }
|
|
111
|
+
!status_buffer_selector?(component_id, status)
|
|
112
|
+
end
|
|
113
|
+
by_code.delete(code) if by_name.empty?
|
|
114
|
+
end
|
|
115
|
+
@status_subscriptions.delete(component_id) if by_code.empty?
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
104
119
|
def fetch_last_sent_status(component, code, name)
|
|
105
120
|
@last_status_sent&.dig component, code, name
|
|
106
121
|
end
|
|
@@ -7,8 +7,9 @@ module RSMP
|
|
|
7
7
|
include Modules::Commands
|
|
8
8
|
include Modules::Alarms
|
|
9
9
|
include Modules::AggregatedStatus
|
|
10
|
+
include Modules::MessageBuffer
|
|
10
11
|
|
|
11
|
-
attr_reader :supervisor_id, :site
|
|
12
|
+
attr_reader :supervisor_id, :site, :message_buffer
|
|
12
13
|
|
|
13
14
|
def initialize(options)
|
|
14
15
|
super(options.merge(node: options[:site]))
|
|
@@ -21,6 +22,7 @@ module RSMP
|
|
|
21
22
|
@accepted_sxls = @sxls.dup
|
|
22
23
|
@rejected_sxls = []
|
|
23
24
|
@synthetic_id = Supervisor.build_id_from_ip_port @ip, @port
|
|
25
|
+
@message_buffer = []
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
# handle communication
|
|
@@ -51,6 +53,11 @@ module RSMP
|
|
|
51
53
|
send_version_request @site_settings['site_id'], core_versions
|
|
52
54
|
end
|
|
53
55
|
|
|
56
|
+
def close
|
|
57
|
+
prune_unbuffered_status_subscriptions
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
54
61
|
# connect to the supervisor and initiate handshake supervisor
|
|
55
62
|
def connect
|
|
56
63
|
log "Connecting to supervisor at #{@ip}:#{@port}", level: :info
|
|
@@ -98,6 +105,7 @@ module RSMP
|
|
|
98
105
|
send_all_aggregated_status
|
|
99
106
|
send_active_alarms if receive_alarms?
|
|
100
107
|
end
|
|
108
|
+
flush_message_buffer
|
|
101
109
|
super
|
|
102
110
|
end
|
|
103
111
|
|
|
@@ -170,11 +178,6 @@ module RSMP
|
|
|
170
178
|
send_watchdog
|
|
171
179
|
end
|
|
172
180
|
|
|
173
|
-
def timer(now)
|
|
174
|
-
super
|
|
175
|
-
status_update_timer now if ready?
|
|
176
|
-
end
|
|
177
|
-
|
|
178
181
|
def process_version(message)
|
|
179
182
|
return extraneous_version message if @version_determined
|
|
180
183
|
|
data/lib/rsmp/version.rb
CHANGED
data/lib/rsmp.rb
CHANGED
|
@@ -61,6 +61,7 @@ require_relative 'rsmp/options/options'
|
|
|
61
61
|
require_relative 'rsmp/options/site_options'
|
|
62
62
|
require_relative 'rsmp/options/supervisor_options'
|
|
63
63
|
require_relative 'rsmp/options/traffic_controller_site_options'
|
|
64
|
+
require_relative 'rsmp/config'
|
|
64
65
|
require_relative 'rsmp/proxy/modules/state'
|
|
65
66
|
require_relative 'rsmp/proxy/modules/watchdogs'
|
|
66
67
|
require_relative 'rsmp/proxy/modules/acknowledgements'
|
|
@@ -77,6 +78,7 @@ require_relative 'rsmp/proxy/supervisor/modules/status'
|
|
|
77
78
|
require_relative 'rsmp/proxy/supervisor/modules/commands'
|
|
78
79
|
require_relative 'rsmp/proxy/supervisor/modules/alarms'
|
|
79
80
|
require_relative 'rsmp/proxy/supervisor/modules/aggregated_status'
|
|
81
|
+
require_relative 'rsmp/proxy/supervisor/modules/message_buffer'
|
|
80
82
|
require_relative 'rsmp/proxy/supervisor/supervisor_proxy'
|
|
81
83
|
require_relative 'rsmp/proxy/site/modules/status'
|
|
82
84
|
require_relative 'rsmp/proxy/site/modules/aggregated_status'
|
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.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emil Tin
|
|
@@ -181,6 +181,7 @@ files:
|
|
|
181
181
|
- lib/rsmp/component/component_base.rb
|
|
182
182
|
- lib/rsmp/component/component_proxy.rb
|
|
183
183
|
- lib/rsmp/component/components.rb
|
|
184
|
+
- lib/rsmp/config.rb
|
|
184
185
|
- lib/rsmp/convert/export/json_schema.rb
|
|
185
186
|
- lib/rsmp/convert/export/json_schema/items.rb
|
|
186
187
|
- lib/rsmp/convert/export/json_schema/outputs.rb
|
|
@@ -204,6 +205,8 @@ files:
|
|
|
204
205
|
- lib/rsmp/node/supervisor/supervisor.rb
|
|
205
206
|
- lib/rsmp/node/task.rb
|
|
206
207
|
- lib/rsmp/options/options.rb
|
|
208
|
+
- lib/rsmp/options/schemas/config_common.json
|
|
209
|
+
- lib/rsmp/options/schemas/log.json
|
|
207
210
|
- lib/rsmp/options/schemas/site.json
|
|
208
211
|
- lib/rsmp/options/schemas/supervisor.json
|
|
209
212
|
- lib/rsmp/options/schemas/supervisor_site.json
|
|
@@ -228,6 +231,7 @@ files:
|
|
|
228
231
|
- lib/rsmp/proxy/supervisor/modules/aggregated_status.rb
|
|
229
232
|
- lib/rsmp/proxy/supervisor/modules/alarms.rb
|
|
230
233
|
- lib/rsmp/proxy/supervisor/modules/commands.rb
|
|
234
|
+
- lib/rsmp/proxy/supervisor/modules/message_buffer.rb
|
|
231
235
|
- lib/rsmp/proxy/supervisor/modules/status.rb
|
|
232
236
|
- lib/rsmp/proxy/supervisor/supervisor_proxy.rb
|
|
233
237
|
- lib/rsmp/schema.rb
|