mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Retain rx path for adverts
This commit is contained in:
@@ -775,7 +775,6 @@ class AppProvider with ChangeNotifier {
|
||||
final receivedPathBytes = receptionDetailsSnapshot?.pathBytes;
|
||||
if (senderContact != null &&
|
||||
enrichedMessage.isChannelMessage &&
|
||||
(enrichedMessage.channelIdx ?? 0) == 0 &&
|
||||
receivedPathBytes != null &&
|
||||
receivedPathBytes.isNotEmpty) {
|
||||
unawaited(
|
||||
@@ -1180,6 +1179,7 @@ class AppProvider with ChangeNotifier {
|
||||
debugPrint(
|
||||
'📡 [AppProvider] Advertisement received: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...',
|
||||
);
|
||||
unawaited(_retainAdvertRxPath(publicKey));
|
||||
// Check if this is an existing contact that might have updated location
|
||||
final contact = contactsProvider.findContactByKey(publicKey);
|
||||
if (contact != null) {
|
||||
@@ -1583,6 +1583,29 @@ class AppProvider with ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _retainAdvertRxPath(Uint8List publicKey) async {
|
||||
final decoded = _findBestMatchingAdvertRxRoute(publicKey);
|
||||
if (decoded == null || decoded.pathBytes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parsedRoute = ContactRouteCodec.parse(
|
||||
decoded.hopHashes.join(','),
|
||||
expectedHashSize: decoded.hashSize,
|
||||
);
|
||||
contactsProvider.retainReceivedRoute(
|
||||
publicKey,
|
||||
signedEncodedPathLen: parsedRoute.signedEncodedPathLen,
|
||||
paddedPathBytes: parsedRoute.paddedPathBytes,
|
||||
devicePublicKey: connectionProvider.deviceInfo.publicKey,
|
||||
);
|
||||
await _pathHistoryService.recordReceivedBytePath(
|
||||
publicKey.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(),
|
||||
decoded.pathBytes,
|
||||
decoded.hashSize,
|
||||
);
|
||||
}
|
||||
|
||||
int _inferReceivedPathHashSize(
|
||||
List<int> pathBytes, {
|
||||
required int preferredHashSize,
|
||||
@@ -2629,6 +2652,37 @@ class AppProvider with ChangeNotifier {
|
||||
return bestLog;
|
||||
}
|
||||
|
||||
DecodedLogRxRoute? _findBestMatchingAdvertRxRoute(Uint8List publicKey) {
|
||||
final publicKeyHex = publicKey
|
||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
||||
.join()
|
||||
.toLowerCase();
|
||||
|
||||
DecodedLogRxRoute? bestRoute;
|
||||
var bestDeltaMs = 999999999;
|
||||
final now = DateTime.now();
|
||||
|
||||
for (final log in connectionProvider.bleService.packetLogs) {
|
||||
if (log.responseCode != 0x88) continue;
|
||||
if (log.rawData.length < 6) continue;
|
||||
|
||||
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||
if (decoded == null) continue;
|
||||
if (decoded.payloadType != 0x04) continue;
|
||||
final senderHash = decoded.originalSenderHashHex;
|
||||
if (senderHash == null || !publicKeyHex.startsWith(senderHash)) continue;
|
||||
|
||||
final deltaMs = (log.timestamp.difference(now).inMilliseconds).abs();
|
||||
if (deltaMs < bestDeltaMs) {
|
||||
bestDeltaMs = deltaMs;
|
||||
bestRoute = decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestDeltaMs > 30000) return null;
|
||||
return bestRoute;
|
||||
}
|
||||
|
||||
List<int>? _extractPathBytesFromLog(BlePacketLog? log) {
|
||||
if (log == null) return null;
|
||||
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||
|
||||
@@ -10,8 +10,15 @@ import '../utils/key_comparison.dart';
|
||||
class PendingAdvert {
|
||||
final Uint8List publicKey;
|
||||
final DateTime receivedAt;
|
||||
final int? signedEncodedPathLen;
|
||||
final Uint8List? paddedPathBytes;
|
||||
|
||||
const PendingAdvert({required this.publicKey, required this.receivedAt});
|
||||
const PendingAdvert({
|
||||
required this.publicKey,
|
||||
required this.receivedAt,
|
||||
this.signedEncodedPathLen,
|
||||
this.paddedPathBytes,
|
||||
});
|
||||
|
||||
String get publicKeyHex =>
|
||||
publicKey.map((b) => b.toRadixString(16).padLeft(2, '0')).join('');
|
||||
@@ -21,14 +28,31 @@ class PendingAdvert {
|
||||
return prefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':');
|
||||
}
|
||||
|
||||
PendingAdvert copyWith({Uint8List? publicKey, DateTime? receivedAt}) {
|
||||
PendingAdvert copyWith({
|
||||
Uint8List? publicKey,
|
||||
DateTime? receivedAt,
|
||||
int? signedEncodedPathLen,
|
||||
Uint8List? paddedPathBytes,
|
||||
}) {
|
||||
return PendingAdvert(
|
||||
publicKey: publicKey ?? this.publicKey,
|
||||
receivedAt: receivedAt ?? this.receivedAt,
|
||||
signedEncodedPathLen: signedEncodedPathLen ?? this.signedEncodedPathLen,
|
||||
paddedPathBytes: paddedPathBytes ?? this.paddedPathBytes,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RetainedRoute {
|
||||
final int signedEncodedPathLen;
|
||||
final Uint8List paddedPathBytes;
|
||||
|
||||
const _RetainedRoute({
|
||||
required this.signedEncodedPathLen,
|
||||
required this.paddedPathBytes,
|
||||
});
|
||||
}
|
||||
|
||||
/// Contacts Provider - manages contact list and telemetry
|
||||
class ContactsProvider with ChangeNotifier {
|
||||
final Map<String, Contact> _contacts = {};
|
||||
@@ -338,8 +362,19 @@ class ContactsProvider with ChangeNotifier {
|
||||
required Contact incomingContact,
|
||||
Contact? existingContact,
|
||||
}) {
|
||||
final retainedRoute = _retainedRouteForContact(
|
||||
keyHex: incomingContact.publicKeyHex,
|
||||
incomingContact: incomingContact,
|
||||
existingContact: existingContact,
|
||||
);
|
||||
|
||||
if (existingContact == null) {
|
||||
var newContact = incomingContact.copyWith(isNew: true);
|
||||
var newContact = incomingContact.copyWith(
|
||||
isNew: true,
|
||||
outPathLen:
|
||||
retainedRoute?.signedEncodedPathLen ?? incomingContact.outPathLen,
|
||||
outPath: retainedRoute?.paddedPathBytes ?? incomingContact.outPath,
|
||||
);
|
||||
if (incomingContact.advertLocation != null) {
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||
incomingContact.lastAdvert * 1000,
|
||||
@@ -363,6 +398,9 @@ class ContactsProvider with ChangeNotifier {
|
||||
isNew: existingContact.isNew,
|
||||
advertHistory: existingContact.advertHistory,
|
||||
telemetry: mergedTelemetry,
|
||||
outPathLen:
|
||||
retainedRoute?.signedEncodedPathLen ?? incomingContact.outPathLen,
|
||||
outPath: retainedRoute?.paddedPathBytes ?? incomingContact.outPath,
|
||||
advLat: incomingAdvertLocation != null
|
||||
? incomingContact.advLat
|
||||
: existingAdvertLocation != null
|
||||
@@ -388,6 +426,37 @@ class ContactsProvider with ChangeNotifier {
|
||||
return updatedContact;
|
||||
}
|
||||
|
||||
_RetainedRoute? _retainedRouteForContact({
|
||||
required String keyHex,
|
||||
required Contact incomingContact,
|
||||
required Contact? existingContact,
|
||||
}) {
|
||||
if (incomingContact.routeHasPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final pendingAdvert = _pendingAdverts[keyHex];
|
||||
final pendingPathBytes = pendingAdvert?.paddedPathBytes;
|
||||
final pendingPathLen = pendingAdvert?.signedEncodedPathLen;
|
||||
if (pendingPathLen != null &&
|
||||
pendingPathBytes != null &&
|
||||
pendingPathBytes.isNotEmpty) {
|
||||
return _RetainedRoute(
|
||||
signedEncodedPathLen: pendingPathLen,
|
||||
paddedPathBytes: Uint8List.fromList(pendingPathBytes),
|
||||
);
|
||||
}
|
||||
|
||||
if (existingContact != null && existingContact.routeHasPath) {
|
||||
return _RetainedRoute(
|
||||
signedEncodedPathLen: existingContact.outPathLen,
|
||||
paddedPathBytes: Uint8List.fromList(existingContact.outPath),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Update contact telemetry
|
||||
void updateTelemetry(Uint8List publicKeyPrefix, Uint8List lppData) {
|
||||
debugPrint('📊 [ContactsProvider] updateTelemetry() called');
|
||||
@@ -660,6 +729,46 @@ class ContactsProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void retainReceivedRoute(
|
||||
Uint8List publicKey, {
|
||||
required int signedEncodedPathLen,
|
||||
required Uint8List paddedPathBytes,
|
||||
Uint8List? devicePublicKey,
|
||||
}) {
|
||||
if (devicePublicKey != null && publicKey.matches(devicePublicKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final keyHex = publicKey
|
||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||
.join('');
|
||||
final contact = _contacts[keyHex];
|
||||
if (contact != null) {
|
||||
_contacts[keyHex] = contact.copyWith(
|
||||
outPathLen: signedEncodedPathLen,
|
||||
outPath: Uint8List.fromList(paddedPathBytes),
|
||||
);
|
||||
_persistContacts();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
final now = DateTime.now();
|
||||
final existing = _pendingAdverts[keyHex];
|
||||
_pendingAdverts[keyHex] =
|
||||
(existing ??
|
||||
PendingAdvert(
|
||||
publicKey: Uint8List.fromList(publicKey),
|
||||
receivedAt: now,
|
||||
))
|
||||
.copyWith(
|
||||
receivedAt: now,
|
||||
signedEncodedPathLen: signedEncodedPathLen,
|
||||
paddedPathBytes: Uint8List.fromList(paddedPathBytes),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void resetContactRouteLocal(Uint8List publicKey) {
|
||||
final contact = findContactByKey(publicKey);
|
||||
if (contact == null) {
|
||||
|
||||
Reference in New Issue
Block a user