rsmp 0.48.4 → 0.51.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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/Gemfile.lock +19 -20
  4. data/README.md +27 -3
  5. data/Rakefile +3 -1
  6. data/documentation/cli.md +79 -4
  7. data/documentation/collecting_message.md +90 -170
  8. data/documentation/configuration.md +8 -0
  9. data/documentation/message_distribution.md +17 -3
  10. data/documentation/tasks.md +65 -117
  11. data/lib/rsmp/cli.rb +93 -5
  12. data/lib/rsmp/collect/collector.rb +117 -134
  13. data/lib/rsmp/collect/distributor.rb +16 -3
  14. data/lib/rsmp/collect/queue.rb +14 -6
  15. data/lib/rsmp/collect/receiver.rb +1 -1
  16. data/lib/rsmp/collect/state_collector.rb +11 -2
  17. data/lib/rsmp/collection.rb +12 -0
  18. data/lib/rsmp/completion.rb +49 -0
  19. data/lib/rsmp/component/component_base.rb +3 -3
  20. data/lib/rsmp/convert/export/json_schema/index.rb +8 -4
  21. data/lib/rsmp/convert/import/yaml.rb +12 -8
  22. data/lib/rsmp/delivery.rb +18 -0
  23. data/lib/rsmp/event.rb +19 -0
  24. data/lib/rsmp/event_source.rb +36 -0
  25. data/lib/rsmp/exchange.rb +10 -0
  26. data/lib/rsmp/failure.rb +23 -0
  27. data/lib/rsmp/helpers/error.rb +14 -23
  28. data/lib/rsmp/message.rb +10 -9
  29. data/lib/rsmp/node/node.rb +3 -21
  30. data/lib/rsmp/node/site/connections.rb +4 -9
  31. data/lib/rsmp/node/site/site.rb +33 -17
  32. data/lib/rsmp/node/supervisor/modules/configuration.rb +24 -11
  33. data/lib/rsmp/node/supervisor/modules/connection.rb +19 -5
  34. data/lib/rsmp/node/supervisor/modules/sites.rb +16 -5
  35. data/lib/rsmp/node/supervisor/supervisor.rb +13 -14
  36. data/lib/rsmp/node/task.rb +59 -53
  37. data/lib/rsmp/proxy/modules/acknowledgements.rb +22 -9
  38. data/lib/rsmp/proxy/modules/lifecycle.rb +134 -0
  39. data/lib/rsmp/proxy/modules/receive.rb +109 -66
  40. data/lib/rsmp/proxy/modules/send.rb +109 -51
  41. data/lib/rsmp/proxy/modules/state.rb +15 -5
  42. data/lib/rsmp/proxy/modules/tasks.rb +96 -70
  43. data/lib/rsmp/proxy/modules/versions.rb +69 -19
  44. data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
  45. data/lib/rsmp/proxy/proxy.rb +7 -69
  46. data/lib/rsmp/proxy/site/connection.rb +27 -22
  47. data/lib/rsmp/proxy/site/modules/aggregated_status.rb +18 -8
  48. data/lib/rsmp/proxy/site/modules/alarms.rb +45 -38
  49. data/lib/rsmp/proxy/site/modules/commands.rb +18 -8
  50. data/lib/rsmp/proxy/site/modules/status.rb +44 -19
  51. data/lib/rsmp/proxy/site/site_proxy.rb +12 -10
  52. data/lib/rsmp/proxy/site/sxl_selection.rb +7 -2
  53. data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
  54. data/lib/rsmp/proxy/supervisor/modules/alarms.rb +1 -1
  55. data/lib/rsmp/proxy/supervisor/modules/commands.rb +1 -1
  56. data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +8 -9
  57. data/lib/rsmp/proxy/supervisor/modules/status.rb +1 -1
  58. data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +1 -1
  59. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +36 -31
  60. data/lib/rsmp/result.rb +95 -0
  61. data/lib/rsmp/schema/validation.rb +5 -3
  62. data/lib/rsmp/schema/version.rb +27 -0
  63. data/lib/rsmp/schema.rb +1 -0
  64. data/lib/rsmp/sxl/interface.rb +8 -1
  65. data/lib/rsmp/sxl/processing/catalogue.rb +61 -0
  66. data/lib/rsmp/sxl/processing/conflict_checker.rb +30 -0
  67. data/lib/rsmp/sxl/processing/dependency_graph.rb +37 -0
  68. data/lib/rsmp/sxl/processing/document.rb +158 -0
  69. data/lib/rsmp/sxl/processing/error.rb +8 -0
  70. data/lib/rsmp/sxl/processing/manifest.rb +144 -0
  71. data/lib/rsmp/sxl/processing/natural_sort.rb +22 -0
  72. data/lib/rsmp/sxl/processing/resolver.rb +94 -0
  73. data/lib/rsmp/sxl/processing/version_requirement.rb +86 -0
  74. data/lib/rsmp/sxl/processing.rb +18 -0
  75. data/lib/rsmp/sxl/supervisor_interface.rb +8 -1
  76. data/lib/rsmp/termination.rb +10 -0
  77. data/lib/rsmp/tlc/modules/plans.rb +0 -2
  78. data/lib/rsmp/tlc/modules/system.rb +2 -2
  79. data/lib/rsmp/tlc/proxy/control.rb +50 -16
  80. data/lib/rsmp/tlc/proxy/detectors.rb +15 -4
  81. data/lib/rsmp/tlc/proxy/io.rb +46 -31
  82. data/lib/rsmp/tlc/proxy/plans.rb +36 -19
  83. data/lib/rsmp/tlc/proxy/plans_bang.rb +44 -0
  84. data/lib/rsmp/tlc/proxy/status.rb +47 -23
  85. data/lib/rsmp/tlc/proxy/system.rb +16 -4
  86. data/lib/rsmp/tlc/supervisor_interface.rb +7 -1
  87. data/lib/rsmp/tlc/traffic_controller_site.rb +6 -9
  88. data/lib/rsmp/validation.rb +22 -0
  89. data/lib/rsmp/version.rb +1 -1
  90. data/lib/rsmp.rb +19 -4
  91. data/rsmp.gemspec +1 -1
  92. metadata +26 -4
  93. data/lib/rsmp/collect/collector/status.rb +0 -34
