Database revolution

- added in fitbitData an interface for keeping track of date
- removed annoying IOException, now will write a LOG
- same thing in sensor
- fixed main and adapted to new Database system
- Database now has Local or Remote (to implement)
This commit is contained in:
2018-09-10 21:32:09 +02:00
parent 662fb40df3
commit a4c6c095c6
13 changed files with 399 additions and 312 deletions

View File

@@ -0,0 +1,156 @@
package support.database;
import device.Fitbit;
import device.fitbitdata.HeartRate;
import device.fitbitdata.Sleep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Timestamp;
import java.util.List;
public interface Database {
/**
* Un logger per scrivere a console eventuali errori o informazioni
*/
Logger LOG = LoggerFactory.getLogger("DB");
/**
* Una costante che indica quanti millisecondi ci sono in un minuto (utile per le conversioni)
*/
int MILLISEC_IN_MINUTE = 60000;
/**
* Dice solamente se e' possibile collegarsi al database e se si possono fare delle query
* @return vero se si pu' falso in altri casi.
*/
boolean isReachable();
/**
* Inserisce nuovi dati del paziente nel database.
* @param dateMilliSec la data che si vuole inserire in millisec
* @param heartRate il battito cardiaco
* @return vero se ha inserito o i dati esistevano gia' falso se non ce l'ha fatta
*/
boolean updateHeart(long dateMilliSec, double heartRate);
/**
* Inserisce nuovi dati del paziente nel database.
* @param dateStartSleep la data che si vuole inserire in millisec
* @param duration per quanto e' durato il sonno
* @return vero se ha inserito o i dati esistevano gia' falso se non ce l'ha fatta
*/
boolean updateSleep(long dateStartSleep, long duration);
/**
* Inserisce nuovi dati del paziente nel database.
* @param dateMilliSec la data che si vuole inserire in millisec
* @param steps i passi fatti
* @return vero se ha inserito o i dati esistevano gia' falso se non ce l'ha fatta
*/
boolean updateSteps(long dateMilliSec, long steps);
/**
* Riceve i dati del cuore dal giorno selezionato fino ad oggi
* @param days quanti giorni devono esser considerati
* @return una lista dei battiti cardiaci degli ultimi X giorni (ordinati da oggi al giorno X)
*/
List<HeartRate> getHeartDataOfLast(int days);
/**
* Prendi il Thread che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco)<br>
* Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro<br>
* @param database il database in cui inserirlo
* @param fitbit la classe che contiene i dati aggiornati
* @param retryMinutes ogni quanti minuti deve riprovare ad inviare la richiesta
* @return un Thread
*/
static Thread insertHourlyDataIn(Database database, Fitbit fitbit, int retryMinutes) {
Runnable runnable = new Runnable() {
@Override
public synchronized void run() {
LOG.info("Aggiornamento orario iniziato");
try {
boolean retry;
long now = System.currentTimeMillis();
double heartRate = 30;//fitbit.getHeartRate(60);
do {
retry = !database.updateHeart(now, heartRate);
LOG.info("Aggiornamento " + (!retry ? "riuscito" : "fallito, riprovo fra " + retryMinutes + " minuti"));
if (retry)
wait(retryMinutes * MILLISEC_IN_MINUTE);
} while(retry);
} catch (Exception e) {
LOG.warn("Aggiornamento interrotto");
}
}
};
return getThreadStartingEach(runnable, "update-hourly-data", 1);
}
/**
* Prendi il Thread che automaticamente gestisce l'inserimento dei dati giornalieri<br>
* Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro<br>
* @param database il database in cui inserirlo
* @param fitbit la classe che contiene i dati aggiornati
* @param retryMinutes ogni quanti minuti deve riprovare ad inviare la richiesta
* @return un Thread da far partire
*/
static Thread insertDailyData(Database database, Fitbit fitbit, int retryMinutes) {
Runnable runnable = new Runnable() {
@Override
public synchronized void run() {
LOG.info("Aggiornamento giornaliero iniziato");
try {
boolean retry;
long steps = fitbit.getSteps();
List<Sleep.SleepData> sleepDatas = fitbit.getDetailedSleep();
long now = System.currentTimeMillis();
do {
retry = !database.updateSteps(now, steps);
for (Sleep.SleepData data : sleepDatas)
retry = retry && !database.updateSleep(data.start_date, data.duration);
LOG.info("Aggiornamento " + (!retry ? "riuscito" : "fallito, riprovo fra " + retryMinutes + " minuti"));
if (retry)
wait(retryMinutes * MILLISEC_IN_MINUTE);
} while (retry);
} catch (Exception e) {
LOG.warn("Aggiornamento interrotto");
}
}
};
return getThreadStartingEach(runnable, "update-daily-data", 24*60);
}
/**
* Restuisce un thread che se fatto partire, esegue il runnable in un sub-thread ogni X minuti<br>
* Il sotto thread lanciato avra' lo stesso nome, ma con un trattino e la data di lancio a seguito<br>
* Se il thread viene interrotto non fa piu' partire il runnable e si ferma
* @param runnable il runnable da lanciare
* @param threadName il nome da dare al thread
* @param minutes i minuti da aspettare, se negativi o 0 ritorna null
*/
static Thread getThreadStartingEach(final Runnable runnable, String threadName, int minutes) {
if(minutes<1)
return null;
return new Thread(new Runnable() {
@Override
public synchronized void run() {
boolean notInterrupted = true;
do {
try {
wait(minutes * MILLISEC_IN_MINUTE);
Thread thread = new Thread(runnable, threadName + "-" + new Timestamp(System.currentTimeMillis()));
thread.start();
} catch (Exception e) {
e.printStackTrace();
notInterrupted = false;
}
} while (notInterrupted);
}
}, threadName);
}
}

