Skip to content

Commit

Permalink
Changed decodes to protocol format with structs
Browse files Browse the repository at this point in the history
general refactoring
  • Loading branch information
kkonteh97 committed Jan 12, 2025
1 parent 6c028c1 commit 262909d
Show file tree
Hide file tree
Showing 21 changed files with 4,044 additions and 629 deletions.
8 changes: 8 additions & 0 deletions Sources/File.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// File.swift
// SwiftOBD2
//
// Created by kemo konteh on 12/12/24.
//

import Foundation
32 changes: 16 additions & 16 deletions Sources/SwiftOBD2/Communication/bleManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ class BLEManager: NSObject, CommProtocol {
private let peripheralSubject = PassthroughSubject<CBPeripheral, Never>()

var peripheralPublisher: AnyPublisher<CBPeripheral, Never> {
return peripheralSubject.eraseToAnyPublisher()
peripheralSubject.eraseToAnyPublisher()
}

static let services = [
CBUUID(string: "FFE0"),
CBUUID(string: "FFF0"),
CBUUID(string: "18F0"), //e.g. VGate iCar Pro
CBUUID(string: "18F0"), // e.g. VGate iCar Pro
]

let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.example.app", category: "BLEManager")
Expand Down Expand Up @@ -125,15 +125,15 @@ class BLEManager: NSObject, CommProtocol {

@Published var foundPeripherals: [CBPeripheral] = []

func appendFoundPeripheral(peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) {
func appendFoundPeripheral(peripheral: CBPeripheral, advertisementData _: [String: Any], rssi: NSNumber) {
if rssi.intValue >= 0 { return }
if let index = foundPeripherals.firstIndex(where: { $0.identifier.uuidString == peripheral.identifier.uuidString }) {
foundPeripherals[index] = peripheral
} else {
peripheralSubject.send(peripheral)
foundPeripherals.append(peripheral)
}
}
}

func connect(to peripheral: CBPeripheral) {
logger.info("Connecting to: \(peripheral.name ?? "")")
Expand All @@ -154,7 +154,7 @@ class BLEManager: NSObject, CommProtocol {

func scanForPeripheralAsync(_ timeout: TimeInterval) async throws -> CBPeripheral? {
// returns a single peripheral with the specified services
return try await Timeout(seconds: timeout) {
try await Timeout(seconds: timeout) {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<CBPeripheral, Error>) in
self.foundPeripheralCompletion = { peripheral, error in
if let peripheral = peripheral {
Expand Down Expand Up @@ -213,7 +213,7 @@ class BLEManager: NSObject, CommProtocol {
}
}

if connectionCompletion != nil && ecuWriteCharacteristic != nil && ecuReadCharacteristic != nil {
if connectionCompletion != nil, ecuWriteCharacteristic != nil, ecuReadCharacteristic != nil {
connectionCompletion?(peripheral, nil)
}
}
Expand All @@ -229,12 +229,12 @@ class BLEManager: NSObject, CommProtocol {
}

switch characteristic {
case ecuReadCharacteristic:
processReceivedData(characteristicValue, completion: sendMessageCompletion)
default:
if let responseString = String(data: characteristicValue, encoding: .utf8) {
logger.info("Unknown characteristic: \(characteristic)\nResponse: \(responseString)")
}
case ecuReadCharacteristic:
processReceivedData(characteristicValue, completion: sendMessageCompletion)
default:
if let responseString = String(data: characteristicValue, encoding: .utf8) {
logger.info("Unknown characteristic: \(characteristic)\nResponse: \(responseString)")
}
}
}

Expand Down Expand Up @@ -262,7 +262,7 @@ class BLEManager: NSObject, CommProtocol {

// MARK: - Async Methods

func connectAsync(timeout: TimeInterval, peripheral: CBPeripheral? = nil) async throws {
func connectAsync(timeout: TimeInterval, peripheral _: CBPeripheral? = nil) async throws {
if connectionState != .disconnected {
return
}
Expand All @@ -282,7 +282,7 @@ class BLEManager: NSObject, CommProtocol {
}
connect(to: peripheral)
}
self.connectionCompletion = nil
connectionCompletion = nil
}

/// Sends a message to the connected peripheral and returns the response.
Expand All @@ -295,7 +295,7 @@ class BLEManager: NSObject, CommProtocol {
/// `BLEManagerError.peripheralNotConnected` if the peripheral is not connected.
/// `BLEManagerError.timeout` if the operation times out.
/// `BLEManagerError.unknownError` if an unknown error occurs.
func sendCommand(_ command: String, retries: Int = 3) async throws -> [String] {
func sendCommand(_ command: String, retries _: Int = 3) async throws -> [String] {
guard sendMessageCompletion == nil else {
throw BLEManagerError.sendingMessagesInProgress
}
Expand Down Expand Up @@ -372,7 +372,7 @@ class BLEManager: NSObject, CommProtocol {
seconds: TimeInterval,
operation: @escaping @Sendable () async throws -> R
) async throws -> R {
return try await withThrowingTaskGroup(of: R.self) { group in
try await withThrowingTaskGroup(of: R.self) { group in
// Start actual work.
group.addTask {
let result = try await operation()
Expand Down
129 changes: 67 additions & 62 deletions Sources/SwiftOBD2/Communication/wifiManager.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//
// wifimanager.swift
// wifiManager.swift
//
//
// Created by kemo konteh on 2/26/24.
//

import CoreBluetooth
import Foundation
import Network
import OSLog
import CoreBluetooth

protocol CommProtocol {
func sendCommand(_ command: String, retries: Int) async throws -> [String]
Expand All @@ -35,45 +35,45 @@ class WifiManager: CommProtocol {

var tcp: NWConnection?

func connectAsync(timeout: TimeInterval, peripheral: CBPeripheral? = nil) async throws {
let host = NWEndpoint.Host("192.168.0.10")
guard let port = NWEndpoint.Port("35000") else {
throw CommunicationError.invalidData
}
tcp = NWConnection(host: host, port: port, using: .tcp)

try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
tcp?.stateUpdateHandler = { [weak self] newState in
guard let self = self else { return }
switch newState {
case .ready:
self.logger.info("Connected to \(host.debugDescription):\(port.debugDescription)")
self.connectionState = .connectedToAdapter
continuation.resume(returning: ())
case let .waiting(error):
self.logger.warning("Connection waiting: \(error.localizedDescription)")
case let .failed(error):
self.logger.error("Connection failed: \(error.localizedDescription)")
self.connectionState = .disconnected
continuation.resume(throwing: CommunicationError.errorOccurred(error))
default:
break
}
func connectAsync(timeout _: TimeInterval, peripheral _: CBPeripheral? = nil) async throws {
let host = NWEndpoint.Host("192.168.0.10")
guard let port = NWEndpoint.Port("35000") else {
throw CommunicationError.invalidData
}
tcp = NWConnection(host: host, port: port, using: .tcp)

try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
tcp?.stateUpdateHandler = { [weak self] newState in
guard let self = self else { return }
switch newState {
case .ready:
self.logger.info("Connected to \(host.debugDescription):\(port.debugDescription)")
self.connectionState = .connectedToAdapter
continuation.resume(returning: ())
case let .waiting(error):
self.logger.warning("Connection waiting: \(error.localizedDescription)")
case let .failed(error):
self.logger.error("Connection failed: \(error.localizedDescription)")
self.connectionState = .disconnected
continuation.resume(throwing: CommunicationError.errorOccurred(error))
default:
break
}
tcp?.start(queue: .main)
}
tcp?.start(queue: .main)
}
}

func sendCommand(_ command: String, retries: Int) async throws -> [String] {
guard let data = "\(command)\r".data(using: .ascii) else {
throw CommunicationError.invalidData
}
logger.info("Sending: \(command)")
return try await self.sendCommandInternal(data: data, retries: retries)
return try await sendCommandInternal(data: data, retries: retries)
}

private func sendCommandInternal(data: Data, retries: Int) async throws -> [String] {
for attempt in 1...retries {
for attempt in 1 ... retries {
do {
let response = try await sendAndReceiveData(data)
if let lines = processResponse(response) {
Expand All @@ -93,53 +93,58 @@ class WifiManager: CommProtocol {
}

private func sendAndReceiveData(_ data: Data) async throws -> String {
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<String, Error>) in
tcp?.send(content: data, completion: .contentProcessed { error in
guard let tcpConnection = tcp else {
throw CommunicationError.invalidData
}
let logger = self.logger // Avoid capturing `self` directly

return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<String, Error>) in
tcpConnection.send(content: data, completion: .contentProcessed { error in
if let error = error {
logger.error("Error sending data: \(error.localizedDescription)")
continuation.resume(throwing: CommunicationError.errorOccurred(error))
return
}

tcpConnection.receive(minimumIncompleteLength: 1, maximumLength: 500) { data, _, _, error in
if let error = error {
self.logger.error("Error sending data: \(error.localizedDescription)")
logger.error("Error receiving data: \(error.localizedDescription)")
continuation.resume(throwing: CommunicationError.errorOccurred(error))
return
}

self.tcp?.receive(minimumIncompleteLength: 1, maximumLength: 500) { data, _, _, error in
if let error = error {
self.logger.error("Error receiving data: \(error.localizedDescription)")
continuation.resume(throwing: CommunicationError.errorOccurred(error))
return
}

guard let response = data, let responseString = String(data: response, encoding: .utf8) else {
self.logger.warning("Received invalid or empty data")
continuation.resume(throwing: CommunicationError.invalidData)
return
}

continuation.resume(returning: responseString)
guard let response = data, let responseString = String(data: response, encoding: .utf8) else {
logger.warning("Received invalid or empty data")
continuation.resume(throwing: CommunicationError.invalidData)
return
}
})
}

continuation.resume(returning: responseString)
}
})
}
}

private func processResponse(_ response: String) -> [String]? {
logger.info("Processing response: \(response)")
var lines = response.components(separatedBy: .newlines).filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }

guard !lines.isEmpty else {
logger.warning("Empty response lines")
return nil
}
logger.info("Processing response: \(response)")
var lines = response.components(separatedBy: .newlines).filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }

if lines.last?.contains(">") == true {
lines.removeLast()
}
guard !lines.isEmpty else {
logger.warning("Empty response lines")
return nil
}

if lines.first?.lowercased() == "no data" {
return nil
}
if lines.last?.contains(">") == true {
lines.removeLast()
}

return lines
if lines.first?.lowercased() == "no data" {
return nil
}

return lines
}

func disconnectPeripheral() {
tcp?.cancel()
}
Expand Down
Loading

0 comments on commit 262909d

Please sign in to comment.