diff --git a/MapView/config.py b/MapView/config.py new file mode 100644 index 0000000..84566ca --- /dev/null +++ b/MapView/config.py @@ -0,0 +1,4 @@ +import os + +STORE_HOST = os.environ.get("STORE_HOST") or "localhost" +STORE_PORT = os.environ.get("STORE_PORT") or 8000 diff --git a/MapView/datasource.py b/MapView/datasource.py new file mode 100644 index 0000000..22c4703 --- /dev/null +++ b/MapView/datasource.py @@ -0,0 +1,81 @@ +import asyncio +import json +from datetime import datetime +import websockets +from kivy import Logger +from pydantic import BaseModel, field_validator +from config import STORE_HOST, STORE_PORT + + +# Pydantic models +class ProcessedAgentData(BaseModel): + road_state: str + user_id: int + x: float + y: float + z: float + latitude: float + longitude: float + timestamp: datetime + + @classmethod + @field_validator("timestamp", mode="before") + def check_timestamp(cls, value): + if isinstance(value, datetime): + return value + try: + return datetime.fromisoformat(value) + except (TypeError, ValueError): + raise ValueError( + "Invalid timestamp format. Expected ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)." + ) + + +class Datasource: + def __init__(self, user_id: int): + self.index = 0 + self.user_id = user_id + self.connection_status = None + self._new_points = [] + asyncio.ensure_future(self.connect_to_server()) + + def get_new_points(self): + Logger.debug(self._new_points) + points = self._new_points + self._new_points = [] + return points + + async def connect_to_server(self): + uri = f"ws://{STORE_HOST}:{STORE_PORT}/ws/{self.user_id}" + while True: + Logger.debug("CONNECT TO SERVER") + async with websockets.connect(uri) as websocket: + self.connection_status = "Connected" + try: + while True: + data = await websocket.recv() + parsed_data = json.loads(data) + self.handle_received_data(parsed_data) + except websockets.ConnectionClosedOK: + self.connection_status = "Disconnected" + Logger.debug("SERVER DISCONNECT") + + def handle_received_data(self, data): + # Update your UI or perform actions with received data here + Logger.debug(f"Received data: {data}") + processed_agent_data_list = sorted( + [ + ProcessedAgentData(**processed_data_json) + for processed_data_json in json.loads(data) + ], + key=lambda v: v.timestamp, + ) + new_points = [ + ( + processed_agent_data.latitude, + processed_agent_data.longitude, + processed_agent_data.road_state, + ) + for processed_agent_data in processed_agent_data_list + ] + self._new_points.extend(new_points) diff --git a/MapView/main.py b/MapView/main.py index e1f4c57..c238059 100644 --- a/MapView/main.py +++ b/MapView/main.py @@ -1,3 +1,4 @@ +import asyncio from kivy.app import App from kivy_garden.mapview import MapMarker, MapView from kivy.clock import Clock @@ -53,4 +54,6 @@ class MapViewApp(App): if __name__ == "__main__": - MapViewApp().run() + loop = asyncio.get_event_loop() + loop.run_until_complete(MapViewApp().async_run(async_lib="asyncio")) + loop.close() diff --git a/MapView/requirements.txt b/MapView/requirements.txt new file mode 100644 index 0000000..5029cca --- /dev/null +++ b/MapView/requirements.txt @@ -0,0 +1,15 @@ +annotated-types==0.6.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +docutils==0.20.1 +idna==3.6 +Kivy==2.3.0 +Kivy-Garden==0.1.5 +kivy-garden.mapview==1.0.6 +pydantic==2.6.2 +pydantic_core==2.16.3 +Pygments==2.17.2 +requests==2.31.0 +typing_extensions==4.10.0 +urllib3==2.2.1 +websockets==12.0