cosmos 3.1.1 → 3.1.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Manifest.txt +6 -0
  3. data/autohotkey/config/tools/handbook_creator/templates/command_packets.html.erb +1 -1
  4. data/autohotkey/config/tools/handbook_creator/templates/footer.html.erb +2 -2
  5. data/autohotkey/config/tools/handbook_creator/templates/header.html.erb +4 -4
  6. data/autohotkey/tools/autohotkey.rb +1 -1
  7. data/autohotkey/tools/cmd_tlm_server.ahk +1 -0
  8. data/autohotkey/tools/packet_viewer.ahk +45 -2
  9. data/data/crc.txt +20 -15
  10. data/demo/Rakefile +16 -0
  11. data/demo/config/data/crc.txt +3 -3
  12. data/demo/config/tools/handbook_creator/templates/footer.html.erb +2 -2
  13. data/demo/config/tools/handbook_creator/templates/header.html.erb +4 -4
  14. data/demo/procedures/example_test.rb +1 -1
  15. data/install/Rakefile +16 -0
  16. data/install/config/data/crc.txt +2 -2
  17. data/install/config/tools/handbook_creator/templates/footer.html.erb +2 -2
  18. data/install/config/tools/handbook_creator/templates/header.html.erb +4 -4
  19. data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +64 -57
  20. data/lib/cosmos/gui/line_graph/line_graph_scaling.rb +0 -9
  21. data/lib/cosmos/gui/qt.rb +22 -18
  22. data/lib/cosmos/packet_logs/packet_log_writer.rb +6 -2
  23. data/lib/cosmos/script/script.rb +1 -1
  24. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +0 -1
  25. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +99 -784
  26. data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +189 -0
  27. data/lib/cosmos/tools/cmd_tlm_server/gui/logging_tab.rb +176 -0
  28. data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +144 -0
  29. data/lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb +240 -0
  30. data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +90 -0
  31. data/lib/cosmos/tools/launcher/launcher_config.rb +142 -110
  32. data/lib/cosmos/tools/test_runner/results_writer.rb +1 -1
  33. data/lib/cosmos/tools/test_runner/test_runner.rb +3 -2
  34. data/lib/cosmos/tools/tlm_grapher/data_objects/data_object.rb +18 -2
  35. data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +4 -9
  36. data/lib/cosmos/tools/tlm_grapher/data_objects/xy_data_object.rb +5 -5
  37. data/lib/cosmos/top_level.rb +1 -1
  38. data/lib/cosmos/version.rb +4 -4
  39. data/run_gui_tests.bat +33 -31
  40. data/spec/core_ext/time_spec.rb +51 -0
  41. data/spec/script/script_spec.rb +96 -0
  42. data/spec/tools/cmd_tlm_server/commanding_spec.rb +28 -0
  43. data/spec/tools/cmd_tlm_server/connections_spec.rb +88 -0
  44. data/spec/tools/cmd_tlm_server/router_thread_spec.rb +78 -25
  45. data/spec/tools/launcher/launcher_config_spec.rb +460 -0
  46. data/spec/top_level/top_level_spec.rb +1 -1
  47. metadata +8 -2
@@ -0,0 +1,240 @@
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
+
14
+ module Cosmos
15
+
16
+ # Implements the status tab in the Command and Telemetry Server GUI
17
+ class StatusTab
18
+
19
+ # Create the status tab and add it to the tab_widget
20
+ #
21
+ # @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
22
+ def populate(tab_widget)
23
+ scroll = Qt::ScrollArea.new
24
+ widget = Qt::Widget.new
25
+ layout = Qt::VBoxLayout.new(widget)
26
+
27
+ populate_limits_status(layout)
28
+ populate_api_status(layout)
29
+ populate_system_status(layout)
30
+ populate_background_status(layout)
31
+
32
+ # Set the scroll area widget last now that all the items have been layed out
33
+ scroll.setWidget(widget)
34
+ tab_widget.addTab(scroll, "Status")
35
+ end
36
+
37
+ # Update the status tab in the GUI
38
+ #
39
+ # @param previous_request_count [Integer] The previous number of
40
+ def update(previous_request_count)
41
+ update_limits_set
42
+ update_api_status(previous_request_count)
43
+ update_system_status
44
+ update_background_task_status
45
+ end
46
+
47
+ private
48
+
49
+ def populate_limits_status(layout)
50
+ limits = Qt::GroupBox.new(Qt::Object.tr("Limits Status"))
51
+ limits_layout = Qt::FormLayout.new(limits)
52
+ current_limits_set = System.limits_set.to_s
53
+
54
+ known_limits_sets = System.limits.sets
55
+ known_limits_sets = known_limits_sets.map {|x| x.to_s}.sort
56
+ current_index = known_limits_sets.index(current_limits_set.to_s)
57
+
58
+ @limits_set_combo = Qt::ComboBox.new
59
+ limits_layout.addRow("Limits Set:", @limits_set_combo)
60
+ layout.addWidget(limits)
61
+
62
+ known_limits_sets.sort.each do |limits_set|
63
+ @limits_set_combo.addItem(limits_set.to_s)
64
+ end
65
+ @limits_set_combo.setMaxVisibleItems(6)
66
+ @limits_set_combo.setCurrentIndex(current_index)
67
+ # Only connect to the signal that is sent when the user chooses an item.
68
+ # If the limits set is changed programatically the code in
69
+ # handle_status_tab will pick up the change.
70
+ @limits_set_combo.connect(SIGNAL('activated(int)')) do
71
+ selected_limits_set = @limits_set_combo.currentText
72
+ if selected_limits_set
73
+ System.limits_set = selected_limits_set.intern if System.limits_set != selected_limits_set.intern
74
+ end
75
+ end
76
+ end
77
+
78
+ def populate_api_status(layout)
79
+ api = Qt::GroupBox.new(Qt::Object.tr("API Status"))
80
+ api_layout = Qt::VBoxLayout.new(api)
81
+ @api_table = Qt::TableWidget.new()
82
+ @api_table.verticalHeader.hide()
83
+ @api_table.setRowCount(1)
84
+ @api_table.setColumnCount(6)
85
+ @api_table.setHorizontalHeaderLabels(["Port", "Num Clients", "Requests", "Requests/Sec", "Avg Request Time", "Estimated Utilization"])
86
+
87
+ @api_table.setItem(0, 0, Qt::TableWidgetItem.new(Qt::Object.tr(System.ports['CTS_API'].to_s)))
88
+ item0 = Qt::TableWidgetItem.new(Qt::Object.tr(CmdTlmServer.json_drb.num_clients.to_s))
89
+ item0.setTextAlignment(Qt::AlignCenter)
90
+ @api_table.setItem(0, 1, item0)
91
+ item = Qt::TableWidgetItem.new(Qt::Object.tr(CmdTlmServer.json_drb.request_count.to_s))
92
+ item.setTextAlignment(Qt::AlignCenter)
93
+ @api_table.setItem(0, 2, item)
94
+ item2 = Qt::TableWidgetItem.new("0.0")
95
+ item2.setTextAlignment(Qt::AlignCenter)
96
+ @api_table.setItem(0, 3, item2)
97
+ item3 = Qt::TableWidgetItem.new("0.0")
98
+ item3.setTextAlignment(Qt::AlignCenter)
99
+ @api_table.setItem(0, 4, item3)
100
+ item4 = Qt::TableWidgetItem.new("0.0")
101
+ item4.setTextAlignment(Qt::AlignCenter)
102
+ @api_table.setItem(0, 5, item4)
103
+ @api_table.displayFullSize
104
+ api_layout.addWidget(@api_table)
105
+ layout.addWidget(api)
106
+ end
107
+
108
+ def populate_system_status(layout)
109
+ system = Qt::GroupBox.new(Qt::Object.tr("System Status"))
110
+ system_layout = Qt::VBoxLayout.new(system)
111
+ @system_table = Qt::TableWidget.new()
112
+ @system_table.verticalHeader.hide()
113
+ @system_table.setRowCount(1)
114
+ @system_table.setColumnCount(4)
115
+ @system_table.setHorizontalHeaderLabels(["Threads", "Total Objs", "Free Objs", "Allocated Objs"])
116
+
117
+ item0 = Qt::TableWidgetItem.new("0")
118
+ item0.setTextAlignment(Qt::AlignCenter)
119
+ @system_table.setItem(0, 0, item0)
120
+ item1 = Qt::TableWidgetItem.new("0.0")
121
+ item1.setTextAlignment(Qt::AlignCenter)
122
+ @system_table.setItem(0, 1, item1)
123
+ item2 = Qt::TableWidgetItem.new("0.0")
124
+ item2.setTextAlignment(Qt::AlignCenter)
125
+ @system_table.setItem(0, 2, item2)
126
+ item3 = Qt::TableWidgetItem.new("0.0")
127
+ item3.setTextAlignment(Qt::AlignCenter)
128
+ @system_table.setItem(0, 3, item3)
129
+ @system_table.displayFullSize
130
+ system_layout.addWidget(@system_table)
131
+ layout.addWidget(system)
132
+ end
133
+
134
+ def populate_background_status(layout)
135
+ background_tasks_groupbox = Qt::GroupBox.new(Qt::Object.tr("Background Tasks"))
136
+ background_tasks_layout = Qt::VBoxLayout.new(background_tasks_groupbox)
137
+ @background_tasks_table = Qt::TableWidget.new()
138
+ @background_tasks_table.verticalHeader.hide()
139
+ @background_tasks_table.setRowCount(CmdTlmServer.background_tasks.all.length)
140
+ @background_tasks_table.setColumnCount(3)
141
+ @background_tasks_table.setHorizontalHeaderLabels(["Name", "State", "Status"])
142
+
143
+ background_tasks = CmdTlmServer.background_tasks.all
144
+ if background_tasks.length > 0
145
+ row = 0
146
+ background_tasks.each_with_index do |background_task, index|
147
+ background_task_name = background_task.name
148
+ background_task_name = "Background Task ##{index + 1}" unless background_task_name
149
+ background_task_name_widget = Qt::TableWidgetItem.new(background_task_name)
150
+ background_task_name_widget.setTextAlignment(Qt::AlignCenter)
151
+ @background_tasks_table.setItem(row, 0, background_task_name_widget)
152
+ if background_task.thread
153
+ status = background_task.thread.status
154
+ status = 'complete' if status == false
155
+ background_task_state_widget = Qt::TableWidgetItem.new(status.to_s)
156
+ else
157
+ background_task_state_widget = Qt::TableWidgetItem.new('no thread')
158
+ end
159
+ background_task_state_widget.setTextAlignment(Qt::AlignCenter)
160
+ background_task_state_widget.setSizeHint(Qt::Size.new(80, 30))
161
+ @background_tasks_table.setItem(row, 1, background_task_state_widget)
162
+ background_task_status_widget = Qt::TableWidgetItem.new(background_task.status.to_s)
163
+ background_task_status_widget.setTextAlignment(Qt::AlignCenter)
164
+ background_task_status_widget.setSizeHint(Qt::Size.new(500, 30))
165
+ @background_tasks_table.setItem(row, 2, background_task_status_widget)
166
+
167
+ row += 1
168
+ end
169
+ end
170
+ @background_tasks_table.displayFullSize
171
+ background_tasks_layout.addWidget(@background_tasks_table)
172
+ layout.addWidget(background_tasks_groupbox)
173
+ end
174
+
175
+ # Update the current limits set in the GUI
176
+ def update_limits_set
177
+ current_limits_set = System.limits_set.to_s
178
+ if @limits_set_combo.currentText != current_limits_set
179
+ known_limits_sets = System.limits.sets
180
+ known_limits_sets = known_limits_sets.map {|x| x.to_s}.sort
181
+ current_index = known_limits_sets.index(current_limits_set.to_s)
182
+ @limits_set_combo.clear
183
+ known_limits_sets.sort.each do |limits_set|
184
+ @limits_set_combo.addItem(limits_set.to_s)
185
+ end
186
+ @limits_set_combo.setCurrentIndex(current_index)
187
+ end
188
+ end
189
+
190
+ # Update the API statistics in the GUI
191
+ def update_api_status(previous_request_count)
192
+ if CmdTlmServer.json_drb
193
+ @api_table.item(0,1).setText(CmdTlmServer.json_drb.num_clients.to_s)
194
+ @api_table.item(0,2).setText(CmdTlmServer.json_drb.request_count.to_s)
195
+ request_count = CmdTlmServer.json_drb.request_count
196
+ requests_per_second = request_count - previous_request_count
197
+ @api_table.item(0,3).setText(requests_per_second.to_s)
198
+ previous_request_count = request_count
199
+ average_request_time = CmdTlmServer.json_drb.average_request_time
200
+ @api_table.item(0,4).setText(sprintf("%0.6f s", average_request_time))
201
+ estimated_utilization = requests_per_second * average_request_time * 100.0
202
+ @api_table.item(0,5).setText(sprintf("%0.2f %", estimated_utilization))
203
+ end
204
+ end
205
+
206
+ # Update the Ruby system statistics in the GUI
207
+ def update_system_status
208
+ @system_table.item(0,0).setText(Thread.list.length.to_s)
209
+ objs = ObjectSpace.count_objects
210
+ @system_table.item(0,1).setText(objs[:TOTAL].to_s)
211
+ @system_table.item(0,2).setText(objs[:FREE].to_s)
212
+ total = 0
213
+ objs.each do |key, val|
214
+ next if key == :TOTAL || key == :FREE
215
+ total += val
216
+ end
217
+ @system_table.item(0,3).setText(total.to_s)
218
+ end
219
+
220
+ # Update the background task status in the GUI
221
+ def update_background_task_status
222
+ background_tasks = CmdTlmServer.background_tasks.all
223
+ if background_tasks.length > 0
224
+ row = 0
225
+ background_tasks.each_with_index do |background_task, index|
226
+ if background_task.thread
227
+ status = background_task.thread.status
228
+ status = 'complete' if status == false
229
+ @background_tasks_table.item(row, 1).setText(status.to_s)
230
+ else
231
+ @background_tasks_table.item(row, 1).setText('no thread')
232
+ end
233
+ @background_tasks_table.item(row, 2).setText(background_task.status.to_s)
234
+ row += 1
235
+ end
236
+ end
237
+ end
238
+
239
+ end
240
+ end # module Cosmos
@@ -0,0 +1,90 @@
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
+
14
+ module Cosmos
15
+
16
+ # Implements the targets tab in the Command and Telemetry Server GUI
17
+ class TargetsTab
18
+
19
+ # Create the targets tab and add it to the tab_widget
20
+ #
21
+ # @param tab_widget [Qt::TabWidget] The tab widget to add the tab to
22
+ def populate(tab_widget)
23
+ num_targets = System.targets.length
24
+ if num_targets > 0
25
+ return if num_targets == 1 and System.targets['SYSTEM']
26
+ num_targets -= 1 if System.targets['SYSTEM']
27
+
28
+ scroll = Qt::ScrollArea.new
29
+ widget = Qt::Widget.new
30
+ layout = Qt::VBoxLayout.new(widget)
31
+ # Since the layout will be inside a scroll area make sure it respects the sizes we set
32
+ layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
33
+
34
+ @targets_table = Qt::TableWidget.new()
35
+ @targets_table.verticalHeader.hide()
36
+ @targets_table.setRowCount(num_targets)
37
+ @targets_table.setColumnCount(4)
38
+ @targets_table.setHorizontalHeaderLabels(["Target Name", "Interface", "Command Count", "Telemetry Count"])
39
+
40
+ populate_targets_table()
41
+
42
+ @targets_table.displayFullSize
43
+
44
+ layout.addWidget(@targets_table)
45
+ scroll.setWidget(widget)
46
+ tab_widget.addTab(scroll, "Targets")
47
+ end
48
+ end
49
+
50
+ # Update the targets tab gui
51
+ def update
52
+ row = 0
53
+ System.targets.sort.each do |target_name, target|
54
+ next if target_name == 'SYSTEM'
55
+ @targets_table.item(row,2).setText(target.cmd_cnt.to_s)
56
+ @targets_table.item(row,3).setText(target.tlm_cnt.to_s)
57
+ row += 1
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def populate_targets_table
64
+ row = 0
65
+ System.targets.sort.each do |target_name, target|
66
+ next if target_name == 'SYSTEM'
67
+ target_name_widget = Qt::TableWidgetItem.new(Qt::Object.tr(target_name))
68
+ target_name_widget.setTextAlignment(Qt::AlignCenter)
69
+ @targets_table.setItem(row, 0, target_name_widget)
70
+ if target.interface
71
+ interface_name_widget = Qt::TableWidgetItem.new(Qt::Object.tr(target.interface.name.to_s))
72
+ else
73
+ interface_name_widget = Qt::TableWidgetItem.new(Qt::Object.tr(''))
74
+ end
75
+ interface_name_widget.setTextAlignment(Qt::AlignCenter)
76
+ @targets_table.setItem(row, 1, interface_name_widget)
77
+ cmd_cnt = Qt::TableWidgetItem.new(Qt::Object.tr(target.cmd_cnt.to_s))
78
+ cmd_cnt.setTextAlignment(Qt::AlignCenter)
79
+ @targets_table.setItem(row, 2, cmd_cnt)
80
+
81
+ tlm_cnt = Qt::TableWidgetItem.new(Qt::Object.tr(target.tlm_cnt.to_s))
82
+ tlm_cnt.setTextAlignment(Qt::AlignCenter)
83
+ @targets_table.setItem(row, 3, tlm_cnt)
84
+
85
+ row += 1
86
+ end
87
+ end
88
+
89
+ end
90
+ end # module Cosmos
@@ -10,6 +10,7 @@
10
10
 
