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
|
@@ -20,38 +20,51 @@ module Cosmos
|
|
|
20
20
|
ROUTERS = 'Routers'
|
|
21
21
|
ALIGN_CENTER = Qt::AlignCenter
|
|
22
22
|
|
|
23
|
-
def initialize(server_gui)
|
|
23
|
+
def initialize(server_gui, name, tab_widget)
|
|
24
24
|
@server_gui = server_gui
|
|
25
|
-
@
|
|
25
|
+
@name = name
|
|
26
|
+
@widget = nil
|
|
27
|
+
reset()
|
|
28
|
+
@scroll = Qt::ScrollArea.new
|
|
29
|
+
@scroll.setMinimumSize(800, 150)
|
|
30
|
+
tab_widget.addTab(@scroll, @name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reset
|
|
34
|
+
Qt.execute_in_main_thread(true) do
|
|
35
|
+
@widget.destroy if @widget
|
|
36
|
+
@widget = nil
|
|
37
|
+
@interfaces_table = nil
|
|
38
|
+
end
|
|
26
39
|
end
|
|
27
40
|
|
|
28
41
|
# Create the interfaces tab and add it to the tab_widget
|
|
29
42
|
#
|
|
30
43
|
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
|
|
31
|
-
def populate_interfaces
|
|
32
|
-
populate(
|
|
44
|
+
def populate_interfaces
|
|
45
|
+
populate(CmdTlmServer.interfaces)
|
|
33
46
|
end
|
|
34
47
|
|
|
35
48
|
# Create the routers tab and add it to the tab_widget
|
|
36
49
|
#
|
|
37
50
|
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
|
|
38
|
-
def populate_routers
|
|
39
|
-
populate(
|
|
51
|
+
def populate_routers
|
|
52
|
+
populate(CmdTlmServer.routers)
|
|
40
53
|
end
|
|
41
54
|
|
|
42
55
|
# Update the interfaces or routers tab
|
|
43
56
|
#
|
|
44
57
|
# @param name [String] Must be Interfaces or Routers
|
|
45
|
-
def update
|
|
46
|
-
if name == ROUTERS
|
|
58
|
+
def update
|
|
59
|
+
if @name == ROUTERS
|
|
47
60
|
interfaces = CmdTlmServer.routers
|
|
48
61
|
else
|
|
49
62
|
interfaces = CmdTlmServer.interfaces
|
|
50
63
|
end
|
|
51
64
|
row = 0
|
|
52
65
|
interfaces.all.each do |interface_name, interface|
|
|
53
|
-
button = @interfaces_table
|
|
54
|
-
state = @interfaces_table
|
|
66
|
+
button = @interfaces_table.cellWidget(row,1)
|
|
67
|
+
state = @interfaces_table.item(row,2)
|
|
55
68
|
if interface.connected? and interface.thread
|
|
56
69
|
button.setText('Disconnect')
|
|
57
70
|
button.setDisabled(true) if interface.disable_disconnect
|
|
@@ -73,17 +86,17 @@ module Cosmos
|
|
|
73
86
|
state.setText('false')
|
|
74
87
|
state.textColor = Cosmos::BLACK
|
|
75
88
|
end
|
|
76
|
-
@interfaces_table
|
|
77
|
-
@interfaces_table
|
|
78
|
-
@interfaces_table
|
|
79
|
-
@interfaces_table
|
|
80
|
-
@interfaces_table
|
|
81
|
-
if name == ROUTERS
|
|
82
|
-
@interfaces_table
|
|
83
|
-
@interfaces_table
|
|
89
|
+
@interfaces_table.item(row,3).setText(interface.num_clients.to_s)
|
|
90
|
+
@interfaces_table.item(row,4).setText(interface.write_queue_size.to_s)
|
|
91
|
+
@interfaces_table.item(row,5).setText(interface.read_queue_size.to_s)
|
|
92
|
+
@interfaces_table.item(row,6).setText(interface.bytes_written.to_s)
|
|
93
|
+
@interfaces_table.item(row,7).setText(interface.bytes_read.to_s)
|
|
94
|
+
if @name == ROUTERS
|
|
95
|
+
@interfaces_table.item(row,8).setText(interface.read_count.to_s)
|
|
96
|
+
@interfaces_table.item(row,9).setText(interface.write_count.to_s)
|
|
84
97
|
else
|
|
85
|
-
@interfaces_table
|
|
86
|
-
@interfaces_table
|
|
98
|
+
@interfaces_table.item(row,8).setText(interface.write_count.to_s)
|
|
99
|
+
@interfaces_table.item(row,9).setText(interface.read_count.to_s)
|
|
87
100
|
end
|
|
88
101
|
row += 1
|
|
89
102
|
end
|
|
@@ -91,13 +104,12 @@ module Cosmos
|
|
|
91
104
|
|
|
92
105
|
private
|
|
93
106
|
|
|
94
|
-
def populate(
|
|
107
|
+
def populate(interfaces)
|
|
108
|
+
reset()
|
|
95
109
|
return if interfaces.all.empty?
|
|
96
110
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
widget = Qt::Widget.new
|
|
100
|
-
layout = Qt::VBoxLayout.new(widget)
|
|
111
|
+
@widget = Qt::Widget.new
|
|
112
|
+
layout = Qt::VBoxLayout.new(@widget)
|
|
101
113
|
# Since the layout will be inside a scroll area make sure it respects the sizes we set
|
|
102
114
|
layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
|
|
103
115
|
|
|
@@ -105,28 +117,27 @@ module Cosmos
|
|
|
105
117
|
interfaces_table.verticalHeader.hide()
|
|
106
118
|
interfaces_table.setRowCount(interfaces.all.length)
|
|
107
119
|
interfaces_table.setColumnCount(11)
|
|
108
|
-
if name == ROUTERS
|
|
120
|
+
if @name == ROUTERS
|
|
109
121
|
interfaces_table.setHorizontalHeaderLabels(["Router", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Pkts Rcvd ", " Pkts Sent ", "View Raw"])
|
|
110
122
|
else
|
|
111
123
|
interfaces_table.setHorizontalHeaderLabels(["Interface", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Cmd Pkts ", " Tlm Pkts ", "View Raw"])
|
|
112
124
|
end
|
|
113
125
|
|
|
114
|
-
populate_interface_table(
|
|
126
|
+
populate_interface_table(interfaces, interfaces_table)
|
|
115
127
|
interfaces_table.displayFullSize
|
|
116
128
|
|
|
117
129
|
layout.addWidget(interfaces_table)
|
|
118
|
-
scroll.setWidget(widget)
|
|
119
|
-
@interfaces_table
|
|
120
|
-
tab_widget.addTab(scroll, name)
|
|
130
|
+
@scroll.setWidget(@widget)
|
|
131
|
+
@interfaces_table = interfaces_table
|
|
121
132
|
end
|
|
122
133
|
|
|
123
|
-
def populate_interface_table(
|
|
134
|
+
def populate_interface_table(interfaces, interfaces_table)
|
|
124
135
|
row = 0
|
|
125
136
|
interfaces.all.each do |interface_name, interface|
|
|
126
137
|
item = Qt::TableWidgetItem.new(Qt::Object.tr(interface_name))
|
|
127
138
|
item.setTextAlignment(ALIGN_CENTER)
|
|
128
139
|
interfaces_table.setItem(row, 0, item)
|
|
129
|
-
interfaces_table.setCellWidget(row, 1, create_button(
|
|
140
|
+
interfaces_table.setCellWidget(row, 1, create_button(interface, interface_name))
|
|
130
141
|
interfaces_table.setItem(row, 2, create_state(interface))
|
|
131
142
|
|
|
132
143
|
index = 3
|
|
@@ -142,7 +153,7 @@ module Cosmos
|
|
|
142
153
|
view_raw = Qt::PushButton.new("View Raw")
|
|
143
154
|
view_raw.connect(SIGNAL('clicked()')) do
|
|
144
155
|
@raw_dialogs ||= []
|
|
145
|
-
if name == ROUTERS
|
|
156
|
+
if @name == ROUTERS
|
|
146
157
|
current_interface = CmdTlmServer.routers.all[interface_name]
|
|
147
158
|
else
|
|
148
159
|
current_interface = CmdTlmServer.interfaces.all[interface_name]
|
|
@@ -154,7 +165,7 @@ module Cosmos
|
|
|
154
165
|
end
|
|
155
166
|
end
|
|
156
167
|
|
|
157
|
-
def create_button(
|
|
168
|
+
def create_button(interface, interface_name)
|
|
158
169
|
if interface.connected? and interface.thread
|
|
159
170
|
button_text = 'Disconnect'
|
|
160
171
|
elsif interface.thread
|
|
@@ -165,7 +176,7 @@ module Cosmos
|
|
|
165
176
|
button_text = 'Connect'
|
|
166
177
|
end
|
|
167
178
|
button = Qt::PushButton.new(button_text)
|
|
168
|
-
if name == ROUTERS
|
|
179
|
+
if @name == ROUTERS
|
|
169
180
|
button.connect(SIGNAL('clicked()')) do
|
|
170
181
|
if CmdTlmServer.routers.all[interface_name].thread
|
|
171
182
|
Logger.info "User disconnecting router #{interface_name}"
|
|
@@ -16,18 +16,29 @@ module Cosmos
|
|
|
16
16
|
# Implements the logging tab in the Command and Telemetry Server GUI
|
|
17
17
|
class LoggingTab
|
|
18
18
|
|
|
19
|
-
def initialize(production)
|
|
19
|
+
def initialize(production, tab_widget)
|
|
20
20
|
@production = production
|
|
21
|
-
@
|
|
21
|
+
@widget = nil
|
|
22
|
+
reset()
|
|
23
|
+
@scroll = Qt::ScrollArea.new
|
|
24
|
+
tab_widget.addTab(@scroll, "Logging")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def reset
|
|
28
|
+
Qt.execute_in_main_thread(true) do
|
|
29
|
+
@widget.destroy if @widget
|
|
30
|
+
@widget = nil
|
|
31
|
+
@logging_layouts = {}
|
|
32
|
+
end
|
|
22
33
|
end
|
|
23
34
|
|
|
24
35
|
# Create the logging tab and add it to the tab_widget
|
|
25
36
|
#
|
|
26
37
|
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
|
|
27
|
-
def populate
|
|
28
|
-
|
|
29
|
-
widget = Qt::Widget.new
|
|
30
|
-
layout = Qt::VBoxLayout.new(widget)
|
|
38
|
+
def populate
|
|
39
|
+
reset()
|
|
40
|
+
@widget = Qt::Widget.new
|
|
41
|
+
layout = Qt::VBoxLayout.new(@widget)
|
|
31
42
|
# Since the layout will be inside a scroll area
|
|
32
43
|
# make sure it respects the sizes we set
|
|
33
44
|
layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
|
|
@@ -38,8 +49,7 @@ module Cosmos
|
|
|
38
49
|
populate_log_file_info(layout)
|
|
39
50
|
|
|
40
51
|
# Set the scroll area widget last now that all the items have been layed out
|
|
41
|
-
scroll.setWidget(widget)
|
|
42
|
-
tab_widget.addTab(scroll, "Logging")
|
|
52
|
+
@scroll.setWidget(@widget)
|
|
43
53
|
end
|
|
44
54
|
|
|
45
55
|
def update
|
|
@@ -19,23 +19,37 @@ module Cosmos
|
|
|
19
19
|
COMMANDS = "Commands"
|
|
20
20
|
TELEMETRY = "Telemetry"
|
|
21
21
|
|
|
22
|
-
def initialize(server_gui)
|
|
22
|
+
def initialize(server_gui, name, tab_widget)
|
|
23
23
|
@server_gui = server_gui
|
|
24
|
-
@
|
|
24
|
+
@name = name
|
|
25
|
+
@widget = nil
|
|
26
|
+
reset()
|
|
27
|
+
@scroll = Qt::ScrollArea.new
|
|
28
|
+
tab_name = "Cmd Packets" if name == COMMANDS
|
|
29
|
+
tab_name = "Tlm Packets" if name == TELEMETRY
|
|
30
|
+
tab_widget.addTab(@scroll, tab_name)
|
|
25
31
|
end
|
|
26
32
|
|
|
27
|
-
def
|
|
28
|
-
|
|
33
|
+
def reset
|
|
34
|
+
Qt.execute_in_main_thread(true) do
|
|
35
|
+
@widget.destroy if @widget
|
|
36
|
+
@widget = nil
|
|
37
|
+
@packets_table = nil
|
|
38
|
+
end
|
|
29
39
|
end
|
|
30
40
|
|
|
31
|
-
def
|
|
32
|
-
populate(
|
|
41
|
+
def populate_commands
|
|
42
|
+
populate(System.commands)
|
|
33
43
|
end
|
|
34
44
|
|
|
35
|
-
def
|
|
45
|
+
def populate_telemetry
|
|
46
|
+
populate(System.telemetry)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def update
|
|
36
50
|
cmd_tlm = nil
|
|
37
|
-
cmd_tlm = System.commands if name == COMMANDS
|
|
38
|
-
cmd_tlm = System.telemetry if name == TELEMETRY
|
|
51
|
+
cmd_tlm = System.commands if @name == COMMANDS
|
|
52
|
+
cmd_tlm = System.telemetry if @name == TELEMETRY
|
|
39
53
|
return if cmd_tlm.nil? || cmd_tlm.target_names.empty?
|
|
40
54
|
|
|
41
55
|
row = 0
|
|
@@ -43,18 +57,19 @@ module Cosmos
|
|
|
43
57
|
packets = cmd_tlm.packets(target_name)
|
|
44
58
|
packets.sort.each do |packet_name, packet|
|
|
45
59
|
next if packet.hidden
|
|
46
|
-
@packets_table
|
|
60
|
+
@packets_table.item(row, 2).setText(packet.received_count.to_s)
|
|
47
61
|
row += 1
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
packet = cmd_tlm.packet('UNKNOWN', 'UNKNOWN')
|
|
51
|
-
@packets_table
|
|
65
|
+
@packets_table.item(row, 2).setText(packet.received_count.to_s)
|
|
52
66
|
row += 1
|
|
53
67
|
end
|
|
54
68
|
|
|
55
69
|
private
|
|
56
70
|
|
|
57
|
-
def populate(
|
|
71
|
+
def populate(cmd_tlm)
|
|
72
|
+
reset()
|
|
58
73
|
return if cmd_tlm.target_names.empty?
|
|
59
74
|
|
|
60
75
|
count = 0
|
|
@@ -66,9 +81,8 @@ module Cosmos
|
|
|
66
81
|
end
|
|
67
82
|
count += 1 # For UNKNOWN UNKNOWN
|
|
68
83
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
layout = Qt::VBoxLayout.new(widget)
|
|
84
|
+
@widget = Qt::Widget.new
|
|
85
|
+
layout = Qt::VBoxLayout.new(@widget)
|
|
72
86
|
# Since the layout will be inside a scroll area
|
|
73
87
|
# make sure it respects the sizes we set
|
|
74
88
|
layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
|
|
@@ -81,23 +95,20 @@ module Cosmos
|
|
|
81
95
|
# Force the last section to fill all available space in the frame
|
|
82
96
|
#~ table.horizontalHeader.setStretchLastSection(true)
|
|
83
97
|
headers = ["Target Name", "Packet Name", "Packet Count", "View Raw"]
|
|
84
|
-
headers << "View in Command Sender" if name == COMMANDS
|
|
85
|
-
headers << "View in Packet Viewer" if name == TELEMETRY
|
|
98
|
+
headers << "View in Command Sender" if @name == COMMANDS
|
|
99
|
+
headers << "View in Packet Viewer" if @name == TELEMETRY
|
|
86
100
|
table.setHorizontalHeaderLabels(headers)
|
|
87
101
|
|
|
88
|
-
populate_packets_table(
|
|
102
|
+
populate_packets_table(cmd_tlm, table)
|
|
89
103
|
table.displayFullSize
|
|
90
104
|
|
|
91
105
|
layout.addWidget(table)
|
|
92
|
-
scroll.setWidget(widget)
|
|
93
|
-
tab_name = "Cmd Packets" if name == COMMANDS
|
|
94
|
-
tab_name = "Tlm Packets" if name == TELEMETRY
|
|
95
|
-
tab_widget.addTab(scroll, tab_name)
|
|
106
|
+
@scroll.setWidget(@widget)
|
|
96
107
|
|
|
97
|
-
@packets_table
|
|
108
|
+
@packets_table = table
|
|
98
109
|
end
|
|
99
110
|
|
|
100
|
-
def populate_packets_table(
|
|
111
|
+
def populate_packets_table(cmd_tlm, table)
|
|
101
112
|
row = 0
|
|
102
113
|
target_names = cmd_tlm.target_names
|
|
103
114
|
target_names << 'UNKNOWN'.freeze
|
|
@@ -116,14 +127,14 @@ module Cosmos
|
|
|
116
127
|
view_raw = Qt::PushButton.new("View Raw")
|
|
117
128
|
view_raw.connect(SIGNAL('clicked()')) do
|
|
118
129
|
@raw_dialogs ||= []
|
|
119
|
-
@raw_dialogs << CmdRawDialog.new(@server_gui, target_name, packet_name) if name == COMMANDS
|
|
120
|
-
@raw_dialogs << TlmRawDialog.new(@server_gui, target_name, packet_name) if name == TELEMETRY
|
|
130
|
+
@raw_dialogs << CmdRawDialog.new(@server_gui, target_name, packet_name) if @name == COMMANDS
|
|
131
|
+
@raw_dialogs << TlmRawDialog.new(@server_gui, target_name, packet_name) if @name == TELEMETRY
|
|
121
132
|
end
|
|
122
133
|
table.setCellWidget(row, 3, view_raw)
|
|
123
134
|
|
|
124
|
-
if name == COMMANDS
|
|
135
|
+
if @name == COMMANDS
|
|
125
136
|
add_tool_button(table, row, target_name, packet_name, "Command Sender")
|
|
126
|
-
elsif name == TELEMETRY
|
|
137
|
+
elsif @name == TELEMETRY
|
|
127
138
|
add_tool_button(table, row, target_name, packet_name, "Packet Viewer")
|
|
128
139
|
end
|
|
129
140
|
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2014 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
|
+
require 'cosmos'
|
|
12
|
+
require 'cosmos/gui/qt'
|
|
13
|
+
require 'cosmos/gui/choosers/string_chooser'
|
|
14
|
+
require 'cosmos/gui/dialogs/packet_log_dialog'
|
|
15
|
+
require 'cosmos/gui/dialogs/progress_dialog'
|
|
16
|
+
|
|
17
|
+
module Cosmos
|
|
18
|
+
|
|
19
|
+
# Implements the replay tab in the Command and Telemetry Server GUI
|
|
20
|
+
class ReplayTab
|
|
21
|
+
|
|
22
|
+
attr_accessor :widget
|
|
23
|
+
|
|
24
|
+
def initialize(tab_widget)
|
|
25
|
+
@widget = nil
|
|
26
|
+
@update_slider = true
|
|
27
|
+
@file_max_index = 0
|
|
28
|
+
reset()
|
|
29
|
+
@scroll = Qt::ScrollArea.new
|
|
30
|
+
tab_widget.addTab(@scroll, "Replay")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reset
|
|
34
|
+
CmdTlmServer.replay_backend.reset if CmdTlmServer.instance
|
|
35
|
+
Qt.execute_in_main_thread(true) do
|
|
36
|
+
@widget.destroy if @widget
|
|
37
|
+
@widget = nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Create the targets tab and add it to the tab_widget
|
|
42
|
+
#
|
|
43
|
+
# @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
|
|
44
|
+
def populate
|
|
45
|
+
return if @widget
|
|
46
|
+
|
|
47
|
+
@widget = Qt::Widget.new
|
|
48
|
+
|
|
49
|
+
layout = Qt::VBoxLayout.new(@widget)
|
|
50
|
+
# Since the layout will be inside a scroll area make sure it respects the sizes we set
|
|
51
|
+
layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
|
|
52
|
+
|
|
53
|
+
@log_widget = Qt::Widget.new
|
|
54
|
+
@log_widget.setSizePolicy(Qt::SizePolicy::MinimumExpanding, Qt::SizePolicy::MinimumExpanding)
|
|
55
|
+
@log_layout = Qt::VBoxLayout.new()
|
|
56
|
+
|
|
57
|
+
# This widget goes inside the top layout so we want 0 contents margins
|
|
58
|
+
@log_layout.setContentsMargins(0,0,0,0)
|
|
59
|
+
@log_widget.setLayout(@log_layout)
|
|
60
|
+
|
|
61
|
+
# Create the log file GUI
|
|
62
|
+
@log_file_selection = Qt::GroupBox.new("Log File Selection")
|
|
63
|
+
@log_select = Qt::HBoxLayout.new(@log_file_selection)
|
|
64
|
+
@log_name = Qt::LineEdit.new
|
|
65
|
+
@log_name.setReadOnly(true)
|
|
66
|
+
@log_select.addWidget(@log_name)
|
|
67
|
+
@log_open = Qt::PushButton.new("Browse...")
|
|
68
|
+
@log_select.addWidget(@log_open)
|
|
69
|
+
@log_layout.addWidget(@log_file_selection)
|
|
70
|
+
|
|
71
|
+
@log_open.connect(SIGNAL('clicked()')) { select_log_file() }
|
|
72
|
+
|
|
73
|
+
# Create the operation buttons GUI
|
|
74
|
+
@op = Qt::GroupBox.new("Playback Control")
|
|
75
|
+
@op_layout = Qt::VBoxLayout.new(@op)
|
|
76
|
+
@op_button_layout = Qt::HBoxLayout.new
|
|
77
|
+
@move_start = Qt::PushButton.new(Cosmos.get_icon('skip_to_start-26.png'), '')
|
|
78
|
+
@move_start.connect(SIGNAL('clicked()')) { CmdTlmServer.replay_backend.move_start() }
|
|
79
|
+
@op_button_layout.addWidget(@move_start)
|
|
80
|
+
@step_back = Qt::PushButton.new(Cosmos.get_icon('rewind-26.png'), '')
|
|
81
|
+
@step_back_timer = Qt::Timer.new
|
|
82
|
+
@step_back_timeout = 100
|
|
83
|
+
@step_back_timer.connect(SIGNAL('timeout()')) { CmdTlmServer.replay_backend.step_back(); @step_back_timeout = (@step_back_timeout / 2).to_i; @step_back_timer.start(@step_back_timeout) }
|
|
84
|
+
@step_back.connect(SIGNAL('pressed()')) { CmdTlmServer.replay_backend.step_back(); @step_back_timeout = 300; @step_back_timer.start(@step_back_timeout) }
|
|
85
|
+
@step_back.connect(SIGNAL('released()')) { @step_back_timer.stop }
|
|
86
|
+
@op_button_layout.addWidget(@step_back)
|
|
87
|
+
@reverse_play = Qt::PushButton.new(Cosmos.get_icon('reverse-play-26.png'), '')
|
|
88
|
+
@reverse_play.connect(SIGNAL('clicked()')) { CmdTlmServer.replay_backend.reverse_play() }
|
|
89
|
+
@op_button_layout.addWidget(@reverse_play)
|
|
90
|
+
@stop = Qt::PushButton.new(Cosmos.get_icon('stop-26.png'), '')
|
|
91
|
+
@stop.connect(SIGNAL('clicked()')) { CmdTlmServer.replay_backend.stop() }
|
|
92
|
+
@op_button_layout.addWidget(@stop)
|
|
93
|
+
@play = Qt::PushButton.new(Cosmos.get_icon('play-26.png'), '')
|
|
94
|
+
@play.connect(SIGNAL('clicked()')) { CmdTlmServer.replay_backend.play() }
|
|
95
|
+
@op_button_layout.addWidget(@play)
|
|
96
|
+
@step_forward = Qt::PushButton.new(Cosmos.get_icon('fast_forward-26.png'), '')
|
|
97
|
+
@step_forward_timer = Qt::Timer.new
|
|
98
|
+
@step_forward_timeout = 100
|
|
99
|
+
@step_forward_timer.connect(SIGNAL('timeout()')) { CmdTlmServer.replay_backend.step_forward(); @step_forward_timeout = (@step_forward_timeout / 2).to_i; @step_forward_timer.start(@step_forward_timeout) }
|
|
100
|
+
@step_forward.connect(SIGNAL('pressed()')) { CmdTlmServer.replay_backend.step_forward(); @step_forward_timeout = 300; @step_forward_timer.start(@step_forward_timeout) }
|
|
101
|
+
@step_forward.connect(SIGNAL('released()')) { @step_forward_timer.stop }
|
|
102
|
+
@op_button_layout.addWidget(@step_forward)
|
|
103
|
+
@move_end = Qt::PushButton.new(Cosmos.get_icon('end-26.png'), '')
|
|
104
|
+
@move_end.connect(SIGNAL('clicked()')) { CmdTlmServer.replay_backend.move_end() }
|
|
105
|
+
@op_button_layout.addWidget(@move_end)
|
|
106
|
+
@op_layout.addLayout(@op_button_layout)
|
|
107
|
+
|
|
108
|
+
# Speed Selection
|
|
109
|
+
@playback_delay = nil
|
|
110
|
+
@speed_select = Qt::ComboBox.new
|
|
111
|
+
@variants = []
|
|
112
|
+
@variants << [Qt::Variant.new(0.0), 0.0]
|
|
113
|
+
@speed_select.addItem("No Delay", @variants[-1][0])
|
|
114
|
+
@variants << [Qt::Variant.new(0.001), 0.001]
|
|
115
|
+
@speed_select.addItem("1ms Delay", @variants[-1][0])
|
|
116
|
+
@variants << [Qt::Variant.new(0.002), 0.002]
|
|
117
|
+
@speed_select.addItem("2ms Delay", @variants[-1][0])
|
|
118
|
+
@variants << [Qt::Variant.new(0.005), 0.005]
|
|
119
|
+
@speed_select.addItem("5ms Delay", @variants[-1][0])
|
|
120
|
+
@variants << [Qt::Variant.new(0.01), 0.01]
|
|
121
|
+
@speed_select.addItem("10ms Delay", @variants[-1][0])
|
|
122
|
+
@variants << [Qt::Variant.new(0.05), 0.05]
|
|
123
|
+
@speed_select.addItem("50ms Delay", @variants[-1][0])
|
|
124
|
+
@variants << [Qt::Variant.new(0.125), 0.125]
|
|
125
|
+
@speed_select.addItem("125ms Delay", @variants[-1][0])
|
|
126
|
+
@variants << [Qt::Variant.new(0.25), 0.25]
|
|
127
|
+
@speed_select.addItem("250ms Delay", @variants[-1][0])
|
|
128
|
+
@variants << [Qt::Variant.new(0.5), 0.5]
|
|
129
|
+
@speed_select.addItem("500ms Delay", @variants[-1][0])
|
|
130
|
+
@variants << [Qt::Variant.new(1.0), 1.0]
|
|
131
|
+
@speed_select.addItem("1s Delay", @variants[-1][0])
|
|
132
|
+
@variants << [Qt::Variant.new(nil), nil]
|
|
133
|
+
@speed_select.addItem("Realtime", @variants[-1][0])
|
|
134
|
+
@speed_select.setMaxVisibleItems(11)
|
|
135
|
+
@speed_select.connect(SIGNAL('currentIndexChanged(int)')) do
|
|
136
|
+
CmdTlmServer.replay_backend.set_playback_delay(@speed_select.itemData(@speed_select.currentIndex).value)
|
|
137
|
+
end
|
|
138
|
+
@speed_layout = Qt::FormLayout.new()
|
|
139
|
+
@speed_layout.addRow("&Delay:", @speed_select)
|
|
140
|
+
@status = Qt::LineEdit.new
|
|
141
|
+
@status.setReadOnly(true)
|
|
142
|
+
@status.setText('Stopped')
|
|
143
|
+
@speed_layout.addRow("&Status:", @status)
|
|
144
|
+
@op_layout.addLayout(@speed_layout)
|
|
145
|
+
@log_layout.addWidget(@op)
|
|
146
|
+
|
|
147
|
+
@file_pos = Qt::GroupBox.new("File Position")
|
|
148
|
+
@file_pos_layout = Qt::VBoxLayout.new(@file_pos)
|
|
149
|
+
@slider = Qt::Slider.new(Qt::Horizontal)
|
|
150
|
+
@slider.setRange(0, 10000)
|
|
151
|
+
@slider.setTickInterval(1000)
|
|
152
|
+
@slider.setTickPosition(Qt::Slider::TicksBothSides)
|
|
153
|
+
@slider.setTracking(false)
|
|
154
|
+
@slider.connect(SIGNAL('sliderPressed()')) { slider_pressed() }
|
|
155
|
+
@slider.connect(SIGNAL('sliderReleased()')) { slider_released() }
|
|
156
|
+
@time_layout = Qt::HBoxLayout.new()
|
|
157
|
+
@start_time = StringChooser.new(@widget, 'Start:', '', 200, true, true, Qt::AlignCenter | Qt::AlignVCenter)
|
|
158
|
+
@end_time = StringChooser.new(@widget, 'End:', '', 200, true, true, Qt::AlignCenter | Qt::AlignVCenter)
|
|
159
|
+
@current_time = StringChooser.new(@widget, 'Current:', '', 200, true, true, Qt::AlignCenter | Qt::AlignVCenter)
|
|
160
|
+
@time_layout.addWidget(@start_time)
|
|
161
|
+
@time_layout.addWidget(@current_time)
|
|
162
|
+
@time_layout.addWidget(@end_time)
|
|
163
|
+
@file_pos_layout.addLayout(@time_layout)
|
|
164
|
+
@file_pos_layout.addWidget(@slider)
|
|
165
|
+
@log_layout.addWidget(@file_pos)
|
|
166
|
+
layout.addWidget(@log_widget)
|
|
167
|
+
|
|
168
|
+
@scroll.setWidget(@widget)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Update the replay tab gui
|
|
172
|
+
def update
|
|
173
|
+
status, playback_delay, filename, file_start, file_current, file_end, file_index, @file_max_index = CmdTlmServer.replay_backend.status
|
|
174
|
+
@status.setText(status)
|
|
175
|
+
@log_name.text = filename
|
|
176
|
+
found = false
|
|
177
|
+
@variants.each_with_index do |data, index|
|
|
178
|
+
value = data[1]
|
|
179
|
+
if !playback_delay.nil? and !value.nil?
|
|
180
|
+
if (playback_delay >= (value - 0.0001)) and (playback_delay <= (value + 0.0001))
|
|
181
|
+
@speed_select.currentIndex = index
|
|
182
|
+
found = true
|
|
183
|
+
break
|
|
184
|
+
end
|
|
185
|
+
else
|
|
186
|
+
if playback_delay == value
|
|
187
|
+
@speed_select.currentIndex = index
|
|
188
|
+
found = true
|
|
189
|
+
break
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
unless found
|
|
194
|
+
@variants << [Qt::Variant.new(playback_delay.to_f), playback_delay.to_f]
|
|
195
|
+
@speed_select.addItem("#{(playback_delay.to_f * 1000.0).to_i}ms Delay", @variants[-1][0])
|
|
196
|
+
@speed_select.currentIndex = @variants.length - 1
|
|
197
|
+
end
|
|
198
|
+
@start_time.value = file_start
|
|
199
|
+
@current_time.value = file_current
|
|
200
|
+
@end_time.value = file_end
|
|
201
|
+
|
|
202
|
+
if @update_slider
|
|
203
|
+
if @file_max_index != 0
|
|
204
|
+
value = (((file_index - 1).to_f / @file_max_index.to_f) * 10000.0).to_i
|
|
205
|
+
else
|
|
206
|
+
value = 0
|
|
207
|
+
end
|
|
208
|
+
@slider.setSliderPosition(value)
|
|
209
|
+
@slider.setValue(value)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def slider_pressed
|
|
214
|
+
@update_slider = false
|
|
215
|
+
CmdTlmServer.replay_backend.stop
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def slider_released
|
|
219
|
+
CmdTlmServer.replay_backend.move_index(((@slider.sliderPosition / 10000.0) * (@file_max_index - 1)).to_i)
|
|
220
|
+
@update_slider = true
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def shutdown
|
|
224
|
+
CmdTlmServer.replay_backend.shutdown
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
private
|
|
228
|
+
|
|
229
|
+
def select_log_file
|
|
230
|
+
packet_log_dialog = PacketLogDialog.new(
|
|
231
|
+
@widget, 'Select Log File', CmdTlmServer.replay_backend.log_directory, CmdTlmServer.replay_backend.packet_log_reader,
|
|
232
|
+
[], nil, false, false, true, Cosmos::TLM_FILE_PATTERN,
|
|
233
|
+
Cosmos::BIN_FILE_PATTERN, false
|
|
234
|
+
)
|
|
235
|
+
case packet_log_dialog.exec
|
|
236
|
+
when Qt::Dialog::Accepted
|
|
237
|
+
CmdTlmServer.replay_backend.select_file(packet_log_dialog.filenames[0], packet_log_dialog.packet_log_reader)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
end
|
|
242
|
+
end # module Cosmos
|