2 * Copyright © 2010 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 package org.altusmetrum.altosuilib_14;
22 import java.awt.event.*;
24 import javax.swing.filechooser.FileNameExtensionFilter;
26 import java.util.concurrent.*;
27 import org.altusmetrum.altoslib_14.*;
29 public class AltosFlashUI
31 implements ActionListener
44 // Hex file with rom image
48 AltosUSBDevice device;
52 // Desired Rom configuration
53 AltosRomconfig rom_config;
56 AltosProgrammer programmer;
58 private static final String[] pair_programmed_files = {
70 private static final String[] pair_programmed_devices = {
83 private boolean is_pair_programmed() {
86 String name = file.getName();
87 for (int i = 0; i < pair_programmed_files.length; i++) {
88 if (name.startsWith(pair_programmed_files[i]))
93 String name = device.toString();
94 for (int i = 0; i < pair_programmed_devices.length; i++) {
95 if (name.startsWith(pair_programmed_devices[i]))
102 public void actionPerformed(ActionEvent e) {
103 if (e.getSource() == cancel) {
104 if (programmer != null)
109 String cmd = e.getActionCommand();
110 if (e.getID() == -1) {
111 JOptionPane.showMessageDialog(frame,
112 e.getActionCommand(),
114 JOptionPane.ERROR_MESSAGE);
117 } else if (cmd.equals(AltosFlashListener.flash_done)) {
120 } else if (cmd.equals(AltosFlashListener.flash_start)) {
123 pbar.setValue(e.getID());
129 public void build_dialog() {
130 GridBagConstraints c;
131 Insets il = new Insets(4,4,4,4);
132 Insets ir = new Insets(4,4,4,4);
134 pane = getContentPane();
135 pane.setLayout(new GridBagLayout());
137 c = new GridBagConstraints();
138 c.gridx = 0; c.gridy = 0;
139 c.fill = GridBagConstraints.NONE;
140 c.anchor = GridBagConstraints.LINE_START;
142 serial_label = new JLabel("Serial:");
143 pane.add(serial_label, c);
145 c = new GridBagConstraints();
146 c.gridx = 1; c.gridy = 0;
147 c.fill = GridBagConstraints.HORIZONTAL;
149 c.anchor = GridBagConstraints.LINE_START;
151 serial_value = new JLabel("");
152 pane.add(serial_value, c);
154 c = new GridBagConstraints();
155 c.fill = GridBagConstraints.NONE;
156 c.gridx = 0; c.gridy = 1;
157 c.anchor = GridBagConstraints.LINE_START;
159 file_label = new JLabel("File:");
160 pane.add(file_label, c);
162 c = new GridBagConstraints();
163 c.fill = GridBagConstraints.HORIZONTAL;
165 c.gridx = 1; c.gridy = 1;
166 c.anchor = GridBagConstraints.LINE_START;
168 file_value = new JLabel(file.toString());
169 pane.add(file_value, c);
171 pbar = new JProgressBar();
173 pbar.setMaximum(100);
176 pbar.setStringPainted(true);
177 pbar.setPreferredSize(new Dimension(600, 20));
178 c = new GridBagConstraints();
179 c.fill = GridBagConstraints.HORIZONTAL;
180 c.anchor = GridBagConstraints.CENTER;
181 c.gridx = 0; c.gridy = 2;
182 c.gridwidth = GridBagConstraints.REMAINDER;
183 Insets ib = new Insets(4,4,4,4);
187 cancel = new JButton("Cancel");
188 c = new GridBagConstraints();
189 c.fill = GridBagConstraints.NONE;
190 c.anchor = GridBagConstraints.CENTER;
191 c.gridx = 0; c.gridy = 3;
192 c.gridwidth = GridBagConstraints.REMAINDER;
193 Insets ic = new Insets(4,4,4,4);
196 cancel.addActionListener(this);
198 setLocationRelativeTo(frame);
201 void set_serial(int serial_number) {
202 serial_value.setText(String.format("%d", serial_number));
205 static class AltosHexfileFilter extends javax.swing.filechooser.FileFilter {
209 public AltosHexfileFilter(String usb_product) {
212 /* Trim off any trailing variants (1.0a vs 1.0) */
213 for (l = usb_product.length(); l > 0; l--) {
214 char c = usb_product.charAt(l-1);
215 if (c < 'a' || 'z' < c)
218 head = usb_product.substring(0, l).toLowerCase();
219 description = String.format("%s Image File", usb_product);
222 public boolean accept(File file) {
223 return !file.isFile() || (file.getName().startsWith(head) && file.getName().endsWith(".ihx"));
226 public String getDescription() {
231 boolean select_source_file() {
232 JFileChooser hexfile_chooser = new JFileChooser();
234 File firmwaredir = AltosUIPreferences.firmwaredir();
235 if (firmwaredir != null)
236 hexfile_chooser.setCurrentDirectory(firmwaredir);
238 hexfile_chooser.setDialogTitle("Select Flash Image");
240 javax.swing.filechooser.FileFilter ihx_filter = new FileNameExtensionFilter("Flash Image", "ihx");
241 hexfile_chooser.addChoosableFileFilter(ihx_filter);
242 hexfile_chooser.setFileFilter(ihx_filter);
244 if (!is_pair_programmed() && !device.matchProduct(AltosLib.product_altusmetrum)) {
245 AltosHexfileFilter filter = new AltosHexfileFilter(device.usb_product());
246 hexfile_chooser.addChoosableFileFilter(filter);
247 hexfile_chooser.setFileFilter(filter);
250 int returnVal = hexfile_chooser.showOpenDialog(frame);
252 if (returnVal != JFileChooser.APPROVE_OPTION)
254 file = hexfile_chooser.getSelectedFile();
257 AltosUIPreferences.set_firmwaredir(file.getParentFile());
262 boolean select_device() {
263 int product = AltosLib.product_any;
265 device = AltosDeviceUIDialog.show_usb(frame, AltosLib.product_any);
272 boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
273 if (a == null || b == null)
274 return (a == null && b == null);
276 if (!a.valid || !b.valid)
279 if (a.usb_id != null && b.usb_id != null &&
280 (a.usb_id.vid != b.usb_id.vid ||
281 a.usb_id.pid != b.usb_id.pid))
284 if (a.usb_product != null && b.usb_product != null &&
285 !a.usb_product.equals(b.usb_product))
291 boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
292 AltosRomconfig new_config;
294 if (!rom_config_matches(existing_config, image_config)) {
296 if (existing_config == null || !existing_config.valid) {
297 ret = JOptionPane.showConfirmDialog(this,
298 String.format("Cannot determine target device type\nImage is %04x:%04x %s\nFlash anyways?",
299 image_config.usb_id.vid,
300 image_config.usb_id.pid,
301 image_config.usb_product),
302 "Unknown Target Device",
303 JOptionPane.YES_NO_OPTION);
305 ret = JOptionPane.showConfirmDialog(this,
306 String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
307 existing_config.usb_id.vid,
308 existing_config.usb_id.pid,
309 existing_config.usb_product,
310 image_config.usb_id.vid,
311 image_config.usb_id.pid,
312 image_config.usb_product),
313 "Image doesn't match Device",
314 JOptionPane.YES_NO_OPTION);
316 if (ret != JOptionPane.YES_OPTION)
320 if (existing_config != null && existing_config.radio_calibration_broken) {
321 int ret = JOptionPane.showConfirmDialog(this,
322 String.format("Radio calibration value %d may be incorrect\nFlash anyways?",
323 existing_config.radio_calibration),
324 "Radio Calibration Invalid",
325 JOptionPane.YES_NO_OPTION);
326 if (ret != JOptionPane.YES_OPTION)
331 new_config = AltosRomconfigUI.show(frame, existing_config);
332 if (new_config == null)
334 rom_config = new_config;
335 set_serial(rom_config.serial_number);
340 void exception (Exception e) {
341 if (e instanceof FileNotFoundException) {
342 JOptionPane.showMessageDialog(frame,
343 ((FileNotFoundException) e).getMessage(),
345 JOptionPane.ERROR_MESSAGE);
346 } else if (e instanceof AltosSerialInUseException) {
347 JOptionPane.showMessageDialog(frame,
348 String.format("Device \"%s\" already in use",
349 device.toShortString()),
351 JOptionPane.ERROR_MESSAGE);
353 JOptionPane.showMessageDialog(frame,
356 JOptionPane.ERROR_MESSAGE);
360 class flash_task implements Runnable, AltosFlashListener {
363 AltosProgrammer programmer;
365 public void position(String in_s, int in_percent) {
366 final String s = in_s;
367 final int percent = in_percent;
368 Runnable r = new Runnable() {
371 ui.actionPerformed(new ActionEvent(this,
374 } catch (Exception ex) {
378 SwingUtilities.invokeLater(r);
383 if (ui.is_pair_programmed())
384 programmer = new AltosFlash(ui.file, link, this);
386 programmer = new AltosSelfFlash(ui.file, link, this);
388 final AltosRomconfig current_config = programmer.target_romconfig(device.usb_id(), device.usb_product());
390 final AltosRomconfig image_config = programmer.image_romconfig();
392 System.out.printf("product %s current %s image %s\n", device.usb_product(), current_config, image_config);
394 final Semaphore await_rom_config = new Semaphore(0);
395 SwingUtilities.invokeLater(new Runnable() {
397 ui.programmer = programmer;
398 ui.update_rom_config_info(current_config, image_config);
399 await_rom_config.release();
402 await_rom_config.acquire();
404 if (ui.rom_config != null) {
405 programmer.set_romconfig(ui.rom_config);
408 } catch (InterruptedException ee) {
409 final Exception e = ee;
410 SwingUtilities.invokeLater(new Runnable() {
415 } catch (IOException ee) {
416 final Exception e = ee;
417 SwingUtilities.invokeLater(new Runnable() {
423 if (programmer != null)
428 public flash_task(AltosFlashUI in_ui) {
430 t = new Thread(this);
438 class open_task implements Runnable {
443 public void do_exception(final Exception e) {
444 SwingUtilities.invokeLater(
447 try { dialog.open_exception(e); } catch (Exception ex) { }
452 public void do_success(final AltosLink link) {
453 SwingUtilities.invokeLater(
456 try { dialog.open_success(link); } catch (Exception ex) { }
461 public void do_failure() {
462 SwingUtilities.invokeLater(
465 try { dialog.open_failure(); } catch (Exception ex) { }
470 public void do_cancel() {
471 SwingUtilities.invokeLater(
474 try { dialog.open_cancel(); } catch (Exception ex) { }
481 AltosLink link = null;
482 boolean new_device = false;
485 System.out.printf("Attempting to open %s\n", device.toShortString());
487 for (int i = 0; i < 20; i++) {
488 link = new AltosSerial(device);
496 System.out.printf("Waiting for device to become ready\n");
501 throw new IOException(String.format("%s: open failed",
502 device.toShortString()));
504 /* See if the link is usable already */
505 if (is_pair_programmed() || link.is_loader()) {
506 System.out.printf("Device ready for use\n");
511 java.util.List<AltosDevice> prev_devices =
512 AltosUSBDevice.list(AltosLib.product_altusmetrum);
514 /* Nope, switch to loader and
515 * wait for it to re-appear
518 System.out.printf("Switch to loader\n");
522 /* This is a bit fragile, but
523 * I'm not sure what else to
524 * do other than ask the user.
526 * Search for a device which
527 * wasn't there before we
528 * asked the target to switch
535 java.util.List<AltosDevice> devices =
536 AltosUSBDevice.list(AltosLib.product_altusmetrum);
538 for (AltosDevice d : devices) {
539 boolean matched = false;
540 System.out.printf("\tfound device %s\n", d.toShortString());
541 for (AltosDevice p : prev_devices)
547 System.out.printf("Identified new device %s\n", d.toShortString());
548 device = (AltosUSBDevice) d;
557 } catch (AltosSerialInUseException ee) {
559 } catch (FileNotFoundException fe) {
561 } catch (IOException ie) {
563 } catch (InterruptedException ie) {
567 public void cancel() {
571 public open_task(AltosDevice device, open_dialog dialog) {
572 this.device = device;
573 this.dialog = dialog;
574 t = new Thread(this);
580 extends AltosUIDialog
581 implements ActionListener
585 private JLabel opening_label;
586 private JButton cancel_button;
588 boolean done = false;
590 AltosLink link = null;
592 open_task open = null;
594 public void open_exception(Exception e) {
595 System.out.printf("open_exception\n");
601 public void open_success(AltosLink link) {
602 System.out.printf("open_success\n");
608 public void open_failure() {
609 System.out.printf("open_failure\n");
614 public void open_cancel() {
615 System.out.printf("open_cancel\n");
620 public AltosLink do_open(open_task open) throws InterruptedException {
626 public void actionPerformed(ActionEvent e) {
627 String cmd = e.getActionCommand();
629 if (cmd.equals("cancel"))
636 public open_dialog(AltosUIFrame in_owner) {
637 super(in_owner, "Open Flash Target Device", true);
640 Container pane = getContentPane();
641 GridBagConstraints c = new GridBagConstraints();
642 Insets i = new Insets(4,4,4,4);
645 pane.setLayout(new GridBagLayout());
647 opening_label = new JLabel("Opening Device");
648 c.fill = GridBagConstraints.HORIZONTAL;
649 c.anchor = GridBagConstraints.LINE_START;
657 pane.add(opening_label, c);
659 cancel_button = new JButton("Cancel");
660 cancel_button.addActionListener(this);
661 cancel_button.setActionCommand("cancel");
664 pane.add(cancel_button, c);
666 setLocationRelativeTo(owner);
670 private boolean open_device() throws InterruptedException {
672 open_dialog dialog = new open_dialog(frame);
674 open_task open = new open_task(device, dialog);
676 link = dialog.do_open(open);
682 * Execute the steps for flashing
683 * a device. Note that this returns immediately;
684 * this dialog is not modal
687 if (!select_device())
689 if (!select_source_file())
694 } catch (InterruptedException ie) {
698 flash_task f = new flash_task(this);
701 public static void show(AltosUIFrame frame) {
702 AltosFlashUI ui = new AltosFlashUI(frame);
706 public AltosFlashUI(AltosUIFrame in_frame) {
707 super(in_frame, "Program Altusmetrum Device", false);