diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..de171cd8 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 8b137891..236a7399 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ +.vs/ +.vscode/ +*.o +*.bin + +*/__pycache__ \ No newline at end of file diff --git a/README.md b/README.md index 0927af9a..734f58d9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,42 @@ # Wireless22A -Code for Wireless22A project, +Code for Wireless22A project. +## Siren +Siren is our [pub/sub](https://www.stackpath.com/edge-academy/what-is-pub-sub-messaging/) server that uses the WebSocket protocol to send telemetry data from the car. Siren is built off of the [Bun framework](https://bun.sh/), which is a JavaScript framework that has the capability to host extremely fast WebSocket servers using pub/sub messaging. -## Websockets -Initial version for a client/server protocol that can be used to send data over websockets. It supports sending data from the server to the client and -sending control commands from the client to the server. The server has a task running to clean up closed connections. +### About WebSockets +For information about WebSockets, check out [this confluence page](https://nerdocs.atlassian.net/wiki/spaces/NER/pages/161972226/WebSocket+Basics) about WebSocket basics. + +### Running with Docker +Coming soon. + +### Local Setup +To set up Siren locally on your machine, simply use the `install` script. + +- For Mac/Linux: +``` +$ ./install +``` + +- For Windows: +``` +No script yet. +``` + +### Running Locally +To run Siren locally on your machine, simply use the `run` script. + +- For Mac/Linux: +``` +$ ./run +``` + +- For Windows: +``` +No script yet. +``` + +After that, Siren should start up and it is open to connections. + +### Testing Siren +To test that Siren is working properly, run the `subscriber.py` and `publisher.py` scripts in the same environment that Siren is hosted in. After a few seconds, the terminal running the `subscriber.py` script should begin receiving messages, which means that Siren is working properly. diff --git a/siren/.gitignore b/siren/.gitignore new file mode 100644 index 00000000..f81d56ea --- /dev/null +++ b/siren/.gitignore @@ -0,0 +1,169 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* diff --git a/siren/Dockerfile b/siren/Dockerfile new file mode 100644 index 00000000..0723a736 --- /dev/null +++ b/siren/Dockerfile @@ -0,0 +1,69 @@ +FROM ubuntu:latest + +# Set up container and time zones +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive TZ="America/New_York" \ + apt-get -y install tzdata + +# Download Linux support tools +RUN apt-get install -y \ + build-essential \ + wget \ + curl \ + openocd \ + git \ + gdb-multiarch \ + minicom \ + vim + +# Download Python, Pip, and install websocket support +RUN apt-get install -y \ + python3 \ + pip +RUN pip install websocket-client + +# Download setup support for Rasperry Pi Probe +RUN apt-get install -y \ + automake \ + autoconf \ + texinfo \ + libtool \ + libftdi-dev \ + libusb-1.0-0-dev \ + pkg-config + +RUN git clone https://github.com/raspberrypi/openocd.git \ + --branch rp2040 \ + --depth=1 \ + --no-single-branch + +# Build Rasberry Pi Probe Package +RUN cd openocd && ./bootstrap +RUN cd openocd && ./configure +RUN cd openocd && make -j4 && make install + +# Set up a development tools directory +WORKDIR /home/dev +ADD . /home/dev + +RUN echo 'if [ $n -e /home/app/shepherd.ioc ]; then echo \ +" ______ __ __ ______ ______ __ __ ______ ______ _____\n\ +/\ ___\ /\ \_\ \ /\ ___\ /\ == \ /\ \_\ \ /\ ___\ /\ == \ /\ __-.\n\ +\ \___ \ \ \ __ \ \ \ __\ \ \ _-/ \ \ __ \ \ \ __\ \ \ __< \ \ \/\ \ \n\ + \/\_____\ \ \_\ \_\ \ \_____\ \ \_\ \ \_\ \_\ \ \_____\ \ \_\ \_\ \ \____- \n\ + \/_____/ \/_/\/_/ \/_____/ \/_/ \/_/\/_/ \/_____/ \/_/ /_/ \/____/"; fi;' >> ~/.bashrc + +RUN echo 'if [ $n -e /home/app/cerberus.ioc ]; then echo \ +"_________ ___. \n\ +\_ ___ \ __________\_ |__ ___________ __ __ ______\n\ +/ \ \/_/ __ \_ __ \ __ \_/ __ \_ __ \ | \/ ___/\n\ +\ \___\ ___/| | \/ \_\ \ ___/| | \/ | /\___ \ \n\ + \______ /\___ >__| |___ /\___ >__| |____//____ >\n\ + \/ \/ \/ \/ \/ "; fi;' >> ~/.bashrc + +# Install cross compiler +RUN wget -qO- https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.10/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 | tar -xvj + +ENV PATH $PATH:/home/dev/gcc-arm-none-eabi-10.3-2021.10/bin + +WORKDIR /home/app diff --git a/siren/bun.lockb b/siren/bun.lockb new file mode 100755 index 00000000..b2e473d2 Binary files /dev/null and b/siren/bun.lockb differ diff --git a/siren/index.ts b/siren/index.ts new file mode 100644 index 00000000..2217b89c --- /dev/null +++ b/siren/index.ts @@ -0,0 +1,35 @@ +const server = Bun.serve({ + port: 3000, + fetch(req, server) { + server.upgrade(req, { + data: { + subTopics: new URL(req.url).searchParams.get("subTopics") + }, + }); + + return new Response("Upgrade to WebSocket failed :(", { status: 500 }); + }, + websocket: { + open(ws) { + + if (ws.data.subTopics) { + ws.subscribe(ws.data.subTopics); // TODO: parse for different topics and subscribe to them all + } + }, + message(ws, message) { + + // TODO: allow subscriptions to additional topics and unsubscribing from topics + + server.publish("test", message); + // TODO: allow selection of publishing channel + }, + close(ws) { + + if (ws.data.subTopics) { + ws.unsubscribe(ws.data.subTopics); // TODO: parse for different topics and unsubscribe to them all + } + } + } +}); + +console.log(`Listening on http://localhost:${server.port}`); \ No newline at end of file diff --git a/siren/install b/siren/install new file mode 100755 index 00000000..fedcaf10 --- /dev/null +++ b/siren/install @@ -0,0 +1,3 @@ +curl -fsSL https://bun.sh/install | bash +exec /bin/zsh +bun add -d bun-types \ No newline at end of file diff --git a/siren/package.json b/siren/package.json new file mode 100644 index 00000000..d998aa20 --- /dev/null +++ b/siren/package.json @@ -0,0 +1,15 @@ +{ + "dependencies": {}, + "devDependencies": { + "bun-types": "^1.0.1" + }, + "name": "bun", + "module": "index.ts", + "type": "module", + "scripts": { + "start": "bun run index.ts" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} \ No newline at end of file diff --git a/siren/publisher.py b/siren/publisher.py new file mode 100644 index 00000000..32c7e11d --- /dev/null +++ b/siren/publisher.py @@ -0,0 +1,42 @@ +import websocket +import time + +# Callback for handling WebSocket open event +def on_open(ws): + print("Started") + + while True: + time.sleep(5) + ws.send("Sending message to test topic!") + +# Callback for handling WebSocket message event +def on_message(ws, message): + print("Received message: {}".format(message)) + +# Callback for handling WebSocket error event +def on_error(ws, error): + print("Error encountered: {}".format(error)) + +# Callback for handling WebSocket close event +def on_close(wsapp, close_status_code, close_msg): + print("WebSocket connection closed.") + + if (close_status_code): + print("Closing status code: " + close_status_code) + if (close_msg): + print("Closing message: " + close_msg) + +# Define the WebSocket server URL +websocket_url = "ws://127.0.0.1:3000" + +# Creating WebSocket connection object and attaching event handlers +ws = websocket.WebSocketApp( + websocket_url, + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close +) + +# Start the WebSocket connection +ws.run_forever() diff --git a/siren/run b/siren/run new file mode 100755 index 00000000..c9ff5436 --- /dev/null +++ b/siren/run @@ -0,0 +1 @@ +bun run start \ No newline at end of file diff --git a/siren/subscriber.py b/siren/subscriber.py new file mode 100644 index 00000000..96b1a6be --- /dev/null +++ b/siren/subscriber.py @@ -0,0 +1,38 @@ +import websocket +import time + +# Callback for handling WebSocket open event +def on_open(ws): + print("Started") + +# Callback for handling WebSocket message event +def on_message(ws, message): + print("Received message: {}".format(message)) + +# Callback for handling WebSocket error event +def on_error(ws, error): + print("Error encountered: {}".format(error)) + +# Callback for handling WebSocket close event +def on_close(wsapp, close_status_code, close_msg): + print("WebSocket connection closed.") + + if (close_status_code): + print("Closing status code: " + close_status_code) + if (close_msg): + print("Closing message: " + close_msg) + +# Define the WebSocket server URL +websocket_url = "ws://127.0.0.1:3000?subTopics=test" + +# Creating WebSocket connection object and attaching event handlers +ws = websocket.WebSocketApp( + websocket_url, + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close +) + +# Start the WebSocket connection +ws.run_forever() diff --git a/siren/tsconfig.json b/siren/tsconfig.json new file mode 100644 index 00000000..1449bc3d --- /dev/null +++ b/siren/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "preserve", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + } +} diff --git a/siren/websocket-data.d.ts b/siren/websocket-data.d.ts new file mode 100644 index 00000000..f39a17fa --- /dev/null +++ b/siren/websocket-data.d.ts @@ -0,0 +1,3 @@ +type WebSocketData = { + subTopics: string | null +} \ No newline at end of file