@@ -6,7 +6,9 @@ module RSMP
6
6
  module IO
7
7
  # M0006 - Set a single input to a given status.
8
8
  def set_input(input:, status:, within:)
9
- validate_ready 'set input'
9
+ readiness = validate_ready 'set input'
10
+ return readiness if readiness.failure?
11
+
10
12
  raise 'TLC main component not found' unless main
11
13
 
12
14
  security_code = security_code_for(2)
@@ -27,12 +29,18 @@ module RSMP
27
29
  'n' => 'input',
28
30
  'v' => command_value('M0006', 'input', input)
29
31
  }]
30
- send_command_and_collect(command_list, within: within).ok!
32
+ send_command_and_collect(command_list, within: within)
33
+ end
34
+
35
+ def set_input!(...)
36
+ set_input(...).value!
31
37
  end
32
38
 
33
39
  # M0013 - Set all inputs via a bit-pattern string.
34
40
  def set_inputs(status, within:)
35
- validate_ready 'set inputs'
41
+ readiness = validate_ready 'set inputs'
42
+ return readiness if readiness.failure?
43
+
36
44
  raise 'TLC main component not found' unless main
37
45
 
38
46
  security_code = security_code_for(2)
@@ -48,53 +56,60 @@ module RSMP
48
56
  'n' => 'securityCode',
49
57
  'v' => security_code.to_s
50
58
  }]
51
- send_command_and_collect(command_list, within: within).ok!
59
+ send_command_and_collect(command_list, within: within)
60
+ end
61
+
62
+ def set_inputs!(...)
63
+ set_inputs(...).value!
52
64
  end
53
65
 
54
66
  # M0019 - Force an input to a given value.
55
67
  def force_input(input:, status:, value:, within:)
56
- validate_ready 'force input'
68
+ readiness = validate_ready 'force input'
69
+ return readiness if readiness.failure?
70
+
57
71
  raise 'TLC main component not found' unless main
58
72
 
59
73
  command_list = force_input_command_list(input, status, value)
60
74
  confirm_status = force_input_confirm_status(input, status, value)
