cosmos 3.6.2 → 3.6.3

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/data/crc.txt +19 -19
  3. data/lib/cosmos/core_ext/array.rb +4 -1
  4. data/lib/cosmos/core_ext/matrix.rb +3 -0
  5. data/lib/cosmos/gui/dialogs/find_replace_dialog.rb +42 -35
  6. data/lib/cosmos/gui/line_graph/line_graph.rb +3 -0
  7. data/lib/cosmos/gui/line_graph/line_graph_drawing.rb +3 -1
  8. data/lib/cosmos/gui/opengl/gl_viewer.rb +115 -127
  9. data/lib/cosmos/gui/utilities/script_module_gui.rb +2 -5
  10. data/lib/cosmos/interfaces/linc_interface.rb +207 -147
  11. data/lib/cosmos/io/win32_serial_driver.rb +1 -1
  12. data/lib/cosmos/script/scripting.rb +2 -2
  13. data/lib/cosmos/script/tools.rb +8 -1
  14. data/lib/cosmos/tools/data_viewer/data_viewer.rb +12 -17
  15. data/lib/cosmos/tools/launcher/launcher_config.rb +1 -1
  16. data/lib/cosmos/tools/script_runner/script_runner.rb +9 -4
  17. data/lib/cosmos/tools/table_manager/table_manager.rb +3 -1
  18. data/lib/cosmos/tools/tlm_grapher/plot_editors/linegraph_plot_editor.rb +11 -1
  19. data/lib/cosmos/tools/tlm_grapher/plot_gui_objects/linegraph_plot_gui_object.rb +1 -0
  20. data/lib/cosmos/tools/tlm_grapher/plots/linegraph_plot.rb +8 -0
  21. data/lib/cosmos/version.rb +4 -4
  22. data/spec/core_ext/array_spec.rb +62 -1
  23. data/spec/core_ext/matrix_spec.rb +61 -0
  24. data/spec/interfaces/linc_interface_spec.rb +0 -9
  25. data/spec/script/script_spec.rb +7 -3
  26. data/spec/script/scripting_spec.rb +2 -2
  27. data/spec/script/tools_spec.rb +2 -0
  28. data/spec/spec_helper.rb +10 -1
  29. data/spec/tools/launcher/launcher_config_spec.rb +21 -11
  30. data/spec/top_level/top_level_spec.rb +1 -1
  31. metadata +2 -2
@@ -53,12 +53,9 @@ class Qt::MessageBox
53
53
  end
54
54
 
55
55
  class Qt::InputDialog
56
- def self.getText(parent, title, label,
57
- mode = Qt::LineEdit::Normal,
58
- text = '', ok = 0, flags = 0,
59
- inputMethodHints = Qt::ImhNone)
56
+ def self.getText(*args)
60
57
  Cosmos.play_wav_file(Cosmos.data_path('input.wav')) if Cosmos::System.sound
61
- super(parent, title, label, mode, text, ok, flags, inputMethodHints)
58
+ super(*args)
62
59
  end
63
60
  end
64
61
 
@@ -15,10 +15,10 @@ module Cosmos
15
15
 
16
16
  # Interface for connecting to Ball Aerospace LINC Labview targets
17
17
  class LincInterface < TcpipClientInterface
18
- # The maximum number of handshake responses we can wait for at a time.
18
+ # The maximum number of asynchronous commands we can wait for at a time.
19
19
  # We don't ever expect to get close to this but we need to limit it
20
20
  # to ensure the Array doesn't grow out of control.
21
- MAX_CONCURRENT_HANDSHAKES = 100
21
+ MAX_CONCURRENT_HANDSHAKES = 1000
22
22
 
23
23
  def initialize(
24
24
  hostname,
@@ -47,9 +47,8 @@ module Cosmos
47
47
 
48
48
  # Other instance variables
49
49
  @ignored_error_codes = []
50
- @handshakes = []
50
+ @handshake_cmds = []
51
51
  @handshakes_mutex = Mutex.new
52
- @handshakes_resource = ConditionVariable.new
53
52
 
54
53
  # Call this once now because the first time is slow
55
54
  UUIDTools::UUID.random_create.raw
@@ -83,7 +82,7 @@ module Cosmos
83
82
  end
84
83
 
85
84
  @handshakes_mutex.synchronize do
86
- @handshakes = []
85
+ @handshake_cmds = []
87
86
  end
88
87
 
89
88
  # Actually connect
@@ -91,16 +90,41 @@ module Cosmos
91
90
  end
92
91
 
93
92
  def write(packet)
94
- ######################################
95
- # Commands handled in the COSMOS interface not the LINC target
96
- ######################################
93
+ return if linc_interface_command(packet)
94
+ raise "Interface not connected" unless connected?()
97
95
 
96
+ # Add a GUID to the GUID field if its defined
97
+ # A GUID means it's an asychronous packet type.
98
+ if @fieldname_guid
99
+ guid = get_guid(packet)
100
+ else
101
+ # If @fieldname_guid is not defined (syncronous) we don't care what the
102
+ # GUID is because we're not trying to match it up with anything.
103
+ # As soon as we get a response we free the command.
104
+ guid = 0
105
+ end
106
+
107
+ # Fix the length field to handle the cases where a variable length packet
108
+ # is defined. COSMOS does not do this automatically.
109
+ update_length_field(packet) if @fieldname_cmd_length
110
+
111
+ # Always take the mutex (even if we aren't handshaking)
112
+ # We do not want any incoming telemetry to be missed because
113
+ # it could be the handshake to this command.
114
+ @handshakes_mutex.synchronize do
115
+ super(packet) # Send the command
116
+ wait_for_response(packet, guid) if @handshake_enabled
117
+ end
118
+ end
119
+
120
+ def linc_interface_command(packet)
121
+ result = false
98
122
  if @error_ignore_command and @error_ignore_command.identify?(packet.buffer(false))
99
123
  linc_cmd = @error_ignore_command.clone
100
124
  linc_cmd.buffer = packet.buffer
101
125
  code = linc_cmd.read('CODE')
102
126
  @ignored_error_codes << code unless @ignored_error_codes.include? code
103
- return
127
+ result = true
104
128
  end
105
129
 
106
130
  if @error_handle_command and @error_handle_command.identify?(packet.buffer(false))
@@ -108,125 +132,100 @@ module Cosmos
108
132
  linc_cmd.buffer = packet.buffer
109
133
  code = linc_cmd.read('CODE')
110
134
  @ignored_error_codes.delete(code) if @ignored_error_codes.include? code
111
- return
135
+ result = true
112
136
  end
113
137
 
114
138
  if @handshake_enable_command and @handshake_enable_command.identify?(packet.buffer(false))
115
139
  @handshake_enabled = true
116
- return
140
+ result = true
117
141
  end
118
142
 
119
143
  if @handshake_disable_command and @handshake_disable_command.identify?(packet.buffer(false))
120
144
  @handshake_enabled = false
121
- return
145
+ result = true
122
146
  end
147
+ return result
148
+ end
123
149
 
124
- # Verify we are connected to the LINC target
125
- if connected?()
126
- # Add a GUID to the GUID field if its defined
127
- if @fieldname_guid
128
- if not packet.read(@fieldname_guid) =~ /[\x01-\xFF]/
129
- # The GUID has not been set already (it has all \x00 values), so make a new one.
130
- # This enables a router GUI to make the GUIDs so it can process handshakes too.
131
- my_guid = UUIDTools::UUID.random_create.raw
132
- packet.write(@fieldname_guid, my_guid, :RAW)
133
- else
134
- my_guid = packet.read(@fieldname_guid)
135
- end
136
- end
150
+ def get_guid(packet)
151
+ if not packet.read(@fieldname_guid) =~ /[\x01-\xFF]/
152
+ # The GUID has not been set already (it has all \x00 values), so make a new one.
153
+ # This enables a router GUI to make the GUIDs so it can process handshakes too.
154
+ guid = UUIDTools::UUID.random_create.raw
155
+ packet.write(@fieldname_guid, guid, :RAW)
156
+ else
157
+ guid = packet.read(@fieldname_guid)
158
+ end
159
+ return guid
160
+ end
137
161
 
138
- # Fix the length field to handle the cases where a variable length packet
139
- # is defined. COSMOS does not do this automatically.
140
- if @fieldname_cmd_length
141
- my_length = packet.length - @length_value_offset
142
- packet.write(@fieldname_cmd_length, my_length, :RAW)
143
- end
162
+ def update_length_field(packet)
163
+ length = packet.length - @length_value_offset
164
+ packet.write(@fieldname_cmd_length, length, :RAW)
165
+ end
166
+
167
+ def wait_for_response(packet, guid)
168
+ # Check the number of commands waiting for handshakes. This is just for sanity
169
+ # If the number of commands waiting for handshakes is very large then it can't be real
170
+ # So raise an error. Something has gone horribly wrong.
171
+ if @handshake_cmds.length > MAX_CONCURRENT_HANDSHAKES
172
+ len = @handshake_cmds.length
173
+ @handshake_cmds = []
174
+ raise "The number of commands waiting for handshakes to #{len}. Clearing all commands!"
175
+ end
176
+
177
+ # Create a handshake command object and add it to the list of commands waiting
178
+ handshake_cmd = LincHandshakeCommand.new(@handshakes_mutex, guid)
179
+ @handshake_cmds.push(handshake_cmd)
180
+
181
+ # wait for that handshake. This releases the mutex so that the telemetry and other commands can start running again.
182
+ timed_out = handshake_cmd.wait_for_handshake(@response_timeout)
183
+ # We now have the mutex again. This interface is blocked for the rest of the command handling,
184
+ # which should be quick because it's just checking variables and logging.
185
+ # We want to hold the mutex during that so that the commands get logged in order of handshake from here.
186
+
187
+ if timed_out
188
+ # Clean this command out of the array of items that require handshakes.
189
+ @handshake_cmds.delete_if {|hsc| hsc == handshake_cmd}
190
+ raise "Timeout waiting for handshake from #{System.commands.format(packet, System.targets[@target_names[0]].ignored_parameters)}"
191
+ end
144
192
 
145
- # Always take the mutex (even if we aren't handshaking)
146
- @handshakes_mutex.synchronize do
147
-
148
- # Send the command
149
- super(packet)
150
-
151
- # Wait for the response if handshaking
152
- if @handshake_enabled
153
- begin
154
- my_handshake = nil
155
-
156
- # The time after which we will give up waiting for a response
157
- deadline = Time.now + @response_timeout
158
-
159
- # Loop until we find our response
160
- while my_handshake.nil?
161
- # How long should we wait in this loop. We could get notified
162
- # multiple times for handshakes that aren't ours so we need to
163
- # recalculate the wait time each time.
164
- to_wait = deadline - Time.now
165
-
166
- # If there is no more time to wait then this is a timeout so we break
167
- # out of the loop looking for handshakes
168
- if to_wait <= 0
169
- raise "Timeout waiting for handshake from #{System.commands.format(packet, System.targets[@target_names[0]].ignored_parameters)}"
170
- end
171
-
172
- # Wait until the telemetry side signals that there is a new handshake to check or timeout.
173
- # This releases the mutex until the telemetry side signals us
174
- @handshakes_resource.wait(@handshakes_mutex, to_wait)
175
- # We now have the mutex again
176
-
177
- # Loop if we have timed out so we can handle the timeout with common code
178
- next if (deadline - Time.now) <= 0
179
-
180
- if @fieldname_guid
181
- # A GUID means it's an asychronous packet type.
182
- # So look at the list of incoming handshakes and pick off (deleting)
183
- # the handshake from the list if it's for this command.
184
- #
185
- # The mutex is required because the telemetry task
186
- # could enqueue a response between the index lookup and the slice
187
- # function which would remove the wrong response. FAIL!
188
- my_handshake_index = @handshakes.index {|hs| hs.get_cmd_guid(@fieldname_guid) == my_guid}
189
- my_handshake = @handshakes.slice!(my_handshake_index) if my_handshake_index
190
- else
191
- # This is the synchronous version
192
- my_handshake = @handshakes.pop
193
- end
194
- end # while my_handshake.nil?
195
-
196
- # Handle handshake warnings and errors
197
- if my_handshake.handshake.read('STATUS') == "OK" and my_handshake.handshake.read('CODE') != 0
198
- unless @ignored_error_codes.include? my_handshake.handshake.read('CODE')
199
- Logger.warn "Warning sending command (#{my_handshake.handshake.read('CODE')}): #{my_handshake.error_source}"
200
- end
201
- elsif my_handshake.handshake.read('STATUS') == "ERROR"
202
- unless @ignored_error_codes.include? my_handshake.handshake.read('CODE')
203
- raise "Error sending command (#{my_handshake.handshake.read('CODE')}): #{my_handshake.error_source}"
204
- end
205
- end
206
- rescue Exception => err
207
- # If anything goes wrong after successfully writing the packet to the LINC target
208
- # ensure that the packet gets updated in the CVT and logged to the packet log writer.
209
- # COSMOS normally only does this if write returns successfully
210
- if packet.identified?
211
- command = System.commands.packet(packet.target_name, packet.packet_name)
212
- else
213
- command = System.commands.packet('UNKNOWN', 'UNKNOWN')
214
- end
215
- command.buffer = packet.buffer
216
-
217
- @packet_log_writer_pairs.each do |packet_log_writer_pair|
218
- packet_log_writer_pair.cmd_log_writer.write(packet)
219
- end
220
-
221
- raise err
222
- end
223
- end # if @handshake_enabled
224
- end # @handshakes_mutex.synchronize
193
+ process_handshake_results(handshake_cmd)
194
+
195
+ rescue Exception => err
196
+ # If anything goes wrong after successfully writing the packet to the LINC target
197
+ # ensure that the packet gets updated in the CVT and logged to the packet log writer.
198
+ # COSMOS normally only does this if write returns successfully
199
+ if packet.identified?
200
+ command = System.commands.packet(packet.target_name, packet.packet_name)
225
201
  else
226
- raise "Interface not connected"
227
- end # if connected
202
+ command = System.commands.packet('UNKNOWN', 'UNKNOWN')
203
+ end
204
+ command.buffer = packet.buffer
205
+
206
+ @packet_log_writer_pairs.each do |packet_log_writer_pair|
207
+ packet_log_writer_pair.cmd_log_writer.write(packet)
208
+ end
228
209
 
229
- end # def write
210
+ raise err
211
+ end
212
+
213
+ def process_handshake_results(handshake_cmd)
214
+ status = handshake_cmd.handshake.handshake.read('STATUS')
215
+ code = handshake_cmd.handshake.handshake.read('CODE')
216
+ source = handshake_cmd.handshake.error_source
217
+
218
+ # Handle handshake warnings and errors
219
+ if status == "OK" and code != 0
220
+ unless @ignored_error_codes.include? code
221
+ Logger.warn "Warning sending command (#{code}): #{source}"
222
+ end
223
+ elsif status == "ERROR"
224
+ unless @ignored_error_codes.include? code
225
+ raise "Error sending command (#{code}): #{source}"
226
+ end
227
+ end
228
+ end
230
229
 
231
230
  def read
232
231
  packet = super()
@@ -234,40 +233,13 @@ module Cosmos
234
233
  if @handshake_packet.identify?(packet.buffer(false))
235
234
  handshake_packet = @handshake_packet.clone
236
235
  handshake_packet.buffer = packet.buffer
237
- my_handshake = LincHandshake.new(handshake_packet, @target_names[0])
236
+ linc_handshake = LincHandshake.new(handshake_packet, @target_names[0])
238
237
 
238
+ # Check for a local handshake
239
239
  if handshake_packet.read('origin') == "LCL"
240
- # Update the current value table for this command
241
- command = System.commands.packet(my_handshake.identified_command.target_name, my_handshake.identified_command.packet_name)
242
- command.received_time = my_handshake.identified_command.received_time
243
- command.buffer = my_handshake.identified_command.buffer
244
- command.received_count += 1
245
-
246
- # Put a log of the command onto the server for the user to see
247
- Logger.info("External Command: " + System.commands.format(my_handshake.identified_command, System.targets[@target_names[0]].ignored_parameters))
248
-
249
- # Log the command to the command log(s)
250
- @packet_log_writer_pairs.each do |packet_log_writer_pair|
251
- packet_log_writer_pair.cmd_log_writer.write(my_handshake.identified_command)
252
- end
240
+ handle_local_handshake(linc_handshake)
253
241
  else
254
- # This is a remote packet (sent from here).
255
- # Add to the array of handshake packet responses (only if handshakes are enabled).
256
- # The mutex is required by the command task due to the way it
257
- # first looks up the handshake before removing it.
258
- if @handshake_enabled
259
- @handshakes_mutex.synchronize do
260
- @handshakes.push(my_handshake)
261
- if @handshakes.length > MAX_CONCURRENT_HANDSHAKES
262
- len = @handshakes.length
263
- @handshakes = []
264
- raise "The handshakes response array has grown to #{len}. Clearing all handshakes!"
265
- end
266
- # Tell all waiting commands to take a look
267
- @handshakes_resource.broadcast
268
- end # @handshakes_mutex.synchronize
269
- end # if @handshake_enabled
270
-
242
+ handle_remote_handshake(linc_handshake) if @handshake_enabled
271
243
  end # if handshake_packet.read('origin') == "LCL"
272
244
  end # @handshake_packet.identify?(packet.buffer(false))
273
245
  end # if packet
@@ -275,9 +247,98 @@ module Cosmos
275
247
  return packet
276
248
  end
277
249
 
250
+ def handle_local_handshake(linc_handshake)
251
+ # Update the current value table for this command
252
+ command = System.commands.packet(linc_handshake.identified_command.target_name, linc_handshake.identified_command.packet_name)
253
+ command.received_time = linc_handshake.identified_command.received_time
254
+ command.buffer = linc_handshake.identified_command.buffer
255
+ command.received_count += 1
256
+
257
+ # Put a log of the command onto the server for the user to see
258
+ Logger.info("External Command: " + System.commands.format(linc_handshake.identified_command, System.targets[@target_names[0]].ignored_parameters))
259
+
260
+ # Log the command to the command log(s)
261
+ @packet_log_writer_pairs.each do |packet_log_writer_pair|
262
+ packet_log_writer_pair.cmd_log_writer.write(linc_handshake.identified_command)
263
+ end
264
+ end
265
+
266
+ def handle_remote_handshake(linc_handshake)
267
+ # This is a remote packet (sent from here).
268
+ # Add to the array of handshake packet responses
269
+ # The mutex is required by the command task due to the way it
270
+ # first looks up the handshake before removing it.
271
+ @handshakes_mutex.synchronize do
272
+ if @fieldname_guid
273
+ # A GUID means it's an asychronous packet type.
274
+ # So look at the list of incoming handshakes and pick off (deleting)
275
+ # the handshake from the list if it's for this command.
276
+ #
277
+ # The mutex is required because the telemetry task
278
+ # could enqueue a response between the index lookup and the slice
279
+ # function which would remove the wrong response. FAIL!
280
+
281
+ # Loop through all waiting commands to see if this handshake belongs to them
282
+ this_handshake_guid = linc_handshake.get_cmd_guid(@fieldname_guid)
283
+ handshake_cmd_index = @handshake_cmds.index {|hsc| hsc.get_cmd_guid == this_handshake_guid}
284
+
285
+ # If command was waiting (ie the loop above found one), then remove it from waiters and signal it
286
+ if handshake_cmd_index
287
+ handshake_cmd = @handshake_cmds.slice!(handshake_cmd_index)
288
+ handshake_cmd.got_your_handshake(linc_handshake)
289
+ else
290
+ # No command match found! Either it gave up and timed out or this wasn't originated from here.
291
+ # Ignore this typically. This case here for clarity.
292
+ end
293
+
294
+ else
295
+ # Synchronous version: just pop the array (pull the command off) and send it the handshake
296
+ handshake_cmd = @handshakes_cmds.pop
297
+ handshake_cmd.got_your_handshake(linc_handshake)
298
+ end # of handshaking type check
299
+ end # @handshakes_mutex.synchronize
300
+ end
301
+
278
302
  end # class LincInterface
279
303
 
280
- # The LincHandshake class is used only by the LincInterface. It processes the handshake and
304
+ # The LincHandshakeCommand class is used only by the LincInterface.
305
+ # It is the command with other required items that is passed to the telemetry
306
+ # thread so it can match it with the handshake.
307
+ class LincHandshakeCommand
308
+ attr_accessor :handshake
309
+
310
+ def initialize(handshakes_mutex,cmd_guid)
311
+ @cmd_guid = cmd_guid
312
+ @handshakes_mutex = handshakes_mutex
313
+ @resource = ConditionVariable.new
314
+ @handshake = nil
315
+ end
316
+
317
+ def wait_for_handshake(response_timeout)
318
+ timed_out = false
319
+
320
+ @resource.wait(@handshakes_mutex,response_timeout)
321
+ if @handshake
322
+ timed_out = false
323
+ else
324
+ Logger.warn "No handshake - must be timeout."
325
+ timed_out = true
326
+ end
327
+
328
+ return timed_out
329
+ end
330
+
331
+ def got_your_handshake(handshake)
332
+ @handshake = handshake
333
+ @resource.signal
334
+ end
335
+
336
+ def get_cmd_guid
337
+ return @cmd_guid
338
+ end
339
+ end # class LincHandshakeCommand
340
+
341
+ # The LincHandshake class is used only by the LincInterface. It processes the handshake and
281
342
  # helps with finding the information regarding the internal command.
282
343
  class LincHandshake
283
344
  attr_accessor :handshake
@@ -354,7 +415,6 @@ module Cosmos
354
415
  def get_cmd_guid(fieldname_guid)
355
416
  return @identified_command.read(fieldname_guid)
356
417
  end
357
-
358
418
  end # class LincHandshake
359
419
 
360
420
  end # module Cosmos
@@ -29,7 +29,7 @@ module Cosmos
29
29
  # Verify Parameters
30
30
  port_name = '\\\\.\\' + port_name if port_name =~ /^COM[0-9]{2,3}$/
31
31
 
32
- raise(ArgumentError, "Invalid baud rate: #{baud_rate}") unless Win32::BAUD_RATES.include?(baud_rate)
32
+ raise(ArgumentError, "Invalid baud rate: #{baud_rate}") unless baud_rate.between?(Win32::BAUD_RATES[0], Win32::BAUD_RATES[-1])
33
33
 
34
34
  raise(ArgumentError, "Invalid parity: #{parity}") if parity and !SerialDriver::VALID_PARITY.include?(parity)
35
35
  case parity