Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add power metrics #1071

Merged
merged 7 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -22432,6 +22432,28 @@
}
}
},
"power.metrics.delete" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Delete all power metrics?"
}
}
}
},
"power.metrics.log" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Power Metrics Log"
}
}
}
},
"Powered" : {
"localizations" : {
"de" : {
Expand Down
12 changes: 11 additions & 1 deletion Meshtastic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */; };
6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */; };
6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; };
8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */; };
8D3F8A412D44C2A6009EAAA4 /* PowerMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */; };
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; };
B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; };
BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; };
Expand Down Expand Up @@ -290,6 +292,9 @@
6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticAppDelegate.swift; sourceTree = "<group>"; };
6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectionSensorLog.swift; sourceTree = "<group>"; };
6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = "<group>"; };
8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 49.xcdatamodel"; sourceTree = "<group>"; };
8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerMetrics.swift; sourceTree = "<group>"; };
8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerMetricsLog.swift; sourceTree = "<group>"; };
B399E8A32B6F486400E4488E /* RetryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryButton.swift; sourceTree = "<group>"; };
B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = "<group>"; };
BCB613802C67290800485544 /* SendWaypointIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendWaypointIntent.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -706,6 +711,7 @@
6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */,
DD15E4F42B8BFC8E00654F61 /* PaxCounterLog.swift */,
DDE5B4032B2279A700FCDD05 /* TraceRouteLog.swift */,
8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */,
);
path = Nodes;
sourceTree = "<group>";
Expand Down Expand Up @@ -1022,6 +1028,7 @@
DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */,
DD5E523D298F5A7D00D21B61 /* Weather */,
DD6F65712C6AB8EC0053C113 /* SecureInput.swift */,
8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1476,6 +1483,7 @@
DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */,
251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */,
D9C983A02B79D0E800BDBE6A /* AlertButton.swift in Sources */,
8D3F8A412D44C2A6009EAAA4 /* PowerMetricsLog.swift in Sources */,
DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */,
DD6F65762C6EA5490053C113 /* AckErrors.swift in Sources */,
DDDB445029F8AC9C00EE2349 /* UIImage.swift in Sources */,
Expand All @@ -1492,6 +1500,7 @@
DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */,
DD4975A52B147BA90026544E /* AmbientLightingConfig.swift in Sources */,
D93068D92B81509C0066FBC8 /* TapbackResponses.swift in Sources */,
8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */,
2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */,
DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */,
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */,
Expand Down Expand Up @@ -1966,6 +1975,7 @@
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */,
DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */,
DDDFE7402D0D4A070044463C /* MeshtasticDataModelV 47.xcdatamodel */,
DD0BE30C2CB785D8000BA445 /* MeshtasticDataModelV 46.xcdatamodel */,
Expand Down Expand Up @@ -2015,7 +2025,7 @@
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
);
currentVersion = DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */;
currentVersion = 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */;
name = Meshtastic.xcdatamodeld;
path = Meshtastic/Meshtastic.xcdatamodeld;
sourceTree = "<group>";
Expand Down
19 changes: 19 additions & 0 deletions Meshtastic/Export/WriteCsvFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
csvString += ", "
csvString += dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized
}
} else if metricsType == 2 {
// Create Power Metrics Header
csvString = "Channel 1 Voltage, Channel 1 Current, Channel 2 Voltage, Channel 2 Current, Channel 3 Voltage, Channel 3 Current, \("timestamp".localized)"
for dm in telemetry where dm.metricsType == 2 {
csvString += "\n"
csvString += String(dm.powerCh1Voltage)
csvString += ", "
csvString += String(dm.powerCh1Current)
csvString += ", "
csvString += String(dm.powerCh2Voltage)
csvString += ", "
csvString += String(dm.powerCh2Current)
csvString += ", "
csvString += String(dm.powerCh3Voltage)
csvString += ", "
csvString += String(dm.powerCh3Current)
csvString += ", "
csvString += dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized
}
}
return csvString
}
Expand Down
9 changes: 9 additions & 0 deletions Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ extension NodeInfoEntity {
return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 1")).lastObject as? TelemetryEntity
}

var latestPowerMetrics: TelemetryEntity? {
return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 2")).lastObject as? TelemetryEntity
}

var hasPositions: Bool {
return self.positions?.count ?? 0 > 0
}
Expand All @@ -39,6 +43,11 @@ extension NodeInfoEntity {
return user?.sensorMessageList.count ?? 0 > 0
}

var hasPowerMetrics: Bool {
let powerMetrics = telemetries?.filter { ($0 as AnyObject).metricsType == 2 }
return powerMetrics?.count ?? 0 > 0
}

var hasTraceRoutes: Bool {
let routes = traceRoutes?.filter { ($0 as AnyObject).response }
return routes?.count ?? 0 > 0
Expand Down
39 changes: 36 additions & 3 deletions Meshtastic/Helpers/MeshPackets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,8 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
let logString = String.localizedStringWithFormat("mesh.log.telemetry.received %@".localized, String(packet.from))
MeshLogger.log("📈 \(logString)")

if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) {

if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) && telemetryMessage.variant != Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) {
/// Other unhandled telemetry packets
return
}
Expand Down Expand Up @@ -736,6 +737,38 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
telemetry.numTotalNodes = Int32(truncatingIfNeeded: telemetryMessage.localStats.numTotalNodes)
telemetry.metricsType = 4
Logger.statistics.info("📈 [Mesh Statistics] Channel Utilization: \(telemetryMessage.localStats.channelUtilization, privacy: .public) Airtime: \(telemetryMessage.localStats.airUtilTx, privacy: .public) Packets Sent: \(telemetryMessage.localStats.numPacketsTx, privacy: .public) Packets Received: \(telemetryMessage.localStats.numPacketsRx, privacy: .public) Bad Packets Received: \(telemetryMessage.localStats.numPacketsRxBad, privacy: .public) Nodes Online: \(telemetryMessage.localStats.numOnlineNodes, privacy: .public) of \(telemetryMessage.localStats.numTotalNodes, privacy: .public) nodes for Node: \(packet.from.toHex(), privacy: .public)")
} else if telemetryMessage.variant == Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) {
Logger.data.info("📈 [Power Metrics] Received for Node: \(packet.from.toHex(), privacy: .public)")

if telemetryMessage.powerMetrics.hasCh1Voltage {
telemetry.powerCh1Voltage = telemetryMessage.powerMetrics.ch1Voltage
telemetry.metricsType = 2
}

if telemetryMessage.powerMetrics.hasCh1Current {
telemetry.powerCh1Current = telemetryMessage.powerMetrics.ch1Current
telemetry.metricsType = 2
}

if telemetryMessage.powerMetrics.hasCh2Voltage {
telemetry.powerCh2Voltage = telemetryMessage.powerMetrics.ch2Voltage
telemetry.metricsType = 2
}

if telemetryMessage.powerMetrics.hasCh1Current {
telemetry.powerCh2Current = telemetryMessage.powerMetrics.ch2Current
telemetry.metricsType = 2
}

if telemetryMessage.powerMetrics.hasCh3Voltage {
telemetry.powerCh3Voltage = telemetryMessage.powerMetrics.ch3Voltage
telemetry.metricsType = 2
}

if telemetryMessage.powerMetrics.hasCh3Current {
telemetry.powerCh3Current = telemetryMessage.powerMetrics.ch3Current
telemetry.metricsType = 2
}
}
telemetry.snr = packet.rxSnr
telemetry.rssi = packet.rxRssi
Expand Down Expand Up @@ -987,8 +1020,8 @@ func textMessageAppPacket(
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)",
messageId: newMessage.messageId,
channel: newMessage.channel,
userNum: Int64(newMessage.fromUser?.userId ?? "0")
)
userNum: Int64(newMessage.fromUser?.userId ?? "0"),
critical: critical)
]
manager.schedule()
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
Expand Down
2 changes: 1 addition & 1 deletion Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>MeshtasticDataModelV 48.xcdatamodel</string>
<string>MeshtasticDataModelV 49.xcdatamodel</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24C101" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22758" systemVersion="23F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
Expand Down
Loading
Loading