61
- send_command_and_collect(command_list, within: within).ok!
62
- wait_for_status "force input #{input}", confirm_status, timeout: within
75
+ send_command_and_collect(command_list, within: within).and_then do |exchange|
76
+ wait_for_status("force input #{input}", confirm_status, timeout: within).map { exchange }
77
+ end
78
+ end
79
+
80
+ def force_input!(...)
81
+ force_input(...).value!
63
82
  end
64
83
 
65
84
  # M0020 - Force an output to a given value.
66
85
  def force_output(output:, status:, value:, within:)
67
- validate_ready 'force output'
86
+ readiness = validate_ready 'force output'
87
+ return readiness if readiness.failure?
88
+
68
89
  raise 'TLC main component not found' unless main
69
90
 
70
- security_code = security_code_for(2)
91
+ send_command_and_collect(force_output_command_list(output, status, value), within: within)
92
+ end
71
93
 
72
- command_list = [{
73
- 'cCI' => 'M0020',
74
- 'cO' => 'setOutput',
75
- 'n' => 'status',
76
- 'v' => command_value('M0020', 'status', status)
77
- }, {
78
- 'cCI' => 'M0020',
79
- 'cO' => 'setOutput',
80
- 'n' => 'securityCode',
81
- 'v' => security_code.to_s
82
- }, {
83
- 'cCI' => 'M0020',
84
- 'cO' => 'setOutput',
85
- 'n' => 'output',
86
- 'v' => command_value('M0020', 'output', output)
87
- }, {
88
- 'cCI' => 'M0020',
89
- 'cO' => 'setOutput',
90
- 'n' => 'outputValue',
91
- 'v' => command_value('M0020', 'outputValue', value)
92
- }]
93
- send_command_and_collect(command_list, within: within).ok!
94
+ def force_output!(...)
95
+ force_output(...).value!
94
96
  end
95
97
 
96
98
  private
97
99
 
100
+ def force_output_command_list(output, status, value)
101
+ security_code = security_code_for(2)
102
+ values = { 'status' => status, 'output' => output, 'outputValue' => value }
103
+ values.map do |name, item|
104
+ { 'cCI' => 'M0020', 'cO' => 'setOutput', 'n' => name, 'v' => command_value('M0020', name, item) }
105
+ end.insert(1, {
106
+ 'cCI' => 'M0020',
107
+ 'cO' => 'setOutput',
108
+ 'n' => 'securityCode',
109
+ 'v' => security_code.to_s
110
+ })
111
+ end
112
+
98
113
  def force_input_command_list(input, status, value)
99
114
  security_code = security_code_for(2)
100
115
  [
@@ -6,7 +6,9 @@ module RSMP
6
6
  module Plans
7
7
  # M0014 - Set dynamic bands for a signal plan.
8
8
  def set_dynamic_bands(plan:, status:, within:)
9
- validate_ready 'set dynamic bands'
9
+ readiness = validate_ready 'set dynamic bands'
10
+ return readiness if readiness.failure?
11
+
10
12
  raise 'TLC main component not found' unless main
11
13
 
12
14
  security_code = security_code_for(2)
@@ -27,12 +29,14 @@ module RSMP
27
29
  'n' => 'plan',
28
30
  'v' => command_value('M0014', 'plan', plan)
29
31
  }]
30
- send_command_and_collect(command_list, within: within).ok!
32
+ send_command_and_collect(command_list, within: within)
31
33
  end
32
34
 
33
35
  # M0023 - Set timeout for dynamic bands.
34
36
  def set_dynamic_bands_timeout(status, within:)
35
- validate_ready 'set dynamic bands timeout'
37
+ readiness = validate_ready 'set dynamic bands timeout'
38
+ return readiness if readiness.failure?
39
+
36
40
  raise 'TLC main component not found' unless main
37
41
 
38
42
  security_code = security_code_for(2)
@@ -48,12 +52,14 @@ module RSMP
48
52
  'n' => 'securityCode',
49
53
  'v' => security_code.to_s
50
54
  }]
51
- send_command_and_collect(command_list, within: within).ok!
55
+ send_command_and_collect(command_list, within: within)
52
56
  end
53
57
 
54
58
  # M0015 - Set offset for a signal plan.
55
59
  def set_offset(plan:, offset:, within:)
56
- validate_ready 'set offset'
60
+ readiness = validate_ready 'set offset'
61
+ return readiness if readiness.failure?
62
+
57
63
  raise 'TLC main component not found' unless main
58
64
 
59
65
  security_code = security_code_for(2)
@@ -74,12 +80,14 @@ module RSMP
74
80
  'n' => 'plan',
75
81
  'v' => command_value('M0015', 'plan', plan)
76
82
  }]
77
- send_command_and_collect(command_list, within: within).ok!
83
+ send_command_and_collect(command_list, within: within)
78
84
  end
79
85
 
80
86
  # Set the timeplan (signal plan) on the remote TLC.
81
87
  def set_timeplan(plan_nr, within:)
82
- validate_ready 'set timeplan'
88
+ readiness = validate_ready 'set timeplan'
89
+ return readiness if readiness.failure?
90
+
83
91
  raise 'TLC main component not found' unless main
84
92
 
85
93
  security_code = security_code_for(2)
@@ -101,13 +109,16 @@ module RSMP
101
109
  'v' => command_value('M0002', 'timeplan', plan_nr)
102
110
  }]
103
111
  confirm_status = [{ 'sCI' => 'S0014', 'n' => 'status', 's' => integer_value(plan_nr) }]
104
- send_command_and_collect(command_list, within: within).ok!
105
- wait_for_status("timeplan #{plan_nr}", confirm_status, timeout: within)
112
+ send_command_and_collect(command_list, within: within).and_then do |exchange|
113
+ wait_for_status("timeplan #{plan_nr}", confirm_status, timeout: within).map { exchange }
114
+ end
106
115
  end
107
116
 
108
117
  # M0016 - Set week table (mapping week days to traffic situations).
109
118
  def set_week_table(status, within:)
110
- validate_ready 'set week table'
119
+ readiness = validate_ready 'set week table'
120
+ return readiness if readiness.failure?
121
+
111
122
  raise 'TLC main component not found' unless main
112
123
 
113
124
  security_code = security_code_for(2)
@@ -123,12 +134,14 @@ module RSMP
123
134
  'n' => 'securityCode',
124
135
  'v' => security_code.to_s
125
136
  }]
126
- send_command_and_collect(command_list, within:).ok!
137
+ send_command_and_collect(command_list, within:)
127
138
  end
128
139
 
129
140
  # M0017 - Set day table (mapping time periods to signal plans).
130
141
  def set_day_table(status, within:)
131
- validate_ready 'set day table'
142
+ readiness = validate_ready 'set day table'
143
+ return readiness if readiness.failure?
144
+
132
145
  raise 'TLC main component not found' unless main
133
146
 
134
147
  security_code = security_code_for(2)
@@ -144,12 +157,14 @@ module RSMP
144
157
  'n' => 'securityCode',
145
158
  'v' => security_code.to_s
146
159
  }]
147
- send_command_and_collect(command_list, within:).ok!
160
+ send_command_and_collect(command_list, within:)
148
161
  end
149
162
 
150
163
  # M0018 - Set cycle time for a signal plan.
151
164
  def set_cycle_time(plan:, cycle_time:, within:)
152
- validate_ready 'set cycle time'
165
+ readiness = validate_ready 'set cycle time'
166
+ return readiness if readiness.failure?
167
+
153
168
  raise 'TLC main component not found' unless main
154
169
 
155
170
  security_code = security_code_for(2)
@@ -170,12 +185,13 @@ module RSMP
170
185
  'n' => 'plan',
171
186
  'v' => command_value('M0018', 'plan', plan)
172
187
  }]
173
- send_command_and_collect(command_list, within:).ok!
188
+ send_command_and_collect(command_list, within:)
174
189
  end
175
190
 
176
191
  # M0010 - Order signal start for a signal group component.
177
192
  def order_signal_start(component_id, within:)
178
- validate_ready 'order signal start'
193
+ readiness = validate_ready 'order signal start'
194
+ return readiness if readiness.failure?
179
195
 
180
196
  security_code = security_code_for(2)
181
197
 
@@ -190,12 +206,13 @@ module RSMP
190
206
  'n' => 'securityCode',
191
207
  'v' => security_code.to_s
192
208
  }]
193
- send_command_and_collect(command_list, component: component_id, within:).ok!
209
+ send_command_and_collect(command_list, component: component_id, within:)
194
210
  end
195
211
 
196
212
  # M0011 - Order signal stop for a signal group component.
197
213
  def order_signal_stop(component_id, within:)
198
- validate_ready 'order signal stop'
214
+ readiness = validate_ready 'order signal stop'
215
+ return readiness if readiness.failure?
199
216
 
200
217
  security_code = security_code_for(2)
201
218
 
@@ -210,7 +227,7 @@ module RSMP
210
227
  'n' => 'securityCode',
211
228
  'v' => security_code.to_s
212
229
  }]
213
- send_command_and_collect(command_list, component: component_id, within:).ok!
230
+ send_command_and_collect(command_list, component: component_id, within:)
214
231
  end
215
232
  end
216
233
  end
@@ -0,0 +1,44 @@
1
+ module RSMP
2
+ module TLC
3
+ module Proxy
4
+ # Explicit exception-raising variants of finite plan operations.
5
+ module PlansBang
6
+ def set_dynamic_bands!(...)
7
+ set_dynamic_bands(...).value!
8
+ end
9
+
10
+ def set_dynamic_bands_timeout!(...)
11
+ set_dynamic_bands_timeout(...).value!
12
+ end
13
+
14
+ def set_offset!(...)
15
+ set_offset(...).value!
16
+ end
17
+
18
+ def set_timeplan!(...)
19
+ set_timeplan(...).value!
20
+ end
21
+
22
+ def set_week_table!(...)
23
+ set_week_table(...).value!
24
+ end
25
+
26
+ def set_day_table!(...)
27
+ set_day_table(...).value!
28
+ end
29
+
30
+ def set_cycle_time!(...)
31
+ set_cycle_time(...).value!
32
+ end
33
+
34
+ def order_signal_start!(...)
35
+ order_signal_start(...).value!
36
+ end
37
+
38
+ def order_signal_stop!(...)
39
+ order_signal_stop(...).value!
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -4,25 +4,28 @@ module RSMP
4
4
  # Status reading and waiting methods for a remote TLC.
5
5
  # Covers status subscriptions, group waits, and plan/band reading.
6
6
  module Status
7
- # Fetch the current signal plan from the remote TLC.
7
+ # Fetch the current signal plan from the remote TLC. Returns Result<Hash>.
8
8
  def fetch_signal_plan(options: {})
9
- validate_ready 'fetch signal plan'
10
9
  timeout = options[:timeout] || @timeouts['status_response']
11
- collector = request_status_and_collect({ S0014: %i[status source] }, within: timeout)
12
- collector.ok!
13
- collector.messages.last.attributes['sS'].to_h do |item|
14
- [item['n'], item['s']]
10
+ request_status_and_collect({ S0014: %i[status source] }, within: timeout).map do |exchange|
11
+ exchange.collection.messages.last.attributes['sS'].to_h do |item|
12
+ [item['n'], item['s']]
13
+ end
15
14
  end
16
15
  end
17
16
 
17
+ def fetch_signal_plan!(...)
18
+ fetch_signal_plan(...).value!
19
+ end
20
+
18
21
  # Subscribe to one or more statuses and wait until they match the expected values.
19
- # Raises RSMP::TimeoutError if the values don't match within the timeout.
20
- #
21
22
  # status_list items: { 'sCI' => ..., 'n' => ..., 's' => <expected value or Regexp> }
22
23
  # component_id defaults to the main TLC component.
23
24
  # timeout defaults to @timeouts['command'].
24
25
  def wait_for_status(description, status_list, update_rate: 0, timeout: nil, component_id: nil)
25
- validate_ready 'wait for status'
26
+ readiness = validate_ready 'wait for status'
27
+ return readiness if readiness.failure?
28
+
26
29
  component_id ||= main.c_id
27
30
  timeout ||= @timeouts['command']
28
31
 
@@ -35,13 +38,17 @@ module RSMP
35
38
  log "Wait for #{description}", level: :debug
36
39
 
37
40
  begin
38
- subscribe_to_status_and_collect(subscribe_list, component: component_id, within: timeout).ok!
41
+ subscribe_to_status_and_collect(subscribe_list, component: component_id, within: timeout)
39
42
  ensure
40
43
  unsubscribe_list = status_list.map { |item| item.slice('sCI', 'n') }
41
44
  unsubscribe_to_status unsubscribe_list, component: component_id
42
45
  end
43
46
  end
44
47
 
