mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
- Clamp ML predictions between physics floor (raw airtime) and ceiling (worst-case formula) so model can never produce unsafe timeouts - Replace hourOfDay feature with secondsSinceLastRx for network activity - Remove unused _ContactStats.stdDev and dead model persistence code - Debounce observation writes (2s) instead of writing on every delivery - Skip recording observations when pathLength is null to avoid corrupting training data - Add comment explaining global (not per-contact) RX time tracking - Remove notifyListeners from retrain to avoid unnecessary widget rebuilds - Run dart format
156 lines
4.8 KiB
Dart
156 lines
4.8 KiB
Dart
import 'dart:convert';
|
|
import '../models/delivery_observation.dart';
|
|
import '../models/path_history.dart';
|
|
import '../storage/prefs_manager.dart';
|
|
|
|
class StorageService {
|
|
static const String _pathHistoryPrefix = 'path_history_';
|
|
static const String _pendingMessagesKey = 'pending_messages';
|
|
static const String _repeaterPasswordsKey = 'repeater_passwords';
|
|
static const String _deliveryObservationsKey = 'delivery_observations';
|
|
|
|
Future<void> savePathHistory(
|
|
String contactPubKeyHex,
|
|
ContactPathHistory history,
|
|
) async {
|
|
final prefs = PrefsManager.instance;
|
|
final key = '$_pathHistoryPrefix$contactPubKeyHex';
|
|
final jsonStr = jsonEncode(history.toJson());
|
|
await prefs.setString(key, jsonStr);
|
|
}
|
|
|
|
Future<ContactPathHistory?> loadPathHistory(String contactPubKeyHex) async {
|
|
final prefs = PrefsManager.instance;
|
|
final key = '$_pathHistoryPrefix$contactPubKeyHex';
|
|
final jsonStr = prefs.getString(key);
|
|
|
|
if (jsonStr == null) return null;
|
|
|
|
try {
|
|
final json = jsonDecode(jsonStr) as Map<String, dynamic>;
|
|
return ContactPathHistory.fromJson(contactPubKeyHex, json);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> clearPathHistory(String contactPubKeyHex) async {
|
|
final prefs = PrefsManager.instance;
|
|
final key = '$_pathHistoryPrefix$contactPubKeyHex';
|
|
await prefs.remove(key);
|
|
}
|
|
|
|
Future<void> clearAllPathHistories() async {
|
|
final prefs = PrefsManager.instance;
|
|
final keys = prefs.getKeys();
|
|
final pathHistoryKeys = keys.where(
|
|
(key) => key.startsWith(_pathHistoryPrefix),
|
|
);
|
|
|
|
for (final key in pathHistoryKeys) {
|
|
await prefs.remove(key);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, String>> loadPendingMessages() async {
|
|
final prefs = PrefsManager.instance;
|
|
final jsonStr = prefs.getString(_pendingMessagesKey);
|
|
|
|
if (jsonStr == null) return {};
|
|
|
|
try {
|
|
final json = jsonDecode(jsonStr) as Map<String, dynamic>;
|
|
return json.map((key, value) => MapEntry(key, value as String));
|
|
} catch (e) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
Future<void> savePendingMessages(Map<String, String> pending) async {
|
|
final prefs = PrefsManager.instance;
|
|
final jsonStr = jsonEncode(pending);
|
|
await prefs.setString(_pendingMessagesKey, jsonStr);
|
|
}
|
|
|
|
Future<void> clearPendingMessages() async {
|
|
final prefs = PrefsManager.instance;
|
|
await prefs.remove(_pendingMessagesKey);
|
|
}
|
|
|
|
/// Save a repeater password by public key hex
|
|
Future<void> saveRepeaterPassword(
|
|
String repeaterPubKeyHex,
|
|
String password,
|
|
) async {
|
|
final prefs = PrefsManager.instance;
|
|
final passwords = await loadRepeaterPasswords();
|
|
passwords[repeaterPubKeyHex] = password;
|
|
final jsonStr = jsonEncode(passwords);
|
|
await prefs.setString(_repeaterPasswordsKey, jsonStr);
|
|
}
|
|
|
|
/// Load all saved repeater passwords (map of pubKeyHex -> password)
|
|
Future<Map<String, String>> loadRepeaterPasswords() async {
|
|
final prefs = PrefsManager.instance;
|
|
final jsonStr = prefs.getString(_repeaterPasswordsKey);
|
|
|
|
if (jsonStr == null) return {};
|
|
|
|
try {
|
|
final json = jsonDecode(jsonStr) as Map<String, dynamic>;
|
|
return json.map((key, value) => MapEntry(key, value as String));
|
|
} catch (e) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/// Get a specific repeater's saved password
|
|
Future<String?> getRepeaterPassword(String repeaterPubKeyHex) async {
|
|
final passwords = await loadRepeaterPasswords();
|
|
return passwords[repeaterPubKeyHex];
|
|
}
|
|
|
|
/// Remove a saved repeater password
|
|
Future<void> removeRepeaterPassword(String repeaterPubKeyHex) async {
|
|
final prefs = PrefsManager.instance;
|
|
final passwords = await loadRepeaterPasswords();
|
|
passwords.remove(repeaterPubKeyHex);
|
|
final jsonStr = jsonEncode(passwords);
|
|
await prefs.setString(_repeaterPasswordsKey, jsonStr);
|
|
}
|
|
|
|
/// Clear all saved repeater passwords
|
|
Future<void> clearAllRepeaterPasswords() async {
|
|
final prefs = PrefsManager.instance;
|
|
await prefs.remove(_repeaterPasswordsKey);
|
|
}
|
|
|
|
Future<void> saveDeliveryObservations(
|
|
List<DeliveryObservation> observations,
|
|
) async {
|
|
final prefs = PrefsManager.instance;
|
|
final jsonStr = jsonEncode(observations.map((o) => o.toJson()).toList());
|
|
await prefs.setString(_deliveryObservationsKey, jsonStr);
|
|
}
|
|
|
|
Future<List<DeliveryObservation>> loadDeliveryObservations() async {
|
|
final prefs = PrefsManager.instance;
|
|
final jsonStr = prefs.getString(_deliveryObservationsKey);
|
|
|
|
if (jsonStr == null) return [];
|
|
|
|
try {
|
|
final list = jsonDecode(jsonStr) as List;
|
|
return list
|
|
.map((e) => DeliveryObservation.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
Future<void> clearDeliveryObservations() async {
|
|
final prefs = PrefsManager.instance;
|
|
await prefs.remove(_deliveryObservationsKey);
|
|
}
|
|
}
|