resolved conflict and update doc

This commit is contained in:
PacoX
2026-07-09 09:08:50 +02:00
93 changed files with 5593 additions and 511 deletions
+78
View File
@@ -162,6 +162,12 @@ class MeshCoreConnector extends ChangeNotifier {
// Message windowing to limit memory usage
static const int _messageWindowSize = 200;
// Cap on discovered (non-contact) nodes retained in memory. Adverts arrive
// continuously from the whole mesh, so without a bound this list grows for
// as long as the app stays connected. When full, the stalest node (oldest
// lastSeen) is evicted to make room for a newly heard one.
static const int _maxDiscoveredContacts = 500;
MeshCoreConnectionState _state = MeshCoreConnectionState.disconnected;
BluetoothDevice? _device;
BluetoothCharacteristic? _rxCharacteristic;
@@ -251,6 +257,9 @@ class MeshCoreConnector extends ChangeNotifier {
DateTime _lastRadioRxTime = DateTime.fromMillisecondsSinceEpoch(0);
DateTime _lastContactMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
DateTime _lastChannelMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
DateTime _lastZeroHopAdvertAt = DateTime.fromMillisecondsSinceEpoch(0);
double? _lastZeroHopAdvertLatitude;
double? _lastZeroHopAdvertLongitude;
static const int _radioQuietMs = 3000;
static const int _radioQuietMaxWaitMs = 3000;
@@ -1066,6 +1075,13 @@ class MeshCoreConnector extends ChangeNotifier {
Future<void> _loadDiscoveredContactCache() async {
final cached = await _discoveryContactStore.loadContacts();
// Trim a previously-saved oversized list down to the freshest entries so a
// device that grew unbounded before the cap existed recovers on load.
if (cached.length > _maxDiscoveredContacts) {
cached.sort((a, b) => b.lastSeen.compareTo(a.lastSeen));
cached.removeRange(_maxDiscoveredContacts, cached.length);
unawaited(_discoveryContactStore.saveContacts(cached));
}
_discoveredContacts
..clear()
..addAll(cached);
@@ -2558,6 +2574,8 @@ class MeshCoreConnector extends ChangeNotifier {
_selfName = null;
_selfLatitude = null;
_selfLongitude = null;
_lastZeroHopAdvertLatitude = null;
_lastZeroHopAdvertLongitude = null;
_awaitingSelfInfo = false;
_webInitialHandshakeRequestSent = false;
_selfInfoRetryTimer?.cancel();
@@ -2724,6 +2742,8 @@ class MeshCoreConnector extends ChangeNotifier {
_selfName = null;
_selfLatitude = null;
_selfLongitude = null;
_lastZeroHopAdvertLatitude = null;
_lastZeroHopAdvertLongitude = null;
_clientRepeat = null;
_rememberedNonRepeatRadioState = null;
_firmwareVerCode = null;
@@ -3883,6 +3903,11 @@ class MeshCoreConnector extends ChangeNotifier {
Future<void> sendSelfAdvert({bool flood = true}) async {
if (!isConnected) return;
await sendFrame(buildSendSelfAdvertFrame(flood: flood));
if (!flood) {
_lastZeroHopAdvertAt = DateTime.now();
_lastZeroHopAdvertLatitude = _selfLatitude;
_lastZeroHopAdvertLongitude = _selfLongitude;
}
}
Future<void> rebootDevice() async {
@@ -4346,6 +4371,42 @@ class MeshCoreConnector extends ChangeNotifier {
tag: 'Connector',
);
}
const locationChangeEpsilon = 2.25e-4; // ~25 meters in degrees.
final lastAdvertLatitude = _lastZeroHopAdvertLatitude;
final lastAdvertLongitude = _lastZeroHopAdvertLongitude;
final currentLatitude = _selfLatitude;
final currentLongitude = _selfLongitude;
final latChanged =
lastAdvertLatitude != null &&
currentLatitude != null &&
(currentLatitude - lastAdvertLatitude).abs() >= locationChangeEpsilon;
final lonChanged =
lastAdvertLongitude != null &&
currentLongitude != null &&
(currentLongitude - lastAdvertLongitude).abs() >= locationChangeEpsilon;
final gpsSampleChanged =
hasValidLocation(currentLatitude, currentLongitude) &&
(!hasValidLocation(lastAdvertLatitude, lastAdvertLongitude) ||
latChanged ||
lonChanged);
final effectiveGpsIntervalSeconds =
_appSettingsService?.resolvedGpsIntervalSeconds(_currentCustomVars) ??
0;
final timeSinceLastZeroHopAdvert = DateTime.now().difference(
_lastZeroHopAdvertAt,
);
final shouldAutoSendZeroHopAdvert =
(gpsSampleChanged || (_clientRepeat ?? false)) &&
_advertLocPolicy == 1 &&
(_appSettingsService?.settings.autoSendZeroHopAdvertOnGpsUpdate ??
false) &&
effectiveGpsIntervalSeconds > 0 &&
timeSinceLastZeroHopAdvert.inSeconds >= effectiveGpsIntervalSeconds;
if (shouldAutoSendZeroHopAdvert) {
unawaited(sendSelfAdvert(flood: false));
}
final selfName = _selfName?.trim();
if (_activeTransport == MeshCoreTransportType.usb &&
selfName != null &&
@@ -7139,6 +7200,10 @@ class MeshCoreConnector extends ChangeNotifier {
isActive: addActive,
flags: 0,
);
if (_discoveredContacts.length >= _maxDiscoveredContacts) {
_evictStalestDiscoveredContact();
}
_discoveredContacts.add(disContact);
unawaited(_persistDiscoveredContacts());
@@ -7156,6 +7221,19 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
void _evictStalestDiscoveredContact() {
if (_discoveredContacts.isEmpty) return;
var stalestIndex = 0;
for (int i = 1; i < _discoveredContacts.length; i++) {
if (_discoveredContacts[i].lastSeen.isBefore(
_discoveredContacts[stalestIndex].lastSeen,
)) {
stalestIndex = i;
}
}
_discoveredContacts.removeAt(stalestIndex);
}
void removeAllDiscoveredContacts() {
_discoveredContacts.clear();
unawaited(_persistDiscoveredContacts());