Skip to content
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

feat: implements webapp for telemetry #59

Merged
merged 19 commits into from
Apr 4, 2024
56 changes: 56 additions & 0 deletions resources/webcontent/css/style.css
Willtura marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
body {
font-family: Arial, sans-serif;
margin: 2em 5em;
}

h1 {
color: #333;
font-size: 28px;
}

#cameraContainer {
gap: 1em;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}

.camera {
width: 45%;
height: auto;
}

img{
display:block;
max-width:500px;
}

button {
margin: 1em 0;
padding: 0.6em 1.2em;
font-size: 16px;
cursor: pointer;
}

.websocket-text button, .websocket-image button {
margin-top: 10px;
padding: 10px 20px;
font-size: 16px;
border: none;
background-color: #333;
color: #000000;
cursor: pointer;
}

.websocket-text button:hover, .websocket-image button:hover {
background-color: #555;
}

.text_field {
max-height: 400px;
overflow-y: scroll;
width: 100%;
border: 1px solid #333;
padding: 10px;
margin-top: 10px;
}
17 changes: 17 additions & 0 deletions resources/webcontent/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/style.css">
<title>Dashboard</title>
</head>
<body>
<h1>Dashboard</h1>
<div id="cameraContainer">
<websocket-image id="left"></websocket-image>
<websocket-image id="center"></websocket-image>
<websocket-image id="right"></websocket-image>
</div>
<websocket-text id="logs" append="true"></websocket-text>
<script src="js/websocket_text_component.js"></script>
<script src="js/websocket_image_component.js"></script>
</body>
</html>
64 changes: 64 additions & 0 deletions resources/webcontent/js/websocket_image_component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* WebsocketImageComponent is a custom HTML element that displays an image received from a websocket server.
* It also includes a button to toggle the websocket connection.
Willtura marked this conversation as resolved.
Show resolved Hide resolved
*/
class WebsocketImageComponent extends HTMLElement {
constructor() {
Willtura marked this conversation as resolved.
Show resolved Hide resolved
super();

this.render();
this.connectWS();
}
/**
Willtura marked this conversation as resolved.
Show resolved Hide resolved
* Render the component by creating an image tag and a button.
* The button is used to toggle the websocket connection.
*/
render() {
// create an image tag
const header = document.createElement('h4')
header.textContent = this.getAttribute('id');

Willtura marked this conversation as resolved.
Show resolved Hide resolved
this.img = document.createElement('img');
this.img.style.width = '100%';
this.img.style.height = 'auto';

const button = document.createElement('button');
button.textContent = 'Toggle';
button.onclick = () => this.toggleWS();

this.appendChild(header);
this.appendChild(button);
this.appendChild(this.img);
}
/**
Willtura marked this conversation as resolved.
Show resolved Hide resolved
* Send a 'toggle' message to the server to toggle the websocket connection.
*/
toggleWS() {
// send a toggle message to the server
this.ws.send('toggle');
}
/**
Willtura marked this conversation as resolved.
Show resolved Hide resolved
* Establish a new websocket connection.
* When a new message is received, update the image.
* The image is sent as a base64 encoded JPEG.
*/
connectWS() {
// create a new websocket connection
this.ws = new WebSocket(`ws://localhost:8000/ws/${this.getAttribute('id')}`);
this.ws.onerror = (event) => {
console.error('Websocket error:', event);
}
this.ws.binaryType = 'arraybuffer';
// when a new message is received, update the image. the image is send as base64 encoded jpeg
this.ws.onmessage = (event) => {
const img_src = "data:image/jpeg;base64," + event.data;
// check if it is the same as the current image
if (this.img.src === img_src) {
return console.log("same img")
}
this.img.src = img_src;
}
}
}
// define the custom element
Willtura marked this conversation as resolved.
Show resolved Hide resolved
customElements.define('websocket-image', WebsocketImageComponent);
66 changes: 66 additions & 0 deletions resources/webcontent/js/websocket_text_component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* WebsocketTextComponent is a custom HTML element that displays text received from a websocket server.
* It also includes a button to toggle the websocket connection.
Willtura marked this conversation as resolved.
Show resolved Hide resolved
*/
class WebsocketTextComponent extends HTMLElement {
constructor() {
Willtura marked this conversation as resolved.
Show resolved Hide resolved
super();

this.render();
this.connectWS();
}
/**
Willtura marked this conversation as resolved.
Show resolved Hide resolved
* Render the component by creating a text div and a button.
* The button is used to toggle the websocket connection.
*/
render() {
// create an image tag
const header = document.createElement('h4')
header.textContent = this.getAttribute('id');

this.text = document.createElement('div');
this.text.classList.add('text_field');

const button = document.createElement('button');
button.textContent = 'Toggle';
button.onclick = () => this.toggleWS();

// append the image and button to the dom
this.appendChild(header);
this.appendChild(button);
this.appendChild(this.text);
}
/**
Willtura marked this conversation as resolved.
Show resolved Hide resolved
* Send a 'toggle' message to the server to toggle the websocket connection.
*/
toggleWS() {
this.ws.send('toggle');
}
/**
Willtura marked this conversation as resolved.
Show resolved Hide resolved
* Establish a new websocket connection.
* When a new message is received, update the text.
*/
connectWS() {
// create a new websocket connection
this.ws = new WebSocket(`ws://localhost:8000/ws/${this.getAttribute('id')}`);
this.ws.onerror = (event) => {
console.error('Websocket error:', event);
}
// when a new message is received, update the image. the image is send as base64 encoded jpeg
this.ws.onmessage = (event) => {
// convert to text
console.log(event.data)
const text_elem = document.createElement('p');
text_elem.textContent = event.data;
if(this.getAttribute('append') === 'true'){
// prepend child
this.text.prepend(text_elem);
} else {
this.text.innerHTML = '';
this.text.appendChild(text_elem);
}
}
}
}
// define the custom element
Willtura marked this conversation as resolved.
Show resolved Hide resolved
customElements.define('websocket-text', WebsocketTextComponent);
7 changes: 6 additions & 1 deletion src/config/config.defaults.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
camera_ids:
left: 0
left: 1
center: 0
right: 0

Expand Down Expand Up @@ -49,3 +49,8 @@ pedestrian_detection:
crosswalk_overlap_margin: 0.2
crosswalk_safe_zone_margin: 0.1
crosswalk_min_distance: 2

telemetry:
server:
port: 8000
host: 0.0.0.0
7 changes: 4 additions & 3 deletions src/main.py
Willtura marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import logging

from config import config
from constants import Gear
Expand All @@ -14,11 +13,11 @@
from object_recognition.object_controller import ObjectController
from object_recognition.object_detector import ObjectDetector
from utils.video_stream import VideoStream
from telemetry.webapp.telemetry_server import TelemetryServer


def main() -> None:
"""Start the main loop."""
# Load cameras
cam_left = VideoStream(config.camera_ids.left)
cam_center = VideoStream(config.camera_ids.center)
cam_right = VideoStream(config.camera_ids.right)
Expand Down Expand Up @@ -64,11 +63,13 @@ def main() -> None:
detector.start()
lane_assist.start()

server = TelemetryServer()
server.start()
input("Press Enter to stop...")


if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
"""The main function."""
from simulator import main as smain

smain()
Empty file added src/telemetry/__init__.py
Empty file.
Empty file.
Empty file.
37 changes: 37 additions & 0 deletions src/telemetry/webapp/data_stream/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from fastapi import APIRouter, WebSocket
from telemetry.webapp.data_stream.websocket_handler import WebsocketHandler


def create_router(websocket_handler: WebsocketHandler) -> APIRouter:
"""Create a FastAPI router for the data stream.

Args:
----
websocket_handler (WebsocketHandler): The handler for the websocket connections.

Returns:
-------
APIRouter: The FastAPI router with the websocket endpoint.

"""
router = APIRouter()

@router.websocket("/ws/{name}")
async def websocket_endpoint(name: str, websocket: WebSocket) -> None:
"""Create a websocket endpoint.

Args:
----
name (str): The name of the websocket.
websocket (WebSocket): The websocket instance.

Returns:
-------
None

"""
await websocket.accept()
client = websocket_handler.add_socket(name, websocket)
await client.rec_messages()

return router
Loading
Loading