11
11
  require 'cosmos'
12
12
  require 'cosmos/config/config_parser'
13
+ require 'ostruct'
13
14
 
14
15
  module Cosmos
15
16
 
@@ -33,8 +34,9 @@ module Cosmos
33
34
  attr_reader :num_columns
34
35
 
35
36
  # Processes a file and adds in the configuration defined in the file
37
+ #
38
+ # @param filename [String] Name of the configuration file to parse
36
39
  def initialize(filename)
37
- # Initialize instance variables
38
40
  @title = 'COSMOS Launcher'
39
41
  @tool_font_settings = ['Arial', 12]
40
42
  @label_font_settings = ['Arial', 16]
@@ -42,132 +44,162 @@ module Cosmos
42
44
  @items = []
43
45
 
44
46
  if File.exist?(filename.to_s)
45
- multitool = false
46
- multitool_text = nil
47
- multitool_icon_filename = nil
48
- multitool_settings = nil
49
-
50
- # Loop over each line of the configuration file
51
- parser = ConfigParser.new
52
- parser.parse_file(filename) do |keyword, params|
53
- # Handle each keyword
54
- case keyword
55
-
56
- when 'TOOL'
57
- if multitool
58
- parser.verify_num_parameters(1, 1, "TOOL <Shell command>")
59
- multitool_settings << [:TOOL, format_shell_command(params[0]), true]
60
- else
61
- parser.verify_num_parameters(2, nil, "TOOL <Button Text> <Shell command> <Icon Filename (optional)> <Parameter Name #1 (optional)> <Parameter Value #1 (optional)> ...")
62
- variable_params = nil
63
- if params.length > 3
64
- raise parser.error("Unbalanced variable params for #{params[0]}") if (params.length % 2) != 1
65
- variable_params = []
66
- params[3..-1].each_slice(2) { |variable_parameter| variable_params << variable_parameter }
67
- end
68
- @items << [:TOOL, params[0], format_shell_command(params[1]), true, ConfigParser.handle_nil(params[2]), variable_params]
69
- end
47
+ parse_file(filename)
48
+ else
49
+ raise "Launcher configuration file does not exist: #{filename}"
50
+ end
51
+ end # def initialize
70
52
 
71
- when 'MULTITOOL_START'
72
- parser.verify_num_parameters(1, 2, "MULTITOOL_START <Button Text> <Icon Filename (optional)>")
73
- multitool = true
74
- multitool_text = params[0]
75
- multitool_icon_filename = ConfigParser.handle_nil(params[1])
76
- multitool_icon_filename = 'multi.png' unless multitool_icon_filename
77
- multitool_settings = []
78
-
79
- when 'MULTITOOL_END'
80
- parser.verify_num_parameters(0, 0, "MULTITOOL_END")
81
- @items << [:MULTITOOL, multitool_text, multitool_settings, true, multitool_icon_filename, nil]
82
- multitool = false
83
- multitool_text = nil
84
- multitool_icon_filename = nil
85
- multitool_settings = nil
86
-
87
- when 'DELAY'
88
- if multitool
89
- parser.verify_num_parameters(1, 1, "DELAY <Delay in seconds>")
90
- multitool_settings << [:DELAY, Float(params[0]), true]
91
- else
92
- raise parser.error("DELAY keyword only valid within MULTITOOL")
53
+ # Create a ConfigParser and parse all the lines in the configuration file
54
+ #
55
+ # @param filename [String] Name of the configuration file to parse
56
+ def parse_file(filename)
57
+ multitool = nil
58
+
59
+ # Loop over each line of the configuration file
60
+ parser = ConfigParser.new
61
+ parser.parse_file(filename) do |keyword, params|
62
+ # Handle each keyword
63
+ case keyword
64
+
65
+ when 'TOOL'
66
+ parse_tool(parser, params, multitool)
67
+
68
+ when 'MULTITOOL_START'
69
+ parser.verify_num_parameters(1, 2, "MULTITOOL_START <Button Text> <Icon Filename (optional)>")
70
+ multitool = OpenStruct.new
71
+ multitool.text = params[0]
72
+ multitool.icon_filename = ConfigParser.handle_nil(params[1])
73
+ multitool.icon_filename = 'multi.png' unless multitool.icon_filename
74
+ multitool.settings = []
75
+
76
+ when 'MULTITOOL_END'
77
+ parser.verify_num_parameters(0, 0, "MULTITOOL_END")
78
+ raise parser.error("No TOOLs defined within the MULTITOOL") if multitool.settings.select {|setting| setting[0] == :TOOL }.empty?
79
+ @items << [:MULTITOOL, multitool.text, multitool.settings, true, multitool.icon_filename, nil]
80
+ multitool = nil
81
+
82
+ when 'DELAY'
83
+ if multitool
84
+ parser.verify_num_parameters(1, 1, "DELAY <Delay in seconds>")
85
+ multitool.settings << [:DELAY, Float(params[0]), true]
86
+ else
87
+ raise parser.error("DELAY keyword only valid within MULTITOOL")
88
+ end
89
+
90
+ when 'DIVIDER'
91
+ parser.verify_num_parameters(0, 0, "DIVIDER")
92
+ @items << [:DIVIDER, nil, nil, nil, nil]
93
+
94
+ when 'LABEL'
95
+ parser.verify_num_parameters(1, 1, "LABEL <Label Text>")
96
+ @items << [:LABEL, params[0], nil, nil, nil]
97
+
98
+ when 'TOOL_FONT', 'LABEL_FONT'
99
+ usage = "#{keyword} <Font Name> <Font Size>"
100
+ parser.verify_num_parameters(2, 2, usage)
101
+ begin
102
+ @tool_font_settings = [params[0], Integer(params[1])] if keyword == 'TOOL_FONT'
103
+ @label_font_settings = [params[0], Integer(params[1])] if keyword == 'LABEL_FONT'
104
+ rescue ArgumentError
105
+ raise parser.error("#{usage} passed '#{params[0]} #{params[1]}'")
106
+ end
107
+
108
+ when 'TITLE'
109
+ parser.verify_num_parameters(1, 1, "TITLE <Title Text>")
110
+ @title = params[0]
111
+
112
+ when 'NUM_COLUMNS'
113
+ usage = "NUM_COLUMNS <Num Columns>"
114
+ parser.verify_num_parameters(1, 1, usage)
115
+ begin
116
+ @num_columns = Integer(params[0])
117
+ rescue ArgumentError
118
+ raise parser.error("#{usage} passed '#{params[0]}'")
119
+ end
120
+
121
+ when 'DONT_CAPTURE_IO'
122
+ parser.verify_num_parameters(0, 0, "DONT_CAPTURE_IO")
123
+ if multitool
124
+ if multitool.settings[-1].nil? || multitool.settings[-1][0] != :TOOL
125
+ raise parser.error("DONT_CAPTURE_IO must follow a TOOL")
93
126
  end
94
-
95
- when 'DIVIDER'
96
- parser.verify_num_parameters(0, 0, "DIVIDER")
97
- @items << [:DIVIDER, nil, nil, nil, nil]
98
-
99
- when 'LABEL'
100
- parser.verify_num_parameters(1, 1, "LABEL <Label Text>")
101
- @items << [:LABEL, params[0], nil, nil, nil]
102
-
103
- when 'TOOL_FONT'
104
- parser.verify_num_parameters(2, 2, "TOOL_FONT <Font Name> <Font Size>")
105
- @tool_font_settings = [params[0], Integer(params[1])]
106
-
107
- when 'LABEL_FONT'
108
- parser.verify_num_parameters(2, 2, "LABEL_FONT <Font Name> <Font Size>")
109
- @label_font_settings = [params[0], Integer(params[1])]
110
-
111
- when 'TITLE'
112
- parser.verify_num_parameters(1, 1, "TITLE <Title Text>")
113
- @title = params[0]
114
-
115
- when 'NUM_COLUMNS'
116
- parser.verify_num_parameters(1, 1, "NUM_COLUMNS <Num Columns>")
117
- @num_columns = params[0].to_i
118
-
119
- when 'DONT_CAPTURE_IO'
120
- parser.verify_num_parameters(0, 0, "DONT_CAPTURE_IO")
121
- if multitool
122
- if multitool_settings[-1].nil? || multitool_settings[-1][0] != :TOOL
123
- raise parser.error("DONT_CAPTURE_IO must follow a TOOL")
124
- end
125
- multitool_settings[-1][2] = false
126
- else
127
- if @items[-1].nil? || @items[-1][0] != :TOOL
128
- raise parser.error("DONT_CAPTURE_IO must follow a TOOL")
129
- end
130
- @items[-1][3] = false
127
+ multitool.settings[-1][2] = false
128
+ else
129
+ if @items[-1].nil? || @items[-1][0] != :TOOL
130
+ raise parser.error("DONT_CAPTURE_IO must follow a TOOL")
131
131
  end
132
+ @items[-1][3] = false
133
+ end
132
134
 
133
- else # UNKNOWN
134
- raise parser.error("Unknown keyword '#{keyword}'.") if keyword
135
+ else # UNKNOWN
136
+ raise parser.error("Unknown keyword '#{keyword}'.") if keyword
137
+ end # case keyword
138
+ end # parser.parse_file
139
+ end
135
140
 
136
- end # case keyword
141
+ protected
137
142
 
138
- end # parser.parse_file
143
+ def parse_tool(parser, params, multitool)
144
+ if multitool
145
+ parser.verify_num_parameters(1, 1, "TOOL <Shell command>")
146
+ multitool.settings << [:TOOL, format_shell_command(parser, params[0]), true]
147
+ else
148
+ parser.verify_num_parameters(2, nil, "TOOL <Button Text> <Shell command> <Icon Filename (optional)> <Parameter Name #1 (optional)> <Parameter Value #1 (optional)> ...")
149
+ variable_params = nil
150
+ # Determine if there are parameters which will be displayed in a GUI
151
+ # dialog when the tool starts
152
+ if params.length > 3
153
+ raise parser.error("Unbalanced variable params for #{params[0]}") if (params.length % 2) != 1
154
+ variable_params = []
155
+ params[3..-1].each_slice(2) { |variable_parameter| variable_params << variable_parameter }
156
+ end
157
+ @items << [:TOOL, params[0], format_shell_command(parser, params[1]), true, ConfigParser.handle_nil(params[2]), variable_params]
158
+ end
159
+ end
139
160
 
161
+ def format_shell_command(parser, shell_command)
162
+ formatted_command = ''
163
+ case shell_command.split[0]
164
+ when 'LAUNCH'
165
+ formatted_command = parse_launch(shell_command)
166
+ when 'LAUNCH_TERMINAL'
167
+ formatted_command = parse_launch_terminal(shell_command)
140
168
  else
141
- raise "Launcher configuration file does not exist: #{filename}"
169
+ # Nothing to do if they aren't using our keywords
170
+ formatted_command = shell_command
142
171
  end
172
+ formatted_command
173
+ end
143
174
 
144
- end # def initialize
175
+ def parse_launch(command)
176
+ split = command.split
177
+ if Kernel.is_mac? and File.exist?(File.join(USERPATH, 'tools', 'mac'))
178
+ formatted = "open tools/mac/#{split[1]}.app --args #{split[2..-1].join(' ')}".strip
179
+ else
180
+ formatted = "RUBYW tools/#{split[1]} #{split[2..-1].join(' ')}".strip
181
+ end
182
+ formatted
183
+ end
184
+
185
+ def parse_launch_terminal(command)
186
+ split = command.split
187
+ if Kernel.is_mac?
188
+ formatted = "osascript -e 'tell application \"Terminal\" to do script \"cd #{File.expand_path(USERPATH)} && ruby tools/#{split[1]} #{split[2..-1].join(' ')}\"' -e 'return'"
189
+ elsif Kernel.is_windows?
190
+ formatted = "start ruby tools/#{split[1]} #{split[2..-1].join(' ')}".strip
191
+ else
192
+ formatted = "gnome-terminal -e \"ruby tools/#{split[1]} #{split[2..-1].join(' ')}\""
193
+ end
145
194
 
