Skip to content

Commit

Permalink
Update direct traceroute handling to support two-way traceroutes
Browse files Browse the repository at this point in the history
  • Loading branch information
bjpetit committed Oct 26, 2024
1 parent 9b34840 commit 38d0e5a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 135 deletions.
12 changes: 1 addition & 11 deletions Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -14117,7 +14117,7 @@
}
},
"mesh.log.traceroute.received.direct %@" : {
"extractionState" : "migrated",
"extractionState" : "manual",
"localizations" : {
"de" : {
"stringUnit" : {
Expand Down Expand Up @@ -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" : {

Expand Down
218 changes: 104 additions & 114 deletions Meshtastic/Helpers/BLEManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 2 additions & 10 deletions Meshtastic/Views/Nodes/TraceRouteLog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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: {
Expand Down

0 comments on commit 38d0e5a

Please sign in to comment.