From a8d08c0b9f83abeac8561b5fc101ea505a3596c0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 24 Dec 2021 21:50:10 -0800 Subject: [PATCH] V 1.35 Connected Peripheral and MyInfo bugs --- Meshtastic Client.xcodeproj/project.pbxproj | 4 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 32 ++----- MeshtasticClient/Helpers/BLEManager.swift | 69 ++++++++------- .../CoreDataSample.xcdatamodel/contents | 3 +- MeshtasticClient/MeshtasticClientApp.swift | 12 +-- MeshtasticClient/Views/ContentView.swift | 9 ++ .../Views/Messages/Contacts.swift | 85 ++++++++++++++++++- MeshtasticClient/Views/Nodes/NodeDetail.swift | 73 ++++++++-------- 8 files changed, 183 insertions(+), 104 deletions(-) diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 5f4628ec..ac85eaa9 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -695,7 +695,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.33; + MARKETING_VERSION = 1.35; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -722,7 +722,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.33; + MARKETING_VERSION = 1.35; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index bd8f0435..5acce548 100644 --- a/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -80,22 +80,6 @@ landmarkType = "24"> - - - - @@ -115,15 +99,15 @@ @@ -131,15 +115,15 @@ diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index ea483441..9af3fb15 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -25,7 +25,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph @Published var peripherals = [Peripheral]() @Published var connectedPeripheral: Peripheral! - //@Published var connectedNode: NodeInfoEntity! @Published var lastConnectedPeripheral: String @Published var lastConnectionError: String @@ -220,6 +219,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } catch { print("💥 Fetch NodeInfo Failed") + if meshLoggingEnabled { MeshLogger.log("💥 Fetch NodeInfo Failed") } } lastConnectedPeripheral = peripheral.identifier.uuidString @@ -257,8 +257,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // We will try and re-connect to this device lastConnectionError = "🚫 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds." if peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" { - if meshLoggingEnabled { MeshLogger.log("BLE Reconnecting: \(peripheral.name ?? "Unknown")") } - print("BLE Reconnecting: \(peripheral.name ?? "Unknown")") + if meshLoggingEnabled { MeshLogger.log("ℹ️ BLE Reconnecting: \(peripheral.name ?? "Unknown")") } + print("ℹ️ BLE Reconnecting: \(peripheral.name ?? "Unknown")") self.connectTo(peripheral: peripheral) } } else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us. @@ -340,7 +340,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph peripheral.readValue(for: FROMRADIO_characteristic) case FROMNUM_UUID: - print("FROMNUM (Notify) characteristic OK") + print("✅ FROMNUM (Notify) characteristic OK") if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") } FROMNUM_characteristic = characteristic peripheral.setNotifyValue(true, for: characteristic) @@ -390,14 +390,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph var decodedInfo = FromRadio() decodedInfo = try! FromRadio(serializedData: characteristic.value!) - print("Print DecodedInfo") - print(decodedInfo) + //print("Print DecodedInfo") + //print(decodedInfo) // MyInfo Data if decodedInfo.myInfo.myNodeNum != 0 { - print("💾 Save a CoreData MyInfoEntity") - let fetchMyInfoRequest:NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.myInfo.myNodeNum)) @@ -413,6 +411,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph myInfo.messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec) myInfo.minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion) myInfo.maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels) + connectedPeripheral.num = myInfo.myNodeNum + connectedPeripheral.firmwareVersion = myInfo.firmwareVersion ?? "Unknown" + } else { @@ -439,17 +440,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } } catch { - print("💥 Fetch MyInfo Error") + print("💥 Fetch MyInfo Error") } - } // NodeInfo Data if decodedInfo.nodeInfo.num != 0 { - print("💾 Save a CoreData NodeInfoEntity") - let fetchNodeRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.nodeInfo.num)) @@ -469,12 +467,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if self.connectedPeripheral != nil && self.connectedPeripheral.num == newNode.id { - newNode.bleName = self.connectedPeripheral.name - - } else { - - let userIdLast4: String = String(decodedInfo.nodeInfo.user.id.suffix(4)) - newNode.bleName = "Meshtastic_" + userIdLast4 + newNode.bleName = self.connectedPeripheral.peripheral.name + if decodedInfo.nodeInfo.hasUser { + + connectedPeripheral.name = decodedInfo.nodeInfo.user.longName + connectedPeripheral.longName = decodedInfo.nodeInfo.user.longName + connectedPeripheral.shortName = decodedInfo.nodeInfo.user.shortName + } } if decodedInfo.nodeInfo.hasUser { @@ -486,6 +485,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph newUser.shortName = decodedInfo.nodeInfo.user.shortName newUser.macaddr = decodedInfo.nodeInfo.user.macaddr newUser.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased() + newUser.team = (String(describing: decodedInfo.nodeInfo.user.team)) newNode.user = newUser } @@ -530,6 +530,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph fetchedNode[0].user!.longName = decodedInfo.nodeInfo.user.longName fetchedNode[0].user!.shortName = decodedInfo.nodeInfo.user.shortName fetchedNode[0].user!.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased() + fetchedNode[0].user!.team = (String(describing: decodedInfo.nodeInfo.user.team)) } let position = PositionEntity(context: context!) @@ -625,9 +626,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if decodedInfo.packet.to == broadcastNodeNum && fetchedUsers.count == 1 { + // Save the broadcast user if it does not exist let bcu: UserEntity = UserEntity(context: context!) - bcu.shortName = "BC" - bcu.longName = "Broadcast" + bcu.shortName = "ALL" + bcu.longName = "Broadcast (^all)" bcu.hwModel = "UNSET" bcu.num = Int64(broadcastNodeNum) bcu.userId = "BROADCASTNODE" @@ -693,12 +695,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return } do { - // print(decodedInfo.packet.decoded.payload) + try context!.save() - if meshLoggingEnabled { - MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(Int64(decodedInfo.nodeInfo.num))") - } + if meshLoggingEnabled { MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(Int64(decodedInfo.nodeInfo.num))")} print("💾 Updated NodeInfo SNR and Time from Packet For: \(fetchedNode[0].num)") } catch { @@ -714,8 +714,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP") } - print(decodedInfo.packet.decoded.payload) - } else if decodedInfo.packet.decoded.portnum == PortNum.positionApp { let fetchNodePositionRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -728,7 +726,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if fetchedNode.count == 1 { fetchedNode[0].id = Int64(decodedInfo.packet.from) fetchedNode[0].num = Int64(decodedInfo.packet.from) - print(decodedInfo.packet.decoded.payload) if(decodedInfo.packet.rxTime == 0) { fetchedNode[0].lastHeard = Date() @@ -830,9 +827,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph success = false } else if message.count < 1 { - // Don's send an empty message - print("Don't Send an Empty Message") + + // Don't send an empty message + print("🚫 Don't Send an Empty Message") success = false + } else { let fromUserNum:Int64 = self.connectedPeripheral.num @@ -846,7 +845,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if fetchedUsers.isEmpty { - print("Message Users Not Found, Fail") + print("🚫 Message Users Not Found, Fail") success = false } else if fetchedUsers.count >= 1 { @@ -860,8 +859,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if newMessage.toUser == nil { let bcu: UserEntity = UserEntity(context: context!) - bcu.shortName = "BC" - bcu.longName = "Broadcast" + bcu.shortName = "ALL" + bcu.longName = "Broadcast (^all)" bcu.hwModel = "UNSET" bcu.num = Int64(broadcastNodeNum) bcu.userId = "BROADCASTNODE" @@ -888,6 +887,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph toRadio.packet = meshPacket let binaryData: Data = try! toRadio.serializedData() + + if meshLoggingEnabled { MeshLogger.log("📲 New message sent to \(newMessage.toUser?.longName! ?? "Unknown")") } + print("📲 New message sent to \(newMessage.toUser?.longName! ?? "Unknown")") + if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) do { @@ -903,7 +906,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph context!.rollback() let nsError = error as NSError - print("Unresolved error \(nsError)") + print("🚫 Unresolved error \(nsError)") } } } diff --git a/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents b/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents index b966753b..3cce2d60 100644 --- a/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents +++ b/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents @@ -58,6 +58,7 @@ + @@ -68,6 +69,6 @@ - + \ No newline at end of file diff --git a/MeshtasticClient/MeshtasticClientApp.swift b/MeshtasticClient/MeshtasticClientApp.swift index 66a2c13d..8bf762bc 100644 --- a/MeshtasticClient/MeshtasticClientApp.swift +++ b/MeshtasticClient/MeshtasticClientApp.swift @@ -21,22 +21,22 @@ struct MeshtasticClientApp: App { .onChange(of: scenePhase) { (newScenePhase) in switch newScenePhase { case .background: + print("ℹ️ Scene is in the background") do { try persistenceController.container.viewContext.save() - print("Saved viewContext when the app went to the background.") + print("💾 Saved CoreData ViewContext when the app went to the background.") } catch { - print("Failed to save viewContext when the app goes to the background.") + print("💥 Failed to save viewContext when the app goes to the background.") } - print("Scene is in the background") case .inactive: - print("Scene is inactive") + print("ℹ️ Scene is inactive") case .active: - print("Scene is active") + print("ℹ️ Scene is active") @unknown default: - print("Apple must have changed something") + print("💥 Apple must have changed something") } } } diff --git a/MeshtasticClient/Views/ContentView.swift b/MeshtasticClient/Views/ContentView.swift index f950a0fc..ca20500d 100644 --- a/MeshtasticClient/Views/ContentView.swift +++ b/MeshtasticClient/Views/ContentView.swift @@ -8,6 +8,7 @@ struct ContentView: View { @State private var selection: Tab = .ble enum Tab { + case contacts case messages case map case ble @@ -18,6 +19,14 @@ struct ContentView: View { var body: some View { TabView(selection: $selection) { +// Contacts() +// .tabItem { +// Label("Contacts", systemImage: "person.crop.circle") +// .symbolRenderingMode(.hierarchical) +// .symbolVariant(.none) +// +// } +// .tag(Tab.contacts) Channels() .tabItem { Label("Messages", systemImage: "text.bubble") diff --git a/MeshtasticClient/Views/Messages/Contacts.swift b/MeshtasticClient/Views/Messages/Contacts.swift index 66093924..8f071694 100644 --- a/MeshtasticClient/Views/Messages/Contacts.swift +++ b/MeshtasticClient/Views/Messages/Contacts.swift @@ -8,8 +8,91 @@ import SwiftUI struct Contacts: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @FetchRequest( + sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)], + animation: .default) + + private var users: FetchedResults + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + NavigationView { + List(users) { user in + + if user.receivedMessages?.count ?? 0 > 0 { + + let mostRecent = user.receivedMessages?.lastObject as! MessageEntity + let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64(mostRecent.messageTimestamp))) + let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 + let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 + + HStack { + VStack { + CircleText(text: user.shortName ?? "???", color: Color.blue) + } + VStack { + + HStack (alignment: .bottom){ + + VStack { + Text(user.longName ?? "Unknown").font(.headline) + } + + VStack { + if lastMessageDay == currentDay { + + Text(lastMessageTime, style: .time ) + .font(.caption) + .foregroundColor(.gray) + + } else if ( lastMessageDay == (currentDay - 1)) { + + Text("Yesterday") + .font(.callout) + .foregroundColor(.gray) + + } else if ( lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) ) { + + Text(lastMessageTime, style: .date) + + } else { + + Text(lastMessageTime, style: .date) + } + }.frame(maxWidth: .infinity, alignment: .trailing) + } + .listRowSeparator(.hidden).frame(height: 5) + HStack (alignment: .top) { + + Text(mostRecent.messagePayload ?? "EMPTY MESSSAGE") + .frame(height: 60) + .truncationMode(.tail) + } + } + }.padding(10) + } else { + HStack { + VStack { + CircleText(text: user.shortName ?? "???", color: Color.blue) + } + VStack { + + HStack{ + + VStack { + Text(user.longName ?? "Unknown").font(.title3) + } + } + } + }.padding() + } + //NavigationLink(note.title, destination: NoteEditor(id: note.id)) + } + .navigationTitle("Contacts") + } } } diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index d491a887..eec2b014 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -175,7 +175,7 @@ struct NodeDetail: View { } }.padding() - if node.positions?.count ?? 0 > 0 { + if node.positions?.count ?? 0 > 1 { Divider() @@ -193,52 +193,51 @@ struct NodeDetail: View { ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in - //if mappin.coordinate != nil { + if mappin.coordinate != nil { VStack { - HStack { - - Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) //.font(.subheadline) - Text("Lat/Long:").font(.caption) - Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))") - .foregroundColor(.gray) - .font(.caption) + HStack { - Text("Altitude:") - .font(.caption) - - Text("\(String(mappin.altitude))m") - .foregroundColor(.gray) - .font(.caption) + Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) //.font(.subheadline) + Text("Lat/Long:").font(.caption) + Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))") + .foregroundColor(.gray) + .font(.caption) + + Text("Altitude:") + .font(.caption) + + Text("\(String(mappin.altitude))m") + .foregroundColor(.gray) + .font(.caption) + } + HStack { + + Image(systemName: "clock.badge.checkmark.fill") + .font(.subheadline) + .foregroundColor(.accentColor) + .symbolRenderingMode(.hierarchical) + Text("Time:") + .font(.caption) + Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)") + .foregroundColor(.gray) + .font(.caption) + Divider() + + Text("Battery").font(.caption).fixedSize() + Text(String(mappin.batteryLevel) + "%") + .font(.caption) + .foregroundColor(.gray) + .symbolRenderingMode(.hierarchical) + } } - HStack { - - Image(systemName: "clock.badge.checkmark.fill") - .font(.subheadline) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Time:") - .font(.caption) - Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)") - .foregroundColor(.gray) - .font(.caption) - Divider() - - Text("Battery").font(.caption).fixedSize() - Text(String(mappin.batteryLevel) + "%") - .font(.caption) - .foregroundColor(.gray) - .symbolRenderingMode(.hierarchical) - } - } .padding(1) Divider() - //} + } } .padding(.bottom, 5) // Without some padding here there is a transparent contentview bug } - } } .navigationTitle(node.user!.longName ?? "Unknown")