eb6ec5e7da1eb70133b9f8afbc5c1b162bc64ad0
[fw/altos] / altosui / AltosIdleMonitorUI.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 java.io.*;
26 import java.util.*;
27 import java.text.*;
28 import java.util.prefs.*;
29 import java.util.concurrent.*;
30 import org.altusmetrum.AltosLib.*;
31
32 class AltosADC {
33         int     tick;
34         int     accel;
35         int     pres;
36         int     temp;
37         int     batt;
38         int     drogue;
39         int     main;
40
41         public AltosADC(AltosSerial serial) throws InterruptedException, TimeoutException {
42                 serial.printf("a\n");
43                 for (;;) {
44                         String line = serial.get_reply_no_dialog(5000);
45                         if (line == null) {
46                                 throw new TimeoutException();
47                         }
48                         if (!line.startsWith("tick:"))
49                                 continue;
50                         String[] items = line.split("\\s+");
51                         for (int i = 0; i < items.length;) {
52                                 if (items[i].equals("tick:")) {
53                                         tick = Integer.parseInt(items[i+1]);
54                                         i += 2;
55                                         continue;
56                                 }
57                                 if (items[i].equals("accel:")) {
58                                         accel = Integer.parseInt(items[i+1]);
59                                         i += 2;
60                                         continue;
61                                 }
62                                 if (items[i].equals("pres:")) {
63                                         pres = Integer.parseInt(items[i+1]);
64                                         i += 2;
65                                         continue;
66                                 }
67                                 if (items[i].equals("temp:")) {
68                                         temp = Integer.parseInt(items[i+1]);
69                                         i += 2;
70                                         continue;
71                                 }
72                                 if (items[i].equals("batt:")) {
73                                         batt = Integer.parseInt(items[i+1]);
74                                         i += 2;
75                                         continue;
76                                 }
77                                 if (items[i].equals("drogue:")) {
78                                         drogue = Integer.parseInt(items[i+1]);
79                                         i += 2;
80                                         continue;
81                                 }
82                                 if (items[i].equals("main:")) {
83                                         main = Integer.parseInt(items[i+1]);
84                                         i += 2;
85                                         continue;
86                                 }
87                         }
88                         break;
89                 }
90         }
91 }
92
93 class AltosGPSQuery extends AltosGPS {
94         public AltosGPSQuery (AltosSerial serial, AltosConfigData config_data)
95                 throws TimeoutException, InterruptedException {
96                 boolean says_done = config_data.compare_version("1.0") >= 0;
97                 serial.printf("g\n");
98                 for (;;) {
99                         String line = serial.get_reply_no_dialog(5000);
100                         if (line == null)
101                                 throw new TimeoutException();
102                         String[] bits = line.split("\\s+");
103                         if (bits.length == 0)
104                                 continue;
105                         if (line.startsWith("Date:")) {
106                                 if (bits.length < 2)
107                                         continue;
108                                 String[] d = bits[1].split(":");
109                                 if (d.length < 3)
110                                         continue;
111                                 year = Integer.parseInt(d[0]) + 2000;
112                                 month = Integer.parseInt(d[1]);
113                                 day = Integer.parseInt(d[2]);
114                                 continue;
115                         }
116                         if (line.startsWith("Time:")) {
117                                 if (bits.length < 2)
118                                         continue;
119                                 String[] d = bits[1].split("/");
120                                 if (d.length < 3)
121                                         continue;
122                                 hour = Integer.parseInt(d[0]);
123                                 minute = Integer.parseInt(d[1]);
124                                 second = Integer.parseInt(d[2]);
125                                 continue;
126                         }
127                         if (line.startsWith("Lat/Lon:")) {
128                                 if (bits.length < 3)
129                                         continue;
130                                 lat = Integer.parseInt(bits[1]) * 1.0e-7;
131                                 lon = Integer.parseInt(bits[2]) * 1.0e-7;
132                                 continue;
133                         }
134                         if (line.startsWith("Alt:")) {
135                                 if (bits.length < 2)
136                                         continue;
137                                 alt = Integer.parseInt(bits[1]);
138                                 continue;
139                         }
140                         if (line.startsWith("Flags:")) {
141                                 if (bits.length < 2)
142                                         continue;
143                                 int status = Integer.decode(bits[1]);
144                                 connected = (status & Altos.AO_GPS_RUNNING) != 0;
145                                 locked = (status & Altos.AO_GPS_VALID) != 0;
146                                 if (!says_done)
147                                         break;
148                                 continue;
149                         }
150                         if (line.startsWith("Sats:")) {
151                                 if (bits.length < 2)
152                                         continue;
153                                 nsat = Integer.parseInt(bits[1]);
154                                 cc_gps_sat = new AltosGPSSat[nsat];
155                                 for (int i = 0; i < nsat; i++) {
156                                         int     svid = Integer.parseInt(bits[2+i*2]);
157                                         int     cc_n0 = Integer.parseInt(bits[3+i*2]);
158                                         cc_gps_sat[i] = new AltosGPSSat(svid, cc_n0);
159                                 }
160                         }
161                         if (line.startsWith("done"))
162                                 break;
163                         if (line.startsWith("Syntax error"))
164                                 break;
165                 }
166         }
167 }
168
169 class AltosIdleMonitor extends Thread {
170         AltosDevice             device;
171         AltosSerial             serial;
172         AltosIdleMonitorUI      ui;
173         AltosState              state;
174         boolean                 remote;
175         double                  frequency;
176         AltosState              previous_state;
177         AltosConfigData         config_data;
178         AltosADC                adc;
179         AltosGPS                gps;
180
181         int AltosRSSI() throws TimeoutException, InterruptedException {
182                 serial.printf("s\n");
183                 String line = serial.get_reply_no_dialog(5000);
184                 if (line == null)
185                         throw new TimeoutException();
186                 String[] items = line.split("\\s+");
187                 if (items.length < 2)
188                         return 0;
189                 if (!items[0].equals("RSSI:"))
190                         return 0;
191                 int rssi = Integer.parseInt(items[1]);
192                 return rssi;
193         }
194
195         void update_state() throws InterruptedException, TimeoutException {
196                 AltosRecordTM   record = new AltosRecordTM();
197                 int             rssi;
198
199                 try {
200                         if (remote) {
201                                 serial.set_radio_frequency(frequency);
202                                 serial.start_remote();
203                         } else
204                                 serial.flush_input();
205                         config_data = new AltosConfigData(serial);
206                         adc = new AltosADC(serial);
207                         gps = new AltosGPSQuery(serial, config_data);
208                 } finally {
209                         if (remote) {
210                                 serial.stop_remote();
211                                 rssi = AltosRSSI();
212                         } else
213                                 rssi = 0;
214                 }
215
216                 record.version = 0;
217                 record.callsign = config_data.callsign;
218                 record.serial = config_data.serial;
219                 record.flight = config_data.log_available() > 0 ? 255 : 0;
220                 record.rssi = rssi;
221                 record.status = 0;
222                 record.state = Altos.ao_flight_idle;
223
224                 record.tick = adc.tick;
225
226                 record.accel = adc.accel;
227                 record.pres = adc.pres;
228                 record.batt = adc.batt;
229                 record.temp = adc.temp;
230                 record.drogue = adc.drogue;
231                 record.main = adc.main;
232
233                 record.ground_accel = record.accel;
234                 record.ground_pres = record.pres;
235                 record.accel_plus_g = config_data.accel_cal_plus;
236                 record.accel_minus_g = config_data.accel_cal_minus;
237                 record.acceleration = 0;
238                 record.speed = 0;
239                 record.height = 0;
240                 record.gps = gps;
241                 state = new AltosState (record, state);
242         }
243
244         void set_frequency(double in_frequency) {
245                 frequency = in_frequency;
246         }
247
248         public void post_state() {
249                 Runnable r = new Runnable() {
250                                 public void run() {
251                                         ui.update(state);
252                                 }
253                         };
254                 SwingUtilities.invokeLater(r);
255         }
256
257         public void run() {
258                 try {
259                         for (;;) {
260                                 try {
261                                         update_state();
262                                         post_state();
263                                 } catch (TimeoutException te) {
264                                         if (AltosSerial.debug)
265                                                 System.out.printf ("monitor idle data timeout\n");
266                                 }
267                                 Thread.sleep(1000);
268                         }
269                 } catch (InterruptedException ie) {
270                         serial.close();
271                 }
272         }
273
274         public AltosIdleMonitor(AltosIdleMonitorUI in_ui, AltosDevice in_device, boolean in_remote)
275                 throws FileNotFoundException, AltosSerialInUseException, InterruptedException, TimeoutException {
276                 device = in_device;
277                 ui = in_ui;
278                 serial = new AltosSerial(device);
279                 remote = in_remote;
280                 state = null;
281         }
282 }
283
284 public class AltosIdleMonitorUI extends AltosFrame implements AltosFlightDisplay, AltosFontListener {
285         AltosDevice             device;
286         JTabbedPane             pane;
287         AltosPad                pad;
288         AltosInfoTable          flightInfo;
289         AltosFlightStatus       flightStatus;
290         AltosIdleMonitor        thread;
291         int                     serial;
292         boolean                 remote;
293
294         void stop_display() {
295                 if (thread != null && thread.isAlive()) {
296                         thread.interrupt();
297                         try {
298                                 thread.join();
299                         } catch (InterruptedException ie) {}
300                 }
301                 thread = null;
302         }
303
304         void disconnect() {
305                 stop_display();
306         }
307
308         public void reset() {
309                 pad.reset();
310                 flightInfo.clear();
311         }
312
313         public void set_font() {
314                 pad.set_font();
315                 flightInfo.set_font();
316         }
317
318         public void font_size_changed(int font_size) {
319                 set_font();
320         }
321
322         AltosFlightStatusUpdate status_update;
323
324         public void show(AltosState state, int crc_errors) {
325                 status_update.saved_state = state;
326                 try {
327                         pad.show(state, crc_errors);
328                         flightStatus.show(state, crc_errors);
329                         flightInfo.show(state, crc_errors);
330                 } catch (Exception e) {
331                         System.out.print("Show exception" + e);
332                 }
333         }
334
335         public void update(AltosState state) {
336                 show (state, 0);
337         }
338
339         Container       bag;
340         AltosFreqList   frequencies;
341
342         public AltosIdleMonitorUI(JFrame in_owner)
343                 throws FileNotFoundException, AltosSerialInUseException, TimeoutException, InterruptedException {
344
345                 device = AltosDeviceDialog.show(in_owner, Altos.product_any);
346                 remote = false;
347                 if (!device.matchProduct(Altos.product_altimeter))
348                         remote = true;
349
350                 serial = device.getSerial();
351                 bag = getContentPane();
352                 bag.setLayout(new GridBagLayout());
353
354                 GridBagConstraints c = new GridBagConstraints();
355
356                 java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg");
357                 if (imgURL != null)
358                         setIconImage(new ImageIcon(imgURL).getImage());
359
360                 setTitle(String.format("AltOS %s", device.toShortString()));
361
362                 /* Stick frequency selector at top of table for telemetry monitoring */
363                 if (remote && serial >= 0) {
364                         // Frequency menu
365                         frequencies = new AltosFreqList(AltosUIPreferences.frequency(serial));
366                         frequencies.addActionListener(new ActionListener() {
367                                         public void actionPerformed(ActionEvent e) {
368                                                 double frequency = frequencies.frequency();
369                                                 thread.set_frequency(frequency);
370                                                 AltosUIPreferences.set_frequency(device.getSerial(),
371                                                                                frequency);
372                                         }
373                         });
374                         c.gridx = 0;
375                         c.gridy = 0;
376                         c.insets = new Insets(3, 3, 3, 3);
377                         c.anchor = GridBagConstraints.WEST;
378                         bag.add (frequencies, c);
379                 }
380
381
382                 /* Flight status is always visible */
383                 flightStatus = new AltosFlightStatus();
384                 c.gridx = 0;
385                 c.gridy = 1;
386                 c.fill = GridBagConstraints.HORIZONTAL;
387                 c.weightx = 1;
388                 c.gridwidth = 2;
389                 bag.add(flightStatus, c);
390                 c.gridwidth = 1;
391
392                 /* The rest of the window uses a tabbed pane to
393                  * show one of the alternate data views
394                  */
395                 pane = new JTabbedPane();
396
397                 pad = new AltosPad();
398                 pane.add("Launch Pad", pad);
399
400                 flightInfo = new AltosInfoTable();
401                 pane.add("Table", new JScrollPane(flightInfo));
402
403                 /* Make the tabbed pane use the rest of the window space */
404                 c.gridx = 0;
405                 c.gridy = 2;
406                 c.fill = GridBagConstraints.BOTH;
407                 c.weightx = 1;
408                 c.weighty = 1;
409                 c.gridwidth = 2;
410                 bag.add(pane, c);
411
412                 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
413
414                 AltosUIPreferences.register_font_listener(this);
415
416                 addWindowListener(new WindowAdapter() {
417                                 @Override
418                                 public void windowClosing(WindowEvent e) {
419                                         disconnect();
420                                         setVisible(false);
421                                         dispose();
422                                         AltosUIPreferences.unregister_font_listener(AltosIdleMonitorUI.this);
423                                 }
424                         });
425
426                 pack();
427                 setVisible(true);
428
429                 thread = new AltosIdleMonitor(this, device, remote);
430
431                 status_update = new AltosFlightStatusUpdate(flightStatus);
432
433                 new javax.swing.Timer(100, status_update).start();
434
435                 thread.start();
436         }
437 }