Add Telemetry log

This commit is contained in:
Garth Vander Houwen 2022-07-08 06:31:47 -07:00
parent ddfcea4ace
commit 3b90bb7ecc
7 changed files with 219 additions and 27 deletions

View file

@ -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 */,

View file

@ -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

View file

@ -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()

View file

@ -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>

View file

@ -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 {

View 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
}
}
}

View file

@ -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 }