From 82adcbd8de184a4d08c6bcf367b7bd7256e0dccb Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 1 Jun 2022 23:23:02 -0700 Subject: [PATCH] Finish refactoring Packet handling out of the blue manager Protobufs update --- Meshtastic Client.xcodeproj/project.pbxproj | 3 +- MeshtasticClient/Helpers/BLEManager.swift | 351 +++------------- MeshtasticClient/Helpers/MeshPackets.swift | 309 +++++++++++++- MeshtasticClient/Protobufs/apponly.pb.swift | 25 +- .../Protobufs/deviceonly.pb.swift | 393 ------------------ .../Views/Settings/ShareChannel.swift | 3 +- 6 files changed, 399 insertions(+), 685 deletions(-) diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 45bc181d..c8959e3b 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -460,7 +460,7 @@ TargetAttributes = { DDC2E15326CE248E0042C5E4 = { CreatedOnToolsVersion = 12.5.1; - LastSwiftMigration = 1320; + LastSwiftMigration = 1340; }; DDC2E16926CE248F0042C5E4 = { CreatedOnToolsVersion = 12.5.1; @@ -805,6 +805,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "MeshtasticClient/Compression/MeshtasticClient-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index 937a0c39..ae9eb44d 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -299,6 +299,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // MARK: Discover Characteristics Event func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { + if let e = error { if meshLoggingEnabled { MeshLogger.log("đŸšĢ BLE didDiscoverCharacteristicsFor error by \(peripheral.name ?? "Unknown") \(e)") } @@ -333,8 +334,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph default: break } - - } + } } func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { @@ -386,7 +386,41 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph switch decodedInfo.packet.decoded.portnum { case .unknownApp: - print("MyInfo or NodeInfo") + if decodedInfo.myInfo.myNodeNum != 0 { + + let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!) + + if myInfo != nil { + + self.connectedPeripheral.bitrate = myInfo!.bitrate + self.connectedPeripheral.num = myInfo!.myNodeNum + lastConnnectionVersion = myInfo?.firmwareVersion ?? myInfo!.firmwareVersion ?? "Unknown" + self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown" + self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown" + } + + } else if decodedInfo.nodeInfo.num != 0 { + + let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, meshLogging: meshLoggingEnabled, context: context!) + + if nodeInfo != nil { + + self.connectedPeripheral.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization + self.connectedPeripheral.airTime = decodedInfo.nodeInfo.deviceMetrics.airUtilTx + + if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num { + + if nodeInfo!.user != nil { + + connectedPeripheral.name = nodeInfo!.user!.longName ?? "Unknown" + } + } + } + + } else { + + if meshLoggingEnabled { MeshLogger.log("â„šī¸ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") } + } case .textMessageApp: textMessageAppPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), meshLogging: meshLoggingEnabled, context: context!) case .remoteHardwareApp: @@ -394,7 +428,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph case .positionApp: positionPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!) case .nodeinfoApp: - nodeInfoPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!) + nodeInfoAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!) case .routingApp: routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!) case .adminApp: @@ -425,288 +459,40 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph print("MAX PORT NUM OF 511") } - // MARK: Incoming MyInfo Packet - if decodedInfo.myInfo.myNodeNum != 0 { - - let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.myInfo.myNodeNum)) - - do { - let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity] - // Not Found Insert - if fetchedMyInfo.isEmpty { - let myInfo = MyInfoEntity(context: context!) - myInfo.myNodeNum = Int64(decodedInfo.myInfo.myNodeNum) - myInfo.hasGps = decodedInfo.myInfo.hasGps_p - myInfo.bitrate = decodedInfo.myInfo.bitrate - self.connectedPeripheral.bitrate = myInfo.bitrate - - // Swift does strings weird, this does work to get the version without the github hash - let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".") - var version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))] - version = version.dropLast() - myInfo.firmwareVersion = String(version) - lastConnnectionVersion = String(version) + // MARK: Check for an All / Broadcast User + let fetchBCUserRequest: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") + fetchBCUserRequest.predicate = NSPredicate(format: "num == %lld", Int64(broadcastNodeNum)) + + do { + let fetchedUser = try context?.fetch(fetchBCUserRequest) as! [UserEntity] - myInfo.messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec) - myInfo.minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion) - myInfo.maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels) - self.connectedPeripheral.num = myInfo.myNodeNum - self.connectedPeripheral.firmwareVersion = myInfo.firmwareVersion ?? "Unknown" - self.connectedPeripheral.name = myInfo.bleName ?? "Unknown" - - let fetchBCUserRequest: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") - fetchBCUserRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.myInfo.myNodeNum)) - - do { - let fetchedUser = try context?.fetch(fetchBCUserRequest) as! [UserEntity] - - if fetchedUser.isEmpty { - // Save the broadcast user if it does not exist - let bcu: UserEntity = UserEntity(context: context!) - bcu.shortName = "ALL" - bcu.longName = "All - Broadcast" - bcu.hwModel = "UNSET" - bcu.num = Int64(broadcastNodeNum) - bcu.userId = "BROADCASTNODE" - print("💾 Saved the All - Broadcast User") - } - - } catch { - - print("đŸ’Ĩ Error Saving the All - Broadcast User") - } - - } else { - - fetchedMyInfo[0].myNodeNum = Int64(decodedInfo.myInfo.myNodeNum) - fetchedMyInfo[0].hasGps = decodedInfo.myInfo.hasGps_p - fetchedMyInfo[0].bitrate = decodedInfo.myInfo.bitrate - - let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1) - var version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset:6, in: decodedInfo.myInfo.firmwareVersion))] - version = version.dropLast() - fetchedMyInfo[0].firmwareVersion = String(version) - lastConnnectionVersion = String(version) - fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec) - fetchedMyInfo[0].minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion) - fetchedMyInfo[0].maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels) - - self.connectedPeripheral.num = fetchedMyInfo[0].myNodeNum - self.connectedPeripheral.firmwareVersion = fetchedMyInfo[0].firmwareVersion ?? "Unknown" - self.connectedPeripheral.name = fetchedMyInfo[0].bleName ?? "Unknown" - self.connectedPeripheral.bitrate = fetchedMyInfo[0].bitrate - - } - do { - - try context!.save() - if meshLoggingEnabled { MeshLogger.log("💾 Saved a myInfo for \(peripheral.name ?? String(decodedInfo.myInfo.myNodeNum))") } - - } catch { - - context!.rollback() - - let nsError = error as NSError - print("đŸ’Ĩ Error Saving Core Data MyInfoEntity: \(nsError)") - } - - } catch { - - print("đŸ’Ĩ Fetch MyInfo Error") + if fetchedUser.isEmpty { + // Save the broadcast user if it does not exist + let bcu: UserEntity = UserEntity(context: context!) + bcu.shortName = "ALL" + bcu.longName = "All - Broadcast" + bcu.hwModel = "UNSET" + bcu.num = Int64(broadcastNodeNum) + bcu.userId = "BROADCASTNODE" + print("💾 Saved the All - Broadcast User") } - // MARK: Share Location Position Update Timer - // Use context to pass the radio name with the timer - // Use a RunLoop to prevent the timer from running on the main UI thread - if userSettings?.provideLocation ?? false { - - if self.positionTimer != nil { - self.positionTimer!.invalidate() - } - let context = ["name": "@\(peripheral.name ?? "Unknown")"] - self.positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval((userSettings?.provideLocationInterval ?? 900)), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true) - RunLoop.current.add(self.positionTimer!, forMode: .common) - } + } catch { + + print("đŸ’Ĩ Error Saving the All - Broadcast User") } - // MARK: Incoming Node Info Packet - if decodedInfo.nodeInfo.num != 0 { - - let fetchNodeRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") - fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.nodeInfo.num)) - - do { - - let fetchedNode = try context?.fetch(fetchNodeRequest) as! [NodeInfoEntity] - // Not Found Insert - if fetchedNode.isEmpty && decodedInfo.nodeInfo.hasUser { - - let newNode = NodeInfoEntity(context: context!) - newNode.id = Int64(decodedInfo.nodeInfo.num) - newNode.num = Int64(decodedInfo.nodeInfo.num) - - if decodedInfo.nodeInfo.hasDeviceMetrics { - - let telemetry = TelemetryEntity(context: context!) - - telemetry.batteryLevel = Int32(decodedInfo.nodeInfo.deviceMetrics.batteryLevel) - telemetry.voltage = decodedInfo.nodeInfo.deviceMetrics.voltage - telemetry.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization - self.connectedPeripheral.channelUtilization = telemetry.channelUtilization - telemetry.airUtilTx = decodedInfo.nodeInfo.deviceMetrics.airUtilTx - self.connectedPeripheral.airTime = decodedInfo.nodeInfo.deviceMetrics.airUtilTx - - var newTelemetries = [TelemetryEntity]() - newTelemetries.append(telemetry) - newNode.telemetries? = NSOrderedSet(array: newTelemetries) - } - - newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.lastHeard))) - newNode.snr = decodedInfo.nodeInfo.snr - - if self.connectedPeripheral != nil && self.connectedPeripheral.num == newNode.num { - - if decodedInfo.nodeInfo.hasUser { - - connectedPeripheral.name = decodedInfo.nodeInfo.user.longName - } - } - - if decodedInfo.nodeInfo.hasUser { - - let newUser = UserEntity(context: context!) - newUser.userId = decodedInfo.nodeInfo.user.id - newUser.num = Int64(decodedInfo.nodeInfo.num) - newUser.longName = decodedInfo.nodeInfo.user.longName - newUser.shortName = decodedInfo.nodeInfo.user.shortName - newUser.macaddr = decodedInfo.nodeInfo.user.macaddr - newUser.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased() - newNode.user = newUser - } - - let position = PositionEntity(context: context!) - position.latitudeI = decodedInfo.nodeInfo.position.latitudeI - position.longitudeI = decodedInfo.nodeInfo.position.longitudeI - position.altitude = decodedInfo.nodeInfo.position.altitude - position.time = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.position.time))) - - var newPostions = [PositionEntity]() - newPostions.append(position) - newNode.positions? = NSOrderedSet(array: newPostions) - - // Look for a MyInfo - let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.nodeInfo.num)) - - do { - - let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity] - if fetchedMyInfo.count > 0 { - newNode.myInfo = fetchedMyInfo[0] - - } - - } catch { - print("đŸ’Ĩ Fetch MyInfo Error") - } - - } else if decodedInfo.nodeInfo.hasUser && decodedInfo.nodeInfo.num > 0 { - - fetchedNode[0].id = Int64(decodedInfo.nodeInfo.num) - fetchedNode[0].num = Int64(decodedInfo.nodeInfo.num) - fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.lastHeard))) - fetchedNode[0].snr = decodedInfo.nodeInfo.snr - - if self.connectedPeripheral != nil && self.connectedPeripheral.num == fetchedNode[0].num { - - if decodedInfo.nodeInfo.hasUser { - - self.connectedPeripheral.name = fetchedNode[0].user!.longName ?? "Unknown" - } - } - - if decodedInfo.nodeInfo.hasUser { - - fetchedNode[0].user!.userId = decodedInfo.nodeInfo.user.id - fetchedNode[0].user!.num = Int64(decodedInfo.nodeInfo.num) - fetchedNode[0].user!.longName = decodedInfo.nodeInfo.user.longName - fetchedNode[0].user!.shortName = decodedInfo.nodeInfo.user.shortName - fetchedNode[0].user!.macaddr = decodedInfo.nodeInfo.user.macaddr - fetchedNode[0].user!.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased() - } - - if decodedInfo.nodeInfo.hasDeviceMetrics { - - let newTelemetry = TelemetryEntity(context: context!) - - newTelemetry.batteryLevel = Int32(decodedInfo.nodeInfo.deviceMetrics.batteryLevel) - newTelemetry.voltage = decodedInfo.nodeInfo.deviceMetrics.voltage - newTelemetry.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization - self.connectedPeripheral.channelUtilization = newTelemetry.channelUtilization - newTelemetry.airUtilTx = decodedInfo.nodeInfo.deviceMetrics.airUtilTx - self.connectedPeripheral.airTime = decodedInfo.nodeInfo.deviceMetrics.airUtilTx - - let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet - fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet - } - - if decodedInfo.nodeInfo.hasPosition { - - let position = PositionEntity(context: context!) - position.latitudeI = decodedInfo.nodeInfo.position.latitudeI - position.longitudeI = decodedInfo.nodeInfo.position.longitudeI - position.altitude = decodedInfo.nodeInfo.position.altitude - position.time = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.nodeInfo.position.time))) - - let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet - - fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet - - } - - // Look for a MyInfo - let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.nodeInfo.num)) - - do { - - let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity] - if fetchedMyInfo.count > 0 { - - fetchedNode[0].myInfo = fetchedMyInfo[0] - } - - } catch { - print("đŸ’Ĩ Fetch MyInfo Error") - } - } - do { - - try context!.save() - print("💾 Saved a nodeInfo for \(decodedInfo.nodeInfo.num)") - - } catch { - - context!.rollback() - - let nsError = error as NSError - print("đŸ’Ĩ Error Saving Core Data NodeInfoEntity: \(nsError)") - } - - } catch { - - print("đŸ’Ĩ Fetch NodeInfoEntity Error") - } - - if decodedInfo.nodeInfo.hasUser { - - if meshLoggingEnabled { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") } - - } else { - - if meshLoggingEnabled { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)") } + // MARK: Share Location Position Update Timer + // Use context to pass the radio name with the timer + // Use a RunLoop to prevent the timer from running on the main UI thread + if userSettings?.provideLocation ?? false { + + if self.positionTimer != nil { + self.positionTimer!.invalidate() } + let context = ["name": "@\(peripheral.name ?? "Unknown")"] + self.positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval((userSettings?.provideLocationInterval ?? 900)), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true) + RunLoop.current.add(self.positionTimer!, forMode: .common) } if decodedInfo.configCompleteID != 0 { @@ -720,7 +506,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph case FROMNUM_UUID : - print("đŸ—žī¸ FROMNUM Notification, value will be read below") + print("đŸ—žī¸ BLE FROMNUM (Notify) characteristic, value will be read next") default: @@ -731,7 +517,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph peripheral.readValue(for: FROMRADIO_characteristic) } - // Send Message public func sendMessage(message: String, toUserNum: Int64, isEmoji: Bool, replyID: Int64) -> Bool { var success = false @@ -854,7 +639,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return success } - // Send Position public func sendPosition(destNum: Int64, wantResponse: Bool) -> Bool { var success = false @@ -1002,4 +786,5 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } + } diff --git a/MeshtasticClient/Helpers/MeshPackets.swift b/MeshtasticClient/Helpers/MeshPackets.swift index f2cf5c50..3b7f00f6 100644 --- a/MeshtasticClient/Helpers/MeshPackets.swift +++ b/MeshtasticClient/Helpers/MeshPackets.swift @@ -8,7 +8,267 @@ import Foundation import CoreData -func nodeInfoPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) { +func myInfoPacket (myInfo: MyNodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> MyInfoEntity? { + + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(myInfo.myNodeNum)) + + do { + let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] + // Not Found Insert + if fetchedMyInfo.isEmpty { + + let myInfoEntity = MyInfoEntity(context: context) + myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum) + myInfoEntity.hasGps = myInfo.hasGps_p + myInfoEntity.hasWifi = myInfo.hasWifi_p + myInfoEntity.bitrate = myInfo.bitrate + + // Swift does strings weird, this does work to get the version without the github hash + let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".") + var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))] + version = version.dropLast() + myInfoEntity.firmwareVersion = String(version) + myInfoEntity.messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) + myInfoEntity.minAppVersion = Int32(bitPattern: myInfo.minAppVersion) + myInfoEntity.maxChannels = Int32(bitPattern: myInfo.maxChannels) + + do { + + try context.save() + if meshLogging { MeshLogger.log("💾 Saved a new myInfo for node number: \(String(myInfo.myNodeNum))") } + return myInfoEntity + + } catch { + + context.rollback() + + let nsError = error as NSError + print("đŸ’Ĩ Error Inserting New Core Data MyInfoEntity: \(nsError)") + } + + } else { + + fetchedMyInfo[0].myNodeNum = Int64(myInfo.myNodeNum) + fetchedMyInfo[0].hasGps = myInfo.hasGps_p + fetchedMyInfo[0].bitrate = myInfo.bitrate + + let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1) + var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset:6, in: myInfo.firmwareVersion))] + version = version.dropLast() + fetchedMyInfo[0].firmwareVersion = String(version) + fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) + fetchedMyInfo[0].minAppVersion = Int32(bitPattern: myInfo.minAppVersion) + fetchedMyInfo[0].maxChannels = Int32(bitPattern: myInfo.maxChannels) + + do { + + try context.save() + if meshLogging { MeshLogger.log("💾 Updated myInfo for node number: \(String(myInfo.myNodeNum))") } + return fetchedMyInfo[0] + + } catch { + + context.rollback() + + let nsError = error as NSError + print("đŸ’Ĩ Error Updating Core Data MyInfoEntity: \(nsError)") + } + } + + } catch { + + print("đŸ’Ĩ Fetch MyInfo Error") + } + return nil +} + + +func nodeInfoPacket (nodeInfo: NodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> NodeInfoEntity? { + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeInfo.num)) + + do { + + let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] + // Not Found Insert + if fetchedNode.isEmpty && nodeInfo.hasUser { + + let newNode = NodeInfoEntity(context: context) + newNode.id = Int64(nodeInfo.num) + newNode.num = Int64(nodeInfo.num) + + if nodeInfo.hasDeviceMetrics { + + let telemetry = TelemetryEntity(context: context) + + telemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel) + telemetry.voltage = nodeInfo.deviceMetrics.voltage + telemetry.channelUtilization = nodeInfo.deviceMetrics.channelUtilization + telemetry.airUtilTx = nodeInfo.deviceMetrics.airUtilTx + + var newTelemetries = [TelemetryEntity]() + newTelemetries.append(telemetry) + newNode.telemetries? = NSOrderedSet(array: newTelemetries) + } + + newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) + newNode.snr = nodeInfo.snr + + if nodeInfo.hasUser { + + let newUser = UserEntity(context: context) + newUser.userId = nodeInfo.user.id + newUser.num = Int64(nodeInfo.num) + newUser.longName = nodeInfo.user.longName + newUser.shortName = nodeInfo.user.shortName + newUser.macaddr = nodeInfo.user.macaddr + newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() + newNode.user = newUser + } + + let position = PositionEntity(context: context) + position.latitudeI = nodeInfo.position.latitudeI + position.longitudeI = nodeInfo.position.longitudeI + position.altitude = nodeInfo.position.altitude + position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time))) + + var newPostions = [PositionEntity]() + newPostions.append(position) + newNode.positions? = NSOrderedSet(array: newPostions) + + // Look for a MyInfo + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num)) + + do { + + let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] + if fetchedMyInfo.count > 0 { + newNode.myInfo = fetchedMyInfo[0] + } + + do { + + try context.save() + + if nodeInfo.hasUser { + + if meshLogging { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.user.longName)") } + + } else { + + if meshLogging { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.num)") } + } + return newNode + + } catch { + + context.rollback() + + let nsError = error as NSError + print("đŸ’Ĩ Error Saving Core Data NodeInfoEntity: \(nsError)") + } + + } catch { + print("đŸ’Ĩ Fetch MyInfo Error") + } + + } else if nodeInfo.hasUser && nodeInfo.num > 0 { + + fetchedNode[0].id = Int64(nodeInfo.num) + fetchedNode[0].num = Int64(nodeInfo.num) + fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard))) + fetchedNode[0].snr = nodeInfo.snr + + + if nodeInfo.hasUser { + + fetchedNode[0].user!.userId = nodeInfo.user.id + fetchedNode[0].user!.num = Int64(nodeInfo.num) + fetchedNode[0].user!.longName = nodeInfo.user.longName + fetchedNode[0].user!.shortName = nodeInfo.user.shortName + fetchedNode[0].user!.macaddr = nodeInfo.user.macaddr + fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() + } + + if nodeInfo.hasDeviceMetrics { + + let newTelemetry = TelemetryEntity(context: context) + + newTelemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel) + newTelemetry.voltage = nodeInfo.deviceMetrics.voltage + newTelemetry.channelUtilization = nodeInfo.deviceMetrics.channelUtilization + newTelemetry.airUtilTx = nodeInfo.deviceMetrics.airUtilTx + + let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet + fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet + } + + if nodeInfo.hasPosition { + + let position = PositionEntity(context: context) + position.latitudeI = nodeInfo.position.latitudeI + position.longitudeI = nodeInfo.position.longitudeI + position.altitude = nodeInfo.position.altitude + position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time))) + + let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet + + fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet + + } + + // Look for a MyInfo + let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num)) + + do { + + let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] + if fetchedMyInfo.count > 0 { + + fetchedNode[0].myInfo = fetchedMyInfo[0] + } + + do { + + try context.save() + + if nodeInfo.hasUser { + + if meshLogging { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.user.longName)") } + + } else { + + if meshLogging { MeshLogger.log("💾 BLE FROMRADIO received and nodeInfo inserted for \(nodeInfo.num)") } + } + + return fetchedNode[0] + + } catch { + + context.rollback() + + let nsError = error as NSError + print("đŸ’Ĩ Error Saving Core Data NodeInfoEntity: \(nsError)") + } + + } catch { + print("đŸ’Ĩ Fetch MyInfo Error") + } + } + + } catch { + + print("đŸ’Ĩ Fetch NodeInfoEntity Error") + } + + return nil +} + +func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) { let fetchNodeInfoAppRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) @@ -144,8 +404,6 @@ func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObj if routingMessage.errorReason == Routing.Error.none { - print("Priority ACK no Error") - let fetchMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID)) @@ -184,9 +442,48 @@ func telemetryPacket(packet: MeshPacket, meshLogging: Bool, context: NSManagedOb let telemetry = TelemetryEntity(context: context) - - - if meshLogging { MeshLogger.log("â„šī¸ MESH PACKET received for Telemetry App UNHANDLED \(telemetryMessage)") } + let fetchNodeTelemetryRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) + + do { + + let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) as! [NodeInfoEntity] + + if fetchedNode.count == 1 { + + // Device Metrics + telemetry.airUtilTx = telemetryMessage.deviceMetrics.airUtilTx + telemetry.channelUtilization = telemetryMessage.deviceMetrics.channelUtilization + telemetry.batteryLevel = Int32(telemetryMessage.deviceMetrics.batteryLevel) + telemetry.voltage = telemetryMessage.deviceMetrics.voltage + + // Environment Metrics + telemetry.barometricPressure = telemetryMessage.environmentMetrics.barometricPressure + telemetry.current = telemetryMessage.environmentMetrics.current + telemetry.gasResistance = telemetryMessage.environmentMetrics.gasResistance + telemetry.relativeHumidity = telemetryMessage.environmentMetrics.relativeHumidity + telemetry.temperature = telemetryMessage.environmentMetrics.temperature + let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet + mutableTelemetries.add(telemetry) + + fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(telemetryMessage.time))) + fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet + fetchedNode[0].objectWillChange.send() + } + + try context.save() + + if meshLogging { + MeshLogger.log("💾 Telemetry Saved for Node: \(packet.from)") + } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("đŸ’Ĩ Error Saving Telemetry for Node \(packet.from) Error: \(nsError)") + } } else { diff --git a/MeshtasticClient/Protobufs/apponly.pb.swift b/MeshtasticClient/Protobufs/apponly.pb.swift index 687030b7..46ab82c8 100644 --- a/MeshtasticClient/Protobufs/apponly.pb.swift +++ b/MeshtasticClient/Protobufs/apponly.pb.swift @@ -32,12 +32,25 @@ struct ChannelSet { // methods supported on all messages. /// - /// TODO: REPLACE + /// Channel list with settings var settings: [ChannelSettings] = [] + /// + /// LoRa config + var loraConfig: Config.LoRaConfig { + get {return _loraConfig ?? Config.LoRaConfig()} + set {_loraConfig = newValue} + } + /// Returns true if `loraConfig` has been explicitly set. + var hasLoraConfig: Bool {return self._loraConfig != nil} + /// Clears the value of `loraConfig`. Subsequent reads from it will return its default value. + mutating func clearLoraConfig() {self._loraConfig = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} + + fileprivate var _loraConfig: Config.LoRaConfig? = nil } #if swift(>=5.5) && canImport(_Concurrency) @@ -50,6 +63,7 @@ extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio static let protoMessageName: String = "ChannelSet" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "settings"), + 2: .standard(proto: "lora_config"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -59,20 +73,29 @@ extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeRepeatedMessageField(value: &self.settings) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._loraConfig) }() default: break } } } func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if !self.settings.isEmpty { try visitor.visitRepeatedMessageField(value: self.settings, fieldNumber: 1) } + try { if let v = self._loraConfig { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: ChannelSet, rhs: ChannelSet) -> Bool { if lhs.settings != rhs.settings {return false} + if lhs._loraConfig != rhs._loraConfig {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/MeshtasticClient/Protobufs/deviceonly.pb.swift b/MeshtasticClient/Protobufs/deviceonly.pb.swift index 95139c11..52a31d33 100644 --- a/MeshtasticClient/Protobufs/deviceonly.pb.swift +++ b/MeshtasticClient/Protobufs/deviceonly.pb.swift @@ -215,180 +215,11 @@ struct OEMStore { init() {} } -struct LocalConfig { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - /// - /// TODO: REPLACE - var device: Config.DeviceConfig { - get {return _storage._device ?? Config.DeviceConfig()} - set {_uniqueStorage()._device = newValue} - } - /// Returns true if `device` has been explicitly set. - var hasDevice: Bool {return _storage._device != nil} - /// Clears the value of `device`. Subsequent reads from it will return its default value. - mutating func clearDevice() {_uniqueStorage()._device = nil} - - /// - /// TODO: REPLACE - var position: Config.PositionConfig { - get {return _storage._position ?? Config.PositionConfig()} - set {_uniqueStorage()._position = newValue} - } - /// Returns true if `position` has been explicitly set. - var hasPosition: Bool {return _storage._position != nil} - /// Clears the value of `position`. Subsequent reads from it will return its default value. - mutating func clearPosition() {_uniqueStorage()._position = nil} - - /// - /// TODO: REPLACE - var power: Config.PowerConfig { - get {return _storage._power ?? Config.PowerConfig()} - set {_uniqueStorage()._power = newValue} - } - /// Returns true if `power` has been explicitly set. - var hasPower: Bool {return _storage._power != nil} - /// Clears the value of `power`. Subsequent reads from it will return its default value. - mutating func clearPower() {_uniqueStorage()._power = nil} - - /// - /// TODO: REPLACE - var wifi: Config.WiFiConfig { - get {return _storage._wifi ?? Config.WiFiConfig()} - set {_uniqueStorage()._wifi = newValue} - } - /// Returns true if `wifi` has been explicitly set. - var hasWifi: Bool {return _storage._wifi != nil} - /// Clears the value of `wifi`. Subsequent reads from it will return its default value. - mutating func clearWifi() {_uniqueStorage()._wifi = nil} - - /// - /// TODO: REPLACE - var display: Config.DisplayConfig { - get {return _storage._display ?? Config.DisplayConfig()} - set {_uniqueStorage()._display = newValue} - } - /// Returns true if `display` has been explicitly set. - var hasDisplay: Bool {return _storage._display != nil} - /// Clears the value of `display`. Subsequent reads from it will return its default value. - mutating func clearDisplay() {_uniqueStorage()._display = nil} - - /// - /// TODO: REPLACE - var lora: Config.LoRaConfig { - get {return _storage._lora ?? Config.LoRaConfig()} - set {_uniqueStorage()._lora = newValue} - } - /// Returns true if `lora` has been explicitly set. - var hasLora: Bool {return _storage._lora != nil} - /// Clears the value of `lora`. Subsequent reads from it will return its default value. - mutating func clearLora() {_uniqueStorage()._lora = nil} - - var unknownFields = SwiftProtobuf.UnknownStorage() - - init() {} - - fileprivate var _storage = _StorageClass.defaultInstance -} - -struct LocalModuleConfig { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - /// - /// TODO: REPLACE - var mqtt: ModuleConfig.MQTTConfig { - get {return _storage._mqtt ?? ModuleConfig.MQTTConfig()} - set {_uniqueStorage()._mqtt = newValue} - } - /// Returns true if `mqtt` has been explicitly set. - var hasMqtt: Bool {return _storage._mqtt != nil} - /// Clears the value of `mqtt`. Subsequent reads from it will return its default value. - mutating func clearMqtt() {_uniqueStorage()._mqtt = nil} - - /// - /// TODO: REPLACE - var serial: ModuleConfig.SerialConfig { - get {return _storage._serial ?? ModuleConfig.SerialConfig()} - set {_uniqueStorage()._serial = newValue} - } - /// Returns true if `serial` has been explicitly set. - var hasSerial: Bool {return _storage._serial != nil} - /// Clears the value of `serial`. Subsequent reads from it will return its default value. - mutating func clearSerial() {_uniqueStorage()._serial = nil} - - /// - /// TODO: REPLACE - var externalNotification: ModuleConfig.ExternalNotificationConfig { - get {return _storage._externalNotification ?? ModuleConfig.ExternalNotificationConfig()} - set {_uniqueStorage()._externalNotification = newValue} - } - /// Returns true if `externalNotification` has been explicitly set. - var hasExternalNotification: Bool {return _storage._externalNotification != nil} - /// Clears the value of `externalNotification`. Subsequent reads from it will return its default value. - mutating func clearExternalNotification() {_uniqueStorage()._externalNotification = nil} - - /// - /// TODO: REPLACE - var storeForward: ModuleConfig.StoreForwardConfig { - get {return _storage._storeForward ?? ModuleConfig.StoreForwardConfig()} - set {_uniqueStorage()._storeForward = newValue} - } - /// Returns true if `storeForward` has been explicitly set. - var hasStoreForward: Bool {return _storage._storeForward != nil} - /// Clears the value of `storeForward`. Subsequent reads from it will return its default value. - mutating func clearStoreForward() {_uniqueStorage()._storeForward = nil} - - /// - /// TODO: REPLACE - var rangeTest: ModuleConfig.RangeTestConfig { - get {return _storage._rangeTest ?? ModuleConfig.RangeTestConfig()} - set {_uniqueStorage()._rangeTest = newValue} - } - /// Returns true if `rangeTest` has been explicitly set. - var hasRangeTest: Bool {return _storage._rangeTest != nil} - /// Clears the value of `rangeTest`. Subsequent reads from it will return its default value. - mutating func clearRangeTest() {_uniqueStorage()._rangeTest = nil} - - /// - /// TODO: REPLACE - var telemetry: ModuleConfig.TelemetryConfig { - get {return _storage._telemetry ?? ModuleConfig.TelemetryConfig()} - set {_uniqueStorage()._telemetry = newValue} - } - /// Returns true if `telemetry` has been explicitly set. - var hasTelemetry: Bool {return _storage._telemetry != nil} - /// Clears the value of `telemetry`. Subsequent reads from it will return its default value. - mutating func clearTelemetry() {_uniqueStorage()._telemetry = nil} - - /// - /// TODO: REPLACE - var cannedMessage: ModuleConfig.CannedMessageConfig { - get {return _storage._cannedMessage ?? ModuleConfig.CannedMessageConfig()} - set {_uniqueStorage()._cannedMessage = newValue} - } - /// Returns true if `cannedMessage` has been explicitly set. - var hasCannedMessage: Bool {return _storage._cannedMessage != nil} - /// Clears the value of `cannedMessage`. Subsequent reads from it will return its default value. - mutating func clearCannedMessage() {_uniqueStorage()._cannedMessage = nil} - - var unknownFields = SwiftProtobuf.UnknownStorage() - - init() {} - - fileprivate var _storage = _StorageClass.defaultInstance -} - #if swift(>=5.5) && canImport(_Concurrency) extension ScreenFonts: @unchecked Sendable {} extension DeviceState: @unchecked Sendable {} extension ChannelFile: @unchecked Sendable {} extension OEMStore: @unchecked Sendable {} -extension LocalConfig: @unchecked Sendable {} -extension LocalModuleConfig: @unchecked Sendable {} #endif // swift(>=5.5) && canImport(_Concurrency) // MARK: - Code below here is support for the SwiftProtobuf runtime. @@ -612,227 +443,3 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB return true } } - -extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = "LocalConfig" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "device"), - 2: .same(proto: "position"), - 3: .same(proto: "power"), - 4: .same(proto: "wifi"), - 5: .same(proto: "display"), - 6: .same(proto: "lora"), - ] - - fileprivate class _StorageClass { - var _device: Config.DeviceConfig? = nil - var _position: Config.PositionConfig? = nil - var _power: Config.PowerConfig? = nil - var _wifi: Config.WiFiConfig? = nil - var _display: Config.DisplayConfig? = nil - var _lora: Config.LoRaConfig? = nil - - static let defaultInstance = _StorageClass() - - private init() {} - - init(copying source: _StorageClass) { - _device = source._device - _position = source._position - _power = source._power - _wifi = source._wifi - _display = source._display - _lora = source._lora - } - } - - fileprivate mutating func _uniqueStorage() -> _StorageClass { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _StorageClass(copying: _storage) - } - return _storage - } - - mutating func decodeMessage(decoder: inout D) throws { - _ = _uniqueStorage() - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularMessageField(value: &_storage._device) }() - case 2: try { try decoder.decodeSingularMessageField(value: &_storage._position) }() - case 3: try { try decoder.decodeSingularMessageField(value: &_storage._power) }() - case 4: try { try decoder.decodeSingularMessageField(value: &_storage._wifi) }() - case 5: try { try decoder.decodeSingularMessageField(value: &_storage._display) }() - case 6: try { try decoder.decodeSingularMessageField(value: &_storage._lora) }() - default: break - } - } - } - } - - func traverse(visitor: inout V) throws { - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every if/case branch local when no optimizations - // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and - // https://github.com/apple/swift-protobuf/issues/1182 - try { if let v = _storage._device { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } }() - try { if let v = _storage._position { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } }() - try { if let v = _storage._power { - try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } }() - try { if let v = _storage._wifi { - try visitor.visitSingularMessageField(value: v, fieldNumber: 4) - } }() - try { if let v = _storage._display { - try visitor.visitSingularMessageField(value: v, fieldNumber: 5) - } }() - try { if let v = _storage._lora { - try visitor.visitSingularMessageField(value: v, fieldNumber: 6) - } }() - } - try unknownFields.traverse(visitor: &visitor) - } - - static func ==(lhs: LocalConfig, rhs: LocalConfig) -> Bool { - if lhs._storage !== rhs._storage { - let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in - let _storage = _args.0 - let rhs_storage = _args.1 - if _storage._device != rhs_storage._device {return false} - if _storage._position != rhs_storage._position {return false} - if _storage._power != rhs_storage._power {return false} - if _storage._wifi != rhs_storage._wifi {return false} - if _storage._display != rhs_storage._display {return false} - if _storage._lora != rhs_storage._lora {return false} - return true - } - if !storagesAreEqual {return false} - } - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = "LocalModuleConfig" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "mqtt"), - 2: .same(proto: "serial"), - 3: .standard(proto: "external_notification"), - 4: .standard(proto: "store_forward"), - 5: .standard(proto: "range_test"), - 6: .same(proto: "telemetry"), - 7: .standard(proto: "canned_message"), - ] - - fileprivate class _StorageClass { - var _mqtt: ModuleConfig.MQTTConfig? = nil - var _serial: ModuleConfig.SerialConfig? = nil - var _externalNotification: ModuleConfig.ExternalNotificationConfig? = nil - var _storeForward: ModuleConfig.StoreForwardConfig? = nil - var _rangeTest: ModuleConfig.RangeTestConfig? = nil - var _telemetry: ModuleConfig.TelemetryConfig? = nil - var _cannedMessage: ModuleConfig.CannedMessageConfig? = nil - - static let defaultInstance = _StorageClass() - - private init() {} - - init(copying source: _StorageClass) { - _mqtt = source._mqtt - _serial = source._serial - _externalNotification = source._externalNotification - _storeForward = source._storeForward - _rangeTest = source._rangeTest - _telemetry = source._telemetry - _cannedMessage = source._cannedMessage - } - } - - fileprivate mutating func _uniqueStorage() -> _StorageClass { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _StorageClass(copying: _storage) - } - return _storage - } - - mutating func decodeMessage(decoder: inout D) throws { - _ = _uniqueStorage() - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularMessageField(value: &_storage._mqtt) }() - case 2: try { try decoder.decodeSingularMessageField(value: &_storage._serial) }() - case 3: try { try decoder.decodeSingularMessageField(value: &_storage._externalNotification) }() - case 4: try { try decoder.decodeSingularMessageField(value: &_storage._storeForward) }() - case 5: try { try decoder.decodeSingularMessageField(value: &_storage._rangeTest) }() - case 6: try { try decoder.decodeSingularMessageField(value: &_storage._telemetry) }() - case 7: try { try decoder.decodeSingularMessageField(value: &_storage._cannedMessage) }() - default: break - } - } - } - } - - func traverse(visitor: inout V) throws { - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every if/case branch local when no optimizations - // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and - // https://github.com/apple/swift-protobuf/issues/1182 - try { if let v = _storage._mqtt { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } }() - try { if let v = _storage._serial { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } }() - try { if let v = _storage._externalNotification { - try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } }() - try { if let v = _storage._storeForward { - try visitor.visitSingularMessageField(value: v, fieldNumber: 4) - } }() - try { if let v = _storage._rangeTest { - try visitor.visitSingularMessageField(value: v, fieldNumber: 5) - } }() - try { if let v = _storage._telemetry { - try visitor.visitSingularMessageField(value: v, fieldNumber: 6) - } }() - try { if let v = _storage._cannedMessage { - try visitor.visitSingularMessageField(value: v, fieldNumber: 7) - } }() - } - try unknownFields.traverse(visitor: &visitor) - } - - static func ==(lhs: LocalModuleConfig, rhs: LocalModuleConfig) -> Bool { - if lhs._storage !== rhs._storage { - let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in - let _storage = _args.0 - let rhs_storage = _args.1 - if _storage._mqtt != rhs_storage._mqtt {return false} - if _storage._serial != rhs_storage._serial {return false} - if _storage._externalNotification != rhs_storage._externalNotification {return false} - if _storage._storeForward != rhs_storage._storeForward {return false} - if _storage._rangeTest != rhs_storage._rangeTest {return false} - if _storage._telemetry != rhs_storage._telemetry {return false} - if _storage._cannedMessage != rhs_storage._cannedMessage {return false} - return true - } - if !storagesAreEqual {return false} - } - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} diff --git a/MeshtasticClient/Views/Settings/ShareChannel.swift b/MeshtasticClient/Views/Settings/ShareChannel.swift index 1756d719..1e252d7a 100644 --- a/MeshtasticClient/Views/Settings/ShareChannel.swift +++ b/MeshtasticClient/Views/Settings/ShareChannel.swift @@ -36,8 +36,9 @@ struct ShareChannel: View { @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings + let channelSet = ChannelSet() - @State private var text = "meshtastic.org" + @State private var text = "https://www.meshtastic.org/e/#" var qrCodeImage = QrCodeImage() var body: some View {