cosmos 3.5.3 → 3.6.0

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Manifest.txt +3 -0
  4. data/autohotkey/procedures/script_test.rb +4 -0
  5. data/autohotkey/tools/script_runner2.ahk +13 -0
  6. data/cosmos.gemspec +2 -2
  7. data/data/crc.txt +17 -17
  8. data/demo/config/data/crc.txt +10 -7
  9. data/demo/config/targets/INST/cmd_tlm/_ccsds_cmd.txt +9 -0
  10. data/demo/config/targets/INST/cmd_tlm/_ccsds_tlm.txt +19 -0
  11. data/demo/config/targets/INST/cmd_tlm/inst_cmds.txt +19 -84
  12. data/demo/config/targets/INST/cmd_tlm/inst_tlm.txt +27 -110
  13. data/demo/config/tools/table_manager/TLMMonitoringTable_def.txt +3 -220
  14. data/demo/config/tools/tlm_extractor/_adcs_time.txt +2 -0
  15. data/demo/config/tools/tlm_extractor/tlm_extractor.txt +1 -1
  16. data/demo/config/tools/tlm_extractor/tlm_extractor2.txt +1 -1
  17. data/demo/config/tools/tlm_extractor/tlm_extractor3.txt +1 -1
  18. data/demo/config/tools/tlm_extractor/tlm_extractor4.txt +1 -1
  19. data/lib/cosmos/config/config_parser.rb +54 -1
  20. data/lib/cosmos/gui/utilities/script_module_gui.rb +31 -20
  21. data/lib/cosmos/io/json_drb.rb +33 -23
  22. data/lib/cosmos/io/json_drb_object.rb +4 -1
  23. data/lib/cosmos/io/tcpip_server.rb +1 -1
  24. data/lib/cosmos/packets/packet_config.rb +5 -1
  25. data/lib/cosmos/packets/parsers/macro_parser.rb +1 -1
  26. data/lib/cosmos/script/scripting.rb +28 -0
  27. data/lib/cosmos/streams/tcpip_socket_stream.rb +72 -19
  28. data/lib/cosmos/system/target.rb +16 -2
  29. data/lib/cosmos/tools/cmd_sender/cmd_sender.rb +28 -17
  30. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +14 -2
  31. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +1 -1
  32. data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +27 -20
  33. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +2 -2
  34. data/lib/cosmos/tools/script_runner/script_runner_frame.rb +40 -36
  35. data/lib/cosmos/version.rb +5 -5
  36. data/spec/config/config_parser_spec.rb +1 -1
  37. data/spec/io/json_drb_spec.rb +7 -21
  38. data/spec/packets/packet_config_spec.rb +12 -12
  39. data/spec/packets/parsers/format_string_parser_spec.rb +3 -3
  40. data/spec/packets/parsers/limits_parser_spec.rb +10 -10
  41. data/spec/packets/parsers/limits_response_parser_spec.rb +2 -2
  42. data/spec/packets/parsers/macro_parser_spec.rb +6 -6
  43. data/spec/packets/parsers/packet_parser_spec.rb +1 -1
  44. data/spec/packets/parsers/processor_parser_spec.rb +2 -2
  45. data/spec/packets/parsers/state_parser_spec.rb +1 -1
  46. data/spec/script/scripting_spec.rb +23 -0
  47. data/spec/streams/tcpip_socket_stream_spec.rb +28 -0
  48. data/spec/system/system_spec.rb +20 -20
  49. data/spec/system/target_spec.rb +10 -10
  50. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +30 -22
  51. metadata +9 -6
@@ -48,6 +48,7 @@ module Cosmos
48
48
  @port = port
49
49
  @mutex = Mutex.new
50
50
  @socket = nil
51
+ @pipe_reader, @pipe_writer = IO.pipe
51
52
  @id = 0
52
53
  @request_in_progress = false
53
54
  @connect_timeout = connect_timeout
@@ -58,6 +59,7 @@ module Cosmos
58
59
  # Disconnects from the JSON server
59
60
  def disconnect
60
61
  Cosmos.close_socket(@socket)
62
+ @pipe_writer.write('.')
61
63
  # Cannot set @socket to nil here because this method can be called by
