cosmos 3.1.1 → 3.1.2

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Manifest.txt +6 -0
  3. data/autohotkey/config/tools/handbook_creator/templates/command_packets.html.erb +1 -1
  4. data/autohotkey/config/tools/handbook_creator/templates/footer.html.erb +2 -2
  5. data/autohotkey/config/tools/handbook_creator/templates/header.html.erb +4 -4
  6. data/autohotkey/tools/autohotkey.rb +1 -1
  7. data/autohotkey/tools/cmd_tlm_server.ahk +1 -0
  8. data/autohotkey/tools/packet_viewer.ahk +45 -2
  9. data/data/crc.txt +20 -15
  10. data/demo/Rakefile +16 -0
  11. data/demo/config/data/crc.txt +3 -3
  12. data/demo/config/tools/handbook_creator/templates/footer.html.erb +2 -2
  13. data/demo/config/tools/handbook_creator/templates/header.html.erb +4 -4
  14. data/demo/procedures/example_test.rb +1 -1
  15. data/install/Rakefile +16 -0
  16. data/install/config/data/crc.txt +2 -2
  17. data/install/config/tools/handbook_creator/templates/footer.html.erb +2 -2
  18. data/install/config/tools/handbook_creator/templates/header.html.erb +4 -4
  19. data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +64 -57
  20. data/lib/cosmos/gui/line_graph/line_graph_scaling.rb +0 -9
  21. data/lib/cosmos/gui/qt.rb +22 -18
  22. data/lib/cosmos/packet_logs/packet_log_writer.rb +6 -2
  23. data/lib/cosmos/script/script.rb +1 -1
  24. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +0 -1
  25. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +99 -784
  26. data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +189 -0
  27. data/lib/cosmos/tools/cmd_tlm_server/gui/logging_tab.rb +176 -0
  28. data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +144 -0
  29. data/lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb +240 -0
  30. data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +90 -0
  31. data/lib/cosmos/tools/launcher/launcher_config.rb +142 -110
  32. data/lib/cosmos/tools/test_runner/results_writer.rb +1 -1
  33. data/lib/cosmos/tools/test_runner/test_runner.rb +3 -2
  34. data/lib/cosmos/tools/tlm_grapher/data_objects/data_object.rb +18 -2
  35. data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +4 -9
  36. data/lib/cosmos/tools/tlm_grapher/data_objects/xy_data_object.rb +5 -5
  37. data/lib/cosmos/top_level.rb +1 -1
  38. data/lib/cosmos/version.rb +4 -4
  39. data/run_gui_tests.bat +33 -31
  40. data/spec/core_ext/time_spec.rb +51 -0
  41. data/spec/script/script_spec.rb +96 -0
  42. data/spec/tools/cmd_tlm_server/commanding_spec.rb +28 -0
  43. data/spec/tools/cmd_tlm_server/connections_spec.rb +88 -0
  44. data/spec/tools/cmd_tlm_server/router_thread_spec.rb +78 -25
  45. data/spec/tools/launcher/launcher_config_spec.rb +460 -0
  46. data/spec/top_level/top_level_spec.rb +1 -1
  47. metadata +8 -2
@@ -275,14 +275,6 @@ module Cosmos
275
275
  @x_grid_lines = []
276
276
 
277
277
  @x_grid_line_scale = determine_grid_line_scale(@manual_x_grid_line_scale, @x_min, @x_max)
278
- if @manual_x_grid_line_scale
279
- @x_grid_line_scale = @manual_x_grid_line_scale
280
- else
281
- diff = @x_max - @x_min
282
- @x_grid_line_scale = calculate_base(diff)
283
- end
284
- @x_grid_line_scale = 1.0 if @x_grid_line_scale < 1
285
-
286
278
  if @show_x_grid_lines
287
279
  states = @lines.single_line_with_x_states
288
280
  if states
@@ -451,7 +443,6 @@ module Cosmos
451
443
  diff = max - min
452
444
  scale = calculate_base(diff)
453
445
  end
454
- scale = 1.0 if scale < 1
455
446
  return scale
456
447
  end
457
448
 
@@ -670,44 +670,46 @@ end
670
670
 
671
671
  class Qt::Painter
672
672
  def addLineColor(x, y, w, h, color = Cosmos::BLACK)
673
- color = Cosmos::getColor(color)
674
- setPen(color)
673
+ setPenColor(color)
675
674
  drawLine(x,y,w,h)
676
675
  end
677
676
 
678
677
  def addRectColor(x, y, w, h, color = Cosmos::BLACK)
679
- color = Cosmos::getColor(color)
680
- setPen(color)
678
+ setPenColor(color)
681
679
  setBrush(nil)
682
680
  drawRect(x,y,w,h)
683
681
  end
684
682
 
685
683
  def addRectColorFill(x, y, w, h, color = Cosmos::BLACK)
686
- color = Cosmos::getColor(color)
687
- setPen(color)
684
+ setPenColor(color)
688
685
  setBrush(Cosmos.getBrush(color))
689
686
  drawRect(x,y,w,h)
690
687
  end
691
688
 
692
689
  def addSimpleTextAt(text, x, y, color = Cosmos::BLACK)
693
- color = Cosmos::getColor(color)
694
- setPen(color)
695
- drawText(x, y, text)
690
+ setPenColor(color)
691
+ drawText(x,y,text)
696
692
  end
697
693
 
698
694
  def addEllipseColor(x, y, w, h, color = Cosmos::BLACK)
699
- color = Cosmos::getColor(color)
700
- setPen(color)
695
+ setPenColor(color)
701
696
  setBrush(nil)
702
697
  drawEllipse(x,y,w,h)
703
698
  end
704
699
 
705
700
  def addEllipseColorFill(x, y, w, h, color = Cosmos::BLACK)
706
- color = Cosmos::getColor(color)
707
- setPen(color)
701
+ setPenColor(color)
708
702
  setBrush(Cosmos.getBrush(color))
709
703
  drawEllipse(x,y,w,h)
710
704
  end
705
+
706
+ private
707
+
708
+ def setPenColor(color)
709
+ color = Cosmos::getColor(color)
710
+ setPen(color)
711
+ end
712
+
711
713
  end
712
714
 
713
715
  class Qt::MatrixLayout < Qt::GridLayout
@@ -720,15 +722,17 @@ class Qt::MatrixLayout < Qt::GridLayout
720
722
 
721
723
  def addWidget(widget)
722
724
  super(widget, @row, @col)
723
- @col += 1 if @col < @num_columns
724
- if @col == @num_columns
725
- @row += 1
726
- @col = 0
727
- end
725
+ increment_row_col()
728
726
  end
729
727
 
730
728
  def addLayout(layout)
731
729
  super(layout, @row, @col)
730
+ increment_row_col()
731
+ end
732
+
733
+ private
734
+
735
+ def increment_row_col
732
736
  @col += 1 if @col < @num_columns
733
737
  if @col == @num_columns
734
738
  @row += 1
@@ -278,8 +278,12 @@ module Cosmos
278
278
 
279
279
  def cycle_thread_body
280
280
  while true
281
- if @logging_enabled and @cycle_time and (Time.now - @start_time) > @cycle_time
282
- close_file()
281
+ # The check against start_time needs to be mutex protected to prevent a packet coming in between the check
282
+ # and closing the file
283
+ @mutex.synchronize do
284
+ if @logging_enabled and @cycle_time and (Time.now - @start_time) > @cycle_time
285
+ close_file(false)
286
+ end
283
287
  end
284
288
  # Only check whether to cycle at a set interval
285
289
  break if @cycle_sleeper.sleep(CYCLE_TIME_INTERVAL)
@@ -926,7 +926,7 @@ module Cosmos
926
926
  tlm_viewer.clear(display_name)
927
927
  tlm_viewer.disconnect
928
928
  rescue DRb::DRbConnError
929
- raise "Unable to Successfully Start Listening Telemetry Viewer: #{display_name} could not be displayed"
929
+ raise "Unable to Successfully Start Listening Telemetry Viewer: #{display_name} could not be cleared"
930
930
  rescue Errno::ENOENT
931
931
  raise "Display Screen File: #{display_name}.txt does not exist"
932
932
  end
@@ -108,7 +108,6 @@ module Cosmos
108
108
  parser.verify_num_parameters(1, 2, usage)
109
109
  target = System.targets[params[0].upcase]
110
110
  raise parser.error("Unknown target: #{params[0].upcase}") unless target
111
- raise parser.error("Cannot use #{keyword} with target name substitutions: #{target.name} != #{target.original_name}") if target.name != target.original_name
112
111
  target_filename = params[1]
113
112
  target_filename = 'cmd_tlm_server.txt' unless target_filename
114
113
  target_filename = File.join(target.dir, target_filename)
@@ -10,9 +10,13 @@
10
10
 
11
11
  require 'cosmos'
12
12
  require 'cosmos/tools/cmd_tlm_server/cmd_tlm_server'
13
+ require 'cosmos/tools/cmd_tlm_server/gui/interfaces_tab'
14
+ require 'cosmos/tools/cmd_tlm_server/gui/targets_tab'
15
+ require 'cosmos/tools/cmd_tlm_server/gui/packets_tab'
16
+ require 'cosmos/tools/cmd_tlm_server/gui/logging_tab'
17
+ require 'cosmos/tools/cmd_tlm_server/gui/status_tab'
13
18
  require 'cosmos/gui/qt_tool'
14
19
  require 'cosmos/gui/dialogs/splash'
15
- require 'cosmos/gui/dialogs/cmd_tlm_raw_dialog'
16
20
  require 'cosmos/gui/dialogs/exception_dialog'
17
21
  require 'cosmos/gui/dialogs/set_tlm_dialog'
18
22
 
@@ -28,8 +32,7 @@ module Cosmos
28
32
  RUNNING = 1
29
33
  ERROR = 2
30
34
 
31
- CMD = "Command"
32
- TLM = "Telemetry"
35
+ TOOL_NAME = "Command and Telemetry Server"
33
36
 
34
37
  # For the CTS we display all the tables as full size
35
38
  # Thus we don't want the table to absorb the scroll wheel events but
@@ -55,17 +58,25 @@ module Cosmos
55
58
  @no_prompt = options.no_prompt
56
59
  @message_log = nil
57
60
  @output_sleeper = Sleeper.new
61
+ @interfaces_tab = InterfacesTab.new
62
+ @targets_tab = TargetsTab.new
63
+ @packets_tab = PacketsTab.new(self)
64
+ @logging_tab = LoggingTab.new(@production)
65
+ @status_tab = StatusTab.new
58
66
 
59
67
  statusBar.showMessage(tr("")) # Show blank message to initialize status bar
60
68
 
61
69
  initialize_actions()
62
70
  initialize_menus()
63
71
  initialize_central_widget()
72
+ configure_tabs(options)
64
73
  complete_initialize()
74
+ end
65
75
 
76
+ def configure_tabs(options)
66
77
  Splash.execute(self) do |splash|
67
78
  ConfigParser.splash = splash
68
- splash.message = "Initializing #{CMD} and #{TLM} Server"
79
+ splash.message = "Initializing #{TOOL_NAME}"
69
80
 
70
81
  # Start the thread that will process server messages and add them to the output text
71
82
  process_server_messages(options)
@@ -81,19 +92,19 @@ module Cosmos
81
92
  # Override the default title if one was given in the config file
82
93
  self.window_title = CmdTlmServer.title if CmdTlmServer.title
83
94
  splash.progress = 0
84
- populate_interfaces('Interfaces')
95
+ @interfaces_tab.populate_interfaces(@tab_widget)
85
96
  splash.progress = 100/7 * 1
86
- populate_targets()
97
+ @targets_tab.populate(@tab_widget)
87
98
  splash.progress = 100/7 * 2
88
- populate_packets(CMD)
99
+ @packets_tab.populate_commands(@tab_widget)
89
100
  splash.progress = 100/7 * 3
90
- populate_packets(TLM)
101
+ @packets_tab.populate_telemetry(@tab_widget)
91
102
  splash.progress = 100/7 * 4
92
- populate_interfaces('Routers')
103
+ @interfaces_tab.populate_routers(@tab_widget)
93
104
  splash.progress = 100/7 * 5
94
- populate_logging()
105
+ @logging_tab.populate(@tab_widget)
95
106
  splash.progress = 100/7 * 6
96
- populate_status()
107
+ @status_tab.populate(@tab_widget)
97
108
  splash.progress = 100
98
109
  end
99
110
  ConfigParser.splash = nil
@@ -106,7 +117,7 @@ module Cosmos
106
117
  # Edit actions
107
118
  @edit_clear_counters = Qt::Action.new(tr('&Clear Counters'), self)
108
119
  @edit_clear_counters.statusTip = tr('Clear counters for all interfaces and targets')
109
- @edit_clear_counters.connect(SIGNAL('triggered()')) { edit_clear_counters() }
120
+ @edit_clear_counters.connect(SIGNAL('triggered()')) { CmdTlmServer.clear_counters }
110
121
  end
111
122
 
112
123
  def initialize_menus
@@ -119,29 +130,20 @@ module Cosmos
119
130
  @edit_menu.addAction(@edit_clear_counters)
120
131
  end
121
132
 
122
- @about_string = "#{CMD} and #{TLM} Server is the heart of the COSMOS system. "
133
+ @about_string = "#{TOOL_NAME} is the heart of the COSMOS system. "
123
134
  @about_string << "It connects to the target and processes command and telemetry requests from other tools."
124
135
 
125
136
  initialize_help_menu()
126
137
  end
127
138
 
128
- ###########################################
129
- # Edit Menu Options
130
- ###########################################
131
-
132
- # Edit->Clear Counters
133
- def edit_clear_counters
134
- CmdTlmServer.clear_counters
135
- end
136
-
137
139
  def initialize_central_widget
138
140
  # Create the central widget
139
141
  @splitter = Qt::Splitter.new(Qt::Vertical, central_widget)
140
142
  setCentralWidget(@splitter)
141
143
 