View File

@@ -0,0 +1,106 @@
package support.database;
import device.fitbitdata.HeartRate;
import java.sql.*;
import java.util.LinkedList;
import java.util.List;
/**
* Classe che utilizza un database sqlite che contiene le seguenti tabelle:<br>
* <ul>
* <li>'heart' battito + orario</li>
* <li>'sleep' inizio + durata</li>
* <li>'steps' data + passi.</li>
* </ul>
*/
public class LocalDB implements Database {
/**
* Il percorso di dove trovare il database, strutturato in: &lt;interfaccia&gt;:&lt;implementazione&gt;:&lt;percorso vero e proprio&gt;
*/
public static final String DB_LOCATION = "jdbc:sqlite:";
/**
* Il nome del database (aka il nome del file)
*/
public static final String DB_NAME = "user_data.db";
/**
* La connessione al database
*/
private final Connection conn;
/**
* Crea una connessione al Database specificato in DB_LOCATION e con il nome DB_NAME.
* Se il Database non esiste lo crea e inizializza anche delle tabelle:
* <ul>
* <li>'heart' battito + orario</li>
* <li>'sleep' inizio + durata</li>
* <li>'step' data + passi.</li>
* </ul>
* @throws SQLException se qualcosa e' andato storto
*/
public LocalDB() throws SQLException {
conn = DriverManager.getConnection(DB_LOCATION + DB_NAME);
Statement statement = conn.createStatement();
statement.execute("CREATE TABLE IF NOT EXISTS heart (day DATE PRIMARY KEY, rate DOUBLE)");
statement.execute("CREATE TABLE IF NOT EXISTS sleep (sleep_start DATE PRIMARY KEY, duration INTEGER)");
statement.execute("CREATE TABLE IF NOT EXISTS steps (day DATE PRIMARY KEY, steps INTEGER)");
}
@Override
public boolean isReachable() { return conn!=null; }
@Override
public boolean updateHeart(long dateMilliSec, double heartRate) {
return query("INSERT INTO heart (day, rate) VALUES ( ' " + new Timestamp(dateMilliSec) + " ', '" + heartRate + "')");
}
@Override
public boolean updateSleep(long dateStartSleep, long duration) {
return query("INSERT INTO sleep (sleep_start, duration) VALUES ( ' " + new Timestamp(dateStartSleep) + " ', '" + duration + "')");
}
@Override
public boolean updateSteps(long dateMilliSec, long steps) {
return query("INSERT INTO steps (day, steps) VALUES ( ' " + new Timestamp(dateMilliSec) + " ', '" + steps + "')");
}
@Override
public List<HeartRate> getHeartDataOfLast(int days) {
try {
int dayToSubtract = 15;
// Calendar calendar = Calendar.getInstance();
// calendar.setTimeInMillis(System.currentTimeMillis());
// calendar.add(Calendar.DATE, -dayToSubtract);
// long time = calendar.getTimeInMillis();
long time = System.currentTimeMillis() - (dayToSubtract * 24 * 60 * 1000); // meno 24 giorni per 60 secondi per 100 millisec
ResultSet result = conn.createStatement().executeQuery("SELECT * FROM heart WHERE day>='" + new Timestamp(time) + "'");
List<HeartRate> list = new LinkedList<>();
while(result.next()) {
HeartRate rate = new HeartRate();
rate.setAverage(result.getDouble("rate"));
rate.setDate(result.getDate("day").getTime());
list.add(rate);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private boolean query(String sql) {
try {
conn.createStatement().execute(sql);
return true;
} catch (SQLException e) {
LOG.error(e.getMessage());
return false;
}
}
}

View File

@@ -0,0 +1,39 @@
package support.database;
import device.fitbitdata.HeartRate;
import java.util.List;
// TODO implement
public class RemoteDB implements Database {
public RemoteDB(String url) {
}
@Override
public boolean isReachable() {
return false;
}
@Override
public boolean updateHeart(long dateMilliSec, double heartRate) {
return false;
}
@Override
public boolean updateSleep(long dateStartSleep, long duration) {
return false;
}
@Override
public boolean updateSteps(long dateMilliSec, long steps) {
return false;
}
@Override
public List<HeartRate> getHeartDataOfLast(int days) {
return null;
}
}