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 = getScrollablePane();
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) {
213 /* Trim off any trailing variants (1.0a vs 1.0) */
214 for (dash = usb_product.length(); dash > 0; dash--) {
215 char c = usb_product.charAt(dash-1);
220 dash = usb_product.length();
222 for (l = usb_product.length(); l > dash; l--) {
223 char c = usb_product.charAt(l-1);
224 if (c < 'a' || 'z' < c)
227 head = usb_product.substring(0, l).toLowerCase();
228 description = String.format("%s Image File", usb_product);
231 public boolean accept(File file) {
232 return !file.isFile() || (file.getName().startsWith(head) && file.getName().endsWith(".ihx"));
235 public String getDescription() {
240 boolean select_source_file() {
241 JFileChooser hexfile_chooser = new JFileChooser();
243 File firmwaredir = AltosUIPreferences.firmwaredir();
244 if (firmwaredir != null)
245 hexfile_chooser.setCurrentDirectory(firmwaredir);
247 hexfile_chooser.setDialogTitle("Select Flash Image");
249 javax.swing.filechooser.FileFilter ihx_filter = new FileNameExtensionFilter("Flash Image", "ihx");
250 hexfile_chooser.addChoosableFileFilter(ihx_filter);
251 hexfile_chooser.setFileFilter(ihx_filter);
253 if (!is_pair_programmed() && !device.matchProduct(AltosLib.product_altusmetrum)) {
254 AltosHexfileFilter filter = new AltosHexfileFilter(device.usb_product());
255 hexfile_chooser.addChoosableFileFilter(filter);
256 hexfile_chooser.setFileFilter(filter);
259 int returnVal = hexfile_chooser.showOpenDialog(frame);
261 if (returnVal != JFileChooser.APPROVE_OPTION)
263 file = hexfile_chooser.getSelectedFile();
266 AltosUIPreferences.set_firmwaredir(file.getParentFile());
271 boolean select_device() {
272 int product = AltosLib.product_any;
274 device = AltosDeviceUIDialog.show_usb(frame, AltosLib.product_any);
281 boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
282 if (a == null || b == null)
283 return (a == null && b == null);
285 if (!a.valid || !b.valid)
288 if (a.usb_id != null && b.usb_id != null &&
289 (a.usb_id.vid != b.usb_id.vid ||
290 a.usb_id.pid != b.usb_id.pid))
293 if (a.usb_product != null && b.usb_product != null &&
294 !a.usb_product.equals(b.usb_product))
300 boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
301 AltosRomconfig new_config;
303 if (!rom_config_matches(existing_config, image_config)) {
305 if (existing_config == null || !existing_config.valid) {
306 ret = JOptionPane.showConfirmDialog(this,
307 String.format("Cannot determine target device type\nImage is %04x:%04x %s\nFlash anyways?",
308 image_config.usb_id.vid,
309 image_config.usb_id.pid,
310 image_config.usb_product),
311 "Unknown Target Device",
312 JOptionPane.YES_NO_OPTION);
314 ret = JOptionPane.showConfirmDialog(this,
315 String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
316 existing_config.usb_id.vid,
317 existing_config.usb_id.pid,
318 existing_config.usb_product,
319 image_config.usb_id.vid,
320 image_config.usb_id.pid,
321 image_config.usb_product),
322 "Image doesn't match Device",
323 JOptionPane.YES_NO_OPTION);
325 if (ret != JOptionPane.YES_OPTION)
329 if (existing_config != null && existing_config.radio_calibration_broken) {
330 int ret = JOptionPane.showConfirmDialog(this,
331 String.format("Radio calibration value %d may be incorrect\nFlash anyways?",
332 existing_config.radio_calibration),
333 "Radio Calibration Invalid",
334 JOptionPane.YES_NO_OPTION);
335 if (ret != JOptionPane.YES_OPTION)
340 new_config = AltosRomconfigUI.show(frame, existing_config);
341 if (new_config == null)
343 rom_config = new_config;
344 set_serial(rom_config.serial_number);
349 void exception (Exception e) {
350 if (e instanceof FileNotFoundException) {
351 JOptionPane.showMessageDialog(frame,
352 ((FileNotFoundException) e).getMessage(),
354 JOptionPane.ERROR_MESSAGE);
355 } else if (e instanceof AltosSerialInUseException) {
356 JOptionPane.showMessageDialog(frame,
357 String.format("Device \"%s\" already in use",
358 device.toShortString()),
360 JOptionPane.ERROR_MESSAGE);
362 JOptionPane.showMessageDialog(frame,
365 JOptionPane.ERROR_MESSAGE);
369 class flash_task implements Runnable, AltosFlashListener {
372 AltosProgrammer programmer;
374 public void position(String in_s, int in_percent) {
375 final String s = in_s;
376 final int percent = in_percent;
377 Runnable r = new Runnable() {
380 ui.actionPerformed(new ActionEvent(this,
383 } catch (Exception ex) {
387 SwingUtilities.invokeLater(r);
392 if (ui.is_pair_programmed())
393 programmer = new AltosFlash(ui.file, link, this);
395 programmer = new AltosSelfFlash(ui.file, link, this);
397 final AltosRomconfig current_config = programmer.target_romconfig(device.usb_id(), device.usb_product());
399 final AltosRomconfig image_config = programmer.image_romconfig();
401 System.out.printf("product %s current %s image %s\n", device.usb_product(), current_config, image_config);
403 final Semaphore await_rom_config = new Semaphore(0);
404 SwingUtilities.invokeLater(new Runnable() {
406 ui.programmer = programmer;
407 ui.update_rom_config_info(current_config, image_config);
408 await_rom_config.release();
411 await_rom_config.acquire();
413 if (ui.rom_config != null) {
414 programmer.set_romconfig(ui.rom_config);
417 } catch (InterruptedException ee) {
418 final Exception e = ee;
419 SwingUtilities.invokeLater(new Runnable() {
424 } catch (IOException ee) {
425 final Exception e = ee;
426 SwingUtilities.invokeLater(new Runnable() {
432 if (programmer != null)
437 public flash_task(AltosFlashUI in_ui) {
439 t = new Thread(this);
447 class open_task implements Runnable {
452 public void do_exception(final Exception e) {
453 SwingUtilities.invokeLater(
456 try { dialog.open_exception(e); } catch (Exception ex) { }
461 public void do_success(final AltosLink link) {
462 SwingUtilities.invokeLater(
465 try { dialog.open_success(link); } catch (Exception ex) { }
470 public void do_failure() {
471 SwingUtilities.invokeLater(
474 try { dialog.open_failure(); } catch (Exception ex) { }
479 public void do_cancel() {
480 SwingUtilities.invokeLater(
483 try { dialog.open_cancel(); } catch (Exception ex) { }
490 AltosLink link = null;
491 boolean new_device = false;
494 System.out.printf("Attempting to open %s\n", device.toShortString());
496 for (int i = 0; i < 20; i++) {
497 link = new AltosSerial(device);
505 System.out.printf("Waiting for device to become ready\n");
510 throw new IOException(String.format("%s: open failed",
511 device.toShortString()));
513 /* See if the link is usable already */
514 if (is_pair_programmed() || link.is_loader()) {
515 System.out.printf("Device ready for use\n");
520 java.util.List<AltosDevice> prev_devices =
521 AltosUSBDevice.list(AltosLib.product_altusmetrum);
523 /* Nope, switch to loader and
524 * wait for it to re-appear
527 System.out.printf("Switch to loader\n");
531 /* This is a bit fragile, but
532 * I'm not sure what else to
533 * do other than ask the user.
535 * Search for a device which
536 * wasn't there before we
537 * asked the target to switch
544 java.util.List<AltosDevice> devices =
545 AltosUSBDevice.list(AltosLib.product_altusmetrum);
547 for (AltosDevice d : devices) {
548 boolean matched = false;
549 System.out.printf("\tfound device %s\n", d.toShortString());
550 for (AltosDevice p : prev_devices)
556 System.out.printf("Identified new device %s\n", d.toShortString());
557 device = (AltosUSBDevice) d;
566 } catch (AltosSerialInUseException ee) {
568 } catch (FileNotFoundException fe) {
570 } catch (IOException ie) {
572 } catch (InterruptedException ie) {
576 public void cancel() {
580 public open_task(AltosDevice device, open_dialog dialog) {
581 this.device = device;
582 this.dialog = dialog;
583 t = new Thread(this);
589 extends AltosUIDialog
590 implements ActionListener
594 private JLabel opening_label;
595 private JButton cancel_button;
597 boolean done = false;
599 AltosLink link = null;
601 open_task open = null;
603 public void open_exception(Exception e) {
604 System.out.printf("open_exception\n");
610 public void open_success(AltosLink link) {
611 System.out.printf("open_success\n");
617 public void open_failure() {
618 System.out.printf("open_failure\n");
623 public void open_cancel() {
624 System.out.printf("open_cancel\n");
629 public AltosLink do_open(open_task open) throws InterruptedException {
635 public void actionPerformed(ActionEvent e) {
636 String cmd = e.getActionCommand();
638 if (cmd.equals("cancel"))
645 public open_dialog(AltosUIFrame in_owner) {
646 super(in_owner, "Open Flash Target Device", true);
649 Container pane = getScrollablePane();
650 GridBagConstraints c = new GridBagConstraints();
651 Insets i = new Insets(4,4,4,4);
654 pane.setLayout(new GridBagLayout());
656 opening_label = new JLabel("Opening Device");
657 c.fill = GridBagConstraints.HORIZONTAL;
658 c.anchor = GridBagConstraints.LINE_START;
666 pane.add(opening_label, c);
668 cancel_button = new JButton("Cancel");
669 cancel_button.addActionListener(this);
670 cancel_button.setActionCommand("cancel");
673 pane.add(cancel_button, c);
675 setLocationRelativeTo(owner);
679 private boolean open_device() throws InterruptedException {
681 open_dialog dialog = new open_dialog(frame);
683 open_task open = new open_task(device, dialog);
685 link = dialog.do_open(open);
691 * Execute the steps for flashing
692 * a device. Note that this returns immediately;
693 * this dialog is not modal
696 if (!select_device())
698 if (!select_source_file())
703 } catch (InterruptedException ie) {
707 flash_task f = new flash_task(this);
710 public static void show(AltosUIFrame frame) {
711 AltosFlashUI ui = new AltosFlashUI(frame);
715 public AltosFlashUI(AltosUIFrame in_frame) {
716 super(in_frame, "Program Altusmetrum Device", false);