diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 65b72c6b..c31ea0c8 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -14117,7 +14117,7 @@ } }, "mesh.log.traceroute.received.direct %@" : { - "extractionState" : "migrated", + "extractionState" : "manual", "localizations" : { "de" : { "stringUnit" : { @@ -21850,16 +21850,6 @@ }, "Trace Route Log" : { - }, - "Trace route received directly by %@ with a SNR of %@ dB" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Trace route received directly by %1$@ with a SNR of %2$@ dB" - } - } - } }, "Trace Route Sent" : { diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 7712dd38..15321b5d 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -834,130 +834,120 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate if let routingMessage = try? RouteDiscovery(serializedBytes: decodedInfo.packet.decoded.payload) { let traceRoute = getTraceRoute(id: Int64(decodedInfo.packet.decoded.requestID), context: context) traceRoute?.response = true - if routingMessage.route.count == 0 { - // Routing messages snr values are snr * 4 stored as an int - // If a traceroute snr value is unknown this field will contain INT8_MIN or -128 - // After converting to a float here, -32 is our unknown value. - let snr = routingMessage.snrBack.count > 0 ? (Float(routingMessage.snrBack[0]) / 4) : -32 - traceRoute?.snr = snr - let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.direct %@".localized, String(snr)) - MeshLogger.log("🪧 \(logString)") - } else { - guard let connectedNode = getNodeInfo(id: Int64(connectedPeripheral.num), context: context) else { - return + guard let connectedNode = getNodeInfo(id: Int64(connectedPeripheral.num), context: context) else { + return + } + var hopNodes: [TraceRouteHopEntity] = [] + let connectedHop = TraceRouteHopEntity(context: context) + connectedHop.time = Date() + connectedHop.num = connectedPeripheral.num + connectedHop.name = connectedNode.user?.longName ?? "???" + // If nil, set to unknown, INT8_MIN (-128) then divide by 4 + connectedHop.snr = Float(routingMessage.snrBack.last ?? -128) / 4 + if let mostRecent = traceRoute?.node?.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { + connectedHop.altitude = mostRecent.altitude + connectedHop.latitudeI = mostRecent.latitudeI + connectedHop.longitudeI = mostRecent.longitudeI + traceRoute?.hasPositions = true + } + var routeString = "\(connectedNode.user?.longName ?? "???") --> " + hopNodes.append(connectedHop) + traceRoute?.hopsTowards = Int32(routingMessage.route.count) + for (index, node) in routingMessage.route.enumerated() { + var hopNode = getNodeInfo(id: Int64(node), context: context) + if hopNode == nil && hopNode?.num ?? 0 > 0 && node != 4294967295 { + hopNode = createNodeInfo(num: Int64(node), context: context) } - var hopNodes: [TraceRouteHopEntity] = [] - let connectedHop = TraceRouteHopEntity(context: context) - connectedHop.time = Date() - connectedHop.num = connectedPeripheral.num - connectedHop.name = connectedNode.user?.longName ?? "???" - // If nil, set to unknown, INT8_MIN (-128) then divide by 4 - connectedHop.snr = Float(routingMessage.snrBack.last ?? -128) / 4 - if let mostRecent = traceRoute?.node?.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { - connectedHop.altitude = mostRecent.altitude - connectedHop.latitudeI = mostRecent.latitudeI - connectedHop.longitudeI = mostRecent.longitudeI - traceRoute?.hasPositions = true + let traceRouteHop = TraceRouteHopEntity(context: context) + traceRouteHop.time = Date() + if routingMessage.snrTowards.count >= index + 1 { + traceRouteHop.snr = Float(routingMessage.snrTowards[index]) / 4 + } else { + // If no snr in route, set unknown + traceRouteHop.snr = -32 } - var routeString = "\(connectedNode.user?.longName ?? "???") --> " - hopNodes.append(connectedHop) - traceRoute?.hopsTowards = Int32(routingMessage.route.count) - for (index, node) in routingMessage.route.enumerated() { - var hopNode = getNodeInfo(id: Int64(node), context: context) - if hopNode == nil && hopNode?.num ?? 0 > 0 && node != 4294967295 { - hopNode = createNodeInfo(num: Int64(node), context: context) - } - let traceRouteHop = TraceRouteHopEntity(context: context) - traceRouteHop.time = Date() - if routingMessage.snrTowards.count >= index + 1 { - traceRouteHop.snr = Float(routingMessage.snrTowards[index]) / 4 - } else { - // If no snr in route, set unknown - traceRouteHop.snr = -32 - } - if let hn = hopNode, hn.hasPositions { - if let mostRecent = hn.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { - traceRouteHop.altitude = mostRecent.altitude - traceRouteHop.latitudeI = mostRecent.latitudeI - traceRouteHop.longitudeI = mostRecent.longitudeI - traceRoute?.hasPositions = true - } + if let hn = hopNode, hn.hasPositions { + if let mostRecent = hn.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { + traceRouteHop.altitude = mostRecent.altitude + traceRouteHop.latitudeI = mostRecent.latitudeI + traceRouteHop.longitudeI = mostRecent.longitudeI + traceRoute?.hasPositions = true } - traceRouteHop.num = hopNode?.num ?? 0 - if hopNode != nil { - if decodedInfo.packet.rxTime > 0 { - hopNode?.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime))) - } + } + traceRouteHop.num = hopNode?.num ?? 0 + if hopNode != nil { + if decodedInfo.packet.rxTime > 0 { + hopNode?.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime))) } - hopNodes.append(traceRouteHop) - routeString += "\(hopNode?.user?.longName ?? (node == 4294967295 ? "Repeater" : String(hopNode?.num.toHex() ?? "unknown".localized))) \(hopNode?.viaMqtt ?? false ? "MQTT" : "") (\(traceRouteHop.snr != -32 ? String(traceRouteHop.snr) : "unknown ".localized)dB) --> " } - let destinationHop = TraceRouteHopEntity(context: context) - destinationHop.name = traceRoute?.node?.user?.longName ?? "unknown".localized - destinationHop.time = Date() - // If nil, set to unknown, INT8_MIN (-128) then divide by 4 - destinationHop.snr = Float(routingMessage.snrTowards.last ?? -128) / 4 - destinationHop.num = traceRoute?.node?.num ?? 0 - if let mostRecent = traceRoute?.node?.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { - destinationHop.altitude = mostRecent.altitude - destinationHop.latitudeI = mostRecent.latitudeI - destinationHop.longitudeI = mostRecent.longitudeI - traceRoute?.hasPositions = true + hopNodes.append(traceRouteHop) + routeString += "\(hopNode?.user?.longName ?? (node == 4294967295 ? "Repeater" : String(hopNode?.num.toHex() ?? "unknown".localized))) \(hopNode?.viaMqtt ?? false ? "MQTT" : "") (\(traceRouteHop.snr != -32 ? String(traceRouteHop.snr) : "unknown ".localized)dB) --> " + } + let destinationHop = TraceRouteHopEntity(context: context) + destinationHop.name = traceRoute?.node?.user?.longName ?? "unknown".localized + destinationHop.time = Date() + // If nil, set to unknown, INT8_MIN (-128) then divide by 4 + destinationHop.snr = Float(routingMessage.snrTowards.last ?? -128) / 4 + destinationHop.num = traceRoute?.node?.num ?? 0 + if let mostRecent = traceRoute?.node?.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { + destinationHop.altitude = mostRecent.altitude + destinationHop.latitudeI = mostRecent.latitudeI + destinationHop.longitudeI = mostRecent.longitudeI + traceRoute?.hasPositions = true + } + hopNodes.append(destinationHop) + /// Add the destination node to the end of the route towards string and the beginning of teh route back string + routeString += "\(traceRoute?.node?.user?.longName ?? "unknown".localized) \((traceRoute?.node?.num ?? 0).toHex()) (\(destinationHop.snr != -32 ? String(destinationHop.snr) : "unknown ".localized)dB)" + var routeBackString = "\(traceRoute?.node?.user?.longName ?? "unknown".localized) \((traceRoute?.node?.num ?? 0).toHex()) --> " + traceRoute?.hopsBack = Int32(routingMessage.routeBack.count) + for (index, node) in routingMessage.routeBack.enumerated() { + var hopNode = getNodeInfo(id: Int64(node), context: context) + if hopNode == nil && hopNode?.num ?? 0 > 0 && node != 4294967295 { + hopNode = createNodeInfo(num: Int64(node), context: context) } - hopNodes.append(destinationHop) - /// Add the destination node to the end of the route towards string and the beginning of teh route back string - routeString += "\(traceRoute?.node?.user?.longName ?? "unknown".localized) \((traceRoute?.node?.num ?? 0).toHex()) (\(destinationHop.snr != -32 ? String(destinationHop.snr) : "unknown ".localized)dB)" - var routeBackString = "\(traceRoute?.node?.user?.longName ?? "unknown".localized) \((traceRoute?.node?.num ?? 0).toHex()) --> " - traceRoute?.hopsBack = Int32(routingMessage.routeBack.count) - for (index, node) in routingMessage.routeBack.enumerated() { - var hopNode = getNodeInfo(id: Int64(node), context: context) - if hopNode == nil && hopNode?.num ?? 0 > 0 && node != 4294967295 { - hopNode = createNodeInfo(num: Int64(node), context: context) - } - let traceRouteHop = TraceRouteHopEntity(context: context) - traceRouteHop.time = Date() - traceRouteHop.back = true - if routingMessage.snrBack.count >= index + 1 { - traceRouteHop.snr = Float(routingMessage.snrBack[index]) / 4 - } else { - // If no snr in route, set to unknown - traceRouteHop.snr = -32 - } - if let hn = hopNode, hn.hasPositions { - if let mostRecent = hn.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { - traceRouteHop.altitude = mostRecent.altitude - traceRouteHop.latitudeI = mostRecent.latitudeI - traceRouteHop.longitudeI = mostRecent.longitudeI - traceRoute?.hasPositions = true - } - } - traceRouteHop.num = hopNode?.num ?? 0 - if hopNode != nil { - if decodedInfo.packet.rxTime > 0 { - hopNode?.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime))) - } + let traceRouteHop = TraceRouteHopEntity(context: context) + traceRouteHop.time = Date() + traceRouteHop.back = true + if routingMessage.snrBack.count >= index + 1 { + traceRouteHop.snr = Float(routingMessage.snrBack[index]) / 4 + } else { + // If no snr in route, set to unknown + traceRouteHop.snr = -32 + } + if let hn = hopNode, hn.hasPositions { + if let mostRecent = hn.positions?.lastObject as? PositionEntity, mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! { + traceRouteHop.altitude = mostRecent.altitude + traceRouteHop.latitudeI = mostRecent.latitudeI + traceRouteHop.longitudeI = mostRecent.longitudeI + traceRoute?.hasPositions = true } - hopNodes.append(traceRouteHop) - routeBackString += "\(hopNode?.user?.longName ?? (node == 4294967295 ? "Repeater" : String(hopNode?.num.toHex() ?? "unknown".localized))) \(hopNode?.viaMqtt ?? false ? "MQTT" : "") (\(traceRouteHop.snr != -32 ? String(traceRouteHop.snr) : "unknown ".localized)dB) --> " } - // If nil, set to unknown, INT8_MIN (-128) then divide by 4 - let snrBackLast = Float(routingMessage.snrBack.last ?? -128) / 4 - routeBackString += "\(connectedNode.user?.longName ?? String(connectedNode.num.toHex())) (\(snrBackLast != -32 ? String(snrBackLast) : "unknown ".localized)dB)" - traceRoute?.routeText = routeString - traceRoute?.routeBackText = routeBackString - traceRoute?.hops = NSOrderedSet(array: hopNodes) - traceRoute?.time = Date() - do { - try context.save() - Logger.data.info("💾 Saved Trace Route") - } catch { - context.rollback() - let nsError = error as NSError - Logger.data.error("Error Updating Core Data TraceRouteHop: \(nsError, privacy: .public)") + traceRouteHop.num = hopNode?.num ?? 0 + if hopNode != nil { + if decodedInfo.packet.rxTime > 0 { + hopNode?.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(decodedInfo.packet.rxTime))) + } } - let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString) - MeshLogger.log("🪧 \(logString)") + hopNodes.append(traceRouteHop) + routeBackString += "\(hopNode?.user?.longName ?? (node == 4294967295 ? "Repeater" : String(hopNode?.num.toHex() ?? "unknown".localized))) \(hopNode?.viaMqtt ?? false ? "MQTT" : "") (\(traceRouteHop.snr != -32 ? String(traceRouteHop.snr) : "unknown ".localized)dB) --> " + } + // If nil, set to unknown, INT8_MIN (-128) then divide by 4 + let snrBackLast = Float(routingMessage.snrBack.last ?? -128) / 4 + routeBackString += "\(connectedNode.user?.longName ?? String(connectedNode.num.toHex())) (\(snrBackLast != -32 ? String(snrBackLast) : "unknown ".localized)dB)" + traceRoute?.routeText = routeString + traceRoute?.routeBackText = routeBackString + traceRoute?.hops = NSOrderedSet(array: hopNodes) + traceRoute?.time = Date() + do { + try context.save() + Logger.data.info("💾 Saved Trace Route") + } catch { + context.rollback() + let nsError = error as NSError + Logger.data.error("Error Updating Core Data TraceRouteHop: \(nsError, privacy: .public)") } + let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString) + MeshLogger.log("🪧 \(logString)") } case .neighborinfoApp: if let neighborInfo = try? NeighborInfo(serializedBytes: decodedInfo.packet.decoded.payload) { diff --git a/Meshtastic/Views/Nodes/TraceRouteLog.swift b/Meshtastic/Views/Nodes/TraceRouteLog.swift index e6e72841..33d4ffe4 100644 --- a/Meshtastic/Views/Nodes/TraceRouteLog.swift +++ b/Meshtastic/Views/Nodes/TraceRouteLog.swift @@ -54,7 +54,7 @@ struct TraceRouteLog: View { .font(.caption) } } icon: { - Image(systemName: route.response ? (route.hops?.count == 0 && route.response ? "person.line.dotted.person" : "point.3.connected.trianglepath.dotted") : "person.slash") + Image(systemName: route.response ? (route.hopsTowards == 0 && route.response ? "person.line.dotted.person" : "point.3.connected.trianglepath.dotted") : "person.slash") .symbolRenderingMode(.hierarchical) } .swipeActions { @@ -76,15 +76,7 @@ struct TraceRouteLog: View { Divider() ScrollView { if selectedRoute != nil { - if selectedRoute?.response ?? false && selectedRoute?.hopsTowards ?? 0 == 0 { - Label { - Text("Trace route received directly by \(selectedRoute?.node?.user?.longName ?? "unknown".localized) with a SNR of \(String(format: "%.2f", selectedRoute?.snr ?? 0.0)) dB") - } icon: { - Image(systemName: "signpost.right.and.left") - .symbolRenderingMode(.hierarchical) - } - .font(.title3) - } else if selectedRoute?.response ?? false && selectedRoute?.hopsTowards ?? 0 > 0 { + if selectedRoute?.response ?? false && selectedRoute?.hopsTowards ?? 0 >= 0 { Label { Text("Route: \(selectedRoute?.routeText ?? "unknown".localized)") } icon: {