mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #1283 from meshtastic/purge_stale_nodes
App option for purging nodes not heard in certain amount of time
This commit is contained in:
commit
8119c0d5bb
5 changed files with 135 additions and 10 deletions
|
|
@ -1378,6 +1378,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0" : {
|
||||
|
||||
},
|
||||
"1" : {
|
||||
|
||||
},
|
||||
"1 byte" : {
|
||||
"localizations" : {
|
||||
|
|
@ -1655,6 +1661,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"180" : {
|
||||
|
||||
},
|
||||
"256 bit" : {
|
||||
"localizations" : {
|
||||
|
|
@ -2481,6 +2490,28 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"After %lld Days" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"variations" : {
|
||||
"plural" : {
|
||||
"one" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "After %lld Day"
|
||||
}
|
||||
},
|
||||
"other" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "After %lld Days"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"After config values save the node will reboot." : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -6354,6 +6385,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Clear Stale Nodes" : {
|
||||
|
||||
},
|
||||
"Client" : {
|
||||
"localizations" : {
|
||||
|
|
@ -19796,6 +19830,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Nodes without PKI keys are cleared from the app database on the schedule set by the user, nodes with PKI keys are cleared only if the interval is set to 7 days or longer. This feature only purges nodes from the app that are not stored in the device node database." : {
|
||||
|
||||
},
|
||||
"None" : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
var timeoutTimer: Timer?
|
||||
var timeoutTimerCount = 0
|
||||
var positionTimer: Timer?
|
||||
var maintenanceTimer: Timer?
|
||||
let mqttManager = MqttClientProxyManager.shared
|
||||
var wantRangeTestPackets = false
|
||||
var wantStoreAndForwardPackets = false
|
||||
|
|
@ -53,6 +54,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
|
||||
let LEGACY_LOGRADIO_UUID = CBUUID(string: "0x6C6FD238-78FA-436B-AACF-15C5BE1EF2E2")
|
||||
let LOGRADIO_UUID = CBUUID(string: "0x5a3d6e49-06e6-4423-9944-e9de8cdf9547")
|
||||
@AppStorage("purgeStaleNodeDays") var purgeStaleNodeDays: Double = 0
|
||||
|
||||
let NONCE_ONLY_CONFIG = 69420
|
||||
let NONCE_ONLY_DB = 69421
|
||||
|
|
@ -78,13 +80,21 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
|
||||
private init(appState: AppState, context: NSManagedObjectContext) {
|
||||
self.appState = appState
|
||||
self.context = context
|
||||
self.lastConnectionError = ""
|
||||
self.connectedVersion = "0.0.0"
|
||||
super.init()
|
||||
centralManager = CBCentralManager(delegate: self, queue: nil)
|
||||
mqttManager.delegate = self
|
||||
self.appState = appState
|
||||
self.context = context
|
||||
self.lastConnectionError = ""
|
||||
self.connectedVersion = "0.0.0"
|
||||
super.init()
|
||||
centralManager = CBCentralManager(delegate: self, queue: nil)
|
||||
mqttManager.delegate = self
|
||||
// Run clearStaleNodes every hour
|
||||
maintenanceTimer = Timer.scheduledTimer(withTimeInterval: 3600, repeats: true, block: { _ in
|
||||
let result = clearStaleNodes(nodeExpireDays: Int(self.purgeStaleNodeDays), context: self.context)
|
||||
// If you are connected and the clear worked, pull nodes back from the node in case we have deleted anything from that app that is in the device nodedb
|
||||
if result && self.isSubscribed {
|
||||
self.sendWantConfig()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: Scanning for BLE Devices
|
||||
|
|
|
|||
|
|
@ -289,9 +289,13 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newTelemetries.append(telemetry)
|
||||
newNode.telemetries? = NSOrderedSet(array: newTelemetries)
|
||||
}
|
||||
|
||||
newNode.firstHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard)))
|
||||
newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard)))
|
||||
if nodeInfo.lastHeard > 0 {
|
||||
newNode.firstHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard)))
|
||||
newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard)))
|
||||
} else {
|
||||
newNode.firstHeard = Date()
|
||||
newNode.lastHeard = Date()
|
||||
}
|
||||
newNode.snr = nodeInfo.snr
|
||||
if nodeInfo.hasUser {
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,44 @@ import CoreData
|
|||
import MeshtasticProtobufs
|
||||
import OSLog
|
||||
|
||||
public func clearStaleNodes(nodeExpireDays: Int, context: NSManagedObjectContext) -> Bool {
|
||||
var nodeExpireTime: TimeInterval {
|
||||
return TimeInterval(-nodeExpireDays * 86400)
|
||||
}
|
||||
var nodePKIExpireTime: TimeInterval {
|
||||
return TimeInterval((nodeExpireDays < 7 ? -7 : -nodeExpireDays) * 86400)
|
||||
}
|
||||
|
||||
if nodeExpireDays == 0 {
|
||||
// Purge Disabled
|
||||
Logger.data.info("💾 [NodeInfoEntity] Skip clearing stale nodes")
|
||||
return false
|
||||
}
|
||||
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NodeInfoEntity")
|
||||
fetchRequest.predicate = NSPredicate(format: "favorite == false AND ignored == false AND ((user.pkiEncrypted == NO AND lastHeard < %@) OR (user.pkiEncrypted == YES AND lastHeard < %@))",
|
||||
NSDate(timeIntervalSinceNow: nodeExpireTime), NSDate(timeIntervalSinceNow: nodePKIExpireTime))
|
||||
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
|
||||
batchDeleteRequest.resultType = .resultTypeCount
|
||||
|
||||
do {
|
||||
Logger.data.info("💾 [NodeInfoEntity] Clearing nodes older than \(nodeExpireDays) days")
|
||||
if let batchDeleteResult = try context.execute(batchDeleteRequest) as? NSBatchDeleteResult {
|
||||
try context.save()
|
||||
let deletedNodes = batchDeleteResult.result as? Int ?? 0
|
||||
Logger.data.info("💾 [NodeInfoEntity] Cleared \(deletedNodes) stale nodes")
|
||||
if deletedNodes > 0 {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
Logger.data.error("💥 [NodeInfoEntity] bad delete results")
|
||||
}
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("💥 [NodeInfoEntity] Error deleting stale nodes")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func clearPax(destNum: Int64, context: NSManagedObjectContext) -> Bool {
|
||||
|
||||
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ import MapKit
|
|||
import OSLog
|
||||
|
||||
struct AppSettings: View {
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@State var totalDownloadedTileSize = ""
|
||||
@State private var isPresentingCoreDataResetConfirm = false
|
||||
@State private var isPresentingDeleteMapTilesConfirm = false
|
||||
@State private var purgeStaleNodes: Bool = false
|
||||
@AppStorage("purgeStaleNodeDays") private var purgeStaleNodeDays: Double = 0
|
||||
@AppStorage("environmentEnableWeatherKit") private var environmentEnableWeatherKit: Bool = true
|
||||
@AppStorage("enableAdministration") private var enableAdministration: Bool = false
|
||||
var body: some View {
|
||||
|
|
@ -40,6 +43,39 @@ struct AppSettings: View {
|
|||
}
|
||||
}
|
||||
Section(header: Text("App Data")) {
|
||||
Toggle(isOn: $purgeStaleNodes ) {
|
||||
Label {
|
||||
Text("Clear Stale Nodes")
|
||||
} icon: {
|
||||
Image(systemName: "list.bullet.circle")
|
||||
}
|
||||
}
|
||||
.onFirstAppear {
|
||||
purgeStaleNodes = purgeStaleNodeDays > 0
|
||||
Logger.services.info("ℹ️ Purge Stale Nodes toggle initialized to \(purgeStaleNodes)")
|
||||
}
|
||||
.onChange(of: purgeStaleNodes) { _, newValue in
|
||||
purgeStaleNodeDays = purgeStaleNodeDays > 0 ? purgeStaleNodeDays : 7
|
||||
purgeStaleNodeDays = newValue ? purgeStaleNodeDays : 0
|
||||
Logger.services.info("ℹ️ Purge Stale Nodes changed to \(purgeStaleNodeDays)")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
.listRowSeparator(purgeStaleNodes ? .hidden : .visible)
|
||||
if purgeStaleNodes {
|
||||
VStack(alignment: .leading) {
|
||||
Text(String(localized: "After \(Int(purgeStaleNodeDays)) Days"))
|
||||
Slider(value: $purgeStaleNodeDays, in: 1...180, step: 1) {
|
||||
} minimumValueLabel: {
|
||||
Text("1")
|
||||
} maximumValueLabel: {
|
||||
Text("180")
|
||||
}
|
||||
}
|
||||
Text("Nodes without PKI keys are cleared from the app database on the schedule set by the user, nodes with PKI keys are cleared only if the interval is set to 7 days or longer. This feature only purges nodes from the app that are not stored in the device node database.")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(idiom == .phone ? .caption : .callout)
|
||||
}
|
||||
Button {
|
||||
isPresentingCoreDataResetConfirm = true
|
||||
} label: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue