mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Trace route context menu item
Lazy grid for all 3 node logs (position, device metrics, environment metrics)
This commit is contained in:
parent
ebe77459ee
commit
31005c1a4f
5 changed files with 97 additions and 30 deletions
|
|
@ -313,6 +313,35 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
|
|||
connectedPeripheral!.peripheral.readValue(for: FROMRADIO_characteristic)
|
||||
}
|
||||
|
||||
func sendTraceRouteRequest(destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var success = false
|
||||
let fromNodeNum = connectedPeripheral.num
|
||||
|
||||
let routePacket = RouteDiscovery()
|
||||
|
||||
var meshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(destNum)
|
||||
meshPacket.from = UInt32(fromNodeNum)//0 // Send 0 as from from phone to device to avoid warning about client trying to set node num
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! routePacket.serializedData()
|
||||
dataMessage.portnum = PortNum.tracerouteApp
|
||||
dataMessage.wantResponse = wantResponse
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
success = true
|
||||
MeshLogger.log("🪧 Sent a Trace Route Packet to node: \(destNum).")
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
func sendWantConfig() {
|
||||
guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return }
|
||||
|
||||
|
|
@ -504,7 +533,19 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
|
|||
case .audioApp:
|
||||
MeshLogger.log("ℹ️ MESH PACKET received for Audio App UNHANDLED \(try! decodedInfo.packet.jsonString())")
|
||||
case .tracerouteApp:
|
||||
MeshLogger.log("ℹ️ MESH PACKET received for Trace Route App UNHANDLED \(try! decodedInfo.packet.jsonString())")
|
||||
if let routingMessage = try? RouteDiscovery(serializedData: decodedInfo.packet.decoded.payload) {
|
||||
|
||||
if routingMessage.route.count == 0 {
|
||||
MeshLogger.log("🪧 Trace Route request sent to \(decodedInfo.packet.from) was recieived directly.")
|
||||
} else {
|
||||
|
||||
var routeString = "🪧 Trace Route request returned: \(decodedInfo.packet.to) --> "
|
||||
for node in routingMessage.route {
|
||||
routeString += "\(node) --> "
|
||||
}
|
||||
routeString += "\(decodedInfo.packet.from)"
|
||||
}
|
||||
}
|
||||
case .UNRECOGNIZED(_):
|
||||
MeshLogger.log("ℹ️ MESH PACKET received for Other App UNHANDLED \(try! decodedInfo.packet.jsonString())")
|
||||
case .max:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ struct Contacts: View {
|
|||
@State private var selection: UserEntity? = nil // Nothing selected by default.
|
||||
@State private var isPresentingDeleteChannelMessagesConfirm: Bool = false
|
||||
@State private var isPresentingDeleteUserMessagesConfirm: Bool = false
|
||||
@State private var isPresentingTraceRouteSentAlert = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -194,6 +195,14 @@ struct Contacts: View {
|
|||
} label: {
|
||||
Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash")
|
||||
}
|
||||
Button {
|
||||
let success = bleManager.sendTraceRouteRequest(destNum: user.num, wantResponse: true)
|
||||
if success {
|
||||
isPresentingTraceRouteSentAlert = true
|
||||
}
|
||||
} label: {
|
||||
Label("Trace Route", systemImage: "signpost.right.and.left")
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
Button(role: .destructive) {
|
||||
isPresentingDeleteUserMessagesConfirm = true
|
||||
|
|
@ -202,12 +211,21 @@ struct Contacts: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.alert(
|
||||
"Trace Route Sent",
|
||||
isPresented: $isPresentingTraceRouteSentAlert
|
||||
)
|
||||
{
|
||||
Button("OK", role: .cancel) { }
|
||||
}
|
||||
message: {
|
||||
Text("This could take a while, response will appear in the mesh log.")
|
||||
}
|
||||
.confirmationDialog(
|
||||
"This conversation will be deleted.",
|
||||
isPresented: $isPresentingDeleteUserMessagesConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
|
||||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: user, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
|
|
|
|||
|
|
@ -75,43 +75,50 @@ struct DeviceMetricsLog: View {
|
|||
|
||||
} else {
|
||||
ScrollView {
|
||||
Grid(alignment: .topLeading, horizontalSpacing: 2) {
|
||||
|
||||
let columns = [
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(.fixed(120))
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1) {
|
||||
GridRow {
|
||||
Text("Batt")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("Voltage")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("ChUtil")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("AirTm")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("Timestamp")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
Divider()
|
||||
ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (dm: TelemetryEntity) in
|
||||
if dm.metricsType == 0 {
|
||||
GridRow {
|
||||
if dm.batteryLevel == 0 {
|
||||
Text("USB")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
} else {
|
||||
Text("\(String(dm.batteryLevel))%")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
}
|
||||
Text(String(dm.voltage))
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", dm.channelUtilization))%")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", dm.airUtilTx))%")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
Text(dm.time?.formattedDate(format: "MM/dd/yy hh:mm") ?? "Unknown time")
|
||||
.font(.callout)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,8 +63,14 @@ struct EnvironmentMetricsLog: View {
|
|||
}
|
||||
} else {
|
||||
ScrollView {
|
||||
|
||||
Grid(alignment: .topLeading, horizontalSpacing: 2) {
|
||||
let columns = [
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(.fixed(115))
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1) {
|
||||
|
||||
GridRow {
|
||||
|
||||
|
|
@ -80,17 +86,10 @@ struct EnvironmentMetricsLog: View {
|
|||
Text("Gas")
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("DC")
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("Volt")
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
Text("Timestamp")
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
Divider()
|
||||
ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (em: TelemetryEntity) in
|
||||
|
||||
if em.metricsType == 1 {
|
||||
|
|
@ -105,10 +104,6 @@ struct EnvironmentMetricsLog: View {
|
|||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", em.gasResistance))")
|
||||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", em.current))")
|
||||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", em.voltage))")
|
||||
.font(.caption)
|
||||
Text(em.time?.formattedDate(format: "MM/dd/yy hh:mm") ?? "Unknown time")
|
||||
.font(.caption)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,14 @@ struct PositionLog: View {
|
|||
|
||||
ScrollView {
|
||||
// Use a grid on iOS as a table only shows a single column
|
||||
Grid(alignment: .topLeading, horizontalSpacing: 2) {
|
||||
let columns = [
|
||||
GridItem(.fixed(95)),
|
||||
GridItem(.fixed(95)),
|
||||
GridItem(),
|
||||
GridItem(),
|
||||
GridItem(.fixed(115))
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1) {
|
||||
|
||||
GridRow {
|
||||
|
||||
|
|
@ -78,7 +85,6 @@ struct PositionLog: View {
|
|||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
Divider()
|
||||
ForEach(node.positions!.reversed() as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
GridRow {
|
||||
Text(String(format: "%.6f", mappin.latitude ?? 0))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue