cosmos 3.9.1 → 3.9.2

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -3
  3. data/Manifest.txt +7 -0
  4. data/autohotkey/tools/cmd_extractor.ahk +17 -0
  5. data/autohotkey/tools/tlm_extractor.ahk +62 -1
  6. data/bin/cosmos +182 -12
  7. data/data/crc.txt +35 -34
  8. data/demo/config/data/crc.txt +6 -2
  9. data/demo/config/targets/INST/screens/adcs.txt +1 -1
  10. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +1 -1
  11. data/demo/config/tools/example_application.css +58 -0
  12. data/demo/config/tools/launcher/launcher.css +7 -0
  13. data/demo/config/tools/launcher/launcher2.css +10 -0
  14. data/demo/config/tools/test_runner/test_runner.css +45 -0
  15. data/ext/cosmos/ext/packet/packet.c +6 -0
  16. data/lib/cosmos/gui/dialogs/scroll_text_dialog.rb +15 -6
  17. data/lib/cosmos/gui/qt_tool.rb +58 -8
  18. data/lib/cosmos/gui/text/ruby_editor.rb +54 -6
  19. data/lib/cosmos/gui/utilities/analyze_log.rb +153 -0
  20. data/lib/cosmos/interfaces/interface.rb +6 -0
  21. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  22. data/lib/cosmos/packets/structure_item.rb +22 -3
  23. data/lib/cosmos/script/cmd_tlm_server.rb +28 -0
  24. data/lib/cosmos/script/extract.rb +10 -9
  25. data/lib/cosmos/script/tools.rb +10 -4
  26. data/lib/cosmos/system/system.rb +2 -1
  27. data/lib/cosmos/tools/cmd_extractor/cmd_extractor.rb +12 -0
  28. data/lib/cosmos/tools/cmd_tlm_server/api.rb +84 -0
  29. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +3 -2
  30. data/lib/cosmos/tools/cmd_tlm_server/commanding.rb +9 -0
  31. data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +2 -2
  32. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +26 -17
  33. data/lib/cosmos/tools/cmd_tlm_server/interfaces.rb +26 -0
  34. data/lib/cosmos/tools/cmd_tlm_server/packet_logging.rb +29 -0
  35. data/lib/cosmos/tools/cmd_tlm_server/routers.rb +33 -0
  36. data/lib/cosmos/tools/data_viewer/data_viewer.rb +1 -1
  37. data/lib/cosmos/tools/launcher/launcher.rb +14 -25
  38. data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +3 -7
  39. data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +6 -4
  40. data/lib/cosmos/tools/script_runner/script_runner.rb +58 -9
  41. data/lib/cosmos/tools/script_runner/script_runner_frame.rb +45 -19
  42. data/lib/cosmos/tools/table_manager/table_manager.rb +7 -7
  43. data/lib/cosmos/tools/test_runner/test_runner.rb +6 -0
  44. data/lib/cosmos/tools/tlm_extractor/tlm_extractor.rb +145 -8
  45. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_config.rb +89 -19
  46. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_config.rb +14 -0
  47. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +1 -1
  48. data/lib/cosmos/tools/tlm_viewer/screen.rb +15 -3
  49. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +23 -12
  50. data/lib/cosmos/version.rb +4 -4
  51. data/spec/packets/packet_item_limits_spec.rb +33 -6
  52. data/spec/packets/packet_item_spec.rb +9 -9
  53. data/spec/packets/packet_spec.rb +18 -6
  54. data/spec/packets/structure_item_spec.rb +22 -4
  55. data/spec/script/cmd_tlm_server_spec.rb +66 -0
  56. data/spec/script/extract_spec.rb +144 -0
  57. data/spec/script/tools_spec.rb +17 -2
  58. data/spec/system/system_spec.rb +1 -1
  59. data/spec/tools/cmd_tlm_server/api_spec.rb +6 -0
  60. data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +1 -1
  61. data/spec/tools/table_manager/table_item_parser_spec.rb +61 -0
  62. data/spec/tools/table_manager/table_item_spec.rb +1 -1
  63. metadata +9 -2
@@ -73,7 +73,7 @@ module Cosmos
73
73
  # The default host
74
74
  DEFAULT_HOST = 'localhost'
75
75
  # The default configuration file name
