diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 66cb348e..0749e8d6 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -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 = ""; }; + 6D825E612C34786C008DBEE4 /* CommonRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonRegex.swift; sourceTree = ""; }; 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticAppDelegate.swift; sourceTree = ""; }; 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectionSensorLog.swift; sourceTree = ""; }; 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = ""; }; @@ -832,6 +834,7 @@ DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */, DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */, DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */, + 6D825E612C34786C008DBEE4 /* CommonRegex.swift */, ); path = Helpers; sourceTree = ""; @@ -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 */, diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 95e7d473..536d7d34 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -6,7 +6,6 @@ import MapKit import MeshtasticProtobufs import CocoaMQTT import OSLog -import RegexBuilder // --------------------------------------------------------------------------------------- // Meshtastic BLE Device Manager @@ -53,7 +52,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate 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() { self.lastConnectionError = "" @@ -318,7 +316,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate FROMNUM_characteristic = characteristic peripheral.setNotifyValue(true, for: characteristic) - case LEGACY_LOGRADIO_LOGRADIO_UUID: + 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) @@ -514,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 { @@ -537,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: diff --git a/Meshtastic/Helpers/CommonRegex.swift b/Meshtastic/Helpers/CommonRegex.swift new file mode 100644 index 00000000..683f8fd7 --- /dev/null +++ b/Meshtastic/Helpers/CommonRegex.swift @@ -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() +}