cosmos 4.0.3-java → 4.1.0-java

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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -5
  3. data/Manifest.txt +11 -1
  4. data/README.md +3 -2
  5. data/Rakefile +18 -4
  6. data/appveyor.yml +19 -0
  7. data/cosmos.gemspec +12 -3
  8. data/data/config/cmd_tlm_server.yaml +3 -0
  9. data/data/crc.txt +63 -60
  10. data/demo/config/targets/INST/cmd_tlm_server.txt +1 -0
  11. data/demo/config/targets/INST/cmd_tlm_server2.txt +7 -0
  12. data/demo/config/tools/cmd_sequence/cmd_sequence.txt +2 -0
  13. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +8 -12
  14. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +7 -9
  15. data/demo/lib/cmd_sequence_exporter.rb +52 -0
  16. data/demo/lib/example_background_task.rb +1 -0
  17. data/demo/procedures/replay_test.rb +32 -0
  18. data/ext/cosmos/ext/structure/structure.c +39 -3
  19. data/install/config/tools/cmd_tlm_server/cmd_tlm_server.txt +1 -0
  20. data/install/config/tools/launcher/launcher.txt +2 -0
  21. data/lib/cosmos/config/config_parser.rb +2 -0
  22. data/lib/cosmos/core_ext/io.rb +89 -60
  23. data/lib/cosmos/gui/qt.rb +5 -8
  24. data/lib/cosmos/gui/qt_tool.rb +8 -8
  25. data/lib/cosmos/gui/text/ruby_editor.rb +12 -12
  26. data/lib/cosmos/gui/utilities/script_module_gui.rb +9 -9
  27. data/lib/cosmos/gui/widgets/realtime_button_bar.rb +18 -17
  28. data/lib/cosmos/interfaces/protocols/fixed_protocol.rb +2 -2
  29. data/lib/cosmos/interfaces/protocols/template_protocol.rb +3 -0
  30. data/lib/cosmos/interfaces/udp_interface.rb +27 -14
  31. data/lib/cosmos/io/buffered_file.rb +0 -1
  32. data/lib/cosmos/io/json_drb.rb +134 -214
  33. data/lib/cosmos/io/json_drb_object.rb +22 -61
  34. data/lib/cosmos/io/json_drb_rack.rb +79 -0
  35. data/lib/cosmos/io/json_rpc.rb +27 -0
  36. data/lib/cosmos/io/udp_sockets.rb +102 -58
  37. data/lib/cosmos/packets/commands.rb +1 -1
  38. data/lib/cosmos/packets/structure.rb +1 -1
  39. data/lib/cosmos/packets/structure_item.rb +37 -5
  40. data/lib/cosmos/script/cmd_tlm_server.rb +76 -2
  41. data/lib/cosmos/script/replay.rb +60 -0
  42. data/lib/cosmos/script/script.rb +20 -2
  43. data/lib/cosmos/script/scripting.rb +9 -9
  44. data/lib/cosmos/script/tools.rb +14 -0
  45. data/lib/cosmos/system/system.rb +185 -92
  46. data/lib/cosmos/system/target.rb +1 -1
  47. data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +44 -4
  48. data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +4 -0
  49. data/lib/cosmos/tools/cmd_sequence/sequence_list.rb +7 -0
  50. data/lib/cosmos/tools/cmd_tlm_server/api.rb +347 -20
  51. data/lib/cosmos/tools/cmd_tlm_server/background_tasks.rb +3 -0
  52. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +329 -111
  53. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +13 -0
  54. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +261 -95
  55. data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +46 -35
  56. data/lib/cosmos/tools/cmd_tlm_server/gui/logging_tab.rb +18 -8
  57. data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +39 -28
  58. data/lib/cosmos/tools/cmd_tlm_server/gui/replay_tab.rb +242 -0
  59. data/lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb +24 -8
  60. data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +18 -6
  61. data/lib/cosmos/tools/cmd_tlm_server/limits_groups_background_task.rb +5 -4
  62. data/lib/cosmos/tools/cmd_tlm_server/replay_backend.rb +375 -0
  63. data/lib/cosmos/tools/cmd_tlm_server/routers.rb +10 -2
  64. data/lib/cosmos/tools/data_viewer/data_viewer.rb +40 -5
  65. data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +18 -20
  66. data/lib/cosmos/tools/launcher/launcher_config.rb +5 -16
  67. data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +65 -39
  68. data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +19 -0
  69. data/lib/cosmos/tools/replay/replay.rb +5 -505
  70. data/lib/cosmos/tools/script_runner/script_audit.rb +1 -0
  71. data/lib/cosmos/tools/script_runner/script_runner.rb +3 -4
  72. data/lib/cosmos/tools/script_runner/script_runner_config.rb +3 -4
  73. data/lib/cosmos/tools/script_runner/script_runner_frame.rb +44 -23
  74. data/lib/cosmos/tools/test_runner/results_writer.rb +4 -0
  75. data/lib/cosmos/tools/test_runner/test_runner.rb +0 -3
  76. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +6 -2
  77. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_tool.rb +26 -1
  78. data/lib/cosmos/tools/tlm_viewer/screen.rb +24 -1
  79. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +25 -0
  80. data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +24 -14
  81. data/lib/cosmos/top_level.rb +34 -24
  82. data/lib/cosmos/utilities/csv.rb +60 -8
  83. data/lib/cosmos/version.rb +5 -5
  84. data/spec/config/config_parser_spec.rb +10 -1
  85. data/spec/core_ext/socket_spec.rb +4 -2
  86. data/spec/gui/utilities/script_module_gui_spec.rb +102 -0
  87. data/spec/install/config/data/data.txt +1 -0
  88. data/spec/install/config/targets/INST/cmd_tlm/inst_cmds.txt +2 -0
  89. data/spec/interfaces/cmd_tlm_server_interface_spec.rb +1 -2
  90. data/spec/interfaces/protocols/template_protocol_spec.rb +72 -2
  91. data/spec/interfaces/serial_interface_spec.rb +1 -1
  92. data/spec/interfaces/udp_interface_spec.rb +14 -0
  93. data/spec/io/buffered_file_spec.rb +37 -0
  94. data/spec/io/json_drb_object_spec.rb +2 -15
  95. data/spec/io/json_drb_spec.rb +61 -121
  96. data/spec/io/udp_sockets_spec.rb +42 -2
  97. data/spec/packet_logs/packet_log_reader_spec.rb +5 -2
  98. data/spec/packets/binary_accessor_spec.rb +1 -1
  99. data/spec/packets/packet_item_spec.rb +1 -1
  100. data/spec/packets/structure_item_spec.rb +5 -6
  101. data/spec/script/cmd_tlm_server_spec.rb +39 -4
  102. data/spec/script/commands_disconnect_spec.rb +1 -1
  103. data/spec/script/commands_spec.rb +2 -1
  104. data/spec/script/scripting_spec.rb +18 -3
  105. data/spec/script/telemetry_spec.rb +5 -0
  106. data/spec/spec_helper.rb +43 -26
  107. data/spec/streams/tcpip_socket_stream_spec.rb +2 -2
  108. data/spec/system/system_spec.rb +11 -9
  109. data/spec/system/target_spec.rb +3 -0
  110. data/spec/tools/cmd_tlm_server/api_spec.rb +543 -29
  111. data/spec/tools/cmd_tlm_server/background_task_spec.rb +2 -2
  112. data/spec/tools/cmd_tlm_server/background_tasks_spec.rb +31 -75
  113. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +199 -66
  114. data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +85 -9
  115. data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +29 -127
  116. data/spec/tools/cmd_tlm_server/router_thread_spec.rb +10 -50
  117. data/spec/tools/launcher/launcher_config_spec.rb +1 -1
  118. data/spec/tools/table_manager/table_item_spec.rb +1 -1
  119. data/spec/tools/table_manager/tablemanager_core_spec.rb +4 -4
  120. data/spec/top_level/top_level_spec.rb +151 -3
  121. data/spec/utilities/csv_spec.rb +24 -5
  122. metadata +61 -9
  123. data/lib/cosmos/tools/replay/replay_server.rb +0 -91
