From 54c2fda9113066a2363ba5e0f217ee150170d9e7 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sat, 29 Jan 2022 10:26:30 +0100 Subject: [PATCH] Wait for device feedback when sending key --- Sources/App/KeyManagement.swift | 37 ++++++++++++++++++++++----------- Sources/App/Response.swift | 27 ++++++++---------------- Sources/App/routes.swift | 18 ++++++++-------- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/Sources/App/KeyManagement.swift b/Sources/App/KeyManagement.swift index 2ccf30d..93e11c1 100644 --- a/Sources/App/KeyManagement.swift +++ b/Sources/App/KeyManagement.swift @@ -9,6 +9,9 @@ final class KeyManagement { /// The size of the individual keys in bytes static let keySize = keySecurity / 8 + + /// The seconds to wait for a response from the device + static let deviceTimeout: Int64 = 20 /// The connection to the device private var connection: WebSocket? @@ -23,7 +26,7 @@ final class KeyManagement { } /// The id of the key which was sent to the device - private var keyInTransit: UInt16? + private var keyInTransit: (id: UInt16, promise: EventLoopPromise)? /// The result transmitted by the device for the sent key var keyResult: KeyResult = .none @@ -50,18 +53,26 @@ final class KeyManagement { keyResult = .none } - func sendKeyToDevice(_ key: Data, keyId: UInt16) -> KeyPostResponse { + func sendKeyToDevice(_ key: Data, keyId: UInt16, on eventLoop: EventLoop) -> EventLoopFuture { guard key.count == KeyManagement.keySize else { - return .invalidKeySize + return eventLoop.makeSucceededFuture(.invalidPayloadSize) } guard let socket = connection, !socket.isClosed else { connection = nil - return .deviceNotConnected + return eventLoop.makeSucceededFuture(.deviceNotConnected) } let keyIdData = [UInt8(keyId >> 8), UInt8(keyId & 0xFF)] - keyInTransit = keyId + let promise = eventLoop.makePromise(of: KeyResult.self) + keyInTransit = (keyId, promise) socket.send(keyIdData + key, promise: nil) - return .success + eventLoop.scheduleTask(in: .seconds(Self.deviceTimeout)) { [weak self] in + guard let (storedKeyId, promise) = self?.keyInTransit, storedKeyId == keyId else { + return + } + self?.keyInTransit = nil + promise.succeed(.deviceTimedOut) + } + return promise.futureResult } func authenticateDevice(psk: String) { @@ -76,22 +87,24 @@ final class KeyManagement { } func processDeviceResponse(_ data: ByteBuffer) { + guard let (_, promise) = keyInTransit else { + print("No key in transit for response from device \(data)") + return + } + defer { keyInTransit = nil } guard data.readableBytes == 1 else { print("Unexpected number of bytes received from device") - keyInTransit = nil - keyResult = .unexpectedSocketEvent + promise.succeed(.unexpectedSocketEvent) return } guard let rawValue = data.getBytes(at: 0, length: 1)?.first else { print("Unreadable data received from device") - keyInTransit = nil - keyResult = .unexpectedSocketEvent + promise.succeed(.unexpectedSocketEvent) return } guard let response = KeyResult(rawValue: rawValue) else { print("Unknown response \(rawValue) received from device") - keyInTransit = nil - keyResult = .unexpectedSocketEvent + promise.succeed(.unexpectedSocketEvent) return } guard keyInTransit != nil else { diff --git a/Sources/App/Response.swift b/Sources/App/Response.swift index 0823c18..1b4306b 100644 --- a/Sources/App/Response.swift +++ b/Sources/App/Response.swift @@ -37,28 +37,19 @@ enum KeyResult: UInt8 { /// The device is not connected through the socket case notConnected = 10 -} - -/** - A response from the server to a key request. - */ -enum KeyPostResponse: Int { - - /// The key will be transmitted to the device - case success = 0 - - /// The key id is out of bounds or otherwise invalid - case invalidKeyId = 1 /// The request did not contain body data with the key - case noBodyData = 2 - - /// The key contained in the body data has an invalid size - case invalidKeySize = 3 + case noBodyData = 11 /// The body data could not be read - case corruptkeyData = 4 + case corruptkeyData = 12 /// The device is not connected - case deviceNotConnected = 5 + case deviceNotConnected = 13 + + /// The device did not respond within the timeout + case deviceTimedOut = 14 + + /// The key was valid and the door will be opened + case success = 15 } diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index 0d40d2c..22d2f90 100755 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -11,20 +11,20 @@ extension PublicAPI { } } -private func handleKeyPost(_ req: Request) -> KeyPostResponse { +private func keyTransmission(_ req: Request) -> EventLoopFuture { guard let keyId = req.parameters.get(PublicAPI.postKeyIdParameter.rawValue, as: UInt16.self) else { - return .invalidKeyId + return req.eventLoop.makeSucceededFuture(.invalidKeyIndex) } guard let body = req.body.data else { - return .noBodyData + return req.eventLoop.makeSucceededFuture(.noBodyData) } guard body.readableBytes == KeyManagement.keySize else { - return .invalidKeySize + return req.eventLoop.makeSucceededFuture(.invalidPayloadSize) } guard let key = body.getData(at: 0, length: KeyManagement.keySize) else { - return .corruptkeyData + return req.eventLoop.makeSucceededFuture(.corruptkeyData) } - return keyManager.sendKeyToDevice(key, keyId: keyId) + return keyManager.sendKeyToDevice(key, keyId: keyId, on: req.eventLoop) } func routes(_ app: Application) throws { @@ -57,6 +57,7 @@ func routes(_ app: Application) throws { return "Success" } + /** Post a key to the device for unlocking. @@ -66,9 +67,8 @@ func routes(_ app: Application) throws { A success of this method does not yet signal successful unlocking. The client should request the status by inquiring the device response. */ - app.post(PublicAPI.postKey.path, PublicAPI.postKeyIdParameter.pathParameter) { req -> String in - let result = handleKeyPost(req) - return String(result.rawValue) + app.post(PublicAPI.postKey.path, PublicAPI.postKeyIdParameter.pathParameter) { req in + keyTransmission(req).map { String($0.rawValue) } } /**