142
- @tabbook = Qt::TabWidget.new
143
- connect(@tabbook, SIGNAL('currentChanged(int)'), self, SLOT('handle_tab_change(int)'))
144
- @splitter.addWidget(@tabbook)
144
+ @tab_widget = Qt::TabWidget.new
145
+ connect(@tab_widget, SIGNAL('currentChanged(int)'), self, SLOT('handle_tab_change(int)'))
146
+ @splitter.addWidget(@tab_widget)
145
147
 
146
148
  # Add the message output
147
149
  @output = Qt::PlainTextEdit.new
@@ -149,7 +151,7 @@ module Cosmos
149
151
  @output.setMaximumBlockCount(10000) # 10000 lines of history will be displayed
150
152
 
151
153
  @splitter.addWidget(@output)
152
- # Set the stretch factor to give priority to the tabbook (index 0) instead of the output (index 1)
154
+ # Set the stretch factor to give priority to the tab_widget (index 0) instead of the output (index 1)
153
155
  @splitter.setStretchFactor(0, 1) # (index, stretch)
154
156
 
155
157
  # Override stdout to the message window
@@ -161,777 +163,83 @@ module Cosmos
161
163
  @tab_thread = nil
162
164
  end
163
165
 
164
- def kill_tab_thread
165
- @tab_sleeper ||= nil
166
- @tab_sleeper.cancel if @tab_sleeper
167
- Cosmos.kill_thread(self, @tab_thread)
168
- @tab_thread = nil
169
- end
170
-
166
+ # Called when the user changes tabs in the Server application. It kills the
167
+ # currently executing tab and then creates a new thread to update the GUI
168
+ # for the selected tab.
171
169
  def handle_tab_change(index)
172
170
  kill_tab_thread()
173
171
  @tab_sleeper = Sleeper.new
172
+
174
173
  case index
175
174
  when 0
176
- handle_interfaces_tab('Interfaces')
175
+ handle_tab('Interfaces') { @interfaces_tab.update(InterfacesTab::INTERFACES) }
177
176
  when 1
178
- handle_targets_tab()
177
+ handle_tab('Targets') { @targets_tab.update }
179
178
  when 2
180
- handle_packets_tab(CMD)
179
+ handle_tab('Packets') { @packets_tab.update(PacketsTab::COMMANDS) }
181
180
  when 3
182
- handle_packets_tab(TLM)
181
+ handle_tab('Telemetry') { @packets_tab.update(PacketsTab::TELEMETRY) }
183
182
  when 4
184
- handle_interfaces_tab('Routers')
183
+ handle_tab('Routers') { @interfaces_tab.update(InterfacesTab::ROUTERS) }
185
184
  when 5
186
- handle_logging_tab()
185
+ handle_tab('Logging') { @logging_tab.update }
187
186
  when 6
188
187
  handle_status_tab()
189
188
  end
190
189
  end
191
190
 