@@ -51,6 +51,13 @@ module Cosmos
51
51
 
52
52
  protected
53
53
 
54
+ def get_target_interface_name(target_name)
55
+ @interfaces.each do |interface_name, interface|
56
+ return interface_name if interface.target_names.include?(target_name)
57
+ end
58
+ nil
59
+ end
60
+
54
61
  # Processes a file and adds in the configuration defined in the file
55
62
  #
56
63
  # @param filename [String] The name of the configuration file to parse
@@ -105,6 +112,8 @@ module Cosmos
105
112
  System.targets.each do |target_name, target|
106
113
  target_filename = File.join(target.dir, 'cmd_tlm_server.txt')
107
114
  if File.exist?(target_filename)
115
+ # Skip this target if it's already been assigned an interface
116
+ next if get_target_interface_name(target.name)
108
117
  raise parser.error("Cannot use #{keyword} with target name substitutions: #{target.name} != #{target.original_name}") if target.name != target.original_name
109
118
  process_file(target_filename, true)
110
119
  end
@@ -116,6 +125,8 @@ module Cosmos
116
125
  parser.verify_num_parameters(1, 2, usage)
117
126
  target = System.targets[params[0].upcase]
118
127
  raise parser.error("Unknown target: #{params[0].upcase}") unless target