62
64
  # other threads and @socket being nil would cause unexpected errors in method_missing
63
65
  # Also don't want to take the mutex so that we can interrupt method_missing if necessary
@@ -113,6 +115,7 @@ module Cosmos
113
115
  addr = Socket.pack_sockaddr_in(@port, @hostname)
114
116
  @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
115
117
  @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
118
+ @pipe_reader, @pipe_writer = IO.pipe
116
119
  begin
117
120
  @socket.connect_nonblock(addr)
118
121
  rescue IO::WaitWritable
@@ -159,7 +162,7 @@ module Cosmos
159
162
  STDOUT.puts request_data if JsonDRb.debug?
160
163
  @request_in_progress = true
161
164
  JsonDRb.send_data(@socket, request_data)
162
- response_data = JsonDRb.receive_message(@socket, '')
165
+ response_data = JsonDRb.receive_message(@socket, '', @pipe_reader)
163
166
  @request_in_progress = false
164
167
  STDOUT.puts "\nResponse:\n" if JsonDRb.debug?
165
168
  STDOUT.puts response_data if JsonDRb.debug?
@@ -483,7 +483,7 @@ module Cosmos
483
483
  stream_protocol.disconnect
484
484
  stream_protocol.stream.raw_logger_pair.stop if stream_protocol.stream.raw_logger_pair
485
485
  indexes_to_delete.unshift(index) # Put later indexes at front of array
486
- rescue Errno::ECONNRESET, Errno::ECONNABORTED
486
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError
487
487
  # Client has disconnected
488
488
  Logger.instance.info "Tcpip server lost write connection to #{hostname}(#{host_ip}):#{port}"
489
489
  stream_protocol.disconnect
@@ -95,13 +95,16 @@ module Cosmos
95
95
  # @param filename [String] The name of the configuration file
96
96
  # @param target_name [String] The target name
97
97
  def process_file(filename, process_target_name)
98
+ # Partial files are included into another file and thus aren't directly processed
99
+ return if File.basename(filename)[0] == '_' # Partials start with underscore
100
+
98
101
  @converted_type = nil
99
102
  @converted_bit_size = nil
100
103
  @proc_text = ''
101
104
  @building_generic_conversion = false
102
105
 
103
106
  process_target_name = process_target_name.upcase
104
- parser = ConfigParser.new("https://github.com/BallAerospace/COSMOS/wiki/Command-and-Telemetry-Configuration")
107
+ parser = ConfigParser.new("http://cosmosrb.com/docs/cmdtlm")
105
108
  parser.parse_file(filename) do |keyword, params|
106
109
 
107
110
  if @building_generic_conversion
@@ -230,6 +233,7 @@ module Cosmos
230
233
  # This simulates an array of structures of multiple items in the packet by repeating
231
234
  # each item in the list multiple times with a different "index" added to the name.
232
235
  when 'MACRO_APPEND_START'
236
+ Logger.warn "MACRO_APPEND_START/END is deprecated. Please use new ERB macro syntax."
233
237
  MacroParser.start(parser)
234
238
 
235
239
  # End the creation of a macro-expanded list of items
@@ -54,7 +54,7 @@ module Cosmos
54
54
  if first_index < last_index
55
55
  @macro.indices = (first_index..last_index).to_a
56
56
  else
57
- @macro.indices = (last_index..first_index).to_a
57
+ @macro.indices = (last_index..first_index).to_a.reverse
58
58
  end
59
59
  @macro.format = parser.parameters[2] ? parser.parameters[2] : '%s%d'
60
60
  @macro.format_order = get_format_order()
@@ -69,6 +69,34 @@ module Cosmos
69
69
  prompt_combo_box(string, options)
70
70
  end
71
71
 
72
+ def _file_dialog(message, directory, select_files = true)
73
+ answer = ''
74
+ files = Dir["#{directory}/*"]
75
+ if select_files
76
+ files.select! {|f| !File.directory? f }
77
+ else
78
+ files.select! {|f| File.directory? f }
79
+ end
80
+ while answer.empty?
81
+ print message + "\n" + files.join("\n") + "\n<Type file name>:"
82
+ answer = gets
83
+ answer.chomp!
84
+ end
85
+ return answer
86
+ end
87
+ def save_file_dialog(directory = Cosmos::USERPATH, message = "Save File")
88
+ _file_dialog(message, directory)
89
+ end
90
+ def open_file_dialog(directory = Cosmos::USERPATH, message = "Open File")
91
+ _file_dialog(message, directory)
92
+ end
93
+ def open_files_dialog(directory = Cosmos::USERPATH, message = "Open File(s)")
94
+ _file_dialog(message, directory)
95
+ end
96
+ def open_directory_dialog(directory = Cosmos::USERPATH, message = "Open Directory")
97
+ _file_dialog(message, directory, false)
98
+ end
99
+
72
100
  # Creates a string with the parameters upcased
73
101
  def _upcase(target_name, packet_name, item_name)
74
102
  "#{target_name.upcase} #{packet_name.upcase} #{item_name.upcase}"
@@ -20,6 +20,8 @@ module Cosmos
20
20
  class TcpipSocketStream < Stream
21
21
  attr_reader :write_socket
22
22
 
23
+ FAST_READ = (RUBY_VERSION > "2.1")
24
+
23
25
  # @param write_socket [Socket] Socket to write
24
26
  # @param read_socket [Socket] Socket to read
25
27
  # @param write_timeout [Float|nil] Number of seconds to wait for the write
@@ -39,6 +41,7 @@ module Cosmos
39
41
  # Mutex on write is needed to protect from commands coming in from more
40
42
  # than one tool
41
43
  @write_mutex = Mutex.new
44
+ @pipe_reader, @pipe_writer = IO.pipe
42
45
  @connected = false
43
46
  end
44
47
 
@@ -48,27 +51,66 @@ module Cosmos
48
51
 
49
52
  # No read mutex is needed because there is only one stream procesor
50
53
  # reading
51
- begin
52
- data = @read_socket.recv_nonblock(65535)
53
- @raw_logger_pair.read_logger.write(data) if @raw_logger_pair
54
- rescue IO::WaitReadable
55
- # Wait for the socket to be ready for reading or for the timeout
54
+ if FAST_READ
56
55
  begin
57
- result = IO.fast_select([@read_socket], nil, nil, @read_timeout)
56
+ while true # Loop until we get some data
57
+ data = @read_socket.read_nonblock(65535, exception: false)
58
+ raise EOFError, 'end of file reached' unless data
59
+ if data == :wait_readable
60
+ # Wait for the socket to be ready for reading or for the timeout
61
+ begin
62
+ result = IO.fast_select([@read_socket, @pipe_reader], nil, nil, @read_timeout)
63
+ # If select returns something it means the socket is now available for
64
+ # reading so retry the read. If it returns nil it means we timed out.
65
+ # If the pipe is present that means we closed the socket
66
+ if result
67
+ if result.include?(@pipe_reader)
68
+ raise IOError
69
+ else
70
+ next
71
+ end
72
+ else
73
+ raise Timeout::Error, "Read Timeout"
74
+ end
75
+ rescue IOError, Errno::ENOTSOCK
76
+ # These can happen with the socket being closed while waiting on select
77
+ data = ''
78
+ end
79
+ end
80
+ @raw_logger_pair.read_logger.write(data) if @raw_logger_pair
81
+ break
82
+ end
83
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError, Errno::ENOTSOCK
84
+ data = ''
85
+ end
86
+ else
87
+ begin
88
+ data = @read_socket.read_nonblock(65535)
89
+ @raw_logger_pair.read_logger.write(data) if @raw_logger_pair
90
+ rescue IO::WaitReadable
91
+ # Wait for the socket to be ready for reading or for the timeout
92
+ begin
93
+ result = IO.fast_select([@read_socket, @pipe_reader], nil, nil, @read_timeout)
58
94
 
