rsmp-validator 0.1.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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/config/cross_rs4s.yaml +55 -0
  3. data/config/gem_supervisor.yaml +56 -0
  4. data/config/gem_tlc.yaml +56 -0
  5. data/config/gem_tlc_secrets.yaml +3 -0
  6. data/config/kapsch_etx.yaml +54 -0
  7. data/config/lightmotion_satellite.yaml +56 -0
  8. data/config/secrets.yaml +3 -0
  9. data/config/secrets_example.yaml +6 -0
  10. data/config/semaforica_cartesio.yaml +56 -0
  11. data/config/simulator/node_log.yaml +17 -0
  12. data/config/simulator/supervisor.yaml +11 -0
  13. data/config/simulator/tlc.yaml +56 -0
  14. data/config/sus.rb +13 -0
  15. data/config/swarco_itc3.yaml +55 -0
  16. data/config/tecsen_tmacs_supervisor.yaml +57 -0
  17. data/config/validator.rb +37 -0
  18. data/config/validator.yaml +5 -0
  19. data/config/validator_example.yaml +23 -0
  20. data/config/validator_log.yaml +19 -0
  21. data/exe/rsmp-validator +121 -0
  22. data/lib/doc_gen/parser.rb +276 -0
  23. data/lib/doc_gen/renderer.rb +153 -0
  24. data/lib/rsmp/validator/async_context.rb +15 -0
  25. data/lib/rsmp/validator/auto_node.rb +82 -0
  26. data/lib/rsmp/validator/auto_site.rb +30 -0
  27. data/lib/rsmp/validator/auto_supervisor.rb +23 -0
  28. data/lib/rsmp/validator/config_normalizer.rb +103 -0
  29. data/lib/rsmp/validator/configuration/loader.rb +79 -0
  30. data/lib/rsmp/validator/configuration/secrets.rb +54 -0
  31. data/lib/rsmp/validator/configuration/validation.rb +115 -0
  32. data/lib/rsmp/validator/configuration.rb +129 -0
  33. data/lib/rsmp/validator/helpers/alarms.rb +66 -0
  34. data/lib/rsmp/validator/helpers/clock.rb +16 -0
  35. data/lib/rsmp/validator/helpers/connection.rb +73 -0
  36. data/lib/rsmp/validator/helpers/handshake.rb +110 -0
  37. data/lib/rsmp/validator/helpers/input.rb +42 -0
  38. data/lib/rsmp/validator/helpers/security.rb +26 -0
  39. data/lib/rsmp/validator/helpers/signal_plans.rb +37 -0
  40. data/lib/rsmp/validator/helpers/signal_priority.rb +130 -0
  41. data/lib/rsmp/validator/helpers/startup.rb +157 -0
  42. data/lib/rsmp/validator/helpers/status.rb +22 -0
  43. data/lib/rsmp/validator/lifecycle.rb +99 -0
  44. data/lib/rsmp/validator/log.rb +11 -0
  45. data/lib/rsmp/validator/mode_detection.rb +84 -0
  46. data/lib/rsmp/validator/options/site_test_options.rb +58 -0
  47. data/lib/rsmp/validator/options/supervisor_test_options.rb +51 -0
  48. data/lib/rsmp/validator/site_tester.rb +113 -0
  49. data/lib/rsmp/validator/supervisor_tester.rb +76 -0
  50. data/lib/rsmp/validator/tester.rb +101 -0
  51. data/lib/rsmp/validator/version.rb +5 -0
  52. data/lib/rsmp/validator/version_filter.rb +44 -0
  53. data/lib/rsmp/validator.rb +50 -0
  54. data/schemas/site_test.json +36 -0
  55. data/schemas/supervisor_test.json +28 -0
  56. data/test/site/core/aggregated_status_spec.rb +43 -0
  57. data/test/site/core/connect_spec.rb +104 -0
  58. data/test/site/core/core_spec.rb +9 -0
  59. data/test/site/core/disconnect_spec.rb +54 -0
  60. data/test/site/site_spec.rb +5 -0
  61. data/test/site/tlc/alarm_spec.rb +134 -0
  62. data/test/site/tlc/clock_spec.rb +252 -0
  63. data/test/site/tlc/detector_logics_spec.rb +76 -0
  64. data/test/site/tlc/emergency_routes_spec.rb +106 -0
  65. data/test/site/tlc/input_spec.rb +102 -0
  66. data/test/site/tlc/invalid_command_spec.rb +103 -0
  67. data/test/site/tlc/invalid_status_spec.rb +70 -0
  68. data/test/site/tlc/modes_spec.rb +260 -0
  69. data/test/site/tlc/output_spec.rb +58 -0
  70. data/test/site/tlc/signal_groups_spec.rb +96 -0
  71. data/test/site/tlc/signal_plans_spec.rb +287 -0
  72. data/test/site/tlc/signal_priority_spec.rb +144 -0
  73. data/test/site/tlc/subscribe_spec.rb +71 -0
  74. data/test/site/tlc/system_spec.rb +76 -0
  75. data/test/site/tlc/tlc_spec.rb +7 -0
  76. data/test/site/tlc/traffic_data_spec.rb +151 -0
  77. data/test/site/tlc/traffic_situations_spec.rb +50 -0
  78. data/test/supervisor/aggregated_status_spec.rb +18 -0
  79. data/test/supervisor/connect_spec.rb +219 -0
  80. data/test/supervisor/supervisor_spec.rb +11 -0
  81. metadata +190 -0