128
+ interface_name = get_target_interface_name(target.name)
129
+ raise parser.error("Target #{target.name} already mapped to interface #{interface_name}") if interface_name
119
130
  target_filename = params[1]
120
131
  target_filename = 'cmd_tlm_server.txt' unless target_filename
121
132
  target_filename = File.join(target.dir, target_filename)
@@ -164,6 +175,8 @@ module Cosmos
164
175
  target_name = params[0].upcase
165
176
  target = System.targets[target_name]
166
177
  if target
178
+ interface_name = get_target_interface_name(target.name)
179
+ raise parser.error("Target #{target.name} already mapped to interface #{interface_name}") if interface_name
167
180
  target.interface = current_interface_or_router
168
181
  current_interface_or_router.target_names << target_name
169
182
  else
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2014 Ball Aerospace & Technologies Corp.
3
+ # Copyright 2017 Ball Aerospace & Technologies Corp.
4
4
  # All Rights Reserved.
5
5
  #
6
6
  # This program is free software; you can modify and/or redistribute it
@@ -16,6 +16,7 @@ if RUBY_ENGINE == 'ruby'
16
16
  require 'cosmos/tools/cmd_tlm_server/gui/packets_tab'
17
17
  require 'cosmos/tools/cmd_tlm_server/gui/logging_tab'
18
18
  require 'cosmos/tools/cmd_tlm_server/gui/status_tab'
19
+ require 'cosmos/tools/cmd_tlm_server/gui/replay_tab'
19
20
  require 'cosmos/gui/qt_tool'
20
21
  require 'cosmos/gui/dialogs/splash'
21
22
  require 'cosmos/gui/dialogs/exception_dialog'
@@ -85,7 +86,8 @@ module Cosmos
85
86
  RUNNING = 1
86
87
  ERROR = 2
87
88
 
88
- TOOL_NAME = "Command and Telemetry Server"
89
+ TOOL_NAME = "Command and Telemetry Server".freeze
90
+ BLANK = ''.freeze
89
91
 
90
92
  attr_writer :no_prompt
91
93
 
@@ -109,69 +111,94 @@ module Cosmos
109
111
 
110
112
  def initialize(options)
111
113
  super(options) # MUST BE FIRST - All code before super is executed twice in RubyQt Based classes
112
- Cosmos.load_cosmos_icon("cts.png")
113
114
 
115
+ @ready = false
116
+ @tabs_ready = false
117
+ if options.replay
118
+ @mode = :REPLAY
119
+ Cosmos.load_cosmos_icon("replay.png")
120
+ else
121
+ @mode = :CMD_TLM_SERVER
122
+ Cosmos.load_cosmos_icon("cts.png")
123
+ end
114
124
  @production = options.production
115
125
  @no_prompt = options.no_prompt
116
126
  @message_log = nil
117
127
  @output_sleeper = Sleeper.new
118
128
  @first_output = 0
119
- @interfaces_tab = InterfacesTab.new(self)
120
- @targets_tab = TargetsTab.new
121
- @packets_tab = PacketsTab.new(self)
122
- @logging_tab = LoggingTab.new(@production)
123
- @status_tab = StatusTab.new
129
+ @options = options
124
130
 
125
131
  statusBar.showMessage(tr("")) # Show blank message to initialize status bar
126
132
 
127
133
  initialize_actions()
128
134
  initialize_menus()
129
135
  initialize_central_widget()
130
- configure_tabs(options)
131
- complete_initialize()
132
- end
133
-
134
- def configure_tabs(options)
135
136
  Splash.execute(self) do |splash|
136
137
  ConfigParser.splash = splash
137
- splash.message = "Initializing #{TOOL_NAME}"
138
+ process_server_messages(@options)
139
+ start(splash)
140
+ ConfigParser.splash = nil
141
+ end
142
+ complete_initialize()
143
+ end
138
144
 
139
- # Start the thread that will process server messages and add them to the output text
140
- process_server_messages(options)
145
+ def start(splash)
146
+ splash.message = "Initializing #{@options.title}" if splash
141
147
 
148
+ if !CmdTlmServer.instance or @mode == :CMD_TLM_SERVER
142
149
  CmdTlmServer.meta_callback = method(:meta_callback)
143
- cts = CmdTlmServer.new(options.config_file, @production)
150
+ cts = CmdTlmServer.new(@options.config_file, @production, false, @mode)
151
+ CmdTlmServerGui.configure_signal_handlers()
144
152
  cts.stop_callback = method(:stop_callback)
153
+ cts.reload_callback = method(:reload)
154
+ CmdTlmServer.replay_backend.config_change_callback = method(:config_change_callback) if @mode != :CMD_TLM_SERVER
145
155
  @message_log = CmdTlmServer.message_log
156
+ @ready = true
157
+ end
146
158
 
147
- # Now that we've started the server (CmdTlmServer.new) we can populate all the tabs
148
- splash.message = "Populating Tabs"
149
- Qt.execute_in_main_thread(true) do
150
- # Override the default title if one was given in the config file
151
- self.window_title = CmdTlmServer.title if CmdTlmServer.title
152
- splash.progress = 0
153
- @interfaces_tab.populate_interfaces(@tab_widget)
154
- splash.progress = 100/7 * 1
155
- @targets_tab.populate(@tab_widget)
156
- splash.progress = 100/7 * 2
157
- @packets_tab.populate_commands(@tab_widget)
158
- splash.progress = 100/7 * 3
159
- @packets_tab.populate_telemetry(@tab_widget)
160
- splash.progress = 100/7 * 4
161
- @interfaces_tab.populate_routers(@tab_widget)
162
- splash.progress = 100/7 * 5
163
- @logging_tab.populate(@tab_widget)
164
- splash.progress = 100/7 * 6
165
- @status_tab.populate(@tab_widget)
166
- splash.progress = 100
159
+ # Now that we've started the server (CmdTlmServer.new) we can populate all the tabs
160
+ splash.message = "Populating Tabs" if splash
161
+ Qt.execute_in_main_thread(true) do
162
+ # Override the default title if one was given in the config file
163
+ self.window_title = CmdTlmServer.title if CmdTlmServer.title
164
+ splash.progress = 0 if splash
165
+ @tabs_ready = false
166
+ if @mode == :CMD_TLM_SERVER
167
+ @interfaces_tab.populate_interfaces
168
+ else
169
+ @replay_tab.populate
167
170
  end
168
- ConfigParser.splash = nil
171
+ splash.progress = 100/7 * 1 if splash
172
+ @targets_tab.populate
173
+ splash.progress = 100/7 * 2 if splash
174
+ @commands_tab.populate_commands
175
+ splash.progress = 100/7 * 3 if splash
176
+ @telemetry_tab.populate_telemetry
177
+ splash.progress = 100/7 * 4 if splash
178
+ @routers_tab.populate_routers
179
+ if @mode == :CMD_TLM_SERVER
180
+ splash.progress = 100/7 * 5 if splash
181
+ @logging_tab.populate
182
+ end
183
+ splash.progress = 100/7 * 6 if splash
184
+ @status_tab.populate
185
+ splash.progress = 100 if splash
186
+ @tabs_ready = true
187
+ @tab_widget.setCurrentIndex(0)
188
+ handle_tab_change(0)
169
189
  end
170
190
  end
171
191
 
172
192
  def initialize_actions
173
193
  super()
174
194
 
195
+ # File actions
196
+ @file_reload = Qt::Action.new(tr('&Reload Configuration'), self)
197
+ @file_reload.statusTip = tr('Reload configuraton and reset')
198
+ @file_reload.connect(SIGNAL('triggered()')) do
199
+ CmdTlmServer.instance.reload()
200
+ end
201
+
175
202
  # Edit actions
176
203
  @edit_clear_counters = Qt::Action.new(tr('&Clear Counters'), self)
177
204
  @edit_clear_counters.statusTip = tr('Clear counters for all interfaces and targets')
@@ -180,6 +207,7 @@ module Cosmos
180
207
 
181
208
  def initialize_menus
182
209
  @file_menu = menuBar.addMenu(tr('&File'))
210
+ @file_menu.addAction(@file_reload)
183
211
  @file_menu.addAction(@exit_action)
