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
7 changes: 7 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
Expand Up @@ -2,30 +2,36 @@ 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;
Expand All @@ -39,6 +45,7 @@ button {
.websocket-text button:hover, .websocket-image button:hover {
background-color: #555;
}

.text_field {
max-height: 400px;
overflow-y: scroll;
Expand Down
12 changes: 6 additions & 6 deletions resources/webcontent/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
</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>
<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>
Expand Down
27 changes: 17 additions & 10 deletions resources/webcontent/js/websocket_image_component.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,56 @@
/**
* 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();

// append the image and button to the dom
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
console.log('toggle')
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) => {
console.log('data')
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) {
Expand All @@ -52,6 +60,5 @@ class WebsocketImageComponent extends HTMLElement {
}
}
}

// define the custom element
Willtura marked this conversation as resolved.
Show resolved Hide resolved
customElements.define('websocket-image', WebsocketImageComponent);
23 changes: 16 additions & 7 deletions resources/webcontent/js/websocket_text_component.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
/**
* 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();
Expand All @@ -23,13 +30,16 @@ class WebsocketTextComponent extends HTMLElement {
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() {
// send a toggle message to the server
console.log('toggle')
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')}`);
Expand All @@ -52,6 +62,5 @@ class WebsocketTextComponent extends HTMLElement {
}
}
}

// define the custom element
Willtura marked this conversation as resolved.
Show resolved Hide resolved
customElements.define('websocket-text', WebsocketTextComponent);
67 changes: 65 additions & 2 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,13 +1,76 @@
import logging

from config import config
from constants import Gear
from driving.can_controller import CANController
from driving.can_controller.can_bus import get_can_interface
from driving.speed_controller import SpeedController, SpeedControllerState
from lane_assist.helpers import td_stitched_image_generator
from lane_assist.lane_assist import LaneAssist
from lane_assist.line_following.path_follower import PathFollower
from object_recognition.handlers.pedestrian_handler import PedestrianHandler
from object_recognition.handlers.speed_limit_handler import SpeedLimitHandler
from object_recognition.handlers.traffic_light_handler import TrafficLightHandler
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."""
cam_left = VideoStream(config.camera_ids.left)
cam_center = VideoStream(config.camera_ids.center)
cam_right = VideoStream(config.camera_ids.right)

cam_left.start()
cam_center.start()
cam_right.start()

# Connect to CAN bus
bus = get_can_interface()
can_controller = CANController(bus)
speed_controller = SpeedController(can_controller)

# Initialize the speed controller
speed_controller.gear = Gear.DRIVE
speed_controller.state = SpeedControllerState.WAITING_TO_STOP
speed_controller.max_speed = 50

# Initialize the path follower
path_follower = PathFollower(1, 0.01, 0.05, look_ahead_distance=10)
path_follower.max_steering_range = 30.0

# # Initialize the object controller
controller = ObjectController(speed_controller)
controller.add_handler(PedestrianHandler(controller))
controller.add_handler(SpeedLimitHandler(controller))
controller.add_handler(TrafficLightHandler(controller))

# Initialize the lane assist
lane_assist = LaneAssist(
td_stitched_image_generator(cam_left, cam_center, cam_right),
path_follower,
speed_controller,
adjust_speed=lambda _: 1,
)

# Initialize the object detector
detector = ObjectDetector.from_model(config.object_detection.model_path, controller, 0)

# Start the system
can_controller.start()
speed_controller.start()
detector.start()
lane_assist.start()

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


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

smain()
21 changes: 19 additions & 2 deletions src/telemetry/webapp/data_stream/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,29 @@


def create_router(websocket_handler: WebsocketHandler) -> APIRouter:
"""Create a FastAPI router for the data stream."""
"""
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.
"""
Willtura marked this conversation as resolved.
Show resolved Hide resolved
router = APIRouter()

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

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

Returns:
None
Willtura marked this conversation as resolved.
Show resolved Hide resolved
"""
await websocket.accept()
client = websocket_handler.add_socket(name, websocket)
await client.rec_messages()
Expand Down
Loading
Loading