cosmos 3.6.2 → 3.6.3

Sign up to get free protection for your applications and to get access to all the features.
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