mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Fix tracing path and display issues
This commit is contained in:
@@ -154,6 +154,26 @@ class LogRxRouteDecoder {
|
||||
return hops;
|
||||
}
|
||||
|
||||
static List<int> reverseHopBytes(
|
||||
List<int> pathBytes, {
|
||||
required int hashSize,
|
||||
}) {
|
||||
if (pathBytes.isEmpty) return const [];
|
||||
if (hashSize < 1 || hashSize > 3 || pathBytes.length % hashSize != 0) {
|
||||
return List<int>.from(pathBytes.reversed);
|
||||
}
|
||||
|
||||
final reversed = <int>[];
|
||||
for (
|
||||
var index = pathBytes.length - hashSize;
|
||||
index >= 0;
|
||||
index -= hashSize
|
||||
) {
|
||||
reversed.addAll(pathBytes.sublist(index, index + hashSize));
|
||||
}
|
||||
return reversed;
|
||||
}
|
||||
|
||||
static ResolvedNodeHash resolveHash(
|
||||
String hashHex, {
|
||||
required Iterable<Contact> contacts,
|
||||
|
||||
132
lib/utils/trace_node_resolver.dart
Normal file
132
lib/utils/trace_node_resolver.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
import '../services/mesh_map_nodes_service.dart';
|
||||
|
||||
class ResolvedTraceNode {
|
||||
final MeshMapNode? node;
|
||||
final int matchCount;
|
||||
final bool usedOnlineFallback;
|
||||
|
||||
const ResolvedTraceNode({
|
||||
required this.node,
|
||||
required this.matchCount,
|
||||
required this.usedOnlineFallback,
|
||||
});
|
||||
|
||||
bool get hasMatch => node != null;
|
||||
bool get isAmbiguous => matchCount > 1;
|
||||
|
||||
String? get matchSummary {
|
||||
if (matchCount <= 1) return null;
|
||||
final source = usedOnlineFallback ? 'online' : 'local';
|
||||
return '$matchCount $source matches';
|
||||
}
|
||||
}
|
||||
|
||||
class TraceNodeResolver {
|
||||
static const Distance _distance = Distance();
|
||||
|
||||
const TraceNodeResolver._();
|
||||
|
||||
static ResolvedTraceNode resolveBest({
|
||||
required List<MeshMapNode> nodes,
|
||||
required Set<String> localPublicKeys,
|
||||
required String? prefixHex,
|
||||
LatLng? referenceA,
|
||||
LatLng? referenceB,
|
||||
String? preferredPrefix,
|
||||
}) {
|
||||
if (prefixHex == null || prefixHex.isEmpty) {
|
||||
return const ResolvedTraceNode(
|
||||
node: null,
|
||||
matchCount: 0,
|
||||
usedOnlineFallback: false,
|
||||
);
|
||||
}
|
||||
|
||||
final allMatches = nodes
|
||||
.where((n) => n.publicKey.startsWith(prefixHex))
|
||||
.toList();
|
||||
if (allMatches.isEmpty) {
|
||||
return const ResolvedTraceNode(
|
||||
node: null,
|
||||
matchCount: 0,
|
||||
usedOnlineFallback: false,
|
||||
);
|
||||
}
|
||||
|
||||
final localMatches = allMatches
|
||||
.where((node) => localPublicKeys.contains(node.publicKey))
|
||||
.toList();
|
||||
var pool = localMatches.isNotEmpty ? localMatches : allMatches;
|
||||
final usedOnlineFallback = localMatches.isEmpty;
|
||||
|
||||
if (preferredPrefix != null && preferredPrefix.isNotEmpty) {
|
||||
final preferredMatches = pool
|
||||
.where((node) => node.publicKey.startsWith(preferredPrefix))
|
||||
.toList();
|
||||
if (preferredMatches.isNotEmpty) {
|
||||
pool = preferredMatches;
|
||||
}
|
||||
}
|
||||
|
||||
pool.sort((a, b) {
|
||||
final distanceCompare =
|
||||
_scoreNode(
|
||||
a,
|
||||
referenceA: referenceA,
|
||||
referenceB: referenceB,
|
||||
).compareTo(
|
||||
_scoreNode(b, referenceA: referenceA, referenceB: referenceB),
|
||||
);
|
||||
if (distanceCompare != 0) return distanceCompare;
|
||||
return b.updatedAtMs.compareTo(a.updatedAtMs);
|
||||
});
|
||||
|
||||
return ResolvedTraceNode(
|
||||
node: pool.first,
|
||||
matchCount: pool.length,
|
||||
usedOnlineFallback: usedOnlineFallback,
|
||||
);
|
||||
}
|
||||
|
||||
static double _scoreNode(
|
||||
MeshMapNode node, {
|
||||
LatLng? referenceA,
|
||||
LatLng? referenceB,
|
||||
}) {
|
||||
final point = LatLng(node.latitude, node.longitude);
|
||||
if (referenceA != null && referenceB != null) {
|
||||
return _distanceToSegmentMeters(point, referenceA, referenceB);
|
||||
}
|
||||
if (referenceA != null) {
|
||||
return _distance.as(LengthUnit.Meter, point, referenceA);
|
||||
}
|
||||
if (referenceB != null) {
|
||||
return _distance.as(LengthUnit.Meter, point, referenceB);
|
||||
}
|
||||
return double.maxFinite;
|
||||
}
|
||||
|
||||
static double _distanceToSegmentMeters(LatLng p, LatLng a, LatLng b) {
|
||||
final ax = a.longitude;
|
||||
final ay = a.latitude;
|
||||
final bx = b.longitude;
|
||||
final by = b.latitude;
|
||||
final px = p.longitude;
|
||||
final py = p.latitude;
|
||||
|
||||
final abx = bx - ax;
|
||||
final aby = by - ay;
|
||||
final apx = px - ax;
|
||||
final apy = py - ay;
|
||||
final ab2 = abx * abx + aby * aby;
|
||||
if (ab2 == 0) {
|
||||
return _distance.as(LengthUnit.Meter, a, p);
|
||||
}
|
||||
var t = (apx * abx + apy * aby) / ab2;
|
||||
t = t.clamp(0.0, 1.0);
|
||||
final closest = LatLng(ay + aby * t, ax + abx * t);
|
||||
return _distance.as(LengthUnit.Meter, closest, p);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user