mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #187 from meshtastic/1.3.42_New_Protos
Clear Log Buttons
This commit is contained in:
commit
c52bc91a8e
17 changed files with 370 additions and 183 deletions
|
|
@ -29,6 +29,7 @@
|
|||
DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */; };
|
||||
DD3CC6BE28E4CD9800FA9159 /* BatteryGauge.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */; };
|
||||
DD3CC6C028E7A60700FA9159 /* MessagingEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */; };
|
||||
DD3CC6C228EB9D4900FA9159 /* UpdateCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */; };
|
||||
DD4033C228B286B70096A444 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4033C128B286B70096A444 /* Onboarding.swift */; };
|
||||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582528582E9B009B0E59 /* DeviceConfig.swift */; };
|
||||
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; };
|
||||
|
|
@ -140,6 +141,7 @@
|
|||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModel.xcdatamodel; sourceTree = "<group>"; };
|
||||
DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryGauge.swift; sourceTree = "<group>"; };
|
||||
DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingEnums.swift; sourceTree = "<group>"; };
|
||||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCoreData.swift; sourceTree = "<group>"; };
|
||||
DD4033C128B286B70096A444 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = "<group>"; };
|
||||
DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = "<group>"; };
|
||||
DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -267,12 +269,12 @@
|
|||
DD47E3CA26F0E50300029299 /* Nodes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */,
|
||||
DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */,
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */,
|
||||
DD47E3CD26F103C600029299 /* NodeList.swift */,
|
||||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */,
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */,
|
||||
DD73FD1028750779000852D6 /* PositionLog.swift */,
|
||||
DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */,
|
||||
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */,
|
||||
);
|
||||
path = Nodes;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -519,6 +521,7 @@
|
|||
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */,
|
||||
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */,
|
||||
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */,
|
||||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -723,6 +726,7 @@
|
|||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */,
|
||||
DD3CC6B528E33FD100FA9159 /* ShareChannels.swift in Sources */,
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */,
|
||||
DD3CC6C228EB9D4900FA9159 /* UpdateCoreData.swift in Sources */,
|
||||
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */,
|
||||
DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */,
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "SQLite.swift",
|
||||
"repositoryURL": "https://github.com/stephencelis/SQLite.swift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "60a65015f6402b7c34b9a924f755ca0a73afeeaa",
|
||||
"version": "0.13.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftProtobuf",
|
||||
"repositoryURL": "https://github.com/apple/swift-protobuf.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "e1499bc69b9040b29184f7f2996f7bab467c1639",
|
||||
"version": "1.19.0"
|
||||
}
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "sqlite.swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/stephencelis/SQLite.swift.git",
|
||||
"state" : {
|
||||
"revision" : "60a65015f6402b7c34b9a924f755ca0a73afeeaa",
|
||||
"version" : "0.13.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639",
|
||||
"version" : "1.19.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func PositionToCsvFile(positions: [PositionEntity]) -> String {
|
|||
csvString += "\n"
|
||||
csvString += String(pos.seqNo)
|
||||
csvString += ", "
|
||||
csvString += String(pos.latitude ?? 0)
|
||||
csvString += String((pos.latitude ?? 0))
|
||||
csvString += ", "
|
||||
csvString += String(pos.longitude ?? 0)
|
||||
csvString += ", "
|
||||
|
|
|
|||
|
|
@ -621,13 +621,16 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
nowKnown = true
|
||||
localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
||||
}
|
||||
// Module Config
|
||||
if decodedInfo.moduleConfig.isInitialized && !invalidVersion {
|
||||
|
||||
nowKnown = true
|
||||
moduleConfig(config: decodedInfo.moduleConfig, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
||||
if decodedInfo.moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(decodedInfo.moduleConfig.cannedMessage) {
|
||||
self.getCannedMessageModuleMessages(destNum: self.connectedPeripheral.num, wantResponse: true)
|
||||
}
|
||||
}
|
||||
// Log any other unknownApp calls
|
||||
if !nowKnown {
|
||||
|
|
@ -656,7 +659,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
}
|
||||
case .adminApp:
|
||||
adminAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
adminAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
case .replyApp:
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Reply App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .ipTunnelApp:
|
||||
|
|
@ -681,10 +684,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Private App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .atakForwarder:
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for ATAK Forwarder App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .simulatorApp:
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Simulator App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .UNRECOGNIZED(_):
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Other App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .max:
|
||||
print("MAX PORT NUM OF 511")
|
||||
|
||||
}
|
||||
|
||||
// MARK: Check for an All / Broadcast User
|
||||
|
|
@ -1134,22 +1140,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a Factory Reset Admin Message for node: \(String(destNum))") }
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Sent a Factory Reset for node: \(String(destNum))") }
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
@ -1182,23 +1175,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Sent a NodeDB Reset Admin Message for node: \(String(destNum))") }
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Sent a NodeDB Reset for node: \(String(destNum))") }
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
PersistenceController.shared.clearDatabase()
|
||||
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
print("💥 Error Sending NodeDB Reset")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1077,63 +1077,69 @@ func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManage
|
|||
func adminAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
|
||||
|
||||
if let channelMessage = try? Channel(serializedData: packet.decoded.payload) {
|
||||
|
||||
let fetchedMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(packet.from))
|
||||
|
||||
do {
|
||||
if channelMessage.hasSettings {
|
||||
|
||||
let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as! [MyInfoEntity]
|
||||
|
||||
if fetchedMyInfo.count == 1 {
|
||||
let fetchedMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(packet.from))
|
||||
|
||||
do {
|
||||
|
||||
// Update
|
||||
if fetchedMyInfo[0].channels?.count ?? 0 >= 1 {
|
||||
let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as! [MyInfoEntity]
|
||||
|
||||
if fetchedMyInfo.count == 1 {
|
||||
|
||||
let newChannel = ChannelEntity(context: context)
|
||||
newChannel.index = Int32(channelMessage.settings.channelNum)
|
||||
newChannel.uplinkEnabled = channelMessage.settings.uplinkEnabled
|
||||
newChannel.downlinkEnabled = channelMessage.settings.downlinkEnabled
|
||||
newChannel.name = channelMessage.settings.name
|
||||
newChannel.role = Int32(channelMessage.role.rawValue)
|
||||
|
||||
let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet
|
||||
|
||||
mutableChannels.add(newChannel)
|
||||
fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet
|
||||
// Update
|
||||
if fetchedMyInfo[0].channels?.count ?? 0 >= 1 {
|
||||
|
||||
let newChannel = ChannelEntity(context: context)
|
||||
newChannel.index = Int32(channelMessage.settings.channelNum)
|
||||
newChannel.uplinkEnabled = channelMessage.settings.uplinkEnabled
|
||||
newChannel.downlinkEnabled = channelMessage.settings.downlinkEnabled
|
||||
newChannel.name = channelMessage.settings.name
|
||||
newChannel.role = Int32(channelMessage.role.rawValue)
|
||||
|
||||
let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet
|
||||
|
||||
mutableChannels.add(newChannel)
|
||||
fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet
|
||||
|
||||
} else {
|
||||
|
||||
let newChannel = ChannelEntity(context: context)
|
||||
newChannel.index = Int32(channelMessage.settings.channelNum)
|
||||
newChannel.uplinkEnabled = channelMessage.settings.uplinkEnabled
|
||||
newChannel.downlinkEnabled = channelMessage.settings.downlinkEnabled
|
||||
newChannel.name = channelMessage.settings.name
|
||||
newChannel.role = Int32(channelMessage.role.rawValue)
|
||||
|
||||
var newChannels = [ChannelEntity]()
|
||||
newChannels.append(newChannel)
|
||||
fetchedMyInfo[0].channels! = NSOrderedSet(array: newChannels)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
let newChannel = ChannelEntity(context: context)
|
||||
newChannel.index = Int32(channelMessage.settings.channelNum)
|
||||
newChannel.uplinkEnabled = channelMessage.settings.uplinkEnabled
|
||||
newChannel.downlinkEnabled = channelMessage.settings.downlinkEnabled
|
||||
newChannel.name = channelMessage.settings.name
|
||||
newChannel.role = Int32(channelMessage.role.rawValue)
|
||||
|
||||
var newChannels = [ChannelEntity]()
|
||||
newChannels.append(newChannel)
|
||||
fetchedMyInfo[0].channels! = NSOrderedSet(array: newChannels)
|
||||
print("💥 Trying to save a channel to a MyInfo that does not exist: \(packet.from)")
|
||||
}
|
||||
|
||||
} else {
|
||||
print("💥 Trying to save a channel to a MyInfo that does not exist: \(packet.from)")
|
||||
}
|
||||
|
||||
try context.save()
|
||||
|
||||
if meshLogging {
|
||||
try context.save()
|
||||
|
||||
MeshLogger.log("💾 Updated MyInfo channel \(channelMessage.settings.channelNum) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)")
|
||||
if meshLogging {
|
||||
|
||||
MeshLogger.log("💾 Updated MyInfo channel \(channelMessage.settings.channelNum) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving MyInfo Channel from ADMIN_APP \(nsError)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving MyInfo Channel from ADMIN_APP \(nsError)")
|
||||
}
|
||||
} else {
|
||||
|
||||
print(try! packet.decoded.jsonString())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
81
Meshtastic/Helpers/UpdateCoreData.swift
Normal file
81
Meshtastic/Helpers/UpdateCoreData.swift
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// UpdateCoreData.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 10/3/22.
|
||||
|
||||
import CoreData
|
||||
|
||||
public func clearPositions(destNum: Int64, context: NSManagedObjectContext) -> Bool {
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(destNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
let newPostions = [PositionEntity]()
|
||||
fetchedNode[0].positions? = NSOrderedSet(array: newPostions)
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
return true
|
||||
|
||||
} catch {
|
||||
context.rollback()
|
||||
return false
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("💥 Fetch NodeInfoEntity Error")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManagedObjectContext) -> Bool {
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(destNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
let emptyTelemetry = [TelemetryEntity]()
|
||||
fetchedNode[0].telemetries? = NSOrderedSet(array: emptyTelemetry)
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
return true
|
||||
|
||||
} catch {
|
||||
context.rollback()
|
||||
return false
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("💥 Fetch NodeInfoEntity Error")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func clearCoreDataDatabase(context: NSManagedObjectContext) {
|
||||
|
||||
let persistenceController = PersistenceController.shared.container
|
||||
|
||||
for i in 0...persistenceController.managedObjectModel.entities.count-1 {
|
||||
let entity = persistenceController.managedObjectModel.entities[i]
|
||||
|
||||
do {
|
||||
let query = NSFetchRequest<NSFetchRequestResult>(entityName: entity.name!)
|
||||
let deleterequest = NSBatchDeleteRequest(fetchRequest: query)
|
||||
try context.execute(deleterequest)
|
||||
try context.save()
|
||||
|
||||
} catch let error as NSError {
|
||||
print("Error: \(error.localizedDescription)")
|
||||
abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@
|
|||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="frequencyOffset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ class PersistenceController {
|
|||
init(inMemory: Bool = false) {
|
||||
|
||||
container = NSPersistentContainer(name: "Meshtastic")
|
||||
//self.clearDatabase()
|
||||
|
||||
if inMemory {
|
||||
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
||||
|
|
@ -63,10 +62,10 @@ class PersistenceController {
|
|||
|
||||
try persistentStoreCoordinator.destroyPersistentStore(at:url, ofType: NSSQLiteStoreType, options: nil)
|
||||
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
|
||||
print("💥 Something went terribly wrong, CoreData database truncated. All app data is lost.")
|
||||
print("💥 CoreData database truncated. All app data has been erased.")
|
||||
|
||||
} catch let error {
|
||||
print("💣 Failed to destroy broken CoreData database, delete the app. Attempted to clear persistent store: " + error.localizedDescription)
|
||||
print("💣 Failed to destroy CoreData database, delete the app and re-install to clear data. Attempted to clear persistent store: " + error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,24 +46,7 @@ struct ChannelSettings {
|
|||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// NOTE: this field is _independent_ and unrelated to the concepts in channel.proto.
|
||||
/// this is controlling the actual hardware frequency the radio is transmitting on.
|
||||
/// In a perfect world we would have called it something else (band?) but I forgot to make this change during the big 1.2 renaming.
|
||||
/// Most users should never need to be exposed to this field/concept.
|
||||
/// A channel number between 1 and 13 (or whatever the max is in the current
|
||||
/// region). If ZERO then the rule is "use the old channel name hash based
|
||||
/// algorithm to derive the channel number")
|
||||
/// If using the hash algorithm the channel number will be: hash(channel_name) %
|
||||
/// NUM_CHANNELS (Where num channels depends on the regulatory region).
|
||||
/// NUM_CHANNELS_US is 13, for other values see MeshRadio.h in the device code.
|
||||
/// hash a string into an integer - djb2 by Dan Bernstein. -
|
||||
/// http://www.cse.yorku.ca/~oz/hash.html
|
||||
/// unsigned long hash(char *str) {
|
||||
/// unsigned long hash = 5381; int c;
|
||||
/// while ((c = *str++) != 0)
|
||||
/// hash = ((hash << 5) + hash) + (unsigned char) c;
|
||||
/// return hash;
|
||||
/// }
|
||||
/// Deprecated in favor of LoraConfig.channel_num
|
||||
var channelNum: UInt32 = 0
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -705,6 +705,16 @@ struct Config {
|
|||
/// Units are in dBm.
|
||||
var txPower: Int32 = 0
|
||||
|
||||
///
|
||||
/// This is controlling the actual hardware frequency the radio is transmitting on.
|
||||
/// Most users should never need to be exposed to this field/concept.
|
||||
/// A channel number between 1 and NUM_CHANNELS (whatever the max is in the current region).
|
||||
/// If ZERO then the rule is "use the old channel name hash based
|
||||
/// algorithm to derive the channel number")
|
||||
/// If using the hash algorithm the channel number will be: hash(channel_name) %
|
||||
/// NUM_CHANNELS (Where num channels depends on the regulatory region).
|
||||
var channelNum: UInt32 = 0
|
||||
|
||||
///
|
||||
/// For testing it is useful sometimes to force a node to never listen to
|
||||
/// particular other nodes (simulating radio out of range). All nodenums listed
|
||||
|
|
@ -1599,6 +1609,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
8: .standard(proto: "hop_limit"),
|
||||
9: .standard(proto: "tx_enabled"),
|
||||
10: .standard(proto: "tx_power"),
|
||||
11: .standard(proto: "channel_num"),
|
||||
103: .standard(proto: "ignore_incoming"),
|
||||
]
|
||||
|
||||
|
|
@ -1618,6 +1629,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
case 8: try { try decoder.decodeSingularUInt32Field(value: &self.hopLimit) }()
|
||||
case 9: try { try decoder.decodeSingularBoolField(value: &self.txEnabled) }()
|
||||
case 10: try { try decoder.decodeSingularInt32Field(value: &self.txPower) }()
|
||||
case 11: try { try decoder.decodeSingularUInt32Field(value: &self.channelNum) }()
|
||||
case 103: try { try decoder.decodeRepeatedUInt32Field(value: &self.ignoreIncoming) }()
|
||||
default: break
|
||||
}
|
||||
|
|
@ -1655,6 +1667,9 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if self.txPower != 0 {
|
||||
try visitor.visitSingularInt32Field(value: self.txPower, fieldNumber: 10)
|
||||
}
|
||||
if self.channelNum != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.channelNum, fieldNumber: 11)
|
||||
}
|
||||
if !self.ignoreIncoming.isEmpty {
|
||||
try visitor.visitPackedUInt32Field(value: self.ignoreIncoming, fieldNumber: 103)
|
||||
}
|
||||
|
|
@ -1672,6 +1687,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if lhs.hopLimit != rhs.hopLimit {return false}
|
||||
if lhs.txEnabled != rhs.txEnabled {return false}
|
||||
if lhs.txPower != rhs.txPower {return false}
|
||||
if lhs.channelNum != rhs.channelNum {return false}
|
||||
if lhs.ignoreIncoming != rhs.ignoreIncoming {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -119,6 +119,13 @@ enum PortNum: SwiftProtobuf.Enum {
|
|||
/// Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS
|
||||
case zpsApp // = 68
|
||||
|
||||
///
|
||||
/// Used to let multiple instances of Linux native applications communicate
|
||||
/// as if they did using their LoRa chip.
|
||||
/// Maintained by GitHub user GUVWAF.
|
||||
/// Project files at https://github.com/GUVWAF/Meshtasticator
|
||||
case simulatorApp // = 69
|
||||
|
||||
///
|
||||
/// Private applications should use portnums >= 256.
|
||||
/// To simplify initial development and testing you can use "PRIVATE_APP"
|
||||
|
|
@ -156,6 +163,7 @@ enum PortNum: SwiftProtobuf.Enum {
|
|||
case 66: self = .rangeTestApp
|
||||
case 67: self = .telemetryApp
|
||||
case 68: self = .zpsApp
|
||||
case 69: self = .simulatorApp
|
||||
case 256: self = .privateApp
|
||||
case 257: self = .atakForwarder
|
||||
case 511: self = .max
|
||||
|
|
@ -181,6 +189,7 @@ enum PortNum: SwiftProtobuf.Enum {
|
|||
case .rangeTestApp: return 66
|
||||
case .telemetryApp: return 67
|
||||
case .zpsApp: return 68
|
||||
case .simulatorApp: return 69
|
||||
case .privateApp: return 256
|
||||
case .atakForwarder: return 257
|
||||
case .max: return 511
|
||||
|
|
@ -211,6 +220,7 @@ extension PortNum: CaseIterable {
|
|||
.rangeTestApp,
|
||||
.telemetryApp,
|
||||
.zpsApp,
|
||||
.simulatorApp,
|
||||
.privateApp,
|
||||
.atakForwarder,
|
||||
.max,
|
||||
|
|
@ -243,6 +253,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
|
|||
66: .same(proto: "RANGE_TEST_APP"),
|
||||
67: .same(proto: "TELEMETRY_APP"),
|
||||
68: .same(proto: "ZPS_APP"),
|
||||
69: .same(proto: "SIMULATOR_APP"),
|
||||
256: .same(proto: "PRIVATE_APP"),
|
||||
257: .same(proto: "ATAK_FORWARDER"),
|
||||
511: .same(proto: "MAX"),
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ struct Connect: View {
|
|||
Text("Bitrate: \(String(format: "%.2f", bleManager.connectedPeripheral.bitrate ?? 0.00))")
|
||||
Text("Ch. Utilization: \(String(format: "%.2f", bleManager.connectedPeripheral.channelUtilization ?? 0.00))")
|
||||
Text("Air Time: \(String(format: "%.2f", bleManager.connectedPeripheral.airTime ?? 0.00))")
|
||||
Text("RSSI: \(bleManager.connectedPeripheral.rssi)")
|
||||
Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ struct DeviceMetricsLog: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@State private var isPresentingClearLogConfirm: Bool = false
|
||||
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
|
||||
|
|
@ -105,19 +107,53 @@ struct DeviceMetricsLog: View {
|
|||
.padding(.trailing, 5)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
HStack {
|
||||
|
||||
Button(role: .destructive) {
|
||||
|
||||
isPresentingClearLogConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Clear Log", systemImage: "trash.fill")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingClearLogConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all device metrics?", role: .destructive) {
|
||||
|
||||
if clearTelemetry(destNum: node.num, metricsType: 0, context: context) {
|
||||
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 0)
|
||||
isExporting = true
|
||||
print("Clear Device Metrics Log Failed")
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
Button {
|
||||
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 0)
|
||||
isExporting = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
|
||||
.navigationTitle("Device Metrics Log")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ struct EnvironmentMetricsLog: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@State private var isPresentingClearLogConfirm: Bool = false
|
||||
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
|
||||
|
|
@ -79,19 +81,50 @@ struct EnvironmentMetricsLog: View {
|
|||
.padding(.trailing, 5)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
|
||||
HStack {
|
||||
|
||||
Button(role: .destructive) {
|
||||
|
||||
isPresentingClearLogConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Clear Log", systemImage: "trash.fill")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingClearLogConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all environment metrics?", role: .destructive) {
|
||||
|
||||
if clearTelemetry(destNum: node.num, metricsType: 1, context: context) {
|
||||
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 1)
|
||||
isExporting = true
|
||||
print("Clear Environment Metrics Log Failed")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
Button {
|
||||
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 1)
|
||||
isExporting = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.navigationTitle("Environment Metrics Log")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
|
|
|||
|
|
@ -326,8 +326,6 @@ struct NodeDetail: View {
|
|||
|
||||
if (node.positions?.count ?? 0) > 0 {
|
||||
|
||||
|
||||
|
||||
NavigationLink {
|
||||
PositionLog(node: node)
|
||||
} label: {
|
||||
|
|
@ -336,7 +334,7 @@ struct NodeDetail: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
Text("Position Log (\(node.positions?.count ?? 0) Points)")
|
||||
Text("Position Log")
|
||||
.font(.title3)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ struct PositionLog: View {
|
|||
@State var exportString = ""
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@State private var isPresentingClearLogConfirm = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -70,38 +72,71 @@ struct PositionLog: View {
|
|||
.padding(.leading, 15)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
Button {
|
||||
|
||||
exportString = PositionToCsvFile(positions: node.positions!.array as! [PositionEntity])
|
||||
isExporting = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
}
|
||||
.fileExporter(
|
||||
isPresented: $isExporting,
|
||||
document: CsvDocument(emptyCsv: exportString),
|
||||
contentType: .commaSeparatedText,
|
||||
defaultFilename: String("\(node.user?.longName ?? "Node") Position Log"),
|
||||
onCompletion: { result in
|
||||
HStack {
|
||||
|
||||
if case .success = result {
|
||||
Button(role: .destructive) {
|
||||
|
||||
isPresentingClearLogConfirm = true
|
||||
|
||||
print("Position log download succeeded.")
|
||||
self.isExporting = false
|
||||
} label: {
|
||||
|
||||
} else {
|
||||
|
||||
print("Position log download failed: \(result).")
|
||||
Label("Clear Log", systemImage: "trash.fill")
|
||||
}
|
||||
}
|
||||
)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingClearLogConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all positions?", role: .destructive) {
|
||||
|
||||
if clearPositions(destNum: node.num, context: context) {
|
||||
|
||||
print("Clear Position Log Failed")
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
||||
exportString = PositionToCsvFile(positions: node.positions!.array as! [PositionEntity])
|
||||
isExporting = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
}
|
||||
.fileExporter(
|
||||
isPresented: $isExporting,
|
||||
document: CsvDocument(emptyCsv: exportString),
|
||||
contentType: .commaSeparatedText,
|
||||
defaultFilename: String("\(node.user?.longName ?? "Node") Position Log"),
|
||||
onCompletion: { result in
|
||||
|
||||
if case .success = result {
|
||||
|
||||
print("Position log download succeeded.")
|
||||
self.isExporting = false
|
||||
|
||||
} else {
|
||||
|
||||
print("Position log download failed: \(result).")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.navigationTitle("Position Log \(node.positions?.count ?? 0) Points")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ struct DeviceConfig: View {
|
|||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingNodeDBResetConfirm = false
|
||||
@State private var isPresentingFactoryResetConfirm = false
|
||||
@State private var isPresentingSaveConfirm = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -61,8 +62,7 @@ struct DeviceConfig: View {
|
|||
HStack {
|
||||
|
||||
Button("Reset NodeDB", role: .destructive) {
|
||||
|
||||
isPresentingFactoryResetConfirm = true
|
||||
isPresentingNodeDBResetConfirm = true
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -71,19 +71,20 @@ struct DeviceConfig: View {
|
|||
.padding()
|
||||
.confirmationDialog(
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingFactoryResetConfirm,
|
||||
isPresented: $isPresentingNodeDBResetConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Erase the NodeDB from node and app?", role: .destructive) {
|
||||
|
||||
Button("Erase all device and app data?", role: .destructive) {
|
||||
if !bleManager.sendNodeDBReset(destNum: bleManager.connectedPeripheral.num) {
|
||||
|
||||
print("NodeDB Reset Failed")
|
||||
} else {
|
||||
// Disconnect from device as we are going to wipe the app database now
|
||||
bleManager.disconnectPeripheral()
|
||||
clearCoreDataDatabase(context: context)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button("Factory Reset", role: .destructive) {
|
||||
|
||||
isPresentingFactoryResetConfirm = true
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
|
@ -96,11 +97,15 @@ struct DeviceConfig: View {
|
|||
isPresented: $isPresentingFactoryResetConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Erase all device settings?", role: .destructive) {
|
||||
Button("Erase all device and app settings and data?", role: .destructive) {
|
||||
|
||||
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num) {
|
||||
|
||||
print("Factory Reset Failed")
|
||||
} else {
|
||||
// Disconnect from device
|
||||
bleManager.disconnectPeripheral()
|
||||
clearCoreDataDatabase(context: context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue