From d5477a077afb6802cfe94fe68097461013351b3a Mon Sep 17 00:00:00 2001 From: Markus Ebner Date: Tue, 7 Apr 2020 16:18:21 +0200 Subject: [PATCH] Implement OrderedLogger Run Logfile-Reordering live on the smartphone itself. --- .idea/caches/build_file_checksums.ser | Bin 591 -> 534 bytes .idea/caches/build_file_checksums.ser.orig | Bin 0 -> 547 bytes .idea/caches/gradle_models.ser | Bin 0 -> 345698 bytes .idea/modules.xml | 4 +- .idea/modules.xml.orig | 14 + app/build.gradle.orig | 38 +++ .../indoor/sensorreadout/MainActivity.java | 37 +-- .../sensorreadout/sensors/OrderedLogger.java | 268 ++++++++++++++++++ build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 10 files changed, 344 insertions(+), 23 deletions(-) create mode 100644 .idea/caches/build_file_checksums.ser.orig create mode 100644 .idea/caches/gradle_models.ser create mode 100644 .idea/modules.xml.orig create mode 100644 app/build.gradle.orig create mode 100644 app/src/main/java/de/fhws/indoor/sensorreadout/sensors/OrderedLogger.java diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index df31f895661b30d548419aca2f21bab78a84c25e..249edd708dbf2741e768d1f3021e428adeb3f924 100644 GIT binary patch delta 113 zcmV-%0FM991eOGlmj!WD$u)_QoQMl0FKBOVWiNAOX=;;%0XHWMVs&Y3WG-iNVPtG& zb#VZG00sa601&9_BZ2iq!6Z?ub}+3M7_yT~0UZ;vfC7d^{)}+HI)SIjbL?Ss01%TT T0xAP^01=ap0UMLo0T2OrIFBfO delta 147 zcmV;E0Brx31kVJJmj!THOY7i~oQMGolfeNN5T3&`B7*@m=Q?iHgxcY%Fq4e|9UKaW zf>?8hKq>erU%t4(IHPm`Brj-hZDlWXZ*G$>0XLKB0W3Xq07@@yZgek1VRU6~FGO!^ zb!}yCbY(Aaa&KpHVQp5fFEb@IQ7^qHF(oHeub?PDD>b=9F91S2 zm1gFoxMk*~I%lLNXBU^|7Q2L-Ts|(GuF1r}v;Nd!V}nL2`N^IX#cW;tZfX zEBmd3hs6>uGBYr_F>vMNC#JY1CYR(Fc`|U8WE7jE0%GnY+h{Ay@X+ku3+$lWbqOPBWJ6a(Bjmh z;uzn=qU_RQ1*g)SoZQ5`ycnpHVh}NfVcZqjo}2v)n^;~paL#!1!>O)>K@g8>?eJ)Z SGQCyGP4jB~%|C?vtN;K(Lb}ob literal 0 HcmV?d00001 diff --git a/.idea/caches/gradle_models.ser b/.idea/caches/gradle_models.ser new file mode 100644 index 0000000000000000000000000000000000000000..f3343205e4cee844996cd1cfa22ccb7d7b5855c8 GIT binary patch literal 345698 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 - + - + \ No newline at end of file diff --git a/.idea/modules.xml.orig b/.idea/modules.xml.orig new file mode 100644 index 0000000..a967c4b --- /dev/null +++ b/.idea/modules.xml.orig @@ -0,0 +1,14 @@ + + + + +<<<<<<< HEAD + + +======= + + +>>>>>>> lindenberg + + + \ No newline at end of file diff --git a/app/build.gradle.orig b/app/build.gradle.orig new file mode 100644 index 0000000..8055b19 --- /dev/null +++ b/app/build.gradle.orig @@ -0,0 +1,38 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 29 + buildToolsVersion '29.0.2' + + defaultConfig { + applicationId "de.fhws.indoor.sensorreadout" + minSdkVersion 21 +<<<<<<< HEAD + targetSdkVersion 23 +======= + targetSdkVersion 29 +>>>>>>> lindenberg + versionCode 1 + versionName "1.0" + multiDexEnabled true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation 'com.android.support:design:28.0.0' + implementation fileTree(include: ['*.jar'], dir: 'libs') + testImplementation 'junit:junit:4.12' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.google.android.gms:play-services:12.0.1' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'org.apmem.tools:layouts:1.10@aar' + //compile 'com.google.android.support:wearable:1.3.0' + //compile 'com.google.android.gms:play-services-wearable:8.4.0' + //provided 'com.google.android.wearable:wearable:1.0.0' +} diff --git a/app/src/main/java/de/fhws/indoor/sensorreadout/MainActivity.java b/app/src/main/java/de/fhws/indoor/sensorreadout/MainActivity.java index 2213a70..5a385e6 100644 --- a/app/src/main/java/de/fhws/indoor/sensorreadout/MainActivity.java +++ b/app/src/main/java/de/fhws/indoor/sensorreadout/MainActivity.java @@ -39,7 +39,8 @@ import de.fhws.indoor.sensorreadout.sensors.LoggerRAM; import de.fhws.indoor.sensorreadout.sensors.PedestrianActivity; import de.fhws.indoor.sensorreadout.sensors.PedestrianActivityButton; import de.fhws.indoor.sensorreadout.sensors.PhoneSensors; -import de.fhws.indoor.sensorreadout.sensors.UnorderedLogger; +import de.fhws.indoor.sensorreadout.sensors.OrderedLogger; +//import de.fhws.indoor.sensorreadout.sensors.UnorderedLogger; import de.fhws.indoor.sensorreadout.sensors.WiFi; import de.fhws.indoor.sensorreadout.sensors.WiFiRTT; import de.fhws.indoor.sensorreadout.sensors.iBeacon; @@ -57,7 +58,7 @@ public class MainActivity extends Activity { private final ArrayList sensors = new ArrayList(); //private final Logger logger = new Logger(this); //private final LoggerRAM logger = new LoggerRAM(this); - private final UnorderedLogger logger = new UnorderedLogger(this); + private final OrderedLogger logger = new OrderedLogger(this); private Button btnStart; private Button btnStop; private Button btnGround; @@ -452,25 +453,25 @@ public class MainActivity extends Activity { @Override public void onData(final SensorType id, final long timestamp, final String csv) {return; } }); } -// if(activeSensors.contains("WIFI")) { -// // log wifi using sensor number 8 -// final WiFi wifi = new WiFi(this); -// sensors.add(wifi); -// wifi.setListener(new mySensor.SensorListener() { -// @Override public void onData(final long timestamp, final String csv) { add(SensorType.WIFI, csv, timestamp); } -// @Override public void onData(final SensorType id, final long timestamp, final String csv) {return; } -// }); -// } if(activeSensors.contains("WIFI")) { - // log wifi RTT using sensor number 17 - final WiFiRTT wifirtt = new WiFiRTT(this); - sensors.add(wifirtt); - wifirtt.setListener(new mySensor.SensorListener() { - @Override public void onData(final long timestamp, final String csv) { add(SensorType.WIFIRTT, csv, timestamp); } - @Override public void onData(final SensorType id, final long timestamp, final String csv) { add(SensorType.WIFIRTT, csv, timestamp); } + // log wifi using sensor number 8 + final WiFi wifi = new WiFi(this); + sensors.add(wifi); + wifi.setListener(new mySensor.SensorListener() { + @Override public void onData(final long timestamp, final String csv) { add(SensorType.WIFI, csv, timestamp); } + @Override public void onData(final SensorType id, final long timestamp, final String csv) {return; } }); - } +// if(activeSensors.contains("WIFI")) { +// // log wifi RTT using sensor number 17 +// final WiFiRTT wifirtt = new WiFiRTT(this); +// sensors.add(wifirtt); +// wifirtt.setListener(new mySensor.SensorListener() { +// @Override public void onData(final long timestamp, final String csv) { add(SensorType.WIFIRTT, csv, timestamp); } +// @Override public void onData(final SensorType id, final long timestamp, final String csv) { add(SensorType.WIFIRTT, csv, timestamp); } +// }); +// +// } if(activeSensors.contains("BLUETOOTH")) { // bluetooth permission if(ActivityCompat.shouldShowRequestPermissionRationale(this, diff --git a/app/src/main/java/de/fhws/indoor/sensorreadout/sensors/OrderedLogger.java b/app/src/main/java/de/fhws/indoor/sensorreadout/sensors/OrderedLogger.java new file mode 100644 index 0000000..5e8c9d3 --- /dev/null +++ b/app/src/main/java/de/fhws/indoor/sensorreadout/sensors/OrderedLogger.java @@ -0,0 +1,268 @@ +package de.fhws.indoor.sensorreadout.sensors; + +import android.content.Context; +import android.os.SystemClock; +import android.support.annotation.NonNull; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * log sensor data to file + * Created by Frank on 25.03.2015. + * Re-Written by Markus on 20.06.2019. + */ +public final class OrderedLogger { + + private static final long CACHED_LINES = 10000; + + private interface ReorderBufferListener { + void onCommit(final List commitSlice); + } + + private static class ReorderBuffer { + private long cacheSize = 0; + private ArrayList inputCache; + private BlockingQueue> pendingInputCaches; + private ReorderThread reorderThread; + private ReorderBufferListener commitListener; + + //statistics + private AtomicInteger cachedEntries = new AtomicInteger(0); + private AtomicInteger writtenEntries = new AtomicInteger(0); + private long sizeTotal = 0; + + // ################# + // # ReorderBuffer # -> File + // ############## # ------------- # + // # InputCache # -> # # + // ############## ################# + // + // The InputCache is the fast-access side used by the logger, to append entries. + // If the InputCache is full, it is committed to the ReorderThread, and swapped + // for a new / empty InputCache. + // The ReorderThread sorts the ReorderBuffer, commits the upper half to the writer + // and inserts the InputCache into the ReorderBuffer + + public ReorderBuffer(long cacheSize, ReorderBufferListener commitListener) { + this.cacheSize = cacheSize; + this.commitListener = commitListener; + } + + public synchronized void start() { + inputCache = new ArrayList<>(); + inputCache.ensureCapacity((int) cacheSize); + pendingInputCaches = new ArrayBlockingQueue<>(5); + reorderThread = new ReorderThread(); + cachedEntries.set(0); + writtenEntries.set(0); + sizeTotal = 0; + reorderThread.start(); + } + + public synchronized void add(LogEntry entry) { + inputCache.add(entry); + cachedEntries.incrementAndGet(); + sizeTotal += entry.csv.length(); + if(inputCache.size() == cacheSize) { + // InputCache full, commit to ReorderThread for processing + commitInputCache(); + } + } + + public synchronized void flushAndStop() { + if(inputCache.size() > 0) { + commitInputCache(); + } + // be sure to commit an empty InputCache to signal the ReorderThread + commitInputCache(); + try { + reorderThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void commitInputCache() { + if(!pendingInputCaches.add(inputCache)) { + throw new RuntimeException("InputCaches overrun. ReorderThread can't keep up."); + } + inputCache = new ArrayList<>(); + inputCache.ensureCapacity((int) cacheSize); + } + + public int getEntriesWritten() { return writtenEntries.get(); } + public int getEntriesCached() { return cachedEntries.get(); } + public long getSizeTotal() { return sizeTotal; } + + private class ReorderThread extends Thread { + + public ReorderThread() { + setName("ReorderThread"); + setPriority(Thread.MIN_PRIORITY); + } + + @Override + public void run() { + ArrayList reorderBuffer = new ArrayList<>(); + reorderBuffer.ensureCapacity((int) cacheSize * 2); + + try { + ArrayList inputCache = null; + while((inputCache = pendingInputCaches.take()) != null) { + boolean draining = inputCache.size() == 0; + Log.d("OrderedLogger[Thread]", "Received InputCache[size:" + inputCache.size() + " -> draining:" + draining + "]"); + if(reorderBuffer.size() > (int) cacheSize || draining) { + // we are (either): + // - in running phase, reorderBuffer is filled and we need to commit + // - in draining phase, commit even if reorderBuffer is not full + Collections.sort(reorderBuffer); + List commitSlice; + if(draining) { + // we received an empty InputCache -> drain the complete ReorderBuffer into the writer. + commitSlice = reorderBuffer.subList(0, reorderBuffer.size()); + } else { + // we are mid-flight, drain cacheSize at max + commitSlice = reorderBuffer.subList(0, Math.min((int) cacheSize, reorderBuffer.size())); + } + Log.d("OrderedLogger[Thread]", "Committing[drain:" + draining + "] Chunk[size:" + commitSlice.size() + "]"); + commitListener.onCommit(commitSlice); + writtenEntries.addAndGet(commitSlice.size()); + cachedEntries.addAndGet(-commitSlice.size()); + commitSlice.clear(); + } + reorderBuffer.addAll(inputCache); + + if(reorderBuffer.size() == 0 && draining) { + Log.d("OrderedLogger[Thread]", "Draining finished"); + return; // we are done with draining the buffer + } + } + } catch(InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + + + + public static final long BEGINNING_TS = -1; + + private StringBuilder stringBuilder = new StringBuilder(); + private File file; + private FileOutputStream fos; + private Context context; + + private ReorderBuffer reorderBuffer; + + /** timestamp of logging start. all entries are relative to this one */ + private long startTS = 0; + + public OrderedLogger(Context context) { + this.context = context; + } + + /** start logging (caching a couple of entries in RAM) */ + public final void start() { + + // start empty + stringBuilder.setLength(0); + reorderBuffer = new ReorderBuffer(CACHED_LINES, new ReorderBufferListener() { + @Override + public void onCommit(List commitSlice) { + for(LogEntry entry : commitSlice) { + try { + fos.write(entry.csv.getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }); + + // starting timestamp + startTS = SystemClock.elapsedRealtimeNanos(); + + // open the output-file immediately (to get permission errors) + // but do NOT yet write anything to the file + final DataFolder folder = new DataFolder(context, "sensorOutFiles"); + file = new File(folder.getFolder(), startTS + ".csv"); + + try { + fos = new FileOutputStream(file); + Log.d("logger", "will write to: " + file.toString()); + } catch (final Exception e) { + throw new MyException("error while opening log-file", e); + } + reorderBuffer.start(); + } + + /** stop logging and flush RAM-data to the flash-chip */ + public final void stop() { + reorderBuffer.flushAndStop(); + close(); + } + + public File getFile() { + return file; + } + + public int getCurrentBufferSize() {return reorderBuffer.getEntriesCached();} + public int getTotalSize() {return (int)reorderBuffer.getSizeTotal();} + + public int getNumEntries() {return reorderBuffer.getEntriesCached();} + + /** add a new CSV entry for the given sensor number to the internal buffer */ + public final void addCSV(final SensorType sensorNr, final long timestamp, final String csv) { + final long relTS = (timestamp == BEGINNING_TS) ? 0 : (timestamp - startTS); + if(relTS >= 0) { // drop pre startTS logs (at the beginning, sensors sometimes deliver old values) + stringBuilder.append(relTS); // relative timestamp (uses less space) + stringBuilder.append(';'); + stringBuilder.append(sensorNr.id()); + stringBuilder.append(';'); + stringBuilder.append(csv); + stringBuilder.append('\n'); + LogEntry logEntry = new LogEntry(relTS, stringBuilder.toString()); + reorderBuffer.add(logEntry); + stringBuilder.setLength(0); + } + } + + private final void close() { + try { + fos.close(); + } catch (final Exception e) { + throw new MyException("error while wriyting log-file", e); + } + } + + public final long getStartTS() { + return startTS; + } + + + private static class LogEntry implements Comparable { + public long timestamp; + public String csv; + + public LogEntry(long timestamp, String csv) { + this.timestamp = timestamp; + this.csv = csv; + } + + @Override + public int compareTo(@NonNull LogEntry another) { + return (timestamp < another.timestamp) ? (-1) : (+1); + } + } +} diff --git a/build.gradle b/build.gradle index 5d652f1..8791699 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index de55c05..18c3465 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Aug 18 15:19:02 CEST 2019 +#Tue Apr 07 13:33:43 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip