diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index be4210bf..4d2e9cf9 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ DD3CC6B528E33FD100FA9159 /* ShareChannels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6B428E33FD100FA9159 /* ShareChannels.swift */; }; 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 */; }; 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 */; }; @@ -138,6 +139,7 @@ DD3CC6B428E33FD100FA9159 /* ShareChannels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannels.swift; sourceTree = ""; }; DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModel.xcdatamodel; sourceTree = ""; }; DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryGauge.swift; sourceTree = ""; }; + DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingEnums.swift; sourceTree = ""; }; DD4033C128B286B70096A444 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = ""; }; DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = ""; }; @@ -349,6 +351,7 @@ DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */, DD1925B828CDA93900720036 /* SerialConfigEnums.swift */, DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */, + DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */, ); path = Enums; sourceTree = ""; @@ -764,6 +767,7 @@ DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */, DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */, DD73FD1128750779000852D6 /* PositionLog.swift in Sources */, + DD3CC6C028E7A60700FA9159 /* MessagingEnums.swift in Sources */, C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */, DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */, DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */, diff --git a/Meshtastic/Enums/MessagingEnums.swift b/Meshtastic/Enums/MessagingEnums.swift new file mode 100644 index 00000000..637cfdc6 --- /dev/null +++ b/Meshtastic/Enums/MessagingEnums.swift @@ -0,0 +1,11 @@ +// +// MessagingEnums.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 9/30/22. +// + +enum BubblePosition { + case left + case right +} diff --git a/Meshtastic/Export/WriteCsvFile.swift b/Meshtastic/Export/WriteCsvFile.swift index accb70a3..e5cd7843 100644 --- a/Meshtastic/Export/WriteCsvFile.swift +++ b/Meshtastic/Export/WriteCsvFile.swift @@ -34,14 +34,14 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin } } - } else { + } else if metricsType == 1 { // Create Environment Telemetry Header csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, Voltage, Current" for dm in telemetry{ - if dm.metricsType == 0 { + if dm.metricsType == 1 { csvString += "\n" csvString += String("\(dm.temperature)°") diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index cd655482..e62f3766 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -63,17 +63,10 @@ struct Connect: View { if bleManager.connectedPeripheral != nil { Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.firmwareVersion) .font(.caption).foregroundColor(Color.gray) - Text("Bitrate: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.bitrate ?? 0.00)) - .font(.caption).foregroundColor(Color.gray) - - - Text("Channel Utilization: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.channelUtilization ?? 0.00)) - .font(.caption).foregroundColor(Color.gray) - Text("Air Time: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.airTime ?? 0.00)) - .font(.caption).foregroundColor(Color.gray) } if bleManager.connectedPeripheral.subscribed { Text("Properly Subscribed").font(.caption) + .foregroundColor(.green) } else { Text("Attempting to connect. . . ").font(.caption) .foregroundColor(.orange) @@ -113,6 +106,8 @@ struct Connect: View { } } + .font(.caption).foregroundColor(Color.gray) + .padding([.top, .bottom]) .swipeActions { Button(role: .destructive) { @@ -124,8 +119,13 @@ struct Connect: View { Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash") } } - .padding([.top, .bottom]) - + .contextMenu{ + + Text("My Node Info") + Label("Bitrate \(String(format: "%.2f", bleManager.connectedPeripheral.bitrate ?? 0.00))", systemImage: "pencil.circle") + Text("Ch. Utilization \(String(format: "%.2f", bleManager.connectedPeripheral.channelUtilization ?? 0.00))") + Text("Air Time \(String(format: "%.2f", bleManager.connectedPeripheral.airTime ?? 0.00))") + } } else { HStack { diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 655e693e..6b17cbbc 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -38,9 +38,6 @@ struct EnvironmentMetricsLog: View { Text("Gas") .font(.caption) .fontWeight(.bold) - Text("Gas") - .font(.caption) - .fontWeight(.bold) Text("DC") .font(.caption) .fontWeight(.bold) diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 24b2e00c..2c335e4f 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -13,7 +13,7 @@ struct PositionFlags: OptionSet static let Altitude = PositionFlags(rawValue: 1) static let AltitudeMsl = PositionFlags(rawValue: 2) - static let GeoSep = PositionFlags(rawValue: 4) + static let GeoidalSeparation = PositionFlags(rawValue: 4) static let Dop = PositionFlags(rawValue: 8) static let Hvdop = PositionFlags(rawValue: 16) static let Satsinview = PositionFlags(rawValue: 32) @@ -48,7 +48,7 @@ struct PositionConfig: View { /// Altitude value is MSL - 2 @State var includeAltitudeMsl = false /// Include geoidal separation - 4 - @State var includeGeoSep = false + @State var includeGeoidalSeparation = false /// Include the DOP value ; PDOP used by default, see below - 8 @State var includeDop = false /// If POS_DOP set, send separate HDOP / VDOP values instead of PDOP - 16 @@ -56,7 +56,7 @@ struct PositionConfig: View { /// Include number of "satellites in view" - 32 @State var includeSatsinview = false /// Include a sequence number incremented per packet - 64 - @State var includeSeqNos = false + @State var includeSeqNo = false /// Include positional timestamp (from GPS solution) - 128 @State var includeTimestamp = false /// Include positional heading - 256 @@ -162,7 +162,7 @@ struct PositionConfig: View { } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includeSeqNos) { //64 + Toggle(isOn: $includeSeqNo) { //64 Label("Sequence number", systemImage: "number") } @@ -188,7 +188,7 @@ struct PositionConfig: View { } Section(header: Text("Advanced Position Flags")) { - Toggle(isOn: $includeGeoSep) { + Toggle(isOn: $includeGeoidalSeparation) { Text("Geoidal Seperation") } @@ -242,11 +242,11 @@ struct PositionConfig: View { if includeAltitude { pf.insert(.Altitude) } if includeAltitudeMsl { pf.insert(.AltitudeMsl) } - if includeGeoSep { pf.insert(.GeoSep) } + if includeGeoidalSeparation { pf.insert(.GeoidalSeparation) } if includeDop { pf.insert(.Dop) } if includeHvdop { pf.insert(.Hvdop) } if includeSatsinview { pf.insert(.Satsinview) } - if includeSeqNos { pf.insert(.SeqNos) } + if includeSeqNo { pf.insert(.SeqNos) } if includeTimestamp { pf.insert(.Timestamp) } if includeSpeed { pf.insert(.Speed) } if includeHeading { pf.insert(.Heading) } @@ -296,11 +296,11 @@ struct PositionConfig: View { if pf.contains(.Altitude) { self.includeAltitude = true } else { self.includeAltitude = false } if pf.contains(.AltitudeMsl) { self.includeAltitudeMsl = true } else { self.includeAltitudeMsl = false } - if pf.contains(.GeoSep) { self.includeGeoSep = true } else { self.includeGeoSep = false } + if pf.contains(.GeoidalSeparation) { self.includeGeoidalSeparation = true } else { self.includeGeoidalSeparation = false } if pf.contains(.Dop) { self.includeDop = true } else { self.includeDop = false } if pf.contains(.Hvdop) { self.includeHvdop = true } else { self.includeHvdop = false } if pf.contains(.Satsinview) { self.includeSatsinview = true } else { self.includeSatsinview = false } - if pf.contains(.SeqNos) { self.includeSeqNos = true } else { self.includeSeqNos = false } + if pf.contains(.SeqNos) { self.includeSeqNo = true } else { self.includeSeqNo = false } if pf.contains(.Timestamp) { self.includeTimestamp = true } else { self.includeTimestamp = false } if pf.contains(.Speed) { self.includeSpeed = true } else { self.includeSpeed = false } if pf.contains(.Heading) { self.includeHeading = true } else { self.includeHeading = false } @@ -352,7 +352,7 @@ struct PositionConfig: View { if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true } } } - .onChange(of: includeAltitude || includeAltitudeMsl || includeGeoSep || includeDop || includeHvdop || includeSatsinview || includeSeqNos || includeTimestamp || includeSpeed || includeHeading) { newFlags in + .onChange(of: includeAltitude || includeAltitudeMsl || includeGeoidalSeparation || includeDop || includeHvdop || includeSatsinview || includeSeqNo || includeTimestamp || includeSpeed || includeHeading) { newFlags in // hasChanges = true } .navigationViewStyle(StackNavigationViewStyle()) diff --git a/Meshtastic/Views/Settings/MeshLog.swift b/Meshtastic/Views/Settings/MeshLog.swift index 1728f5ee..b9cfd355 100644 --- a/Meshtastic/Views/Settings/MeshLog.swift +++ b/Meshtastic/Views/Settings/MeshLog.swift @@ -17,11 +17,33 @@ struct MeshLog: View { let url = logFile! logs.removeAll() - for try await log in url.lines { - logs.append(log) - document.logFile.append("\(log) \n") + + var lineCount = 0 + let lineLimit = 500 + + // Get the number of lines + for try await _ in url.lines { + lineCount += 1 } - logs.reverse() + + var startingLog = 0 + // Set the record to start with if we have more lines than the limit + if lineCount > lineLimit { + startingLog = lineCount - lineLimit + } + + var lineNumber = 0 + + for try await log in url.lines { + if lineNumber >= startingLog { + + logs.append(log) + document.logFile.append("\(log) \n") + } + lineNumber += 1 + } + logs.reverse() + } catch { // Stop adding logs when an error is thrown } diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 6ce8dae1..ea89cb6d 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -46,7 +46,7 @@ struct ShareChannels: View { var node: NodeInfoEntity? - @State private var text = "https://meshtastic.org/E/#test" + @State private var channelsUrl = "https://meshtastic.org/e/#test" var qrCodeImage = QrCodeImage() var body: some View { @@ -133,47 +133,41 @@ struct ShareChannels: View { } } } - let image = qrCodeImage.generateQRCode(from: text) + let qrImage = qrCodeImage.generateQRCode(from: channelsUrl) VStack { Divider() - + ShareLink("Share QR Code & Link", - item: Image(uiImage: image), - subject: Text("Meshtastic Channel Settings From Node \(node?.user?.shortName ?? "????")"), - message: Text("Open the link or scan the QR code on Android, iOS, iPadOS or macOS with the Meshtastic app and you will be prompted to save these channel settings to your device: \(text)"), - preview: SharePreview("Meshtastic Channel Settings From Node \(node?.user?.shortName ?? "????")", - image: Image(uiImage: image)) + item: Image(uiImage: qrImage), + subject: Text("Meshtastic Node \(node?.user?.shortName ?? "????") has shared channels with you"), + message: Text(channelsUrl), + preview: SharePreview("Meshtastic Node \(node?.user?.shortName ?? "????") has shared channels with you", + image: Image(uiImage: qrImage)) ) .presentationDetents([.large, .large]) - .font(.title3) - - Divider() - Image(uiImage: image) + Divider() + + Image(uiImage: qrImage) .resizable() .scaledToFit() .frame( - minWidth: smallest * 0.75, - maxWidth: smallest * 0.75, - minHeight: smallest * 0.75, - maxHeight: smallest * 0.75, - alignment: .center + minWidth: smallest * 0.7, + maxWidth: smallest * 0.7, + minHeight: smallest * 0.7, + maxHeight: smallest * 0.7, + alignment: .top ) - Divider() - - VStack { - - } } } - .navigationTitle("Share Channels") - .navigationBarTitleDisplayMode(.automatic) + .navigationTitle("Generate QR Code") + .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: - ZStack { + ZStack { ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") })