59
- # If select returns something it means the socket is now available for
60
- # reading so retry the read. If it returns nil it means we timed out.
61
- if result
62
- retry
63
- else
64
- raise Timeout::Error, "Read Timeout"
95
+ # If select returns something it means the socket is now available for
96
+ # reading so retry the read. If it returns nil it means we timed out.
97
+ # If the pipe is present that means we closed the socket
98
+ if result
99
+ if result.include?(@pipe_reader)
100
+ raise IOError
101
+ else
102
+ retry
103
+ end
104
+ else
105
+ raise Timeout::Error, "Read Timeout"
106
+ end
107
+ rescue IOError, Errno::ENOTSOCK
108
+ # These can happen with the socket being closed while waiting on select
109
+ data = ''
65
110
  end
66
- rescue IOError, Errno::ENOTSOCK
67
- # These can happen with the socket being closed while waiting on select
111
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError, Errno::ENOTSOCK
68
112
  data = ''
69
113
  end
70
- rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError, Errno::ENOTSOCK
71
- data = ''
72
114
  end
73
115
 
74
116
  data
@@ -79,9 +121,19 @@ module Cosmos
79
121
  # No read mutex is needed because there is only one stream procesor
80
122
  # reading
81
123
  begin
82
- data = @read_socket.recv_nonblock(65535)
83
- @raw_logger_pair.read_logger.write(data) if @raw_logger_pair
84
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNRESET, Errno::ECONNABORTED
124
+ if FAST_READ
125
+ data = @read_socket.read_nonblock(65535, exception: false)
126
+ raise EOFError, 'end of file reached' unless data
127
+ if data == :wait_readable
128
+ data = ''
129
+ else
130
+ @raw_logger_pair.read_logger.write(data) if @raw_logger_pair
131
+ end
132
+ else
133
+ data = @read_socket.read_nonblock(65535)
134
+ @raw_logger_pair.read_logger.write(data) if @raw_logger_pair
135
+ end
136
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNRESET, Errno::ECONNABORTED, IOError
85
137
  data = ''
86
138
  end
87
139
 
@@ -135,6 +187,7 @@ module Cosmos
135
187
  def disconnect
136
188
  Cosmos.close_socket(@write_socket)
137
189
  Cosmos.close_socket(@read_socket)
190
+ @pipe_writer.write('.')
138
191
  @connected = false
139
192
  end
140
193
 
@@ -107,6 +107,8 @@ module Cosmos
107
107
  # If target.txt didn't specify specific cmd/tlm files then add everything
108
108
  if @cmd_tlm_files.empty?
109
109
  @cmd_tlm_files = add_all_cmd_tlm(@dir)
110
+ else
111
+ add_cmd_tlm_partials(@dir)
110
112
  end
111
113
  end
112
114
 
@@ -184,14 +186,26 @@ module Cosmos
184
186
  def add_all_cmd_tlm(dir)
185
187
  cmd_tlm_files = []
186
188
  if Dir.exist?(File.join(dir, 'cmd_tlm'))
187
- # Only grab *.txt files in the root of the cmd_tlm folder
188
- Dir[File.join(dir, 'cmd_tlm', '*.txt')].each do |filename|
189
+ # Grab All *.txt files in the cmd_tlm folder and subfolders
190
+ Dir[File.join(dir, 'cmd_tlm', '**', '*.txt')].each do |filename|
189
191
  cmd_tlm_files << filename
190
192
  end
191
193
  end
192
194
  cmd_tlm_files.sort!
193
195
  end
194
196
 
197
+ # Make sure all partials are included in the cmd_tlm list for the MD5 calculation
198
+ def add_cmd_tlm_partials(dir)
199
+ if Dir.exist?(File.join(dir, 'cmd_tlm'))
200
+ # Grab all _*.txt files in the cmd_tlm folder and subfolders
201
+ Dir[File.join(dir, 'cmd_tlm', '**', '_*.txt')].each do |filename|
202
+ @cmd_tlm_files << filename
203
+ end
204
+ end
205
+ @cmd_tlm_files.uniq!
206
+ @cmd_tlm_files.sort!
207
+ end
208
+
195
209
  end # class Target
196
210
 
197
211
  end # module Cosmos
@@ -84,6 +84,24 @@ module Cosmos
84
84
  initialize_menus()
85
85
  initialize_central_widget()
86
86
  complete_initialize() # defined in qt_tool
