Commit cb6b9f53 authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬
Browse files

Merge branch 'ui-data-pane' into 'main'

Ui data pane

See merge request !4
parents 069108fc b80210db
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-07-17 13:50:49.319377","version":"1.20.0-3.0.pre.126"} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-08-21 09:20:35.704809","version":"1.21.0-10.0.pre.212"}
\ No newline at end of file \ No newline at end of file
import 'dart:async'; import 'dart:async';
import 'dart:collection';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_map/plugin_api.dart';
import 'package:latlong/latlong.dart'; import 'package:latlong/latlong.dart';
import 'package:logair_application/logic/handlers/bluetooth_le_handler.dart';
import 'package:logair_application/logic/handlers/data_handler.dart'; import 'package:logair_application/logic/handlers/data_handler.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/logic/handlers/network_handler.dart'; import 'package:logair_application/logic/handlers/network_handler.dart';
import 'package:logair_application/logic/data_header.dart'; import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/logic/data_packet.dart'; import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/logic/handlers/position_handler.dart'; import 'package:logair_application/logic/handlers/position_handler.dart';
import 'package:logair_application/utils/pm_symbol.dart';
/// The [MapDisplayController] is a [Singleton] that handles the mapping of information. /// The [MapDisplayController] is a [Singleton] that handles the mapping of information.
class MapDisplayController { class MapDisplayController {
...@@ -33,37 +33,60 @@ class MapDisplayController { ...@@ -33,37 +33,60 @@ class MapDisplayController {
/// [Marker] representing the user's current [Position]. /// [Marker] representing the user's current [Position].
Marker _marker; Marker _marker;
HashMap<String, List<CircleMarker>> _pmMarkers = HashMap.from({'PM1': <CircleMarker>[], 'PM2_5': <CircleMarker>[], 'PM4': <CircleMarker>[], 'PM10': <CircleMarker>[]});
/// [List] of [Color]s to be smudged as a gradient and cherry picked based on a value (lerped).
/// @see [_colorizeValue]
List<Color> _colors = [ Color.fromARGB(0xff, 0, 0xcc, 0), Color.fromARGB(0xff, 0, 0xcc, 0), Color.fromARGB(0xff, 0xff, 0xff, 0), Color.fromARGB(0xff, 0xeb, 0x8a, 0x14), Color.fromARGB(0xff, 0xff, 0, 0), Color.fromARGB(0xff, 0xa1, 0x06, 0x49), Color.fromARGB(0xff, 0x7e, 0, 0x23) ];
/// [List] of stops for the lerping to occur. Future<void> _rebuildMarkers() async {
/// @see [_colorizeValue] List<DataPacket> packets = await DatabaseHandler().getLastPackets(200);
List<double> _stops = [0, 12, 35.4, 55.4, 150.4, 250.4, 500.4]; packets.forEach((DataPacket packet) {
switch (_pmSymbol) {
case PMSymbol.PM1:
this.addToList(this._currentPos, packet.pm1(), 200);
break;
case PMSymbol.PM1:
this.addToList(this._currentPos, packet.pm2_5(), 200);
break;
case PMSymbol.PM4:
this.addToList(this._currentPos, packet.pm4(), 200);
break;
case PMSymbol.PM10:
this.addToList(this._currentPos, packet.pm10(), 200);
break;
default:
break;
}
});
}
List<CircleMarker> _pmMarkers = <CircleMarker>[];
/// [Singleton]() instantiation factory. /// [Singleton]() instantiation factory.
factory MapDisplayController() => _singleton; factory MapDisplayController() => _singleton;
/// @Getter for [_polylines]. /// @Getter for [_polylines].
List<Polyline> polylines() => this._polylines; List<Polyline> get polylines => this._polylines;
/// @Getter for [_marker]. /// @Getter for [_marker].
Marker marker() => this._marker != null ? this._marker : _buildLocationMarker(LatLng(0, 0)); Marker get marker => this._marker != null ? this._marker : _buildLocationMarker(LatLng(0, 0));
/// Object used to calculate the real-life distance of two [Position]s /// Object used to calculate the real-life distance of two [Position]s
final Distance _distance = new Distance(); final Distance _distance = new Distance();
// TODO Faster to rebuild than to store ?
List<CircleMarker> pmMarkers() => this._pmMarkers[_pmKey[_pmKeyIndex]];
List<String> _pmKey = ["PM1", "PM2_5", "PM4", "PM10"]; List<CircleMarker> get pmMarkers => this._pmMarkers;
int _pmKeyIndex = 1; PMSymbol _pmSymbol = PMSymbol.PM2_5;
void setPMKeyIndex(PMSymbol value) {
print('PM Index $value gives ${value.key}');
this._pmSymbol = value;
this._rebuildMarkers();
}
void setPMKeyIndex(int value) { print('PM Index $value gives ${_pmKey[value]}'); this._pmKeyIndex = value; } void addToList(LatLng position, double value, int maxPoints) {
if (this._pmMarkers.length > maxPoints)
this._pmMarkers.removeRange(0, this._pmMarkers.length - 200);
this._pmMarkers.add(_buildPMMarker(position, value));
}
/// This function is executed on first initialization of the [MapDisplayController] /// This function is executed on first initialization of the [MapDisplayController]
/// TODO: Throttle to 1 in 5 updates for efficiency ? /// TODO: Throttle to 1 in 5 updates for efficiency ?
...@@ -73,6 +96,7 @@ class MapDisplayController { ...@@ -73,6 +96,7 @@ class MapDisplayController {
this._controller = new MapController(); this._controller = new MapController();
/// This function is hooked to the [PositionHandler]s progress, ie. when a new [Position] is acquired (once per second), it is broadcasted across the app and can be listened to. /// This function is hooked to the [PositionHandler]s progress, ie. when a new [Position] is acquired (once per second), it is broadcasted across the app and can be listened to.
// TODO Fix this to allow external data (bounded map data)
PositionHandler().getCurrentOrLastPosition().listen((posChanged) { PositionHandler().getCurrentOrLastPosition().listen((posChanged) {
if (posChanged == null) if (posChanged == null)
return; return;
...@@ -109,10 +133,26 @@ class MapDisplayController { ...@@ -109,10 +133,26 @@ class MapDisplayController {
this._polylines.last.points.add(this._currentPos); this._polylines.last.points.add(this._currentPos);
if (latest != null) { if (latest != null) {
if (latest.pm1() != null) this._pmMarkers['PM1'].add(_buildPMMarker(this._currentPos, latest.pm1())); switch (_pmSymbol) {
if (latest.pm2_5() != null) this._pmMarkers['PM2_5'].add(_buildPMMarker(this._currentPos, latest.pm2_5())); case PMSymbol.PM1:
if (latest.pm4() != null) this._pmMarkers['PM4'].add(_buildPMMarker(this._currentPos, latest.pm4())); if (latest.pm1() != null)
if (latest.pm10() != null) this._pmMarkers['PM10'].add(_buildPMMarker(this._currentPos, latest.pm10())); this.addToList(this._currentPos, latest.pm1(), 200);
break;
case PMSymbol.PM2_5:
if (latest.pm2_5() != null)
this.addToList(this._currentPos, latest.pm2_5(), 200);
break;
case PMSymbol.PM4:
if (latest.pm4() != null)
this.addToList(this._currentPos, latest.pm4(), 200);
break;
case PMSymbol.PM10:
if (latest.pm10() != null)
this.addToList(this._currentPos, latest.pm10(), 200);
break;
default:
break;
}
} }
} }
}); });
...@@ -121,8 +161,10 @@ class MapDisplayController { ...@@ -121,8 +161,10 @@ class MapDisplayController {
// TODO Set user defined duration // TODO Set user defined duration
// TODO Set user defined radius // TODO Set user defined radius
new Timer.periodic(Duration(minutes: 1), (Timer t) { new Timer.periodic(Duration(minutes: 1), (Timer t) {
if (this._controller != null && this._controller.bounds != null) if (this._controller != null && this._controller.bounds != null) {
print('${this._controller.bounds.southWest}, ${this._controller.bounds.northEast}');
NetworkHandler().getProximityPoints(DataHeader().deviceID, this._controller.bounds); NetworkHandler().getProximityPoints(DataHeader().deviceID, this._controller.bounds);
}
}); });
} }
...@@ -144,14 +186,15 @@ class MapDisplayController { ...@@ -144,14 +186,15 @@ class MapDisplayController {
} }
/// Make the map respond to automatic events once more. /// Make the map respond to automatic events once more.
/// TODO Possibly remove the one tick delay.
void setControllerAutomatic() => this._manualMotion = false; void setControllerAutomatic() => this._manualMotion = false;
/// Enables the user to have a controlled zoom in, zoom out experience. /// Enables the user to have a controlled zoom in, zoom out experience.
void setControllerZoom(bool zoomIn) { void setControllerZoom(bool zoomIn) {
/// Bound the zoomability. /// Bound the zoomability.
if (zoomIn && this._zoom <= 19) this._zoom += 0.5; if (zoomIn && this._zoom <= 19)
else if (!zoomIn && this._zoom >= 1) this._zoom -= 0.5; this._zoom += 0.5;
else if (!zoomIn && this._zoom >= 1)
this._zoom -= 0.5;
this._controller.move(this.controller().center, this._zoom); this._controller.move(this.controller().center, this._zoom);
} }
...@@ -164,21 +207,6 @@ class MapDisplayController { ...@@ -164,21 +207,6 @@ class MapDisplayController {
} }
} }
/// Gets the lerped [Color] value at a for a given PM value.
Color _colorizeValue(double pmValue) {
for (int stop = 0; stop < _stops.length - 1; stop++) {
final double left = _stops[stop], right = _stops[stop + 1];
final Color colorLeft = _colors[stop], colorRight = _colors[stop + 1];
if (pmValue <= left)
return colorLeft;
else if (pmValue < right) {
final section = (pmValue - left) / (right - left);
return Color.lerp(colorLeft, colorRight, section);
}
}
return _colors.last;
}
/// Wrapper function to build a [Marker], with default values. /// Wrapper function to build a [Marker], with default values.
Marker _buildLocationMarker(LatLng position) => Marker( Marker _buildLocationMarker(LatLng position) => Marker(
width: 20.0, width: 20.0,
...@@ -189,12 +217,12 @@ class MapDisplayController { ...@@ -189,12 +217,12 @@ class MapDisplayController {
), ),
); );
/// TODO MAKE PM Value be switchable.
CircleMarker _buildPMMarker(LatLng position, double pmValue) { CircleMarker _buildPMMarker(LatLng position, double pmValue) {
return CircleMarker( return CircleMarker(
radius: 8, radius: 8,
point: position, point: position,
color: _colorizeValue(pmValue).withAlpha(0xff), color: this._pmSymbol.colorizeValue(pmValue).withAlpha(0xff),
borderStrokeWidth: 1, borderStrokeWidth: 1,
borderColor: Colors.blue[300] borderColor: Colors.blue[300]
); );
......
...@@ -25,6 +25,7 @@ class DataHeader { ...@@ -25,6 +25,7 @@ class DataHeader {
/// Retrieve the deviceID from the header /// Retrieve the deviceID from the header
String get deviceID => (this._headerData != null && this._headerData.length > 0) ? this._headerData[0] : ''; String get deviceID => (this._headerData != null && this._headerData.length > 0) ? this._headerData[0] : '';
String get url => (this._headerData != null && this._headerData.length > 0) ? this._headerData[1] : '';
/// @Getter for _headerSet; /// @Getter for _headerSet;
bool get headerSet => this._headerSet; bool get headerSet => this._headerSet;
......
...@@ -56,8 +56,8 @@ class DataPacket { ...@@ -56,8 +56,8 @@ class DataPacket {
this._speed = (acquiredData[4] != '') ? parseDoubleWrapper(acquiredData[4]) : null; this._speed = (acquiredData[4] != '') ? parseDoubleWrapper(acquiredData[4]) : null;
this._temperature = (acquiredData[5] != '') ? parseDoubleWrapper(acquiredData[5]) : null; this._temperature = (acquiredData[5] != '') ? parseDoubleWrapper(acquiredData[5]) : null;
this._pressure = (acquiredData[6] != '') ? parseDoubleWrapper(acquiredData[6]) : null; this._relativeHumidity = (acquiredData[6] != '') ? parseDoubleWrapper(acquiredData[6]) : null;
this._relativeHumidity = (acquiredData[7] != '') ? parseDoubleWrapper(acquiredData[7]) : null; this._pressure = (acquiredData[7] != '') ? parseDoubleWrapper(acquiredData[7]) : null;
this._pm1 = (acquiredData[8] != '') ? parseDoubleWrapper(acquiredData[8]) : null; this._pm1 = (acquiredData[8] != '') ? parseDoubleWrapper(acquiredData[8]) : null;
this._pm2_5 = (acquiredData[9] != '') ? parseDoubleWrapper(acquiredData[9]) : null; this._pm2_5 = (acquiredData[9] != '') ? parseDoubleWrapper(acquiredData[9]) : null;
......
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_blue/flutter_blue.dart'; import 'package:flutter_blue/flutter_blue.dart';
import 'package:kotlin_flavor/scope_functions.dart'; import 'package:kotlin_flavor/scope_functions.dart';
import 'package:logair_application/logic/handlers/preference_handler.dart'; import 'package:logair_application/logic/handlers/preference_handler.dart';
...@@ -144,7 +145,12 @@ class BTLEHandler { ...@@ -144,7 +145,12 @@ class BTLEHandler {
/// Wrapper function that aggregates all of the steps to connect to a [BluetoothDevice] and stream its data. /// Wrapper function that aggregates all of the steps to connect to a [BluetoothDevice] and stream its data.
Future<void> _asyncConnectionProcess() async { Future<void> _asyncConnectionProcess() async {
await this._device.connect(); try {
await this._device.connect();
} on PlatformException {
await this._device.disconnect();
await this._device.connect();
}
if (Platform.isAndroid) { if (Platform.isAndroid) {
/// Android can negotiate greater Message Transmission Units. /// Android can negotiate greater Message Transmission Units.
/// 517 is the maximum defined in the BLE specification, the request will assign the largest available MTU instead. /// 517 is the maximum defined in the BLE specification, the request will assign the largest available MTU instead.
......
...@@ -2,11 +2,15 @@ import 'package:logair_application/logic/handlers/bluetooth_le_handler.dart'; ...@@ -2,11 +2,15 @@ import 'package:logair_application/logic/handlers/bluetooth_le_handler.dart';
import 'package:logair_application/logic/data_header.dart'; import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/logic/data_packet.dart'; import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/logic/handlers/database_handler.dart'; import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/logic/handlers/network_handler.dart';
class DataHandler { class DataHandler {
factory DataHandler() => _singleton; factory DataHandler() => _singleton;
DataHandler._internal(); DataHandler._internal() {
NetworkHandler();
}
static const HEADER_SIZE = 11; static const HEADER_SIZE = 11;
static final DataHandler _singleton = new DataHandler._internal(); static final DataHandler _singleton = new DataHandler._internal();
...@@ -54,14 +58,14 @@ class DataHandler { ...@@ -54,14 +58,14 @@ class DataHandler {
DataHeader().setHeader(packet); DataHeader().setHeader(packet);
/// Retroactively add all previous packets /// Retroactively add all previous packets
if (!wasSet && DataHeader().headerSet) if (!wasSet && DataHeader().headerSet)
_sortedData.forEach((packet) => DatabaseHandler().insertData(packet, DataHeader().deviceID)); _sortedData.forEach((packet) => DatabaseHandler().insertData(packet, DataHeader().deviceID, DataHeader().url));
/// Or if it is a [DataPacket] instance. /// Or if it is a [DataPacket] instance.
} else if (packet[0] == 91) { } else if (packet[0] == 91) {
_sortedData.add(DataPacket(packet)); _sortedData.add(DataPacket(packet));
print('PACKET ${String.fromCharCodes(packet)}'); print('PACKET ${String.fromCharCodes(packet)}');
if (DataHeader().headerSet) if (DataHeader().headerSet)
DatabaseHandler().insertData(_sortedData.last, DataHeader().deviceID); DatabaseHandler().insertData(_sortedData.last, DataHeader().deviceID, DataHeader().url);
} }
} }
/// TODO Used for debugging, later /// TODO Used for debugging, later
...@@ -70,12 +74,8 @@ class DataHandler { ...@@ -70,12 +74,8 @@ class DataHandler {
} }
/// Get the entirety of the [DataPacket]s. /// Get the entirety of the [DataPacket]s.
/// TODO Caching
List<DataPacket> getData() => _sortedData; List<DataPacket> getData() => _sortedData;
/// Get a subrange of [DataPacket]s.
List<DataPacket> getDataRange(int range) => (range > _sortedData.length) ? _sortedData : _sortedData.sublist(0, range);
/// Remove the entirety of the acquired data. /// Remove the entirety of the acquired data.
void clearData() { void clearData() {
_sortedData.removeWhere((element) => true); _sortedData.removeWhere((element) => true);
...@@ -83,7 +83,8 @@ class DataHandler { ...@@ -83,7 +83,8 @@ class DataHandler {
/// Remove the first element of the [DataPacket]s. Used for surgical removal. /// Remove the first element of the [DataPacket]s. Used for surgical removal.
void pop() { void pop() {
_sortedData.removeAt(0); if (_sortedData.isNotEmpty)
_sortedData.removeAt(0);
} }
/// Get the latest [DataPacket] /// Get the latest [DataPacket]
...@@ -93,13 +94,15 @@ class DataHandler { ...@@ -93,13 +94,15 @@ class DataHandler {
Stream<DataPacket> getDataStream() async* { Stream<DataPacket> getDataStream() async* {
while(true) { while(true) {
yield (_sortedData.length > 0) ? _sortedData.last : null; yield (_sortedData.length > 0) ? _sortedData.last : null;
await Future.delayed(Duration(milliseconds: 500)); await Future.delayed(Duration(seconds: 1));
} }
} }
@deprecated Stream<DataPacket> getMockDataStream() async* {
/// Used to monitor the amount of unsent [DataPacket]s. while(true) {
Stream<int> getUnsentPacketsLength() async* { yield new DataPacket.fromData(1597957282000, 5, 42, 420, 3.0, 32, 24, 10123, 32, null, 5, null, 9, "");
await Future.delayed(Duration(seconds: 1));
}
} }
void dispose() { void dispose() {
......
import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/logic/data_packet.dart'; import 'package:logair_application/logic/data_packet.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
...@@ -12,68 +11,53 @@ class DatabaseHandler { ...@@ -12,68 +11,53 @@ class DatabaseHandler {
bool shouldCache; bool shouldCache;
final String createString = '''CREATE TABLE data(
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT,
url TEXT,
timestamp_nix INTEGER,
latitude REAL,
longitude REAL,
altitude INTEGER,
speed REAL,
heading INTEGER,
temperature FLOAT,
relative_humidity FLOAT,
pressure FLOAT,
pm_1 FLOAT,
pm_2_5 FLOAT,
pm_4 FLOAT,
pm_10 FLOAT,
extra TEXT,
exported TINYINT DEFAULT 0
)''';
Future<Database> get _database async { Future<Database> get _database async {
if (_db == null) if (_db == null)
this._db = openDatabase( this._db = openDatabase(
join((await getDatabasesPath()), 'logair_data.db'), join((await getDatabasesPath()), 'logair_data.db'),
version: 1, version: 2,
onCreate: (db, version) => db.execute('''CREATE TABLE data( onCreate: (db, version) => db.execute(createString)
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT,
timestamp_nix INTEGER,
latitude REAL,
longitude REAL,
altitude INTEGER,
speed REAL,
heading INTEGER,
temperature FLOAT,
relative_humidity FLOAT,
pressure FLOAT,
pm_1 FLOAT,
pm_2_5 FLOAT,
pm_4 FLOAT,
pm_10 FLOAT,
extra TEXT,
exported TINYINT DEFAULT 0
)''')
); );
return this._db; return this._db;
} }
// TODO REMOVE !
Future<void> recreateDB() async { Future<void> recreateDB() async {
final Database database = await _database; final Database database = await _database;
await database.rawQuery('DROP TABLE IF EXISTS data'); await database.rawQuery('DROP TABLE IF EXISTS data');
await database.rawQuery('''CREATE TABLE data( await database.rawQuery(createString);
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT,
timestamp_nix INTEGER,
latitude REAL,
longitude REAL,
altitude INTEGER,
speed REAL,
heading INTEGER,
temperature FLOAT,
relative_humidity FLOAT,
pressure FLOAT,
pm_1 FLOAT,
pm_2_5 FLOAT,
pm_4 FLOAT,
pm_10 FLOAT,
extra TEXT,
exported TINYINT DEFAULT 0
)''');
} }
DatabaseHandler._internal(); DatabaseHandler._internal();
static final DatabaseHandler _singleton = new DatabaseHandler._internal(); static final DatabaseHandler _singleton = new DatabaseHandler._internal();
Future<void> insertData(DataPacket packet, String deviceId) async { Future<void> insertData(DataPacket packet, String deviceId, String url) async {
final Database database = await _database; final Database database = await _database;
Map<String, dynamic> insert = packet.toMap(); Map<String, dynamic> insert = packet.toMap();
insert['device_id'] = deviceId; insert['device_id'] = deviceId;
insert['url'] = url;
await database.insert( await database.insert(
'data', 'data',
insert, insert,
...@@ -89,14 +73,12 @@ class DatabaseHandler { ...@@ -89,14 +73,12 @@ class DatabaseHandler {
return List.generate(data.length, (i) => DataPacket.fromMappedData(data[i])); return List.generate(data.length, (i) => DataPacket.fromMappedData(data[i]));
} }
Future<int> deleteAll() async => await (await this._database).delete('data');
Future<void> close() async => (await this._database).close(); Future<void> close() async => (await this._database).close();
Stream<int> getDBSize() async* { Stream<int> getDBSize() async* {
final Database database = await _database; final Database database = await _database;
while (true) { while (true) {
final List<Map<String, dynamic>> data = await database.rawQuery('SELECT COUNT(*) AS count FROM data'); final List<Map<String, dynamic>> data = await database.query('data', columns: ['COUNT(*) AS count']);
yield data[0]['count'] ?? 0; yield data[0]['count'] ?? 0;
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
} }
...@@ -116,10 +98,12 @@ class DatabaseHandler { ...@@ -116,10 +98,12 @@ class DatabaseHandler {
return List.generate(data.length