altosui: Add support for telemetry version 4
[fw/altos] / altosui / AltosConfigUI.java
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package altosui;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import javax.swing.*;
23 import javax.swing.filechooser.FileNameExtensionFilter;
24 import javax.swing.table.*;
25 import javax.swing.event.*;
26 import java.io.*;
27 import java.util.*;
28 import java.text.*;
29 import java.util.prefs.*;
30 import java.util.concurrent.LinkedBlockingQueue;
31
32 import libaltosJNI.*;
33
34 public class AltosConfigUI
35         extends JDialog
36         implements ActionListener, ItemListener, DocumentListener
37 {
38
39         Container       pane;
40         Box             box;
41         JLabel          product_label;
42         JLabel          version_label;
43         JLabel          serial_label;
44         JLabel          main_deploy_label;
45         JLabel          apogee_delay_label;
46         JLabel          radio_channel_label;
47         JLabel          radio_calibration_label;
48         JLabel          flight_log_max_label;
49         JLabel          callsign_label;
50
51         public boolean          dirty;
52
53         JFrame          owner;
54         JLabel          product_value;
55         JLabel          version_value;
56         JLabel          serial_value;
57         JComboBox       main_deploy_value;
58         JComboBox       apogee_delay_value;
59         JComboBox       radio_channel_value;
60         JTextField      radio_calibration_value;
61         JComboBox       flight_log_max_value;
62         JTextField      callsign_value;
63
64         JButton         save;
65         JButton         reset;
66         JButton         reboot;
67         JButton         close;
68
69         ActionListener  listener;
70
71         static String[] main_deploy_values = {
72                 "100", "150", "200", "250", "300", "350",
73                 "400", "450", "500"
74         };
75
76         static String[] apogee_delay_values = {
77                 "0", "1", "2", "3", "4", "5"
78         };
79
80         static String[] flight_log_max_values = {
81                 "64", "128", "192", "256", "320",
82                 "384", "448", "512", "576", "640",
83                 "704", "768", "832", "896", "960",
84         };
85
86         static String[] radio_channel_values = new String[10];
87                 {
88                         for (int i = 0; i <= 9; i++)
89                                 radio_channel_values[i] = String.format("Channel %1d (%7.3fMHz)",
90                                                                         i, 434.550 + i * 0.1);
91                 }
92
93         /* A window listener to catch closing events and tell the config code */
94         class ConfigListener extends WindowAdapter {
95                 AltosConfigUI   ui;
96
97                 public ConfigListener(AltosConfigUI this_ui) {
98                         ui = this_ui;
99                 }
100
101                 public void windowClosing(WindowEvent e) {
102                         ui.actionPerformed(new ActionEvent(e.getSource(),
103                                                            ActionEvent.ACTION_PERFORMED,
104                                                            "Close"));
105                 }
106         }
107
108         /* Build the UI using a grid bag */
109         public AltosConfigUI(JFrame in_owner, boolean remote) {
110                 super (in_owner, "Configure TeleMetrum", false);
111
112                 owner = in_owner;
113                 GridBagConstraints c;
114
115                 Insets il = new Insets(4,4,4,4);
116                 Insets ir = new Insets(4,4,4,4);
117
118                 pane = getContentPane();
119                 pane.setLayout(new GridBagLayout());
120
121                 /* Product */
122                 c = new GridBagConstraints();
123                 c.gridx = 0; c.gridy = 0;
124                 c.gridwidth = 4;
125                 c.fill = GridBagConstraints.NONE;
126                 c.anchor = GridBagConstraints.LINE_START;
127                 c.insets = il;
128                 product_label = new JLabel("Product:");
129                 pane.add(product_label, c);
130
131                 c = new GridBagConstraints();
132                 c.gridx = 4; c.gridy = 0;
133                 c.gridwidth = 4;
134                 c.fill = GridBagConstraints.HORIZONTAL;
135                 c.weightx = 1;
136                 c.anchor = GridBagConstraints.LINE_START;
137                 c.insets = ir;
138                 product_value = new JLabel("");
139                 pane.add(product_value, c);
140
141                 /* Version */
142                 c = new GridBagConstraints();
143                 c.gridx = 0; c.gridy = 1;
144                 c.gridwidth = 4;
145                 c.fill = GridBagConstraints.NONE;
146                 c.anchor = GridBagConstraints.LINE_START;
147                 c.insets = il;
148                 c.ipady = 5;
149                 version_label = new JLabel("Software version:");
150                 pane.add(version_label, c);
151
152                 c = new GridBagConstraints();
153                 c.gridx = 4; c.gridy = 1;
154                 c.gridwidth = 4;
155                 c.fill = GridBagConstraints.HORIZONTAL;
156                 c.weightx = 1;
157                 c.anchor = GridBagConstraints.LINE_START;
158                 c.insets = ir;
159                 c.ipady = 5;
160                 version_value = new JLabel("");
161                 pane.add(version_value, c);
162
163                 /* Serial */
164                 c = new GridBagConstraints();
165                 c.gridx = 0; c.gridy = 2;
166                 c.gridwidth = 4;
167                 c.fill = GridBagConstraints.NONE;
168                 c.anchor = GridBagConstraints.LINE_START;
169                 c.insets = il;
170                 c.ipady = 5;
171                 serial_label = new JLabel("Serial:");
172                 pane.add(serial_label, c);
173
174                 c = new GridBagConstraints();
175                 c.gridx = 4; c.gridy = 2;
176                 c.gridwidth = 4;
177                 c.fill = GridBagConstraints.HORIZONTAL;
178                 c.weightx = 1;
179                 c.anchor = GridBagConstraints.LINE_START;
180                 c.insets = ir;
181                 c.ipady = 5;
182                 serial_value = new JLabel("");
183                 pane.add(serial_value, c);
184
185                 /* Main deploy */
186                 c = new GridBagConstraints();
187                 c.gridx = 0; c.gridy = 3;
188                 c.gridwidth = 4;
189                 c.fill = GridBagConstraints.NONE;
190                 c.anchor = GridBagConstraints.LINE_START;
191                 c.insets = il;
192                 c.ipady = 5;
193                 main_deploy_label = new JLabel("Main Deploy Altitude(m):");
194                 pane.add(main_deploy_label, c);
195
196                 c = new GridBagConstraints();
197                 c.gridx = 4; c.gridy = 3;
198                 c.gridwidth = 4;
199                 c.fill = GridBagConstraints.HORIZONTAL;
200                 c.weightx = 1;
201                 c.anchor = GridBagConstraints.LINE_START;
202                 c.insets = ir;
203                 c.ipady = 5;
204                 main_deploy_value = new JComboBox(main_deploy_values);
205                 main_deploy_value.setEditable(true);
206                 main_deploy_value.addItemListener(this);
207                 pane.add(main_deploy_value, c);
208
209                 /* Apogee delay */
210                 c = new GridBagConstraints();
211                 c.gridx = 0; c.gridy = 4;
212                 c.gridwidth = 4;
213                 c.fill = GridBagConstraints.NONE;
214                 c.anchor = GridBagConstraints.LINE_START;
215                 c.insets = il;
216                 c.ipady = 5;
217                 apogee_delay_label = new JLabel("Apogee Delay(s):");
218                 pane.add(apogee_delay_label, c);
219
220                 c = new GridBagConstraints();
221                 c.gridx = 4; c.gridy = 4;
222                 c.gridwidth = 4;
223                 c.fill = GridBagConstraints.HORIZONTAL;
224                 c.weightx = 1;
225                 c.anchor = GridBagConstraints.LINE_START;
226                 c.insets = ir;
227                 c.ipady = 5;
228                 apogee_delay_value = new JComboBox(apogee_delay_values);
229                 apogee_delay_value.setEditable(true);
230                 apogee_delay_value.addItemListener(this);
231                 pane.add(apogee_delay_value, c);
232
233                 /* Radio channel */
234                 c = new GridBagConstraints();
235                 c.gridx = 0; c.gridy = 5;
236                 c.gridwidth = 4;
237                 c.fill = GridBagConstraints.NONE;
238                 c.anchor = GridBagConstraints.LINE_START;
239                 c.insets = il;
240                 c.ipady = 5;
241                 radio_channel_label = new JLabel("Radio Channel:");
242                 pane.add(radio_channel_label, c);
243
244                 c = new GridBagConstraints();
245                 c.gridx = 4; c.gridy = 5;
246                 c.gridwidth = 4;
247                 c.fill = GridBagConstraints.HORIZONTAL;
248                 c.weightx = 1;
249                 c.anchor = GridBagConstraints.LINE_START;
250                 c.insets = ir;
251                 c.ipady = 5;
252                 radio_channel_value = new JComboBox(radio_channel_values);
253                 radio_channel_value.setEditable(false);
254                 radio_channel_value.addItemListener(this);
255                 if (remote)
256                         radio_channel_value.setEnabled(false);
257                 pane.add(radio_channel_value, c);
258
259                 /* Radio Calibration */
260                 c = new GridBagConstraints();
261                 c.gridx = 0; c.gridy = 6;
262                 c.gridwidth = 4;
263                 c.fill = GridBagConstraints.NONE;
264                 c.anchor = GridBagConstraints.LINE_START;
265                 c.insets = il;
266                 c.ipady = 5;
267                 radio_calibration_label = new JLabel("RF Calibration:");
268                 pane.add(radio_calibration_label, c);
269
270                 c = new GridBagConstraints();
271                 c.gridx = 4; c.gridy = 6;
272                 c.gridwidth = 4;
273                 c.fill = GridBagConstraints.HORIZONTAL;
274                 c.weightx = 1;
275                 c.anchor = GridBagConstraints.LINE_START;
276                 c.insets = ir;
277                 c.ipady = 5;
278                 radio_calibration_value = new JTextField(String.format("%d", 1186611));
279                 radio_calibration_value.getDocument().addDocumentListener(this);
280                 if (remote)
281                         radio_calibration_value.setEnabled(false);
282                 pane.add(radio_calibration_value, c);
283
284                 /* Callsign */
285                 c = new GridBagConstraints();
286                 c.gridx = 0; c.gridy = 7;
287                 c.gridwidth = 4;
288                 c.fill = GridBagConstraints.NONE;
289                 c.anchor = GridBagConstraints.LINE_START;
290                 c.insets = il;
291                 c.ipady = 5;
292                 callsign_label = new JLabel("Callsign:");
293                 pane.add(callsign_label, c);
294
295                 c = new GridBagConstraints();
296                 c.gridx = 4; c.gridy = 7;
297                 c.gridwidth = 4;
298                 c.fill = GridBagConstraints.HORIZONTAL;
299                 c.weightx = 1;
300                 c.anchor = GridBagConstraints.LINE_START;
301                 c.insets = ir;
302                 c.ipady = 5;
303                 callsign_value = new JTextField(AltosPreferences.callsign());
304                 callsign_value.getDocument().addDocumentListener(this);
305                 pane.add(callsign_value, c);
306
307                 /* Flight log max */
308                 c = new GridBagConstraints();
309                 c.gridx = 0; c.gridy = 8;
310                 c.gridwidth = 4;
311                 c.fill = GridBagConstraints.NONE;
312                 c.anchor = GridBagConstraints.LINE_START;
313                 c.insets = il;
314                 c.ipady = 5;
315                 flight_log_max_label = new JLabel("Maximum Flight Log Size:");
316                 pane.add(flight_log_max_label, c);
317
318                 c = new GridBagConstraints();
319                 c.gridx = 4; c.gridy = 8;
320                 c.gridwidth = 4;
321                 c.fill = GridBagConstraints.HORIZONTAL;
322                 c.weightx = 1;
323                 c.anchor = GridBagConstraints.LINE_START;
324                 c.insets = ir;
325                 c.ipady = 5;
326                 flight_log_max_value = new JComboBox(flight_log_max_values);
327                 flight_log_max_value.setEditable(true);
328                 flight_log_max_value.addItemListener(this);
329                 pane.add(flight_log_max_value, c);
330
331                 /* Buttons */
332                 c = new GridBagConstraints();
333                 c.gridx = 0; c.gridy = 9;
334                 c.gridwidth = 2;
335                 c.fill = GridBagConstraints.NONE;
336                 c.anchor = GridBagConstraints.LINE_START;
337                 c.insets = il;
338                 save = new JButton("Save");
339                 pane.add(save, c);
340                 save.addActionListener(this);
341                 save.setActionCommand("Save");
342
343                 c = new GridBagConstraints();
344                 c.gridx = 2; c.gridy = 9;
345                 c.gridwidth = 2;
346                 c.fill = GridBagConstraints.NONE;
347                 c.anchor = GridBagConstraints.CENTER;
348                 c.insets = il;
349                 reset = new JButton("Reset");
350                 pane.add(reset, c);
351                 reset.addActionListener(this);
352                 reset.setActionCommand("Reset");
353
354                 c = new GridBagConstraints();
355                 c.gridx = 4; c.gridy = 9;
356                 c.gridwidth = 2;
357                 c.fill = GridBagConstraints.NONE;
358                 c.anchor = GridBagConstraints.CENTER;
359                 c.insets = il;
360                 reboot = new JButton("Reboot");
361                 pane.add(reboot, c);
362                 reboot.addActionListener(this);
363                 reboot.setActionCommand("Reboot");
364
365                 c = new GridBagConstraints();
366                 c.gridx = 6; c.gridy = 9;
367                 c.gridwidth = 2;
368                 c.fill = GridBagConstraints.NONE;
369                 c.anchor = GridBagConstraints.LINE_END;
370                 c.insets = il;
371                 close = new JButton("Close");
372                 pane.add(close, c);
373                 close.addActionListener(this);
374                 close.setActionCommand("Close");
375
376                 addWindowListener(new ConfigListener(this));
377         }
378
379         /* Once the initial values are set, the config code will show the dialog */
380         public void make_visible() {
381                 pack();
382                 setLocationRelativeTo(owner);
383                 setVisible(true);
384         }
385
386         /* If any values have been changed, confirm before closing */
387         public boolean check_dirty(String operation) {
388                 if (dirty) {
389                         Object[] options = { String.format("%s anyway", operation), "Keep editing" };
390                         int i;
391                         i = JOptionPane.showOptionDialog(this,
392                                                          String.format("Configuration modified. %s anyway?", operation),
393                                                          "Configuration Modified",
394                                                          JOptionPane.DEFAULT_OPTION,
395                                                          JOptionPane.WARNING_MESSAGE,
396                                                          null, options, options[1]);
397                         if (i != 0)
398                                 return false;
399                 }
400                 return true;
401         }
402
403         /* Listen for events from our buttons */
404         public void actionPerformed(ActionEvent e) {
405                 String  cmd = e.getActionCommand();
406
407                 if (cmd.equals("Close") || cmd.equals("Reboot"))
408                         if (!check_dirty(cmd))
409                                 return;
410                 listener.actionPerformed(e);
411                 if (cmd.equals("Close") || cmd.equals("Reboot")) {
412                         setVisible(false);
413                         dispose();
414                 }
415                 dirty = false;
416         }
417
418         /* ItemListener interface method */
419         public void itemStateChanged(ItemEvent e) {
420                 dirty = true;
421         }
422
423         /* DocumentListener interface methods */
424         public void changedUpdate(DocumentEvent e) {
425                 dirty = true;
426         }
427
428         public void insertUpdate(DocumentEvent e) {
429                 dirty = true;
430         }
431
432         public void removeUpdate(DocumentEvent e) {
433                 dirty = true;
434         }
435
436         /* Let the config code hook on a listener */
437         public void addActionListener(ActionListener l) {
438                 listener = l;
439         }
440
441         /* set and get all of the dialog values */
442         public void set_product(String product) {
443                 product_value.setText(product);
444         }
445
446         public void set_version(String version) {
447                 version_value.setText(version);
448         }
449
450         public void set_serial(int serial) {
451                 serial_value.setText(String.format("%d", serial));
452         }
453
454         public void set_main_deploy(int new_main_deploy) {
455                 main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy));
456         }
457
458         public int main_deploy() {
459                 return Integer.parseInt(main_deploy_value.getSelectedItem().toString());
460         }
461
462         public void set_apogee_delay(int new_apogee_delay) {
463                 apogee_delay_value.setSelectedItem(Integer.toString(new_apogee_delay));
464         }
465
466         public int apogee_delay() {
467                 return Integer.parseInt(apogee_delay_value.getSelectedItem().toString());
468         }
469
470         public void set_radio_channel(int new_radio_channel) {
471                 radio_channel_value.setSelectedIndex(new_radio_channel);
472         }
473
474         public int radio_channel() {
475                 return radio_channel_value.getSelectedIndex();
476         }
477
478         public void set_radio_calibration(int new_radio_calibration) {
479                 radio_calibration_value.setText(String.format("%d", new_radio_calibration));
480         }
481
482         public int radio_calibration() {
483                 return Integer.parseInt(radio_calibration_value.getText());
484         }
485
486         public void set_callsign(String new_callsign) {
487                 callsign_value.setText(new_callsign);
488         }
489
490         public String callsign() {
491                 return callsign_value.getText();
492         }
493
494         public void set_flight_log_max(int new_flight_log_max) {
495                 if (new_flight_log_max == 0)
496                         flight_log_max_value.setEnabled(false);
497                 flight_log_max_value.setSelectedItem(Integer.toString(new_flight_log_max));
498         }
499
500         public int flight_log_max() {
501                 return Integer.parseInt(flight_log_max_value.getSelectedItem().toString());
502         }
503
504         public void set_clean() {
505                 dirty = false;
506         }
507 }