import Foundation import WebSocketKit import Vapor final class DeviceManager { /// The seconds to wait for a response from the device static let deviceTimeout: Int64 = 20 /// The connection to the device private var connection: WebSocket? /// The authentication token of the device for the socket connection private let deviceKey: String /// Indicate that the socket is fully initialized with an authorized device var deviceIsAuthenticated = false /// Indicator for device availability var deviceIsConnected: Bool { !(connection?.isClosed ?? true) && deviceIsAuthenticated } /// A promise to finish the request once the device responds or times out private var requestInProgress: EventLoopPromise? init(deviceKey: String) { self.deviceKey = deviceKey } // MARK: API var deviceStatus: String { deviceIsConnected ? "1" : "0" } func sendMessageToDevice(_ message: Message, on eventLoop: EventLoop) -> EventLoopFuture { guard let socket = connection, !socket.isClosed else { connection = nil return eventLoop.makeSucceededFuture(.deviceNotConnected) } guard requestInProgress == nil else { return eventLoop.makeSucceededFuture(.operationInProgress) } requestInProgress = eventLoop.makePromise(of: DeviceResponse.self) socket.send(message.bytes, promise: nil) eventLoop.scheduleTask(in: .seconds(Self.deviceTimeout)) { [weak self] in guard let promise = self?.requestInProgress else { return } self?.requestInProgress = nil promise.succeed(.deviceTimedOut) } return requestInProgress!.futureResult } func authenticateDevice(psk: String) { guard psk == self.deviceKey else { print("Invalid device key") _ = connection?.close() deviceIsAuthenticated = false return } print("Device authenticated") deviceIsAuthenticated = true } func processDeviceResponse(_ data: ByteBuffer) { guard let promise = requestInProgress else { print("No message in transit for response from device \(data)") return } defer { requestInProgress = nil } promise.succeed(DeviceResponse(data) ?? .unexpectedSocketEvent) } func didCloseDeviceSocket() { deviceIsAuthenticated = false guard connection != nil else { return } connection = nil print("Socket closed") } func removeDeviceConnection() { _ = connection?.close() connection = nil } func createNewDeviceConnection(_ socket: WebSocket) { removeDeviceConnection() connection = socket deviceIsAuthenticated = false print("Socket connected") } }