@@ -0,0 +1,43 @@
1
+ describe 'Site::Core' do
2
+ describe 'Aggregated Status' do
3
+ # Verify that the controller responds to an aggregated status request.
4
+ #
5
+ # 1. Given the site is connected
6
+ # 2. When we request aggregated status
7
+ # 3. Then we should receive an aggregated status
8
+ it 'can be requested' do
9
+ with_site(:connected, core: '>=3.1.5') do |site_proxy|
10
+ log 'Request aggregated status'
11
+ site_proxy.request_aggregated_status_and_collect(
12
+ RSMP::Validator.get_config('main_component'),
13
+ within: RSMP::Validator.get_config('timeouts', 'status_response')
14
+ ).ok!
15
+ end
16
+ end
17
+
18
+ # Verify that aggregated status uses null for unused attributes, from SXL 1.1
19
+ # For SXL versions before 1.1 empty strings "" is also allowed.
20
+ #
21
+ # 1. Given the is reconnected
22
+ # 2. When we receive an aggregated status
23
+ # 3. Then fP and fS should be null
24
+ it 'uses null for functional position/state' do
25
+ with_site(:isolated, sxl: '>=1.1',
26
+ 'collect' => {
27
+ filter: RSMP::Filter.new(type: 'AggregatedStatus'),
28
+ timeout: RSMP::Validator.get_config('timeouts', 'ready'),
29
+ num: 1,
30
+ ingoing: true
31
+ }) do |site_proxy|
32
+ collector = site_proxy.collector
33
+ collector.use_task Async::Task.current
34
+ collector.wait!
35
+ aggregated_status = site_proxy.collector.messages.first
36
+
37
+ expect(aggregated_status).to be_a(RSMP::AggregatedStatus)
38
+ expect(aggregated_status.attribute('fP')).to be_nil
39
+ expect(aggregated_status.attribute('fS')).to be_nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,104 @@
1
+ describe 'Site::Core' do
2
+ describe 'Connection Sequence' do
3
+ include RSMP::Validator::Helpers::Handshake
4
+
5
+ # Verify the connection sequence when using rsmp core 3.1.1
6
+ #
7
+ # 1. Given the site is connected and using core 3.1.1
8
+ # 2. When handshake messages are sent and received
9
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.1
10
+ # 4. And the connection sequence should be complete
11
+ it 'is correct for rsmp version 3.1.1' do
12
+ skip 'requires core == 3.1.1' unless RSMP::Validator.core_matches?('3.1.1')
13
+ check_sequence '3.1.1'
14
+ end
15
+
16
+ # Verify the connection sequence when using rsmp core 3.1.2
17
+ #
18
+ # 1. Given the site is connected and using core 3.1.2
19
+ # 2. When handshake messages are sent and received
20
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.2
21
+ # 4. And the connection sequence should be complete
22
+ it 'is correct for rsmp version 3.1.2' do
23
+ skip 'requires core == 3.1.2' unless RSMP::Validator.core_matches?('3.1.2')
24
+ check_sequence '3.1.2'
25
+ end
26
+
27
+ # Verify the connection sequence when using rsmp core 3.1.3
28
+ #
29
+ # 1. Given the site is connected and using core 3.1.3
30
+ # 2. When handshake messages are sent and received
31
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.3
32
+ # 4. And the connection sequence should be complete
33
+ it 'is correct for rsmp version 3.1.3' do
34
+ skip 'requires core == 3.1.3' unless RSMP::Validator.core_matches?('3.1.3')
35
+ check_sequence '3.1.3'
36
+ end
37
+
38
+ # Verify the connection sequence when using rsmp core 3.1.4
39
+ #
40
+ # 1. Given the site is connected and using core 3.1.4
41
+ # 2. When handshake messages are sent and received
42
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.4
43
+ # 4. And the connection sequence should be complete
44
+ it 'is correct for rsmp version 3.1.4' do
45
+ skip 'requires core == 3.1.4' unless RSMP::Validator.core_matches?('3.1.4')
46
+ check_sequence '3.1.4'
47
+ end
48
+
49
+ # Verify the connection sequence when using rsmp core 3.1.5
50
+ #
51
+ # 1. Given the site is connected and using core 3.1.5
52
+ # 2. When handshake messages are sent and received
53
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.5
54
+ # 4. And the connection sequence should be complete
55
+ it 'is correct for rsmp version 3.1.5' do
56
+ skip 'requires core == 3.1.5' unless RSMP::Validator.core_matches?('3.1.5')
57
+ check_sequence '3.1.5'
58
+ end
59
+
60
+ # Verify the connection sequence when using rsmp core 3.2
61
+ #
62
+ # 1. Given the site is connected and using core 3.2
63
+ # 2. When handshake messages are sent and received
64
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.5
65
+ # 4. And the connection sequence should be complete
66
+ it 'is correct for rsmp version 3.2' do
67
+ skip 'requires core == 3.2' unless RSMP::Validator.core_matches?('3.2')
68
+ check_sequence '3.2'
69
+ end
70
+
71
+ # Verify the connection sequence when using rsmp core 3.2.1
72
+ #
73
+ # 1. Given the site is connected and using core 3.2.1
74
+ # 2. When handshake messages are sent and received
75
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.5
76
+ # 4. And the connection sequence should be complete
77
+ it 'is correct for rsmp version 3.2.1' do
78
+ skip 'requires core == 3.2.1' unless RSMP::Validator.core_matches?('3.2.1')
79
+ check_sequence '3.2.1'
80
+ end
81
+
82
+ # Verify the connection sequence when using rsmp core 3.2.2
83
+ #
84
+ # 1. Given the site is connected and using core 3.2.2
85
+ # 2. When handshake messages are sent and received
86
+ # 3. Then the handshake messages should be in the specified sequence corresponding to version 3.1.5
87
+ # 4. And the connection sequence should be complete
88
+ it 'is correct for rsmp version 3.2.2' do
89
+ skip 'requires core == 3.2.2' unless RSMP::Validator.core_matches?('3.2.2')
90
+ check_sequence '3.2.2'
91
+ end
92
+
93
+ # Verify the connection sequence when using rsmp core 3.3.0
94
+ #
95
+ # 1. Given the site is connected and using core 3.3.0
96
+ # 2. When handshake messages are sent and received
97
+ # 3. Then ComponentList should be exchanged before application traffic
98
+ # 4. And the connection sequence should be complete
99
+ it 'is correct for rsmp version 3.3.0' do
100
+ skip 'requires core == 3.3.0' unless RSMP::Validator.core_matches?('3.3.0')
101
+ check_sequence '3.3.0'
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,9 @@
1
+ describe 'Site' do
2
+ # Tests covering the core specification for sites.
3
+ #
4
+ # These tests are generic and can be used to test all types of RSMP sites.
5
+
6
+ describe 'Core' do
7
+ # This group is intentionally empty, but exist so for documentation generation purposes.
8
+ end
9
+ end
@@ -0,0 +1,54 @@
1
+ describe 'Site::Core' do
2
+ # Check that the site closed the connection as required when faced with
3
+ # various types of incorrect behaviour from our side.
4
+ #
5
+ # The site object passed by RSMP::Validator::SiteTester a SiteProxy object. We can redefine methods
6
+ # on this object to modify behaviour after the connection has been established. To ensure
7
+ # that the modfid SityProxy is not reused in later tests, we use RSMP::Validator::SiteTester.isolate,
8
+ # rather than the more common RSMP::Validator::SiteTester.connect.
9
+
10
+ describe 'Connection' do
11
+ # 1. Given the site has just connected
12
+ # 2. When our supervisor does not acknowledge watchdogs
13
+ # 3. Then the site should disconnect
14
+ it 'is closed if watchdogs are not acknowledged' do
15
+ with_site(:isolated, sxl: '>=1.0.7') do |site_proxy|
16
+ timeout = RSMP::Validator.get_config('timeouts', 'disconnect')
17
+ site_proxy.node.ignore_errors RSMP::DisconnectError do
18
+ log 'Disabling watchdog acknowledgements, site should disconnect'
19
+ def site_proxy.acknowledge(original)
20
+ if original.is_a? RSMP::Watchdog
21
+ log 'Not acknowledgning watchdog', message: original
22
+ else
23
+ super
24
+ end
25
+ end
26
+ site_proxy.wait_for_state :disconnected, timeout: timeout
27
+ end
28
+ rescue RSMP::TimeoutError
29
+ raise "Site did not disconnect within #{timeout}s"
30
+ end
31
+ end
32
+
33
+ # 1. Given the site has just connected
34
+ # 2. When our supervisor stops sending watchdogs
35
+ # 3. Then the site should not disconnect
36
+ it 'is not closed if watchdogs are not received' do
37
+ with_site(:isolated, sxl: '>=1.0.7') do |site_proxy|
38
+ timeout = RSMP::Validator.get_config('timeouts', 'disconnect')
39
+
40
+ wait_task = Async::Task.current.async do
41
+ site_proxy.wait_for_state :disconnected, timeout: timeout
42
+ raise RSMP::DisconnectError
43
+ rescue RSMP::TimeoutError
44
+ # ok, no disconnect happened
45
+ end
46
+
47
+ log 'Stop sending watchdogs, site should not disconnect'
48
+ site_proxy.with_watchdog_disabled do
49
+ wait_task.wait
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ # Tests for RSMP sites (as opposed to supervisors), like traffic light controllers.
2
+ #
3
+ describe 'Site' do
4
+ # This group is intentionally empty, but exist so for documentation generation purposes
5
+ end
@@ -0,0 +1,134 @@
1
+ describe 'Site::Tlc::Alarm' do
2
+ include RSMP::Validator::Helpers::Alarms
3
+
4
+ # Testing alarms require a reliable way of rainsing them.
5
+ #
6
+ # There's no way to trigger alarms directly via RSMP yet,
7
+ # but often you can program the equipment to raise an alarm
8
+ # when a specific input is activated. If that's the case,
9
+ # set the `alarm_activcation` item in the validator config to
10
+ # specify which input activates which alarm. See docs for details.
11
+ #
12
+ # Triggered alarms manually on the equipment is not used,
13
+ # because validator is meant for automated testing.
14
+
15
+ # Validate that a detector logic fault A0302 is raises and cleared.
16
+ #
17
+ # The test requires that the device is programmed so that the alarm
18
+ # is raise when a specific input is activated, as specified in the
19
+ # test configuration.
20
+ #
21
+ # 1. Given the site is connected
22
+ # 2. When we force the input to True
23
+ # 3. Then an alarm should be raised, with a timestamp close to now
24
+ # 4. When we force the input to False
25
+ # 5. Then the alarm issue should become inactive, with a timestamp close to now
26
+
27
+ it 'raises A0302 when input is activated' do
28
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
29
+ alarm_code_id = 'A0302'
30
+ def verify_timestamp(alarm, duration = 1.minute)
31
+ alarm_time = Time.parse(alarm.attributes['aTs'])
32
+ expect(alarm_time).to be_within(duration).of(Time.now.utc)
33
+ end
34
+ # Raise alarm by activating input
35
+ deactivated, component_id = with_alarm_activated(site_proxy, alarm_code_id) do |alarm, component_id|
36
+ verify_timestamp alarm
37
+ log "Alarm #{alarm_code_id} is now Active on component #{component_id}"
38
+ end
39
+ verify_timestamp deactivated
40
+ log "Alarm #{alarm_code_id} is now Inactive on component #{component_id}"
41
+ end
42
+ end
43
+
44
+ # Validate that an alarm can be acknowledged.
45
+ #
46
+ # The test expects that the TLC is programmed so that an detector logic fault
47
+ # alarm A0302 is raised and can be acknowledged when a specific input is activated.
48
+ # The alarm code and input nr is read from the test configuration.
49
+ #
50
+ # 1. Given the site is connected
51
+ # 2. When we trigger an alarm
52
+ # 3. Then we should receive an unacknowledged alarm issue
53
+ # 4. When we acknowledge the alarm
54
+ # 5. Then we should receive an acknowledged alarm issue
55
+
56
+ it 'can acknowledge A0302' do
57
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
58
+ alarm_code_id = 'A0302' # what alarm to expect
59
+ timeout = RSMP::Validator.get_config('timeouts', 'alarm')
60
+
61
+ log "Activating alarm #{alarm_code_id}"
62
+ with_alarm_activated(site_proxy, alarm_code_id) do |alarm, component_id| # raise alarm, by activating input
63
+ log "Alarm #{alarm_code_id} is now active on component #{component_id}"
64
+
65
+ # verify timestamp
66
+ alarm_time = Time.parse(alarm.attributes['aTs'])
67
+ expect(alarm_time).to be_within(1.minute).of(Time.now.utc)
68
+
69
+ # verify that the alarm is not acknowledged when initially raised
70
+ ack_message = "Alarm should not be acknowledged when raised, got: #{alarm.attributes['ack']}"
71
+ assert(alarm.attributes['ack'].match?(/notAcknowledged/i), ack_message)
72
+ log "Verified alarm #{alarm_code_id} is correctly not acknowledged when raised"
73
+
74
+ # test acknowledge and confirm
75
+ log "Acknowledge alarm #{alarm_code_id}"
76
+
77
+ collect_task = Async::Task.current.async do
78
+ RSMP::AlarmCollector.new(site_proxy,
79
+ num: 1,
80
+ matcher: {
81
+ 'aCId' => alarm_code_id,
82
+ 'aSp' => /Acknowledge/i,
83
+ 'ack' => /Acknowledged/i,
84
+ 'aS' => /Active/i
85
+ },
86
+ timeout: timeout).collect!
87
+ end
88
+
89
+ site_proxy.send_message RSMP::AlarmAcknowledge.new(
90
+ 'cId' => component_id,
91
+ 'aTs' => site_proxy.clock.to_s,
92
+ 'aCId' => alarm_code_id
93
+ )
94
+ messages = collect_task.wait
95
+ expect(messages).to be_a(Array)
96
+ expect(messages.first).to be_a(RSMP::Alarm)
97
+ end
98
+ end
99
+ end
100
+
101
+ # Validate that alarms can be suspended. We're using A0302 in this test.
102
+ #
103
+ # 1. Given the site is connected
104
+ # 2. And the alarm is resumed
105
+ # 3. When we suspend the alarm
106
+ # 4. Then we should received an alarm suspended messsage
107
+ # 5. When we resume the alarm
108
+ # 6. Then we should receive an alarm resumed message
109
+
110
+ it 'can suspende and resume A0302' do
111
+ with_site(:connected) do |site_proxy|
112
+ alarm_code_id = 'A0302'
113
+ _, component_id = find_alarm_programming(alarm_code_id)
114
+
115
+ # first resume alarm to make sure something happens when we suspend
116
+ site_proxy.resume_alarm Async::Task.current, c_id: component_id, a_c_id: alarm_code_id, collect: false
117
+
118
+ begin
119
+ # suspend alarm
120
+ _, response = site_proxy.suspend_alarm Async::Task.current, c_id: component_id, a_c_id: alarm_code_id,
121
+ collect: true
122
+ expect(response).to be_a(RSMP::AlarmSuspended)
123
+
124
+ # resume alarm
125
+ _, response = site_proxy.resume_alarm Async::Task.current, c_id: component_id, a_c_id: alarm_code_id,
126
+ collect: true
127
+ expect(response).to be_a(RSMP::AlarmResumed)
128
+ ensure
129
+ # always end with resuming alarm
130
+ site_proxy.resume_alarm Async::Task.current, c_id: component_id, a_c_id: alarm_code_id, collect: false
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,252 @@
1
+ describe 'Site::Tlc::Clock' do
2
+ include RSMP::Validator::Helpers::Clock
3
+ include RSMP::Validator::Helpers::Alarms
4
+
5
+ # Tests related to the clock.
6
+ # When you set the clock, the adjusted time should be used
7
+ # everywhere you get back a timestamp.
8
+ # Note that watchdog messages can be used to synchronize the clock,
9
+ # which can interfere with our tests. So we disable sending watchdogs
10
+ # during tests.
11
+
12
+ let(:clock) { Time.new(2020, 9, 29, 17, 29, 51, '+00:00') }
13
+
14
+ # Verify status 0096 current date and time
15
+ #
16
+ # 1. Given the site_proxy is connected
17
+ # 2. Request status
18
+ # 3. Expect status response before timeout
19
+ it 'can be read with S0096' do
20
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
21
+ site_proxy.request_status_and_collect({ S0096: %i[year month day hour minute second] },
22
+ within: RSMP::Validator.get_config('timeouts', 'status_response')).ok!
23
+ end
24
+ end
25
+
26
+ # Verify that the controller responds to M0104
27
+ #
28
+ # 1. Given the site_proxy is connected
29
+ # 2. Send command
30
+ # 3. Expect status response before timeout
31
+ it 'can be set with M0104' do
32
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
33
+ timeout = RSMP::Validator.get_config('timeouts', 'command_response')
34
+ site_proxy.tlc.set_clock(clock, within: timeout)
35
+ end
36
+ end
37
+
38
+ # Verify status S0096 clock after changing clock
39
+ #
40
+ # 1. Given the site_proxy is connected
41
+ # 2. Send control command to set_clock
42
+ # 3. Request status S0096
43
+ # 4. Compare set_clock and status timestamp
44
+ # 5. Expect the difference to be within max_diff
45
+ it 'is used for S0096 status response' do
46
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
47
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
48
+ command_timeout = RSMP::Validator.get_config('timeouts', 'command_response')
49
+ with_clock_set site_proxy, clock, within: command_timeout do
50
+ status_list = { S0096: %i[
51
+ year
52
+ month
53
+ day
54
+ hour
55
+ minute
56
+ second
57
+ ] }
58
+ timeout = RSMP::Validator.get_config('timeouts', 'status_update')
59
+ collector = site_proxy.request_status_and_collect(
60
+ status_list,
61
+ within: timeout
62
+ )
63
+ collector.ok!
64
+ status = status_list.keys.first.to_s
65
+
66
+ received = Time.new(
67
+ collector.matcher_result({ 'sCI' => status, 'n' => 'year' })['s'],
68
+ collector.matcher_result({ 'sCI' => status, 'n' => 'month' })['s'],
69
+ collector.matcher_result({ 'sCI' => status, 'n' => 'day' })['s'],
70
+ collector.matcher_result({ 'sCI' => status, 'n' => 'hour' })['s'],
71
+ collector.matcher_result({ 'sCI' => status, 'n' => 'minute' })['s'],
72
+ collector.matcher_result({ 'sCI' => status, 'n' => 'second' })['s'],
73
+ 'UTC'
74
+ )
75
+
76
+ max_diff = RSMP::Validator.get_config('timeouts', 'command_response') +
77
+ RSMP::Validator.get_config('timeouts', 'status_response')
78
+
79
+ diff = received - clock
80
+ diff = diff.round
81
+ assert(diff.abs <= max_diff,
82
+ "Clock reported by S0096 is off by #{diff}s, should be within #{max_diff}s")
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # Verify status response timestamp after changing clock
89
+ #
90
+ # 1. Given the site_proxy is connected
91
+ # 2. Send control command to set_clock
92
+ # 3. Request status S0096
93
+ # 4. Compare set_clock and response timestamp
94
+ # 5. Expect the difference to be within max_diff
95
+ it 'is used for S0096 response timestamp' do
96
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
97
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
98
+ with_clock_set site_proxy, clock, within: RSMP::Validator.get_config('timeouts', 'command_response') do
99
+ status_list = { S0096: %i[
100
+ year
101
+ month
102
+ day
103
+ hour
104
+ minute
105
+ second
106
+ ] }
107
+
108
+ timeout = RSMP::Validator.get_config('timeouts', 'status_response')
109
+ collector = site_proxy.request_status_and_collect(status_list,
110
+ within: timeout)
111
+ collector.ok!
112
+
113
+ max_diff = RSMP::Validator.get_config('timeouts', 'command_response') + timeout
114
+ diff = Time.parse(collector.messages.first.attributes['sTs']) - clock
115
+ diff = diff.round
116
+ assert(diff.abs <= max_diff,
117
+ "Timestamp of S0096 is off by #{diff}s, should be within #{max_diff}s")
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # Verify aggregated status response timestamp after changing clock
124
+ #
125
+ # 1. Given the site_proxy is connected
126
+ # 2. Send control command to set clock
127
+ # 3. Wait for status = true
128
+ # 4. Request aggregated status
129
+ # 5. Compare set_clock and response timestamp
130
+ # 6. Expect the difference to be within max_diff
131
+ it 'is used for aggregated status timestamp' do
132
+ skip 'requires core >= 3.1.5' unless RSMP::Validator.core_matches?('>=3.1.5')
133
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
134
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
135
+ with_clock_set site_proxy, clock, within: RSMP::Validator.get_config('timeouts', 'command_response') do
136
+ component = RSMP::Validator.get_config('main_component')
137
+ timeout = RSMP::Validator.get_config('timeouts', 'status_response')
138
+ collector = site_proxy.request_aggregated_status_and_collect(component, within: timeout)
139
+ max_diff = RSMP::Validator.get_config('timeouts', 'command_response') + timeout
140
+ diff = Time.parse(collector.messages.first.attributes['aSTS']) - clock
141
+ diff = diff.round
142
+ assert(diff.abs <= max_diff,
143
+ "Timestamp of aggregated status is off by #{diff}s, should be within #{max_diff}s")
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ # Verify command response timestamp after changing clock
150
+ #
151
+ # 1. Given the site_proxy is connected
152
+ # 2. Send control command to set clock
153
+ # 3. Send command to set functional position
154
+ # 4. Compare set_clock and response timestamp
155
+ # 5. Expect the difference to be within max_diff
156
+ it 'is used for M0001 response timestamp' do
157
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
158
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
159
+ timeout = RSMP::Validator.get_config('timeouts', 'command_response')
160
+ with_clock_set site_proxy, clock, within: timeout do
161
+ result = site_proxy.tlc.set_functional_position('NormalControl', within: timeout)
162
+ collector = result[:collector]
163
+ max_diff = timeout * 2
164
+ diff = Time.parse(collector.messages.first.attributes['cTS']) - clock
165
+ diff = diff.round
166
+ assert(diff.abs <= max_diff,
167
+ "Timestamp of command response is off by #{diff}s, should be within #{max_diff}s")
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ # Verify command response timestamp after changing clock
174
+ #
175
+ # 1. Given the site_proxy is connected
176
+ # 2. Send control command to set clock
177
+ # 3. Send command to set functional position
178
+ # 4. Compare set_clock and response timestamp
179
+ # 5. Expect the difference to be within max_diff
180
+ it 'is used for M0104 response timestamp' do
181
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
182
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
183
+ timeout = RSMP::Validator.get_config('timeouts', 'command_response')
184
+ with_clock_set site_proxy, clock, within: timeout do
185
+ result = site_proxy.tlc.set_functional_position('NormalControl', within: timeout)
186
+ collector = result[:collector]
187
+ max_diff = timeout
188
+ diff = Time.parse(collector.messages.first.attributes['cTS']) - clock
189
+ diff = diff.round
190
+ assert(diff.abs <= max_diff,
191
+ "Timestamp of command response is off by #{diff}s, should be within #{max_diff}s")
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ # Verify timestamp of alarm after changing clock
198
+ # The test requires the device to be programmed so that
199
+ # a A0302 alarm can be raise by activating a specific input, as
200
+ # configuted in the test config.
201
+ #
202
+ # 1. Given the site_proxy is connected
203
+ # 2. When we send a command to change the clock
204
+ # 3. And we raise an alarm, by acticate an input
205
+ # 4. Then we should receive an alarm
206
+ # 5. And the alarm timestamp should be close to the time set the clock to
207
+
208
+ it 'is used for alarm timestamp' do
209
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
210
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
211
+ command_timeout = RSMP::Validator.get_config('timeouts', 'command_response')
212
+ with_clock_set site_proxy, clock, within: command_timeout do # set clock
213
+ with_alarm_activated(site_proxy, 'A0302') do |alarm| # raise alarm, by activating input
214
+ alarm_time = Time.parse(alarm.attributes['aTs'])
215
+ max_diff = RSMP::Validator.get_config('timeouts', 'command_response') +
216
+ RSMP::Validator.get_config('timeouts', 'status_response')
217
+ diff = alarm_time - clock
218
+ assert(diff.round.abs <= max_diff,
219
+ "Timestamp of alarm is off by #{diff}s, should be within #{max_diff}s")
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ # Verify timestamp of watchdog after changing clock
227
+ #
228
+ # 1. Given the site_proxy is connected
229
+ # 2. Send control command to setset_clock
230
+ # 3. Wait for Watchdog
231
+ # 4. Compare set_clock and alarm response timestamp
232
+ # 5. Expect the difference to be within max_diff
233
+ it 'is used for watchdog timestamp' do
234
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
235
+ site_proxy.with_watchdog_disabled do # avoid time synchronization by disabling watchdogs
236
+ with_clock_set site_proxy, clock, within: RSMP::Validator.get_config('timeouts', 'command_response') do
237
+ log 'Checking watchdog timestamp'
238
+ watchdog_timeout = RSMP::Validator.get_config('timeouts', 'watchdog')
239
+ collector = RSMP::Collector.new(site_proxy, task: Async::Task.current, type: 'Watchdog', num: 1,
240
+ timeout: watchdog_timeout)
241
+ collector.collect!
242
+ max_diff = RSMP::Validator.get_config('timeouts', 'command_response') +
243
+ RSMP::Validator.get_config('timeouts', 'status_response')
244
+ diff = Time.parse(collector.messages.first.attributes['wTs']) - clock
245
+ diff = diff.round
246
+ assert(diff.abs <= max_diff,
247
+ "Timestamp of watchdog is off by #{diff}s, should be within #{max_diff}s")
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,76 @@
1
+ describe 'Site::Tlc::DetectorLogics' do
2
+ include RSMP::Validator::Helpers::Status
3
+ include RSMP::Validator::Helpers::Input
4
+
5
+ # Verify status S0016 number of detector logics
6
+ #
7
+ # 1. Given the site_proxy is connected
8
+ # 2. Request status
9
+ # 3. Expect status response before timeout
10
+ it 'list size is read with S0016' do
11
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
12
+ site_proxy.request_status_and_collect({ S0016: [:number] },
13
+ within: RSMP::Validator.get_config('timeouts', 'status_response')).ok!
14
+ end
15
+ end
16
+
17
+ # Verify status S0002 detector logic status
18
+ #
19
+ # 1. Given the site_proxy is connected
20
+ # 2. Request status
21
+ # 3. Expect status response before timeout
22
+ it 'status is read with S0002' do
23
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
24
+ site_proxy.request_status_and_collect({ S0002: [:detectorlogicstatus] },
25
+ within: RSMP::Validator.get_config('timeouts', 'status_response')).ok!
26
+ end
27
+ end
28
+
29
+ # Verify status S0021 manually set detector logic
30
+ #
31
+ # 1. Given the site_proxy is connected
32
+ # 2. Request status
33
+ # 3. Expect status response before timeout
34
+ it 'forcing is read with S0021' do
35
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
36
+ site_proxy.request_status_and_collect({ S0021: [:detectorlogics] },
37
+ within: RSMP::Validator.get_config('timeouts', 'status_response')).ok!
38
+ end
39
+ end
40
+
41
+ # 1. Verify connection
42
+ # 2. Send control command to switch detector_logic= true
43
+ # 3. Wait for status = true
44
+ it 'forcing is set with M0008' do
45
+ with_site(:connected, sxl: '>=1.0.7') do |site_proxy|
46
+ RSMP::Validator.get_config('components', 'detector_logic').keys.each_with_index do |component, indx|
47
+ timeout = RSMP::Validator.get_config('timeouts', 'command_response')
48
+ site_proxy.tlc.force_detector_logic(component, status: 'True', mode: 'True', within: timeout)
49
+ wait_for_status(
50
+ site_proxy,
51
+ "detector logic #{component} to be True",
52
+ [{ 'sCI' => 'S0002', 'n' => 'detectorlogicstatus', 's' => /^.{#{indx}}1/ }]
53
+ )
54
+
55
+ site_proxy.tlc.force_detector_logic(component, status: 'True', mode: 'False', within: timeout)
56
+ wait_for_status(
57
+ site_proxy,
58
+ "detector logic #{component} to be False",
59
+ [{ 'sCI' => 'S0002', 'n' => 'detectorlogicstatus', 's' => /^.{#{indx}}0/ }]
60
+ )
61
+ end
62
+ end
63
+ end
64
+
65
+ # Verify status S0031 trigger level sensitivity for loop detector
66
+ #
67
+ # 1. Given the site_proxy is connected
68
+ # 2. Request status
69
+ # 3. Expect status response before timeout
70
+ it 'sensitivity is read with S0031' do
71
+ with_site(:connected, sxl: '>=1.0.15') do |site_proxy|
72
+ site_proxy.request_status_and_collect({ S0031: [:status] },
73
+ within: RSMP::Validator.get_config('timeouts', 'status_response')).ok!
74
+ end
75
+ end
76
+ end