mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Add Telemetry log
This commit is contained in:
parent
ddfcea4ace
commit
3b90bb7ecc
7 changed files with 219 additions and 27 deletions
|
|
@ -32,6 +32,7 @@
|
|||
DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C158B2824A91E0032668E /* module_config.pb.swift */; };
|
||||
DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C158D2824AA7E0032668E /* config.pb.swift */; };
|
||||
DD4DED9027AD2975004BA27E /* cannedmessages.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */; };
|
||||
DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4F23CC28779A3C001D37CB /* TelemetryLog.swift */; };
|
||||
DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; };
|
||||
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; };
|
||||
DD539502276DAA6A00AD86B1 /* MapLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD539501276DAA6A00AD86B1 /* MapLocation.swift */; };
|
||||
|
|
@ -121,6 +122,7 @@
|
|||
DD4C158B2824A91E0032668E /* module_config.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = module_config.pb.swift; sourceTree = "<group>"; };
|
||||
DD4C158D2824AA7E0032668E /* config.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = config.pb.swift; sourceTree = "<group>"; };
|
||||
DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = cannedmessages.pb.swift; sourceTree = "<group>"; };
|
||||
DD4F23CC28779A3C001D37CB /* TelemetryLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryLog.swift; sourceTree = "<group>"; };
|
||||
DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DD539501276DAA6A00AD86B1 /* MapLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLocation.swift; sourceTree = "<group>"; };
|
||||
DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
|
|
@ -228,6 +230,7 @@
|
|||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */,
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */,
|
||||
DD73FD1028750779000852D6 /* LocationHistory.swift */,
|
||||
DD4F23CC28779A3C001D37CB /* TelemetryLog.swift */,
|
||||
);
|
||||
path = Nodes;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -620,6 +623,7 @@
|
|||
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */,
|
||||
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */,
|
||||
DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */,
|
||||
DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */,
|
||||
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */,
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
DDC4D568275499A500A4208E /* Persistence.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -1259,12 +1259,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
}
|
||||
|
||||
public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
|
||||
public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, messages: String, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
|
||||
|
||||
var newMessageId: Int64 = 0
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setModuleConfig.cannedMessage = config
|
||||
//adminPacket.setCannedMessageModulePart1 = messages
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(toUser.num)
|
||||
|
|
@ -1316,6 +1317,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return newMessageId
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func saveExternalNotificationModuleConfig(config: ModuleConfig.ExternalNotificationConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
|
||||
|
||||
var newMessageId: Int64 = 0
|
||||
|
|
|
|||
|
|
@ -1271,7 +1271,7 @@ func telemetryPacket(packet: MeshPacket, meshLogging: Bool, context: NSManagedOb
|
|||
|
||||
if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) {
|
||||
|
||||
let telemetry = TelemetryEntity(context: context)
|
||||
let telemetry = TelemetryEntity(context: context)
|
||||
|
||||
let fetchNodeTelemetryRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
|
@ -1282,24 +1282,32 @@ func telemetryPacket(packet: MeshPacket, meshLogging: Bool, context: NSManagedOb
|
|||
|
||||
if fetchedNode.count == 1 {
|
||||
|
||||
// Device Metrics
|
||||
telemetry.airUtilTx = telemetryMessage.deviceMetrics.airUtilTx
|
||||
telemetry.channelUtilization = telemetryMessage.deviceMetrics.channelUtilization
|
||||
telemetry.batteryLevel = Int32(telemetryMessage.deviceMetrics.batteryLevel)
|
||||
telemetry.voltage = telemetryMessage.deviceMetrics.voltage
|
||||
if telemetryMessage.variant == Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) {
|
||||
|
||||
// Device Metrics
|
||||
telemetry.airUtilTx = telemetryMessage.deviceMetrics.airUtilTx
|
||||
telemetry.channelUtilization = telemetryMessage.deviceMetrics.channelUtilization
|
||||
telemetry.batteryLevel = Int32(telemetryMessage.deviceMetrics.batteryLevel)
|
||||
telemetry.voltage = telemetryMessage.deviceMetrics.voltage
|
||||
telemetry.metricsType = 0
|
||||
|
||||
} else if telemetryMessage.variant == Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) {
|
||||
|
||||
// Environment Metrics
|
||||
telemetry.barometricPressure = telemetryMessage.environmentMetrics.barometricPressure
|
||||
telemetry.current = telemetryMessage.environmentMetrics.current
|
||||
telemetry.gasResistance = telemetryMessage.environmentMetrics.gasResistance
|
||||
telemetry.relativeHumidity = telemetryMessage.environmentMetrics.relativeHumidity
|
||||
telemetry.temperature = telemetryMessage.environmentMetrics.temperature
|
||||
// Environment Metrics
|
||||
telemetry.barometricPressure = telemetryMessage.environmentMetrics.barometricPressure
|
||||
telemetry.current = telemetryMessage.environmentMetrics.current
|
||||
telemetry.gasResistance = telemetryMessage.environmentMetrics.gasResistance
|
||||
telemetry.relativeHumidity = telemetryMessage.environmentMetrics.relativeHumidity
|
||||
telemetry.temperature = telemetryMessage.environmentMetrics.temperature
|
||||
telemetry.metricsType = 1
|
||||
|
||||
}
|
||||
telemetry.time = Date(timeIntervalSince1970: TimeInterval(Int64(telemetryMessage.time)))
|
||||
let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutableTelemetries.add(telemetry)
|
||||
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(telemetryMessage.time)))
|
||||
fetchedNode[0].lastHeard = telemetry.time
|
||||
fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet
|
||||
fetchedNode[0].objectWillChange.send()
|
||||
}
|
||||
|
||||
try context.save()
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@
|
|||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
|
|
@ -204,7 +205,7 @@
|
|||
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
|
||||
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
|
||||
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="179"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="194"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="215"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -69,6 +69,21 @@ struct NodeDetail: View {
|
|||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
Image(node.user?.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(10)
|
||||
.frame(width: bounds.size.width, height: bounds.size.height / 2)
|
||||
}
|
||||
|
||||
List {
|
||||
|
||||
if (node.positions?.count ?? 0) > 0 {
|
||||
|
||||
NavigationLink {
|
||||
LocationHistory(node: node)
|
||||
} label: {
|
||||
|
|
@ -80,20 +95,30 @@ struct NodeDetail: View {
|
|||
Text("Position History \(node.positions?.count ?? 0) Points")
|
||||
.font(.title2)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
} else {
|
||||
|
||||
Image(node.user?.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(10)
|
||||
.frame(width: bounds.size.width, height: bounds.size.height / 2)
|
||||
NavigationLink {
|
||||
TelemetryLog(node: node)
|
||||
} label: {
|
||||
|
||||
Image(systemName: "chart.xyaxis.line")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
Text("Telemetry Log \(node.telemetries?.count ?? 0) Readings")
|
||||
.font(.title2)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
}
|
||||
|
||||
|
||||
.frame(height: 160)
|
||||
|
||||
|
||||
ScrollView {
|
||||
|
||||
|
||||
|
||||
|
||||
if self.bleManager.connectedPeripheral != nil && self.bleManager.connectedPeripheral.num == node.num && self.bleManager.connectedPeripheral.num == node.num {
|
||||
HStack {
|
||||
|
||||
|
|
|
|||
134
Meshtastic/Views/Nodes/TelemetryLog.swift
Normal file
134
Meshtastic/Views/Nodes/TelemetryLog.swift
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// TelemetryLog.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 7/7/22.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct TelemetryLog: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
List {
|
||||
|
||||
|
||||
ForEach(node.telemetries!.array as! [TelemetryEntity], id: \.self) { (tel: TelemetryEntity) in
|
||||
|
||||
VStack {
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
if tel.metricsType == 0 {
|
||||
|
||||
HStack {
|
||||
|
||||
Text("Device Metrics")
|
||||
.font(.title3)
|
||||
BatteryIcon(batteryLevel: tel.batteryLevel, font: .callout, color: .accentColor)
|
||||
|
||||
if tel.batteryLevel == 0 {
|
||||
|
||||
Text("Plugged In")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
} else {
|
||||
|
||||
Text("Battery Level: \(String(tel.batteryLevel))%")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
if tel.batteryLevel > 0 {
|
||||
|
||||
Image(systemName: "bolt")
|
||||
.font(.callout)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Voltage: \(String(tel.voltage))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
Text("Channel Utilization: \(String(format: "%.2f", tel.channelUtilization))%")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
|
||||
Text("Airtime Utilization: \(String(format: "%.2f", tel.airUtilTx))%")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.callout)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
DateTimeText(dateTime: tel.time)
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
HStack {
|
||||
|
||||
// Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline)
|
||||
// Text("Lat/Long:").font(.caption)
|
||||
// Text("\(String(tel.batteryLevel ?? 0))")
|
||||
// .foregroundColor(.gray)
|
||||
// .font(.callout)
|
||||
|
||||
Image(systemName: "arrow.up.arrow.down.circle")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Alt:")
|
||||
.font(.caption)
|
||||
|
||||
Text("\(String(tel.voltage))m")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.font(.caption)
|
||||
DateTimeText(dateTime: tel.time)
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Telemetry Log \(node.telemetries?.count ?? 0) Readings")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -127,6 +127,8 @@ struct CannedMessagesConfig: View {
|
|||
/// Generate input event on Press of this kind.
|
||||
@State var inputbrokerEventPress = 0
|
||||
|
||||
@State var messagesPart1 = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
|
@ -157,6 +159,10 @@ struct CannedMessagesConfig: View {
|
|||
.padding(.bottom, 10)
|
||||
|
||||
}
|
||||
Section(header: Text("Messages")) {
|
||||
|
||||
TextEditor(text: $messagesPart1)
|
||||
}
|
||||
Section(header: Text("Control Type")) {
|
||||
|
||||
|
||||
|
|
@ -310,7 +316,7 @@ struct CannedMessagesConfig: View {
|
|||
cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue()
|
||||
cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue()
|
||||
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, messages: "Where are you garth?, Hello",fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -363,12 +369,23 @@ struct CannedMessagesConfig: View {
|
|||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if newEnabled != node!.cannedMessageConfig!.enabled { hasChanges = true }
|
||||
if node!.cannedMessageConfig != nil {
|
||||
|
||||
if newEnabled != node!.cannedMessageConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: sendBell) { newBell in
|
||||
|
||||
if newBell != node!.cannedMessageConfig!.sendBell { hasChanges = true }
|
||||
}
|
||||
.onChange(of: rotary1Enabled) { newRot1 in
|
||||
|
||||
if newRot1 != node!.cannedMessageConfig!.rotary1Enabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: updown1Enabled) { newUpDown in
|
||||
|
||||
if newUpDown != node!.cannedMessageConfig!.updown1Enabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerPinA) { newPinA in
|
||||
|
||||
if newPinA != node!.cannedMessageConfig!.inputbrokerPinA { hasChanges = true }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue