mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-12 00:40:28 +00:00
Update iOS project version
This commit is contained in:
@@ -1041,6 +1041,32 @@ class AppProvider with ChangeNotifier {
|
||||
retryAttempt: retryAttempt,
|
||||
);
|
||||
};
|
||||
|
||||
messagesProvider.onDirectPathFailedCallback =
|
||||
({required contact, required failureStreak}) async {
|
||||
debugPrint(
|
||||
'🧭 [AppProvider] Clearing unhealthy path for ${contact.advName} after $failureStreak failed send chain(s)',
|
||||
);
|
||||
|
||||
contactsProvider.markPathUnhealthy(contact.publicKey);
|
||||
|
||||
if (!connectionProvider.deviceInfo.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await connectionProvider.resetPath(contact.publicKey);
|
||||
Future.delayed(const Duration(milliseconds: 150), () {
|
||||
if (connectionProvider.deviceInfo.isConnected) {
|
||||
connectionProvider.getContact(contact.publicKey);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'⚠️ [AppProvider] Failed to reset path for ${contact.advName}: $e',
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Initialize the app (load contacts, sync time, etc.)
|
||||
|
||||
@@ -517,6 +517,22 @@ class ContactsProvider with ChangeNotifier {
|
||||
return _contacts[keyHex];
|
||||
}
|
||||
|
||||
/// Clear a contact's learned path locally so the UI and next send both
|
||||
/// prefer flood routing until the radio reports a fresh route.
|
||||
void markPathUnhealthy(Uint8List publicKey) {
|
||||
final contact = findContactByKey(publicKey);
|
||||
if (contact == null || !contact.hasPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
_contacts[contact.publicKeyHex] = contact.copyWith(
|
||||
outPathLen: -1,
|
||||
outPath: Uint8List(0),
|
||||
);
|
||||
_persistContacts();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Add or refresh a pending advert entry from PUSH_CODE_ADVERT (0x80).
|
||||
/// Excludes self key and existing contacts.
|
||||
void addPendingAdvert(Uint8List publicKey, {Uint8List? devicePublicKey}) {
|
||||
|
||||
@@ -20,6 +20,7 @@ class MessageRetryManager {
|
||||
// Track retry state for each message ID
|
||||
final Map<String, int> _retryAttempts = {};
|
||||
final Map<String, DateTime> _lastRetryTimes = {};
|
||||
final Map<String, int> _pathFailureStreaks = {};
|
||||
|
||||
// Progressive timeout values in milliseconds
|
||||
// These are app-level timeouts, separate from firmware's suggested timeout
|
||||
@@ -120,6 +121,7 @@ class MessageRetryManager {
|
||||
void clearAll() {
|
||||
_retryAttempts.clear();
|
||||
_lastRetryTimes.clear();
|
||||
_pathFailureStreaks.clear();
|
||||
}
|
||||
|
||||
/// Get current retry attempt for a message (for debugging)
|
||||
@@ -132,6 +134,27 @@ class MessageRetryManager {
|
||||
return _lastRetryTimes[messageId];
|
||||
}
|
||||
|
||||
/// Record a successful delivery for a contact and clear any accumulated
|
||||
/// route failure streak for future sends.
|
||||
void recordDeliverySuccess(Contact contact) {
|
||||
_pathFailureStreaks.remove(contact.publicKeyHex);
|
||||
}
|
||||
|
||||
/// Record a permanent route failure for a contact.
|
||||
///
|
||||
/// Returns the updated failure streak so callers can decide when to reset
|
||||
/// the learned path on the radio and in local state.
|
||||
int recordPathFailure(Contact contact) {
|
||||
final contactKey = contact.publicKeyHex;
|
||||
final next = (_pathFailureStreaks[contactKey] ?? 0) + 1;
|
||||
_pathFailureStreaks[contactKey] = next;
|
||||
return next;
|
||||
}
|
||||
|
||||
int? getPathFailureStreak(Contact contact) {
|
||||
return _pathFailureStreaks[contact.publicKeyHex];
|
||||
}
|
||||
|
||||
int _estimateLoRaAirtimeMs(int payloadLenBytes) {
|
||||
final sf = _defaultLoRaSf;
|
||||
final bw = _defaultLoRaBwHz.toDouble();
|
||||
|
||||
@@ -74,6 +74,12 @@ class MessagesProvider with ChangeNotifier {
|
||||
})?
|
||||
sendMessageCallback;
|
||||
|
||||
Future<void> Function({
|
||||
required Contact contact,
|
||||
required int failureStreak,
|
||||
})?
|
||||
onDirectPathFailedCallback;
|
||||
|
||||
List<Message> get messages => List.unmodifiable(_messages);
|
||||
|
||||
List<Message> get contactMessages =>
|
||||
@@ -1388,6 +1394,10 @@ class MessagesProvider with ChangeNotifier {
|
||||
|
||||
// Clear retry tracking on successful delivery
|
||||
_retryManager.clearRetry(message.id);
|
||||
final deliveredContact = _messageContactMap[message.id];
|
||||
if (deliveredContact != null) {
|
||||
_retryManager.recordDeliverySuccess(deliveredContact);
|
||||
}
|
||||
|
||||
debugPrint(
|
||||
'✅ [MessagesProvider] Message ${message.id} delivered in ${roundTripTimeMs}ms (ACK $ackCode)',
|
||||
@@ -1427,6 +1437,10 @@ class MessagesProvider with ChangeNotifier {
|
||||
_rememberCompletedAck(ackCode);
|
||||
_clearAckHistoryForMessage(historicalMessageId);
|
||||
_retryManager.clearRetry(historicalMessageId);
|
||||
final deliveredContact = _messageContactMap[historicalMessageId];
|
||||
if (deliveredContact != null) {
|
||||
_retryManager.recordDeliverySuccess(deliveredContact);
|
||||
}
|
||||
_persistMessages();
|
||||
notifyListeners();
|
||||
debugPrint(
|
||||
@@ -1663,6 +1677,22 @@ class MessagesProvider with ChangeNotifier {
|
||||
// Clear retry tracking
|
||||
_retryManager.clearRetry(messageId);
|
||||
|
||||
final failedContact = _messageContactMap[messageId];
|
||||
if (failedContact != null && failedContact.hasPath) {
|
||||
final failureStreak = _retryManager.recordPathFailure(failedContact);
|
||||
debugPrint(
|
||||
' Path failure streak for ${failedContact.advName}: $failureStreak',
|
||||
);
|
||||
if (failureStreak >= 2 && onDirectPathFailedCallback != null) {
|
||||
unawaited(
|
||||
onDirectPathFailedCallback!(
|
||||
contact: failedContact,
|
||||
failureStreak: failureStreak,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_persistMessages();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user