mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #749 from meshtastic/log-record-characteristic
New BLE LogRecord characteristic
This commit is contained in:
commit
1d1b886dc7
3 changed files with 117 additions and 63 deletions
|
|
@ -15,6 +15,7 @@
|
|||
25AECD4F2C2F723200862C8E /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 25AECD4E2C2F723200862C8E /* Localizable.xcstrings */; };
|
||||
25F26B1E2C2F610D00C9CD9D /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD5BB0C2C285F00007E03CA /* Logger.swift */; };
|
||||
25F26B1F2C2F611300C9CD9D /* AppData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD5BB152C28B1E4007E03CA /* AppData.swift */; };
|
||||
6D825E622C34786C008DBEE4 /* CommonRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D825E612C34786C008DBEE4 /* CommonRegex.swift */; };
|
||||
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 */; };
|
||||
|
|
@ -222,6 +223,7 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
25AECD4E2C2F723200862C8E /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
6D825E612C34786C008DBEE4 /* CommonRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonRegex.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
|
|
@ -832,6 +834,7 @@
|
|||
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */,
|
||||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */,
|
||||
DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */,
|
||||
6D825E612C34786C008DBEE4 /* CommonRegex.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1099,6 +1102,7 @@
|
|||
DD798B072915928D005217CD /* ChannelMessageList.swift in Sources */,
|
||||
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */,
|
||||
DD77093D2AA1AFA3007A8BF0 /* ChannelTips.swift in Sources */,
|
||||
6D825E622C34786C008DBEE4 /* CommonRegex.swift in Sources */,
|
||||
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */,
|
||||
DDDB444C29F8AAA600EE2349 /* Color.swift in Sources */,
|
||||
DDB8F4122A9EE5DD00230ECE /* UserList.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import MapKit
|
|||
import MeshtasticProtobufs
|
||||
import CocoaMQTT
|
||||
import OSLog
|
||||
import RegexBuilder
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Meshtastic BLE Device Manager
|
||||
|
|
@ -43,13 +42,15 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
var TORADIO_characteristic: CBCharacteristic!
|
||||
var FROMRADIO_characteristic: CBCharacteristic!
|
||||
var FROMNUM_characteristic: CBCharacteristic!
|
||||
var LEGACY_LOGRADIO_characteristic: CBCharacteristic!
|
||||
var LOGRADIO_characteristic: CBCharacteristic!
|
||||
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
|
||||
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
|
||||
let FROMRADIO_UUID = CBUUID(string: "0x2C55E69E-4993-11ED-B878-0242AC120002")
|
||||
let EOL_FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
|
||||
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
|
||||
let LOGRADIO_UUID = CBUUID(string: "0x6C6FD238-78FA-436B-AACF-15C5BE1EF2E2")
|
||||
let LEGACY_LOGRADIO_UUID = CBUUID(string: "0x6C6FD238-78FA-436B-AACF-15C5BE1EF2E2")
|
||||
let LOGRADIO_UUID = CBUUID(string: "0x5a3d6e49-06e6-4423-9944-e9de8cdf9547")
|
||||
|
||||
// MARK: init BLEManager
|
||||
override init() {
|
||||
|
|
@ -281,7 +282,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
guard let services = peripheral.services else { return }
|
||||
for service in services where service.uuid == meshtasticServiceCBUUID {
|
||||
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID, LOGRADIO_UUID], for: service)
|
||||
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID, LEGACY_LOGRADIO_UUID, LOGRADIO_UUID], for: service)
|
||||
Logger.services.info("✅ [BLE] Service for Meshtastic discovered by \(peripheral.name ?? "Unknown", privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
|
@ -315,6 +316,11 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
FROMNUM_characteristic = characteristic
|
||||
peripheral.setNotifyValue(true, for: characteristic)
|
||||
|
||||
case LEGACY_LOGRADIO_UUID:
|
||||
Logger.services.info("✅ [BLE] did discover legacy LOGRADIO (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown", privacy: .public)")
|
||||
LEGACY_LOGRADIO_characteristic = characteristic
|
||||
peripheral.setNotifyValue(true, for: characteristic)
|
||||
|
||||
case LOGRADIO_UUID:
|
||||
Logger.services.info("✅ [BLE] did discover LOGRADIO (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown", privacy: .public)")
|
||||
LOGRADIO_characteristic = characteristic
|
||||
|
|
@ -506,7 +512,54 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Data Read / Update Characteristic Event
|
||||
fileprivate func handleRadioLog(radioLog: String) {
|
||||
var log = radioLog
|
||||
/// Debug Log Level
|
||||
if log.starts(with: "DEBUG |") {
|
||||
do {
|
||||
let logString = log
|
||||
if let coordsMatch = try CommonRegex.COORDS_REGEX.firstMatch(in: logString) {
|
||||
log = "\(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces))"
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🕵🏻♂️ \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🕵🏻♂️ \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} else if log.starts(with: "INFO |") {
|
||||
do {
|
||||
let logString = log
|
||||
if let coordsMatch = try CommonRegex.COORDS_REGEX.firstMatch(in: logString) {
|
||||
log = "\(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces))"
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("📢 \(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("📢 \(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} else if log.starts(with: "WARN |") {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.warning("⚠️ \(log.replacingOccurrences(of: "WARN |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else if log.starts(with: "ERROR |") {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.error("💥 \(log.replacingOccurrences(of: "ERROR |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else if log.starts(with: "CRIT |") {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.critical("🧨 \(log.replacingOccurrences(of: "CRIT |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("📟 \(log, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
||||
|
||||
if let error {
|
||||
|
|
@ -529,67 +582,35 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if characteristic.value == nil || characteristic.value!.isEmpty {
|
||||
return
|
||||
}
|
||||
let coordsSearch = Regex {
|
||||
Capture {
|
||||
Regex {
|
||||
"lat="
|
||||
OneOrMore(.digit)
|
||||
}
|
||||
}
|
||||
Capture {" "}
|
||||
Capture {
|
||||
Regex {
|
||||
"long="
|
||||
OneOrMore(.digit)
|
||||
}
|
||||
do {
|
||||
let logRecord = try LogRecord(serializedData: characteristic.value!)
|
||||
var message = logRecord.source.isEmpty ? logRecord.message : "[\(logRecord.source)] \(logRecord.message)"
|
||||
switch logRecord.level {
|
||||
case .debug:
|
||||
message = "DEBUG | \(message)"
|
||||
case .info:
|
||||
message = "INFO | \(message)"
|
||||
case .warning:
|
||||
message = "WARN | \(message)"
|
||||
case .error:
|
||||
message = "ERROR | \(message)"
|
||||
case .critical:
|
||||
message = "CRIT | \(message)"
|
||||
default:
|
||||
message = "DEBUG | \(message)"
|
||||
}
|
||||
handleRadioLog(radioLog: message)
|
||||
}
|
||||
.anchorsMatchLineEndings()
|
||||
if var log = String(data: characteristic.value!, encoding: .utf8) {
|
||||
/// Debug Log Level
|
||||
if log.starts(with: "DEBUG |") {
|
||||
do {
|
||||
let logString = log
|
||||
if let coordsMatch = try coordsSearch.firstMatch(in: logString) {
|
||||
log = "\(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces))"
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🕵🏻♂️ \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🕵🏻♂️ \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} else if log.starts(with: "INFO |") {
|
||||
do {
|
||||
let logString = log
|
||||
if let coordsMatch = try coordsSearch.firstMatch(in: logString) {
|
||||
log = "\(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces))"
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("📢 \(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("📢 \(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} else if log.starts(with: "WARN |") {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.warning("⚠️ \(log.replacingOccurrences(of: "WARN |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else if log.starts(with: "ERROR |") {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.error("💥 \(log.replacingOccurrences(of: "ERROR |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else if log.starts(with: "CRIT |") {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.critical("🧨 \(log.replacingOccurrences(of: "CRIT |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("📟 \(log, privacy: .public)")
|
||||
}
|
||||
catch {
|
||||
// Ignore fail to parse as LogRecord
|
||||
}
|
||||
|
||||
case LEGACY_LOGRADIO_UUID:
|
||||
if characteristic.value == nil || characteristic.value!.isEmpty {
|
||||
return
|
||||
}
|
||||
if let log = String(data: characteristic.value!, encoding: .utf8) {
|
||||
handleRadioLog(radioLog: log)
|
||||
}
|
||||
|
||||
case FROMRADIO_UUID:
|
||||
|
|
|
|||
29
Meshtastic/Helpers/CommonRegex.swift
Normal file
29
Meshtastic/Helpers/CommonRegex.swift
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// CommonRegex.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Ben Meadors on 7/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RegexBuilder
|
||||
|
||||
class CommonRegex
|
||||
{
|
||||
static let COORDS_REGEX = Regex {
|
||||
Capture {
|
||||
Regex {
|
||||
"lat="
|
||||
OneOrMore(.digit)
|
||||
}
|
||||
}
|
||||
Capture {" "}
|
||||
Capture {
|
||||
Regex {
|
||||
"long="
|
||||
OneOrMore(.digit)
|
||||
}
|
||||
}
|
||||
}
|
||||
.anchorsMatchLineEndings()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue