Skip to content

Commit

Permalink
⚗️ #1 sending data to the browser, failing
Browse files Browse the repository at this point in the history
  • Loading branch information
Balaji-Ganesh committed Jun 16, 2023
1 parent 99b66fa commit 468f7d4
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 60 deletions.
6 changes: 0 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
{
"[python]": {
"editor.defaultFormatter": "ms-python.autopep8"
},
"python.formatting.provider": "none"
}
139 changes: 120 additions & 19 deletions middleware/communication/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@
import asyncio
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
import cv2
from enum import Enum
import logging

# Get the helpers..
from . import esp32

class StatusManager(Enum):
ESTABLISHED = 1 # to indicate - connection is established between two parties
TERMINATED = 2 # to indicate - connection is terminated/not connected between two parties

class ESP32Manager:

class ESP32Manager():
"""Manages all the communication related to the ESP32.
"""
# status maintainers -- to use in other modules
conn_status = StatusManager.TERMINATED
# Get the helpers..
from .esp32 import camera_client, collision_dist_fetcher, _connection_establisher, _connection_terminater # make the

def __init__(self, esp32IP: str, cam_port: int = 81, data_port: int = 82):
# set it to the assigned IP address to ESP32 when connected to WiFi.
Expand Down Expand Up @@ -40,16 +48,18 @@ async def connections_handler(function: str):
if function == 'establish':
if self.cam_ws is None and self.data_ws is None:
task = asyncio.create_task(
esp32._connection_establisher(self))
self._connection_establisher())
success = await task
ESP32Manager.conn_status = StatusManager.ESTABLISHED
return {"message": "Connection to ESP32 established."} if success else {"message": "Failure in ESP32 connection establishment. Please check log."}
else:
return {"message": "Connections already established."}
elif function == 'terminate':
if self.cam_ws is not None and self.data_ws is not None:
task = asyncio.create_task(
esp32._connection_terminater(self))
self._connection_terminater())
success = await task
ESP32Manager.conn_status = StatusManager.TERMINATED
return {"message": "ES32 connections terminated successfully.."} if success else {"message": "Failure in termination of ESP32 connections. Please check log."}
else:
return {"message": "Connections already terminated."}
Expand All @@ -65,7 +75,7 @@ async def camera_feed_handler(function: str):
# when connection already established..
if self.cam_task is None:
self.cam_task = asyncio.create_task(
esp32._camera_client(self))
self.camera_client())
return {"message": "Camera task started."}
else:
return {"message": "Camera task is already running."}
Expand Down Expand Up @@ -94,7 +104,7 @@ async def collision_dist_handler(function: str):
# when connection already established..
if self.data_task is None:
self.data_task = asyncio.create_task(
esp32._collision_dist_fetcher(self))
self.collision_dist_fetcher())
return {"message": "Collision-data task started."}
else:
return {"message": "Collision-data task is already running."}
Expand All @@ -114,6 +124,7 @@ async def collision_dist_handler(function: str):


class ConnectionManager:
# FIXME: Later if could work even withoug this class, remove this.
def __init__(self):
self.active_connections: list[WebSocket] = []

Expand All @@ -124,37 +135,127 @@ async def connect(self, websocket: WebSocket):
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)

async def send_personal_message(self, message: str, websocket: WebSocket):
async def send_data(self, message: str, websocket: WebSocket):
await websocket.send_text(message)

async def send_response(self, message: dict, websocket: WebSocket):
await websocket.send_json(message)

async def send_bin(self, message: str, websocket: WebSocket):
await websocket.send_bytes(message)

async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)


manager = ConnectionManager()


class WebManager:
class WebManager():
"""Manages all the connections to the web-app.
"""
conn_status = StatusManager.TERMINATED

def __init__(self):
self.router = APIRouter()

self.collision_task = None

@self.router.get('/web')
def sayhello():
return 'Hello Web app'

@self.router.websocket("/web/text")
async def websocket_endpoint(websocket: WebSocket):
await manager.connect(websocket)
@self.router.websocket("/web/stream")
async def camera_stream(websocket: WebSocket):
from main import conn_mngr, esp32_mngr
await conn_mngr.connect(websocket) # connect to the client

try:
if esp32_mngr.conn_status == StatusManager.ESTABLISHED:
await conn_mngr.send_response({"success": "esp32 connection is active.\n\
Streaming begins..."}, websocket)
await esp32_mngr.camera_client(to_web=True, ws=websocket)
else:
await conn_mngr.send_response({"error": "esp32 connection is inactive. \n\
First try establishing."}, websocket)
# while True:
# # data = await websocket.receive_text()
# await conn_mngr.send_data("camera stream", websocket)
except WebSocketDisconnect:
conn_mngr.disconnect(websocket)
# await conn_mngr.broadcast("Camera web-client has disconnected")
logging.debug("Camera web-client has disconnected")



@self.router.websocket("/web/collision")
async def collision_stream(websocket: WebSocket):
from main import conn_mngr, esp32_mngr
await conn_mngr.connect(websocket)
print("Collision client connected")
try:
command = await websocket.receive_text()
print("received command: ", command)
if command == 'start':
asyncio.create_task(self.send_collision_data(websocket))
# counter, data=0, 0
# while counter <= 200:
# # await websocket.send_text(str(counter)) # sending some dummy data
# counter+=1
# data = int(await esp32_mngr.data_ws.recv())
# await websocket.send_text(str(data+counter))
# if esp32_mngr.conn_status == StatusManager.ESTABLISHED:
# await conn_mngr.send_response({"success": "esp32 connection is active.\n\
# Streaming begins..."}, websocket)
# print("[DEBUG] About to send to web")
# if self.collision_task is None:
# # self.collision_task = asyncio.create_task(esp32_mngr.collision_dist_fetcher(to_web=True, ws=websocket))
# # await self.collision_task
# counter=0
# while counter <= 200:
# websocket.send_text(str(counter))
# counter+=1
# else: logging.error(" collision fetching is already running")
# else:
# await conn_mngr.send_response({"error": "esp32 connection is inactive. \n\
# First try establishing."}, websocket)
elif command == 'stop':
if self.collision_task is not None:
self.collision_task.cancel() # try stopping the task
try:
await self.collision_task
except asyncio.CancelledError:
pass
self.collision_task = None
print("Collision task stopped as per command")
logging.debug({"message": "Collision-data task stopped."})
else:
logging.debug({"message": "No collision-data task is currently running."})
else:
return {'error': 'Invalid function invoked for collision feed.'}
except WebSocketDisconnect:
conn_mngr.disconnect(websocket)
# await conn_mngr.broadcast("Collision client disconnected")
print('Collision client disconnected')

@self.router.websocket("/web/navigations")
async def navigations(websocket: WebSocket):
from main import conn_mngr
await conn_mngr.connect(websocket)
try:

while True:
# data = await websocket.receive_text()
await manager.send_personal_message("You wrote: Hello Rama..!!", websocket)
# await manager.broadcast("Client #123 says: How are you Rama?")
await conn_mngr.send_data("navigations", websocket)
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast("Client #123 left the chat")
conn_mngr.disconnect(websocket)
# await conn_mngr.broadcast("Camera web-client has disconnected")
logging.debug("Camera web-client has disconnected")

async def send_collision_data(self, ws: WebSocket):
print("came to send collision data")
from main import esp32_mngr
i=0.1
while True:
data = int(await esp32_mngr.data_ws.recv())
print("----------------- data recvd: ", data, "----------------------------")
await ws.send_text(str(data))
i+=0.01
26 changes: 22 additions & 4 deletions middleware/communication/esp32.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import websockets
import numpy as np
import logging
import base64
from fastapi import WebSocket

# The below functions will become data members of ESP32Manager in __init__.py

Expand Down Expand Up @@ -35,13 +37,21 @@ async def _connection_terminater(self) -> bool:
return False


async def _camera_client(self) -> bool:
async def camera_client(self, to_web: bool = False, ws: WebSocket = None, ) -> bool:
try:
while True:
msg = await self.cam_ws.recv()
# even try with msg.data
npimg = np.array(bytearray(msg), dtype=np.uint8)
img = cv2.imdecode(npimg, -1)

# if needed to send to web-client.. encode it..
if to_web:
logging.debug("Sending camera feed to web client")
frame = cv2.imencode('.jpg', img)[1].tobytes()
frame = base64.encodebytes(frame).decode("utf-8")
ws.send_bytes(frame)

cv2.imshow("img", img)
if cv2.waitKey(1) == 27:
print('camera task stopping..')
Expand All @@ -53,12 +63,20 @@ async def _camera_client(self) -> bool:
return False


async def _collision_dist_fetcher(self):
async def collision_dist_fetcher(self, to_web: bool = False, ws: WebSocket = None, ) -> bool:
counter=0
try:
print("------------------ Collision data fetcher ------------------------------")
while True:
message = await self.data_ws.recv()
print(f"Received message: {message}")
dist = int(await self.data_ws.recv())
print(f"Received message: {dist}")

# if need send to web-client..
if to_web:
print("Sending collision feed to web client")
await ws.send_text(data=str(dist+counter))
print("Fetching...", str(dist+counter))
counter+=1
# FIXME: Find way to send some status (True, False) -- like using some condition at While loop
# Process the message or perform any other task logic

Expand Down
19 changes: 3 additions & 16 deletions middleware/communication/web_communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,16 @@
import base64


def handle_connect(self, ):
print("[DEBUG] web: Client connected successfully")
self.sock.send({'ack': "Connection established"})


def handle_disconnect(self, ):
print("[DEBUG] web: Client disconnected successfully")
self.sock.send({'ack': "Connection terminated."})


def handle_ack(self, data):
print("[DEBUG] web: client's ack: ", data)


def stream_cam(self):
from .esp32_communicator import get_cam_feed
# Stream from esp32 cam feed
from . import esp32_comm
print("------------------------------ object check ----------------------", hasattr(esp32_comm, 'cameraws'))
print("------------------------------ object check ----------------------",
hasattr(esp32_comm, 'cameraws'))
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(get_cam_feed())

# # Stream from webcam
# # -- test: First, could able to stream the webcam or not? w/ a separate process or thread.
# # With threading (after monkey patching), this way working.
Expand Down
9 changes: 6 additions & 3 deletions middleware/main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# This file is from the branch `experiments` as `fastapi_as_api_in_class.py`
from fastapi import FastAPI, APIRouter
from communication import ESP32Manager, WebManager
from communication import ESP32Manager, WebManager, ConnectionManager
import uvicorn
app = FastAPI()
esp32 = ESP32Manager(esp32IP='192.168.134.165')
esp32_mngr = ESP32Manager(esp32IP='192.168.182.165')
conn_mngr = ConnectionManager()
web = WebManager()

app.include_router(esp32.router)

app.include_router(esp32_mngr.router)
app.include_router(web.router)


# if __name__ == "__main__":
# uvicorn.run(app, host="127.0.0.2", port=8500)
2 changes: 1 addition & 1 deletion web/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ def autonomous_mode():


if __name__ == '__main__':
app.run()
app.run(host='127.0.0.2', port=5000, debug=True)
Loading

0 comments on commit 468f7d4

Please sign in to comment.