add 30 sec timeout for sonde reading stabilization

This commit is contained in:
ALim Aidrus 2025-10-04 21:34:37 +08:00
parent fc14740b01
commit 8931ed9297
7 changed files with 158 additions and 90 deletions

View File

@ -27,9 +27,9 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<!-- END: STORAGE PERMISSIONS --> <!-- END: STORAGE PERMISSIONS -->
<!-- MMS V4 1.2.06 --> <!-- MMS V4 1.2.08 -->
<application <application
android:label="MMS V4 1.2.07" android:label="MMS V4 1.2.08"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true"> android:requestLegacyExternalStorage="true">

View File

@ -80,7 +80,7 @@ class BluetoothManager {
} }
/// Starts a periodic timer that requests data automatically. /// Starts a periodic timer that requests data automatically.
void startAutoReading({Duration interval = const Duration(seconds: 5)}) { void startAutoReading({Duration interval = const Duration(seconds: 2)}) {
// Cancel any existing timer to prevent duplicates. // Cancel any existing timer to prevent duplicates.
stopAutoReading(); stopAutoReading();

View File

@ -34,6 +34,12 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
bool _isAutoReading = false; bool _isAutoReading = false;
StreamSubscription? _dataSubscription; StreamSubscription? _dataSubscription;
// --- START MODIFICATION: Countdown Timer State ---
Timer? _lockoutTimer;
int _lockoutSecondsRemaining = 30;
bool _isLockedOut = false;
// --- END MODIFICATION ---
// --- START FIX: Declare service variable --- // --- START FIX: Declare service variable ---
late final MarineInSituSamplingService _samplingService; late final MarineInSituSamplingService _samplingService;
// --- END FIX --- // --- END FIX ---
@ -85,6 +91,7 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
@override @override
void dispose() { void dispose() {
_dataSubscription?.cancel(); _dataSubscription?.cancel();
_lockoutTimer?.cancel(); // --- MODIFICATION: Cancel timer on dispose ---
// --- START FIX: Use the pre-fetched service instance --- // --- START FIX: Use the pre-fetched service instance ---
if (_samplingService.bluetoothConnectionState.value != BluetoothConnectionState.disconnected) { if (_samplingService.bluetoothConnectionState.value != BluetoothConnectionState.disconnected) {
@ -251,12 +258,40 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
); );
} }
// --- START MODIFICATION: Countdown Timer Logic ---
void _startLockoutTimer() {
_lockoutTimer?.cancel();
setState(() {
_isLockedOut = true;
_lockoutSecondsRemaining = 30;
});
_lockoutTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_lockoutSecondsRemaining > 0) {
if (mounted) {
setState(() {
_lockoutSecondsRemaining--;
});
}
} else {
timer.cancel();
if (mounted) {
setState(() {
_isLockedOut = false;
});
}
}
});
}
// --- END MODIFICATION ---
void _toggleAutoReading(String activeType) { void _toggleAutoReading(String activeType) {
final service = context.read<MarineInSituSamplingService>(); final service = context.read<MarineInSituSamplingService>();
setState(() { setState(() {
_isAutoReading = !_isAutoReading; _isAutoReading = !_isAutoReading;
if (_isAutoReading) { if (_isAutoReading) {
if (activeType == 'bluetooth') service.startBluetoothAutoReading(); else service.startSerialAutoReading(); if (activeType == 'bluetooth') service.startBluetoothAutoReading(); else service.startSerialAutoReading();
_startLockoutTimer(); // --- MODIFICATION: Start countdown
} else { } else {
if (activeType == 'bluetooth') service.stopBluetoothAutoReading(); else service.stopSerialAutoReading(); if (activeType == 'bluetooth') service.stopBluetoothAutoReading(); else service.stopSerialAutoReading();
} }
@ -272,8 +307,12 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
} }
_dataSubscription?.cancel(); _dataSubscription?.cancel();
_dataSubscription = null; _dataSubscription = null;
_lockoutTimer?.cancel(); // --- MODIFICATION: Cancel timer on disconnect ---
if (mounted) { if (mounted) {
setState(() => _isAutoReading = false); setState(() {
_isAutoReading = false;
_isLockedOut = false; // --- MODIFICATION: Reset lockout state ---
});
} }
} }
@ -304,6 +343,13 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
} }
void _validateAndProceed() { void _validateAndProceed() {
// --- START MODIFICATION: Add lockout check ---
if (_isLockedOut) {
_showSnackBar("Please wait for the initial reading period to complete.", isError: true);
return;
}
// --- END MODIFICATION ---
if (_isAutoReading) { if (_isAutoReading) {
_showStopReadingDialog(); _showStopReadingDialog();
return; return;
@ -480,7 +526,16 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
final activeConnection = _getActiveConnectionDetails(); final activeConnection = _getActiveConnectionDetails();
final String? activeType = activeConnection?['type'] as String?; final String? activeType = activeConnection?['type'] as String?;
return Form( // --- START MODIFICATION: Add WillPopScope to block back navigation ---
return WillPopScope(
onWillPop: () async {
if (_isLockedOut) {
_showSnackBar("Please wait for the initial reading period to complete.", isError: true);
return false; // Prevent back navigation
}
return true; // Allow back navigation
},
child: Form(
key: _formKey, key: _formKey,
child: ListView( child: ListView(
padding: const EdgeInsets.all(24.0), padding: const EdgeInsets.all(24.0),
@ -553,14 +608,18 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
}).toList(), }).toList(),
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
// --- START MODIFICATION: Add countdown to Next button ---
ElevatedButton( ElevatedButton(
onPressed: _validateAndProceed, onPressed: _isLockedOut ? null : _validateAndProceed,
style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16)), style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16)),
child: const Text('Next'), child: Text(_isLockedOut ? 'Next ($_lockoutSecondsRemaining\s)' : 'Next'),
), ),
// --- END MODIFICATION ---
], ],
), ),
),
); );
// --- END MODIFICATION ---
} }
Widget _buildComparisonView() { Widget _buildComparisonView() {
@ -773,12 +832,21 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
// --- START MODIFICATION: Add countdown to Stop Reading button ---
ElevatedButton.icon( ElevatedButton.icon(
icon: Icon(_isAutoReading ? Icons.stop_circle_outlined : Icons.play_circle_outlined), icon: Icon(_isAutoReading ? Icons.stop_circle_outlined : Icons.play_circle_outlined),
label: Text(_isAutoReading ? 'Stop Reading' : 'Start Reading'), label: Text(_isAutoReading
onPressed: () => _toggleAutoReading(type), ? (_isLockedOut ? 'Stop Reading ($_lockoutSecondsRemaining\s)' : 'Stop Reading')
style: ElevatedButton.styleFrom(backgroundColor: _isAutoReading ? Colors.orange : Colors.green, foregroundColor: Colors.white), : 'Start Reading'),
onPressed: (_isAutoReading && _isLockedOut) ? null : () => _toggleAutoReading(type),
style: ElevatedButton.styleFrom(
backgroundColor: _isAutoReading
? (_isLockedOut ? Colors.grey.shade600 : Colors.orange)
: Colors.green,
foregroundColor: Colors.white,
), ),
),
// --- END MODIFICATION ---
TextButton.icon( TextButton.icon(
icon: const Icon(Icons.link_off), icon: const Icon(Icons.link_off),
label: const Text('Disconnect'), label: const Text('Disconnect'),

View File

@ -744,7 +744,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
ListTile( ListTile(
leading: const Icon(Icons.info_outline), leading: const Icon(Icons.info_outline),
title: const Text('App Version'), title: const Text('App Version'),
subtitle: const Text('MMS V4 1.2.07'), subtitle: const Text('MMS V4 1.2.08'),
dense: true, dense: true,
), ),
ListTile( ListTile(

View File

@ -164,7 +164,7 @@ class SerialManager {
} }
/// Starts a periodic timer to automatically request data from the device. /// Starts a periodic timer to automatically request data from the device.
void startAutoReading({Duration interval = const Duration(seconds: 5)}) { void startAutoReading({Duration interval = const Duration(seconds: 2)}) {
stopAutoReading(); // Stop any existing auto-reading timer first stopAutoReading(); // Stop any existing auto-reading timer first
if (connectionState.value == SerialConnectionState.connected) { if (connectionState.value == SerialConnectionState.connected) {
//startLiveReading(); // Initiate the first reading immediately //startLiveReading(); // Initiate the first reading immediately

View File

@ -128,7 +128,7 @@ class MarineInSituSamplingService {
Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices(); Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices();
Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device); Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device);
void disconnectFromBluetooth() => _bluetoothManager.disconnect(); void disconnectFromBluetooth() => _bluetoothManager.disconnect();
void startBluetoothAutoReading({Duration? interval}) => _bluetoothManager.startAutoReading(interval: interval ?? const Duration(seconds: 5)); void startBluetoothAutoReading({Duration? interval}) => _bluetoothManager.startAutoReading(interval: interval ?? const Duration(seconds: 2));
void stopBluetoothAutoReading() => _bluetoothManager.stopAutoReading(); void stopBluetoothAutoReading() => _bluetoothManager.stopAutoReading();
// --- USB Serial Methods --- // --- USB Serial Methods ---
@ -153,7 +153,7 @@ class MarineInSituSamplingService {
} }
void disconnectFromSerial() => _serialManager.disconnect(); void disconnectFromSerial() => _serialManager.disconnect();
void startSerialAutoReading({Duration? interval}) => _serialManager.startAutoReading(interval: interval ?? const Duration(seconds: 5)); void startSerialAutoReading({Duration? interval}) => _serialManager.startAutoReading(interval: interval ?? const Duration(seconds: 2));
void stopSerialAutoReading() => _serialManager.stopAutoReading(); void stopSerialAutoReading() => _serialManager.stopAutoReading();
void dispose() { void dispose() {

View File

@ -129,7 +129,7 @@ class RiverInSituSamplingService {
Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices(); Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices();
Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device); Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device);
void disconnectFromBluetooth() => _bluetoothManager.disconnect(); void disconnectFromBluetooth() => _bluetoothManager.disconnect();
void startBluetoothAutoReading({Duration? interval}) => _bluetoothManager.startAutoReading(interval: interval ?? const Duration(seconds: 5)); void startBluetoothAutoReading({Duration? interval}) => _bluetoothManager.startAutoReading(interval: interval ?? const Duration(seconds: 2));
void stopBluetoothAutoReading() => _bluetoothManager.stopAutoReading(); void stopBluetoothAutoReading() => _bluetoothManager.stopAutoReading();
Future<List<UsbDevice>> getAvailableSerialDevices() => _serialManager.getAvailableDevices(); Future<List<UsbDevice>> getAvailableSerialDevices() => _serialManager.getAvailableDevices();
@ -152,7 +152,7 @@ class RiverInSituSamplingService {
} }
void disconnectFromSerial() => _serialManager.disconnect(); void disconnectFromSerial() => _serialManager.disconnect();
void startSerialAutoReading({Duration? interval}) => _serialManager.startAutoReading(interval: interval ?? const Duration(seconds: 5)); void startSerialAutoReading({Duration? interval}) => _serialManager.startAutoReading(interval: interval ?? const Duration(seconds: 2));
void stopSerialAutoReading() => _serialManager.stopAutoReading(); void stopSerialAutoReading() => _serialManager.stopAutoReading();
void dispose() { void dispose() {
_bluetoothManager.dispose(); _bluetoothManager.dispose();