48
+ def wait_for_status!(...)
49
+ wait_for_status(...).value!
50
+ end
51
+
45
52
  # Wait for all signal groups to match state (as regex string, e.g. 'c' for yellow flash).
46
53
  def wait_for_groups(state, timeout:)
47
54
  regex = /^#{state}+$/
@@ -52,6 +59,10 @@ module RSMP
52
59
  )
53
60
  end
54
61
 
62
+ def wait_for_groups!(...)
63
+ wait_for_groups(...).value!
64
+ end
65
+
55
66
  # Wait for the TLC to return to normal control mode (functional position NormalControl,
56
67
  # yellow flash off, startup mode off).
57
68
  def wait_for_normal_control(timeout: nil)
@@ -66,36 +77,49 @@ module RSMP
66
77
  )
67
78
  end
68
79
 
80
+ def wait_for_normal_control!(...)
81
+ wait_for_normal_control(...).value!
82
+ end
83
+
69
84
  # Read cycle times for all plans via S0028.
70
85
  # Returns a hash of plan_nr (Integer) => cycle_time (Integer, seconds).
71
86
  def read_cycle_times(options: {})
72
- validate_ready 'read cycle times'
73
87
  timeout = options[:timeout] || @timeouts['status_response']
74
- collector = request_status_and_collect({ S0028: [:status] }, within: timeout)
75
- collector.ok!
76
- status_items(collector.messages.first.attributes['sS'].first['s']).to_h do |item|
77
- item.split('-').map(&:to_i)
88
+ request_status_and_collect({ S0028: [:status] }, within: timeout).map do |exchange|
89
+ status_items(exchange.collection.messages.first.attributes['sS'].first['s']).to_h do |item|
90
+ item.split('-').map(&:to_i)
91
+ end
78
92
  end
79
93
  end
80
94
 
95
+ def read_cycle_times!(...)
96
+ read_cycle_times(...).value!
97
+ end
98
+
81
99
  # Read the current signal plan number via S0014.
82
100
  # Returns the plan number as an Integer.
83
101
  def read_current_plan(options: {})
84
- validate_ready 'read current plan'
85
102
  timeout = options[:timeout] || @timeouts['status_response']
86
- collector = request_status_and_collect({ S0014: [:status] }, within: timeout)
87
- collector.ok!
88
- collector.messages.first.attributes['sS'].first['s'].to_i
103
+ request_status_and_collect({ S0014: [:status] }, within: timeout).map do |exchange|
104
+ exchange.collection.messages.first.attributes['sS'].first['s'].to_i
105
+ end
106
+ end
107
+
108
+ def read_current_plan!(...)
109
+ read_current_plan(...).value!
89
110
  end
90
111
 
91
112
  # Read the value of a single dynamic band for a given plan and band index via S0023.
92
113
  # Returns the band value as an Integer, or nil if not found.
93
114
  def read_dynamic_band(plan:, band:, options: {})
94
- validate_ready 'read dynamic band'
95
115
  timeout = options[:timeout] || @timeouts['status_response']
96
- collector = request_status_and_collect({ S0023: [:status] }, within: timeout)
97
- collector.ok!
98
- extract_band_value(collector, plan, band)
116
+ request_status_and_collect({ S0023: [:status] }, within: timeout).map do |exchange|
117
+ extract_band_value(exchange.collection, plan, band)
118
+ end
119
+ end
120
+
121
+ def read_dynamic_band!(...)
122
+ read_dynamic_band(...).value!
99
123
  end
100
124
 
101
125
  private
@@ -7,7 +7,9 @@ module RSMP
7
7
  # M0103 - Change security code for a given level.
8
8
  # Does not use security_code_for since the codes are passed explicitly.
9
9
  def set_security_code(level:, old_code:, new_code:, within:)
10
- validate_ready 'set security code'
10
+ readiness = validate_ready 'set security code'
11
+ return readiness if readiness.failure?
12
+
11
13
  raise 'TLC main component not found' unless main
12
14
 
13
15
  command_list = [{
@@ -26,15 +28,25 @@ module RSMP
26
28
  'n' => 'newSecurityCode',
27
29
  'v' => new_code.to_s
28
30
  }]
29
- send_command_and_collect(command_list, within: within).ok!
31
+ send_command_and_collect(command_list, within: within)
32
+ end
33
+
34
+ def set_security_code!(...)
35
+ set_security_code(...).value!
30
36
  end
31
37
 
32
38
  # M0104 - Set the clock on the remote TLC. clock must respond to year/month/day/hour/min/sec.
33
39
  def set_clock(clock, within:)
34
- validate_ready 'set clock'
40
+ readiness = validate_ready 'set clock'
41
+ return readiness if readiness.failure?
42
+
35
43
  raise 'TLC main component not found' unless main
36
44
 
37
- send_command_and_collect(clock_command_list(clock), within: within).ok!
45
+ send_command_and_collect(clock_command_list(clock), within: within)
46
+ end
47
+
48
+ def set_clock!(...)
49
+ set_clock(...).value!
38
50
  end
39
51
 
40
52
  private
@@ -6,6 +6,7 @@ module RSMP
6
6
  include Proxy::Control
7
7
  include Proxy::IO
8
8
  include Proxy::Plans
9
+ include Proxy::PlansBang
9
10
  include Proxy::Status
10
11
  include Proxy::Detectors
11
12
  include Proxy::System
@@ -32,7 +33,8 @@ module RSMP
32
33
  end
33
34
 
34
35
  def subscribe_to_timeplan
35
- validate_ready 'subscribe to timeplan'
36
+ readiness = validate_ready 'subscribe to timeplan'
37
+ return readiness if readiness.failure?
36
38
 
