rsmp 0.48.3 → 0.51.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -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/message_distribution.md +17 -3
  9. data/documentation/tasks.md +65 -117
  10. data/lib/rsmp/cli.rb +93 -5
  11. data/lib/rsmp/collect/collector.rb +117 -134
  12. data/lib/rsmp/collect/distributor.rb +16 -3
  13. data/lib/rsmp/collect/queue.rb +14 -6
  14. data/lib/rsmp/collect/receiver.rb +1 -1
  15. data/lib/rsmp/collect/state_collector.rb +11 -2
  16. data/lib/rsmp/collection.rb +12 -0
  17. data/lib/rsmp/completion.rb +49 -0
  18. data/lib/rsmp/component/component_base.rb +3 -3
  19. data/lib/rsmp/convert/export/json_schema/index.rb +8 -3
  20. data/lib/rsmp/convert/import/yaml.rb +12 -8
  21. data/lib/rsmp/delivery.rb +18 -0
  22. data/lib/rsmp/event.rb +19 -0
  23. data/lib/rsmp/event_source.rb +36 -0
  24. data/lib/rsmp/exchange.rb +10 -0
  25. data/lib/rsmp/failure.rb +23 -0
  26. data/lib/rsmp/helpers/error.rb +14 -23
  27. data/lib/rsmp/message.rb +9 -7
  28. data/lib/rsmp/node/node.rb +3 -21
  29. data/lib/rsmp/node/site/connections.rb +4 -9
  30. data/lib/rsmp/node/site/site.rb +31 -16
  31. data/lib/rsmp/node/supervisor/modules/connection.rb +19 -5
  32. data/lib/rsmp/node/supervisor/modules/sites.rb +16 -5
  33. data/lib/rsmp/node/supervisor/supervisor.rb +13 -14
  34. data/lib/rsmp/node/task.rb +59 -53
  35. data/lib/rsmp/proxy/modules/acknowledgements.rb +22 -9
  36. data/lib/rsmp/proxy/modules/lifecycle.rb +134 -0
  37. data/lib/rsmp/proxy/modules/receive.rb +109 -66
  38. data/lib/rsmp/proxy/modules/send.rb +109 -51
  39. data/lib/rsmp/proxy/modules/state.rb +15 -5
  40. data/lib/rsmp/proxy/modules/tasks.rb +96 -70
  41. data/lib/rsmp/proxy/modules/versions.rb +8 -8
  42. data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
  43. data/lib/rsmp/proxy/proxy.rb +4 -66
  44. data/lib/rsmp/proxy/site/connection.rb +27 -22
  45. data/lib/rsmp/proxy/site/modules/aggregated_status.rb +18 -8
  46. data/lib/rsmp/proxy/site/modules/alarms.rb +45 -38
  47. data/lib/rsmp/proxy/site/modules/commands.rb +18 -8
  48. data/lib/rsmp/proxy/site/modules/status.rb +44 -19
  49. data/lib/rsmp/proxy/site/site_proxy.rb +12 -10
  50. data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
  51. data/lib/rsmp/proxy/supervisor/modules/alarms.rb +1 -1
  52. data/lib/rsmp/proxy/supervisor/modules/commands.rb +1 -1
  53. data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +8 -9
  54. data/lib/rsmp/proxy/supervisor/modules/status.rb +1 -1
  55. data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +1 -1
  56. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +34 -30
  57. data/lib/rsmp/result.rb +95 -0
  58. data/lib/rsmp/schema/validation.rb +5 -3
  59. data/lib/rsmp/sxl/interface.rb +8 -1
  60. data/lib/rsmp/sxl/processing/catalogue.rb +61 -0
  61. data/lib/rsmp/sxl/processing/conflict_checker.rb +30 -0
  62. data/lib/rsmp/sxl/processing/dependency_graph.rb +37 -0
  63. data/lib/rsmp/sxl/processing/document.rb +158 -0
  64. data/lib/rsmp/sxl/processing/error.rb +8 -0
  65. data/lib/rsmp/sxl/processing/manifest.rb +144 -0
  66. data/lib/rsmp/sxl/processing/natural_sort.rb +22 -0
  67. data/lib/rsmp/sxl/processing/resolver.rb +94 -0
  68. data/lib/rsmp/sxl/processing/version_requirement.rb +86 -0
  69. data/lib/rsmp/sxl/processing.rb +18 -0
  70. data/lib/rsmp/sxl/supervisor_interface.rb +8 -1
  71. data/lib/rsmp/termination.rb +10 -0
  72. data/lib/rsmp/tlc/modules/plans.rb +0 -2
  73. data/lib/rsmp/tlc/proxy/control.rb +50 -16
  74. data/lib/rsmp/tlc/proxy/detectors.rb +15 -4
  75. data/lib/rsmp/tlc/proxy/io.rb +46 -31
  76. data/lib/rsmp/tlc/proxy/plans.rb +36 -19
  77. data/lib/rsmp/tlc/proxy/plans_bang.rb +44 -0
  78. data/lib/rsmp/tlc/proxy/status.rb +47 -23
  79. data/lib/rsmp/tlc/proxy/system.rb +16 -4
  80. data/lib/rsmp/tlc/supervisor_interface.rb +7 -1
  81. data/lib/rsmp/tlc/traffic_controller_site.rb +6 -9
  82. data/lib/rsmp/validation.rb +22 -0
  83. data/lib/rsmp/version.rb +1 -1
  84. data/lib/rsmp.rb +19 -4
  85. data/rsmp.gemspec +1 -1
  86. data/schemas/tlc/1.0.10/commands/M0001.json +3 -3
  87. data/schemas/tlc/1.0.10/source/sxl.yaml +3 -3
  88. data/schemas/tlc/1.0.10/sxl_index.json +3 -3
  89. data/schemas/tlc/1.0.13/commands/M0001.json +3 -3
  90. data/schemas/tlc/1.0.13/source/sxl.yaml +3 -3
  91. data/schemas/tlc/1.0.13/sxl_index.json +3 -3
  92. data/schemas/tlc/1.0.14/commands/M0001.json +3 -3
  93. data/schemas/tlc/1.0.14/source/sxl.yaml +3 -3
  94. data/schemas/tlc/1.0.14/sxl_index.json +3 -3
  95. data/schemas/tlc/1.0.15/commands/M0001.json +3 -3
  96. data/schemas/tlc/1.0.15/source/sxl.yaml +3 -3
  97. data/schemas/tlc/1.0.15/sxl_index.json +3 -3
  98. data/schemas/tlc/1.0.7/commands/M0001.json +3 -3
  99. data/schemas/tlc/1.0.7/source/sxl.yaml +3 -3
  100. data/schemas/tlc/1.0.7/sxl_index.json +3 -3
  101. data/schemas/tlc/1.0.8/commands/M0001.json +3 -3
  102. data/schemas/tlc/1.0.8/source/sxl.yaml +3 -3
  103. data/schemas/tlc/1.0.8/sxl_index.json +3 -3
  104. data/schemas/tlc/1.0.9/commands/M0001.json +3 -3
  105. data/schemas/tlc/1.0.9/source/sxl.yaml +3 -3
  106. data/schemas/tlc/1.0.9/sxl_index.json +3 -3
  107. metadata +25 -4
  108. data/lib/rsmp/collect/collector/status.rb +0 -34
