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 static final String[] log_erased_devices = {
87 private boolean is_pair_programmed() {
90 String name = file.getName();
91 for (int i = 0; i < pair_programmed_files.length; i++) {
92 if (name.startsWith(pair_programmed_files[i]))
97 String name = device.toString();
98 for (int i = 0; i < pair_programmed_devices.length; i++) {
99 if (name.startsWith(pair_programmed_devices[i]))
106 private boolean is_log_erased() {
107 if (device != null) {
108 String name = device.toString();
109 for (int i = 0; i < log_erased_devices.length; i++) {
110 if (name.startsWith(log_erased_devices[i]))
117 public void actionPerformed(ActionEvent e) {
118 if (e.getSource() == cancel) {
119 if (programmer != null)
124 String cmd = e.getActionCommand();
125 if (e.getID() == -1) {
126 JOptionPane.showMessageDialog(frame,
127 e.getActionCommand(),
129 JOptionPane.ERROR_MESSAGE);
132 } else if (cmd.equals(AltosFlashListener.flash_done)) {
135 } else if (cmd.equals(AltosFlashListener.flash_start)) {
138 pbar.setValue(e.getID());
144 public void build_dialog() {
145 GridBagConstraints c;
146 Insets il = new Insets(4,4,4,4);
147 Insets ir = new Insets(4,4,4,4);
149 pane = getScrollablePane();
150 pane.setLayout(new GridBagLayout());
152 c = new GridBagConstraints();
153 c.gridx = 0; c.gridy = 0;
154 c.fill = GridBagConstraints.NONE;
155 c.anchor = GridBagConstraints.LINE_START;
157 serial_label = new JLabel("Serial:");
158 pane.add(serial_label, c);
160 c = new GridBagConstraints();
161 c.gridx = 1; c.gridy = 0;
162 c.fill = GridBagConstraints.HORIZONTAL;
164 c.anchor = GridBagConstraints.LINE_START;
166 serial_value = new JLabel("");
167 pane.add(serial_value, c);
169 c = new GridBagConstraints();
170 c.fill = GridBagConstraints.NONE;
171 c.gridx = 0; c.gridy = 1;
172 c.anchor = GridBagConstraints.LINE_START;
174 file_label = new JLabel("File:");
175 pane.add(file_label, c);
177 c = new GridBagConstraints();
178 c.fill = GridBagConstraints.HORIZONTAL;
180 c.gridx = 1; c.gridy = 1;
181 c.anchor = GridBagConstraints.LINE_START;
183 file_value = new JLabel(file.toString());
184 pane.add(file_value, c);
186 pbar = new JProgressBar();
188 pbar.setMaximum(100);
191 pbar.setStringPainted(true);
192 pbar.setPreferredSize(new Dimension(600, 20));
193 c = new GridBagConstraints();
194 c.fill = GridBagConstraints.HORIZONTAL;
195 c.anchor = GridBagConstraints.CENTER;
196 c.gridx = 0; c.gridy = 2;
197 c.gridwidth = GridBagConstraints.REMAINDER;
198 Insets ib = new Insets(4,4,4,4);
202 cancel = new JButton("Cancel");
203 c = new GridBagConstraints();
204 c.fill = GridBagConstraints.NONE;
205 c.anchor = GridBagConstraints.CENTER;
206 c.gridx = 0; c.gridy = 3;
207 c.gridwidth = GridBagConstraints.REMAINDER;
208 Insets ic = new Insets(4,4,4,4);
211 cancel.addActionListener(this);
213 setLocationRelativeTo(frame);
216 void set_serial(int serial_number) {
217 serial_value.setText(String.format("%d", serial_number));
220 static class AltosHexfileFilter extends javax.swing.filechooser.FileFilter {
224 public AltosHexfileFilter(String usb_product) {
228 /* Trim off any trailing variants (1.0a vs 1.0) */
229 for (dash = usb_product.length(); dash > 0; dash--) {
230 char c = usb_product.charAt(dash-1);
235 dash = usb_product.length();
237 for (l = usb_product.length(); l > dash; l--) {
238 char c = usb_product.charAt(l-1);
239 if (c < 'a' || 'z' < c)
242 head = usb_product.substring(0, l).toLowerCase();
243 description = String.format("%s Image File", usb_product);
246 public boolean accept(File file) {
247 return !file.isFile() || (file.getName().startsWith(head) && file.getName().endsWith(".ihx"));
250 public String getDescription() {
255 boolean select_source_file() {
256 JFileChooser hexfile_chooser = new JFileChooser();
258 File firmwaredir = AltosUIPreferences.firmwaredir();
259 if (firmwaredir != null)
260 hexfile_chooser.setCurrentDirectory(firmwaredir);
262 hexfile_chooser.setDialogTitle("Select Flash Image");
264 javax.swing.filechooser.FileFilter ihx_filter = new FileNameExtensionFilter("Flash Image", "ihx");
265 hexfile_chooser.addChoosableFileFilter(ihx_filter);
266 hexfile_chooser.setFileFilter(ihx_filter);
268 if (!is_pair_programmed() && !device.matchProduct(AltosLib.product_altusmetrum)) {
269 AltosHexfileFilter filter = new AltosHexfileFilter(device.usb_product());
270 hexfile_chooser.addChoosableFileFilter(filter);
271 hexfile_chooser.setFileFilter(filter);
274 int returnVal = hexfile_chooser.showOpenDialog(frame);
276 if (returnVal != JFileChooser.APPROVE_OPTION)
278 file = hexfile_chooser.getSelectedFile();
281 AltosUIPreferences.set_firmwaredir(file.getParentFile());
286 boolean select_device() {
287 int product = AltosLib.product_any;
289 device = AltosDeviceUIDialog.show_usb(frame, AltosLib.product_any);
296 boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
297 if (a == null || b == null)
298 return (a == null && b == null);
300 if (!a.valid || !b.valid)
303 if (a.usb_id != null && b.usb_id != null &&
304 (a.usb_id.vid != b.usb_id.vid ||
305 a.usb_id.pid != b.usb_id.pid))
308 if (a.usb_product != null && b.usb_product != null &&
309 !a.usb_product.equals(b.usb_product))
315 boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
316 AltosRomconfig new_config;
318 if (!rom_config_matches(existing_config, image_config)) {
320 if (existing_config == null || !existing_config.valid) {
321 ret = JOptionPane.showConfirmDialog(this,
322 String.format("Cannot determine target device type\nImage is %04x:%04x %s\nFlash anyways?",
323 image_config.usb_id.vid,
324 image_config.usb_id.pid,
325 image_config.usb_product),
326 "Unknown Target Device",
327 JOptionPane.YES_NO_OPTION);
329 ret = JOptionPane.showConfirmDialog(this,
330 String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
331 existing_config.usb_id.vid,
332 existing_config.usb_id.pid,
333 existing_config.usb_product,
334 image_config.usb_id.vid,
335 image_config.usb_id.pid,
336 image_config.usb_product),
337 "Image doesn't match Device",
338 JOptionPane.YES_NO_OPTION);
340 if (ret != JOptionPane.YES_OPTION)
344 if (existing_config != null && existing_config.radio_calibration_broken) {
345 int ret = JOptionPane.showConfirmDialog(this,
346 String.format("Radio calibration value %d may be incorrect\nFlash anyways?",
347 existing_config.radio_calibration),
348 "Radio Calibration Invalid",
349 JOptionPane.YES_NO_OPTION);
350 if (ret != JOptionPane.YES_OPTION)
355 new_config = AltosRomconfigUI.show(frame, existing_config);
356 if (new_config == null)
358 rom_config = new_config;
359 set_serial(rom_config.serial_number);
364 void exception (Exception e) {
365 if (e instanceof FileNotFoundException) {
366 JOptionPane.showMessageDialog(frame,
367 ((FileNotFoundException) e).getMessage(),
369 JOptionPane.ERROR_MESSAGE);
370 } else if (e instanceof AltosSerialInUseException) {
371 JOptionPane.showMessageDialog(frame,
372 String.format("Device \"%s\" already in use",
373 device.toShortString()),
375 JOptionPane.ERROR_MESSAGE);
377 JOptionPane.showMessageDialog(frame,
380 JOptionPane.ERROR_MESSAGE);
384 class flash_task implements Runnable, AltosFlashListener {
387 AltosProgrammer programmer;
389 public void position(String in_s, int in_percent) {
390 final String s = in_s;
391 final int percent = in_percent;
392 Runnable r = new Runnable() {
395 ui.actionPerformed(new ActionEvent(this,
398 } catch (Exception ex) {
402 SwingUtilities.invokeLater(r);
407 if (ui.is_pair_programmed())
408 programmer = new AltosFlash(ui.file, link, this);
410 programmer = new AltosSelfFlash(ui.file, link, this);
412 final AltosRomconfig current_config = programmer.target_romconfig(device.usb_id(), device.usb_product());
414 final AltosRomconfig image_config = programmer.image_romconfig();
416 System.out.printf("product %s current %s image %s\n", device.usb_product(), current_config, image_config);
418 final Semaphore await_rom_config = new Semaphore(0);
419 SwingUtilities.invokeLater(new Runnable() {
421 ui.programmer = programmer;
422 ui.update_rom_config_info(current_config, image_config);
423 await_rom_config.release();
426 await_rom_config.acquire();
428 if (ui.rom_config != null) {
429 programmer.set_romconfig(ui.rom_config);
432 } catch (InterruptedException ee) {
433 final Exception e = ee;
434 SwingUtilities.invokeLater(new Runnable() {
439 } catch (IOException ee) {
440 final Exception e = ee;
441 SwingUtilities.invokeLater(new Runnable() {
447 if (programmer != null)
452 public flash_task(AltosFlashUI in_ui) {
454 t = new Thread(this);
461 boolean erase_answer;
463 class open_task implements Runnable {
469 public void do_exception(final Exception e) {
473 } catch (Exception ex) {}
475 SwingUtilities.invokeLater(
478 try { dialog.open_exception(e); } catch (Exception ex) { }
483 public void do_success(final AltosLink link) {
484 SwingUtilities.invokeLater(
487 try { dialog.open_success(link); } catch (Exception ex) { }
492 public boolean do_notify_erase(final AltosConfigData config_data) {
493 erase_answer = false;
494 final Semaphore erase_answer_done = new Semaphore(0);
495 SwingUtilities.invokeLater(
498 int ret = JOptionPane.showConfirmDialog(dialog.owner,
499 String.format("Updating %s from firmware %s will erase stored data, continue?",
501 config_data.version),
502 "Erase Stored Data?",
503 JOptionPane.YES_NO_OPTION);
504 erase_answer = ret == JOptionPane.YES_OPTION;
505 erase_answer_done.release();
509 erase_answer_done.acquire();
510 } catch (Exception ex) {
519 boolean new_device = false;
522 System.out.printf("Attempting to open %s\n", device.toShortString());
524 for (int i = 0; i < 20; i++) {
525 link = new AltosSerial(device);
533 System.out.printf("Waiting for device to become ready\n");
538 throw new IOException(String.format("%s: open failed",
539 device.toShortString()));
541 System.out.printf("Checking device ready\n");
543 /* See if the link is usable already */
544 if (is_pair_programmed() || link.is_loader()) {
545 System.out.printf("Device ready for use\n");
550 System.out.printf("Checking log erased\n");
552 if (is_log_erased()) {
553 System.out.printf("Fetching config data\n");
554 AltosConfigData config_data = link.config_data();
555 System.out.printf("version %s\n", config_data.version);
556 /* Completely erase TeleGPS flash when firmware is old */
557 if (config_data.compare_version("1.9.7") < 0)
559 if (!do_notify_erase(config_data))
560 throw new IOException(String.format("%s: not erasing log",
561 device.toShortString()));
562 System.out.printf("Erasing log\n");
563 link.printf("Z DoIt\n");
564 link.synchronize(120 * 1000);
568 java.util.List<AltosDevice> prev_devices =
569 AltosUSBDevice.list(AltosLib.product_altusmetrum);
571 /* Nope, switch to loader and
572 * wait for it to re-appear
575 System.out.printf("Switch to loader\n");
579 /* This is a bit fragile, but
580 * I'm not sure what else to
581 * do other than ask the user.
583 * Search for a device which
584 * wasn't there before we
585 * asked the target to switch
592 java.util.List<AltosDevice> devices =
593 AltosUSBDevice.list(AltosLib.product_altusmetrum);
595 for (AltosDevice d : devices) {
596 boolean matched = false;
597 System.out.printf("\tfound device %s\n", d.toShortString());
598 for (AltosDevice p : prev_devices)
604 System.out.printf("Identified new device %s\n", d.toShortString());
605 device = (AltosUSBDevice) d;
614 } catch (AltosSerialInUseException ee) {
616 } catch (FileNotFoundException fe) {
618 } catch (IOException ie) {
620 } catch (TimeoutException te) {
622 } catch (InterruptedException ie) {
627 public void cancel() {
631 public open_task(AltosDevice device, open_dialog dialog) {
632 this.device = device;
633 this.dialog = dialog;
634 t = new Thread(this);
640 extends AltosUIDialog
641 implements ActionListener
645 private JLabel opening_label;
646 private JButton cancel_button;
648 boolean done = false;
650 AltosLink link = null;
652 open_task open = null;
654 public void open_exception(Exception e) {
655 System.out.printf("open_exception\n");
661 public void open_success(AltosLink link) {
662 System.out.printf("open_success\n");
668 public AltosLink do_open(open_task open) throws InterruptedException {
674 public void actionPerformed(ActionEvent e) {
675 String cmd = e.getActionCommand();
677 if (cmd.equals("cancel"))
684 public open_dialog(AltosUIFrame in_owner) {
685 super(in_owner, "Open Flash Target Device", true);
688 Container pane = getScrollablePane();
689 GridBagConstraints c = new GridBagConstraints();
690 Insets i = new Insets(4,4,4,4);
693 pane.setLayout(new GridBagLayout());
695 opening_label = new JLabel("Opening Device");
696 c.fill = GridBagConstraints.HORIZONTAL;
697 c.anchor = GridBagConstraints.LINE_START;
705 pane.add(opening_label, c);
707 cancel_button = new JButton("Cancel");
708 cancel_button.addActionListener(this);
709 cancel_button.setActionCommand("cancel");
712 pane.add(cancel_button, c);
714 setLocationRelativeTo(owner);
718 private boolean open_device() throws InterruptedException {
720 open_dialog dialog = new open_dialog(frame);
722 open_task open = new open_task(device, dialog);
724 link = dialog.do_open(open);
730 * Execute the steps for flashing
731 * a device. Note that this returns immediately;
732 * this dialog is not modal
735 if (!select_device())
737 if (!select_source_file())
742 } catch (InterruptedException ie) {
746 flash_task f = new flash_task(this);
749 public static void show(AltosUIFrame frame) {
750 AltosFlashUI ui = new AltosFlashUI(frame);
754 public AltosFlashUI(AltosUIFrame in_frame) {
755 super(in_frame, "Program Altusmetrum Device", false);