From 2c04e4415123120b81c2b1ca514b39c1e36a220e Mon Sep 17 00:00:00 2001 From: Maximilian Betz <Maximilian.Betz@awi.de> Date: Thu, 10 Mar 2022 15:41:01 +0100 Subject: [PATCH] properlly closing the GNSS data stream when leaving add event widget --- lib/addevent.dart | 424 ++++++++++++++++++++++------------------- lib/configuration.dart | 30 ++- lib/datamodel.dart | 4 +- lib/main.dart | 30 +-- 4 files changed, 268 insertions(+), 220 deletions(-) diff --git a/lib/addevent.dart b/lib/addevent.dart index d3e61c5..66fe92b 100644 --- a/lib/addevent.dart +++ b/lib/addevent.dart @@ -15,47 +15,19 @@ class AddEvent extends StatefulWidget { class _AddEventPageState extends State<AddEvent> { bool syncGNSSData = true; - late LocationPermission permission; - late Position position; late String long = ""; late String lat = ""; late String alt = ""; late double accuracy = 0.0; - late StreamSubscription<Position> positionStream; - LocationSettings locationSettings = const LocationSettings( - accuracy: LocationAccuracy.high, - distanceFilter: 0, - ); + late StreamSubscription<Position> streamHandler; - getLocation() async { - StreamSubscription<Position> positionStream = Geolocator.getPositionStream( - locationSettings: locationSettings).listen((Position position) { - debugPrint('Get Location: Lat:' + position.latitude.toString() + - ' Long:' + position.longitude.toString() + - ' Alt:' + position.altitude.toString()); - - long = position.longitude.toString(); - lat = position.latitude.toString(); - alt = position.altitude.toString(); - accuracy = position.accuracy; - - - if (syncGNSSData == true) { - if(mounted){ - setState(() { - //refresh UI on update - });} - }; - }); - } - - startGNSS() async { + Future startGNSS() async { debugPrint("Check Location Permission"); bool serviceStatus = false; bool hasPermission = false; serviceStatus = await Geolocator.isLocationServiceEnabled(); if(serviceStatus){ - permission = await Geolocator.checkPermission(); + LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); @@ -69,15 +41,34 @@ class _AddEventPageState extends State<AddEvent> { }else{ hasPermission = true; } - if(hasPermission){ debugPrint('Location permissions granted'); if(mounted){ setState(() { //refresh the UI - });}; + });} + debugPrint('Starting location stream'); + streamHandler = Geolocator.getPositionStream( + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 0, + )).listen((Position position) { + debugPrint('Get Location: Lat:' + position.latitude.toString() + + ' Long:' + position.longitude.toString() + + ' Alt:' + position.altitude.toString()); + + long = position.longitude.toString(); + lat = position.latitude.toString(); + alt = position.altitude.toString(); + accuracy = position.accuracy; - getLocation(); + if (syncGNSSData == true) { + if(mounted){ + setState(() { + //refresh UI on update + });} + } + }); } }else{ debugPrint("GPS Service is not enabled, turn on GPS location"); @@ -91,9 +82,24 @@ class _AddEventPageState extends State<AddEvent> { @override void initState() { startGNSS(); + super.initState(); } + @override + void dispose() { + + try { + streamHandler.cancel(); + debugPrint('Cancel location stream'); + }catch(e){ + debugPrint('Canceling location stream failed'); + } + + super.dispose(); + //TODO: only if initialized positionStream.cancel(); + } + void _storeCurrentEvent() { final EventStoreInstance eventsStore = EventStoreInstance(); eventsStore.currentEvent.status = "PENDING"; @@ -122,8 +128,6 @@ class _AddEventPageState extends State<AddEvent> { final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); String gnssStatusText = ""; - final formKey = GlobalKey<FormState>(); //key for form - if (true == syncGNSSData){ // Update current event coordinates from GNSS stream @@ -146,183 +150,205 @@ class _AddEventPageState extends State<AddEvent> { gnssStatusText = "GNSS Disabled"; // Just display existing event coordinates } - return Scaffold( - appBar: AppBar(title: const Text("Add Event")), - body: - Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: <Widget>[ - TextFormField( - initialValue: eventsStore.currentEvent.label, - autovalidateMode: AutovalidateMode.onUserInteraction, - decoration: const InputDecoration( - labelText: 'Label', - errorText: 'Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :' - ), - onChanged: (value) { - eventsStore.currentEvent.label = value; - }, - validator: (value){ - if(!RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(value!)){ - return "Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :"; - }else{ - eventsStore.currentEvent.label = value; - return ''; // Entered Text is valid - } - }, - - ), - DropdownButtonFormField( - value: eventsStore.currentEvent.type, + if (configuration.initialized == true) { + return Scaffold( + appBar: AppBar(title: const Text("Add Event")), + body: + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + TextFormField( + initialValue: eventsStore.currentEvent.label, + autovalidateMode: AutovalidateMode.onUserInteraction, decoration: const InputDecoration( - labelText: 'Event Type', + labelText: 'Label', + errorText: 'Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :' ), - items: - configuration.eventTypes.map((EventType event) { - return DropdownMenuItem( - value: event.name, - child: Text(event.name), - ); - }).toList(), onChanged: (value) { - eventsStore.currentEvent.type = value.toString(); - } - ), - DropdownButtonFormField( - value: eventsStore.currentEvent.urn, + eventsStore.currentEvent.label = value; + }, + validator: (value) { + if (!RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch( + value!)) { + return "Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :"; + } else { + eventsStore.currentEvent.label = value; + return ''; // Entered Text is valid + } + }, + ), + DropdownButtonFormField( + value: eventsStore.currentEvent.type, + isExpanded: true, + decoration: const InputDecoration( + labelText: 'Event Type', + ), + items: + configuration.eventTypes.map((EventType event) { + return DropdownMenuItem( + value: event.name, + child: Text(event.name), + ); + }).toList(), + onChanged: (value) { + eventsStore.currentEvent.type = value.toString(); + } + ), + DropdownButtonFormField( + value: eventsStore.currentEvent.urn, + isExpanded: true, + decoration: const InputDecoration( + labelText: 'URN', + ), + items: + configuration.devices.map((Device device) { + return DropdownMenuItem( + value: device.urn, + child: Text(device.urn), + ); + }).toList(), + onChanged: (value) { + eventsStore.currentEvent.urn = value.toString(); + eventsStore.currentEvent.id = + configuration.getDeviceIdFromUrn(value.toString()); + } + ), + TextFormField( + initialValue: eventsStore.currentEvent.description, decoration: const InputDecoration( - labelText: 'URN', + labelText: 'Description' ), - items: - configuration.devices.map((Device device) { - return DropdownMenuItem( - value: device.urn, - child: Text(device.urn), - ); - }).toList(), onChanged: (value) { - eventsStore.currentEvent.urn = value.toString(); - eventsStore.currentEvent.id = configuration.getDeviceIdFromUrn(value.toString()); - } - ), - TextFormField( - initialValue: eventsStore.currentEvent.description, - decoration: const InputDecoration( - labelText: 'Description' + eventsStore.currentEvent.description = value; + }, ), - onChanged: (value) { - eventsStore.currentEvent.description = value; - }, - ), - Row( - mainAxisSize: MainAxisSize.min, - children: <Widget>[ - Flexible(child: - TextFormField( - controller: TextEditingController(text: eventsStore.currentEvent.startDate), - readOnly: true, - decoration: const InputDecoration( - labelText: 'Timestamp', - //helperText: '', //Adds some space below field - border: OutlineInputBorder(), + Row( + mainAxisSize: MainAxisSize.min, + children: <Widget>[ + Flexible(child: + TextFormField( + controller: TextEditingController( + text: eventsStore.currentEvent.startDate), + readOnly: true, + decoration: const InputDecoration( + labelText: 'Timestamp', + //helperText: '', //Adds some space below field + border: OutlineInputBorder(), + ), + onTap: () { + DatePicker.showDateTimePicker(context, + showTitleActions: true, + onConfirm: (date) { + //Only one field for start and end date. + eventsStore.currentEvent.startDate = '$date'; + eventsStore.currentEvent.endDate = '$date'; + debugPrint('Date set to : $date'); + setState(() {}); + }, + currentTime: DateTime.now().toUtc(), + locale: LocaleType.en); + }, + ), + ), + ElevatedButton( + onPressed: () { + var date = DateTime.now().toUtc(); + eventsStore.currentEvent.startDate = '$date'; + eventsStore.currentEvent.endDate = '$date'; + debugPrint('Date set to : ' + + eventsStore.currentEvent.endDate.toString()); + setState(() {}); + }, + child: Text('Now'), ), - onTap: () { - DatePicker.showDateTimePicker(context, - showTitleActions: true, - onConfirm: (date) { - //Only one field for start and end date. - eventsStore.currentEvent.startDate = '$date'; - eventsStore.currentEvent.endDate = '$date'; - debugPrint('Date set to : $date'); - setState(() { - }); - }, - currentTime: DateTime.now().toUtc(), - locale: LocaleType.en); - }, + ] + ), + TextFormField( + readOnly: false, + enabled: !syncGNSSData, + controller: TextEditingController( + text: eventsStore.currentEvent.latitude.toString()), + decoration: const InputDecoration( + labelText: 'Latitude', + border: OutlineInputBorder(), ), + onChanged: (value) { + eventsStore.currentEvent.latitude = value; + } + ), + TextFormField( + readOnly: false, + enabled: !syncGNSSData, + controller: TextEditingController( + text: eventsStore.currentEvent.longitude.toString()), + decoration: const InputDecoration( + labelText: 'Longitude', + border: OutlineInputBorder(), + ), - ElevatedButton( - onPressed: () { - var date = DateTime.now().toUtc(); - eventsStore.currentEvent.startDate = '$date'; - eventsStore.currentEvent.endDate = '$date'; - debugPrint('Date set to : ' + eventsStore.currentEvent.endDate.toString()); - setState(() { - }); - }, - child: Text('Now'), + onChanged: (value) { + eventsStore.currentEvent.longitude = value; + } + ), + TextFormField( + readOnly: false, + enabled: !syncGNSSData, + controller: TextEditingController( + text: eventsStore.currentEvent.elevation.toString()), + decoration: const InputDecoration( + labelText: 'Elevation', + border: OutlineInputBorder(), ), - ] - ), - TextFormField( - readOnly: false, - enabled: !syncGNSSData, - controller: TextEditingController(text: eventsStore.currentEvent.latitude.toString()), - decoration: const InputDecoration( - labelText: 'Latitude', - border: OutlineInputBorder(), - ), - onChanged: (value) { - eventsStore.currentEvent.latitude = value; - } - ), - TextFormField( - readOnly: false, - enabled: !syncGNSSData, - controller: TextEditingController(text: eventsStore.currentEvent.longitude.toString()), - decoration: const InputDecoration( - labelText: 'Longitude', - border: OutlineInputBorder(), + onChanged: (value) { + eventsStore.currentEvent.elevation = value; + } + ), + ]), + bottomNavigationBar: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text(gnssStatusText), + const SizedBox(width: 10), + Switch( + value: syncGNSSData, + onChanged: (value) { + syncGNSSData = value; + debugPrint('Switched to:' + syncGNSSData.toString()); - ), - onChanged: (value) { - eventsStore.currentEvent.longitude = value; - } + setState(() { + //refresh the UI + }); + }, ), - TextFormField( - readOnly: false, - enabled: !syncGNSSData, - controller: TextEditingController(text: eventsStore.currentEvent.elevation.toString()), - decoration: const InputDecoration( - labelText: 'Elevation', - border: OutlineInputBorder(), - ), - onChanged: (value) { - eventsStore.currentEvent.elevation = value; - } + const SizedBox(width: 50), + FloatingActionButton( + heroTag: null, + onPressed: () { + _storeCurrentEvent(); + HapticFeedback.vibrate(); + }, + tooltip: 'Add Event', + child: const Icon(Icons.check), ), - ]), - bottomNavigationBar: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text(gnssStatusText), - const SizedBox(width: 10), - Switch( - value: syncGNSSData, - onChanged: (value) { - syncGNSSData = value; - debugPrint('Switched to:' + syncGNSSData.toString()); - - setState(() { - //refresh the UI - }); - }, - ), - const SizedBox(width: 50), - FloatingActionButton( - heroTag: null, - onPressed: () { - _storeCurrentEvent(); - HapticFeedback.vibrate(); - }, - tooltip: 'Add Event', - child: const Icon(Icons.check), - ), - const SizedBox(width: 20), - ], - ), - ); + const SizedBox(width: 20), + ], + ), + ); + }else { + return Scaffold( + appBar: AppBar(title: const Text("Add Event")), + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.end, + children: const <Widget>[ + Text( + ' Visit Configuration first.', + style: TextStyle(fontSize: 20) + ), + ], + ), + ); + } } } diff --git a/lib/configuration.dart b/lib/configuration.dart index 52ca2f6..e5903fd 100644 --- a/lib/configuration.dart +++ b/lib/configuration.dart @@ -50,7 +50,7 @@ Future<List<Device>> updateDevices(int collectionId) async { String url = 'https://sandbox.sensor.awi.de/rest/sensors/collections/getItemsOfCollection/' + collectionId.toString(); // Get Access to local device and current event store. - EventStoreInstance eventsStore = EventStoreInstance(); + EventStoreInstance events = EventStoreInstance(); ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); List<Device> collectionDevices = []; @@ -72,8 +72,17 @@ Future<List<Device>> updateDevices(int collectionId) async { /*Update to local device store*/ configuration.devices = collectionDevices; //Update id and urn for the add event widget - eventsStore.currentEvent.id = collectionDevices[0].id; - eventsStore.currentEvent.urn = collectionDevices[0].urn; + events.currentEvent.id = collectionDevices[0].id; + events.currentEvent.urn = collectionDevices[0].urn; + events.currentEvent.description = ''; + events.currentEvent.label = ''; + events.currentEvent.type = configuration.eventTypes[0].name; + + var date = DateTime.now().toUtc(); + events.currentEvent.startDate = '$date'; + events.currentEvent.endDate = '$date'; + configuration.initialized = true; + return collectionDevices; } else { throw Exception('Failed to load Collection'); @@ -155,6 +164,7 @@ class _MyHomePageState extends State<Configuration> { Widget build(BuildContext context) { final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); + final EventStoreInstance events = EventStoreInstance(); return Scaffold( appBar: AppBar( @@ -235,17 +245,27 @@ class _MyHomePageState extends State<Configuration> { ), ), bottomNavigationBar: Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + FloatingActionButton.extended( + heroTag: null, + tooltip: 'Reset all data', + icon: const Icon(Icons.cancel), + label: const Text('Reset all'), + onPressed: () { + events.reset(); + configuration.reset(); + }, + ), FloatingActionButton.extended( heroTag: null, tooltip: 'Select Collection and download corresponding devices', icon: const Icon(Icons.save), label: const Text('Store'), onPressed: () { - updateDevices(configuration.currentCollection.id); login(); fetchEventTypes(); + updateDevices(configuration.currentCollection.id); HapticFeedback.vibrate(); }, ), diff --git a/lib/datamodel.dart b/lib/datamodel.dart index e21f5ee..a4f671d 100644 --- a/lib/datamodel.dart +++ b/lib/datamodel.dart @@ -146,6 +146,7 @@ abstract class ConfigurationStoreBase { List<Device> devices = []; List<EventType> eventTypes = []; SensorLogin loginInformation = SensorLogin('', '', ''); + bool initialized = false; Collection getCollectionFromName(String name) { for (var collection in collections) { @@ -179,7 +180,8 @@ abstract class ConfigurationStoreBase { devices = []; eventTypes = []; currentCollection = Collection(id: -1, description: '', collectionName: ''); - loginInformation = SensorLogin('', '', ''); + loginInformation = SensorLogin('admin', 'adminadmin', ''); + initialized = false; } } diff --git a/lib/main.dart b/lib/main.dart index 456ee90..08d841b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,19 +23,19 @@ void main() { // Add some dummy devices // TODO: load from shared preferences. // TODO: this shall be requested from sensor.awi.de after selecting a collection in configuration. - configuration.devices.add(Device.fromJson({'id': 8311, 'urn':'station:neumayer_iii:awi_snow_sampler_1'})); - configuration.devices.add(Device.fromJson({'id': 4086, 'urn':'acoustic_backscatter_sensor:test'})); - configuration.devices.add(Device.fromJson({'id': 1393, 'urn':'vessel:polarstern:hydrosweep_ds3'})); + //configuration.devices.add(Device.fromJson({'id': 8311, 'urn':'station:neumayer_iii:awi_snow_sampler_1'})); + //configuration.devices.add(Device.fromJson({'id': 4086, 'urn':'acoustic_backscatter_sensor:test'})); + //configuration.devices.add(Device.fromJson({'id': 1393, 'urn':'vessel:polarstern:hydrosweep_ds3'})); // Fill the textboxes in addevent with some usefull data. // TODO: store this in shared preferences so that last entered data reappears when restarting the app. - events.currentEvent.urn = 'acoustic_backscatter_sensor:test'; - events.currentEvent.id = 4086; - events.currentEvent.description = 'blabla'; - events.currentEvent.label = 'PS129_ABC'; - events.currentEvent.type = 'Deployment'; - events.currentEvent.startDate = '2022-03-09 15:06:00.000Z'; - events.currentEvent.endDate = '2022-03-09 15:08:00.000Z'; + //events.currentEvent.urn = 'vessel:polarstern:hydrosweep_ds3'; + //events.currentEvent.id = -1; + //events.currentEvent.description = ''; + //events.currentEvent.label = ''; + //events.currentEvent.type = 'Configuration'; + //events.currentEvent.startDate = '2022-03-09 15:06:00.000Z'; + //events.currentEvent.endDate = '2022-03-09 15:08:00.000Z'; //Add some dummy events to event store. Just development purpose. TODO: Remove after development. @@ -83,11 +83,11 @@ void main() { //Add some dummy eventtypes. // TODO: loard from shared preferences. // TODO: request from https://sensor.awi.de/rest/sensors/events/getAllEventTypes in configuration widget. - configuration.eventTypes.add(EventType.fromJson({'id': 317, 'name':'Configuration'})); - configuration.eventTypes.add(EventType.fromJson({'id': 216, 'name':'Decommissioned'})); - configuration.eventTypes.add(EventType.fromJson({'id': 187, 'name':'Deployment'})); - configuration.eventTypes.add(EventType.fromJson({'id': 50, 'name':'Information'})); - configuration.eventTypes.add(EventType.fromJson({'id': 16, 'name':'Maintenance'})); + //configuration.eventTypes.add(EventType.fromJson({'id': 317, 'generalName':'Configuration'})); + //configuration.eventTypes.add(EventType.fromJson({'id': 216, 'generalName':'Decommissioned'})); + //configuration.eventTypes.add(EventType.fromJson({'id': 187, 'generalName':'Deployment'})); + //configuration.eventTypes.add(EventType.fromJson({'id': 50, 'generalName':'Information'})); + //configuration.eventTypes.add(EventType.fromJson({'id': 16, 'generalName':'Maintenance'})); runApp(MaterialApp( title: 'Mobile Event Log', -- GitLab