87
+
88
+ # Bring up slash screen for long duration tasks after creation
89
+ Splash.execute(self) do |splash|
90
+ # Configure CosmosConfig to interact with splash screen
91
+ ConfigParser.splash = splash
92
+
93
+ System.commands
94
+ Qt.execute_in_main_thread(true) do
95
+ update_targets()
96
+ @target_select.setCurrentText(options.packet[0]) if options.packet
97
+ update_commands()
98
+ @cmd_select.setCurrentText(options.packet[1]) if options.packet
99
+ update_cmd_params()
100
+ end
101
+
102
+ # Unconfigure CosmosConfig to interact with splash screen
103
+ ConfigParser.splash = nil
104
+ end
87
105
  end
88
106
 
89
107
  def initialize_actions
@@ -245,24 +263,8 @@ module Cosmos
245
263
  layout.addWidget(splitter)
246
264
  central_widget.layout = layout
247
265
 
248
- #Mark this window as the window for popups
266
+ # Mark this window as the window for popups
249
267
  set_cmd_tlm_gui_window(self)
250
-
251
- # Bring up slash screen for long duration tasks after creation
252
- Splash.execute(self) do |splash|
253
- # Configure CosmosConfig to interact with splash screen
254
- ConfigParser.splash = splash
255
-
256
- System.commands
257
- Qt.execute_in_main_thread(true) do
258
- update_targets()
259
- update_commands()
260
- update_cmd_params()
261
- end
262
-
263
- # Unconfigure CosmosConfig to interact with splash screen
264
- ConfigParser.splash = nil
265
- end
266
268
  end
267
269
 
268
270
  def menu_states_in_hex(checked)
@@ -706,6 +708,15 @@ module Cosmos
706
708
  options.width = 600
707
709
  options.height = 425
708
710
  options.title = 'Command Sender'
711
+ option_parser.separator "Command Sender Specific Options:"
712
+ option_parser.on("-p", "--packet 'TARGET_NAME PACKET_NAME'", "Start with the specified command selected") do |arg|
713
+ split = arg.split
714
+ if split.length != 2
715
+ puts "Packet must be specified as 'TARGET_NAME PACKET_NAME' in quotes"
716
+ exit
717
+ end
718
+ options.packet = split
719
+ end
709
720
  end
710
721
 
711
722
  super(option_parser, options)
@@ -79,15 +79,27 @@ module Cosmos
79
79
  when 'PACKET_LOG_WRITER'
80
80
  usage = "PACKET_LOG_WRITER <Name> <Filename> <Specific Parameters>"
81
81
  parser.verify_num_parameters(2, nil, usage)
82
+ packet_log_writer_name = params[0].upcase
82
83
  packet_log_writer_class = Cosmos.require_class(params[1])
84
+
85
+ # Verify not overridding a packet log writer that is already associated with an interface
86
+ packet_log_writer_pair = @packet_log_writer_pairs[packet_log_writer_name]
87
+ if packet_log_writer_pair
88
+ @interfaces.each do |interface_name, interface|
89
+ if interface.packet_log_writer_pairs.include?(packet_log_writer_pair)
90
+ raise parser.error("Redefining Packet Log Writer #{packet_log_writer_name} not allowed after it is associated with an interface")
91
+ end
92
+ end
93
+ end
94
+
83
95
  if params[2]
84
96
  cmd_log_writer = packet_log_writer_class.new(:CMD, *params[2..-1])
85
97
  tlm_log_writer = packet_log_writer_class.new(:TLM, *params[2..-1])
86
- @packet_log_writer_pairs[params[0].upcase] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
98
+ @packet_log_writer_pairs[packet_log_writer_name] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
87
99
  else
88
100
  cmd_log_writer = packet_log_writer_class.new(:CMD)
89
101
  tlm_log_writer = packet_log_writer_class.new(:TLM)
90
- @packet_log_writer_pairs[params[0].upcase] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
102
+ @packet_log_writer_pairs[packet_log_writer_name] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
91
103
  end
92
104
 
93
105
  when 'AUTO_INTERFACE_TARGETS'
@@ -179,7 +179,7 @@ module Cosmos
179
179
  when 1