146
- def format_shell_command(shell_command)
147
195
  if Kernel.is_windows?
148
196
  rubyw_sub = 'rubyw'
149
197
  else
150
198
  rubyw_sub = 'ruby'
151
199
  end
152
200
 
153
- split_command = shell_command.split
154
- if split_command[0] == 'LAUNCH'
155
- if Kernel.is_mac? and File.exist?(File.join(USERPATH, 'tools', 'mac'))
156
- shell_command = "open tools/mac/#{split_command[1]}.app --args #{split_command[2..-1].join(' ')}"
157
- else
158
- shell_command = "RUBYW tools/#{split_command[1]} #{split_command[2..-1].join(' ')}"
159
- end
160
- elsif split_command[0] == 'LAUNCH_TERMINAL'
161
- if Kernel.is_mac?
162
- shell_command = "osascript -e 'tell application \"Terminal\" to do script \"cd #{File.expand_path(USERPATH)} && ruby tools/#{split_command[1]} #{split_command[2..-1].join(' ')}\"' -e 'return'"
163
- elsif Kernel.is_windows?
164
- shell_command = "start ruby tools/#{split_command[1]} #{split_command[2..-1].join(' ')}"
165
- else
166
- shell_command = "gnome-terminal -e \"ruby tools/#{split_command[1]} #{split_command[2..-1].join(' ')}\""
167
- end
168
- end
169
- shell_command.gsub!('RUBYW', rubyw_sub)
170
- shell_command
201
+ formatted.gsub!('RUBYW', rubyw_sub)
202
+ formatted
171
203
  end
172
204
 
173
205
  end # class LauncherConfig