cosmos 4.0.3 → 4.1.0

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