Lighthouse Test
+ ++ +
+diff --git a/Package.resolved b/Package.resolved index 2e0bdcb..76cfaf2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,51 @@ { "object": { "pins": [ + { + "package": "async-http-client", + "repositoryURL": "https://github.com/swift-server/async-http-client.git", + "state": { + "branch": null, + "revision": "7a4dfe026f6ee0f8ad741b58df74c60af296365d", + "version": "1.9.0" + } + }, + { + "package": "async-kit", + "repositoryURL": "https://github.com/vapor/async-kit.git", + "state": { + "branch": null, + "revision": "e2f741640364c1d271405da637029ea6a33f754e", + "version": "1.11.1" + } + }, + { + "package": "console-kit", + "repositoryURL": "https://github.com/vapor/console-kit.git", + "state": { + "branch": null, + "revision": "75ea3b627d88221440b878e5dfccc73fd06842ed", + "version": "4.2.7" + } + }, + { + "package": "leaf", + "repositoryURL": "https://github.com/vapor/leaf.git", + "state": { + "branch": null, + "revision": "41741b782ac49959af2f7612661154326ac24d00", + "version": "4.1.5" + } + }, + { + "package": "leaf-kit", + "repositoryURL": "https://github.com/vapor/leaf-kit.git", + "state": { + "branch": null, + "revision": "983fcbe89e7153c4d5870bdc76bf57a56817225f", + "version": "1.4.0" + } + }, { "package": "MessagePack", "repositoryURL": "https://github.com/Flight-School/MessagePack.git", @@ -10,6 +55,24 @@ "version": "1.2.4" } }, + { + "package": "multipart-kit", + "repositoryURL": "https://github.com/vapor/multipart-kit.git", + "state": { + "branch": null, + "revision": "2dd9368a3c9580792b77c7ef364f3735909d9996", + "version": "4.5.1" + } + }, + { + "package": "routing-kit", + "repositoryURL": "https://github.com/vapor/routing-kit.git", + "state": { + "branch": null, + "revision": "5603b81ceb744b8318feab1e60943704977a866b", + "version": "4.3.1" + } + }, { "package": "swift-argument-parser", "repositoryURL": "https://github.com/apple/swift-argument-parser.git", @@ -19,6 +82,24 @@ "version": "1.0.3" } }, + { + "package": "swift-backtrace", + "repositoryURL": "https://github.com/swift-server/swift-backtrace.git", + "state": { + "branch": null, + "revision": "d3e04a9d4b3833363fb6192065b763310b156d54", + "version": "1.3.1" + } + }, + { + "package": "swift-crypto", + "repositoryURL": "https://github.com/apple/swift-crypto.git", + "state": { + "branch": null, + "revision": "a8911e0fadc25aef1071d582355bd1037a176060", + "version": "2.0.4" + } + }, { "package": "swift-log", "repositoryURL": "https://github.com/apple/swift-log.git", @@ -28,6 +109,15 @@ "version": "1.4.2" } }, + { + "package": "swift-metrics", + "repositoryURL": "https://github.com/apple/swift-metrics.git", + "state": { + "branch": null, + "revision": "3edd2f57afc4e68e23c3e4956bc8b65ca6b5b2ff", + "version": "2.2.0" + } + }, { "package": "swift-nio", "repositoryURL": "https://github.com/apple/swift-nio.git", @@ -37,6 +127,24 @@ "version": "2.38.0" } }, + { + "package": "swift-nio-extras", + "repositoryURL": "https://github.com/apple/swift-nio-extras.git", + "state": { + "branch": null, + "revision": "f73ca5ee9c6806800243f1ac415fcf82de9a4c91", + "version": "1.10.2" + } + }, + { + "package": "swift-nio-http2", + "repositoryURL": "https://github.com/apple/swift-nio-http2.git", + "state": { + "branch": null, + "revision": "000ca94f9de92c95b9ac85d44600b7b0fe25a3e5", + "version": "1.19.2" + } + }, { "package": "swift-nio-ssl", "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", @@ -46,6 +154,24 @@ "version": "2.17.2" } }, + { + "package": "swift-nio-transport-services", + "repositoryURL": "https://github.com/apple/swift-nio-transport-services.git", + "state": { + "branch": null, + "revision": "8ab824b140d0ebcd87e9149266ddc353e3705a3e", + "version": "1.11.4" + } + }, + { + "package": "vapor", + "repositoryURL": "https://github.com/vapor/vapor.git", + "state": { + "branch": null, + "revision": "18e9419cae5049e43ca1e8002ca3cf0449f2c8ed", + "version": "4.55.0" + } + }, { "package": "websocket-kit", "repositoryURL": "https://github.com/vapor/websocket-kit.git", diff --git a/Package.swift b/Package.swift index 84c280a..737fe9e 100644 --- a/Package.swift +++ b/Package.swift @@ -10,12 +10,16 @@ let package = Package( // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "LighthouseClient", - targets: ["LighthouseClient"] + targets: ["LighthouseProtocol", "LighthouseClient"] ), .executable( name: "LighthouseDemo", targets: ["LighthouseDemo"] - ) + ), + .executable( + name: "LighthouseTestServer", + targets: ["LighthouseTestServer"] + ), ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -23,26 +27,47 @@ let package = Package( .package(url: "https://github.com/Flight-School/MessagePack.git", from: "1.2.4"), .package(url: "https://github.com/apple/swift-log.git", from: "1.4.2"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"), + .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), + .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "LighthouseProtocol", + dependencies: [ + .product(name: "Logging", package: "swift-log"), + ] + ), .target( name: "LighthouseClient", dependencies: [ - .product(name: "WebSocketKit", package: "websocket-kit"), - .product(name: "MessagePack", package: "MessagePack"), + .target(name: "LighthouseProtocol"), .product(name: "Logging", package: "swift-log"), + .product(name: "MessagePack", package: "MessagePack"), + .product(name: "WebSocketKit", package: "websocket-kit"), ] ), .executableTarget( name: "LighthouseDemo", dependencies: [ + .target(name: "LighthouseProtocol"), .target(name: "LighthouseClient"), .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "Logging", package: "swift-log"), ] ), + .executableTarget( + name: "LighthouseTestServer", + dependencies: [ + .target(name: "LighthouseProtocol"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Logging", package: "swift-log"), + .product(name: "MessagePack", package: "MessagePack"), + .product(name: "Vapor", package: "vapor"), + .product(name: "Leaf", package: "leaf"), + ] + ), // .testTarget( // name: "LighthouseClientTests", // dependencies: [ diff --git a/Public/scripts.js b/Public/scripts.js new file mode 100644 index 0000000..035014a --- /dev/null +++ b/Public/scripts.js @@ -0,0 +1,96 @@ +const lighthouseRows = 14; +const lighthouseCols = 28; +const xScale = 14; +const yScale = 2 * xScale; + +let connection = null; +let auth = null; +let display = null; + +function updateDisplay(rgb) { + const ctx = display.getContext("2d"); + + for (let i = 0; i < (rgb.length / 3); i++) { + const r = rgb[3 * i]; + const g = rgb[3 * i + 1]; + const b = rgb[3 * i + 2]; + + const y = Math.floor(i / lighthouseCols); + const x = i % lighthouseCols; + + ctx.fillStyle = `rgb(${r},${g},${b})`; + ctx.fillRect(x * xScale, y * yScale, xScale, yScale); + } +} + +function setUpDisplay() { + display = document.getElementById("display"); + display.width = xScale * lighthouseCols; + display.height = yScale * lighthouseRows; + updateDisplay(new Uint8Array(3 * lighthouseRows * lighthouseCols)); +} + +function setUpConnection() { + connection = new WebSocket(`${location.origin.replace(/^http/, "ws")}/websocket`); + connection.binaryType = "arraybuffer"; + + connection.addEventListener("open", () => { + console.log("Connected!"); + }); + + connection.addEventListener("message", event => { + try { + const message = MessagePack.decode(new Uint8Array(event.data)); + + if (message.PAYL instanceof Uint8Array) { + updateDisplay(message.PAYL); + } else { + console.log(`Something else: ${message.PAYL instanceof Uint8Array}`); + } + } catch (e) { + console.log(`Error while decoding message from WebSocket: ${e}`); + } + }); +} + +function setUpFormListener() { + const form = document.getElementById("auth-form"); + const fieldset = document.getElementById("auth-form-fieldset"); + const usernameField = document.getElementById("username"); + const tokenField = document.getElementById("token"); + + form.addEventListener("submit", event => { + event.preventDefault(); + + if (auth) { + alert("You are already authenticated!"); + return; + } + + const username = usernameField.value; + const token = tokenField.value; + + if (!username) { + alert("Please provide a username!"); + return; + } + + auth = { USER: username, TOKEN: token }; + fieldset.disabled = true; + + connection.send(MessagePack.encode({ + VERB: "STREAM", + PATH: ["user", username, "model"], + AUTH: auth, + META: {}, + REID: 0, + PAYL: null, + })); + }); +} + +window.addEventListener("load", () => { + setUpDisplay(); + setUpConnection(); + setUpFormListener(); +}); diff --git a/README.md b/README.md index 0d682cf..91ca6b5 100644 --- a/README.md +++ b/README.md @@ -7,26 +7,38 @@ An API client for a light installation at the University of Kiel using Swift 5.5 ## Example ```swift -// Prepare connection -let conn = Connection(authentication: Authentication( - username: "[your username]", - token: "[your token]" -)) - -// Handle incoming input events -conn.onInput { input in - print("Got input \(input)") +import LighthouseClient +import LighthouseProtocol +import Dispatch + +func runApp() async throws { + // Prepare connection + let conn = Connection(authentication: Authentication( + username: "[your username]", + token: "[your token]" + )) + + // Handle incoming input events + conn.onInput { input in + print("Got input \(input)") + } + + // Connect to the lighthouse server and request events + try await conn.connect() + try await conn.requestStream() + + // Repeatedly send colored displays to the lighthouse + while true { + try await conn.send(display: Display(fill: .random())) + try await Task.sleep(nanoseconds: 1_000_000_000) + } } -// Connect to the lighthouse server and request events -try await conn.connect() -try await conn.requestStream() - -// Repeatedly send colored displays to the lighthouse -while true { - try await conn.send(display: Display(fill: .random())) - try await Task.sleep(nanoseconds: 1_000_000_000) +Task { + try! await runApp() } + +dispatchMain() ``` ## Usage diff --git a/Resources/Views/index.leaf b/Resources/Views/index.leaf new file mode 100644 index 0000000..79081e4 --- /dev/null +++ b/Resources/Views/index.leaf @@ -0,0 +1,23 @@ + + +
++ +
+