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
@@ -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
- @interfaces_table = {}
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(tab_widget)
32
- populate(INTERFACES, CmdTlmServer.interfaces, tab_widget)
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(tab_widget)
39
- populate(ROUTERS, CmdTlmServer.routers, tab_widget)
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(name)
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[name].cellWidget(row,1)
54
- state = @interfaces_table[name].item(row,2)
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[name].item(row,3).setText(interface.num_clients.to_s)
77
- @interfaces_table[name].item(row,4).setText(interface.write_queue_size.to_s)
78
- @interfaces_table[name].item(row,5).setText(interface.read_queue_size.to_s)
79
- @interfaces_table[name].item(row,6).setText(interface.bytes_written.to_s)
80
- @interfaces_table[name].item(row,7).setText(interface.bytes_read.to_s)
81
- if name == ROUTERS
82
- @interfaces_table[name].item(row,8).setText(interface.read_count.to_s)
83
- @interfaces_table[name].item(row,9).setText(interface.write_count.to_s)
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[name].item(row,8).setText(interface.write_count.to_s)
86
- @interfaces_table[name].item(row,9).setText(interface.read_count.to_s)
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(name, interfaces, tab_widget)
107
+ def populate(interfaces)
108
+ reset()
95
109
  return if interfaces.all.empty?
96
110
 
97
- scroll = Qt::ScrollArea.new
98
- scroll.setMinimumSize(800, 150)
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(name, interfaces, interfaces_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[name] = 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(name, interfaces, interfaces_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(name, interface, interface_name))
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(name, interface, interface_name)
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
- @logging_layouts = {}
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(tab_widget)
28
- scroll = Qt::ScrollArea.new
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
- @packets_table = {}
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 populate_commands(tab_widget)
28
- populate(COMMANDS, System.commands, tab_widget)
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 populate_telemetry(tab_widget)
32
- populate(TELEMETRY, System.telemetry, tab_widget)
41
+ def populate_commands
42
+ populate(System.commands)
33
43
  end
34
44
 
35
- def update(name)
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[name].item(row, 2).setText(packet.received_count.to_s)
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[name].item(row, 2).setText(packet.received_count.to_s)
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(name, cmd_tlm, tab_widget)
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
- scroll = Qt::ScrollArea.new
70
- widget = Qt::Widget.new
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(name, cmd_tlm, 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[name] = table
108
+ @packets_table = table
98
109
  end
99
110
 
100
- def populate_packets_table(name, cmd_tlm, 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