184
212
 
185
213
  # Do not allow clear counters in production mode
@@ -188,8 +216,12 @@ module Cosmos
188
216
  @edit_menu.addAction(@edit_clear_counters)
189
217
  end
190
218
 
191
- @about_string = "#{TOOL_NAME} is the heart of the COSMOS system. "
192
- @about_string << "It connects to the target and processes command and telemetry requests from other tools."
219
+ if @mode == :CMD_TLM_SERVER
220
+ @about_string = "#{TOOL_NAME} is the heart of the COSMOS system. "
221
+ @about_string << "It connects to the target and processes command and telemetry requests from other tools."
222
+ else
223
+ @about_string = "Replay allows playing back data into the COSMOS realtime tools. "
224
+ end
193
225
 
194
226
  initialize_help_menu()
195
227
  end
@@ -221,30 +253,97 @@ module Cosmos
221
253
  Logger.level = Logger::INFO
222
254
 
223
255
  @tab_thread = nil
256
+
257
+ if @mode == :CMD_TLM_SERVER
258
+ @interfaces_tab = InterfacesTab.new(self, InterfacesTab::INTERFACES, @tab_widget)
259
+ else
260
+ @replay_tab = ReplayTab.new(@tab_widget)
261
+ end
262
+ @targets_tab = TargetsTab.new(@tab_widget)
263
+ @commands_tab = PacketsTab.new(self, PacketsTab::COMMANDS, @tab_widget)
264
+ @telemetry_tab = PacketsTab.new(self, PacketsTab::TELEMETRY, @tab_widget)
265
+ @routers_tab = InterfacesTab.new(self, InterfacesTab::ROUTERS, @tab_widget)
266
+ if @mode == :CMD_TLM_SERVER
267
+ @logging_tab = LoggingTab.new(@production, @tab_widget)
268
+ end
269
+ @status_tab = StatusTab.new(@tab_widget)
270
+ end
271
+
272
+ def config_change_callback
273
+ start(nil)
274
+ end
275
+
276
+ def reload(confirm = true)
277
+ Qt.execute_in_main_thread(true) do
278
+ if confirm
279
+ msg = Qt::MessageBox.new(self)
280
+ msg.setIcon(Qt::MessageBox::Question)
281
+ msg.setText("Are you sure? All connections will temporarily disconnect as the server restarts")
282
+ msg.setWindowTitle('Confirm Reload')
283
+ msg.setStandardButtons(Qt::MessageBox::Yes | Qt::MessageBox::No)
284
+ continue = false
285
+ continue = true if msg.exec() == Qt::MessageBox::Yes
286
+ msg.dispose
287
+ else
288
+ continue = true
289
+ end
290
+
291
+ if continue
292
+ Splash.execute(self) do |splash|
293
+ ConfigParser.splash = splash
294
+ Qt.execute_in_main_thread(true) do
295
+ @tab_widget.setCurrentIndex(0)
296
+ end
297
+ if @mode == :CMD_TLM_SERVER
298
+ Qt.execute_in_main_thread(true) do
299
+ splash.message = "Stopping Threads"
300
+ CmdTlmServer.instance.stop_callback = nil
301
+ stop_threads()
302
+ end
303
+ end
304
+ System.reset
305
+ start(splash)
306
+ Qt.execute_in_main_thread(true) do
307
+ @tab_widget.setCurrentIndex(0)
308
+ handle_tab_change(0)
309
+ end
310
+ ConfigParser.splash = nil
311
+ end
312
+ end
313
+ end
224
314
  end
225
315
 
226
316
  # Called when the user changes tabs in the Server application. It kills the
227
317
  # currently executing tab and then creates a new thread to update the GUI
228
318
  # for the selected tab.
229
319
  def handle_tab_change(index)
320
+ return unless @tabs_ready
230
321
  kill_tab_thread()
231
322
  @tab_sleeper = Sleeper.new
232
323
 
233
324
  case index
234
325
  when 0
235
- handle_tab('Interfaces') { @interfaces_tab.update(InterfacesTab::INTERFACES) }
326
+ if @mode == :CMD_TLM_SERVER
327
+ handle_tab('Interfaces') { @interfaces_tab.update }
328
+ else
329
+ handle_tab('Replay', 0.5) { @replay_tab.update }
330
+ end
236
331
  when 1
237
332
  handle_tab('Targets') { @targets_tab.update }
238
333
  when 2
239
- handle_tab('Commands') { @packets_tab.update(PacketsTab::COMMANDS) }
334
+ handle_tab('Commands') { @commands_tab.update }
240
335
  when 3
241
- handle_tab('Telemetry') { @packets_tab.update(PacketsTab::TELEMETRY) }
336
+ handle_tab('Telemetry') { @telemetry_tab.update }
242
337
  when 4
243
- handle_tab('Routers') { @interfaces_tab.update(InterfacesTab::ROUTERS) }
338
+ handle_tab('Routers') { @routers_tab.update }
244
339
  when 5
245
- handle_tab('Logging') { @logging_tab.update }
340
+ if @mode == :CMD_TLM_SERVER
341
+ handle_tab('Logging') { @logging_tab.update }
342
+ else
343
+ handle_tab('Status') { @status_tab.update }
344
+ end
246
345
  when 6
247
- handle_status_tab()
346
+ handle_tab('Status') { @status_tab.update }
248
347
  end
249
348
  end
250
349
 
@@ -252,7 +351,9 @@ module Cosmos
252
351
  def kill_tab_thread
253
352
  @tab_sleeper ||= nil
254
353
  @tab_sleeper.cancel if @tab_sleeper
354
+ @tab_thread_shutdown = true
255
355
  Qt::CoreApplication.instance.processEvents
356
+ Qt::RubyThreadFix.queue.pop.call until Qt::RubyThreadFix.queue.empty?
256
357
  Cosmos.kill_thread(self, @tab_thread)
257
358
  @tab_thread = nil
258
359
  end
@@ -262,12 +363,18 @@ module Cosmos
262
363
  # Finally it sleeps using a sleeper so it can be interrupted.
263
364
  #
264
365
  # @param name [String] Name of the tab
265
- def handle_tab(name)
366
+ def handle_tab(name, period = 1.0)
367
+ @tab_thread_shutdown = false
266
368
  @tab_thread = Thread.new do
267
369
  begin
268
370
  while true
371
+ start_time = Time.now.sys
372
+ break if @tab_thread_shutdown
269
373
  Qt.execute_in_main_thread(true) { yield }
270
- break if @tab_sleeper.sleep(1)
374
+ total_time = Time.now.sys - start_time
375
+ if total_time > 0.0 and total_time < period
376
+ break if @tab_sleeper.sleep(period - total_time)
377
+ end
271
378
  end
272
379
  rescue Exception => error
273
380
  Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : #{name} Tab Thread")}
@@ -275,22 +382,11 @@ module Cosmos
275
382
  end
276
383
  end
277
384
 
278
- # Update the status tab of the server
279
- def handle_status_tab
280
- @tab_thread = Thread.new do
281
- begin
282
- while true
283
- start_time = Time.now.sys
284
- Qt.execute_in_main_thread(true) { @status_tab.update }
285
- total_time = Time.now.sys - start_time
286
- if total_time > 0.0 and total_time < 1.0
287
- break if @tab_sleeper.sleep(1.0 - total_time)
288
- end
289
- end
290
- rescue Exception => error
291
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "COSMOS CTS : Status Tab Thread")}
292
- end
293
- end
385
+ def stop_threads
386
+ kill_tab_thread()
387
+ @replay_tab.shutdown if @replay_tab
388
+ CmdTlmServer.instance.stop_logging('ALL') if @mode == :CMD_TLM_SERVER
389
+ CmdTlmServer.instance.stop
294
390
  end
295
391
 
296
392
  # Called when the user tries to close the server application. Popup a
@@ -303,7 +399,11 @@ module Cosmos
303
399
  else
304
400
  msg = Qt::MessageBox.new(self)
305
401
  msg.setIcon(Qt::MessageBox::Question)
306
- msg.setText("Are you sure? All tools connected to this CmdTlmServer will lose connections and cease to function if the CmdTlmServer is closed.")
402
+ if @mode == :CMD_TLM_SERVER
403
+ msg.setText("Are you sure? All tools connected to this CmdTlmServer will lose connections and cease to function if the CmdTlmServer is closed.")
404
+ else
405
+ msg.setText("Are you sure? All tools connected to this Replay will lose connections and cease to function if the Replay is closed.")
406
+ end
307
407
  msg.setWindowTitle('Confirm Close')
308
408
  msg.setStandardButtons(Qt::MessageBox::Yes | Qt::MessageBox::No)
309
409
  continue = false
@@ -312,9 +412,7 @@ module Cosmos
312
412
  end
313
413
 
314
414
  if continue
315
- kill_tab_thread()
316
- CmdTlmServer.instance.stop_logging('ALL')
317
- CmdTlmServer.instance.stop
415
+ stop_threads()
318
416
  super(event)
319
417
  else
320
418
  event.ignore()
@@ -327,7 +425,7 @@ module Cosmos
327
425
  # Start thread to read server messages
328
426
  @output_thread = Thread.new do
329
427
  begin
330
- while !@message_log
428
+ while !@ready
331
429
  sleep(1)
332
430
  end
333
431
  while true
@@ -347,10 +445,10 @@ module Cosmos
347
445
  def handle_string_output
348
446
  if @string_output.string[-1..-1] == "\n"
349
447
  Qt.execute_in_main_thread(true) do
350
- lines_to_write = ''
448
+ lines_to_write = []
351
449
  string = @string_output.string.clone
352
450
  @string_output.string = @string_output.string[string.length..-1]
353
- string.each_line {|out_line| @output.add_formatted_text(out_line); lines_to_write << out_line }
451
+ string.each_line {|out_line| lines_to_write << out_line; @output.add_formatted_text(out_line) }
354
452
  @output.flush
355
453
  if @first_output < 2
356
454
  # Scroll to the bottom on the first two outputs for Linux
@@ -358,7 +456,9 @@ module Cosmos
358
456
  @output.verticalScrollBar.value = @output.verticalScrollBar.maximum
359
457
  @first_output += 1
360
458
  end
361
- @message_log.write(lines_to_write)
459
+ clean_lines, messages = CmdTlmServerGui.process_output_colors(lines_to_write)
460
+ @message_log.write(clean_lines) if @message_log
461
+ messages.each {|msg| CmdTlmServer.instance.post_server_message(msg) }
362
462
  end
363
463
  end
364
464
  end
@@ -366,11 +466,13 @@ module Cosmos
366
466
  # CmdTlmServer stop callback called by CmdTlmServer.stop. Ensures all the
367
467
  # output is written to the message logs.
368
468
  def stop_callback
369
- handle_string_output()
370
- @output_sleeper.cancel
371
- Qt::CoreApplication.processEvents()
372
- Cosmos.kill_thread(self, @output_thread)
373
- handle_string_output()
469
+ Qt.execute_in_main_thread(true) do
470
+ handle_string_output()
471
+ @output_sleeper.cancel
472
+ Qt::CoreApplication.processEvents()
473
+ Cosmos.kill_thread(self, @output_thread)
474
+ handle_string_output()
475
+ end
374
476
  end
375
477
 
376
478
  def graceful_kill
@@ -383,12 +485,14 @@ module Cosmos
383
485
 
384
486
  def self.no_gui_handle_string_output
385
487
  if @string_output.string[-1..-1] == "\n"
386
- lines_to_write = ''
488
+ lines_to_write = []
387
489
  string = @string_output.string.clone
388
490
  @string_output.string = @string_output.string[string.length..-1]
389
491
  string.each_line {|out_line| lines_to_write << out_line }
390
- @message_log.write(lines_to_write)
391
- STDOUT.print lines_to_write if STDIN.isatty # Have a console
492
+ clean_lines, messages = CmdTlmServerGui.process_output_colors(lines_to_write)
493
+ @message_log.write(clean_lines) if @mode == :CMD_TLM_SERVER
494
+ messages.each {|msg| CmdTlmServer.instance.post_server_message(msg) }
495
+ STDOUT.print clean_lines if STDIN.isatty # Have a console
392
496
  end
393
497
  end
394
498
 
@@ -399,7 +503,47 @@ module Cosmos
399
503
  no_gui_handle_string_output()
400
504
  end
401
505
 
