Wait for device feedback when sending key

This commit is contained in:
Christoph Hagen 2022-01-29 10:26:30 +01:00
parent f891087705
commit 54c2fda911
3 changed files with 43 additions and 39 deletions

View File

@ -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<KeyResult>)?
/// 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<KeyResult> {
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 {

View File

@ -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
}

View File

@ -11,20 +11,20 @@ extension PublicAPI {
}
}
private func handleKeyPost(_ req: Request) -> KeyPostResponse {
private func keyTransmission(_ req: Request) -> EventLoopFuture<KeyResult> {
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) }
}
/**