cosmos 3.1.1 → 3.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +6 -0
- data/autohotkey/config/tools/handbook_creator/templates/command_packets.html.erb +1 -1
- data/autohotkey/config/tools/handbook_creator/templates/footer.html.erb +2 -2
- data/autohotkey/config/tools/handbook_creator/templates/header.html.erb +4 -4
- data/autohotkey/tools/autohotkey.rb +1 -1
- data/autohotkey/tools/cmd_tlm_server.ahk +1 -0
- data/autohotkey/tools/packet_viewer.ahk +45 -2
- data/data/crc.txt +20 -15
- data/demo/Rakefile +16 -0
- data/demo/config/data/crc.txt +3 -3
- data/demo/config/tools/handbook_creator/templates/footer.html.erb +2 -2
- data/demo/config/tools/handbook_creator/templates/header.html.erb +4 -4
- data/demo/procedures/example_test.rb +1 -1
- data/install/Rakefile +16 -0
- data/install/config/data/crc.txt +2 -2
- data/install/config/tools/handbook_creator/templates/footer.html.erb +2 -2
- data/install/config/tools/handbook_creator/templates/header.html.erb +4 -4
- data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +64 -57
- data/lib/cosmos/gui/line_graph/line_graph_scaling.rb +0 -9
- data/lib/cosmos/gui/qt.rb +22 -18
- data/lib/cosmos/packet_logs/packet_log_writer.rb +6 -2
- data/lib/cosmos/script/script.rb +1 -1
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +0 -1
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +99 -784
- data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +189 -0
- data/lib/cosmos/tools/cmd_tlm_server/gui/logging_tab.rb +176 -0
- data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +144 -0
- data/lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb +240 -0
- data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +90 -0
- data/lib/cosmos/tools/launcher/launcher_config.rb +142 -110
- data/lib/cosmos/tools/test_runner/results_writer.rb +1 -1
- data/lib/cosmos/tools/test_runner/test_runner.rb +3 -2
- data/lib/cosmos/tools/tlm_grapher/data_objects/data_object.rb +18 -2
- data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +4 -9
- data/lib/cosmos/tools/tlm_grapher/data_objects/xy_data_object.rb +5 -5
- data/lib/cosmos/top_level.rb +1 -1
- data/lib/cosmos/version.rb +4 -4
- data/run_gui_tests.bat +33 -31
- data/spec/core_ext/time_spec.rb +51 -0
- data/spec/script/script_spec.rb +96 -0
- data/spec/tools/cmd_tlm_server/commanding_spec.rb +28 -0
- data/spec/tools/cmd_tlm_server/connections_spec.rb +88 -0
- data/spec/tools/cmd_tlm_server/router_thread_spec.rb +78 -25
- data/spec/tools/launcher/launcher_config_spec.rb +460 -0
- data/spec/top_level/top_level_spec.rb +1 -1
- 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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
134
|
-
|
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
|
-
|
141
|
+
protected
|
137
142
|
|
138
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
154
|
-
|
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
|