506
+ def self.no_gui_reload_callback(confirm = false)
507
+ CmdTlmServer.instance.stop_logging('ALL') if @mode == :CMD_TLM_SERVER
508
+ CmdTlmServer.instance.stop_callback = nil
509
+ CmdTlmServer.instance.stop
510
+ System.reset
511
+ cts = CmdTlmServer.new(@options.config_file, @options.production, false, @mode)
512
+
513
+ # Signal catching needs to be repeated here because Puma interferes
514
+ ["TERM", "INT"].each {|sig| Signal.trap(sig) {exit}}
515
+
516
+ @message_log = CmdTlmServer.message_log
517
+ cts.stop_callback = method(:no_gui_stop_callback)
518
+ cts.reload_callback = method(:no_gui_reload_callback)
519
+ end
520
+
521
+ def self.process_output_colors(lines)
522
+ clean_lines = ''
523
+ messages = []
524
+ lines.each do |line|
525
+ if line =~ /<G>/
526
+ line.gsub!(/<G>/, BLANK)
527
+ messages << [line.strip, 'GREEN']
528
+ elsif line =~ /<Y>/
529
+ line.gsub!(/<Y>/, BLANK)
530
+ messages << [line.strip, 'YELLOW']
531
+ elsif line =~ /<R>/
532
+ line.gsub!(/<R>/, BLANK)
533
+ messages << [line.strip, 'RED']
534
+ elsif line =~ /<B>/
535
+ line.gsub!(/<B>/, BLANK)
536
+ messages << [line.strip, 'BLUE']
537
+ else
538
+ messages << [line.strip, 'BLACK']
539
+ end
540
+ clean_lines << line
541
+ end
542
+ return [clean_lines, messages]
543
+ end
544
+
402
545
  def self.post_options_parsed_hook(options)
546
+ @options = options
403
547
  if options.no_gui
404
548
  ["TERM", "INT"].each {|sig| Signal.trap(sig) {exit}}
405
549
 
@@ -408,8 +552,18 @@ module Cosmos
408
552
  @string_output = StringIO.new("", "r+")
409
553
  $stdout = @string_output
410
554
  Logger.level = Logger::INFO
411
- cts = CmdTlmServer.new(options.config_file, options.production)
555
+ if options.replay
556
+ @mode = :REPLAY
557
+ else
558
+ @mode = :CMD_TLM_SERVER
559
+ end
560
+ cts = CmdTlmServer.new(options.config_file, options.production, false, @mode)
561
+
562
+ # Signal catching needs to be repeated here because Puma interferes
563
+ ["TERM", "INT"].each {|sig| Signal.trap(sig) {exit}}
564
+
412
565
  @message_log = CmdTlmServer.message_log
566
+ @ready = true
413
567
  @output_thread = Thread.new do
414
568
  while true
415
569
  no_gui_handle_string_output()
@@ -417,29 +571,34 @@ module Cosmos
417
571
  end
418
572
  end
419
573
  cts.stop_callback = method(:no_gui_stop_callback)
574
+ cts.reload_callback = method(:no_gui_reload_callback)
420
575
  sleep # Sleep until waked by signal
421
576
  ensure
422
- if defined? cts and cts
423
- cts.stop_logging('ALL')
424
- cts.stop
577
+ if CmdTlmServer.instance
578
+ CmdTlmServer.instance.stop_logging('ALL') if @mode == :CMD_TLM_SERVER
579
+ CmdTlmServer.instance.stop
425
580
  end
426
581
  end
427
582
  return false
428
583
  else
429
- ["TERM", "INT"].each do |sig|
430
- Signal.trap(sig) do
431
- # No synchronization is allowed in trap context, so we have
432
- # to spawn a thread here to send the close event.
433
- Thread.new do
434
- Qt.execute_in_main_thread(true) do
435
- @@window.no_prompt = true
436
- @@window.closeEvent(Qt::CloseEvent.new())
437
- exit
438
- end
584
+ CmdTlmServerGui.configure_signal_handlers()
585
+ return true
586
+ end
587
+ end
588
+
589
+ def self.configure_signal_handlers
590
+ ["TERM", "INT"].each do |sig|
591
+ Signal.trap(sig) do
592
+ # No synchronization is allowed in trap context, so we have
593
+ # to spawn a thread here to send the close event.
594
+ Thread.new do
595
+ Qt.execute_in_main_thread(true) do
596
+ @@window.no_prompt = true
597
+ @@window.closeEvent(Qt::CloseEvent.new())
598
+ exit
439
599
  end
440
600
  end
441
601
  end
442
- return true
443
602
  end
444
603
  end
445
604
 
@@ -457,6 +616,13 @@ module Cosmos
457
616
  options.production = false
458
617
  options.no_prompt = false
459
618
  options.no_gui = false
619
+ if self.name == "Cosmos::Replay"
620
+ options.replay = true
621
+ options.title = "Replay"
622
+ else
623
+ options.replay = false
624
+ end
625
+
460
626
  option_parser.separator "CTS Specific Options:"
461
627
  option_parser.on("-c", "--config FILE", "Use the specified configuration file") do |arg|
462
628
  options.config_file = arg