-
Notifications
You must be signed in to change notification settings - Fork 0
[TEL-313] Establish WebSocket connection between Grafana and backend #265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dastine0308
wants to merge
13
commits into
main
Choose a base branch
from
TEL-313-Setup-Websocket-connection-between-Grafana-and-Backend-Helios-Telemetry
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
4272a9f
feat: add example environment variables for server package
justin-phxm b1d702b
feat: establish WebSocket connection between Grafana and backend
dastine0308 161abff
fix: remove unused SQLite dependencies from package.json and move typ…
dastine0308 b0d7b56
feat: use environment variable for WebSocket path in GrafanaWebSocket
dastine0308 187eaa6
feat: add Grafana + Telemetry Visualizer setup documentation
dastine0308 09a9795
fix: comment out WebSocket broadcast for lap data in BackendControlle…
dastine0308 e46eb3a
fix: add error handling for WebSocket message broadcasting in Grafana…
dastine0308 dd2db57
refactor: change to an custom WS class that other services can levera…
dastine0308 ae4ebb5
fix: correct default export to NativeWebSocket in WebSocket.ts
dastine0308 2cd009f
fix: update dependencies in yarn.lock for app-root-path and dedent
dastine0308 55b763f
feat: add Grafana WebSocket path to example environment variables
dastine0308 8fecfb5
fix: yarn build error (#313)
dastine0308 ab2c8b9
3rd: move rimraf to dependencies for runtime and build
dastine0308 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # Grafana + Telemetry Visualizer Setup | ||
|
|
||
| This document explains how to run the Grafana visualizer together with the Helios telemetry backend and a test MQTT publisher. Follow these steps to run everything locally. | ||
|
|
||
| ## Overview | ||
|
|
||
| You will run three repositories locally (or in Docker): | ||
|
|
||
| - `Helios-Telemetry` — backend + Aedes MQTT server | ||
| - `Telemetry-Visualizer` — Grafana docker setup that hosts dashboards | ||
| - `Helios-Mqtt-Webserver-Test` — MQTT client container to publish test messages | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node 18+ | ||
| - Docker & `docker-compose` | ||
| - Ask your lead for required environment variable files for each repo. | ||
|
|
||
| ## 1) Helios-Telemetry | ||
|
|
||
| 1. Get the repo and set the environment variables provided by your lead in `packages/server/.env`. | ||
| 2. Start the server (runs the Aedes MQTT server and backend): | ||
|
|
||
| ```bash | ||
| yarn dev:server | ||
| ``` | ||
|
|
||
| Note: When running test MQTT publishers you may not want those fake packets written into DynamoDB. Locate the packet handler in the server code (the `handlePacketReceive` flow) and comment out the DB insert line when running locally: | ||
|
|
||
| ```typescript | ||
| // this.dynamoDB.insertPacketData(message); // comment out for local testing | ||
| ``` | ||
|
|
||
| ## 2) Telemetry-Visualizer | ||
|
|
||
| 1. Clone or open the repo: `https://github.com/UCSolarCarTeam/Telemetry-Visualizer` | ||
| 2. Start Grafana using docker-compose: | ||
|
|
||
| ```bash | ||
| cd Telemetry-Visualizer/grafana | ||
| docker-compose up -d | ||
| ``` | ||
|
|
||
| After the container is up, open Grafana at `http://localhost:3000`. | ||
|
|
||
| Default login: | ||
|
|
||
| - Username: `admin` | ||
| - Password: `admin` | ||
|
|
||
| ## 3) Helios-Mqtt-Webserver-Test (MQTT client) | ||
|
|
||
| This repo provides a test MQTT publisher to simulate telemetry. | ||
|
|
||
| 1. Clone or open the repo: `https://github.com/UCSolarCarTeam/Helios-Mqtt-Webserver-Test` | ||
| 2. Build and run the test publisher (this will publish test packets to the backend MQTT broker): | ||
|
|
||
| ```bash | ||
| cd Helios-Mqtt-Webserver-Test | ||
| docker-compose up --build | ||
| ``` | ||
|
|
||
| Do not commit `.env` files containing secrets to the repository. Use a secure secrets manager or share them privately with your teammates. | ||
|
|
||
| ## Accessing Grafana | ||
|
|
||
| Once the Grafana container is running, open: | ||
|
|
||
| ``` | ||
| http://localhost:3000 | ||
| ``` | ||
|
|
||
| Login as `admin` / `admin` and verify the dashboards and data source configuration. | ||
|
|
||
| ## WebSocket (Grafana) — quick setup | ||
|
|
||
| 1. Add the WebSocket path environment variable to the server env file used by `Helios-Telemetry` (i.e. `packages/server/.env`): | ||
|
|
||
| ```bash | ||
| GRAFANA_WS_PATH=/grafana-ws | ||
| ``` | ||
|
|
||
| 2. Restart the backend if it was already running so it picks up the new environment variable. | ||
|
|
||
| 3. In Grafana (open `http://localhost:3000` and login): | ||
| - Click **Explore** in the left-hand menu. | ||
| - For **Data source**, choose **WebSocket API** (the WebSocket data source in the Telemetry-Visualizer setup). | ||
| - In the **Field** input enter: `$.data` | ||
justin-phxm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| You should now see the telemetry payload returned by the backend (the `data` object) in the Explore panel. The backend uses the `GRAFANA_WS_PATH` value for the WebSocket endpoint path (default `/grafana-ws`). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| # Example environment variables for the server package | ||
| # Copy this file to .env.local (or .env) and fill in real values. | ||
| # DO NOT commit real secrets. This file contains only example placeholders. | ||
|
|
||
| ######################## | ||
| # Database (DynamoDB) # | ||
| ######################## | ||
| LAP_TABLE_NAME="your-dynamodb-lap-table-name" | ||
| PACKET_TABLE_NAME="your-dynamodb-packet-table-name" | ||
| DRIVER_TABLE_NAME="your-dynamodb-driver-table-name" | ||
| GPS_CALCULATED_LAP_DATA_TABLE="your-dynamodb-gps-lap-table-name" | ||
| GPS_TABLE_NAME="your-dynamodb-gps-table-name" | ||
|
|
||
| ######################## | ||
| # Loki / Logger # | ||
| ######################## | ||
| # When LOKI_URL is set the server will POST logs to Loki's push API. | ||
| LOKI_URL="" # e.g. "https://loki.example.com" or "http://loki:3100" | ||
|
|
||
| # Authentication options (choose one): | ||
| # 1) Provide a base64 encoded username:password | ||
| LOKI_BASIC_AUTH="" # e.g. "dXNlcjpwYXNzd29yZA==" | ||
| # OR | ||
| # 2) Provide username/password and the logger will encode them automatically | ||
| LOKI_USERNAME="" | ||
| LOKI_PASSWORD="" | ||
|
|
||
| # Optional static labels for Loki streams (comma-separated key=value) | ||
| # Example: "app=helios,env=development,team=ucsolarcar" | ||
| LOKI_LABELS="" | ||
|
|
||
| ######################## | ||
| # MQTT # | ||
| ######################## | ||
| MQTT_USERNAME="primaryuser" | ||
| MQTT_PASSWORD="changeme" | ||
|
|
||
| ######################## | ||
| # App secrets / other # | ||
| ######################## | ||
| LAP_POSITION_PASSWORD="changeme" | ||
| NEXT_PUBLIC_MAPSAPIKEY="pk.YOUR_PUBLIC_MAP_KEY_HERE" | ||
|
|
||
| ######################## | ||
| # Local logging / misc # | ||
| ######################## | ||
| LOG_DIR="logs" | ||
| LOG_FILE="app.log" | ||
| LOG_LEVEL="info" # e.g. "debug", "info", "warn", "error" | ||
| LOG_ERROR_STACK="false" # When "true", include error stacks in console output | ||
|
|
||
| ######################## | ||
| # Node / Environment # | ||
| ######################## | ||
| NODE_ENV="development" | ||
| ENV="development" | ||
|
|
||
| ######################## | ||
| # Grafana WebSocket # | ||
| ######################## | ||
| GRAFANA_WS_PATH=/grafana-ws |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import type { IncomingMessage, Server, ServerResponse } from "http"; | ||
| import { type WebSocket, WebSocketServer } from "ws"; | ||
|
|
||
dastine0308 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import { type BackendController } from "@/controllers/BackendController/BackendController"; | ||
|
|
||
| import { type WebSocketType } from "@/datasources/WebSocket/WebSocket.types"; | ||
|
|
||
| import { createLightweightApplicationLogger } from "@/utils/logger"; | ||
|
|
||
| import type { ILapData, ITelemetryData } from "@shared/helios-types"; | ||
|
|
||
| const logger = createLightweightApplicationLogger("WebSocket.ts"); | ||
|
|
||
| export class NativeWebSocket implements WebSocketType { | ||
| wss: WebSocketServer; | ||
| backendController: BackendController; | ||
| constructor( | ||
| wsPath: string, | ||
| httpsServer: Server<typeof IncomingMessage, typeof ServerResponse>, | ||
| backendController: BackendController, | ||
| ) { | ||
| this.backendController = backendController; | ||
| this.wss = new WebSocketServer({ | ||
| path: wsPath, | ||
| server: httpsServer, | ||
| }); | ||
|
|
||
| this.wss.on("connection", (ws: WebSocket, req: IncomingMessage) => { | ||
| logger.info( | ||
| `WebSocket client connected from ${req.socket.remoteAddress}`, | ||
| ); | ||
| this.initializeWebSocketListeners(ws); | ||
| ws.send( | ||
| JSON.stringify({ | ||
| message: "Connected to Helios Telemetry WebSocket", | ||
| type: "connection", | ||
| }), | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
| public broadcastPacket(packet: ITelemetryData): void { | ||
| if (this.wss.clients.size === 0) { | ||
| logger.debug("No WebSocket clients connected, skipping broadcast"); | ||
| return; | ||
| } | ||
| const message = { | ||
| data: packet, | ||
| type: "packet", | ||
| }; | ||
| this.wss.clients.forEach((ws) => { | ||
| try { | ||
| ws.send(JSON.stringify(message)); | ||
| } catch (error) { | ||
| logger.error("Error broadcasting telemetry packet to WS", error); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| public broadcastLapData(lapData: ILapData): void { | ||
| if (this.wss.clients.size === 0) { | ||
| logger.debug("No WebSocket clients connected, skipping broadcast"); | ||
| return; | ||
| } | ||
|
|
||
| const message = { | ||
| data: lapData, | ||
| type: "lapData", | ||
| }; | ||
| this.wss.clients.forEach((ws) => { | ||
| try { | ||
| ws.send(JSON.stringify(message)); | ||
| } catch (error) { | ||
| logger.error("Error broadcasting lap data to WS", error); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| public initializeWebSocketListeners(ws: WebSocket): void { | ||
| ws.on("close", () => { | ||
| logger.info("WebSocket client disconnected"); | ||
| }); | ||
|
|
||
| ws.on("error", (error: Error) => { | ||
| logger.error("WebSocket error:", error); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| export default NativeWebSocket; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import type { WebSocketServer } from "ws"; | ||
|
|
||
| import type { ILapData, ITelemetryData } from "@shared/helios-types"; | ||
|
|
||
| export interface WebSocketType { | ||
| broadcastLapData(lapData: ILapData): void; | ||
| broadcastPacket(packet: ITelemetryData): void; | ||
| wss: WebSocketServer; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.