cosmos 3.3.3 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.travis.yml +2 -1
  4. data/Gemfile +4 -3
  5. data/Manifest.txt +22 -0
  6. data/autohotkey/tools/handbook_creator.ahk +9 -0
  7. data/autohotkey/tools/packet_viewer.ahk +4 -0
  8. data/bin/exchndl20-x64.dll +0 -0
  9. data/bin/exchndl20.dll +0 -0
  10. data/bin/exchndl21-x64.dll +0 -0
  11. data/bin/exchndl21.dll +0 -0
  12. data/bin/exchndl22-x64.dll +0 -0
  13. data/bin/exchndl22.dll +0 -0
  14. data/bin/mgwhelp-x64.dll +0 -0
  15. data/bin/mgwhelp.dll +0 -0
  16. data/cosmos.gemspec +1 -0
  17. data/data/crc.txt +30 -24
  18. data/demo/config/data/crc.txt +3 -3
  19. data/demo/config/tools/handbook_creator/templates/command_packets.html.erb +3 -1
  20. data/demo/config/tools/handbook_creator/templates/telemetry_packets.html.erb +3 -1
  21. data/demo/procedures/cosmos_api_test.rb +1 -1
  22. data/ext/cosmos/ext/low_fragmentation_array/low_fragmentation_array.c +4 -0
  23. data/ext/cosmos/ext/platform/platform.c +22 -2
  24. data/ext/cosmos/ext/structure/structure.c +631 -104
  25. data/ext/cosmos/ext/telemetry/telemetry.c +3 -2
  26. data/lib/cosmos/gui/line_graph/line_graph_drawing.rb +71 -92
  27. data/lib/cosmos/gui/line_graph/overview_graph.rb +1 -1
  28. data/lib/cosmos/gui/qt.rb +38 -24
  29. data/lib/cosmos/gui/text/ruby_editor.rb +1 -1
  30. data/lib/cosmos/packets/binary_accessor.rb +1 -288
  31. data/lib/cosmos/packets/telemetry.rb +2 -1
  32. data/lib/cosmos/script/cmd_tlm_server.rb +110 -0
  33. data/lib/cosmos/script/commands.rb +166 -0
  34. data/lib/cosmos/script/extract.rb +2 -2
  35. data/lib/cosmos/script/limits.rb +108 -0
  36. data/lib/cosmos/script/script.rb +28 -1487
  37. data/lib/cosmos/script/scripting.rb +889 -0
  38. data/lib/cosmos/script/telemetry.rb +174 -0
  39. data/lib/cosmos/script/tools.rb +138 -0
  40. data/lib/cosmos/streams/stream_protocol.rb +9 -6
  41. data/lib/cosmos/system/target.rb +55 -38
  42. data/lib/cosmos/tools/cmd_tlm_server/api.rb +6 -3
  43. data/lib/cosmos/tools/cmd_tlm_server/connections.rb +0 -1
  44. data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +17 -7
  45. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +15 -4
  46. data/lib/cosmos/tools/handbook_creator/handbook_creator.rb +15 -8
  47. data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +41 -13
  48. data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +18 -1
  49. data/lib/cosmos/tools/tlm_viewer/widgets/canvasline_widget.rb +1 -1
  50. data/lib/cosmos/tools/tlm_viewer/widgets/canvaslinevalue_widget.rb +1 -1
  51. data/lib/cosmos/tools/tlm_viewer/widgets/limitsbar_widget.rb +1 -1
  52. data/lib/cosmos/tools/tlm_viewer/widgets/rangebar_widget.rb +1 -1
  53. data/lib/cosmos/top_level.rb +1 -1
  54. data/lib/cosmos/utilities/ruby_lex_utils.rb +1 -1
  55. data/lib/cosmos/version.rb +5 -5
  56. data/spec/gui/line_graph/line_clip_spec.rb +6 -6
  57. data/spec/gui/qt_spec.rb +102 -0
  58. data/spec/interfaces/interface_spec.rb +9 -9
  59. data/spec/interfaces/linc_interface_spec.rb +72 -15
  60. data/spec/interfaces/serial_interface_spec.rb +9 -9
  61. data/spec/interfaces/simulated_target_interface_spec.rb +7 -7
  62. data/spec/interfaces/stream_interface_spec.rb +4 -4
  63. data/spec/interfaces/tcpip_client_interface_spec.rb +8 -8
  64. data/spec/interfaces/tcpip_server_interface_spec.rb +9 -9
  65. data/spec/interfaces/udp_interface_spec.rb +20 -20
  66. data/spec/io/json_drb_spec.rb +4 -4
  67. data/spec/io/raw_logger_pair_spec.rb +20 -20
  68. data/spec/io/raw_logger_spec.rb +3 -3
  69. data/spec/io/tcpip_server_spec.rb +9 -9
  70. data/spec/io/udp_sockets_spec.rb +2 -2
  71. data/spec/io/win32_serial_driver_spec.rb +2 -2
  72. data/spec/packets/binary_accessor_spec.rb +143 -6
  73. data/spec/packets/commands_spec.rb +5 -5
  74. data/spec/packets/limits_spec.rb +15 -15
  75. data/spec/packets/packet_config_spec.rb +19 -19
  76. data/spec/packets/packet_item_limits_spec.rb +3 -3
  77. data/spec/packets/packet_item_spec.rb +4 -4
  78. data/spec/packets/packet_spec.rb +33 -33
  79. data/spec/packets/structure_item_spec.rb +19 -19
  80. data/spec/packets/telemetry_spec.rb +6 -6
  81. data/spec/script/cmd_tlm_server_spec.rb +110 -0
  82. data/spec/script/commands_disconnect_spec.rb +270 -0
  83. data/spec/script/commands_spec.rb +288 -0
  84. data/spec/script/limits_spec.rb +153 -0
  85. data/spec/script/script_spec.rb +32 -696
  86. data/spec/script/scripting_spec.rb +436 -0
  87. data/spec/script/telemetry_spec.rb +130 -0
  88. data/spec/script/tools_spec.rb +117 -0
  89. data/spec/spec_helper.rb +10 -5
  90. data/spec/streams/preidentified_stream_protocol_spec.rb +4 -4
  91. data/spec/streams/serial_stream_spec.rb +8 -8
  92. data/spec/streams/stream_protocol_spec.rb +4 -4
  93. data/spec/streams/tcpip_client_stream_spec.rb +3 -3
  94. data/spec/streams/tcpip_socket_stream_spec.rb +7 -7
  95. data/spec/streams/template_stream_protocol_spec.rb +1 -1
  96. data/spec/system/system_spec.rb +6 -6
  97. data/spec/system/target_spec.rb +2 -0
  98. data/spec/tools/cmd_tlm_server/api_spec.rb +17 -17
  99. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +5 -5
  100. data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +3 -3
  101. data/spec/top_level/top_level_spec.rb +8 -8
  102. data/spec/utilities/csv_spec.rb +3 -3
  103. data/spec/utilities/message_log_spec.rb +3 -3
  104. data/spec/utilities/ruby_lex_utils_spec.rb +7 -7
  105. data/test/performance/config/tools/launcher/launcher_threads.txt +8 -1
  106. data/test/performance/tools/CmdTlmServerMemProf +1 -1
  107. data/test/performance/tools/TlmGrapherMemProf +19 -0
  108. data/test/performance/tools/TlmGrapherMemProf.bat +59 -0
  109. metadata +38 -2
@@ -0,0 +1,889 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2014 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+
11
+ require 'cosmos/script/extract'
12
+
13
+ module Cosmos
14
+ module Script
15
+ DEFAULT_TLM_POLLING_RATE = 0.25
16
+
17
+ private
18
+ # Include various methods to extract fields from text
19
+ include Extract
20
+
21
+ def play_wav_file(wav_filename)
22
+ if defined? Qt
23
+ Qt.execute_in_main_thread(true) do
24
+ if Qt::CoreApplication.instance and Qt::Sound.isAvailable
25
+ Cosmos.set_working_dir do
26
+ Qt::Sound.play(wav_filename.to_s)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def status_bar(message)
34
+ script_runner = nil
35
+ ObjectSpace.each_object {|object| if ScriptRunner === object then script_runner = object; break; end}
36
+ script_runner.script_set_status(message) if script_runner
37
+ end
38
+
39
+ def ask_string(question, blank_or_default = false, password = false)
40
+ answer = ''
41
+ default = ''
42
+ if blank_or_default != true && blank_or_default != false
43
+ question << " (default = #{blank_or_default})"
44
+ allow_blank = true
45
+ else
46
+ allow_blank = blank_or_default
47
+ end
48
+ while answer.empty?
49
+ print question + " "
50
+ answer = gets
51
+ answer.chomp!
52
+ break if allow_blank
53
+ end
54
+ answer = default if answer.empty? and !default.empty?
55
+ return answer
56
+ end
57
+
58
+ def ask(question, blank_or_default = false, password = false)
59
+ string = ask_string(question, blank_or_default, password)
60
+ value = string.convert_to_value
61
+ return value
62
+ end
63
+
64
+ def prompt(string)
65
+ prompt_to_continue(string)
66
+ end
67
+
68
+ def message_box(string, *buttons)
69
+ prompt_message_box(string, buttons)
70
+ end
71
+
72
+ # Creates a string with the parameters upcased
73
+ def _upcase(target_name, packet_name, item_name)
74
+ "#{target_name.upcase} #{packet_name.upcase} #{item_name.upcase}"
75
+ end
76
+
77
+ # Implementaiton of the various check commands. It yields back to the
78
+ # caller to allow the return of the value through various telemetry calls.
79
+ # This method should not be called directly by application code.
80
+ def _check(*args)
81
+ target_name, packet_name, item_name, comparison_to_eval = check_process_args(args, 'check')
82
+ value = yield(target_name, packet_name, item_name)
83
+ if comparison_to_eval
84
+ check_eval(target_name, packet_name, item_name, comparison_to_eval, value)
85
+ else
86
+ Logger.info "CHECK: #{_upcase(target_name, packet_name, item_name)} == #{value}"
87
+ end
88
+ end
89
+
90
+ # Check the converted value of a telmetry item against a condition
91
+ # Always print the value of the telemetry item to STDOUT
92
+ # If the condition check fails, raise an error
93
+ # Supports two signatures:
94
+ # check(target_name, packet_name, item_name, comparison_to_eval)
95
+ # or
96
+ # check('target_name packet_name item_name > 1')
97
+ def check(*args)
98
+ _check(*args) {|tgt,pkt,item| tlm(tgt,pkt,item) }
99
+ end
100
+
101
+ # Check the formatted value of a telmetry item against a condition
102
+ # Always print the value of the telemetry item to STDOUT
103
+ # If the condition check fails, raise an error
104
+ # Supports two signatures:
105
+ # check(target_name, packet_name, item_name, comparison_to_eval)
106
+ # or
107
+ # check('target_name packet_name item_name > 1')
108
+ def check_formatted(*args)
109
+ _check(*args) {|tgt,pkt,item| tlm_formatted(tgt,pkt,item) }
110
+ end
111
+
112
+ # Check the formatted with units value of a telmetry item against a condition
113
+ # Always print the value of the telemetry item to STDOUT
114
+ # If the condition check fails, raise an error
115
+ # Supports two signatures:
116
+ # check(target_name, packet_name, item_name, comparison_to_eval)
117
+ # or
118
+ # check('target_name packet_name item_name > 1')
119
+ def check_with_units(*args)
120
+ _check(*args) {|tgt,pkt,item| tlm_with_units(tgt,pkt,item) }
121
+ end
122
+
123
+ # Check the raw value of a telmetry item against a condition
124
+ # Always print the value of the telemetry item to STDOUT
125
+ # If the condition check fails, raise an error
126
+ # Supports two signatures:
127
+ # check(target_name, packet_name, item_name, comparison_to_eval)
128
+ # or
129
+ # check('target_name packet_name item_name > 1')
130
+ def check_raw(*args)
131
+ _check(*args) {|tgt,pkt,item| tlm_raw(tgt,pkt,item) }
132
+ end
133
+
134
+ def _check_tolerance(*args)
135
+ target_name, packet_name, item_name, expected_value, tolerance =
136
+ check_tolerance_process_args(args, 'check_tolerance')
137
+ value = yield(target_name, packet_name, item_name)
138
+ range = (expected_value - tolerance)..(expected_value + tolerance)
139
+ check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)}"
140
+ range_str = "range #{range.first} to #{range.last} with value == #{value}"
141
+ if range.include?(value)
142
+ Logger.info "#{check_str} was within #{range_str}"
143
+ else
144
+ message = "#{check_str} failed to be within #{range_str}"
145
+ if $cmd_tlm_disconnect
146
+ Logger.error message
147
+ else
148
+ raise CheckError, message
149
+ end
150
+ end
151
+ end
152
+
153
+ # Check the converted value of a telmetry item against an expected value with a tolerance
154
+ # Always print the value of the telemetry item to STDOUT
155
+ # If the condition check fails, raise an error
156
+ # Supports two signatures:
157
+ # check_tolerance(target_name, packet_name, item_name, expected_value, tolerance)
158
+ # or
159
+ # check_tolerance('target_name packet_name item_name', expected_value, tolerance)
160
+ def check_tolerance(*args)
161
+ _check_tolerance(*args) {|tgt,pkt,item| tlm(tgt,pkt,item) }
162
+ end
163
+
164
+ # Check the raw value of a telmetry item against an expected value with a tolerance
165
+ # Always print the value of the telemetry item to STDOUT
166
+ # If the condition check fails, raise an error
167
+ # Supports two signatures:
168
+ # check_tolerance_raw(target_name, packet_name, item_name, expected_value, tolerance)
169
+ # or
170
+ # check_tolerance_raw('target_name packet_name item_name', expected_value, tolerance)
171
+ def check_tolerance_raw(*args)
172
+ _check_tolerance(*args) {|tgt,pkt,item| tlm_raw(tgt,pkt,item) }
173
+ end
174
+
175
+ # Check to see if an expression is true without waiting. If the expression
176
+ # is not true, the script will pause.
177
+ def check_expression(exp_to_eval, context = nil)
178
+ success = cosmos_script_wait_implementation_expression(exp_to_eval, 0, DEFAULT_TLM_POLLING_RATE, context)
179
+ if success
180
+ Logger.info "CHECK: #{exp_to_eval} is TRUE"
181
+ else
182
+ message = "CHECK: #{exp_to_eval} is FALSE"
183
+ if $cmd_tlm_disconnect
184
+ Logger.error message
185
+ else
186
+ raise CheckError, message
187
+ end
188
+ end
189
+ end
190
+
191
+ # Wait on an expression to be true. On a timeout, the script will continue.
192
+ # Supports multiple signatures:
193
+ # wait(time)
194
+ # wait('target_name packet_name item_name > 1', timeout, polling_rate)
195
+ # wait('target_name', 'packet_name', 'item_name', comparison_to_eval, timeout, polling_rate)
196
+ def wait(*args)
197
+ wait_process_args(args, 'wait', :CONVERTED)
198
+ end
199
+
200
+ # Wait on an expression to be true. On a timeout, the script will continue.
201
+ # Supports multiple signatures:
202
+ # wait(time)
203
+ # wait_raw('target_name packet_name item_name > 1', timeout, polling_rate)
204
+ # wait_raw('target_name', 'packet_name', 'item_name', comparison_to_eval, timeout, polling_rate)
205
+ def wait_raw(*args)
206
+ wait_process_args(args, 'wait_raw', :RAW)
207
+ end
208
+
209
+ def _wait_tolerance(raw, *args)
210
+ type = (raw ? :RAW : :CONVERTED)
211
+ type_string = 'wait_tolerance'
212
+ type_string << '_raw' if raw
213
+ target_name, packet_name, item_name, expected_value, tolerance, timeout, polling_rate = wait_tolerance_process_args(args, type_string)
214
+ start_time = Time.now
215
+ success, value = cosmos_script_wait_implementation_tolerance(target_name, packet_name, item_name, type, expected_value, tolerance, timeout, polling_rate)
216
+ time = Time.now - start_time
217
+ range = (expected_value - tolerance)..(expected_value + tolerance)
218
+ wait_str = "WAIT: #{_upcase(target_name, packet_name, item_name)}"
219
+ range_str = "range #{range.first} to #{range.last} with value == #{value} after waiting #{time} seconds"
220
+ if success
221
+ Logger.info "#{wait_str} was within #{range_str}"
222
+ else
223
+ Logger.warn "#{wait_str} failed to be within #{range_str}"
224
+ end
225
+ time
226
+ end
227
+
228
+ # Wait on an expression to be true. On a timeout, the script will continue.
229
+ # Supports multiple signatures:
230
+ # wait_tolerance('target_name packet_name item_name', expected_value, tolerance, timeout, polling_rate)
231
+ # wait_tolerance('target_name', 'packet_name', 'item_name', expected_value, tolerance, timeout, polling_rate)
232
+ def wait_tolerance(*args)
233
+ _wait_tolerance(false, *args)
234
+ end
235
+
236
+ # Wait on an expression to be true. On a timeout, the script will continue.
237
+ # Supports multiple signatures:
238
+ # wait_tolerance_raw('target_name packet_name item_name', expected_value, tolerance, timeout, polling_rate)
239
+ # wait_tolerance_raw('target_name', 'packet_name', 'item_name', expected_value, tolerance, timeout, polling_rate)
240
+ def wait_tolerance_raw(*args)
241
+ _wait_tolerance(true, *args)
242
+ end
243
+
244
+ # Wait on a custom expression to be true
245
+ def wait_expression(exp_to_eval, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE, context = nil)
246
+ start_time = Time.now
247
+ success = cosmos_script_wait_implementation_expression(exp_to_eval, timeout, polling_rate, context)
248
+ time = Time.now - start_time
249
+ if success
250
+ Logger.info "WAIT: #{exp_to_eval} is TRUE after waiting #{time} seconds"
251
+ else
252
+ Logger.warn "WAIT: #{exp_to_eval} is FALSE after waiting #{time} seconds"
253
+ end
254
+ time
255
+ end
256
+
257
+ def _wait_check(raw, *args)
258
+ type = (raw ? :RAW : :CONVERTED)
259
+ target_name, packet_name, item_name, comparison_to_eval, timeout, polling_rate = wait_check_process_args(args, 'wait_check')
260
+ start_time = Time.now
261
+ success, value = cosmos_script_wait_implementation(target_name, packet_name, item_name, type, comparison_to_eval, timeout, polling_rate)
262
+ time = Time.now - start_time
263
+ check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}"
264
+ with_value_str = "with value == #{value} after waiting #{time} seconds"
265
+ if success
266
+ Logger.info "#{check_str} success #{with_value_str}"
267
+ else
268
+ message = "#{check_str} failed #{with_value_str}"
269
+ if $cmd_tlm_disconnect
270
+ Logger.error message
271
+ else
272
+ raise CheckError, message
273
+ end
274
+ end
275
+ time
276
+ end
277
+
278
+ # Wait for the converted value of a telmetry item against a condition or for a timeout
279
+ # and then check against the condition
280
+ # Supports two signatures:
281
+ # wait_check(target_name, packet_name, item_name, comparison_to_eval, timeout, polling_rate)
282
+ # or
283
+ # wait_check('target_name packet_name item_name > 1', timeout, polling_rate)
284
+ def wait_check(*args)
285
+ _wait_check(false, *args)
286
+ end
287
+
288
+ # Wait for the raw value of a telmetry item against a condition or for a timeout
289
+ # and then check against the condition
290
+ # Supports two signatures:
291
+ # wait_check_raw(target_name, packet_name, item_name, comparison_to_eval, timeout, polling_rate)
292
+ # or
293
+ # wait_check_raw('target_name packet_name item_name > 1', timeout, polling_rate)
294
+ def wait_check_raw(*args)
295
+ _wait_check(true, *args)
296
+ end
297
+
298
+ def _wait_check_tolerance(raw, *args)
299
+ type_string = 'wait_check_tolerance'
300
+ type_string << '_raw' if raw
301
+ type = (raw ? :RAW : :CONVERTED)
302
+ target_name, packet_name, item_name, expected_value, tolerance, timeout, polling_rate = wait_tolerance_process_args(args, type_string)
303
+ start_time = Time.now
304
+ success, value = cosmos_script_wait_implementation_tolerance(target_name, packet_name, item_name, type, expected_value, tolerance, timeout, polling_rate)
305
+ time = Time.now - start_time
306
+ range = (expected_value - tolerance)..(expected_value + tolerance)
307
+ check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)}"
308
+ range_str = "range #{range.first} to #{range.last} with value == #{value} after waiting #{time} seconds"
309
+ if success
310
+ Logger.info "#{check_str} was within #{range_str}"
311
+ else
312
+ message = "#{check_str} failed to be within #{range_str}"
313
+ if $cmd_tlm_disconnect
314
+ Logger.error message
315
+ else
316
+ raise CheckError, message
317
+ end
318
+ end
319
+ time
320
+ end
321
+
322
+ def wait_check_tolerance(*args)
323
+ _wait_check_tolerance(false, *args)
324
+ end
325
+
326
+ def wait_check_tolerance_raw(*args)
327
+ _wait_check_tolerance(true, *args)
328
+ end
329
+
330
+ # Wait on an expression to be true. On a timeout, the script will pause.
331
+ def wait_check_expression(exp_to_eval,
332
+ timeout,
333
+ polling_rate = DEFAULT_TLM_POLLING_RATE,
334
+ context = nil)
335
+ start_time = Time.now
336
+ success = cosmos_script_wait_implementation_expression(exp_to_eval,
337
+ timeout,
338
+ polling_rate,
339
+ context)
340
+ time = Time.now - start_time
341
+ if success
342
+ Logger.info "CHECK: #{exp_to_eval} is TRUE after waiting #{time} seconds"
343
+ else
344
+ message = "CHECK: #{exp_to_eval} is FALSE after waiting #{time} seconds"
345
+ if $cmd_tlm_disconnect
346
+ Logger.error message
347
+ else
348
+ raise CheckError, message
349
+ end
350
+ end
351
+ time
352
+ end
353
+ alias wait_expression_stop_on_timeout wait_check_expression
354
+
355
+ # Wait for a telemetry packet to be received a certain number of times or timeout
356
+ def _wait_packet(check,
357
+ target_name,
358
+ packet_name,
359
+ num_packets,
360
+ timeout,
361
+ polling_rate = DEFAULT_TLM_POLLING_RATE)
362
+ type = (check ? 'CHECK' : 'WAIT')
363
+ initial_count = tlm(target_name, packet_name, 'RECEIVED_COUNT')
364
+ start_time = Time.now
365
+ success, value = cosmos_script_wait_implementation(target_name,
366
+ packet_name,
367
+ 'RECEIVED_COUNT',
368
+ :CONVERTED,
369
+ ">= #{initial_count + num_packets}",
370
+ timeout,
371
+ polling_rate)
372
+ time = Time.now - start_time
373
+ if success
374
+ Logger.info "#{type}: #{target_name.upcase} #{packet_name.upcase} received #{value - initial_count} times after waiting #{time} seconds"
375
+ else
376
+ message = "#{type}: #{target_name.upcase} #{packet_name.upcase} expected to be received #{num_packets} times but only received #{value - initial_count} times after waiting #{time} seconds"
377
+ if check
378
+ if $cmd_tlm_disconnect
379
+ Logger.error message
380
+ else
381
+ raise CheckError, message
382
+ end
383
+ else
384
+ Logger.warn message
385
+ end
386
+ end
387
+ time
388
+ end
389
+
390
+ def wait_packet(target_name,
391
+ packet_name,
392
+ num_packets,
393
+ timeout,
394
+ polling_rate = DEFAULT_TLM_POLLING_RATE)
395
+ _wait_packet(false, target_name, packet_name, num_packets, timeout, polling_rate)
396
+ end
397
+
398
+ # Wait for a telemetry packet to be received a certain number of times or timeout and raise an error
399
+ def wait_check_packet(target_name,
400
+ packet_name,
401
+ num_packets,
402
+ timeout,
403
+ polling_rate = DEFAULT_TLM_POLLING_RATE)
404
+ _wait_packet(true, target_name, packet_name, num_packets, timeout, polling_rate)
405
+ end
406
+
407
+ def _get_procedure_path(procedure_name)
408
+ # Handle not-giving an extension
409
+ procedure_name_with_extension = nil
410
+ procedure_name_with_extension = procedure_name + '.rb' if File.extname(procedure_name).empty?
411
+
412
+ path = nil
413
+
414
+ # Find filename in search path
415
+ ($:).each do |directory|
416
+ if File.exist?(directory + '/' + procedure_name) and not File.directory?(directory + '/' + procedure_name)
417
+ path = directory + '/' + procedure_name
418
+ break
419
+ end
420
+
421
+ if procedure_name_with_extension and File.exist?(directory + '/' + procedure_name_with_extension)
422
+ procedure_name = procedure_name_with_extension
423
+ path = directory + '/' + procedure_name
424
+ break
425
+ end
426
+ end
427
+
428
+ # Handle absolute path
429
+ path = procedure_name if !path and File.exist?(procedure_name)
430
+ path = procedure_name_with_extension if !path and procedure_name_with_extension and File.exist?(procedure_name_with_extension)
431
+
432
+ raise "Procedure not found : #{procedure_name}" unless path
433
+ path
434
+ end
435
+
436
+ def check_file_cache_for_instrumented_script(path, md5)
437
+ instrumented_script = nil
438
+ cached = true
439
+ use_file_cache = true
440
+
441
+ Cosmos.set_working_dir do
442
+ cache_path = File.join(System.paths['TMP'], 'script_runner')
443
+ unless File.directory?(cache_path)
444
+ # Try to create .cache directory
445
+ begin
446
+ Dir.mkdir(cache_path)
447
+ rescue
448
+ use_file_cache = false
449
+ end
450
+ end
451
+
452
+ cache_filename = nil
453
+ if use_file_cache
454
+ # Check file based instrumented cache
455
+ flat_path = path.tr("/", "_").gsub("\\", "_").tr(":", "_").tr(" ", "_")
456
+ flat_path_with_md5 = flat_path + '_' + md5
457
+ cache_filename = File.join(cache_path, flat_path_with_md5)
458
+ end
459
+
460
+ if use_file_cache and File.exist?(cache_filename)
461
+ # Use file cached instrumentation
462
+ File.open(cache_filename, 'r') {|file| instrumented_script = file.read}
463
+ else
464
+ cached = false
465
+
466
+ # Build instrumentation
467
+ file_text = ''
468
+ begin
469
+ file_text = File.read(path)
470
+ rescue Exception => error
471
+ raise "Error reading procedure file : #{path}"
472
+ end
473
+
474
+ instrumented_script = ScriptRunnerFrame.instrument_script(file_text, path, true)
475
+
476
+ # Cache instrumentation into file
477
+ if use_file_cache
478
+ begin
479
+ File.open(cache_filename, 'w') {|file| file.write(instrumented_script)}
480
+ rescue
481
+ # Oh well, failed to write cache file
482
+ end
483
+ end
484
+ end
485
+ end
486
+ [instrumented_script, cached]
487
+ end
488
+
489
+ def start(procedure_name)
490
+ cached = true
491
+ path = _get_procedure_path(procedure_name)
492
+
493
+ if defined? ScriptRunnerFrame and ScriptRunnerFrame.instance
494
+ md5 = nil
495
+ begin
496
+ md5 = Cosmos.md5_files([path]).hexdigest
497
+ rescue Exception => error
498
+ raise "Error calculating md5 on procedure file : #{path}"
499
+ end
500
+
501
+ # Check RAM based instrumented cache
502
+ instrumented_cache = ScriptRunnerFrame.instrumented_cache[path]
503
+ instrumented_script = nil
504
+ if instrumented_cache and md5 == instrumented_cache[1]
505
+ # Use cached instrumentation
506
+ instrumented_script = instrumented_cache[0]
507
+ else
508
+ instrumented_script, cached = check_file_cache_for_instrumented_script(path, md5)
509
+ # Cache instrumentation into RAM
510
+ ScriptRunnerFrame.instrumented_cache[path] = [instrumented_script, md5]
511
+ end
512
+
513
+ Object.class_eval(instrumented_script, path, 1)
514
+ else # No ScriptRunnerFrame so just start it locally
515
+ cached = false
516
+ begin
517
+ Kernel::load(path)
518
+ rescue LoadError => error
519
+ raise RuntimeError.new("Error loading : #{procedure_name} : #{error.message}")
520
+ end
521
+ end
522
+ # Return whether we had to load and instrument this file, i.e. it was not cached
523
+ !cached
524
+ end
525
+
526
+ # Require an additional ruby file
527
+ def load_utility(procedure_name)
528
+ cached = true
529
+ if defined? ScriptRunnerFrame and ScriptRunnerFrame.instance
530
+ saved = ScriptRunnerFrame.instance.use_instrumentation
531
+ begin
532
+ ScriptRunnerFrame.instance.use_instrumentation = false
533
+ cached = start(procedure_name)
534
+ ensure
535
+ ScriptRunnerFrame.instance.use_instrumentation = saved
536
+ end
537
+ else # Just call start
538
+ cached = start(procedure_name)
539
+ end
540
+ # Return whether we had to load and instrument this file, i.e. it was not cached
541
+ # This is designed to match the behavior of Ruby's require and load keywords
542
+ !cached
543
+ end
544
+ alias require_utility load_utility
545
+
546
+ ##########################################
547
+ # Protected Methods
548
+ ##########################################
549
+
550
+ def check_process_args(args, function_name)
551
+ case args.length
552
+ when 1
553
+ target_name, packet_name, item_name, comparison_to_eval = extract_fields_from_check_text(args[0])
554
+ when 4
555
+ target_name = args[0]
556
+ packet_name = args[1]
557
+ item_name = args[2]
558
+ comparison_to_eval = args[3]
559
+ else
560
+ # Invalid number of arguments
561
+ raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{function_name}()"
562
+ end
563
+ return [target_name, packet_name, item_name, comparison_to_eval]
564
+ end
565
+
566
+ def check_tolerance_process_args(args, function_name)
567
+ case args.length
568
+ when 3
569
+ target_name, packet_name, item_name = extract_fields_from_tlm_text(args[0])
570
+ expected_value = args[1]
571
+ tolerance = args[2]
572
+ when 5
573
+ target_name = args[0]
574
+ packet_name = args[1]
575
+ item_name = args[2]
576
+ expected_value = args[3]
577
+ tolerance = args[4]
578
+ else
579
+ # Invalid number of arguments
580
+ raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{function_name}()"
581
+ end
582
+ return [target_name, packet_name, item_name, expected_value, tolerance]
583
+ end
584
+
585
+ def _execute_wait(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate)
586
+ start_time = Time.now
587
+ success, value = cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate)
588
+ time = Time.now - start_time
589
+ wait_str = "WAIT: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}"
590
+ value_str = "with value == #{value} after waiting #{time} seconds"
591
+ if success
592
+ Logger.info "#{wait_str} success #{value_str}"
593
+ else
594
+ Logger.warn "#{wait_str} failed #{value_str}"
595
+ end
596
+ end
597
+
598
+ def wait_process_args(args, function_name, value_type)
599
+ time = nil
600
+
601
+ case args.length
602
+ when 0
603
+ start_time = Time.now
604
+ cosmos_script_sleep()
605
+ time = Time.now - start_time
606
+ Logger.info("WAIT: Indefinite for actual time of #{time} seconds")
607
+ when 1
608
+ if args[0].kind_of? Numeric
609
+ start_time = Time.now
610
+ cosmos_script_sleep(args[0])
611
+ time = Time.now - start_time
612
+ Logger.info("WAIT: #{args[0]} seconds with actual time of #{time} seconds")
613
+ else
614
+ raise "Non-numeric wait time specified"
615
+ end
616
+ when 2, 3
617
+ target_name, packet_name, item_name, comparison_to_eval = extract_fields_from_check_text(args[0])
618
+ timeout = args[1]
619
+ if args.length == 3
620
+ polling_rate = args[2]
621
+ else
622
+ polling_rate = DEFAULT_TLM_POLLING_RATE
623
+ end
624
+ _execute_wait(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate)
625
+
626
+ when 5, 6
627
+ target_name = args[0]
628
+ packet_name = args[1]
629
+ item_name = args[2]
630
+ comparison_to_eval = args[3]
631
+ timeout = args[4]
632
+ if args.length == 6
633
+ polling_rate = args[5]
634
+ else
635
+ polling_rate = DEFAULT_TLM_POLLING_RATE
636
+ end
637
+ _execute_wait(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate)
638
+
639
+ else
640
+ # Invalid number of arguments
641
+ raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{function_name}()"
642
+ end
643
+ time
644
+ end
645
+
646
+ def wait_tolerance_process_args(args, function_name)
647
+ case args.length
648
+ when 4, 5
649
+ target_name, packet_name, item_name = extract_fields_from_tlm_text(args[0])
650
+ expected_value = args[1]
651
+ tolerance = args[2]
652
+ timeout = args[3]
653
+ if args.length == 5
654
+ polling_rate = args[4]
655
+ else
656
+ polling_rate = DEFAULT_TLM_POLLING_RATE
657
+ end
658
+ when 6, 7
659
+ target_name = args[0]
660
+ packet_name = args[1]
661
+ item_name = args[2]
662
+ expected_value = args[3]
663
+ tolerance = args[4]
664
+ timeout = args[5]
665
+ if args.length == 7
666
+ polling_rate = args[6]
667
+ else
668
+ polling_rate = DEFAULT_TLM_POLLING_RATE
669
+ end
670
+ else
671
+ # Invalid number of arguments
672
+ raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{function_name}()"
673
+ end
674
+ return [target_name, packet_name, item_name, expected_value, tolerance, timeout, polling_rate]
675
+ end
676
+
677
+ def wait_check_process_args(args, function_name)
678
+ case args.length
679
+ when 2, 3
680
+ target_name, packet_name, item_name, comparison_to_eval = extract_fields_from_check_text(args[0])
681
+ timeout = args[1]
682
+ if args.length == 3
683
+ polling_rate = args[2]
684
+ else
685
+ polling_rate = DEFAULT_TLM_POLLING_RATE
686
+ end
687
+ when 5, 6
688
+ target_name = args[0]
689
+ packet_name = args[1]
690
+ item_name = args[2]
691
+ comparison_to_eval = args[3]
692
+ timeout = args[4]
693
+ if args.length == 6
694
+ polling_rate = args[5]
695
+ else
696
+ polling_rate = DEFAULT_TLM_POLLING_RATE
697
+ end
698
+ else
699
+ # Invalid number of arguments
700
+ raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{function_name}()"
701
+ end
702
+ return [target_name, packet_name, item_name, comparison_to_eval, timeout, polling_rate]
703
+ end
704
+
705
+ # sleep in a script - returns true if canceled mid sleep
706
+ def cosmos_script_sleep(sleep_time = nil)
707
+ return false if $cmd_tlm_disconnect
708
+ if defined? ScriptRunnerFrame and ScriptRunnerFrame.instance
709
+ sleep_time = 30000000 unless sleep_time # Handle infinite wait
710
+ if sleep_time > 0.0
711
+ end_time = Time.now + sleep_time
712
+ until (Time.now >= end_time)
713
+ sleep(0.01)
714
+ if ScriptRunnerFrame.instance.pause?
715
+ ScriptRunnerFrame.instance.perform_pause
716
+ return true
717
+ end
718
+ return true if ScriptRunnerFrame.instance.go?
719
+ raise StopScript if ScriptRunnerFrame.instance.stop?
720
+ end
721
+ end
722
+ else
723
+ if sleep_time
724
+ sleep(sleep_time)
725
+ else
726
+ print 'Infinite Wait - Press Enter to Continue: '
727
+ gets()
728
+ end
729
+ end
730
+ return false
731
+ end
732
+
733
+ def _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate)
734
+ end_time = Time.now + timeout
735
+ exp_to_eval = yield
736
+
737
+ while true
738
+ work_start = Time.now
739
+ value = tlm_variable(target_name, packet_name, item_name, value_type)
740
+ if eval(exp_to_eval)
741
+ return true, value
742
+ end
743
+ break if Time.now >= end_time || $cmd_tlm_disconnect
744
+
745
+ delta = Time.now - work_start
746
+ sleep_time = polling_rate - delta
747
+ end_delta = end_time - Time.now
748
+ sleep_time = end_delta if end_delta < sleep_time
749
+ sleep_time = 0 if sleep_time < 0
750
+ canceled = cosmos_script_sleep(sleep_time)
751
+
752
+ if canceled
753
+ value = tlm_variable(target_name, packet_name, item_name, value_type)
754
+ if eval(exp_to_eval)
755
+ return true, value
756
+ else
757
+ return false, value
758
+ end
759
+ end
760
+ end
761
+
762
+ return false, value
763
+ end
764
+
765
+ # Wait for a converted telemetry item to pass a comparison
766
+ def cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, comparison_to_eval, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE)
767
+ _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate) do
768
+ "value " + comparison_to_eval
769
+ end
770
+ end
771
+
772
+ def cosmos_script_wait_implementation_tolerance(target_name, packet_name, item_name, value_type, expected_value, tolerance, timeout, polling_rate = DEFAULT_TLM_POLLING_RATE)
773
+ _cosmos_script_wait_implementation(target_name, packet_name, item_name, value_type, timeout, polling_rate) do
774
+ "((#{expected_value} - #{tolerance})..(#{expected_value} + #{tolerance})).include? value"
775
+ end
776
+ end
777
+
778
+ # Wait on an expression to be true.
779
+ def cosmos_script_wait_implementation_expression(exp_to_eval, timeout, polling_rate, context)
780
+ end_time = Time.now + timeout
781
+ context = ScriptRunnerFrame.instance.script_binding if !context and defined? ScriptRunnerFrame and ScriptRunnerFrame.instance
782
+
783
+ while true
784
+ work_start = Time.now
785
+ if eval(exp_to_eval, context)
786
+ return true
787
+ end
788
+ break if Time.now >= end_time
789
+
790
+ delta = Time.now - work_start
791
+ sleep_time = polling_rate - delta
792
+ end_delta = end_time - Time.now
793
+ sleep_time = end_delta if end_delta < sleep_time
794
+ sleep_time = 0 if sleep_time < 0
795
+ canceled = cosmos_script_sleep(sleep_time)
796
+
797
+ if canceled
798
+ if eval(exp_to_eval, context)
799
+ return true
800
+ else
801
+ return nil
802
+ end
803
+ end
804
+ end
805
+
806
+ return nil
807
+ end
808
+
809
+ def check_eval(target_name, packet_name, item_name, comparison_to_eval, value)
810
+ string = "value " + comparison_to_eval
811
+ check_str = "CHECK: #{_upcase(target_name, packet_name, item_name)} #{comparison_to_eval}"
812
+ value_str = "with value == #{value}"
813
+ if eval(string)
814
+ Logger.info "#{check_str} success #{value_str}"
815
+ else
816
+ message = "#{check_str} failed #{value_str}"
817
+ if $cmd_tlm_disconnect
818
+ Logger.error message
819
+ else
820
+ raise CheckError, message
821
+ end
822
+ end
823
+ end
824
+
825
+ def build_cmd_output_string(target_name, cmd_name, cmd_params, raw = false)
826
+ if raw
827
+ output_string = 'cmd_raw("'
828
+ else
829
+ output_string = 'cmd("'
830
+ end
831
+ output_string << target_name + ' ' + cmd_name
832
+ if cmd_params.nil? or cmd_params.empty?
833
+ output_string << '")'
834
+ else
835
+ params = []
836
+ cmd_params.each do |key, value|
837
+ if value.class == String
838
+ if value.convert_to_value.class == String
839
+ value = value.inspect
840
+ if value.length > 256
841
+ value = value[0..255] + "...'"
842
+ end
843
+ value.tr!('"',"'")
844
+ end
845
+ end
846
+ params << "#{key} #{value}"
847
+ end
848
+ params = params.join(", ")
849
+ output_string << ' with ' + params + '")'
850
+ end
851
+ return output_string
852
+ end
853
+
854
+ def prompt_for_hazardous(target_name, cmd_name, hazardous_description)
855
+ message = "Warning: Command #{target_name} #{cmd_name} is Hazardous. "
856
+ message << "\n#{hazardous_description}\n" if hazardous_description
857
+ message << "Send? (y,n): "
858
+ print message
859
+ answer = gets.chomp
860
+ if answer.downcase == 'y'
861
+ return true
862
+ else
863
+ return false
864
+ end
865
+ end
866
+
867
+ def prompt_for_script_abort
868
+ print "Stop running script? (y,n): "
869
+ answer = gets.chomp
870
+ if answer.downcase == 'y'
871
+ exit
872
+ else
873
+ return false # Not aborted - Retry
874
+ end
875
+ end
876
+
877
+ def prompt_to_continue(string)
878
+ print "#{string}: "
879
+ gets.chomp
880
+ end
881
+
882
+ def prompt_message_box(string, buttons)
883
+ print "#{string} (#{buttons.join(", ")}): "
884
+ gets.chomp
885
+ end
886
+
887
+ end # module Script
888
+
889
+ end # module Cosmos