rsmp 0.1.21 → 0.1.27
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/.gitmodules +0 -4
- data/Gemfile.lock +13 -11
- data/config/supervisor.yaml +12 -6
- data/config/tlc.yaml +8 -0
- data/documentation/message_distribution.md +8 -8
- data/lib/rsmp.rb +4 -0
- data/lib/rsmp/archive.rb +7 -4
- data/lib/rsmp/cli.rb +131 -102
- data/lib/rsmp/collector.rb +42 -30
- data/lib/rsmp/component.rb +2 -0
- data/lib/rsmp/components.rb +1 -1
- data/lib/rsmp/convert/export/json_schema.rb +204 -0
- data/lib/rsmp/convert/import/yaml.rb +38 -0
- data/lib/rsmp/inspect.rb +46 -0
- data/lib/rsmp/listener.rb +4 -14
- data/lib/rsmp/logger.rb +3 -1
- data/lib/rsmp/logging.rb +1 -1
- data/lib/rsmp/message.rb +22 -31
- data/lib/rsmp/node.rb +11 -1
- data/lib/rsmp/notifier.rb +7 -2
- data/lib/rsmp/proxy.rb +69 -28
- data/lib/rsmp/rsmp.rb +34 -23
- data/lib/rsmp/site.rb +16 -8
- data/lib/rsmp/site_proxy.rb +86 -20
- data/lib/rsmp/site_proxy_wait.rb +63 -38
- data/lib/rsmp/supervisor.rb +47 -18
- data/lib/rsmp/supervisor_proxy.rb +18 -10
- data/lib/rsmp/tlc.rb +55 -33
- data/lib/rsmp/version.rb +1 -1
- data/rsmp.gemspec +3 -14
- metadata +12 -112
data/lib/rsmp/supervisor.rb
CHANGED
@@ -20,7 +20,7 @@ module RSMP
|
|
20
20
|
def handle_supervisor_settings options
|
21
21
|
@supervisor_settings = {
|
22
22
|
'port' => 12111,
|
23
|
-
'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4'],
|
23
|
+
'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4','3.1.5'],
|
24
24
|
'timer_interval' => 0.1,
|
25
25
|
'watchdog_interval' => 1,
|
26
26
|
'watchdog_timeout' => 2,
|
@@ -32,7 +32,9 @@ module RSMP
|
|
32
32
|
'site_ready_timeout' => 1,
|
33
33
|
'stop_after_first_session' => false,
|
34
34
|
'sites' => {
|
35
|
-
:any => {
|
35
|
+
:any => {
|
36
|
+
'sxl' => 'tlc'
|
37
|
+
}
|
36
38
|
}
|
37
39
|
}
|
38
40
|
|
@@ -47,17 +49,35 @@ module RSMP
|
|
47
49
|
check_required_settings @supervisor_settings, required
|
48
50
|
|
49
51
|
@rsmp_versions = @supervisor_settings["rsmp_versions"]
|
52
|
+
|
53
|
+
check_site_sxl_types
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_site_sxl_types
|
57
|
+
@supervisor_settings['sites'].each do |ip,settings|
|
58
|
+
unless settings
|
59
|
+
raise RSMP::ConfigurationError.new("Configuration for site '#{ip}' is empty")
|
60
|
+
end
|
61
|
+
sxl = settings['sxl']
|
62
|
+
sxl = 'tlc' unless sxl # temporary fix until configs are updated
|
63
|
+
unless sxl
|
64
|
+
raise RSMP::ConfigurationError.new("Configuration error for site '#{ip}': No SXL specified")
|
65
|
+
end
|
66
|
+
RSMP::Schemer.find_schemas! sxl if sxl
|
67
|
+
rescue RSMP::Schemer::UnknownSchemaError => e
|
68
|
+
raise RSMP::ConfigurationError.new("Configuration error for site '#{ip}': #{e}")
|
69
|
+
end
|
50
70
|
end
|
51
71
|
|
52
72
|
def start_action
|
53
73
|
@endpoint = Async::IO::Endpoint.tcp('0.0.0.0', @supervisor_settings["port"])
|
54
|
-
@endpoint.accept do |socket|
|
74
|
+
@endpoint.accept do |socket| # creates async tasks
|
55
75
|
handle_connection(socket)
|
76
|
+
rescue StandardError => e
|
77
|
+
notify_error e, level: :internal
|
56
78
|
end
|
57
|
-
rescue SystemCallError => e # all ERRNO errors
|
58
|
-
log "Exception: #{e.to_s}", level: :error
|
59
79
|
rescue StandardError => e
|
60
|
-
|
80
|
+
notify_error e, level: :internal
|
61
81
|
end
|
62
82
|
|
63
83
|
def stop
|
@@ -74,16 +94,17 @@ module RSMP
|
|
74
94
|
remote_hostname = socket.remote_address.ip_address
|
75
95
|
remote_ip = socket.remote_address.ip_address
|
76
96
|
|
77
|
-
info = {ip:remote_ip, port:remote_port, hostname:remote_hostname, now:
|
97
|
+
info = {ip:remote_ip, port:remote_port, hostname:remote_hostname, now:Clock.now}
|
78
98
|
if accept? socket, info
|
79
99
|
connect socket, info
|
80
100
|
else
|
81
101
|
reject socket, info
|
82
102
|
end
|
83
|
-
rescue
|
84
|
-
log "
|
103
|
+
rescue ConnectionError => e
|
104
|
+
log "Rejected connection from #{remote_ip}, #{e.to_s}", level: :info
|
85
105
|
rescue StandardError => e
|
86
|
-
log "
|
106
|
+
log "Connection: #{e.to_s}", exception: e, level: :error
|
107
|
+
notify_error e, level: :internal
|
87
108
|
ensure
|
88
109
|
close socket, info
|
89
110
|
end
|
@@ -91,7 +112,7 @@ module RSMP
|
|
91
112
|
def starting
|
92
113
|
log "Starting supervisor on port #{@supervisor_settings["port"]}",
|
93
114
|
level: :info,
|
94
|
-
timestamp:
|
115
|
+
timestamp: @clock.now
|
95
116
|
end
|
96
117
|
|
97
118
|
def accept? socket, info
|
@@ -115,11 +136,15 @@ module RSMP
|
|
115
136
|
ip: info[:ip],
|
116
137
|
port: info[:port],
|
117
138
|
level: :info,
|
118
|
-
timestamp:
|
139
|
+
timestamp: Clock.now
|
140
|
+
|
141
|
+
settings = ip_to_site_settings info[:ip]
|
142
|
+
raise ConnectionError.new('unknown ip not allowed') unless settings
|
119
143
|
|
120
|
-
settings = @supervisor_settings['sites'][info[:ip]] || @supervisor_settings['sites'][:any]
|
121
144
|
proxy = build_proxy({
|
122
145
|
supervisor: self,
|
146
|
+
ip: info[:ip],
|
147
|
+
port: info[:port],
|
123
148
|
task: @task,
|
124
149
|
settings: settings,
|
125
150
|
socket: socket,
|
@@ -146,9 +171,9 @@ module RSMP
|
|
146
171
|
|
147
172
|
def close socket, info
|
148
173
|
if info
|
149
|
-
log "Connection to #{format_ip_and_port(info)} closed", ip: info[:ip], level: :info, timestamp:
|
174
|
+
log "Connection to #{format_ip_and_port(info)} closed", ip: info[:ip], level: :info, timestamp: Clock.now
|
150
175
|
else
|
151
|
-
log "Connection closed", level: :info, timestamp:
|
176
|
+
log "Connection closed", level: :info, timestamp: Clock.now
|
152
177
|
end
|
153
178
|
|
154
179
|
socket.close
|
@@ -170,13 +195,13 @@ module RSMP
|
|
170
195
|
return site if site
|
171
196
|
wait_for(@site_id_condition,timeout) { find_site site_id }
|
172
197
|
rescue Async::TimeoutError
|
173
|
-
|
198
|
+
raise RSMP::TimeoutError.new "Site '#{site_id}'' did not connect within #{timeout}s"
|
174
199
|
end
|
175
200
|
|
176
201
|
def wait_for_site_disconnect site_id, timeout
|
177
202
|
wait_for(@site_id_condition,timeout) { true unless find_site site_id }
|
178
203
|
rescue Async::TimeoutError
|
179
|
-
|
204
|
+
raise RSMP::TimeoutError.new "Site '#{site_id}'' did not disconnect within #{timeout}s"
|
180
205
|
end
|
181
206
|
|
182
207
|
def check_site_id site_id
|
@@ -185,7 +210,7 @@ module RSMP
|
|
185
210
|
end
|
186
211
|
|
187
212
|
def check_site_already_connected site_id
|
188
|
-
raise FatalError.new "Site #{site_id} already connected" if find_site(site_id)
|
213
|
+
raise FatalError.new "Site '#{site_id}'' already connected" if find_site(site_id)
|
189
214
|
end
|
190
215
|
|
191
216
|
def find_allowed_site_setting site_id
|
@@ -198,6 +223,10 @@ module RSMP
|
|
198
223
|
raise FatalError.new "site id #{site_id} rejected"
|
199
224
|
end
|
200
225
|
|
226
|
+
def ip_to_site_settings ip
|
227
|
+
@supervisor_settings['sites'][ip] || @supervisor_settings['sites'][:any]
|
228
|
+
end
|
229
|
+
|
201
230
|
def aggregated_status_changed site_proxy, component
|
202
231
|
end
|
203
232
|
|
@@ -49,12 +49,12 @@ module RSMP
|
|
49
49
|
@endpoint = Async::IO::Endpoint.tcp(@ip, @port)
|
50
50
|
@socket = @endpoint.connect
|
51
51
|
@stream = Async::IO::Stream.new(@socket)
|
52
|
-
@protocol = Async::IO::Protocol::Line.new(@stream,
|
52
|
+
@protocol = Async::IO::Protocol::Line.new(@stream,WRAPPING_DELIMITER) # rsmp messages are json terminated with a form-feed
|
53
53
|
end
|
54
54
|
|
55
55
|
def connection_complete
|
56
56
|
super
|
57
|
-
log "Connection to supervisor established", level: :info
|
57
|
+
log "Connection to supervisor established, using core #{@rsmp_version}, #{sxl} #{sxl_version}", level: :info
|
58
58
|
start_watchdog
|
59
59
|
end
|
60
60
|
|
@@ -65,6 +65,8 @@ module RSMP
|
|
65
65
|
when StatusUpdate
|
66
66
|
when AggregatedStatus
|
67
67
|
will_not_handle message
|
68
|
+
when AggregatedStatusRequest
|
69
|
+
process_aggregated_status_request message
|
68
70
|
when CommandRequest
|
69
71
|
process_command_request message
|
70
72
|
when CommandResponse
|
@@ -123,7 +125,7 @@ module RSMP
|
|
123
125
|
|
124
126
|
def send_aggregated_status component
|
125
127
|
message = AggregatedStatus.new({
|
126
|
-
"aSTS" =>
|
128
|
+
"aSTS" => clock.to_s,
|
127
129
|
"cId" => component.c_id,
|
128
130
|
"fP" => 'NormalControl',
|
129
131
|
"fS" => nil,
|
@@ -161,6 +163,14 @@ module RSMP
|
|
161
163
|
sorted
|
162
164
|
end
|
163
165
|
|
166
|
+
def process_aggregated_status_request message
|
167
|
+
log "Received #{message.type}", message: message, level: :log
|
168
|
+
component_id = message.attributes["cId"]
|
169
|
+
component = @site.find_component component_id
|
170
|
+
acknowledge message
|
171
|
+
send_aggregated_status component
|
172
|
+
end
|
173
|
+
|
164
174
|
def process_command_request message
|
165
175
|
log "Received #{message.type}", message: message, level: :log
|
166
176
|
component_id = message.attributes["cId"]
|
@@ -177,7 +187,7 @@ module RSMP
|
|
177
187
|
end
|
178
188
|
response = CommandResponse.new({
|
179
189
|
"cId"=>component_id,
|
180
|
-
"cTS"=>
|
190
|
+
"cTS"=>clock.to_s,
|
181
191
|
"rvs"=>rvs
|
182
192
|
})
|
183
193
|
acknowledge message
|
@@ -194,7 +204,7 @@ module RSMP
|
|
194
204
|
end
|
195
205
|
response = StatusResponse.new({
|
196
206
|
"cId"=>component_id,
|
197
|
-
"sTs"=>
|
207
|
+
"sTs"=>clock.to_s,
|
198
208
|
"sS"=>sS,
|
199
209
|
"mId" => options[:m_id]
|
200
210
|
})
|
@@ -221,7 +231,7 @@ module RSMP
|
|
221
231
|
update_list[component] ||= {}
|
222
232
|
|
223
233
|
subs = @status_subscriptions[component]
|
224
|
-
now =
|
234
|
+
now = Time.now # internal timestamp
|
225
235
|
|
226
236
|
message.attributes["sS"].each do |arg|
|
227
237
|
sCI = arg["sCI"]
|
@@ -310,12 +320,10 @@ module RSMP
|
|
310
320
|
end
|
311
321
|
end
|
312
322
|
send_status_updates update_list
|
313
|
-
rescue StandardError => e
|
314
|
-
log ["Status update exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
|
315
323
|
end
|
316
324
|
|
317
325
|
def send_status_updates update_list
|
318
|
-
now =
|
326
|
+
now = clock.to_s
|
319
327
|
update_list.each_pair do |component_id,by_code|
|
320
328
|
component = @site.find_component component_id
|
321
329
|
sS = []
|
@@ -343,7 +351,7 @@ module RSMP
|
|
343
351
|
|
344
352
|
def send_alarm
|
345
353
|
message = Alarm.new({
|
346
|
-
"aSTS"=>
|
354
|
+
"aSTS"=>clock.to_s,
|
347
355
|
"fP"=>nil,
|
348
356
|
"fS"=>nil,
|
349
357
|
"se"=>@site.aggregated_status_bools
|
data/lib/rsmp/tlc.rb
CHANGED
@@ -13,7 +13,6 @@ module RSMP
|
|
13
13
|
@cycle_time = cycle_time
|
14
14
|
@num_traffic_situations = 1
|
15
15
|
@num_inputs = 8
|
16
|
-
|
17
16
|
reset
|
18
17
|
end
|
19
18
|
|
@@ -41,6 +40,11 @@ module RSMP
|
|
41
40
|
@input_results = '0'*@num_inputs
|
42
41
|
end
|
43
42
|
|
43
|
+
def clock
|
44
|
+
node.clock
|
45
|
+
end
|
46
|
+
|
47
|
+
|
44
48
|
def add_signal_group group
|
45
49
|
@signal_groups << group
|
46
50
|
end
|
@@ -48,8 +52,12 @@ module RSMP
|
|
48
52
|
def add_detector_logic logic
|
49
53
|
@detector_logics << logic
|
50
54
|
end
|
55
|
+
|
51
56
|
def timer now
|
52
|
-
|
57
|
+
# TODO
|
58
|
+
# We should use a monotone timer, to avoid jumps
|
59
|
+
# in case the user sets the system time
|
60
|
+
pos = Time.now.to_i % @cycle_time
|
53
61
|
if pos != @pos
|
54
62
|
@pos = pos
|
55
63
|
move pos
|
@@ -202,6 +210,17 @@ module RSMP
|
|
202
210
|
|
203
211
|
def handle_m0104 arg
|
204
212
|
@node.verify_security_code 1, arg['securityCode']
|
213
|
+
time = Time.new(
|
214
|
+
arg['year'],
|
215
|
+
arg['month'],
|
216
|
+
arg['day'],
|
217
|
+
arg['hour'],
|
218
|
+
arg['minute'],
|
219
|
+
arg['second'],
|
220
|
+
'UTC'
|
221
|
+
)
|
222
|
+
@node.clock.set time
|
223
|
+
log "Clock set to #{time}, (adjustment is #{@node.clock.adjustment}s)", level: :info
|
205
224
|
end
|
206
225
|
|
207
226
|
def set_input i, value
|
@@ -518,35 +537,37 @@ module RSMP
|
|
518
537
|
end
|
519
538
|
|
520
539
|
def handle_s0096 status_code, status_name=nil
|
540
|
+
now = clock.now
|
521
541
|
case status_name
|
522
542
|
when 'year'
|
523
|
-
RSMP::Tlc.make_status
|
543
|
+
RSMP::Tlc.make_status now.year.to_s.rjust(4, "0")
|
524
544
|
when 'month'
|
525
|
-
RSMP::Tlc.make_status
|
545
|
+
RSMP::Tlc.make_status now.month.to_s.rjust(2, "0")
|
526
546
|
when 'day'
|
527
|
-
RSMP::Tlc.make_status
|
547
|
+
RSMP::Tlc.make_status now.day.to_s.rjust(2, "0")
|
528
548
|
when 'hour'
|
529
|
-
RSMP::Tlc.make_status
|
549
|
+
RSMP::Tlc.make_status now.hour.to_s.rjust(2, "0")
|
530
550
|
when 'minute'
|
531
|
-
RSMP::Tlc.make_status
|
551
|
+
RSMP::Tlc.make_status now.min.to_s.rjust(2, "0")
|
532
552
|
when 'second'
|
533
|
-
RSMP::Tlc.make_status
|
553
|
+
RSMP::Tlc.make_status now.sec.to_s.rjust(2, "0")
|
534
554
|
end
|
535
555
|
end
|
536
556
|
|
537
557
|
def handle_s0097 status_code, status_name=nil
|
538
558
|
case status_name
|
539
|
-
when '
|
540
|
-
RSMP::Tlc.make_status '1'
|
541
|
-
when 'hash'
|
559
|
+
when 'checksum'
|
542
560
|
RSMP::Tlc.make_status '1'
|
561
|
+
when 'timestamp'
|
562
|
+
now = @node.clock.to_s
|
563
|
+
RSMP::Tlc.make_status now
|
543
564
|
end
|
544
565
|
end
|
545
566
|
|
546
567
|
def handle_s0205 status_code, status_name=nil
|
547
568
|
case status_name
|
548
569
|
when 'start'
|
549
|
-
RSMP::Tlc.make_status
|
570
|
+
RSMP::Tlc.make_status clock.to_s
|
550
571
|
when 'vehicles'
|
551
572
|
RSMP::Tlc.make_status 0
|
552
573
|
end
|
@@ -555,7 +576,7 @@ module RSMP
|
|
555
576
|
def handle_s0206 status_code, status_name=nil
|
556
577
|
case status_name
|
557
578
|
when 'start'
|
558
|
-
RSMP::Tlc.make_status
|
579
|
+
RSMP::Tlc.make_status clock.to_s
|
559
580
|
when 'speed'
|
560
581
|
RSMP::Tlc.make_status 0
|
561
582
|
end
|
@@ -564,7 +585,7 @@ module RSMP
|
|
564
585
|
def handle_s0207 status_code, status_name=nil
|
565
586
|
case status_name
|
566
587
|
when 'start'
|
567
|
-
RSMP::Tlc.make_status
|
588
|
+
RSMP::Tlc.make_status clock.to_s
|
568
589
|
when 'occupancy'
|
569
590
|
RSMP::Tlc.make_status 0
|
570
591
|
end
|
@@ -573,7 +594,7 @@ module RSMP
|
|
573
594
|
def handle_s0208 status_code, status_name=nil
|
574
595
|
case status_name
|
575
596
|
when 'start'
|
576
|
-
RSMP::Tlc.make_status
|
597
|
+
RSMP::Tlc.make_status clock.to_s
|
577
598
|
when 'P'
|
578
599
|
RSMP::Tlc.make_status 0
|
579
600
|
when 'PS'
|
@@ -653,21 +674,22 @@ module RSMP
|
|
653
674
|
end
|
654
675
|
|
655
676
|
def handle_s0025 status_code, status_name=nil
|
677
|
+
now = @node.clock.to_s
|
656
678
|
case status_name
|
657
679
|
when 'minToGEstimate'
|
658
|
-
RSMP::Tlc.make_status
|
680
|
+
RSMP::Tlc.make_status now
|
659
681
|
when 'maxToGEstimate'
|
660
|
-
RSMP::Tlc.make_status
|
682
|
+
RSMP::Tlc.make_status now
|
661
683
|
when 'likelyToGEstimate'
|
662
|
-
RSMP::Tlc.make_status
|
684
|
+
RSMP::Tlc.make_status now
|
663
685
|
when 'ToGConfidence'
|
664
686
|
RSMP::Tlc.make_status 0
|
665
687
|
when 'minToREstimate'
|
666
|
-
RSMP::Tlc.make_status
|
688
|
+
RSMP::Tlc.make_status now
|
667
689
|
when 'maxToREstimate'
|
668
|
-
RSMP::Tlc.make_status
|
690
|
+
RSMP::Tlc.make_status now
|
669
691
|
when 'likelyToREstimate'
|
670
|
-
RSMP::Tlc.make_status
|
692
|
+
RSMP::Tlc.make_status now
|
671
693
|
when 'ToRConfidence'
|
672
694
|
RSMP::Tlc.make_status 0
|
673
695
|
end
|
@@ -695,7 +717,7 @@ module RSMP
|
|
695
717
|
def handle_s0201 status_code, status_name=nil
|
696
718
|
case status_name
|
697
719
|
when 'starttime'
|
698
|
-
RSMP::Tlc.make_status
|
720
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
699
721
|
when 'vehicles'
|
700
722
|
RSMP::Tlc.make_status 0
|
701
723
|
end
|
@@ -704,7 +726,7 @@ module RSMP
|
|
704
726
|
def handle_s0202 status_code, status_name=nil
|
705
727
|
case status_name
|
706
728
|
when 'starttime'
|
707
|
-
RSMP::Tlc.make_status
|
729
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
708
730
|
when 'speed'
|
709
731
|
RSMP::Tlc.make_status 0
|
710
732
|
end
|
@@ -713,7 +735,7 @@ module RSMP
|
|
713
735
|
def handle_s0203 status_code, status_name=nil
|
714
736
|
case status_name
|
715
737
|
when 'starttime'
|
716
|
-
RSMP::Tlc.make_status
|
738
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
717
739
|
when 'occupancy'
|
718
740
|
RSMP::Tlc.make_status 0
|
719
741
|
end
|
@@ -722,7 +744,7 @@ module RSMP
|
|
722
744
|
def handle_s0204 status_code, status_name=nil
|
723
745
|
case status_name
|
724
746
|
when 'starttime'
|
725
|
-
RSMP::Tlc.make_status
|
747
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
726
748
|
when 'P'
|
727
749
|
RSMP::Tlc.make_status 0
|
728
750
|
when 'PS'
|
@@ -767,6 +789,7 @@ module RSMP
|
|
767
789
|
end
|
768
790
|
|
769
791
|
class Tlc < Site
|
792
|
+
attr_accessor :main
|
770
793
|
def initialize options={}
|
771
794
|
super options
|
772
795
|
@sxl = 'traffic_light_controller'
|
@@ -806,22 +829,21 @@ module RSMP
|
|
806
829
|
next_time = Time.now.to_f
|
807
830
|
loop do
|
808
831
|
begin
|
809
|
-
now
|
810
|
-
timer(now)
|
832
|
+
timer(@clock.now)
|
811
833
|
rescue EOFError => e
|
812
|
-
log "
|
834
|
+
log "Connection closed: #{e}", level: :warning
|
813
835
|
rescue IOError => e
|
814
|
-
log "
|
836
|
+
log "IOError", level: :warning
|
815
837
|
rescue Errno::ECONNRESET
|
816
|
-
log "
|
838
|
+
log "Connection reset by peer", level: :warning
|
817
839
|
rescue Errno::EPIPE => e
|
818
|
-
log "
|
840
|
+
log "Broken pipe", level: :warning
|
819
841
|
rescue StandardError => e
|
820
|
-
|
842
|
+
notify_error e, level: :internal
|
821
843
|
ensure
|
822
844
|
# adjust sleep duration to avoid drift. so wake up always happens on the
|
823
845
|
# same fractional second.
|
824
|
-
# note that Time.now is not monotonic. If the clock
|
846
|
+
# note that Time.now is not monotonic. If the clock is changed,
|
825
847
|
# either manaully or via NTP, the sleep interval might jump.
|
826
848
|
# an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
|
827
849
|
# to get the current time. this ensures a constant interval, but
|