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

@ -10,6 +10,9 @@ final class KeyManagement {
/// The size of the individual keys in bytes /// The size of the individual keys in bytes
static let keySize = keySecurity / 8 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 /// The connection to the device
private var connection: WebSocket? private var connection: WebSocket?
@ -23,7 +26,7 @@ final class KeyManagement {
} }
/// The id of the key which was sent to the device /// 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 /// The result transmitted by the device for the sent key
var keyResult: KeyResult = .none var keyResult: KeyResult = .none
@ -50,18 +53,26 @@ final class KeyManagement {
keyResult = .none 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 { guard key.count == KeyManagement.keySize else {
return .invalidKeySize return eventLoop.makeSucceededFuture(.invalidPayloadSize)
} }
guard let socket = connection, !socket.isClosed else { guard let socket = connection, !socket.isClosed else {
connection = nil connection = nil
return .deviceNotConnected return eventLoop.makeSucceededFuture(.deviceNotConnected)
} }
let keyIdData = [UInt8(keyId >> 8), UInt8(keyId & 0xFF)] 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) 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) { func authenticateDevice(psk: String) {
@ -76,22 +87,24 @@ final class KeyManagement {
} }
func processDeviceResponse(_ data: ByteBuffer) { 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 { guard data.readableBytes == 1 else {
print("Unexpected number of bytes received from device") print("Unexpected number of bytes received from device")
keyInTransit = nil promise.succeed(.unexpectedSocketEvent)
keyResult = .unexpectedSocketEvent
return return
} }
guard let rawValue = data.getBytes(at: 0, length: 1)?.first else { guard let rawValue = data.getBytes(at: 0, length: 1)?.first else {
print("Unreadable data received from device") print("Unreadable data received from device")
keyInTransit = nil promise.succeed(.unexpectedSocketEvent)
keyResult = .unexpectedSocketEvent
return return
} }
guard let response = KeyResult(rawValue: rawValue) else { guard let response = KeyResult(rawValue: rawValue) else {
print("Unknown response \(rawValue) received from device") print("Unknown response \(rawValue) received from device")
keyInTransit = nil promise.succeed(.unexpectedSocketEvent)
keyResult = .unexpectedSocketEvent
return return
} }
guard keyInTransit != nil else { guard keyInTransit != nil else {

View File

@ -37,28 +37,19 @@ enum KeyResult: UInt8 {
/// The device is not connected through the socket /// The device is not connected through the socket
case notConnected = 10 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 /// The request did not contain body data with the key
case noBodyData = 2 case noBodyData = 11
/// The key contained in the body data has an invalid size
case invalidKeySize = 3
/// The body data could not be read /// The body data could not be read
case corruptkeyData = 4 case corruptkeyData = 12
/// The device is not connected /// 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 { 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 { guard let body = req.body.data else {
return .noBodyData return req.eventLoop.makeSucceededFuture(.noBodyData)
} }
guard body.readableBytes == KeyManagement.keySize else { guard body.readableBytes == KeyManagement.keySize else {
return .invalidKeySize return req.eventLoop.makeSucceededFuture(.invalidPayloadSize)
} }
guard let key = body.getData(at: 0, length: KeyManagement.keySize) else { 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 { func routes(_ app: Application) throws {
@ -57,6 +57,7 @@ func routes(_ app: Application) throws {
return "Success" return "Success"
} }
/** /**
Post a key to the device for unlocking. 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. A success of this method does not yet signal successful unlocking.
The client should request the status by inquiring the device response. The client should request the status by inquiring the device response.
*/ */
app.post(PublicAPI.postKey.path, PublicAPI.postKeyIdParameter.pathParameter) { req -> String in app.post(PublicAPI.postKey.path, PublicAPI.postKeyIdParameter.pathParameter) { req in
let result = handleKeyPost(req) keyTransmission(req).map { String($0.rawValue) }
return String(result.rawValue)
} }
/** /**