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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -5
- data/Manifest.txt +11 -1
- data/README.md +3 -2
- data/Rakefile +18 -4
- data/appveyor.yml +19 -0
- data/cosmos.gemspec +12 -3
- data/data/config/cmd_tlm_server.yaml +3 -0
- data/data/crc.txt +63 -60
- data/demo/config/targets/INST/cmd_tlm_server.txt +1 -0
- data/demo/config/targets/INST/cmd_tlm_server2.txt +7 -0
- data/demo/config/tools/cmd_sequence/cmd_sequence.txt +2 -0
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +8 -12
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +7 -9
- data/demo/lib/cmd_sequence_exporter.rb +52 -0
- data/demo/lib/example_background_task.rb +1 -0
- data/demo/procedures/replay_test.rb +32 -0
- data/ext/cosmos/ext/structure/structure.c +39 -3
- data/install/config/tools/cmd_tlm_server/cmd_tlm_server.txt +1 -0
- data/install/config/tools/launcher/launcher.txt +2 -0
- data/lib/cosmos/config/config_parser.rb +2 -0
- data/lib/cosmos/core_ext/io.rb +89 -60
- data/lib/cosmos/gui/qt.rb +5 -8
- data/lib/cosmos/gui/qt_tool.rb +8 -8
- data/lib/cosmos/gui/text/ruby_editor.rb +12 -12
- data/lib/cosmos/gui/utilities/script_module_gui.rb +9 -9
- data/lib/cosmos/gui/widgets/realtime_button_bar.rb +18 -17
- data/lib/cosmos/interfaces/protocols/fixed_protocol.rb +2 -2
- data/lib/cosmos/interfaces/protocols/template_protocol.rb +3 -0
- data/lib/cosmos/interfaces/udp_interface.rb +27 -14
- data/lib/cosmos/io/buffered_file.rb +0 -1
- data/lib/cosmos/io/json_drb.rb +134 -214
- data/lib/cosmos/io/json_drb_object.rb +22 -61
- data/lib/cosmos/io/json_drb_rack.rb +79 -0
- data/lib/cosmos/io/json_rpc.rb +27 -0
- data/lib/cosmos/io/udp_sockets.rb +102 -58
- data/lib/cosmos/packets/commands.rb +1 -1
- data/lib/cosmos/packets/structure.rb +1 -1
- data/lib/cosmos/packets/structure_item.rb +37 -5
- data/lib/cosmos/script/cmd_tlm_server.rb +76 -2
- data/lib/cosmos/script/replay.rb +60 -0
- data/lib/cosmos/script/script.rb +20 -2
- data/lib/cosmos/script/scripting.rb +9 -9
- data/lib/cosmos/script/tools.rb +14 -0
- data/lib/cosmos/system/system.rb +185 -92
- data/lib/cosmos/system/target.rb +1 -1
- data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +44 -4
- data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +4 -0
- data/lib/cosmos/tools/cmd_sequence/sequence_list.rb +7 -0
- data/lib/cosmos/tools/cmd_tlm_server/api.rb +347 -20
- data/lib/cosmos/tools/cmd_tlm_server/background_tasks.rb +3 -0
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +329 -111
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +13 -0
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +261 -95
- data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +46 -35
- data/lib/cosmos/tools/cmd_tlm_server/gui/logging_tab.rb +18 -8
- data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +39 -28
- data/lib/cosmos/tools/cmd_tlm_server/gui/replay_tab.rb +242 -0
- data/lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb +24 -8
- data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +18 -6
- data/lib/cosmos/tools/cmd_tlm_server/limits_groups_background_task.rb +5 -4
- data/lib/cosmos/tools/cmd_tlm_server/replay_backend.rb +375 -0
- data/lib/cosmos/tools/cmd_tlm_server/routers.rb +10 -2
- data/lib/cosmos/tools/data_viewer/data_viewer.rb +40 -5
- data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +18 -20
- data/lib/cosmos/tools/launcher/launcher_config.rb +5 -16
- data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +65 -39
- data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +19 -0
- data/lib/cosmos/tools/replay/replay.rb +5 -505
- data/lib/cosmos/tools/script_runner/script_audit.rb +1 -0
- data/lib/cosmos/tools/script_runner/script_runner.rb +3 -4
- data/lib/cosmos/tools/script_runner/script_runner_config.rb +3 -4
- data/lib/cosmos/tools/script_runner/script_runner_frame.rb +44 -23
- data/lib/cosmos/tools/test_runner/results_writer.rb +4 -0
- data/lib/cosmos/tools/test_runner/test_runner.rb +0 -3
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +6 -2
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_tool.rb +26 -1
- data/lib/cosmos/tools/tlm_viewer/screen.rb +24 -1
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +25 -0
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +24 -14
- data/lib/cosmos/top_level.rb +34 -24
- data/lib/cosmos/utilities/csv.rb +60 -8
- data/lib/cosmos/version.rb +5 -5
- data/spec/config/config_parser_spec.rb +10 -1
- data/spec/core_ext/socket_spec.rb +4 -2
- data/spec/gui/utilities/script_module_gui_spec.rb +102 -0
- data/spec/install/config/data/data.txt +1 -0
- data/spec/install/config/targets/INST/cmd_tlm/inst_cmds.txt +2 -0
- data/spec/interfaces/cmd_tlm_server_interface_spec.rb +1 -2
- data/spec/interfaces/protocols/template_protocol_spec.rb +72 -2
- data/spec/interfaces/serial_interface_spec.rb +1 -1
- data/spec/interfaces/udp_interface_spec.rb +14 -0
- data/spec/io/buffered_file_spec.rb +37 -0
- data/spec/io/json_drb_object_spec.rb +2 -15
- data/spec/io/json_drb_spec.rb +61 -121
- data/spec/io/udp_sockets_spec.rb +42 -2
- data/spec/packet_logs/packet_log_reader_spec.rb +5 -2
- data/spec/packets/binary_accessor_spec.rb +1 -1
- data/spec/packets/packet_item_spec.rb +1 -1
- data/spec/packets/structure_item_spec.rb +5 -6
- data/spec/script/cmd_tlm_server_spec.rb +39 -4
- data/spec/script/commands_disconnect_spec.rb +1 -1
- data/spec/script/commands_spec.rb +2 -1
- data/spec/script/scripting_spec.rb +18 -3
- data/spec/script/telemetry_spec.rb +5 -0
- data/spec/spec_helper.rb +43 -26
- data/spec/streams/tcpip_socket_stream_spec.rb +2 -2
- data/spec/system/system_spec.rb +11 -9
- data/spec/system/target_spec.rb +3 -0
- data/spec/tools/cmd_tlm_server/api_spec.rb +543 -29
- data/spec/tools/cmd_tlm_server/background_task_spec.rb +2 -2
- data/spec/tools/cmd_tlm_server/background_tasks_spec.rb +31 -75
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +199 -66
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +85 -9
- data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +29 -127
- data/spec/tools/cmd_tlm_server/router_thread_spec.rb +10 -50
- data/spec/tools/launcher/launcher_config_spec.rb +1 -1
- data/spec/tools/table_manager/table_item_spec.rb +1 -1
- data/spec/tools/table_manager/tablemanager_core_spec.rb +4 -4
- data/spec/top_level/top_level_spec.rb +151 -3
- data/spec/utilities/csv_spec.rb +24 -5
- metadata +61 -9
- data/lib/cosmos/tools/replay/replay_server.rb +0 -91
|
@@ -14,21 +14,33 @@ require 'cosmos/gui/qt'
|
|
|
14
14
|
module Cosmos
|
|
15
15
|
# Implements the status tab in the Command and Telemetry Server GUI
|
|
16
16
|
class StatusTab
|
|
17
|
+
def initialize(tab_widget)
|
|
18
|
+
@widget = nil
|
|
19
|
+
reset()
|
|
20
|
+
@scroll = Qt::ScrollArea.new
|
|
21
|
+
tab_widget.addTab(@scroll, "Status")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reset
|
|
25
|
+
Qt.execute_in_main_thread(true) do
|
|
26
|
+
@widget.destroy if @widget
|
|
27
|
+
@widget = nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
17
31
|
# Create the status tab and add it to the tab_widget
|
|
18
32
|
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
|
|
19
|
-
def populate
|
|
20
|
-
|
|
21
|
-
widget = Qt::Widget.new
|
|
22
|
-
layout = Qt::VBoxLayout.new(widget)
|
|
33
|
+
def populate
|
|
34
|
+
reset()
|
|
35
|
+
@widget = Qt::Widget.new
|
|
36
|
+
layout = Qt::VBoxLayout.new(@widget)
|
|
23
37
|
|
|
24
38
|
populate_limits_status(layout)
|
|
25
39
|
populate_api_status(layout)
|
|
26
40
|
populate_system_status(layout)
|
|
27
41
|
populate_background_status(layout)
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
scroll.setWidget(widget)
|
|
31
|
-
tab_widget.addTab(scroll, "Status")
|
|
43
|
+
@scroll.setWidget(@widget)
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
# Update the status tab in the GUI
|
|
@@ -85,7 +97,11 @@ module Cosmos
|
|
|
85
97
|
@api_table.setColumnCount(6)
|
|
86
98
|
@api_table.setHorizontalHeaderLabels(["Port", "Num Clients", "Requests", "Requests/Sec", "Avg Request Time", "Estimated Utilization"])
|
|
87
99
|
|
|
88
|
-
|
|
100
|
+
if CmdTlmServer.mode == :CMD_TLM_SERVER
|
|
101
|
+
@api_table.setItem(0, 0, Qt::TableWidgetItem.new(Qt::Object.tr(System.ports['CTS_API'].to_s)))
|
|
102
|
+
else
|
|
103
|
+
@api_table.setItem(0, 0, Qt::TableWidgetItem.new(Qt::Object.tr(System.ports['REPLAY_API'].to_s)))
|
|
104
|
+
end
|
|
89
105
|
item0 = Qt::TableWidgetItem.new(Qt::Object.tr(CmdTlmServer.json_drb.num_clients.to_s))
|
|
90
106
|
item0.setTextAlignment(Qt::AlignCenter)
|
|
91
107
|
@api_table.setItem(0, 1, item0)
|
|
@@ -15,16 +15,29 @@ module Cosmos
|
|
|
15
15
|
|
|
16
16
|
# Implements the targets tab in the Command and Telemetry Server GUI
|
|
17
17
|
class TargetsTab
|
|
18
|
+
def initialize(tab_widget)
|
|
19
|
+
@widget = nil
|
|
20
|
+
reset()
|
|
21
|
+
@scroll = Qt::ScrollArea.new
|
|
22
|
+
tab_widget.addTab(@scroll, "Targets")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def reset
|
|
26
|
+
Qt.execute_in_main_thread(true) do
|
|
27
|
+
@widget.destroy if @widget
|
|
28
|
+
@widget = nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
18
31
|
|
|
19
32
|
# Create the targets tab and add it to the tab_widget
|
|
20
33
|
#
|
|
21
34
|
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
|
|
22
|
-
def populate
|
|
35
|
+
def populate
|
|
36
|
+
reset()
|
|
23
37
|
num_targets = System.targets.length
|
|
24
38
|
if num_targets > 0
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
layout = Qt::VBoxLayout.new(widget)
|
|
39
|
+
@widget = Qt::Widget.new
|
|
40
|
+
layout = Qt::VBoxLayout.new(@widget)
|
|
28
41
|
# Since the layout will be inside a scroll area make sure it respects the sizes we set
|
|
29
42
|
layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
|
|
30
43
|
|
|
@@ -39,8 +52,7 @@ module Cosmos
|
|
|
39
52
|
@targets_table.displayFullSize
|
|
40
53
|
|
|
41
54
|
layout.addWidget(@targets_table)
|
|
42
|
-
scroll.setWidget(widget)
|
|
43
|
-
tab_widget.addTab(scroll, "Targets")
|
|
55
|
+
@scroll.setWidget(@widget)
|
|
44
56
|
end
|
|
45
57
|
end
|
|
46
58
|
|
|
@@ -24,6 +24,7 @@ module Cosmos
|
|
|
24
24
|
@task_delay = Float(task_delay)
|
|
25
25
|
@name = "Limits Groups"
|
|
26
26
|
@groups = get_limits_groups()
|
|
27
|
+
@sleeper = Sleeper.new
|
|
27
28
|
# Initialize all the group names as instance variables
|
|
28
29
|
@groups.each {|group| self.instance_variable_set("@#{group.downcase}", nil) }
|
|
29
30
|
end
|
|
@@ -80,9 +81,9 @@ module Cosmos
|
|
|
80
81
|
|
|
81
82
|
def call
|
|
82
83
|
@status = "Starting the LimitsGroupsBackgroundTask"
|
|
83
|
-
check_methods = find_check_methods()
|
|
84
|
-
sleep @initial_delay
|
|
85
84
|
@sleeper = Sleeper.new
|
|
85
|
+
check_methods = find_check_methods()
|
|
86
|
+
return if @sleeper.sleep(@initial_delay)
|
|
86
87
|
loop do
|
|
87
88
|
start = Time.now
|
|
88
89
|
check_methods.each {|method| self.send(method.intern) }
|
|
@@ -90,13 +91,13 @@ module Cosmos
|
|
|
90
91
|
@status = "#{now.formatted}: Checking groups took #{now - start}s"
|
|
91
92
|
sleep_time = @task_delay - (now - start)
|
|
92
93
|
sleep_time = 0 if sleep_time < 0
|
|
93
|
-
|
|
94
|
-
break if broken
|
|
94
|
+
return if @sleeper.sleep(sleep_time)
|
|
95
95
|
end
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def stop
|
|
99
99
|
@sleeper.cancel
|
|
100
|
+
@status = "Stopped at #{Time.now.sys.formatted}"
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
protected
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2017 Ball Aerospace & Technologies Corp.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
|
7
|
+
# under the terms of the GNU General Public License
|
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
|
10
|
+
|
|
11
|
+
module Cosmos
|
|
12
|
+
|
|
13
|
+
# Handles logic for the Replay mode
|
|
14
|
+
class ReplayBackend
|
|
15
|
+
|
|
16
|
+
# The number of bytes to print when an UNKNOWN packet is received
|
|
17
|
+
UNKNOWN_BYTES_TO_PRINT = 36
|
|
18
|
+
|
|
19
|
+
attr_accessor :log_directory
|
|
20
|
+
attr_accessor :log_filename
|
|
21
|
+
attr_accessor :packet_log_reader
|
|
22
|
+
attr_accessor :config_change_callback
|
|
23
|
+
|
|
24
|
+
# @param cmd_tlm_server_config [CmdTlmServerConfig]
|
|
25
|
+
def initialize(cmd_tlm_server_config)
|
|
26
|
+
@config = cmd_tlm_server_config
|
|
27
|
+
reset()
|
|
28
|
+
@config_change_callback = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Reset internal state
|
|
32
|
+
def reset
|
|
33
|
+
@cancel = false
|
|
34
|
+
@playback_delay = 0.0
|
|
35
|
+
@default_packet_log_reader = System.default_packet_log_reader.new(*System.default_packet_log_reader_params)
|
|
36
|
+
@packet_log_reader = @default_packet_log_reader
|
|
37
|
+
@log_directory = System.paths['LOGS']
|
|
38
|
+
@log_directory << '/' unless @log_directory[-1..-1] == '\\' or @log_directory[-1..-1] == '/'
|
|
39
|
+
@log_filename = nil
|
|
40
|
+
@playing = false
|
|
41
|
+
@playback_sleeper = nil
|
|
42
|
+
@thread = nil
|
|
43
|
+
@playback_index = 0
|
|
44
|
+
@playback_max_index = 0
|
|
45
|
+
@packet_offsets = []
|
|
46
|
+
@progress = 0
|
|
47
|
+
@status = ''
|
|
48
|
+
@start_time = ''
|
|
49
|
+
@current_time = ''
|
|
50
|
+
@end_time = ''
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Select and start analyzing a file for replay
|
|
54
|
+
#
|
|
55
|
+
# filename [String] filename relative to output logs folder or absolute filename
|
|
56
|
+
def select_file(filename, packet_log_reader = 'DEFAULT')
|
|
57
|
+
stop()
|
|
58
|
+
Cosmos.kill_thread(self, @thread)
|
|
59
|
+
@thread = Thread.new do
|
|
60
|
+
begin
|
|
61
|
+
stop()
|
|
62
|
+
if String === packet_log_reader
|
|
63
|
+
if packet_log_reader == 'DEFAULT'
|
|
64
|
+
@packet_log_reader = @default_packet_log_reader
|
|
65
|
+
else
|
|
66
|
+
packet_log_reader = Cosmos.require_class(packet_log_reader)
|
|
67
|
+
@packet_log_reader = packet_log_reader_class.new
|
|
68
|
+
end
|
|
69
|
+
elsif !packet_log_reader.nil?
|
|
70
|
+
# Instantiated object
|
|
71
|
+
@packet_log_reader = packet_log_reader
|
|
72
|
+
end # Else use existing
|
|
73
|
+
|
|
74
|
+
@log_filename = filename
|
|
75
|
+
@log_directory = File.dirname(@log_filename)
|
|
76
|
+
@log_directory << '/' unless @log_directory[-1..-1] == '\\'
|
|
77
|
+
|
|
78
|
+
System.telemetry.reset
|
|
79
|
+
@cancel = false
|
|
80
|
+
@progress = 0
|
|
81
|
+
@status = "Analyzing: #{@progress}%"
|
|
82
|
+
start_config_name = System.configuration_name
|
|
83
|
+
config_change_success, config_error = Cosmos.check_log_configuration(@packet_log_reader, @log_filename)
|
|
84
|
+
if System.configuration_name != start_config_name
|
|
85
|
+
@config_change_callback.call() if @config_change_callback
|
|
86
|
+
end
|
|
87
|
+
@packet_offsets = @packet_log_reader.packet_offsets(@log_filename, lambda {|percentage|
|
|
88
|
+
progress_int = (percentage * 100).to_i
|
|
89
|
+
if @progress != progress_int
|
|
90
|
+
@progress = progress_int
|
|
91
|
+
@status = "Analyzing: #{@progress}%"
|
|
92
|
+
end
|
|
93
|
+
@cancel
|
|
94
|
+
})
|
|
95
|
+
@playback_index = 0
|
|
96
|
+
@playback_max_index = @packet_offsets.length
|
|
97
|
+
@packet_log_reader.open(@log_filename)
|
|
98
|
+
|
|
99
|
+
if @cancel
|
|
100
|
+
@packet_log_reader.close
|
|
101
|
+
@log_filename = ''
|
|
102
|
+
@packet_offsets = []
|
|
103
|
+
@playback_index = 0
|
|
104
|
+
@start_time = ''
|
|
105
|
+
@current_time = ''
|
|
106
|
+
@end_time = ''
|
|
107
|
+
else
|
|
108
|
+
packet = read_at_index(@packet_offsets.length - 1, :FORWARD)
|
|
109
|
+
@end_time = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
|
|
110
|
+
packet = read_at_index(0, :FORWARD)
|
|
111
|
+
@start_time = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
|
|
112
|
+
end
|
|
113
|
+
rescue Exception => error
|
|
114
|
+
Logger.error "Error in Analysis Thread\n#{error.formatted}"
|
|
115
|
+
ensure
|
|
116
|
+
@status = 'Stopped'
|
|
117
|
+
@playing = false
|
|
118
|
+
@playback_sleeper = nil
|
|
119
|
+
@thread = nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Get current replay status
|
|
125
|
+
#
|
|
126
|
+
# @return [status, playback_delay, filename, file_start, file_current, file_end, file_index, file_max_index]
|
|
127
|
+
def status
|
|
128
|
+
[@status,
|
|
129
|
+
@playback_delay,
|
|
130
|
+
@log_filename.to_s,
|
|
131
|
+
@start_time,
|
|
132
|
+
@current_time,
|
|
133
|
+
@end_time,
|
|
134
|
+
@playback_index,
|
|
135
|
+
@playback_max_index]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Set the replay delay
|
|
139
|
+
#
|
|
140
|
+
# @param delay [Float] delay between packets in seconds 0.0 to 1.0, nil = No Delay, -1.0 = REALTIME
|
|
141
|
+
def set_playback_delay(delay)
|
|
142
|
+
if delay
|
|
143
|
+
delay = delay.to_f
|
|
144
|
+
if delay <= 0.0
|
|
145
|
+
@playback_delay = 0.0
|
|
146
|
+
elsif delay > 1.0
|
|
147
|
+
@playback_delay = 1.0
|
|
148
|
+
else
|
|
149
|
+
@playback_delay = delay
|
|
150
|
+
end
|
|
151
|
+
else
|
|
152
|
+
@playback_delay = nil
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Replay start playing forward
|
|
157
|
+
def play
|
|
158
|
+
if @log_filename and !@thread
|
|
159
|
+
@playback_index = 1 if @playback_index < 0
|
|
160
|
+
start_playback(:FORWARD)
|
|
161
|
+
else
|
|
162
|
+
stop()
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Replay start playing backward
|
|
167
|
+
def reverse_play
|
|
168
|
+
if @log_filename and !@thread
|
|
169
|
+
@playback_index = @packet_offsets.length - 2 if @playback_index >= @packet_offsets.length
|
|
170
|
+
start_playback(:BACKWARD)
|
|
171
|
+
else
|
|
172
|
+
stop()
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Replay stop
|
|
177
|
+
def stop
|
|
178
|
+
@cancel = true
|
|
179
|
+
@playing = false
|
|
180
|
+
@playback_sleeper.cancel if @playback_sleeper
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Replay step forward one packet
|
|
184
|
+
def step_forward
|
|
185
|
+
if @log_filename and !@thread
|
|
186
|
+
@playback_index = 1 if @playback_index < 0
|
|
187
|
+
read_at_index(@playback_index, :FORWARD)
|
|
188
|
+
else
|
|
189
|
+
stop()
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Replay step backward one packet
|
|
194
|
+
def step_back
|
|
195
|
+
if @log_filename and !@thread
|
|
196
|
+
@playback_index = @packet_offsets.length - 2 if @playback_index >= @packet_offsets.length
|
|
197
|
+
read_at_index(@playback_index, :BACKWARD)
|
|
198
|
+
else
|
|
199
|
+
stop()
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Replay move to start of file
|
|
204
|
+
def move_start
|
|
205
|
+
if @log_filename and !@thread
|
|
206
|
+
packet = read_at_index(0, :FORWARD)
|
|
207
|
+
@start_time = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
|
|
208
|
+
else
|
|
209
|
+
stop()
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Replay move to end of file
|
|
214
|
+
def move_end
|
|
215
|
+
if @log_filename and !@thread
|
|
216
|
+
packet = read_at_index(@packet_offsets.length - 1, :FORWARD)
|
|
217
|
+
@end_time = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
|
|
218
|
+
else
|
|
219
|
+
stop()
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Replay move to index
|
|
224
|
+
#
|
|
225
|
+
# @param index [Integer] packet index into file
|
|
226
|
+
def move_index(index)
|
|
227
|
+
if @log_filename and !@thread
|
|
228
|
+
read_at_index(index, :FORWARD)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def shutdown
|
|
233
|
+
stop()
|
|
234
|
+
Cosmos.kill_thread(self, @thread)
|
|
235
|
+
reset()
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Gracefully kill threads
|
|
239
|
+
def graceful_kill
|
|
240
|
+
stop()
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
private
|
|
244
|
+
|
|
245
|
+
def start_playback(direction)
|
|
246
|
+
@thread = Thread.new do
|
|
247
|
+
@playback_sleeper = Sleeper.new
|
|
248
|
+
error = nil
|
|
249
|
+
begin
|
|
250
|
+
@playing = true
|
|
251
|
+
@status = 'Playing'
|
|
252
|
+
|
|
253
|
+
previous_packet = nil
|
|
254
|
+
while (@playing)
|
|
255
|
+
if @playback_delay != 0.0
|
|
256
|
+
packet_start = Time.now.sys
|
|
257
|
+
packet = read_at_index(@playback_index, direction)
|
|
258
|
+
break unless packet
|
|
259
|
+
delay_time = 0.0
|
|
260
|
+
if @playback_delay
|
|
261
|
+
# Fixed Time Delay
|
|
262
|
+
delay_time = @playback_delay - (Time.now.sys - packet_start)
|
|
263
|
+
elsif previous_packet and packet.received_time and previous_packet.received_time
|
|
264
|
+
# Realtime
|
|
265
|
+
if direction == :FORWARD
|
|
266
|
+
delay_time = packet.received_time - previous_packet.received_time - (Time.now.sys - packet_start)
|
|
267
|
+
else
|
|
268
|
+
delay_time = previous_packet.received_time - packet.received_time - (Time.now.sys - packet_start)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
if delay_time > 0.0
|
|
272
|
+
break if @playback_sleeper.sleep(delay_time)
|
|
273
|
+
end
|
|
274
|
+
previous_packet = packet
|
|
275
|
+
else
|
|
276
|
+
# No Delay
|
|
277
|
+
packet = read_at_index(@playback_index, direction)
|
|
278
|
+
break unless packet
|
|
279
|
+
previous_packet = packet
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
rescue Exception => error
|
|
283
|
+
Logger.error "Error in Playback Thread\n#{error.formatted}"
|
|
284
|
+
ensure
|
|
285
|
+
@status = 'Stopped'
|
|
286
|
+
@playing = false
|
|
287
|
+
@playback_sleeper = nil
|
|
288
|
+
@thread = nil
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def read_at_index(index, direction)
|
|
294
|
+
packet_offset = nil
|
|
295
|
+
packet_offset = @packet_offsets[index] if index >= 0
|
|
296
|
+
if packet_offset
|
|
297
|
+
# Read the packet
|
|
298
|
+
packet = @packet_log_reader.read_at_offset(packet_offset, false)
|
|
299
|
+
handle_packet(packet)
|
|
300
|
+
|
|
301
|
+
# Adjust index for next read
|
|
302
|
+
if direction == :FORWARD
|
|
303
|
+
@playback_index = index + 1
|
|
304
|
+
else
|
|
305
|
+
@playback_index = index - 1
|
|
306
|
+
end
|
|
307
|
+
@current_time = packet.received_time.formatted(true, 3, true) if packet and packet.received_time
|
|
308
|
+
|
|
309
|
+
return packet
|
|
310
|
+
else
|
|
311
|
+
return nil
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def handle_packet(packet)
|
|
316
|
+
# For replay we will try our best here but not crash on errors
|
|
317
|
+
begin
|
|
318
|
+
interface = nil
|
|
319
|
+
|
|
320
|
+
# Identify and update packet
|
|
321
|
+
if packet.identified?
|
|
322
|
+
# Preidentifed packet - place it into the current value table
|
|
323
|
+
identified_packet = System.telemetry.update!(packet.target_name,
|
|
324
|
+
packet.packet_name,
|
|
325
|
+
packet.buffer)
|
|
326
|
+
else
|
|
327
|
+
# Packet needs to be identified
|
|
328
|
+
identified_packet = System.telemetry.identify!(packet.buffer)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
if identified_packet and packet.target_name != 'UNKNOWN'
|
|
332
|
+
identified_packet.received_time = packet.received_time
|
|
333
|
+
packet = identified_packet
|
|
334
|
+
target = System.targets[packet.target_name.upcase]
|
|
335
|
+
interface = target.interface if target
|
|
336
|
+
else
|
|
337
|
+
unknown_packet = System.telemetry.update!('UNKNOWN', 'UNKNOWN', packet.buffer)
|
|
338
|
+
unknown_packet.received_time = packet.received_time
|
|
339
|
+
packet = unknown_packet
|
|
340
|
+
data_length = packet.length
|
|
341
|
+
string = "Unknown #{data_length} byte packet starting: "
|
|
342
|
+
num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, data_length].min
|
|
343
|
+
data_to_print = packet.buffer(false)[0..(num_bytes_to_print - 1)]
|
|
344
|
+
data_to_print.each_byte do |byte|
|
|
345
|
+
string << sprintf("%02X", byte)
|
|
346
|
+
end
|
|
347
|
+
time_string = ''
|
|
348
|
+
time_string = packet.received_time.formatted << ' ' if packet.received_time
|
|
349
|
+
puts "#{time_string}ERROR: #{string}"
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
target = System.targets[packet.target_name]
|
|
353
|
+
target.tlm_cnt += 1 if target
|
|
354
|
+
packet.received_count += 1
|
|
355
|
+
packet.check_limits(System.limits_set)
|
|
356
|
+
CmdTlmServer.instance.post_packet(packet)
|
|
357
|
+
|
|
358
|
+
# Write to routers
|
|
359
|
+
if interface
|
|
360
|
+
interface.routers.each do |router|
|
|
361
|
+
begin
|
|
362
|
+
router.write(packet) if router.write_allowed? and router.connected?
|
|
363
|
+
rescue => err
|
|
364
|
+
Logger.error "Problem writing to router #{router.name} - #{err.class}:#{err.message}"
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
rescue Exception => err
|
|
369
|
+
Logger.error "Problem handling packet #{packet.target_name} #{packet.packet_name} - #{err.class}:#{err.message}"
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
end # class ReplayBackend
|
|
374
|
+
|
|
375
|
+
end # module Cosmos
|