From a89490097f1885c917953d518dd3818cf2a797bf Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 10 Feb 2024 17:21:31 -0800 Subject: [PATCH] Add Singapore Update store and forward logic --- Meshtastic.xcodeproj/project.pbxproj | 4 +- Meshtastic/Enums/LoraConfigEnums.swift | 7 + Meshtastic/Helpers/BLEManager.swift | 75 +++- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 415 ++++++++++++++++++ Meshtastic/Persistence/UpdateCoreData.swift | 1 + .../Protobufs/meshtastic/config.pb.swift | 8 + .../Protobufs/meshtastic/portnums.pb.swift | 9 + .../Views/Nodes/Helpers/NodeListItem.swift | 6 +- Meshtastic/Views/Nodes/NodeList.swift | 20 + Meshtastic/Views/Settings/Firmware.swift | 2 +- 11 files changed, 521 insertions(+), 28 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 26.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 37094294..6b056a19 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -229,6 +229,7 @@ C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = ""; }; DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoEntityExtension.swift; sourceTree = ""; }; DD007BAF2AA5981000F5FA12 /* NodeInfoEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoEntityExtension.swift; sourceTree = ""; }; + DD05296F2B77F454008E44CD /* MeshtasticDataModelV 26.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 26.xcdatamodel"; sourceTree = ""; }; DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = ""; }; DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = ""; }; DD13AA482AB73BF400BA0C98 /* PositionPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionPopover.swift; sourceTree = ""; }; @@ -1793,6 +1794,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD05296F2B77F454008E44CD /* MeshtasticDataModelV 26.xcdatamodel */, DD23D9AB2B7133F6003F5CBE /* MeshtasticDataModelV 25.xcdatamodel */, DDB234392B5CA9B000DA6FB1 /* MeshtasticDataModelV 24.xcdatamodel */, DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */, @@ -1819,7 +1821,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DD23D9AB2B7133F6003F5CBE /* MeshtasticDataModelV 25.xcdatamodel */; + currentVersion = DD05296F2B77F454008E44CD /* MeshtasticDataModelV 26.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift index 3e638773..53084e54 100644 --- a/Meshtastic/Enums/LoraConfigEnums.swift +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -26,6 +26,7 @@ enum RegionCodes: Int, CaseIterable, Identifiable { case ua868 = 15 case my_433 = 16 case my_919 = 17 + case sg_923 = 18 case lora24 = 13 var id: Int { self.rawValue } @@ -67,6 +68,8 @@ enum RegionCodes: Int, CaseIterable, Identifiable { return "Malaysia 433mhz" case .my_919: return "Malaysia 919mhz" + case .sg_923: + return "Singapore 923mhz" } } var dutyCycle: Int { @@ -107,6 +110,8 @@ enum RegionCodes: Int, CaseIterable, Identifiable { return 100 case .my_919: return 100 + case .sg_923: + return 100 } } func protoEnumValue() -> Config.LoRaConfig.RegionCode { @@ -148,6 +153,8 @@ enum RegionCodes: Int, CaseIterable, Identifiable { return Config.LoRaConfig.RegionCode.my433 case .my_919: return Config.LoRaConfig.RegionCode.my919 + case .sg_923: + return Config.LoRaConfig.RegionCode.sg923 } } } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 5e9e19c1..33c64012 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -703,6 +703,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate MeshLogger.log("🕸️ MESH PACKET received for Other App UNHANDLED \(try! decodedInfo.packet.jsonString())") case .max: print("MAX PORT NUM OF 511") + case .atakPlugin: + MeshLogger.log("🕸️ MESH PACKET received for ATAK Plugin App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") } if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce { @@ -2367,6 +2369,36 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate return false } + public func requestStoreAndForwardClientHistory(fromUser: UserEntity, toUser: UserEntity) -> Bool { + + /// send a request for ClientHistory with a time period matching the heartbeat + var sfPacket = StoreAndForward() + sfPacket.rr = StoreAndForward.RequestResponse.clientHistory + sfPacket.history.window = 120 // storeAndForwardMessage.heartbeat.period + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(toUser.num) + meshPacket.from = UInt32(fromUser.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. _XCCurrentVersionName - MeshtasticDataModelV 25.xcdatamodel + MeshtasticDataModelV 26.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 26.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 26.xcdatamodel/contents new file mode 100644 index 00000000..9dec53eb --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 26.xcdatamodel/contents @@ -0,0 +1,415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index b6aae30b..b2822b05 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -282,6 +282,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) fetchedNode[0].snr = packet.rxSnr fetchedNode[0].rssi = packet.rxRssi fetchedNode[0].viaMqtt = packet.viaMqtt + fetchedNode[0].channel = Int32(packet.channel) fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet do { diff --git a/Meshtastic/Protobufs/meshtastic/config.pb.swift b/Meshtastic/Protobufs/meshtastic/config.pb.swift index ef43b94a..38991510 100644 --- a/Meshtastic/Protobufs/meshtastic/config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/config.pb.swift @@ -1168,6 +1168,10 @@ struct Config { /// /// Malaysia 919mhz case my919 // = 17 + + /// + /// Singapore 923mhz + case sg923 // = 18 case UNRECOGNIZED(Int) init() { @@ -1194,6 +1198,7 @@ struct Config { case 15: self = .ua868 case 16: self = .my433 case 17: self = .my919 + case 18: self = .sg923 default: self = .UNRECOGNIZED(rawValue) } } @@ -1218,6 +1223,7 @@ struct Config { case .ua868: return 15 case .my433: return 16 case .my919: return 17 + case .sg923: return 18 case .UNRECOGNIZED(let i): return i } } @@ -1488,6 +1494,7 @@ extension Config.LoRaConfig.RegionCode: CaseIterable { .ua868, .my433, .my919, + .sg923, ] } @@ -2416,6 +2423,7 @@ extension Config.LoRaConfig.RegionCode: SwiftProtobuf._ProtoNameProviding { 15: .same(proto: "UA_868"), 16: .same(proto: "MY_433"), 17: .same(proto: "MY_919"), + 18: .same(proto: "SG_923"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift index 319d48f9..ea5ce5bd 100644 --- a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift @@ -176,6 +176,11 @@ enum PortNum: SwiftProtobuf.Enum { /// ENCODING: Protobuf case neighborinfoApp // = 71 + /// + /// ATAK Plugin + /// Portnum for payloads from the official Meshtastic ATAK plugin + case atakPlugin // = 72 + /// /// Private applications should use portnums >= 256. /// To simplify initial development and testing you can use "PRIVATE_APP" @@ -220,6 +225,7 @@ enum PortNum: SwiftProtobuf.Enum { case 69: self = .simulatorApp case 70: self = .tracerouteApp case 71: self = .neighborinfoApp + case 72: self = .atakPlugin case 256: self = .privateApp case 257: self = .atakForwarder case 511: self = .max @@ -251,6 +257,7 @@ enum PortNum: SwiftProtobuf.Enum { case .simulatorApp: return 69 case .tracerouteApp: return 70 case .neighborinfoApp: return 71 + case .atakPlugin: return 72 case .privateApp: return 256 case .atakForwarder: return 257 case .max: return 511 @@ -287,6 +294,7 @@ extension PortNum: CaseIterable { .simulatorApp, .tracerouteApp, .neighborinfoApp, + .atakPlugin, .privateApp, .atakForwarder, .max, @@ -325,6 +333,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding { 69: .same(proto: "SIMULATOR_APP"), 70: .same(proto: "TRACEROUTE_APP"), 71: .same(proto: "NEIGHBORINFO_APP"), + 72: .same(proto: "ATAK_PLUGIN"), 256: .same(proto: "PRIVATE_APP"), 257: .same(proto: "ATAK_FORWARDER"), 511: .same(proto: "MAX"), diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index 4dfb1af2..f4d87e08 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -95,14 +95,16 @@ struct NodeListItem: View { .font(.callout) .symbolRenderingMode(.hierarchical) Text("Channel: \(node.channel)") - .font(.callout) + .foregroundColor(.gray) + .font(.caption) } if node.viaMqtt && connectedNode != node.num { Image(systemName: "network") .symbolRenderingMode(.hierarchical) .font(.callout) Text("Via MQTT") - .font(.callout) + .foregroundColor(.gray) + .font(.caption) } } if !connected { diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index fec6c003..18b923ad 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -12,6 +12,7 @@ struct NodeList: View { @State private var columnVisibility = NavigationSplitViewVisibility.all @State private var selectedNode: NodeInfoEntity? @State private var isPresentingTraceRouteSentAlert = false + @State private var isPresentingClientHistorySentAlert = false @State private var isPresentingDeleteNodeAlert = false @State private var deleteNodeId: Int64 = 0 @@ -84,6 +85,17 @@ struct NodeList: View { } label: { Label("Trace Route", systemImage: "signpost.right.and.left") } + if true {//node?.storeForwardConfig != nil && node.storeForwardConfig?.isRouter ?? false { + + Button { + let success = bleManager.requestStoreAndForwardClientHistory(fromUser: connectedNode!.user!, toUser: node.user!) + if success { + isPresentingClientHistorySentAlert = true + } + } label: { + Label("Client History", systemImage: "envelope.arrow.triangle.branch") + } + } } if bleManager.connectedPeripheral != nil { Button (role: .destructive) { @@ -103,6 +115,14 @@ struct NodeList: View { } message: { Text("This could take a while, response will appear in the trace route log for the node it was sent to.") } + .alert( + "Store and Forward Client Hitory Request Sent", + isPresented: $isPresentingClientHistorySentAlert + ) { + Button("OK", role: .cancel) { } + } message: { + Text("Any messages you have missed will be delivered again.") + } } .searchable(text: nodesQuery, prompt: "Find a node") .navigationTitle(String.localizedStringWithFormat("nodes %@".localized, String(nodes.count))) diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 20efcff7..21aa3b82 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -12,7 +12,7 @@ struct Firmware: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager var node: NodeInfoEntity? - @State var minimumVersion = "2.2.17" + @State var minimumVersion = "2.2.21" @State var version = "" @State private var currentDevice: DeviceHardware? @State private var latestStable: FirmwareRelease?