cosmos 3.3.3 → 3.4.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 (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