I have to build a GPS parser. I need to parse the NMEA string in another thread, which will be parsing a single NMEA string and update chart at 1 Hz. For now I build part of my code, but I parse data in main thread in while loop; my teacher said that is wrong. I was programming some on Java but not in multi-threading aspects. How I could move parsing process and refreshing chart to background thread?
public class MainFrame extends JFrame {
private JButton btnWybPlik;
private JLabel jlDroga;
private JLabel jlPredkosc;
private JLabel jlCzas;
private JPanel mainjpanel;
private JPanel jpMenu;
private JPanel jpTablica;
//private String sciezkaPliku;
private SekwencjaGGA sekGGA = null;
private SekwencjaGGA popSekGGA = null;
private SekwencjaGSA sekGSA;
private SekwencjaGLL sekGLL;
private SekwencjaRMC sekRMC;
private double droga;
private double predkosc;
private XYSeries series1;
private XYSeriesCollection dataset;
public MainFrame() {
droga = 0;
btnWybPlik.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
int result = fileChooser.showOpenDialog(mainjpanel);
if (result == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
//System.out.println("Selected file: " + selectedFile.getAbsolutePath());
String sciezkaPliku = selectedFile.getAbsolutePath();
wczytaniePliku(sciezkaPliku);
}
}
});
jpTablica = new JPanel();
mainjpanel.add(jpTablica);
this.series1 = new XYSeries("Trasa", false);
final XYSeriesCollection dataset = new XYSeriesCollection(this.series1);
final JFreeChart chart = createChart(dataset);
final ChartPanel chartPanel = new ChartPanel(chart);
jpTablica.add(chartPanel);
}
private void wczytaniePliku(String sciezkaDoPliku) {
try (BufferedReader br = new BufferedReader(new FileReader(sciezkaDoPliku))) {
String line;
//series1.add(53.448, 14.4907);
while ((line = br.readLine()) != null) {
parseLine(line);
}
//series1.add(53.4485, 14.4910);
} catch (IOException e) {
e.printStackTrace();
}
}
private void parseLine(String line) {
String bezSumKont = line.substring(0, line.length() - 3);
List<String> podzSekw = Arrays.asList(bezSumKont.split(","));
if (podzSekw.get(0).equalsIgnoreCase("$GPGGA")) {
if (check(line)) {
if (sekGGA != null)
popSekGGA = sekGGA;
sekGGA = new SekwencjaGGA(podzSekw);
if (popSekGGA != null) {
droga += obliczOdleglosc(popSekGGA, sekGGA);
jlDroga.setText(String.valueOf(droga));
}
series1.add(sekGGA.getWspolzedne().getLongitude(), sekGGA.getWspolzedne().getLatitude());
System.out.println(sekGGA.getWspolzedne().getLatitude() + " " + sekGGA.getWspolzedne().getLongitude());
//System.out.println(series1.getMaxY() + " " + series1.getMinY());
} else {
//TODO: Zlicz błąd
}
}
if (podzSekw.get(0).equalsIgnoreCase("$GPGSA")) {
if (check(line)) {
sekGSA = new SekwencjaGSA(podzSekw);
} else {
//TODO: Zlicz błąd
}
}
if (podzSekw.get(0).equalsIgnoreCase("$GPGLL")) {
if (check(line)) {
sekGLL = new SekwencjaGLL(podzSekw);
} else {
//TODO: Zlicz błąd
}
}
if (podzSekw.get(0).equalsIgnoreCase("$GPRMC")) {
//TODO: Sekwencja RMC (Recommended minimum of data)
if (check(line)) {
sekRMC = new SekwencjaRMC(podzSekw);
} else {
//TODO: Zlicz błąd
}
}
}
private double obliczOdleglosc(SekwencjaGGA pkt1, SekwencjaGGA pkt2) {
double odleglosc = 0;
double earthRadius = 6371000; //meters
double dLat = Math.toRadians(pkt2.getWspolzedne().getLatitude() - pkt1.getWspolzedne().getLatitude());
double dLng = Math.toRadians(pkt2.getWspolzedne().getLongitude() - pkt1.getWspolzedne().getLongitude());
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(pkt1.getWspolzedne().getLatitude())) * Math.cos(Math.toRadians(pkt1.getWspolzedne().getLatitude())) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
odleglosc = earthRadius * c;
return odleglosc;
}
/**
* Funkcja sprawdzająca sume kontrolną
*
* @param tekst cała linia NMEA
* @return true jeśli się suma kontrolna zgadza
*/
private boolean check(String tekst) {
String suma = tekst.substring(tekst.length() - 2, tekst.length());
tekst = tekst.substring(1, tekst.length() - 3);
int checksum = 0;
for (int i = 0; i < tekst.length(); i++) {
checksum = checksum ^ tekst.charAt(i);
}
if (Integer.parseInt(suma, 16) == checksum) {
return true;
}
return false;
}
private JFreeChart createChart(final XYDataset dataset) { ... }
private void customizeChart(JFreeChart chart) { ... }
public static void main(String[] args) {
JFrame frame = new JFrame("MainFrame");
frame.setContentPane(new MainFrame().mainjpanel);
frame.setPreferredSize(new Dimension(640, 480));
frame.pack();
frame.setVisible(true);
}
To avoid blocking the event dispatch thread, construct an instance of
SwingWorker
. Collect data in your implementation ofdoInBackground()
,publish()
intermediate results, and update theXYSeries
in your implementation ofprocess()
. The listening chart will update itself in response. A related example that uses jfreechart is seen below and examined here.