@@ -6,20 +6,29 @@ module RSMP
6
6
  module Control
7
7
  # M0001 - Set functional position (NormalControl, YellowFlash, Dark).
8
8
  def set_functional_position(status, within:, timeout_minutes: 0)
9
- validate_ready 'set functional position'
9
+ readiness = validate_ready 'set functional position'
10
+ return readiness if readiness.failure?
11
+
10
12
  raise 'TLC main component not found' unless main
11
13
 
12
14
  command_list = functional_position_command_list(status, timeout_minutes)
13
15
  confirm_status = functional_position_confirm_status(status)
14
- collector = send_command_and_collect(command_list, within: within)
15
- collector.ok!
16
- wait_for_status "functional position #{status}", confirm_status, timeout: within unless confirm_status.empty?
17
- { collector: collector }
16
+ send_command_and_collect(command_list, within: within).and_then do |exchange|
17
+ next Result.success(exchange) if confirm_status.empty?
18
+
19
+ wait_for_status("functional position #{status}", confirm_status, timeout: within).map { exchange }
20
+ end
21
+ end
22
+
23
+ def set_functional_position!(...)
24
+ set_functional_position(...).value!
18
25
  end
19
26
 
20
27
  # M0005 - Set or clear an emergency route.
21
28
  def set_emergency_route(route:, active:, within:)
22
- validate_ready 'set emergency route'
29
+ readiness = validate_ready 'set emergency route'
30
+ return readiness if readiness.failure?
31
+
23
32
  raise 'TLC main component not found' unless main
24
33
 
25
34
  security_code = security_code_for(2)
@@ -41,12 +50,18 @@ module RSMP
41
50
  'v' => command_value('M0005', 'emergencyroute', route)
42
51
  }]
43
52
 
44
- send_command_and_collect(command_list, within: within).ok!
53
+ send_command_and_collect(command_list, within: within)
54
+ end
55
+
56
+ def set_emergency_route!(...)
57
+ set_emergency_route(...).value!
45
58
  end
46
59
 
47
60
  # M0007 - Enable or disable fixed-time control.
48
61
  def set_fixed_time(status, within:)
49
- validate_ready 'set fixed time'
62
+ readiness = validate_ready 'set fixed time'
63
+ return readiness if readiness.failure?
64
+
50
65
  raise 'TLC main component not found' unless main