37
39
  status_list = [
38
40
  { 'sCI' => 'S0014', 'n' => 'status', 'uRt' => '0' },
@@ -45,6 +47,10 @@ module RSMP
45
47
  subscribe_to_status status_list, component: main.c_id
46
48
  end
47
49
 
50
+ def subscribe_to_timeplan!(...)
51
+ subscribe_to_timeplan(...).value!
52
+ end
53
+
48
54
  def process_status_update(message)
49
55
  status_values = message.attribute('sS')
50
56
  return unless status_values
@@ -28,9 +28,9 @@ module RSMP
28
28
  'TLC'
29
29
  end
30
30
 
31
- def start
31
+ def start_auxiliary_tasks(parent)
32
32
  super
33
- start_tlc_timer
33
+ start_tlc_timer(parent: parent)
34
34
  main.initiate_startup_sequence
35
35
  end
36
36
 
@@ -82,11 +82,11 @@ module RSMP
82
82
  end
83
83
  end
84
84
 
85
- def start_tlc_timer
85
+ def start_tlc_timer(parent:)
86
86
  task_name = 'tlc timer'
87
87
  log "Starting #{task_name} with interval #{@interval} seconds", level: :debug
88
88
 
89
- @timer = @task.async do |task|
89
+ @timer = parent.async do |task|
90
90
  task.annotate task_name
91
91
  run_tlc_timer task
92
92
  end
@@ -96,9 +96,6 @@ module RSMP
96
96
  next_time = Time.now.to_f
97
97
  loop do
98
98
  timer(@clock.now)
99
- rescue StandardError => e
100
- distribute_error e, level: :internal
101
- ensure
102
99
  # adjust sleep duration to avoid drift. so wake up always happens on the
103
100
  # same fractional second.
104
101
  # note that Time.now is not monotonic. If the clock is changed,
@@ -108,7 +105,7 @@ module RSMP
108
105
  # if the clock is changed, the wake up would then happen on a different
109
106
  # fractional second
110
107
  next_time += @interval
111
- duration = next_time - Time.now.to_f
108
+ duration = [next_time - Time.now.to_f, 0].max
112
109
  task.sleep duration
113
110
  end
114
111
  end
@@ -116,7 +113,7 @@ module RSMP
116
113
  def stop_tlc_timer
117
114
  return unless @timer
118
115
 
119
- @timer.stop
116
+ @timer.cancel if @timer.running?
120
117
  @timer = nil
121
118
  end
122
119
 
@@ -0,0 +1,22 @@
1
+ module RSMP
2
+ # The result of validating a message against one or more schemas.
3
+ Validation = Data.define(:violations) do
4
+ def initialize(violations: [])
5
+ raise ArgumentError, 'violations must be an Array' unless violations.is_a?(Array)
6
+
7
+ super(violations: violations.map { |violation| Array(violation).dup.freeze }.freeze)
8
+ end
9
+
10
+ def valid?
11
+ violations.empty?
12
+ end
13
+
14
+ def invalid?
15
+ !valid?
16
+ end
17
+
18
+ def message
19
+ violations.map { |item| item.reject { |part| part == '' } }.compact.join(', ').strip
20
+ end
21
+ end
22
+ end
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = '0.48.4'.freeze
2
+ VERSION = '0.51.1'.freeze
3
3
  end
data/lib/rsmp.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'yaml'
2
2
  require 'socket'
3
3
  require 'time'
4
+ require 'English'
4
5
  require 'async'
5
6
  require 'io/endpoint'
6
7
  require 'io/endpoint/host_endpoint'
@@ -10,16 +11,28 @@ require 'json'
10
11
  require 'securerandom'
11
12
  require 'json_schemer'
12
13
  require 'async/queue'
14
+ require 'async/barrier'
15
+ require 'async/promise'
13
16
  require_relative 'rsmp/schema_error'
17
+ require_relative 'rsmp/helpers/clock'
18
+ require_relative 'rsmp/helpers/deep_merge'
19
+ require_relative 'rsmp/helpers/error'
20
+ require_relative 'rsmp/failure'
21
+ require_relative 'rsmp/result'
22
+ require_relative 'rsmp/validation'
23
+ require_relative 'rsmp/event'
24
+ require_relative 'rsmp/event_source'
25
+ require_relative 'rsmp/completion'
26
+ require_relative 'rsmp/termination'
27
+ require_relative 'rsmp/collection'
28
+ require_relative 'rsmp/exchange'
29
+ require_relative 'rsmp/delivery'
14
30
  require_relative 'rsmp/schema'
15
31
  require_relative 'rsmp/convert/import/yaml'
16
32
  require_relative 'rsmp/convert/export/json_schema'
17
33
 
18
34
  require_relative 'rsmp/status_list'
19
35
  require_relative 'rsmp/command_list'
20
- require_relative 'rsmp/helpers/clock'
21
- require_relative 'rsmp/helpers/deep_merge'
22
- require_relative 'rsmp/helpers/error'
23
36
  require_relative 'rsmp/log/logging'
24
37
  require_relative 'rsmp/log/archive'
25
38
  require_relative 'rsmp/log/filtering'
@@ -35,7 +48,6 @@ require_relative 'rsmp/component/component_proxy'
35
48
  require_relative 'rsmp/collect/distributor'
36
49
  require_relative 'rsmp/collect/receiver'
37
50
  require_relative 'rsmp/collect/queue'
38
- require_relative 'rsmp/collect/collector/status'
39
51
  require_relative 'rsmp/collect/collector/reporting'
40
52
  require_relative 'rsmp/collect/collector/logging'
41
53
  require_relative 'rsmp/collect/collector'
@@ -71,11 +83,13 @@ require_relative 'rsmp/proxy/modules/send'
71
83
  require_relative 'rsmp/proxy/modules/receive'
72
84
  require_relative 'rsmp/proxy/modules/versions'
73
85
  require_relative 'rsmp/proxy/modules/tasks'
86
+ require_relative 'rsmp/proxy/modules/lifecycle'
74
87
  require_relative 'rsmp/proxy/proxy'
75
88
  require_relative 'rsmp/sxl/interface'
76
89
  require_relative 'rsmp/sxl/supervisor_interface'
77
90
  require_relative 'rsmp/sxl/site_interface'
78
91
  require_relative 'rsmp/sxl/registry'
92
+ require_relative 'rsmp/sxl/processing'
79
93
  require_relative 'rsmp/proxy/supervisor/modules/status_updates'
80
94
  require_relative 'rsmp/proxy/supervisor/modules/status'
81
95
  require_relative 'rsmp/proxy/supervisor/modules/commands'
@@ -108,6 +122,7 @@ require_relative 'rsmp/tlc/proxy/control'
108
122
  require_relative 'rsmp/tlc/proxy/detectors'
109
123
  require_relative 'rsmp/tlc/proxy/io'
110
124
  require_relative 'rsmp/tlc/proxy/plans'
125
+ require_relative 'rsmp/tlc/proxy/plans_bang'
111
126
  require_relative 'rsmp/tlc/proxy/status'
112
127
  require_relative 'rsmp/tlc/proxy/system'
113
128
  require_relative 'rsmp/tlc/supervisor_interface'