cosmos 3.5.3 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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