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
@@ -102,6 +102,10 @@ module Cosmos
102
102
  @option_action.statusTip = tr('Application Options')
103
103
  connect(@option_action, SIGNAL('triggered()'), self, SLOT('file_options()'))
104
104
 
105
+ @replay_action = Qt::Action.new(tr('Toggle Replay Mode'), self)
106
+ @replay_action.statusTip = tr('Toggle Replay Mode')
107
+ @replay_action.connect(SIGNAL('triggered()')) { toggle_replay_mode() }
108
+
105
109
  @color_blind_action = Qt::Action.new(tr('Color&blind Mode'), self)
106
110
  @color_blind_keyseq = Qt::KeySequence.new(tr('Ctrl+B'))
107
111
  @color_blind_action.shortcut = @color_blind_keyseq
@@ -180,6 +184,7 @@ module Cosmos
180
184
  file_menu.addAction(@edit_action)
181
185
  file_menu.addAction(@reset_action)
182
186
  file_menu.addAction(@option_action)
187
+ file_menu.addAction(@replay_action)
183
188
  file_menu.addSeparator()
184
189
  file_menu.addAction(@exit_action)
185
190
 
@@ -207,6 +212,11 @@ module Cosmos
207
212
  # Create the top level vertical layout
208
213
  top_layout = Qt::VBoxLayout.new(central_widget)
209
214
 
215
+ @replay_flag = Qt::Label.new("Replay Mode")
216
+ @replay_flag.setStyleSheet("background:green;color:white;padding:5px;font-weight:bold;")
217
+ top_layout.addWidget(@replay_flag)
218
+ @replay_flag.hide
219
+
210
220
  # Set the target combobox selection
211
221
  @target_select = Qt::ComboBox.new
212
222
  @target_select.setMaxVisibleItems(6)
@@ -265,6 +275,15 @@ module Cosmos
265
275
  @polling_rate, 0, 1000, 1, nil)
266
276
  end
267
277
 
278
+ def toggle_replay_mode
279
+ set_replay_mode(!get_replay_mode())
280
+ if get_replay_mode()
281
+ @replay_flag.show
282
+ else
283
+ @replay_flag.hide
284
+ end
285
+ end
286
+
268
287
  def edit_definition
269
288
  # Grab all the cmd_tlm_files and processes them in reverse sort order
270
289
  # because typically we'll have cmd.txt and tlm.txt and we want to process
@@ -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
@@ -9,509 +9,9 @@
9
9
  # attribution addendums as found in the LICENSE.txt
10
10
 
11
11
  require 'cosmos'
12
- Cosmos.catch_fatal_exception do
13
- require 'cosmos/gui/qt_tool'
14
- require 'cosmos/gui/dialogs/splash'
15
- require 'cosmos/gui/dialogs/progress_dialog'
16
- require 'cosmos/gui/dialogs/packet_log_dialog'
17
- require 'cosmos/tools/replay/replay_server'
18
- require 'cosmos/gui/choosers/string_chooser'
19
- end
12
+ require 'cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui'
20
13
 
21
14
  module Cosmos
22
-
23
- class Replay < QtTool
24
- # The number of bytes to print when an UNKNOWN packet is received
25
- UNKNOWN_BYTES_TO_PRINT = 36
26
-
27
- def initialize(options)
28
- # MUST BE FIRST - All code before super is executed twice in RubyQt Based classes
29
- super(options)
30
- Cosmos.load_cosmos_icon("replay.png")
31
-
32
- initialize_actions()
33
- initialize_menus()
34
- initialize_central_widget()
35
- complete_initialize()
36
-
37
- @ready = false
38
- Splash.execute(self) do |splash|
39
- ConfigParser.splash = splash
40
- splash.message = "Initializing Replay Server"
41
-
42
- # Start the thread that will process server messages and add them to the output text
43
- process_server_messages(options)
44
-
45
- ReplayServer.new(options.config_file, false, false, false)
46
- @ready = true
47
-
48
- ConfigParser.splash = nil
49
- end
50
-
51
- # Initialize variables
52
- @packet_log_reader = System.default_packet_log_reader.new(*System.default_packet_log_reader_params)
53
- @log_directory = System.paths['LOGS']
54
- @log_directory << '/' unless @log_directory[-1..-1] == '\\' or @log_directory[-1..-1] == '/'
55
- @log_filename = nil
56
- @playing = false
57
- @playback_thread = nil
58
- @playback_index = 0
59
- @packet_offsets = []
60
- end
61
-
62
- def initialize_menus
63
- # File Menu
64
- @file_menu = menuBar.addMenu(tr('&File'))
65
- @file_menu.addAction(@exit_action)
66
-
67
- # Help Menu
68
- @about_string = "Telemetry Viewer provides a view of every telemetry packet in the system."
69
- @about_string << " Packets can be viewed in numerous represenations ranging from the raw data to formatted with units."
70
-
71
- initialize_help_menu()
72
- end
73
-
74
- def initialize_central_widget
75
- # Create the central widget
76
- @central_widget = Qt::Widget.new
77
- setCentralWidget(@central_widget)
78
-
79
- @top_layout = Qt::VBoxLayout.new
80
-
81
- @log_widget = Qt::Widget.new
82
- @log_widget.setSizePolicy(Qt::SizePolicy::MinimumExpanding, Qt::SizePolicy::MinimumExpanding)
83
- @log_layout = Qt::VBoxLayout.new()
84
-
85
- # This widget goes inside the top layout so we want 0 contents margins
86
- @log_layout.setContentsMargins(0,0,0,0)
87
- @log_widget.setLayout(@log_layout)
88
-
89
- # Create the log file GUI
90
- @log_file_selection = Qt::GroupBox.new("Log File Selection")
91
- @log_select = Qt::HBoxLayout.new(@log_file_selection)
92
- @log_name = Qt::LineEdit.new
93
- @log_name.setReadOnly(true)
94
- @log_select.addWidget(@log_name)
95
- @log_open = Qt::PushButton.new("Browse...")
96
- @log_select.addWidget(@log_open)
97
- @log_layout.addWidget(@log_file_selection)
98
-
99
- @log_open.connect(SIGNAL('clicked()')) { select_log_file() }
100
-
101
- # Create the operation buttons GUI
102
- @op = Qt::GroupBox.new(tr("Playback Control"))
103
- @op_layout = Qt::VBoxLayout.new(@op)
104
- @op_button_layout = Qt::HBoxLayout.new
105
- @move_start = Qt::PushButton.new(Cosmos.get_icon('skip_to_start-26.png'), '')
106
- @move_start.connect(SIGNAL('clicked()')) { move_start() }
107
- @op_button_layout.addWidget(@move_start)
108
- @step_back = Qt::PushButton.new(Cosmos.get_icon('rewind-26.png'), '')
109
- @step_back_timer = Qt::Timer.new
110
- @step_back_timeout = 100
111
- @step_back_timer.connect(SIGNAL('timeout()')) { step_back(); @step_back_timeout = (@step_back_timeout / 2).to_i; @step_back_timer.start(@step_back_timeout) }
112
- @step_back.connect(SIGNAL('pressed()')) { step_back(); @step_back_timeout = 300; @step_back_timer.start(@step_back_timeout) }
113
- @step_back.connect(SIGNAL('released()')) { @step_back_timer.stop }
114
- @op_button_layout.addWidget(@step_back)
115
- @reverse_play = Qt::PushButton.new(Cosmos.get_icon('reverse-play-26.png'), '')
116
- @reverse_play.connect(SIGNAL('clicked()')) { reverse_play() }
117
- @op_button_layout.addWidget(@reverse_play)
118
- @stop = Qt::PushButton.new(Cosmos.get_icon('stop-26.png'), '')
119
- @stop.connect(SIGNAL('clicked()')) { stop() }
120
- @op_button_layout.addWidget(@stop)
121
- @play = Qt::PushButton.new(Cosmos.get_icon('play-26.png'), '')
122
- @play.connect(SIGNAL('clicked()')) { play() }
123
- @op_button_layout.addWidget(@play)
124
- @step_forward = Qt::PushButton.new(Cosmos.get_icon('fast_forward-26.png'), '')
125
- @step_forward_timer = Qt::Timer.new
126
- @step_forward_timeout = 100
127
- @step_forward_timer.connect(SIGNAL('timeout()')) { step_forward(); @step_forward_timeout = (@step_forward_timeout / 2).to_i; @step_forward_timer.start(@step_forward_timeout) }
128
- @step_forward.connect(SIGNAL('pressed()')) { step_forward(); @step_forward_timeout = 300; @step_forward_timer.start(@step_forward_timeout) }
129
- @step_forward.connect(SIGNAL('released()')) { @step_forward_timer.stop }
130
- @op_button_layout.addWidget(@step_forward)
131
- @move_end = Qt::PushButton.new(Cosmos.get_icon('end-26.png'), '')
132
- @move_end.connect(SIGNAL('clicked()')) { move_end() }
133
- @op_button_layout.addWidget(@move_end)
134
- @op_layout.addLayout(@op_button_layout)
135
-
136
- # Speed Selection
137
- @playback_delay = nil
138
- @speed_select = Qt::ComboBox.new
139
- @variants = []
140
- @variants << Qt::Variant.new(nil)
141
- @speed_select.addItem("No Delay", @variants[-1])
142
- @variants << Qt::Variant.new(0.001)
143
- @speed_select.addItem("1ms Delay", @variants[-1])
144
- @variants << Qt::Variant.new(0.002)
145
- @speed_select.addItem("2ms Delay", @variants[-1])
146
- @variants << Qt::Variant.new(0.005)
147
- @speed_select.addItem("5ms Delay", @variants[-1])
148
- @variants << Qt::Variant.new(0.01)
149
- @speed_select.addItem("10ms Delay", @variants[-1])
150
- @variants << Qt::Variant.new(0.05)
151
- @speed_select.addItem("50ms Delay", @variants[-1])
152
- @variants << Qt::Variant.new(0.125)
153
- @speed_select.addItem("125ms Delay", @variants[-1])
154
- @variants << Qt::Variant.new(0.25)
155
- @speed_select.addItem("250ms Delay", @variants[-1])
156
- @variants << Qt::Variant.new(0.5)
157
- @speed_select.addItem("500ms Delay", @variants[-1])
158
- @variants << Qt::Variant.new(1.0)
159
- @speed_select.addItem("1s Delay", @variants[-1])
160
- @variants << Qt::Variant.new(-1.0)
161
- @speed_select.addItem("Realtime", @variants[-1])
162
- @speed_select.setMaxVisibleItems(11)
163
- @speed_select.connect(SIGNAL('currentIndexChanged(int)')) do
164
- @playback_delay = @speed_select.itemData(@speed_select.currentIndex).value
165
- end
166
- @speed_layout = Qt::FormLayout.new()
167
- @speed_layout.addRow("&Delay:", @speed_select)
168
- @status = Qt::LineEdit.new
169
- @status.setReadOnly(true)
170
- @status.setText('Stopped')
171
- @speed_layout.addRow("&Status:", @status)
172
- @op_layout.addLayout(@speed_layout)
173
- @log_layout.addWidget(@op)
174
-
175
- @file_pos = Qt::GroupBox.new(tr("File Position"))
176
- @file_pos_layout = Qt::VBoxLayout.new(@file_pos)
177
- @slider = Qt::Slider.new(Qt::Horizontal)
178
- @slider.setRange(0, 10000)
179
- @slider.setTickInterval(1000)
180
- @slider.setTickPosition(Qt::Slider::TicksBothSides)
181
- @slider.setTracking(false)
182
- @slider.connect(SIGNAL('sliderReleased()')) { slider_released() }
183
- @time_layout = Qt::HBoxLayout.new()
184
- @start_time = StringChooser.new(self, 'Start:', '', 200, true, true, Qt::AlignCenter | Qt::AlignVCenter)
185
- @end_time = StringChooser.new(self, 'End:', '', 200, true, true, Qt::AlignCenter | Qt::AlignVCenter)
186
- @current_time = StringChooser.new(self, 'Current:', '', 200, true, true, Qt::AlignCenter | Qt::AlignVCenter)
187
- @time_layout.addWidget(@start_time)
188
- @time_layout.addWidget(@current_time)
189
- @time_layout.addWidget(@end_time)
190
- @file_pos_layout.addLayout(@time_layout)
191
- @file_pos_layout.addWidget(@slider)
192
- @log_layout.addWidget(@file_pos)
193
- @top_layout.addWidget(@log_widget)
194
-
195
- # Add the message output
196
- @output = Qt::PlainTextEdit.new
197
- @output.setReadOnly(true)
198
- @output.setMaximumBlockCount(100)
199
-
200
- @top_layout.addWidget(@output, 500)
201
-
202
- # Override stdout to the message window
203
- # All code attempting to print into the GUI must use $stdout rather than STDOUT
204
- @string_output = StringIO.new("", "r+")
205
- $stdout = @string_output
206
- Logger.level = Logger::INFO
207
-
208
- @central_widget.setLayout(@top_layout)
209
- end
210
-
211
- def select_log_file
212
- unless @playback_thread
213
- packet_log_dialog = PacketLogDialog.new(
214
- self, 'Select Log File', @log_directory, @packet_log_reader,
215
- [], nil, false, false, true, Cosmos::TLM_FILE_PATTERN,
216
- Cosmos::BIN_FILE_PATTERN, false
217
- )
218
- case packet_log_dialog.exec
219
- when Qt::Dialog::Accepted
220
- stop()
221
- @packet_log_reader = packet_log_dialog.packet_log_reader
222
- @log_filename = packet_log_dialog.filenames[0]
223
- @log_directory = File.dirname(@log_filename)
224
- @log_directory << '/' unless @log_directory[-1..-1] == '\\'
225
- @log_name.text = @log_filename
226
-
227
- System.telemetry.reset
228
- @cancel = false
229
- ProgressDialog.execute(self, 'Analyzing Log File', 500, 10, true, false, true, false, true) do |progress_dialog|
230
- progress_dialog.append_text("Processing File: #{@log_filename}\n")
231
- progress_dialog.set_overall_progress(0.0)
232
- progress_dialog.cancel_callback = method(:cancel_callback)
233
- progress_dialog.enable_cancel_button
234
- Cosmos.check_log_configuration(@packet_log_reader, @log_filename)
235
- @packet_offsets = @packet_log_reader.packet_offsets(@log_filename, lambda {|percentage| progress_dialog.set_overall_progress(percentage); @cancel})
236
- @playback_index = 0
237
- update_slider_and_current_time(nil)
238
- @packet_log_reader.open(@log_filename)
239
- progress_dialog.close_done
240
- end
241
-
242
- if ProgressDialog.canceled?
243
- @packet_log_reader.close
244
- @log_name.text = ''
245
- @log_filename = nil
246
- @packet_offsets = []
247
- @playback_index = 0
248
- @start_time.value = ''
249
- @current_time.value = ''
250
- @end_time.value = ''
251
- else
252
- move_end()
253
- move_start()
254
- end
255
- end
256
- end
257
- end
258
-
259
- def cancel_callback(progress_dialog = nil)
260
- @cancel = true
261
- return true, false
262
- end
263
-
264
- def move_start
265
- if @log_filename and !@playback_thread
266
- packet = read_at_index(0, :FORWARD)
267
- @start_time.value = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
268
- else
269
- stop()
270
- end
271
- end
272
-
273
- def step_back
274
- if @log_filename and !@playback_thread
275
- @playback_index = @packet_offsets.length - 2 if @playback_index >= @packet_offsets.length
276
- read_at_index(@playback_index, :BACKWARD)
277
- else
278
- stop()
279
- end
280
- end
281
-
282
- def reverse_play
283
- if @log_filename and !@playback_thread
284
- @playback_index = @packet_offsets.length - 2 if @playback_index >= @packet_offsets.length
285
- start_playback(:BACKWARD)
286
- else
287
- stop()
288
- end
289
- end
290
-
291
- def stop
292
- @playing = false
293
- end
294
-
295
- def play
296
- if @log_filename and !@playback_thread
297
- @playback_index = 1 if @playback_index < 0
298
- start_playback(:FORWARD)
299
- else
300
- stop()
301
- end
302
- end
303
-
304
- def step_forward
305
- if @log_filename and !@playback_thread
306
- @playback_index = 1 if @playback_index < 0
307
- read_at_index(@playback_index, :FORWARD)
308
- else
309
- stop()
310
- end
311
- end
312
-
313
- def move_end
314
- if @log_filename and !@playback_thread
315
- packet = read_at_index(@packet_offsets.length - 1, :FORWARD)
316
- @end_time.value = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
317
- else
318
- stop()
319
- end
320
- end
321
-
322
- def slider_released
323
- if @log_filename and !@playback_thread
324
- read_at_index(((@slider.sliderPosition / 10000.0) * (@packet_offsets.length - 1)).to_i, :FORWARD)
325
- end
326
- end
327
-
328
- def start_playback(direction)
329
- @playback_thread = Thread.new do
330
- error = nil
331
- begin
332
- @playing = true
333
- Qt.execute_in_main_thread(true) do
334
- @status.setText('Playing')
335
- end
336
- previous_packet = nil
337
- while (@playing)
338
- if @playback_delay
339
- packet_start = Time.now.sys
340
- packet = read_at_index(@playback_index, direction)
341
- break unless packet
342
- delay_time = 0.0
343
- if @playback_delay > 0.0
344
- delay_time = @playback_delay - (Time.now.sys - packet_start)
345
- elsif previous_packet and packet.received_time and previous_packet.received_time
346
- if direction == :FORWARD
347
- delay_time = packet.received_time - previous_packet.received_time - (Time.now.sys - packet_start)
348
- else
349
- delay_time = previous_packet.received_time - packet.received_time - (Time.now.sys - packet_start)
350
- end
351
- end
352
- sleep(delay_time) if delay_time > 0.0
353
- previous_packet = packet
354
- else
355
- packet = read_at_index(@playback_index, direction)
356
- break unless packet
357
- end
358
- end
359
- rescue Exception => error
360
- Qt.execute_in_main_thread(true) {|| ExceptionDialog.new(self, error, "Playback Thread")}
361
- ensure
362
- Qt.execute_in_main_thread(true) do
363
- @status.setText('Stopped')
364
- end
365
- @playing = false
366
- @playback_thread = nil
367
- end
368
- end
369
- end
370
-
371
- def read_at_index(index, direction)
372
- packet_offset = nil
373
- packet_offset = @packet_offsets[index] if index >= 0
374
- if packet_offset
375
- # Read the packet
376
- packet = @packet_log_reader.read_at_offset(packet_offset, false)
377
- handle_packet(packet)
378
-
379
- # Adjust index for next read
380
- if direction == :FORWARD
381
- @playback_index = index + 1
382
- else
383
- @playback_index = index - 1
384
- end
385
- update_slider_and_current_time(packet)
386
-
387
- return packet
388
- else
389
- return nil
390
- end
391
- end
392
-
393
- def update_slider_and_current_time(packet)
394
- Qt.execute_in_main_thread(false) do
395
- value = (((@playback_index - 1) / @packet_offsets.length.to_f) * 10000).to_i
396
- @slider.setSliderPosition(value)
397
- @slider.setValue(value)
398
- @current_time.value = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
399
- end
400
- end
401
-
402
- def handle_packet(packet)
403
- # For replay we will try our best here but not crash on errors
404
- begin
405
- interface = nil
406
-
407
- # Identify and update packet
408
- if packet.identified?
409
- # Preidentifed packet - place it into the current value table
410
- identified_packet = System.telemetry.update!(packet.target_name,
411
- packet.packet_name,
412
- packet.buffer)
413
- else
414
- # Packet needs to be identified
415
- identified_packet = System.telemetry.identify!(packet.buffer)
416
- end
417
-
418
- if identified_packet and packet.target_name != 'UNKNOWN'
419
- identified_packet.received_time = packet.received_time
420
- packet = identified_packet
421
- target = System.targets[packet.target_name.upcase]
422
- interface = target.interface if target
423
- else
424
- unknown_packet = System.telemetry.update!('UNKNOWN', 'UNKNOWN', packet.buffer)
425
- unknown_packet.received_time = packet.received_time
426
- packet = unknown_packet
427
- data_length = packet.length
428
- string = "Unknown #{data_length} byte packet starting: "
429
- num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, data_length].min
430
- data_to_print = packet.buffer(false)[0..(num_bytes_to_print - 1)]
431
- data_to_print.each_byte do |byte|
432
- string << sprintf("%02X", byte)
433
- end
434
- time_string = ''
435
- time_string = packet.received_time.formatted << ' ' if packet.received_time
436
- puts "#{time_string}ERROR: #{string}"
437
- end
438
-
439
- target = System.targets[packet.target_name]
440
- target.tlm_cnt += 1 if target
441
- packet.received_count += 1
442
- packet.check_limits(System.limits_set)
443
- ReplayServer.instance.post_packet(packet)
444
-
445
- # Write to routers
446
- if interface
447
- interface.routers.each do |router|
448
- begin
449
- router.write(packet) if router.write_allowed? and router.connected?
450
- rescue => err
451
- Logger.error "Problem writing to router #{router.name} - #{err.class}:#{err.message}"
452
- end
453
- end
454
- end
455
- rescue Exception => err
456
- Logger.error "Problem handling packet #{packet.target_name} #{packet.packet_name} - #{err.class}:#{err.message}"
457
- end
458
- end
459
-
460
- def process_server_messages(options)
461
- # Start thread to read server messages
462
- @output_thread = Thread.new do
463
- begin
464
- while !@ready
465
- sleep(1)
466
- end
467
- while true
468
- if @string_output.string[-1..-1] == "\n"
469
- Qt.execute_in_main_thread(true) do
470
- string = @string_output.string.clone
471
- @string_output.string = @string_output.string[string.length..-1]
472
- string.each_line {|out_line| @output.add_formatted_text(out_line) }
473
- @output.flush
474
- end
475
- end
476
- sleep(1)
477
- end
478
- rescue Exception => error
479
- Qt.execute_in_main_thread(true) do
480
- ExceptionDialog.new(self, error, "#{options.title}: Messages Thread")
481
- end
482
- end
483
- end
484
- end
485
-
486
- def closeEvent(event)
487
- Cosmos.kill_thread(self, @playback_thread)
488
- super(event)
489
- end
490
-
491
- # Gracefully kill threads
492
- def graceful_kill
493
- stop()
494
- end
495
-
496
- def self.run(option_parser = nil, options = nil)
497
- Cosmos.catch_fatal_exception do
498
- unless option_parser and options
499
- option_parser, options = create_default_options()
500
- options.title = 'Replay'
501
- options.width = 800
502
- options.height = 500
503
- options.auto_size = false
504
- options.config_file = CmdTlmServer::DEFAULT_CONFIG_FILE
505
- option_parser.separator "Replay Specific Options:"
506
- option_parser.on("-c", "--config FILE", "Use the specified configuration file") do |arg|
507
- options.config_file = arg
508
- end
509
- end
510
-
511
- super(option_parser, options)
512
- end
513
- end
514
-
515
- end # class Replay
516
-
517
- end # module Cosmos
15
+ class Replay < CmdTlmServerGui
16
+ end
17
+ end