diff --git a/examples/disco/package.json b/examples/disco/package.json index 9f98d2a..681f6e4 100644 --- a/examples/disco/package.json +++ b/examples/disco/package.json @@ -4,6 +4,7 @@ "description": "", "main": "build/index.js", "scripts": { + "dev": "tsc -w", "build": "tsc", "start": "node .", "build:start": "npm run build && npm run start" diff --git a/examples/disco/src/index.ts b/examples/disco/src/index.ts index 26af3b9..1cccaff 100644 --- a/examples/disco/src/index.ts +++ b/examples/disco/src/index.ts @@ -1,14 +1,20 @@ /* eslint-disable no-await-in-loop */ import { config } from "dotenv"; -import { LighthouseAuth, LighthouseWebsocket } from "lighthouse.js"; +import { LighthouseAuth, LighthouseWebsocket, LIGHTHOUSE_WIDTH, LIGHTHOUSE_HEIGHT } from "lighthouse.js"; config(); -const user = process.env.LIGHTHOUSE_USER ?? ""; +function getEnv(name: string): string { + const value = process.env[name]; + if (!value) throw Error(`Environment variable ${name} is not defined!`); + return value; +} + +const user = getEnv("LIGHTHOUSE_USER"); const auth: LighthouseAuth = { USER: user, - TOKEN: process.env.LIGHTHOUSE_TOKEN ?? "", + TOKEN: getEnv("LIGHTHOUSE_TOKEN"), }; async function sleep(time: number) { @@ -24,8 +30,8 @@ async function sleep(time: number) { // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-loop-func - const data = new Array(28 * 14 * 3).fill(0).map((_, j) => (j % 3 === i ? 255 : 0)); - const msg = await lh.send(data); + const data = new Uint8Array(LIGHTHOUSE_WIDTH * LIGHTHOUSE_HEIGHT * 3).map((_, j) => (j % 3 === i ? 255 : 0)); + const msg = await lh.sendDisplay(data); // eslint-disable-next-line no-console console.log(msg); diff --git a/src/Lighthouse/LighthouseWebsocket.ts b/src/Lighthouse/LighthouseWebsocket.ts deleted file mode 100644 index 67bbf98..0000000 --- a/src/Lighthouse/LighthouseWebsocket.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { decode, encode } from "@msgpack/msgpack"; -import { v4 as uuid } from "uuid"; -import { WebSocket } from "ws"; -import { LighthouseAuth, RequestPayload, ResponsePayload } from "./types"; - -type ResponseHandler = (data: ResponsePayload) => void; - -export class LighthouseWebsocket { - private static readonly serverAddress = "wss://lighthouse.uni-kiel.de/websocket"; - - private ws?: WebSocket; - - private responseHandlers: Map; - - constructor(private readonly auth: LighthouseAuth) { - this.responseHandlers = new Map(); - } - - public async open(address = LighthouseWebsocket.serverAddress): Promise { - this.ws = new WebSocket(address); - - this.ws.on("message", (data) => { - const response: ResponsePayload = decode(new Uint8Array(data as Buffer)) as ResponsePayload; - const handler = this.responseHandlers.get(response.REID); - if (handler && typeof handler === "function") { - handler(response); - this.responseHandlers.delete(response.REID); - } - }); - return new Promise((res) => { - this.ws?.once("open", (code: number) => { - res(code); - }); - }); - } - - public async send(payload: number[]): Promise { - const id = uuid(); - const data: RequestPayload = { - AUTH: this.auth, - META: {}, - PATH: ["user", this.auth.USER, "model"], - PAYL: new Uint8Array(payload), - REID: id, - VERB: "PUT", - }; - if (this.ws?.readyState === WebSocket.OPEN) { - const prom = new Promise((res) => { - this.registerResponseHandler(id, res); - }); - this.ws?.send(encode(data)); - return prom; - } - throw new Error("Websocket is currently not open!"); - } - - private registerResponseHandler(id: string, cb: ResponseHandler) { - this.responseHandlers.set(id, cb); - } - - public close(): void { - this.ws?.close(); - } -} diff --git a/src/Lighthouse/constants.ts b/src/Lighthouse/constants.ts new file mode 100644 index 0000000..433c7ba --- /dev/null +++ b/src/Lighthouse/constants.ts @@ -0,0 +1,2 @@ +export const LIGHTHOUSE_WIDTH: number = 28; +export const LIGHTHOUSE_HEIGHT: number = 14; diff --git a/src/Lighthouse/protocol.ts b/src/Lighthouse/protocol.ts new file mode 100644 index 0000000..1e53d2d --- /dev/null +++ b/src/Lighthouse/protocol.ts @@ -0,0 +1,25 @@ +export type LighthouseVerb = "POST" | "CREATE" | "MKDIR" | "DELETE" | "LIST" | "GET" | "PUT" | "STREAM" | "STOP" | "LINK" | "UNLINK"; +export type LighthousePath = ["user", U, "model"]; + +export interface LighthouseAuth { + USER: U; + TOKEN: string; +} + +export interface LighthouseRequest { + REID: string; + AUTH: LighthouseAuth; + VERB: LighthouseVerb; + PATH: LighthousePath; + META: object; + PAYL: P; +} + +export interface LighthouseEvent

{ + REID: string; + RNUM: number; + RESPONSE: string; + META: object; + PAYL: P; + WARNINGS: string[]; +} diff --git a/src/Lighthouse/types.ts b/src/Lighthouse/types.ts deleted file mode 100644 index 1c65652..0000000 --- a/src/Lighthouse/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -type Verb = "POST" | "CREATE" | "MKDIR" | "DELETE" | "LIST" | "GET" | "PUT" | "STREAM" | "STOP" | "LINK" | "UNLINK"; - -export interface LighthouseAuth { - USER: U; - TOKEN: string; -} - -export interface RequestPayload { - REID: string; - AUTH: LighthouseAuth; - VERB: Verb; - PATH: ["user", U, "model"]; - META: object; - PAYL: unknown; -} - -export interface ResponsePayload { - REID: string; - RNUM: number; - RESPONSE: string; - META: object; - PAYL: unknown; - WARNINGS: string[]; -} diff --git a/src/Lighthouse/websocket.ts b/src/Lighthouse/websocket.ts new file mode 100644 index 0000000..6bb50a7 --- /dev/null +++ b/src/Lighthouse/websocket.ts @@ -0,0 +1,85 @@ +import { decode, encode } from "@msgpack/msgpack"; +import { v4 as uuid } from "uuid"; +import { WebSocket } from "ws"; +import { LighthouseAuth, LighthousePath, LighthouseRequest, LighthouseEvent, LighthouseVerb } from "./protocol"; + +type LighthouseEventHandler

= (event: LighthouseEvent

) => void; + +export class LighthouseWebsocket { + private static readonly serverAddress = "wss://lighthouse.uni-kiel.de/websocket"; + + private ws?: WebSocket; + + private responseHandlers: Map> = new Map(); + private eventHandlers: LighthouseEventHandler[] = []; + + constructor(private readonly auth: LighthouseAuth) {} + + public async open(address = LighthouseWebsocket.serverAddress): Promise { + this.ws = new WebSocket(address); + + this.ws.on("message", (data) => { + const response = decode(new Uint8Array(data as Buffer)) as LighthouseEvent; + const handler = this.responseHandlers.get(response.REID); + if (handler && typeof handler === "function") { + handler(response); + this.responseHandlers.delete(response.REID); + } else { + for (const handler of this.eventHandlers) { + handler(response); + } + } + }); + return new Promise((res) => { + this.ws?.once("open", (code: number) => { + res(code); + }); + }); + } + + public async sendDisplay(rgbValues: Uint8Array): Promise> { + return await this.send("PUT", ["user", this.auth.USER, "model"], rgbValues); + } + + public async requestStream(): Promise> { + return await this.send("STREAM", ["user", this.auth.USER, "model"], undefined); + } + + private async send

(verb: LighthouseVerb, path: LighthousePath, payload: P): Promise> { + const id = uuid(); + const request: LighthouseRequest = { + AUTH: this.auth, + META: {}, + PATH: path, + PAYL: payload, + REID: id, + VERB: verb, + }; + if (this.ws?.readyState === WebSocket.OPEN) { + const prom = new Promise>((resolve, reject) => { + this.registerResponseHandler(id, response => { + if (response.RNUM === 200) { + resolve(response); + } else { + reject(`${response.RNUM} ${response.RESPONSE}`); + } + }); + }); + this.ws?.send(encode(request)); + return prom; + } + throw new Error("Websocket is currently not open!"); + } + + private registerResponseHandler

(id: string, cb: LighthouseEventHandler

): void { + this.responseHandlers.set(id, cb as LighthouseEventHandler); + } + + private registerEventHandler

(cb: LighthouseEventHandler

): void { + this.eventHandlers.push(cb as LighthouseEventHandler); + } + + public close(): void { + this.ws?.close(); + } +} diff --git a/src/index.ts b/src/index.ts index 2ea1cdf..6f4000a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ -export * from "./Lighthouse/LighthouseWebsocket"; -export * from "./Lighthouse/types"; +export * from "./lighthouse/websocket"; +export * from "./lighthouse/constants"; +export * from "./lighthouse/protocol";