180
180
  handle_tab('Targets') { @targets_tab.update }
181
181
  when 2
182
- handle_tab('Packets') { @packets_tab.update(PacketsTab::COMMANDS) }
182
+ handle_tab('Commands') { @packets_tab.update(PacketsTab::COMMANDS) }
183
183
  when 3
184
184
  handle_tab('Telemetry') { @packets_tab.update(PacketsTab::TELEMETRY) }
185
185
  when 4
@@ -76,12 +76,12 @@ module Cosmos
76
76
  table = Qt::TableWidget.new()
77
77
  table.verticalHeader.hide()
78
78
  table.setRowCount(count)
79
- column_cnt = 4
80
- column_cnt += 1 if name == TELEMETRY
79
+ column_cnt = 5
81
80
  table.setColumnCount(column_cnt)
82
81
  # Force the last section to fill all available space in the frame
83
82
  #~ table.horizontalHeader.setStretchLastSection(true)
84
83
  headers = ["Target Name", "Packet Name", "Packet Count", "View Raw"]
84
+ headers << "View in Command Sender" if name == COMMANDS
85
85
  headers << "View in Packet Viewer" if name == TELEMETRY
86
86
  table.setHorizontalHeaderLabels(headers)
87
87
 
@@ -121,24 +121,10 @@ module Cosmos
121
121
  end
122
122
  table.setCellWidget(row, 3, view_raw)
123
123
 
124
- if name == TELEMETRY
125
- if target_name != 'UNKNOWN' and packet_name != 'UNKNOWN'
126
- view_pv = Qt::PushButton.new("View in Packet Viewer")
127
- view_pv.connect(SIGNAL('clicked()')) do
128
- if Kernel.is_windows?
129
- Cosmos.run_process("rubyw tools/PacketViewer -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
130
- elsif Kernel.is_mac? and File.exist?("tools/mac/PacketViewer.app")
131
- Cosmos.run_process("open tools/mac/PacketViewer.app --args -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
132
- else
133
- Cosmos.run_process("ruby tools/PacketViewer -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
134
- end
135
- end
136
- table.setCellWidget(row, 4, view_pv)
137
- else
138
- table_widget = Qt::TableWidgetItem.new(Qt::Object.tr('N/A'))
139
- table_widget.setTextAlignment(Qt::AlignCenter)
140
- table.setItem(row, 4, table_widget)
141
- end
124
+ if name == COMMANDS
125
+ add_tool_button(table, row, target_name, packet_name, "Command Sender")
126
+ elsif name == TELEMETRY
127
+ add_tool_button(table, row, target_name, packet_name, "Packet Viewer")
142
128
  end
143
129
 
144
130
  row += 1
@@ -146,5 +132,26 @@ module Cosmos
146
132
  end
147
133
  end
148
134
 
135
+ def add_tool_button(table, row, target_name, packet_name, tool_name)
136
+ if target_name != 'UNKNOWN' and packet_name != 'UNKNOWN'
137
+ view_pv = Qt::PushButton.new("View in #{tool_name}")
138
+ view_pv.connect(SIGNAL('clicked()')) do
139
+ tool_name = tool_name.split.join.gsub("Command","Cmd") # remove space and convert name
140
+ if Kernel.is_windows?
141
+ Cosmos.run_process("rubyw tools/#{tool_name} -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
142
+ elsif Kernel.is_mac? and File.exist?("tools/mac/#{tool_name}.app")
143
+ Cosmos.run_process("open tools/mac/#{tool_name}.app --args -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
144
+ else
145
+ Cosmos.run_process("ruby tools/#{tool_name} -p \"#{target_name} #{packet_name}\" --system #{File.basename(System.initial_filename)}")
146
+ end
147
+ end
148
+ table.setCellWidget(row, 4, view_pv)
149
+ else
150
+ table_widget = Qt::TableWidgetItem.new(Qt::Object.tr('N/A'))
151
+ table_widget.setTextAlignment(Qt::AlignCenter)
152
+ table.setItem(row, 4, table_widget)
153
+ end
154
+ end
155
+
149
156
  end
150
157
  end # module Cosmos