From ed7d1f897b1f15fc0716d280d550bdc90c8ed144 Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 6 Jul 2023 15:34:26 +0200 Subject: [PATCH] Refactor LocalServer into downloaders +basic bt downloader --- .../piotro/sondechaser/data/BlueAdapter.java | 40 +++- .../sondechaser/data/DataCollector.java | 18 +- .../data/LocalServerCollector.java | 183 +++++++++--------- .../sondechaser/data/RadiosondyCollector.java | 3 +- .../data/local/LocalServerDownloader.java | 15 ++ .../data/local/MySondyDownloader.java | 74 +++++++ .../data/local/PipeServerDownloader.java | 125 ++++++++++++ .../ui/gallery/GalleryFragment.java | 5 + app/src/main/res/layout/fragment_gallery.xml | 1 + 9 files changed, 361 insertions(+), 103 deletions(-) create mode 100644 app/src/main/java/eu/piotro/sondechaser/data/local/LocalServerDownloader.java create mode 100644 app/src/main/java/eu/piotro/sondechaser/data/local/MySondyDownloader.java create mode 100644 app/src/main/java/eu/piotro/sondechaser/data/local/PipeServerDownloader.java diff --git a/app/src/main/java/eu/piotro/sondechaser/data/BlueAdapter.java b/app/src/main/java/eu/piotro/sondechaser/data/BlueAdapter.java index 6300af1..74fb52b 100644 --- a/app/src/main/java/eu/piotro/sondechaser/data/BlueAdapter.java +++ b/app/src/main/java/eu/piotro/sondechaser/data/BlueAdapter.java @@ -21,6 +21,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; public class BlueAdapter { private final Activity rootActivity; @@ -159,11 +160,14 @@ public class BlueAdapter { }); } - class BlockedReaderThread implements Runnable { + public class BlockedReaderThread implements Runnable { private String lastLine; + private boolean new_line = false; + + private Object lock = new Object(); @Override public void run() { - for (;;) { + while (!Thread.interrupted()) { // kill thread when interruptd System.out.println("BTHREAD: loop "); String line = readLine(); // this fails in all cases (device offline, closed transmission error) if (line == null) { @@ -173,16 +177,44 @@ public class BlueAdapter { } else { System.out.println("BTHREAD: received " + line); } - lastLine = line; + + synchronized (lock) { + lastLine = line; + new_line = true; + } try { Thread.sleep(200); } catch (InterruptedException ignored) {} } + System.out.println("BTHREAD: exit"); } public String getLine() { - return lastLine; + String line; + synchronized (lock) { + if (new_line) { + new_line = false; + line = lastLine; + } else { + line = null; + } + } + return line; } + } + public void close() { + try { + reader.close(); + writer.close(); + } catch (IOException ignored) {} + + try { + bluetoothSocket.close(); + } catch (IOException ignored) {} + + bluetoothSocket = null; + reader = null; + writer = null; } private BlockedReaderThread thread = null; diff --git a/app/src/main/java/eu/piotro/sondechaser/data/DataCollector.java b/app/src/main/java/eu/piotro/sondechaser/data/DataCollector.java index 4b033b2..2bd7024 100644 --- a/app/src/main/java/eu/piotro/sondechaser/data/DataCollector.java +++ b/app/src/main/java/eu/piotro/sondechaser/data/DataCollector.java @@ -41,6 +41,8 @@ public class DataCollector implements Runnable { private SlideshowFragment compassUpdater = null; private boolean stop = false; + private BlueAdapter ba; + public boolean showSondeSet = false; public DataCollector(Activity rootActivity) { @@ -52,6 +54,9 @@ public class DataCollector implements Runnable { locationProvider.startLocationProvider(null); orientationProvider = new Orientation(rootActivity); + + ba = new BlueAdapter(rootActivity); + ba.setDeviceAddress("aaa (98:F4:AB:6D:2B:5E)"); } public void setMapUpdater(MapUpdater mapUpdater) { @@ -115,7 +120,9 @@ public class DataCollector implements Runnable { rs_col.setSondeName(sharedPref.getString("rsid","")); sh_col.setSondeName(sharedPref.getString("shid","")); - lc_col = new LocalServerCollector(sharedPref.getString("lsip","")); + lc_col = new LocalServerCollector(); + + lc_col.setPipeSource(sharedPref.getString("lsip","")); rs_col_thread = new Thread(rs_col, "rscol"); sh_col_thread = new Thread(sh_col, "shcol"); @@ -142,7 +149,6 @@ public class DataCollector implements Runnable { Sonde lc_last_sonde = lc_col.getLastSonde(); Sonde sh_last_sonde = sh_col.getLastSonde(); - if (lc_last_sonde != null && (rs_last_sonde == null || rs_last_sonde.time <= lc_last_sonde.time)) updatePosition(lc_last_sonde, "LOCAL"); else if (rs_last_sonde != null && (sh_last_sonde == null || sh_last_sonde.time <= rs_last_sonde.time)) @@ -244,12 +250,8 @@ public class DataCollector implements Runnable { void updateStatus() { long time = new Date().getTime(); - int lc = Color.RED; - if (time - lc_col.last_success < 10000) { - lc = Color.YELLOW; - if (time - lc_col.last_decoded < 20000) - lc = Color.GREEN; - } + int lc = lc_col.getStatus() == LocalServerCollector.Status.RED ? Color.RED : + (lc_col.getStatus() == LocalServerCollector.Status.YELLOW ? Color.YELLOW : Color.GREEN); int scol = (time - sh_col.last_decoded < 60000) ? Color.GREEN : Color.RED; int rcol = (time - rs_col.last_decoded < 60000) ? Color.GREEN : Color.RED; diff --git a/app/src/main/java/eu/piotro/sondechaser/data/LocalServerCollector.java b/app/src/main/java/eu/piotro/sondechaser/data/LocalServerCollector.java index 4827a78..a0a5ef3 100644 --- a/app/src/main/java/eu/piotro/sondechaser/data/LocalServerCollector.java +++ b/app/src/main/java/eu/piotro/sondechaser/data/LocalServerCollector.java @@ -14,133 +14,130 @@ import java.util.Date; import javax.net.ssl.HttpsURLConnection; +import eu.piotro.sondechaser.data.local.LocalServerDownloader; +import eu.piotro.sondechaser.data.local.MySondyDownloader; +import eu.piotro.sondechaser.data.local.PipeServerDownloader; + public class LocalServerCollector implements Runnable { - private final String BASE_URL; + private Sonde lastSonde; + private Sonde prevSonde; + private ArrayList track; private final Object dataLock = new Object(); private ArrayList prediction; private Point pred_point; - public long last_success; - public long last_decoded; + private Status status; + + private long last_decoded; private int terrain_alt = 0; private volatile boolean stop = false; - public LocalServerCollector(String ip) { - BASE_URL = "http://" + ip + "/"; + public LocalServerCollector() {} + + private enum Mode { + NONE, + PIPE, + MYSONDY, } + + public enum Status { + RED, + YELLOW, + GREEN, + } + + private Mode source; + + private PipeServerDownloader pipeDownloader; + private MySondyDownloader mySondyDownloader; + + private void disable() { + // pipe downloader does not need disabling + mySondyDownloader.disable(); + source = Mode.NONE; + } + public void setPipeSource(String ip) { + disable(); + pipeDownloader = new PipeServerDownloader(ip); + source = Mode.PIPE; + } + + public void setMySondySource(BlueAdapter blueAdapter) { + disable(); + mySondyDownloader = new MySondyDownloader(blueAdapter); + mySondyDownloader.enable(); + source = Mode.MYSONDY; + } + @Override public void run() { lastSonde = null; track = new ArrayList<>(); pred_point = null; prediction = new ArrayList<>(); - last_success = 0; + last_decoded = 0; + status = Status.RED; while (!stop) { - download(); + getData(); + generatePrediction(); + try { Thread.sleep(2000); } catch (InterruptedException ignored) {} boolean ignored = Thread.interrupted(); } + disable(); } - private void downloadData(URL url, SondeParser parser) { - try { - System.err.println(url); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - try { - System.err.println(conn.getResponseCode()); - if (conn.getResponseCode() == 200) { - BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); - StringBuilder resp = new StringBuilder(); - for (String line; (line = br.readLine()) != null; resp.append(line)); - parser.parse(resp.toString()); - last_success = new Date().getTime(); - } + private void getData() { + if (source == Mode.NONE) { + status = Status.RED; + return; + } + LocalServerDownloader downloader = (source == Mode.PIPE ? pipeDownloader : mySondyDownloader); + downloader.download(); - } finally { - conn.disconnect(); - } - } catch (Exception e) { - e.printStackTrace(); + prevSonde = lastSonde; + synchronized (dataLock) { + lastSonde = downloader.getLastSonde(); + last_decoded = downloader.getLastDecoded(); + status = downloader.getStatus(); } } - private class Parser implements SondeParser { - public void parse(String data) { - try { - JSONObject json = new JSONObject(data); - if (json.length() == 0 || !json.getBoolean("valid")) { - return; - } - - Sonde sonde = new Sonde(); - - float lat = (float)json.getDouble("lat"); - float lon = (float)json.getDouble("lon"); - sonde.loc = new GeoPoint(lat, lon); - - sonde.alt = (int)Math.round(json.getDouble("alt")); - - sonde.time = json.getLong("time")*1000; - - sonde.vspeed = (float)json.getDouble("vs"); - - sonde.freq = null; - sonde.sid = null; - - System.out.println(sonde.alt); - if (lastSonde != null && sonde.time == lastSonde.time) - return; - - if(sonde.time > new Date().getTime() && sonde.time - new Date().getTime() < 600_000) { - // Sonde clocks tend to shift in time - sonde.time = new Date().getTime(); - } - - if (lastSonde != null) { - float timedev = (sonde.time - lastSonde.time) / 1000.f; - float latdev = (float)(sonde.loc.getLatitude() - lastSonde.loc.getLatitude()) / timedev; - float londev = (float)(sonde.loc.getLongitude() - lastSonde.loc.getLongitude()) / timedev; - float tgalt = terrain_alt; - float nextlat = (float)sonde.loc.getLatitude() + (latdev * ((sonde.alt - tgalt) / (sonde.vspeed * -1))); - float nextlon = (float)sonde.loc.getLongitude() + (londev * ((sonde.alt - tgalt) / (sonde.vspeed * -1))); - - synchronized (dataLock) { - pred_point = new Point(); - pred_point.point = new GeoPoint(nextlat, nextlon); - pred_point.alt = (int) tgalt; - pred_point.time = 0; - prediction = new ArrayList<>(); - prediction.add(sonde.loc); - prediction.add(pred_point.point); - } - } + private void generatePrediction() { + Sonde sonde = lastSonde; + { + Sonde lastSonde = prevSonde; // shadow + if (sonde.time > new Date().getTime() && sonde.time - new Date().getTime() < 600_000) { + // Sonde clocks tend to shift in time + sonde.time = new Date().getTime(); + } + if (lastSonde != null) { + float timedev = (sonde.time - lastSonde.time) / 1000.f; + float latdev = (float) (sonde.loc.getLatitude() - lastSonde.loc.getLatitude()) / timedev; + float londev = (float) (sonde.loc.getLongitude() - lastSonde.loc.getLongitude()) / timedev; + float tgalt = terrain_alt; + float nextlat = (float) sonde.loc.getLatitude() + (latdev * ((sonde.alt - tgalt) / (sonde.vspeed * -1))); + float nextlon = (float) sonde.loc.getLongitude() + (londev * ((sonde.alt - tgalt) / (sonde.vspeed * -1))); synchronized (dataLock) { - System.out.println("localupd"); - lastSonde = sonde; - track.add(sonde.loc); + pred_point = new Point(); + pred_point.point = new GeoPoint(nextlat, nextlon); + pred_point.alt = (int) tgalt; + pred_point.time = 0; + prediction = new ArrayList<>(); + prediction.add(sonde.loc); + prediction.add(pred_point.point); } - last_decoded = new Date().getTime(); - } catch (Exception e) { - e.printStackTrace(); } } } - - private void download() { - try { - URL url = new URL(BASE_URL + "get"); - downloadData(url, new Parser()); - } catch (Exception ignored) {} - } - public Sonde getLastSonde() { synchronized (dataLock) { return lastSonde; @@ -161,6 +158,14 @@ public class LocalServerCollector implements Runnable { synchronized (dataLock) {return pred_point;} } + public Status getStatus() { + return status; + } + + public long getLastDecoded() { + return last_decoded; + } + public void updateTerrainAlt(int alt) { terrain_alt = alt; } diff --git a/app/src/main/java/eu/piotro/sondechaser/data/RadiosondyCollector.java b/app/src/main/java/eu/piotro/sondechaser/data/RadiosondyCollector.java index f2a2c1e..8e12041 100644 --- a/app/src/main/java/eu/piotro/sondechaser/data/RadiosondyCollector.java +++ b/app/src/main/java/eu/piotro/sondechaser/data/RadiosondyCollector.java @@ -225,7 +225,7 @@ public class RadiosondyCollector implements Runnable { //sonde.vspeed = 0; String time_str = curr.getJSONObject("properties").getString("description"); - System.err.println(time_str.substring(11, 11+19)); + System.err.println("Radiosondy" + time_str.substring(11, 11+19)); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); sonde.time = sdf.parse(time_str.substring(11, 11+19)).getTime(); @@ -239,7 +239,6 @@ public class RadiosondyCollector implements Runnable { for (int i=path.length()-1; i>=0; i-=10) { JSONArray entry = path.getJSONArray(i); gps.add(new GeoPoint(entry.getDouble(1), entry.getDouble(0))); - System.out.println(entry.getDouble(0)); } synchronized (dataLock) { track = gps; diff --git a/app/src/main/java/eu/piotro/sondechaser/data/local/LocalServerDownloader.java b/app/src/main/java/eu/piotro/sondechaser/data/local/LocalServerDownloader.java new file mode 100644 index 0000000..cbf0a12 --- /dev/null +++ b/app/src/main/java/eu/piotro/sondechaser/data/local/LocalServerDownloader.java @@ -0,0 +1,15 @@ +package eu.piotro.sondechaser.data.local; + +import eu.piotro.sondechaser.data.LocalServerCollector; +import eu.piotro.sondechaser.data.Sonde; + +public interface LocalServerDownloader { + void download(); + + Sonde getLastSonde(); + long getLastDecoded(); + LocalServerCollector.Status getStatus(); + + void enable(); + void disable(); +} diff --git a/app/src/main/java/eu/piotro/sondechaser/data/local/MySondyDownloader.java b/app/src/main/java/eu/piotro/sondechaser/data/local/MySondyDownloader.java new file mode 100644 index 0000000..0fa7fe5 --- /dev/null +++ b/app/src/main/java/eu/piotro/sondechaser/data/local/MySondyDownloader.java @@ -0,0 +1,74 @@ +package eu.piotro.sondechaser.data.local; + +import java.util.Date; + +import eu.piotro.sondechaser.data.BlueAdapter; +import eu.piotro.sondechaser.data.LocalServerCollector; +import eu.piotro.sondechaser.data.Sonde; + +public class MySondyDownloader implements LocalServerDownloader { + private final BlueAdapter blueAdapter; + + private BlueAdapter.BlockedReaderThread bt_runnable; + private Thread bt_thread; + + private Sonde lastSonde; + private long last_decoded; + + private LocalServerCollector.Status mStatus = LocalServerCollector.Status.RED; + + public MySondyDownloader(BlueAdapter blueAdapter) { + this.blueAdapter = blueAdapter; + } + + @Override + public void enable() { + disable(); + // Address and mode should be configured already in passed object + bt_runnable = blueAdapter.getRunnable(); + bt_thread = new Thread(bt_runnable); + bt_thread.start(); + } + + @Override + public void disable() { + if(bt_thread == null) + return; + bt_thread.interrupt(); + blueAdapter.close(); + } + + @Override + public void download() { + if (!blueAdapter.isConnected()) + mStatus = LocalServerCollector.Status.RED; + else + mStatus = LocalServerCollector.Status.YELLOW; + + String line = bt_runnable.getLine(); + + if (line == null) + return; + + System.out.println("PARSE" + line); + + last_decoded = new Date().getTime(); + } + + @Override + public Sonde getLastSonde() { + return lastSonde; + } + + @Override + public long getLastDecoded() { + return last_decoded; + } + + @Override + public LocalServerCollector.Status getStatus() { + return mStatus; + } + + +} diff --git a/app/src/main/java/eu/piotro/sondechaser/data/local/PipeServerDownloader.java b/app/src/main/java/eu/piotro/sondechaser/data/local/PipeServerDownloader.java new file mode 100644 index 0000000..f24a665 --- /dev/null +++ b/app/src/main/java/eu/piotro/sondechaser/data/local/PipeServerDownloader.java @@ -0,0 +1,125 @@ +package eu.piotro.sondechaser.data.local; + +import org.json.JSONObject; +import org.osmdroid.util.GeoPoint; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Date; + +import eu.piotro.sondechaser.data.LocalServerCollector; +import eu.piotro.sondechaser.data.Sonde; +import eu.piotro.sondechaser.data.SondeParser; + +public class PipeServerDownloader implements LocalServerDownloader { + private final String BASE_URL; + + public PipeServerDownloader(String ip) { + BASE_URL = "http://" + ip + "/"; + } + + private long last_received; + private long last_decoded; + + private Sonde lastSonde = null; + + private LocalServerCollector.Status mStatus = LocalServerCollector.Status.RED; + + private void downloadData(URL url, SondeParser parser) { + try { + System.err.println(url); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + try { + System.err.println(conn.getResponseCode()); + if (conn.getResponseCode() == 200) { + BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); + StringBuilder resp = new StringBuilder(); + for (String line; (line = br.readLine()) != null; resp.append(line)); + parser.parse(resp.toString()); + last_received = new Date().getTime(); + } + + } finally { + conn.disconnect(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private class Parser implements SondeParser { + public void parse(String data) { + try { + JSONObject json = new JSONObject(data); + if (json.length() == 0 || !json.getBoolean("valid")) { + return; + } + + Sonde sonde = new Sonde(); + + float lat = (float)json.getDouble("lat"); + float lon = (float)json.getDouble("lon"); + sonde.loc = new GeoPoint(lat, lon); + + sonde.alt = (int)Math.round(json.getDouble("alt")); + + sonde.time = json.getLong("time")*1000; + + sonde.vspeed = (float)json.getDouble("vs"); + + sonde.freq = null; + sonde.sid = null; + + System.out.println("LocalPipeServer" + sonde.time); + + if (lastSonde != null && sonde.time != lastSonde.time) + last_decoded = new Date().getTime(); + + lastSonde = sonde; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void updateStatus() { + LocalServerCollector.Status lc = LocalServerCollector.Status.RED; + if (new Date().getTime() - last_received < 10000) { + lc = LocalServerCollector.Status.YELLOW; + if (new Date().getTime() - last_decoded < 20000) + lc = LocalServerCollector.Status.GREEN; + } + mStatus = lc; + } + + @Override + public void download() { + try { + updateStatus(); + URL url = new URL(BASE_URL + "get"); + downloadData(url, new Parser()); + } catch (Exception ignored) {} + } + + @Override + public Sonde getLastSonde() { + return lastSonde; + } + + @Override + public long getLastDecoded() { + return last_decoded; + } + + @Override + public LocalServerCollector.Status getStatus() { + return mStatus; + } + + @Override + public void enable() {} + @Override + public void disable() {} +} diff --git a/app/src/main/java/eu/piotro/sondechaser/ui/gallery/GalleryFragment.java b/app/src/main/java/eu/piotro/sondechaser/ui/gallery/GalleryFragment.java index 7754088..ff4e1b3 100644 --- a/app/src/main/java/eu/piotro/sondechaser/ui/gallery/GalleryFragment.java +++ b/app/src/main/java/eu/piotro/sondechaser/ui/gallery/GalleryFragment.java @@ -20,6 +20,7 @@ import java.time.Duration; import eu.piotro.sondechaser.MainActivity; import eu.piotro.sondechaser.R; +import eu.piotro.sondechaser.data.BlueAdapter; import eu.piotro.sondechaser.data.RadiosondyCollector; import eu.piotro.sondechaser.data.SondeHubCollector; import eu.piotro.sondechaser.databinding.FragmentGalleryBinding; @@ -101,6 +102,10 @@ public class GalleryFragment extends Fragment { ((TextView)v.findViewById(R.id.tfsh)).setText(entry); return true; }); + +// PopupMenu btPopupMenu = new PopupMenu(context, v.findViewById(R.id.set_awake)); +// new BlueAdapter(getActivity()).fillMenu(getActivity(), btPopupMenu); +// btPopupMenu.show(); } @Override diff --git a/app/src/main/res/layout/fragment_gallery.xml b/app/src/main/res/layout/fragment_gallery.xml index 261548f..2639eb2 100644 --- a/app/src/main/res/layout/fragment_gallery.xml +++ b/app/src/main/res/layout/fragment_gallery.xml @@ -108,4 +108,5 @@ android:text="Keep screen awake" app:layout_constraintStart_toStartOf="@+id/tfip" app:layout_constraintTop_toBottomOf="@+id/tfip" /> + \ No newline at end of file