51
66
 
52
67
  security_code = security_code_for(2)
@@ -64,13 +79,20 @@ module RSMP
64
79
  }]
65
80
 
66
81
  confirm_status = [{ 'sCI' => 'S0009', 'n' => 'status', 's' => [boolean_value(status)] }]
67
- send_command_and_collect(command_list, within: within).ok!
68
- wait_for_status "fixed time #{status}", confirm_status, timeout: within
82
+ send_command_and_collect(command_list, within: within).and_then do |exchange|
83
+ wait_for_status("fixed time #{status}", confirm_status, timeout: within).map { exchange }
84
+ end
85
+ end
86
+
87
+ def set_fixed_time!(...)
88
+ set_fixed_time(...).value!
69
89
  end
70
90
 
71
91
  # M0003 - Set traffic situation (activate a specific situation number).
72
92
  def set_traffic_situation(situation, within:)
73
- validate_ready 'set traffic situation'
93
+ readiness = validate_ready 'set traffic situation'
94
+ return readiness if readiness.failure?
95
+
74
96
  raise 'TLC main component not found' unless main
75
97
 
76
98
  security_code = security_code_for(2)
@@ -93,13 +115,20 @@ module RSMP
93
115
  }]
94
116
 
95
117
  confirm_status = [{ 'sCI' => 'S0015', 'n' => 'status', 's' => integer_value(situation) }]
96
- send_command_and_collect(command_list, within: within).ok!
97
- wait_for_status "traffic situation #{situation}", confirm_status, timeout: within
118
+ send_command_and_collect(command_list, within: within).and_then do |exchange|
119
+ wait_for_status("traffic situation #{situation}", confirm_status, timeout: within).map { exchange }
120
+ end
121
+ end
122
+
123
+ def set_traffic_situation!(...)
124
+ set_traffic_situation(...).value!
98
125
  end
99
126
 
100
127
  # M0003 - Clear the active traffic situation.
101
128
  def unset_traffic_situation(within:)
102
- validate_ready 'unset traffic situation'
129
+ readiness = validate_ready 'unset traffic situation'
130
+ return readiness if readiness.failure?
131
+
103
132
  raise 'TLC main component not found' unless main
104
133
 
105
134
  security_code = security_code_for(2)
@@ -122,8 +151,13 @@ module RSMP
122
151
  }]
123
152
 
124
153
  confirm_status = [{ 'sCI' => 'S0015', 'n' => 'status', 's' => 1 }]
125
- send_command_and_collect(command_list, within: within).ok!
126
- wait_for_status 'traffic situation unset', confirm_status, timeout: within
154
+ send_command_and_collect(command_list, within: within).and_then do |exchange|
155
+ wait_for_status('traffic situation unset', confirm_status, timeout: within).map { exchange }
156
+ end
157
+ end
158
+
159
+ def unset_traffic_situation!(...)
160
+ unset_traffic_situation(...).value!
127
161
  end
128
162
 
129
163
  private
@@ -7,7 +7,8 @@ module RSMP
7
7
  # M0008 - Force detector logic to a given mode and status.
8
8
  # component_id must refer to the detector logic component, not main.
9
9
  def force_detector_logic(component_id, status:, mode:, within:)
10
- validate_ready 'force detector logic'
10
+ readiness = validate_ready 'force detector logic'
11
+ return readiness if readiness.failure?
11
12
 
12
13
  security_code = security_code_for(2)
13
14
 
@@ -27,12 +28,18 @@ module RSMP
27
28
  'n' => 'mode',
28
29
  'v' => command_value('M0008', 'mode', mode)
29
30
  }]
30
- send_command_and_collect(command_list, component: component_id, within: within).ok!
31
+ send_command_and_collect(command_list, component: component_id, within: within)
32
+ end
33
+
34
+ def force_detector_logic!(...)
35
+ force_detector_logic(...).value!
31
36
  end
32
37
 
33
38
  # M0021 - Set the trigger level for traffic counting.
34
39
  def set_trigger_level(status, within:)
35
- validate_ready 'set trigger level'
40
+ readiness = validate_ready 'set trigger level'
41
+ return readiness if readiness.failure?
42
+
36
43
  raise 'TLC main component not found' unless main
37
44
 
38
45
  security_code = security_code_for(2)
@@ -48,7 +55,11 @@ module RSMP
48
55
  'n' => 'securityCode',
49
56
  'v' => security_code.to_s
50
57
  }]
51
- send_command_and_collect(command_list, within: within).ok!
58
+ send_command_and_collect(command_list, within: within)
59
+ end
60
+
61
+ def set_trigger_level!(...)
62
+ set_trigger_level(...).value!
52
63
  end
53
64
  end
54
65
  end
@@ -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