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

Graphing, need temp displays and selected locality display

parent 069108fc
{"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"}
\ No newline at end of file
{"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-21 17:28:31.765660","version":"1.19.0-2.0.pre.49"}
\ No newline at end of file
......@@ -4,7 +4,6 @@ import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.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/network_handler.dart';
import 'package:logair_application/logic/data_header.dart';
......@@ -150,8 +149,10 @@ class MapDisplayController {
/// Enables the user to have a controlled zoom in, zoom out experience.
void setControllerZoom(bool zoomIn) {
/// Bound the zoomability.
if (zoomIn && this._zoom <= 19) this._zoom += 0.5;
else if (!zoomIn && this._zoom >= 1) this._zoom -= 0.5;
if (zoomIn && this._zoom <= 19)
this._zoom += 0.5;
else if (!zoomIn && this._zoom >= 1)
this._zoom -= 0.5;
this._controller.move(this.controller().center, this._zoom);
}
......
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:kotlin_flavor/scope_functions.dart';
import 'package:logair_application/logic/handlers/preference_handler.dart';
......@@ -144,7 +145,12 @@ class BTLEHandler {
/// Wrapper function that aggregates all of the steps to connect to a [BluetoothDevice] and stream its data.
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) {
/// 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.
......
......@@ -83,7 +83,8 @@ class DataHandler {
/// Remove the first element of the [DataPacket]s. Used for surgical removal.
void pop() {
_sortedData.removeAt(0);
if (_sortedData.isNotEmpty)
_sortedData.removeAt(0);
}
/// Get the latest [DataPacket]
......
......@@ -96,7 +96,7 @@ class DatabaseHandler {
Stream<int> getDBSize() async* {
final Database database = await _database;
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;
await Future.delayed(Duration(seconds: 5));
}
......@@ -118,8 +118,12 @@ class DatabaseHandler {
Future<void> setExported(List<int> ids) async {
final Database database = await _database;
print('UPDATE data SET exported = 1 WHERE id IN (${ids.join(', ')})');
int count = await database.rawUpdate('UPDATE data SET exported = 1 WHERE id IN (${ids.join(', ')})');
int count = await database.update(
'data',
{'exported': 1},
where: 'id IN (?)',
whereArgs: ['${ids.join(', ')}']
);
print('Exported $count');
}
......@@ -127,7 +131,11 @@ class DatabaseHandler {
Stream<int> getUnsentPacketsLength() async* {
final Database database = await _database;
while (true) {
final List<Map<String, dynamic>> data = await database.rawQuery('SELECT COUNT(*) AS count FROM data WHERE exported = 0');
final List<Map<String, dynamic>> data = await database.query('data',
columns: ['COUNT(*) AS count'],
where: 'exported = ?',
whereArgs: [0]
);
yield data[0]['count'] ?? 0;
await Future.delayed(Duration(seconds: 1));
}
......@@ -136,7 +144,9 @@ class DatabaseHandler {
Stream<Map<String, dynamic>> getLatest() async* {
final Database database = await _database;
while (true) {
final List<Map<String, dynamic>> data = await database.rawQuery('SELECT MIN(timestamp_nix) AS start, AVG(pm_1) AS pm_1, AVG(pm_2_5) AS pm_2_5, AVG(pm_4) AS pm_4, AVG(pm_10) AS pm_10 FROM (SELECT * FROM data ORDER BY id DESC LIMIT 5)');
// TODO Change query to timestamp - x time
final timestampLimit = DateTime.now().millisecondsSinceEpoch - 10000;
final List<Map<String, dynamic>> data = await database.rawQuery('SELECT MIN(timestamp_nix) AS start, AVG(pm_1) AS pm_1, AVG(pm_2_5) AS pm_2_5, AVG(pm_4) AS pm_4, AVG(pm_10) AS pm_10 FROM (SELECT * FROM data WHERE timestamp_nix >= $timestampLimit ORDER BY id DESC LIMIT 5)');
yield data[0] ?? Map();
await Future.delayed(Duration(seconds: 5));
}
......
......@@ -47,7 +47,10 @@ class _BluetoothSelectionDialogState extends State<BluetoothSelectionDialog> {
builder: (context, sizingInfo) => Container(
height: sizingInfo.screenSize.height * 0.9,
child: RefreshIndicator(
onRefresh: () => BTLEHandler().fblueInstance.startScan(),
onRefresh: () async {
await BTLEHandler().disconnect();
BTLEHandler().fblueInstance.startScan();
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.all(0),
......@@ -77,10 +80,9 @@ class _BluetoothSelectionDialogState extends State<BluetoothSelectionDialog> {
if (await BTLEHandler().fblueInstance.isScanning.first)
BTLEHandler().fblueInstance.stopScan();
try {
await BTLEHandler().disconnect();
BTLEHandler().fblueInstance.startScan(timeout: Duration(seconds: 10));
} catch (e) {
}
} catch (e) { }
},
),
),
......
......@@ -3,6 +3,7 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/ui/components/graph/graph_colors.dart';
import 'package:logair_application/ui/components/graph/graph_controller.dart';
import 'package:logair_application/ui/components/graph/indicator.dart';
/// WIP
......@@ -17,191 +18,150 @@ class Graph extends StatefulWidget {
}
class _GraphState extends State<Graph> {
_GraphState({Key key}) {
DatabaseHandler().getLatest().listen((newData) {
this.start = this.start ?? newData['start'].toDouble();
this.now = (newData['start'].toDouble() - this.start) / 1000;
print(newData);
if (newData['pm_1'] != null)
spotsPM1.add(FlSpot(this.now, newData['pm_1']));
if (newData['pm_2_5'] != null)
spotsPM2_5.add(FlSpot(this.now, newData['pm_2_5']));
if (newData['pm_4'] != null)
spotsPM4.add(FlSpot(this.now, newData['pm_4']));
if (newData['pm_10'] != null)
spotsPM10.add(FlSpot(this.now, newData['pm_10']));
GraphController().getGraphData().listen((Map<String, dynamic> newData) {
if (this.mounted) {
setState(() {
start = newData['start'];
spotsPM1 = newData['spotsPM1'];
spotsPM2_5 = newData['spotsPM2_5'];
spotsPM4 = newData['spotsPM4'];
spotsPM10 = newData['spotsPM10'];
max = newData['max'];
now = newData['now'];
});
}
});
}
int touchedIndex;
closeTo(double value, double reference, double delta) => reference - delta <= value && value <= reference + delta;
double start;
double now;
double max = 0;
static List<FlSpot> spotsPM1 = [FlSpot(0, 0)];
static List<FlSpot> spotsPM2_5 = [FlSpot(0, 0)];
static List<FlSpot> spotsPM4 = [FlSpot(0, 0)];
static List<FlSpot> spotsPM10 = [FlSpot(0, 0)];
List<FlSpot> spotsPM1 = [FlSpot(0, 0)];
List<FlSpot> spotsPM2_5 = [FlSpot(0, 0)];
List<FlSpot> spotsPM4 = [FlSpot(0, 0)];
List<FlSpot> spotsPM10 = [FlSpot(0, 0)];
final LineChartBarData linePM1 = LineChartBarData(
spots: spotsPM1,
generateLine(List<FlSpot> data, Color color) => LineChartBarData(
spots: data,
isCurved: false,
colors: [
GraphColors.PM1.color,
],
barWidth: 4,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
);
final LineChartBarData linePM2_5 = LineChartBarData(
spots: spotsPM2_5,
colors: [
GraphColors.PM2_5.color,
],
colors: [color],
barWidth: 4,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
);
final LineChartBarData linePM4 = LineChartBarData(
spots: spotsPM4,
colors: [
GraphColors.PM4.color,
],
barWidth: 4,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
);
final LineChartBarData linePM10 = LineChartBarData(
spots: spotsPM10,
colors: [
GraphColors.PM10.color,
],
barWidth: 4,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
dotData: FlDotData(show: false)
);
@override
Widget build(BuildContext context) {
return Row(
return Column(
children: [
Expanded(
flex: 2,
child: AspectRatio(
aspectRatio: 1.5,
child: Padding(
padding: EdgeInsets.only(top: 10, bottom: 4, right: 4),
child: LineChart(
LineChartData(
lineTouchData: LineTouchData(
touchCallback: (lineTouchResponse) => {
print(lineTouchResponse.lineBarSpots.map((e) => e.props))
AspectRatio(
aspectRatio: 2,
child: Padding(
padding: EdgeInsets.only(top: 10, right: 10),
child: LineChart(
LineChartData(
lineTouchData: LineTouchData(
touchCallback: (lineTouchResponse) => {
print(lineTouchResponse.lineBarSpots.map((e) => e.props))
},
handleBuiltInTouches: true,
),
gridData: FlGridData(
show: true,
drawHorizontalLine: true,
horizontalInterval: 5,
),
// Build from DataPacket would enable getting every 10 seconds add HH:mm:ss
titlesData: FlTitlesData(
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 22,
textStyle: const TextStyle(
color: Color(0xff72719b),
fontSize: 12,
),
margin: 15,
rotateAngle: -30,
getTitles: (value) {
DateTime dtValue = DateTime.fromMillisecondsSinceEpoch(
((value * 1000) + (start ?? DateTime.now().millisecondsSinceEpoch)).round()
);
if (dtValue.second % 20 == 0 && dtValue.millisecondsSinceEpoch < DateTime.now().millisecondsSinceEpoch)
return '${dtValue.hour}:${dtValue.minute}:${dtValue.second.toString().padLeft(2, '0')}';
return '';
},
handleBuiltInTouches: true,
),
gridData: FlGridData(
show: true,
),
// Build from DataPacket would enable getting every 10 seconds add HH:mm:ss
titlesData: FlTitlesData(
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 22,
textStyle: const TextStyle(
color: Color(0xff72719b),
fontSize: 12,
),
margin: 10,
getTitles: (value) {
if (value % 10 == 0)
return value.toString();
return '';
},
),
// TODO Set values for PM
leftTitles: SideTitles(
showTitles: true,
textStyle: const TextStyle(
color: Color(0xff75729e),
fontSize: 12,
),
getTitles: (value) {
if (value % 5 == 0)
return value.toString();
return '';
},
margin: 8,
reservedSize: 30,
// TODO Set values for PM
leftTitles: SideTitles(
showTitles: true,
interval: 10,
textStyle: const TextStyle(
color: Color(0xff75729e),
fontSize: 12,
),
getTitles: (value) {
if (value % 10 == 0)
return value.toString();
return '';
},
margin: 8,
reservedSize: 30,
),
borderData: FlBorderData(
border: const Border(
bottom: BorderSide(
color: Color(0xff4e4965),
width: 4,
),
),
borderData: FlBorderData(
border: const Border(
bottom: BorderSide(
color: Color(0xff4e4965),
width: 4,
),
),
minX: (now ?? 0) - 10,
maxX: (now ?? 0),
maxY: 10,
minY: 0,
clipToBorder: true,
lineBarsData: [
linePM1,
linePM2_5,
linePM4,
linePM10
],
),
minX: (now ?? 0) - 130,
maxX: (now ?? 0) + 10,
maxY: (this.max ?? 0) + 10,
minY: 0,
clipToBorder: true,
lineBarsData: [
generateLine(spotsPM1, GraphColors.PM1.color),
generateLine(spotsPM2_5, GraphColors.PM2_5.color),
generateLine(spotsPM4, GraphColors.PM4.color),
generateLine(spotsPM10, GraphColors.PM10.color),
],
axisTitleData: FlAxisTitleData(
show: true,
leftTitle: AxisTitle(
titleText: 'PM density µg/m³',
textStyle: TextStyle(
)
)
)
),
)
),
),
),
Expanded(
flex: 1,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Indicator(
color: GraphColors.PM1.color,
text: 'PM 1',
isSquare: true,
),
SizedBox(
height: 4,
),
Indicator(
color: GraphColors.PM2_5.color,
text: 'PM 2.5',
isSquare: true,
),
SizedBox(
height: 4,
),
Indicator(
color: GraphColors.PM4.color,
text: 'PM 4',
isSquare: true,
),
SizedBox(
height: 4,
),
Indicator(
color: GraphColors.PM10.color,
text: 'PM 10',
isSquare: true,
),
SizedBox(
height: 18,
),
],
)
)
SizedBox(
height: 4,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: GraphColors.list.map((GraphColors gc) =>
Indicator(
text: gc.text,
color: gc.color,
isSquare: true,
)
).toList()
),
SizedBox(
height: 10,
),
]
);
}
......
......@@ -8,10 +8,13 @@ class GraphColors {
const GraphColors._internal(this._key, this._value);
toString() => 'Enum.$_key ~> $_value';
String get text => _key;
Color get color => _value;
static const PM1 = const GraphColors._internal('PM1', Color(0xff0293ee));
static const PM2_5 = const GraphColors._internal('PM2.5', Color(0xfff8b250));
static const PM4 = const GraphColors._internal('PM4', Color(0xff845bef));
static const PM10 = const GraphColors._internal('PM10', Color(0xff13d38e));
static const PM1 = const GraphColors._internal('PM 1', Color(0xff0293ee));
static const PM2_5 = const GraphColors._internal('PM 2.5', Color(0xfff8b250));
static const PM4 = const GraphColors._internal('PM 4', Color(0xff845bef));
static const PM10 = const GraphColors._internal('PM 10', Color(0xff13d38e));
static List<GraphColors> get list => [PM1, PM2_5, PM4, PM10];
}
\ No newline at end of file
import 'package:fl_chart/fl_chart.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
class GraphController {
factory GraphController() => _singleton;
GraphController._internal() {
DatabaseHandler().getLatest().listen((newData) {
this.start = this.start ?? DateTime.now().millisecondsSinceEpoch * 1.0;
this.now = (DateTime.now().millisecondsSinceEpoch - this.start) / 1000;
this.max = [getMax(spotsPM1), getMax(spotsPM2_5), getMax(spotsPM4), getMax(spotsPM10)].reduce((curr, next) => curr > next? curr: next);
if (newData['start'] != null) {
spotsPM1 = appendDataPoint(this.now, newData['pm_1'], spotsPM1);
spotsPM2_5 = appendDataPoint(this.now, newData['pm_2_5'], spotsPM2_5);
spotsPM4 = appendDataPoint(this.now, newData['pm_4'], spotsPM4);
spotsPM10 = appendDataPoint(this.now, newData['pm_10'], spotsPM10);
}
});
}
static final GraphController _singleton = new GraphController._internal();
static List<FlSpot> spotsPM1 = [FlSpot(0, 0)];
static List<FlSpot> spotsPM2_5 = [FlSpot(0, 0)];
static List<FlSpot> spotsPM4 = [FlSpot(0, 0)];
static List<FlSpot> spotsPM10 = [FlSpot(0, 0)];
double max;
double now;
double start;
bool _displaying = false;
List<FlSpot> appendDataPoint(double timestamp, double datapoint, List<FlSpot> currentData) {
List<FlSpot> data = currentData.where((element) => true).toList();
if (datapoint != null)
data.add(FlSpot(timestamp, datapoint));
if (data.length > 50)
data.removeAt(0);
return data;
}
double getMax(List<FlSpot> data) => data.map((point) => point.y).reduce((curr, next) => curr > next? curr: next);
set displaying(bool isDisplaying) => this._displaying = isDisplaying;
bool get displaying => this._displaying;
Stream<Map<String, dynamic>> getGraphData() async* {
while (true) {
yield {
'now': now,
'start': start,
'spotsPM1': spotsPM1,
'spotsPM2_5': spotsPM2_5,
'spotsPM4': spotsPM4,
'spotsPM10': spotsPM10,
'max': max,
};
await Future.delayed(Duration(seconds: 1));
}
}
}
\ No newline at end of file
......@@ -85,7 +85,9 @@ class DataWidget extends StatelessWidget {
stream: PositionHandler().getCurrentOrLastPosition(),
builder: (context, snapshot) {
Position data = snapshot.data;
return Text('Latitude ${data.latitude} | Longitude ${data.longitude}');
if (data != null)
return Text('Latitude ${data.latitude} | Longitude ${data.longitude}');
else return Text('Latitude N/A | Longitude N/A');
},
),
],
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment