Merge pull request #187 from meshtastic/1.3.42_New_Protos

Clear Log Buttons
This commit is contained in:
Garth Vander Houwen 2022-10-03 21:24:16 -07:00 committed by GitHub
commit c52bc91a8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 370 additions and 183 deletions

View file

@ -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 */,

View file

@ -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
}

View file

@ -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 += ", "

View file

@ -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")
}
}

View file

@ -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())
}
}

View 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()
}
}
}

View file

@ -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"/>

View file

@ -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)
}
}
}

View file

@ -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
///

View file

@ -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

View file

@ -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"),

View file

@ -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 {

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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:

View file

@ -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)
}
}
}