76
- DEFAULT_CONFIG_FILE = 'cmd_tlm_server.txt'
76
+ DEFAULT_CONFIG_FILE = File.join(Cosmos::USERPATH, 'config', 'tools', 'cmd_tlm_server', 'cmd_tlm_server.txt')
77
77
  # The maximum number of limits events that are queued. Used when
78
78
  # subscribing to limits events.
79
79
  DEFAULT_LIMITS_EVENT_QUEUE_SIZE = 1000
@@ -128,7 +128,7 @@ module Cosmos
128
128
  @next_packet_data_queue_id = 1
129
129
 
130
130
  # Process cmd_tlm_server.txt
131
- @config = CmdTlmServerConfig.new(File.join('config', 'tools', 'cmd_tlm_server', config_file))
131
+ @config = CmdTlmServerConfig.new(config_file)
132
132
  @background_tasks = BackgroundTasks.new(@config)
133
133
  @commanding = Commanding.new(@config)
134
134
  @interfaces = Interfaces.new(@config, method(:identified_packet_callback))
@@ -187,6 +187,7 @@ module Cosmos
187
187
  end
188
188
 
189
189
  @routers.add_preidentified('PREIDENTIFIED_ROUTER', System.instance.ports['CTS_PREIDENTIFIED'])
190
+ @routers.add_cmd_preidentified('PREIDENTIFIED_CMD_ROUTER', System.instance.ports['CTS_CMD_ROUTER'])
190
191
  System.telemetry.limits_change_callback = method(:limits_change_callback)
191
192
  @interfaces.start
192
193
  @routers.start
@@ -89,6 +89,15 @@ module Cosmos
89
89
  command.buffer = packet.buffer
90
90
  end
91
91
 
92
+ # Write to Command Routers
93
+ interface.cmd_routers.each do |cmd_router|
94
+ begin
95
+ cmd_router.write(command) if cmd_router.write_allowed? and cmd_router.connected?
96
+ rescue => err
97
+ Logger.error "Problem writing to cmd router #{cmd_router.name} - #{err.class}:#{err.message}"
98
+ end
99
+ end
100
+
92
101
  # Write to command packet logs
93
102
  interface.packet_log_writer_pairs.each do |packet_log_writer_pair|
94
103
  packet_log_writer_pair.cmd_log_writer.write(command)
@@ -104,9 +104,9 @@ module Cosmos
104
104
  interfaces_table.setRowCount(interfaces.all.length)
105
105
  interfaces_table.setColumnCount(10)
106
106
  if name == ROUTERS
107
- interfaces_table.setHorizontalHeaderLabels(["Router", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Cmd Pkts ", " Tlm Pkts "])
107
+ interfaces_table.setHorizontalHeaderLabels(["Router", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Pkts Rcvd ", " Pkts Sent "])
108
108
  else
109
- interfaces_table.setHorizontalHeaderLabels(["Interface", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Cmd Pkts ", " Tlm Pkts "])
109
+ interfaces_table.setHorizontalHeaderLabels(["Interface", "Connect/Disconnect", "Connected?", "Clients", "Tx Q Size", "Rx Q Size", " Bytes Tx ", " Bytes Rx ", " Cmd Pkts ", " Tlm Pkts "])
110
110
  end
111
111
 
112
112
  populate_interface_table(name, interfaces, interfaces_table)
@@ -57,7 +57,11 @@ module Cosmos
57
57
  @thread = Thread.new do
58
58
  @cancel_thread = false
59
59
  begin
60
- Logger.info "Starting packet reading for #{@interface.name}"
60
+ if @interface.read_allowed?
61
+ Logger.info "Starting packet reading for #{@interface.name}"
62
+ else
63
+ Logger.info "Starting connection maintenance for #{@interface.name}"
64
+ end
61
65
  while true
62
66
  break if @cancel_thread
63
67
  unless @interface.connected?
@@ -77,28 +81,33 @@ module Cosmos
77
81
  end
78
82
  end
79
83
 
80
- begin
81
- packet = @interface.read
82
- unless packet
83
- Logger.info "Clean disconnect from #{@interface.name} (returned nil)"
84
- handle_connection_lost(nil)
84
+ if @interface.read_allowed?
85
+ begin
86
+ packet = @interface.read
87
+ unless packet
88
+ Logger.info "Clean disconnect from #{@interface.name} (returned nil)"
89
+ handle_connection_lost(nil)
90
+ if @cancel_thread
91
+ break
92
+ else
93
+ next
94
+ end
95
+ end
96
+ packet.received_time = Time.now unless packet.received_time
97
+ rescue Exception => err
98
+ handle_connection_lost(err)
85
99
  if @cancel_thread
86
100
  break
87
101
  else
88
102
  next
89
103
  end
90
104
  end
91
- packet.received_time = Time.now unless packet.received_time
92
- rescue Exception => err
93
- handle_connection_lost(err)
94
- if @cancel_thread
95
- break
96
- else
97
- next
98
- end
99
- end
100
105
 
101
- handle_packet(packet)
106
+ handle_packet(packet)
107
+ else
108
+ @thread_sleeper.sleep(1)
109
+ handle_connection_lost(nil) if !@interface.connected?
110
+ end
102
111
  end # loop
103
112
  rescue Exception => error
104
113
  if @fatal_exception_callback
@@ -121,7 +130,7 @@ module Cosmos
121
130
  @thread_sleeper.cancel
122
131
  @interface.disconnect
123
132
  end
124
- Cosmos.kill_thread(self, @thread) if @thread != Thread.current
133
+ Cosmos.kill_thread(self, @thread) if @thread and @thread != Thread.current
125
134
  end
126
135
 
127
136
  def graceful_kill
@@ -27,6 +27,16 @@ module Cosmos
27
27
  @identified_packet_callback = identified_packet_callback
28
28
  end
29
29
 
30
+ # Get the targets of an interface
31
+ #
32
+ # @param interface_name [String] Interface to return target list
33
+ # @return [Array<String>] All the targets mapped to this interface
34
+ def targets(interface_name)
35
+ interface = @config.interfaces[interface_name.upcase]
36
+ raise "Unknown interface: #{interface_name}" unless interface
37
+ interface.target_names
38
+ end
39
+
30
40
  # Determines all targets in the system and maps them to the given interface
31
41
  #
32
42
  # @param interface_name [String] The interface to map all targets to
@@ -102,6 +112,22 @@ module Cosmos
102
112
  return new_interface
103
113
  end
104
114
 
115
+ # Get info about an interface by name
116
+ #
117
+ # @return [Array<String, Numeric, Numeric, Numeric, Numeric, Numeric,
118
+ # Numeric, Numeric>] Array containing \[state, num_clients,
119
+ # write_queue_size, read_queue_size, bytes_written, bytes_read,
120
+ # write_count, read_count] for the interface
121
+ def get_info(interface_name)
122
+ interface = @config.interfaces[interface_name.upcase]
123
+ raise "Unknown interface: #{interface_name}" unless interface
124
+
125
+ return [state(interface_name), interface.num_clients,
126
+ interface.write_queue_size, interface.read_queue_size,
127
+ interface.bytes_written, interface.bytes_read,
128
+ interface.write_count, interface.read_count]
129
+ end
130
+
105
131
  protected
106
132
 
107
133
  # Start an interface's packet reading thread
@@ -121,6 +121,35 @@ module Cosmos
121
121
  return packet_log_writer_pair.tlm_log_writer.filename
122
122
  end
123
123
 
124
+ # @param packet_log_writer_name [String] The name of the log writer to
125
+ # get info for
126
+ # @return [Array<Boolean, Numeric, String, Numeric, Boolean, Numeric,
127
+ # String, Numeric>] array containing \[cmd log enabled, cmd queue size,
128
+ # cmd filename, cmd file size, tlm log enabled, tlm queue size,
129
+ # tlm filename, tlm file size] for the packet log writer
130
+ def get_info(packet_log_writer_name = 'DEFAULT')
131
+ packet_log_writer_pair = @config.packet_log_writer_pairs[packet_log_writer_name.upcase]
132
+ raise "Unknown packet log writer: #{packet_log_writer_name}" unless packet_log_writer_pair
133
+ cmd_file_size = 0
134
+ begin
135
+ cmd_file_size = File.size(packet_log_writer_pair.cmd_log_writer.filename) if packet_log_writer_pair.cmd_log_writer.filename
136
+ rescue Exception
137
+ end
138
+ tlm_file_size = 0
139
+ begin
140
+ tlm_file_size = File.size(packet_log_writer_pair.tlm_log_writer.filename) if packet_log_writer_pair.tlm_log_writer.filename
141
+ rescue Exception
142
+ end
143
+ return [packet_log_writer_pair.cmd_log_writer.logging_enabled,
144
+ packet_log_writer_pair.cmd_log_writer.queue.size,
145
+ packet_log_writer_pair.cmd_log_writer.filename,
146
+ cmd_file_size,
147
+ packet_log_writer_pair.tlm_log_writer.logging_enabled,
148
+ packet_log_writer_pair.tlm_log_writer.queue.size,
149
+ packet_log_writer_pair.tlm_log_writer.filename,
150
+ tlm_file_size]
151
+ end
152
+
124
153
  # @return [Hash<String, PacketLogWriterPair>] Packet log writer hash. Each
125
154
  # pair encapsulates a command and telemetry log writer.
126
155
  def all
@@ -45,6 +45,23 @@ module Cosmos
45
45
  end
46
46
  end
47
47
 
48
+ # Adds a Preidentified command router to the system with given name and port.
49
+ # All interfaces defined by the Command/Telemetry configuration are
50
+ # directed to this router to output commands
51
+ #
52
+ # @param router_name [String] Name of the command router
53
+ # @param port [Integer] Port to pass to the {TcpipServerInterface}
54
+ def add_cmd_preidentified(cmd_router_name, port)
55
+ cmd_router_name = cmd_router_name.upcase
56
+ cmd_router = TcpipServerInterface.new(port, nil, 10.0, nil, 'PREIDENTIFIED')
57
+ cmd_router.name = cmd_router_name
58
+ cmd_router.disable_disconnect = true
59
+ @config.routers[cmd_router_name] = cmd_router
60
+ @config.interfaces.each do |interface_name, interface|
61
+ interface.cmd_routers << cmd_router
62
+ end
63
+ end
64
+
48
65
  # Recreate a router with new initialization parameters
49
66
  #
50
67
  # @param router_name [String] Name of the router
@@ -73,6 +90,22 @@ module Cosmos
73
90
  return new_router
74
91
  end
75
92
 
93
+ # Get info about an router by name
94
+ #
95
+ # @return [Array<String, Numeric, Numeric, Numeric, Numeric, Numeric,
96
+ # Numeric, Numeric>] Array containing \[state, num_clients,
97
+ # write_queue_size, read_queue_size, bytes_written, bytes_read,
98
+ # read_count, write_count] for the interface
99
+ def get_info(router_name)
100
+ router = @config.routers[router_name.upcase]
101
+ raise "Unknown router: #{router_name}" unless router
102
+
103
+ return [state(router_name), router.num_clients,
104
+ router.write_queue_size, router.read_queue_size,
105
+ router.bytes_written, router.bytes_read,
106
+ router.read_count, router.write_count]
107
+ end
108
+
76
109
  protected
77
110
 
78
111
  # Start an router's packet reading thread
@@ -57,7 +57,7 @@ module Cosmos
57
57
  @components = []
58
58
  @packets = []
59
59
  @packet_to_components_mapping = {}
60
- @config_filename = File.join(Cosmos::USERPATH, 'config', 'tools', 'data_viewer', options.config_file)
60
+ @config_filename = options.config_file
61
61
  process_config()
62
62
 
63
63
  # Load System Definition and Event Data
@@ -18,19 +18,21 @@ Cosmos.catch_fatal_exception do
18
18
  end
19
19
 
20
20
  module Cosmos
21
-
21
+ # Provides a group of buttons which can be configured to launch any of the
22
+ # COSMOS tools as well as any custom applications.
22
23
  class Launcher < QtTool
23
-
24
24
  def initialize(options)
25
25
  super(options) # MUST BE FIRST - All code before super is executed twice in RubyQt Based classes
26
26
 
27
- # Set environment variable of COSMOS_USERPATH so that all launched apps know where to find the configuration
27
+ # Set environment variable of COSMOS_USERPATH so that all launched apps
28
+ # know where to find the configuration
28
29
  ENV['COSMOS_USERPATH'] = Cosmos::USERPATH
29
30
 
30
31
  Cosmos.load_cosmos_icon("launcher.png")
31
32
  layout.setSizeConstraint(Qt::Layout::SetFixedSize)
32
33
 
33
- @about_string = "Launcher provides a list of applications to launch at the click of a button. It can also launch multiple applications and configure their exact placement on the screen."
34
+ @about_string = "Launcher provides a list of applications to launch at the click of a button. "\
35
+ "It can also launch multiple applications and configure their exact placement on the screen."
34
36
  initialize_actions()
35
37
  initialize_menus()
36
38
  initialize_central_widget()
@@ -38,29 +40,21 @@ module Cosmos
38
40
  end
39
41
 
40
42
  def initialize_menus
41
- # File Menu
42
43
  @file_menu = menuBar().addMenu(tr('&File'))
43
44
  @file_menu.addAction(@exit_action)
44
-
45
- # Help Menu
46
45
  initialize_help_menu()
47
46
  end
48
47
 
49
48
  def initialize_central_widget
50
- # Create the central widget
51
- central_widget = Qt::Widget.new
52
- setCentralWidget(central_widget)
53
-
54
- # Read the configuration file
55
- config_file = File.join(Cosmos::USERPATH, 'config', 'tools', 'launcher', @options.config_file)
56
49
  begin
57
- config = LauncherConfig.new(config_file)
50
+ config = LauncherConfig.new(@options.config_file)
58
51
  rescue => error
59
- ExceptionDialog.new(self, error, "Error parsing #{config_file}")
52
+ ExceptionDialog.new(self, error, "Error parsing #{@options.config_file}")
60
53
  end
61
54
 
62
- # Set the title
63
55
  self.window_title = config.title
56
+ central_widget = Qt::Widget.new
57
+ setCentralWidget(central_widget)
64
58
 
65
59
  # Create each button or divider
66
60
  default_icon_filename = 'COSMOS_64x64.png'
@@ -102,6 +96,7 @@ module Cosmos
102
96
  "font-family:#{config.tool_font_settings[0]}; " \
103
97
  "font-size:#{config.tool_font_settings[1]}px"
104
98
  label.setStyleSheet(stylesheet)
99
+ label.setObjectName("ButtonLabel")
105
100
  label.wordWrap = true
106
101
  label.setFixedWidth(70)
107
102
  label.setSizePolicy(Qt::SizePolicy::Fixed, Qt::SizePolicy::Fixed)
@@ -115,6 +110,7 @@ module Cosmos
115
110
 
116
111
  when :DIVIDER
117
112
  divider = Qt::Frame.new
113
+ divider.setObjectName("Divider")
118
114
  divider.setFrameStyle(Qt::Frame::HLine | Qt::Frame::Raised)
119
115
  divider.setLineWidth(1)
120
116
  divider.setMidLineWidth(1)
@@ -126,6 +122,7 @@ module Cosmos
126
122
  "font-family:#{config.label_font_settings[0]}; " \
127
123
  "font-size:#{config.label_font_settings[1]}px"
128
124
  label.setStyleSheet(stylesheet)
125
+ label.setObjectName("Label")
129
126
  widgets << label
130
127
 
131
128
  else
@@ -172,17 +169,9 @@ module Cosmos
172
169
  unless option_parser and options
173
170
  option_parser, options = create_default_options()
174
171
  options.title = 'Launcher'
175
- options.config_file = 'launcher.txt'
176
- option_parser.separator "Launcher Specific Options:"
177
- option_parser.on("-c", "--config FILE", "Use the specified configuration file") do |arg|
178
- options.config_file = arg
179
- end
180
172
  end
181
-
182
173
  super(option_parser, options)
183
174
  end
184
175
  end
185
-
186
- end # class Launcher
187
-
176
+ end
188
177
  end
@@ -228,10 +228,6 @@ module Cosmos
228
228
  # @return [String] Message indicating success or fail
229
229
  def open_config(filename)
230
230
  return "" unless filename
231
-
232
- unless Pathname.new(filename).absolute?
233
- filename = File.join(::Cosmos::USERPATH, 'config', 'tools', 'limits_monitor', filename)
234
- end
235
231
  return "Configuration file #{filename} not found!" unless File.exist?(filename)
236
232
 
237
233
  @ignored = []
@@ -595,7 +591,7 @@ module Cosmos
595
591
  end
596
592
 
597
593
  # @return [String] Fully qualified path to the configuration file
598
- def config_path
594
+ def default_config_path
599
595
  # If the config file has been set then just return it
600
596
  return @filename if @filename
601
597
  # This is the default path to the configuration files
@@ -605,7 +601,7 @@ module Cosmos
605
601
  # Opens the configuration file and loads the ignored items
606
602
  def open_config_file
607
603
  filename = Qt::FileDialog::getOpenFileName(self,
608
- "Open Configuration File", config_path())
604
+ "Open Configuration File", default_config_path())
609
605
  unless filename.nil? || filename.empty?
610
606
  result = @limits_items.open_config(filename)
611
607
  statusBar.showMessage(tr(result))
@@ -615,7 +611,7 @@ module Cosmos
615
611
  # Saves the ignored items to the configuration file
616
612
  def save_config_file
617
613
  filename = Qt::FileDialog.getSaveFileName(self,
618
- 'Save As...', config_path(), 'Configuration Files (*.txt)')
614
+ 'Save As...', default_config_path(), 'Configuration Files (*.txt)')
619
615
  unless filename.nil? || filename.empty?
620
616
  result = @limits_items.save_config(filename)
621
617
  statusBar.showMessage(tr(result))
@@ -334,16 +334,16 @@ module Cosmos
334
334
  derived = []
335
335
  System.telemetry.items(target_name, packet_name).each do |item|
336
336
  if item.data_type == :DERIVED
337
- derived << [item.name, item.states, item.description]
337
+ derived << [item.name, item.states, item.description, true]
338
338
  else
339
- tlm_items << [item.name, item.states, item.description]
339
+ tlm_items << [item.name, item.states, item.description, false]
340
340
  @derived_row += 1
341
341
  end
342
342
  end
343
343
  tlm_items.concat(derived) # Tack the derived onto the end
344
344
  else
345
345
  System.telemetry.items(target_name, packet_name).each do |item|
346
- tlm_items << [item.name, item.states, item.description]
346
+ tlm_items << [item.name, item.states, item.description, item.data_type == :DERIVED]
347
347
  end
348
348
  end
349
349
  rescue
@@ -360,8 +360,9 @@ module Cosmos
360
360
  row = 0
361
361
  featured_item = nil
362
362
  @ignored_rows = []
363
- tlm_items.each do |tlm_name, states, description|
363
+ tlm_items.each do |tlm_name, states, description, derived|
364
364
  @ignored_rows << row if System.targets[target_name].ignored_items.include?(tlm_name)
365
+ tlm_name = "*#{tlm_name}" if derived
365
366
  item = Qt::TableWidgetItem.new(tr("#{tlm_name}:"))
366
367
  item.setTextAlignment(Qt::AlignRight)
367
368
  item.setFlags(Qt::NoItemFlags | Qt::ItemIsSelectable)
@@ -497,6 +498,7 @@ module Cosmos
497
498
  item = @table.itemAt(point)
498
499
  if item
499
500
  item_name = @table.item(item.row, 0).text[0..-2] # Remove :
501
+ item_name = item_name[1..-1] if item_name[0] == '*'
500
502
  if target_name.length > 0 and packet_name.length > 0 and item_name.length > 0
501
503
  menu = Qt::Menu.new()
502
504
 
@@ -33,7 +33,6 @@ module Cosmos
33
33
  Cosmos.load_cosmos_icon("script_runner.png")
34
34
  setAcceptDrops(true) # Allow dropping in files
35
35
 
36
- @config_file = options.config_file
37
36
  @server_config_file = options.server_config_file
38
37
 
39
38
  # Add procedures to search path
@@ -50,22 +49,35 @@ module Cosmos
50
49
  @running_icon = Cosmos.get_icon('running.png')
51
50
  @no_icon = Qt::Icon.new
52
51
 
53
- # Handle configuration
54
- filename = File.join(::Cosmos::USERPATH, %w(config tools script_runner), @config_file)
55
- if File.exist?(filename)
56
- ScriptRunnerConfig.new(filename)
57
- elsif @config_file != 'script_runner.txt'
58
- raise "Could not find config file #{filename}"
52
+ if File.exist?(options.config_file)
53
+ ScriptRunnerConfig.new(options.config_file)
54
+ else
55
+ raise "Could not find config file #{options.config_file}"
59
56
  end
60
57
 
61
58
  @procedure_dir = System.paths['PROCEDURES'][0]
62
59
  @debug = false
63
60
 
64
61
  # Create the initial tab(s)
65
- if ARGV.length == 0
62
+ initial_tabs = []
63
+ ARGV.each do |argv_filename|
64
+ proc_filename = find_procedure(argv_filename)
65
+ initial_tabs << proc_filename if proc_filename
66
+ end
67
+ if options.run_procedure
68
+ options.run_procedure = find_procedure(options.run_procedure)
69
+ initial_tabs << options.run_procedure if options.run_procedure
70
+ end
71
+ initial_tabs.uniq!
72
+
73
+ if initial_tabs.length == 0
66
74
  create_tab()
67
75
  else
68
- ARGV.each {|argv_filename| create_tab(argv_filename)}
76
+ initial_tabs.each {|filename| create_tab(filename)}
77
+ end
78
+
79
+ if options.run_procedure
80
+ run_procedure(options.run_procedure)
69
81
  end
70
82
  end
71
83
 
@@ -468,6 +480,7 @@ module Cosmos
468
480
  close_active_tab()
469
481
  else
470
482
  active_script_runner_frame().stop_message_log
483
+ active_script_runner_frame().clear_breakpoints
471
484
  @tab_book.setTabText(0, ' Untitled ')
472
485
  @tab_book.currentTab.clear
473
486
  end
@@ -852,6 +865,7 @@ module Cosmos
852
865
  def close_active_tab
853
866
  if @tab_book.count > 1
854
867
  active_script_runner_frame().stop_message_log
868
+ active_script_runner_frame().clear_breakpoints
855
869
  tab_index = @tab_book.currentIndex
856
870
  @tab_book.removeTab(tab_index)
857
871
  if tab_index >= 1
@@ -902,6 +916,37 @@ module Cosmos
902
916
  return safe_to_continue
903
917
  end
904
918
 
919
+ def find_procedure(filename)
920
+ # If the filename is already sufficient, just expand the path.
921
+ return File.expand_path(filename) if File.exist?(filename)
922
+
923
+ # If the filename wasn't sufficient, can we find the file in one of the
924
+ # system procedure directories?
925
+ System.paths['PROCEDURES'].each do |path|
926
+ new_filename = File.join(path, filename)
927
+ return File.expand_path(new_filename) if File.exist?(new_filename)
928
+ end
929
+
930
+ # Ok, how about one of the target procedure directories?
931
+ System.targets.each do |target_name, target|
932
+ new_filename = File.join(target.dir, 'procedures', filename)
933
+ return File.expand_path(new_filename) if File.exist?(new_filename)
934
+ end
935
+
936
+ # Couldn't find the file anywhere.
937
+ return nil
938
+ end
939
+
940
+ def run_procedure(filename)
941
+ # Switch to the desired tab and begin execution
942
+ @tab_book.tabs.each_with_index do |tab, index|
943
+ if tab.filename == filename
944
+ @tab_book.setCurrentIndex(index)
945
+ end
946
+ end
947
+ @tab_book.currentTab.run()
948
+ end
949
+
905
950
  def self.run(option_parser = nil, options = nil)
906
951
  Cosmos.catch_fatal_exception do
907
952
  unless option_parser and options
@@ -912,6 +957,7 @@ module Cosmos
912
957
  options.auto_size = false
913
958
  options.config_file = "script_runner.txt"
914
959
  options.server_config_file = CmdTlmServer::DEFAULT_CONFIG_FILE
960
+ options.run_procedure = nil
915
961
 
916
962
  option_parser.separator "Script Runner Specific Options:"
917
963
  option_parser.on("-c", "--config FILE", "Use the specified configuration file") do |arg|
@@ -920,6 +966,9 @@ module Cosmos
920
966
  option_parser.on("-s", "--server FILE", "Use the specified server configuration file for disconnect mode") do |arg|
921
967
  options.server_config_file = arg
922
968
  end
969
+ option_parser.on("-r", "--run FILE", "Open and run the specified procedure") do |arg|
970
+ options.run_procedure = arg
971
+ end
923
972
  end
924
973
 
925
974
  super(option_parser, options)