192
- def populate_interfaces(name)
193
- @interfaces_table ||= {}
194
-
195
- if name == 'Routers'
196
- interfaces = CmdTlmServer.routers
197
- else
198
- interfaces = CmdTlmServer.interfaces
199
- end
200
-
201
- num_interfaces = interfaces.names.length
202
- if interfaces.names.length > 0
203
- scroll = Qt::ScrollArea.new
204
- scroll.setMinimumSize(800, 150)
205
- widget = Qt::Widget.new
206
- layout = Qt::VBoxLayout.new(widget)
207
- # Since the layout will be inside a scroll area make sure it respects the sizes we set
208
- layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
209
-
210
- interfaces_table = Qt::TableWidget.new()
211
- interfaces_table.verticalHeader.hide()
212
- interfaces_table.setRowCount(num_interfaces)
213
- interfaces_table.setColumnCount(10)
214
- if name == 'Routers'
215
- interfaces_table.setHorizontalHeaderLabels(["Router", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Cmd Pkts ", " Tlm Pkts "])
216
- else
217
- interfaces_table.setHorizontalHeaderLabels(["Interface", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Cmd Pkts ", " Tlm Pkts "])
218
- end
219
-
220
- row = 0
221
- interfaces.all.each do |interface_name, interface|
222
- item = Qt::TableWidgetItem.new(tr(interface_name))
223
- item.setTextAlignment(Qt::AlignCenter)
224
- interfaces_table.setItem(row, 0, item)
225
-
226
- if interface.connected?
227
- button_text = 'Disconnect'
228
- elsif interface.thread.nil?
229
- button_text = 'Connect'
230
- else
231
- button_text = 'Cancel Connect'
232
- end
233
- button = Qt::PushButton.new(button_text)
234
- if name == 'Routers'
235
- button.connect(SIGNAL('clicked()')) do
236
- if interface.thread
237
- Logger.info "User disconnecting router #{interface_name}"
238
- CmdTlmServer.instance.disconnect_router(interface_name)
239
- else
240
- Logger.info "User connecting router #{interface_name}"
241
- CmdTlmServer.instance.connect_router(interface_name)
242
- end
243
- end
244
- else
245
- button.connect(SIGNAL('clicked()')) do
246
- if interface.thread
247
- Logger.info "User disconnecting interface #{interface_name}"
248
- CmdTlmServer.instance.disconnect_interface(interface_name)
249
- else
250
- Logger.info "User connecting interface #{interface_name}"
251
- CmdTlmServer.instance.connect_interface(interface_name)
252
- end
253
- end
254
- end
255
- button.setDisabled(true) if interface.disable_disconnect
256
- interfaces_table.setCellWidget(row, 1, button)
257
-
258
- state = Qt::TableWidgetItem.new
259
- if interface.connected?
260
- state.setText('true')
261
- state.textColor = Cosmos::GREEN
262
- elsif interface.thread
263
- state.setText('attempting')
264
- state.textColor = Cosmos::YELLOW
265
- else
266
- state.setText('false')
267
- state.textColor = Cosmos::BLACK
268
- end
269
- state.setTextAlignment(Qt::AlignCenter)
270
- interfaces_table.setItem(row, 2, state)
271
-
272
- num_clients = Qt::TableWidgetItem.new(tr(interface.num_clients.to_s))
273
- num_clients.setTextAlignment(Qt::AlignCenter)
274
- interfaces_table.setItem(row, 3, num_clients)
275
-
276
- write_queue_size = Qt::TableWidgetItem.new(tr(interface.write_queue_size.to_s))
277
- write_queue_size.setTextAlignment(Qt::AlignCenter)
278
- interfaces_table.setItem(row, 4, write_queue_size)
279
-
280
- read_queue_size = Qt::TableWidgetItem.new(tr(interface.read_queue_size.to_s))
281
- read_queue_size.setTextAlignment(Qt::AlignCenter)
282
- interfaces_table.setItem(row, 5, read_queue_size)
283
-
284
- bytes_written = Qt::TableWidgetItem.new(tr(interface.bytes_written.to_s))
285
- bytes_written.setTextAlignment(Qt::AlignCenter)
286
- interfaces_table.setItem(row, 6, bytes_written)
287
-
288
- bytes_read = Qt::TableWidgetItem.new(tr(interface.bytes_read.to_s))
289
- bytes_read.setTextAlignment(Qt::AlignCenter)
290
- interfaces_table.setItem(row, 7, bytes_read)
291
-
292
- write_count = Qt::TableWidgetItem.new(tr(interface.write_count.to_s))
293
- write_count.setTextAlignment(Qt::AlignCenter)
294
- interfaces_table.setItem(row, 8, write_count)
295
-
296
- read_count = Qt::TableWidgetItem.new(tr(interface.read_count.to_s))
297
- read_count.setTextAlignment(Qt::AlignCenter)
298
- interfaces_table.setItem(row, 9, read_count)
299
-
300
- row += 1
301
- end
302
- interfaces_table.displayFullSize
303
-
304
- layout.addWidget(interfaces_table)
305
- scroll.setWidget(widget)
306
- if name == 'Routers'
307
- @tabbook.addTab(scroll, "Routers")
308
- @interfaces_table['Routers'] = interfaces_table
309
- else
310
- @tabbook.addTab(scroll, "Interfaces")
311
- @interfaces_table['Interfaces'] = interfaces_table
312
- end
313
- end
314
- end
315
-
316
- def handle_interfaces_tab(name)
317
- @tab_thread = Thread.new do
318
- if @interfaces_table[name]
319
- begin
320
- while true
321
- Qt.execute_in_main_thread(true) do
322
- row = 0
323
- if name == 'Routers'
324
- interfaces = CmdTlmServer.routers
325
- else
326
- interfaces = CmdTlmServer.interfaces
327
- end
328
- interfaces.all.each do |interface_name, interface|
329
- button = @interfaces_table[name].cellWidget(row,1)
330
- state = @interfaces_table[name].item(row,2)
331
- if interface.connected?
332
- button.setText('Disconnect')
333
- button.setDisabled(true) if interface.disable_disconnect
334
- state.setText('true')
335
- state.textColor = Cosmos::GREEN
336
- elsif interface.thread
337
- button.text = 'Cancel Connect'
338
- button.setDisabled(false)
339
- state.text = 'attempting'
340
- state.textColor = Cosmos::RED
341
- else
342
- button.setText('Connect')
343
- button.setDisabled(false)
344
- state.setText('false')
345
- state.textColor = Cosmos::BLACK
346
- end
347
- @interfaces_table[name].item(row,3).setText(interface.num_clients.to_s)
348
- @interfaces_table[name].item(row,4).setText(interface.write_queue_size.to_s)
349
- @interfaces_table[name].item(row,5).setText(interface.read_queue_size.to_s)
350
- @interfaces_table[name].item(row,6).setText(interface.bytes_written.to_s)
351
- @interfaces_table[name].item(row,7).setText(interface.bytes_read.to_s)
352
- if name == 'Routers'
353
- @interfaces_table[name].item(row,8).setText(interface.read_count.to_s)
354
- @interfaces_table[name].item(row,9).setText(interface.write_count.to_s)
355
- else
356
- @interfaces_table[name].item(row,8).setText(interface.write_count.to_s)
357
- @interfaces_table[name].item(row,9).setText(interface.read_count.to_s)
358
- end
359
- row += 1
360
- end
361
- end
362
- break if @tab_sleeper.sleep(1)
363
- end
364
- rescue Exception => error
365
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : #{name} Tab Thread")}
366
- end
367
- end
368
- end
369
- end
370
-
371
- def populate_targets
372
- num_targets = System.targets.length
373
- if num_targets > 0
374
- return if num_targets == 1 and System.targets['SYSTEM']
375
- num_targets -= 1 if System.targets['SYSTEM']
376
-
377
- scroll = Qt::ScrollArea.new
378
- widget = Qt::Widget.new
379
- layout = Qt::VBoxLayout.new(widget)
380
- # Since the layout will be inside a scroll area make sure it respects the sizes we set
381
- layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
382
-
383
- @targets_table = Qt::TableWidget.new()
384
- @targets_table.verticalHeader.hide()
385
- @targets_table.setRowCount(num_targets)
386
- @targets_table.setColumnCount(4)
387
- @targets_table.setHorizontalHeaderLabels(["Target Name", "Interface", "#{CMD} Count", "#{TLM} Count"])
388
-
389
- row = 0
390
- System.targets.sort.each do |target_name, target|
391
- next if target_name == 'SYSTEM'
392
- target_name_widget = Qt::TableWidgetItem.new(tr(target_name))
393
- target_name_widget.setTextAlignment(Qt::AlignCenter)
394
- @targets_table.setItem(row, 0, target_name_widget)
395
- if target.interface
396
- interface_name_widget = Qt::TableWidgetItem.new(tr(target.interface.name.to_s))
397
- else
398
- interface_name_widget = Qt::TableWidgetItem.new(tr(''))
399
- end
400
- interface_name_widget.setTextAlignment(Qt::AlignCenter)
401
- @targets_table.setItem(row, 1, interface_name_widget)
402
- cmd_cnt = Qt::TableWidgetItem.new(tr(target.cmd_cnt.to_s))
403
- cmd_cnt.setTextAlignment(Qt::AlignCenter)
404
- @targets_table.setItem(row, 2, cmd_cnt)
405
-
406
- tlm_cnt = Qt::TableWidgetItem.new(tr(target.tlm_cnt.to_s))
407
- tlm_cnt.setTextAlignment(Qt::AlignCenter)
408
- @targets_table.setItem(row, 3, tlm_cnt)
409
-
410
- row += 1
411
- end
412
- @targets_table.displayFullSize
413
-
414
- layout.addWidget(@targets_table)
415
- scroll.setWidget(widget)
416
- @tabbook.addTab(scroll, "Targets")
417
- end
418
- end
419
-
420
- def handle_targets_tab
421
- @tab_thread = Thread.new do
422
- begin
423
- while true
424
- Qt.execute_in_main_thread(true) do
425
- row = 0
426
- System.targets.sort.each do |target_name, target|
427
- next if target_name == 'SYSTEM'
428
- @targets_table.item(row,2).setText(target.cmd_cnt.to_s)
429
- @targets_table.item(row,3).setText(target.tlm_cnt.to_s)
430
- row += 1
431
- end
432
- end
433
- break if @tab_sleeper.sleep(1)
434
- end
435
- rescue Exception => error
436
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : Targets Tab Thread")}
437
- end
438
- end
439
- end
440
-
441
- def get_cmd_tlm_def(name)
442
- case name
443
- when CMD
444
- return System.commands
445
- when TLM
446
- return System.telemetry
447
- end
448
- end
449
-
450
- def populate_packets(name)
451
- cmd_tlm = get_cmd_tlm_def(name)
452
-
453
- unless cmd_tlm.target_names.empty?
454
- count = 0
455
- cmd_tlm.target_names.sort.each do |target_name|
456
- packets = cmd_tlm.packets(target_name)
457
- packets.each do |packet_name, packet|
458
- count += 1 unless packet.hidden
459
- end
460
- end
461
-
462
- scroll = Qt::ScrollArea.new
463
- widget = Qt::Widget.new
464
- layout = Qt::VBoxLayout.new(widget)
465
- # Since the layout will be inside a scroll area
466
- # make sure it respects the sizes we set
467
- layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
468
-
469
- table = Qt::TableWidget.new()
470
- table.verticalHeader.hide()
471
- table.setRowCount(count)
472
- column_cnt = 4
473
- column_cnt += 1 if name == TLM
474
- table.setColumnCount(column_cnt)
475
- # Force the last section to fill all available space in the frame
476
- #~ table.horizontalHeader.setStretchLastSection(true)
477
- headers = ["Target Name", "Packet Name", "Packet Count", "View Raw"]
478
- headers << "View in Packet Viewer" if name == TLM
479
- table.setHorizontalHeaderLabels(headers)
480
-
481
- row = 0
482
- cmd_tlm.target_names.sort.each do |target_name|
483
- packets = cmd_tlm.packets(target_name)
484
- packets.sort.each do |packet_name, packet|
485
- packet.received_count ||= 0
486
- next if packet.hidden
487
- target_name_widget = Qt::TableWidgetItem.new(tr(target_name))
488
- target_name_widget.setTextAlignment(Qt::AlignRight | Qt::AlignVCenter)
489
- table.setItem(row, 0, target_name_widget)
490
- table.setItem(row, 1, Qt::TableWidgetItem.new(tr(packet_name)))
491
- packet_count = Qt::TableWidgetItem.new(tr(packet.received_count.to_s))
492
- packet_count.setTextAlignment(Qt::AlignCenter)
493
- table.setItem(row, 2, packet_count)
494
- view_raw = Qt::PushButton.new("View Raw")
495
- view_raw.connect(SIGNAL('clicked()')) do
496
- @raw_dialogs ||= []
497
- @raw_dialogs << CmdRawDialog.new(self, target_name, packet_name) if name == CMD
498
- @raw_dialogs << TlmRawDialog.new(self, target_name, packet_name) if name == TLM
499
- end
500
- table.setCellWidget(row, 3, view_raw)
501
-
502
- if name == TLM
503
- if target_name != 'UNKNOWN' and packet_name != 'UNKNOWN'
504
- view_pv = Qt::PushButton.new("View in Packet Viewer")
505
- view_pv.connect(SIGNAL('clicked()')) do
506
- if Kernel.is_windows?
507
- Cosmos.run_process("rubyw tools/PacketViewer -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
508
- elsif Kernel.is_mac? and File.exist?("tools/mac/PacketViewer.app")
509
- Cosmos.run_process("open tools/mac/PacketViewer.app --args -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
510
- else
511
- Cosmos.run_process("ruby tools/PacketViewer -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
512
- end
513
- end
514
- table.setCellWidget(row, 4, view_pv)
515
- else
516
- table_widget = Qt::TableWidgetItem.new(tr('N/A'))
517
- table_widget.setTextAlignment(Qt::AlignCenter)
518
- table.setItem(row, 4, table_widget)
519
- end
520
- end
521
-
522
- row += 1
523
- end
524
- end
525
- table.displayFullSize
526
-
527
- layout.addWidget(table)
528
- scroll.setWidget(widget)
529
- tab_name = "Cmd Packets" if name == CMD
530
- tab_name = "Tlm Packets" if name == TLM
531
- @tabbook.addTab(scroll, tab_name)
532
-
533
- @packets_table ||= {}
534
- @packets_table[name] = table
535
- end
536
- end
537
-
538
- def handle_packets_tab(name)
539
- cmd_tlm = get_cmd_tlm_def(name)
540
-
541
- @tab_thread = Thread.new do
542
- begin
543
- unless cmd_tlm.target_names.empty?
544
- while true
545
- Qt.execute_in_main_thread(true) do
546
- row = 0
547
- cmd_tlm.target_names.sort.each do |target_name|
548
- packets = cmd_tlm.packets(target_name)
549
- packets.sort.each do |packet_name, packet|
550
- next if packet.hidden
551
- @packets_table[name].item(row, 2).setText(packet.received_count.to_s)
552
- row += 1
553
- end
554
- end
555
- end
556
- break if @tab_sleeper.sleep(1)
557
- end
558
- end
559
- rescue Exception => error
560
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : #{name} Packets Tab Thread")}
561
- end
562
- end
563
- end
564
-
565
- def populate_logging_actions(layout)
566
- # Add all the action buttons
567
- actions = Qt::GroupBox.new(tr("Actions"))
568
- actions_layout = Qt::VBoxLayout.new(actions)
569
- button_layout = Qt::GridLayout.new
570
-
571
- log_buttons = [
572
- ["Start Logging on All", [0,0], :start_logging],
573
- ["Stop Logging on All", [0,1], :stop_logging],
574
- ["Start #{TLM} Logging on All", [1,0], :start_tlm_log],
575
- ["Stop #{TLM} Logging on All", [1,1], :stop_tlm_log],
576
- ["Start #{CMD} Logging on All", [2,0], :start_cmd_log],
577
- ["Stop #{CMD} Logging on All", [2,1], :stop_cmd_log]
578
- ]
579
-
580
- log_buttons.each do |text, location, method|
581
- next if text =~ /Stop/ and @production
582
- button = Qt::PushButton.new(tr(text))
583
- button_layout.addWidget(button, location[0], location[1])
584
- button.connect(SIGNAL('clicked()')) do
585
- begin
586
- CmdTlmServer.instance.send(method, 'ALL')
587
- rescue Exception => error
588
- statusBar.showMessage(tr(error.message))
589
- end
590
- end
591
- end
592
-
593
- actions_layout.addLayout(button_layout)
594
-
595
- layout.addWidget(actions)
596
- end
597
-
598
- def create_log_layout(form_layout, log_writer, label_prefix)
599
- label = Qt::Label.new(tr(log_writer.logging_enabled.to_s))
600
- label.setTextInteractionFlags(Qt::TextSelectableByMouse)
601
- form_layout.addRow("#{label_prefix} Logging:", label)
602
- label = Qt::Label.new(tr(log_writer.queue.size.to_s))
603
- label.setTextInteractionFlags(Qt::TextSelectableByMouse)
604
- form_layout.addRow("#{label_prefix} Queue Size:", label)
605
- label = Qt::Label.new(tr(log_writer.filename))
606
- label.setTextInteractionFlags(Qt::TextSelectableByMouse)
607
- form_layout.addRow("#{label_prefix} Filename:", label)
608
- file_size = 0
609
- begin
610
- file_size = File.size(log_writer.filename) if log_writer.filename
611
- rescue Exception
612
- # Do nothing on error
613
- end
614
- label = Qt::Label.new(tr(file_size.to_s))
615
- label.setTextInteractionFlags(Qt::TextSelectableByMouse)
616
- form_layout.addRow("#{label_prefix} File Size:", label)
617
- end
618
-
619
- def populate_log_file_info(layout)
620
- CmdTlmServer.packet_logging.all.sort.each do |packet_log_writer_pair_name, packet_log_writer_pair|
621
- log = Qt::GroupBox.new("#{packet_log_writer_pair_name} Packet Log Writer")
622
- log_layout = Qt::VBoxLayout.new(log)
623
- interfaces = []
624
- CmdTlmServer.interfaces.all.each do |interface_name, interface|
625
- if interface.packet_log_writer_pairs.include?(packet_log_writer_pair)
626
- interfaces << interface.name
627
- end
628
- end
629
-
630
- form_layout = Qt::FormLayout.new
631
- @logging_layouts[packet_log_writer_pair_name] = form_layout
632
- label = Qt::Label.new(tr(interfaces.join(", ")))
633
- label.setTextInteractionFlags(Qt::TextSelectableByMouse)
634
- form_layout.addRow("Interfaces:", label)
635
- create_log_layout(form_layout, packet_log_writer_pair.cmd_log_writer, 'Cmd')
636
- create_log_layout(form_layout, packet_log_writer_pair.tlm_log_writer, 'Tlm')
637
-
638
- button_layout = Qt::HBoxLayout.new
639
- start_button = Qt::PushButton.new(tr('Start Cmd Logging'))
640
- button_layout.addWidget(start_button)
641
- start_button.connect(SIGNAL('clicked()')) do
642
- CmdTlmServer.instance.start_cmd_log(packet_log_writer_pair_name)
643
- end
644
- start_button = Qt::PushButton.new(tr('Start Tlm Logging'))
645
- button_layout.addWidget(start_button)
646
- start_button.connect(SIGNAL('clicked()')) do
647
- CmdTlmServer.instance.start_tlm_log(packet_log_writer_pair_name)
648
- end
649
- if @production == false
650
- stop_button = Qt::PushButton.new(tr('Stop Cmd Logging'))
651
- button_layout.addWidget(stop_button)
652
- stop_button.connect(SIGNAL('clicked()')) do
653
- CmdTlmServer.instance.stop_cmd_log(packet_log_writer_pair_name)
654
- end
655
- stop_button = Qt::PushButton.new(tr('Stop Tlm Logging'))
656
- button_layout.addWidget(stop_button)
657
- stop_button.connect(SIGNAL('clicked()')) do
658
- CmdTlmServer.instance.stop_tlm_log(packet_log_writer_pair_name)
659
- end
660
- end
661
- form_layout.addRow("Actions:", button_layout)
662
- log_layout.addLayout(form_layout)
663
- layout.addWidget(log)
664
- end
665
- layout.addWidget(Qt::Label.new(tr("Note: Buffered IO operations cause file size to not reflect total logged data size until the log file is closed.")))
666
- end
667
-
668
- def populate_logging
669
- scroll = Qt::ScrollArea.new
670
- widget = Qt::Widget.new
671
- layout = Qt::VBoxLayout.new(widget)
672
- # Since the layout will be inside a scroll area
673
- # make sure it respects the sizes we set
674
- layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
675
-
676
- populate_logging_actions(layout)
677
-
678
- # Create the hash which will hold the logging layouts
679
- @logging_layouts = {}
680
-
681
- # Add the cmd/tlm log files information
682
- populate_log_file_info(layout)
683
-
684
- # Set the scroll area widget last now that all the items have been layed out
685
- scroll.setWidget(widget)
686
- @tabbook.addTab(scroll, "Logging")
687
- end
688
-
689
- def update_filename_and_logging_state
690
- CmdTlmServer.packet_logging.all.each do |packet_log_writer_pair_name, packet_log_writer_pair|
691
- @logging_layouts[packet_log_writer_pair_name].itemAt(1, Qt::FormLayout::FieldRole).widget.setText(packet_log_writer_pair.cmd_log_writer.logging_enabled.to_s)
692
- @logging_layouts[packet_log_writer_pair_name].itemAt(2, Qt::FormLayout::FieldRole).widget.setText(packet_log_writer_pair.cmd_log_writer.queue.size.to_s)
693
- @logging_layouts[packet_log_writer_pair_name].itemAt(3, Qt::FormLayout::FieldRole).widget.setText(packet_log_writer_pair.cmd_log_writer.filename)
694
- file_size = 0
695
- begin
696
- file_size = File.size(packet_log_writer_pair.cmd_log_writer.filename) if packet_log_writer_pair.cmd_log_writer.filename
697
- rescue Exception
698
- # Do nothing on error
699
- end
700
- @logging_layouts[packet_log_writer_pair_name].itemAt(4, Qt::FormLayout::FieldRole).widget.setText(file_size.to_s)
701
- @logging_layouts[packet_log_writer_pair_name].itemAt(5, Qt::FormLayout::FieldRole).widget.setText(packet_log_writer_pair.tlm_log_writer.logging_enabled.to_s)
702
- @logging_layouts[packet_log_writer_pair_name].itemAt(6, Qt::FormLayout::FieldRole).widget.setText(packet_log_writer_pair.tlm_log_writer.queue.size.to_s)
703
- @logging_layouts[packet_log_writer_pair_name].itemAt(7, Qt::FormLayout::FieldRole).widget.setText(packet_log_writer_pair.tlm_log_writer.filename)
704
- file_size = 0
705
- begin
706
- file_size = File.size(packet_log_writer_pair.tlm_log_writer.filename) if packet_log_writer_pair.tlm_log_writer.filename
707
- rescue Exception
708
- # Do nothing on error
709
- end
710
- @logging_layouts[packet_log_writer_pair_name].itemAt(8, Qt::FormLayout::FieldRole).widget.setText(file_size.to_s)
711
- end
191
+ # Cancel the tab sleeper and kill the tab thread so we can create a new one
192
+ def kill_tab_thread
193
+ @tab_sleeper ||= nil
194
+ @tab_sleeper.cancel if @tab_sleeper
195
+ Cosmos.kill_thread(self, @tab_thread)
196
+ @tab_thread = nil
712
197
  end
713
198
 
714
- def handle_logging_tab
199
+ # Wrapper method that starts a new thread and then loops. It ensures we are
200
+ # executing in the main thread and then yields to allow updates to the GUI.
201
+ # Finally it sleeps using a sleeper so it can be interrupted.
202
+ #
203
+ # @param name [String] Name of the tab
204
+ def handle_tab(name)
715
205
  @tab_thread = Thread.new do
716
206
  begin
717
207
  while true
718
- Qt.execute_in_main_thread(true) do
719
- update_filename_and_logging_state()
720
- end
208
+ Qt.execute_in_main_thread(true) { yield }
721
209
  break if @tab_sleeper.sleep(1)
722
210
  end
723
211
  rescue Exception => error
724
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : Logging Tab Thread")}
212
+ Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : #{name} Tab Thread")}
725
213
  end
726
214
  end
727
215
  end
728
216
 
729
- def populate_status
730
- scroll = Qt::ScrollArea.new
731
- widget = Qt::Widget.new
732
- layout = Qt::VBoxLayout.new(widget)
733
-
734
- limits = Qt::GroupBox.new(tr("Limits Status"))
735
- limits_layout = Qt::FormLayout.new(limits)
736
- current_limits_set = System.limits_set.to_s
737
-
738
- known_limits_sets = System.limits.sets
739
- known_limits_sets = known_limits_sets.map {|x| x.to_s}.sort
740
- current_index = known_limits_sets.index(current_limits_set.to_s)
741
-
742
- @limits_set_combo = Qt::ComboBox.new
743
- limits_layout.addRow("Limits Set:", @limits_set_combo)
744
- layout.addWidget(limits)
745
-
746
- known_limits_sets.sort.each do |limits_set|
747
- @limits_set_combo.addItem(limits_set.to_s)
748
- end
749
- @limits_set_combo.setMaxVisibleItems(6)
750
- @limits_set_combo.setCurrentIndex(current_index)
751
- # Only connect to the signal that is sent when the user chooses an item.
752
- # If the limits set is changed programatically the code in
753
- # handle_status_tab will pick up the change.
754
- @limits_set_combo.connect(SIGNAL('activated(int)')) do
755
- selected_limits_set = @limits_set_combo.currentText
756
- if selected_limits_set
757
- System.limits_set = selected_limits_set.intern if System.limits_set != selected_limits_set.intern
758
- end
759
- end
760
-
761
- api = Qt::GroupBox.new(tr("API Status"))
762
- api_layout = Qt::VBoxLayout.new(api)
763
- @api_table = Qt::TableWidget.new()
764
- @api_table.verticalHeader.hide()
765
- @api_table.setRowCount(1)
766
- @api_table.setColumnCount(6)
767
- @api_table.setHorizontalHeaderLabels(["Port", "Num Clients", "Requests", "Requests/Sec", "Avg Request Time", "Estimated Utilization"])
768
-
769
- @api_table.setItem(0, 0, Qt::TableWidgetItem.new(tr(System.ports['CTS_API'].to_s)))
770
- item0 = Qt::TableWidgetItem.new(tr(CmdTlmServer.json_drb.num_clients.to_s))
771
- item0.setTextAlignment(Qt::AlignCenter)
772
- @api_table.setItem(0, 1, item0)
773
- item = Qt::TableWidgetItem.new(tr(CmdTlmServer.json_drb.request_count.to_s))
774
- item.setTextAlignment(Qt::AlignCenter)
775
- @api_table.setItem(0, 2, item)
776
- item2 = Qt::TableWidgetItem.new("0.0")
777
- item2.setTextAlignment(Qt::AlignCenter)
778
- @api_table.setItem(0, 3, item2)
779
- item3 = Qt::TableWidgetItem.new("0.0")
780
- item3.setTextAlignment(Qt::AlignCenter)
781
- @api_table.setItem(0, 4, item3)
782
- item4 = Qt::TableWidgetItem.new("0.0")
783
- item4.setTextAlignment(Qt::AlignCenter)
784
- @api_table.setItem(0, 5, item4)
785
- @api_table.displayFullSize
786
- api_layout.addWidget(@api_table)
787
- layout.addWidget(api)
788
-
789
- system = Qt::GroupBox.new(tr("System Status"))
790
- system_layout = Qt::VBoxLayout.new(system)
791
- @system_table = Qt::TableWidget.new()
792
- @system_table.verticalHeader.hide()
793
- @system_table.setRowCount(1)
794
- @system_table.setColumnCount(4)
795
- @system_table.setHorizontalHeaderLabels(["Threads", "Total Objs", "Free Objs", "Allocated Objs"])
796
-
797
- item0 = Qt::TableWidgetItem.new("0")
798
- item0.setTextAlignment(Qt::AlignCenter)
799
- @system_table.setItem(0, 0, item0)
800
- item1 = Qt::TableWidgetItem.new("0.0")
801
- item1.setTextAlignment(Qt::AlignCenter)
802
- @system_table.setItem(0, 1, item1)
803
- item2 = Qt::TableWidgetItem.new("0.0")
804
- item2.setTextAlignment(Qt::AlignCenter)
805
- @system_table.setItem(0, 2, item2)
806
- item3 = Qt::TableWidgetItem.new("0.0")
807
- item3.setTextAlignment(Qt::AlignCenter)
808
- @system_table.setItem(0, 3, item3)
809
- @system_table.displayFullSize
810
- system_layout.addWidget(@system_table)
811
- layout.addWidget(system)
812
-
813
- background_tasks_groupbox = Qt::GroupBox.new(tr("Background Tasks"))
814
- background_tasks_layout = Qt::VBoxLayout.new(background_tasks_groupbox)
815
- @background_tasks_table = Qt::TableWidget.new()
816
- @background_tasks_table.verticalHeader.hide()
817
- @background_tasks_table.setRowCount(CmdTlmServer.background_tasks.all.length)
818
- @background_tasks_table.setColumnCount(3)
819
- @background_tasks_table.setHorizontalHeaderLabels(["Name", "State", "Status"])
820
-
821
- background_tasks = CmdTlmServer.background_tasks.all
822
- if background_tasks.length > 0
823
- row = 0
824
- background_tasks.each_with_index do |background_task, index|
825
- background_task_name = background_task.name
826
- background_task_name = "Background Task ##{index + 1}" unless background_task_name
827
- background_task_name_widget = Qt::TableWidgetItem.new(background_task_name)
828
- background_task_name_widget.setTextAlignment(Qt::AlignCenter)
829
- @background_tasks_table.setItem(row, 0, background_task_name_widget)
830
- if background_task.thread
831
- status = background_task.thread.status
832
- status = 'complete' if status == false
833
- background_task_state_widget = Qt::TableWidgetItem.new(status.to_s)
834
- else
835
- background_task_state_widget = Qt::TableWidgetItem.new('no thread')
836
- end
837
- background_task_state_widget.setTextAlignment(Qt::AlignCenter)
838
- background_task_state_widget.setSizeHint(Qt::Size.new(80, 30))
839
- @background_tasks_table.setItem(row, 1, background_task_state_widget)
840
- background_task_status_widget = Qt::TableWidgetItem.new(background_task.status.to_s)
841
- background_task_status_widget.setTextAlignment(Qt::AlignCenter)
842
- background_task_status_widget.setSizeHint(Qt::Size.new(500, 30))
843
- @background_tasks_table.setItem(row, 2, background_task_status_widget)
844
-
845
- row += 1
846
- end
847
- end
848
- @background_tasks_table.displayFullSize
849
- background_tasks_layout.addWidget(@background_tasks_table)
850
- layout.addWidget(background_tasks_groupbox)
851
-
852
- # Set the scroll area widget last now that all the items have been layed out
853
- scroll.setWidget(widget)
854
- @tabbook.addTab(scroll, "Status")
855
- end
856
-
217
+ # Update the status tab of the server
857
218
  def handle_status_tab
219
+ if CmdTlmServer.json_drb
220
+ previous_request_count = CmdTlmServer.json_drb.request_count
221
+ else
222
+ previous_request_count = 0
223
+ end
858
224
  @tab_thread = Thread.new do
859
225
  begin
860
- if CmdTlmServer.json_drb
861
- previous_request_count = CmdTlmServer.json_drb.request_count
862
- else
863
- previous_request_count = 0
864
- end
865
226
  while true
866
227
  start_time = Time.now
867
- Qt.execute_in_main_thread(true) do
868
- # Update limits set
869
- current_limits_set = System.limits_set.to_s
870
- if @limits_set_combo.currentText != current_limits_set
871
- known_limits_sets = System.limits.sets
872
- known_limits_sets = known_limits_sets.map {|x| x.to_s}.sort
873
- current_index = known_limits_sets.index(current_limits_set.to_s)
874
- @limits_set_combo.clear
875
- known_limits_sets.sort.each do |limits_set|
876
- @limits_set_combo.addItem(limits_set.to_s)
877
- end
878
- @limits_set_combo.setCurrentIndex(current_index)
879
- end
880
-
881
- # Update API status
882
- if CmdTlmServer.json_drb
883
- @api_table.item(0,1).setText(CmdTlmServer.json_drb.num_clients.to_s)
884
- @api_table.item(0,2).setText(CmdTlmServer.json_drb.request_count.to_s)
885
- request_count = CmdTlmServer.json_drb.request_count
886
- requests_per_second = request_count - previous_request_count
887
- @api_table.item(0,3).setText(requests_per_second.to_s)
888
- previous_request_count = request_count
889
- average_request_time = CmdTlmServer.json_drb.average_request_time
890
- @api_table.item(0,4).setText(sprintf("%0.6f s", average_request_time))
891
- estimated_utilization = requests_per_second * average_request_time * 100.0
892
- @api_table.item(0,5).setText(sprintf("%0.2f %", estimated_utilization))
893
- end
894
-
895
- # Update system status
896
- @system_table.item(0,0).setText(Thread.list.length.to_s)
897
- objs = ObjectSpace.count_objects
898
- @system_table.item(0,1).setText(objs[:TOTAL].to_s)
899
- @system_table.item(0,2).setText(objs[:FREE].to_s)
900
- total = 0
901
- objs.each do |key, val|
902
- next if key == :TOTAL || key == :FREE
903
- total += val
904
- end
905
- @system_table.item(0,3).setText(total.to_s)
906
-
907
- # Update background task status
908
- background_tasks = CmdTlmServer.background_tasks.all
909
- if background_tasks.length > 0
910
- row = 0
911
- background_tasks.each_with_index do |background_task, index|
912
- if background_task.thread
913
- status = background_task.thread.status
914
- status = 'complete' if status == false
915
- @background_tasks_table.item(row, 1).setText(status.to_s)
916
- else
917
- @background_tasks_table.item(row, 1).setText('no thread')
918
- end
919
- @background_tasks_table.item(row, 2).setText(background_task.status.to_s)
920
- row += 1
921
- end
922
- end
923
- end
228
+ Qt.execute_in_main_thread(true) { @status_tab.update(previous_request_count) }
924
229
  total_time = Time.now - start_time
925
230
  if total_time > 0.0 and total_time < 1.0
926
231
  break if @tab_sleeper.sleep(1.0 - total_time)
927
232
  end
928
233
  end
929
234
  rescue Exception => error
930
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : Settings Tab Thread")}
235
+ Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : Status Tab Thread")}
931
236
  end
932
237
  end
933
238
  end
934
239
 
240
+ # Called when the user tries to close the server application. Popup a
241
+ # dialog to warn them about the consequences and then gracefully shut
242
+ # everything down.
935
243
  def closeEvent(event)
936
244
  # Create are you sure dialog
937
245
  if @no_prompt
@@ -957,34 +265,8 @@ module Cosmos
957
265
  end
958
266
  end
959
267
 
960
- def self.run(option_parser = nil, options = nil)
961
- Cosmos.catch_fatal_exception do
962
- unless option_parser and options
963
- option_parser, options = create_default_options()
964
- options.width = 800
965
- options.height = 500
966
- # Set the default title which can be overridden in the config file
967
- options.title = "#{CMD} and #{TLM} Server"
968
- options.auto_size = false
969
- options.config_file = CmdTlmServer::DEFAULT_CONFIG_FILE
970
- options.production = false
971
- options.no_prompt = false
972
- option_parser.separator "CTS Specific Options:"
973
- option_parser.on("-c", "--config FILE", "Use the specified configuration file") do |arg|
974
- options.config_file = arg
975
- end
976
- option_parser.on("-p", "--production", "Run the server in production mode which disables the ability to stop logging.") do |arg|
977
- options.production = true
978
- end
979
- option_parser.on("-n", "--no-prompt", "Don't prompt with Are You Sure dialog on close.") do |arg|
980
- options.no_prompt = true
981
- end
982
- end
983
-
984
- super(option_parser, options)
985
- end
986
- end
987
-
268
+ # Thread to process server messages. Uses handle_string_output to process
269
+ # the output.
988
270
  def process_server_messages(options)
989
271
  # Start thread to read server messages
990
272
  @output_thread = Thread.new do
@@ -1004,6 +286,8 @@ module Cosmos
1004
286
  end
1005
287
  end
1006
288
 
289
+ # Write any available messages to the output pane in the server GUI
290
+ # as well as to the server message log.
1007
291
  def handle_string_output
1008
292
  if @string_output.string[-1..-1] == "\n"
1009
293
  Qt.execute_in_main_thread(true) do
@@ -1017,6 +301,8 @@ module Cosmos
1017
301
  end
1018
302
  end
1019
303
 
304
+ # CmdTlmServer stop callback called by CmdTlmServer.stop. Ensures all the
305
+ # output is written to the message logs.
1020
306
  def stop_callback
1021
307
  handle_string_output()
1022
308
  @output_sleeper.cancel
@@ -1028,6 +314,35 @@ module Cosmos
1028
314
  def graceful_kill
1029
315
  # Just to avoid warning
1030
316
  end
1031
- end # class CmdTlmServerGui
1032
317
 
318
+ # Entry point to the server application
319
+ def self.run(option_parser = nil, options = nil)
320
+ Cosmos.catch_fatal_exception do
321
+ unless option_parser and options
322
+ option_parser, options = create_default_options()
323
+ options.width = 800
324
+ options.height = 500
325
+ # Set the default title which can be overridden in the config file
326
+ options.title = TOOL_NAME
327
+ options.auto_size = false
328
+ options.config_file = CmdTlmServer::DEFAULT_CONFIG_FILE
329
+ options.production = false
330
+ options.no_prompt = false
331
+ option_parser.separator "CTS Specific Options:"
332
+ option_parser.on("-c", "--config FILE", "Use the specified configuration file") do |arg|
333
+ options.config_file = arg
334
+ end
335
+ option_parser.on("-p", "--production", "Run the server in production mode which disables the ability to stop logging.") do |arg|
336
+ options.production = true
337
+ end
338
+ option_parser.on("-n", "--no-prompt", "Don't prompt with Are You Sure dialog on close.") do |arg|
339
+ options.no_prompt = true
340
+ end
341
+ end
342
+
343
+ super(option_parser, options)
344
+ end
345
+ end
346
+
347
+ end # class CmdTlmServerGui
1033
348
  end # module Cosmos