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
@@ -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