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" />
<!-- END: STORAGE PERMISSIONS -->
<!-- MMS V4 1.2.06 -->
<!-- MMS V4 1.2.08 -->
<application
android:label="MMS V4 1.2.07"
android:label="MMS V4 1.2.08"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">

View File

@ -80,7 +80,7 @@ class BluetoothManager {
}
/// 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.
stopAutoReading();

View File

@ -34,6 +34,12 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
bool _isAutoReading = false;
StreamSubscription? _dataSubscription;
// --- START MODIFICATION: Countdown Timer State ---
Timer? _lockoutTimer;
int _lockoutSecondsRemaining = 30;
bool _isLockedOut = false;
// --- END MODIFICATION ---
// --- START FIX: Declare service variable ---
late final MarineInSituSamplingService _samplingService;
// --- END FIX ---
@ -85,6 +91,7 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
@override
void dispose() {
_dataSubscription?.cancel();
_lockoutTimer?.cancel(); // --- MODIFICATION: Cancel timer on dispose ---
// --- START FIX: Use the pre-fetched service instance ---
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) {
final service = context.read<MarineInSituSamplingService>();
setState(() {
_isAutoReading = !_isAutoReading;
if (_isAutoReading) {
if (activeType == 'bluetooth') service.startBluetoothAutoReading(); else service.startSerialAutoReading();
_startLockoutTimer(); // --- MODIFICATION: Start countdown
} else {
if (activeType == 'bluetooth') service.stopBluetoothAutoReading(); else service.stopSerialAutoReading();
}
@ -272,8 +307,12 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
}
_dataSubscription?.cancel();
_dataSubscription = null;
_lockoutTimer?.cancel(); // --- MODIFICATION: Cancel timer on disconnect ---
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() {
// --- START MODIFICATION: Add lockout check ---
if (_isLockedOut) {
_showSnackBar("Please wait for the initial reading period to complete.", isError: true);
return;
}
// --- END MODIFICATION ---
if (_isAutoReading) {
_showStopReadingDialog();
return;
@ -480,7 +526,16 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
final activeConnection = _getActiveConnectionDetails();
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,
child: ListView(
padding: const EdgeInsets.all(24.0),
@ -553,14 +608,18 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
}).toList(),
),
const SizedBox(height: 32),
// --- START MODIFICATION: Add countdown to Next button ---
ElevatedButton(
onPressed: _validateAndProceed,
onPressed: _isLockedOut ? null : _validateAndProceed,
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() {
@ -773,12 +832,21 @@ class _InSituStep3DataCaptureState extends State<InSituStep3DataCapture> with Wi
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// --- START MODIFICATION: Add countdown to Stop Reading button ---
ElevatedButton.icon(
icon: Icon(_isAutoReading ? Icons.stop_circle_outlined : Icons.play_circle_outlined),
label: Text(_isAutoReading ? 'Stop Reading' : 'Start Reading'),
onPressed: () => _toggleAutoReading(type),
style: ElevatedButton.styleFrom(backgroundColor: _isAutoReading ? Colors.orange : Colors.green, foregroundColor: Colors.white),
label: Text(_isAutoReading
? (_isLockedOut ? 'Stop Reading ($_lockoutSecondsRemaining\s)' : 'Stop Reading')
: '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(
icon: const Icon(Icons.link_off),
label: const Text('Disconnect'),

View File

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

View File

@ -164,7 +164,7 @@ class SerialManager {
}
/// 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
if (connectionState.value == SerialConnectionState.connected) {
//startLiveReading(); // Initiate the first reading immediately

View File

@ -128,7 +128,7 @@ class MarineInSituSamplingService {
Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices();
Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device);
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();
// --- USB Serial Methods ---
@ -153,7 +153,7 @@ class MarineInSituSamplingService {
}
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 dispose() {

View File

@ -129,7 +129,7 @@ class RiverInSituSamplingService {
Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices();
Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device);
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();
Future<List<UsbDevice>> getAvailableSerialDevices() => _serialManager.getAvailableDevices();
@ -152,7 +152,7 @@ class RiverInSituSamplingService {
}
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 dispose() {
_bluetoothManager.dispose();