Compare commits
15 Commits
project/ko
...
3b49638afa
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b49638afa | |||
| 366488a8df | |||
| 7355d64971 | |||
| fe4ec12428 | |||
| 805b2658d4 | |||
| 77d6968297 | |||
| 0c2392dc0b | |||
|
|
65f767d38e | ||
| 0695e3d092 | |||
|
|
d6e094e6c0 | ||
|
|
2167eb2960 | ||
|
|
38374a6723 | ||
| c08612f71a | |||
| bde51ca5e1 | |||
| a204bb1676 |
@@ -72,8 +72,8 @@ class Datasource:
|
|||||||
)
|
)
|
||||||
new_points = [
|
new_points = [
|
||||||
(
|
(
|
||||||
processed_agent_data.latitude,
|
|
||||||
processed_agent_data.longitude,
|
processed_agent_data.longitude,
|
||||||
|
processed_agent_data.latitude,
|
||||||
processed_agent_data.road_state,
|
processed_agent_data.road_state,
|
||||||
processed_agent_data.user_id
|
processed_agent_data.user_id
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Accelerometer:
|
class Accelerometer:
|
||||||
x: int
|
x: float
|
||||||
y: int
|
y: float
|
||||||
z: int
|
z: float
|
||||||
|
|||||||
@@ -14,25 +14,38 @@ line_layer_colors = [
|
|||||||
[1, 0, 1, 1],
|
[1, 0, 1, 1],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_lat_lon(point: dict[str, float] | list[float] | tuple[float, float]) -> tuple[float, float] | None:
|
||||||
|
if isinstance(point, dict):
|
||||||
|
lat = point.get("lat")
|
||||||
|
lon = point.get("lon")
|
||||||
|
else:
|
||||||
|
lat, lon = point
|
||||||
|
|
||||||
|
if lat is None or lon is None:
|
||||||
|
return None
|
||||||
|
return lat, lon
|
||||||
|
|
||||||
|
|
||||||
class MapViewApp(App):
|
class MapViewApp(App):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.mapview = None
|
self.mapview: MapView | None = None
|
||||||
self.datasource = Datasource(user_id=1)
|
self.datasource = Datasource(user_id=1)
|
||||||
self.line_layers = dict()
|
self.line_layers = dict()
|
||||||
self.car_markers = dict()
|
self.car_markers = dict()
|
||||||
|
|
||||||
# додати необхідні змінні
|
# додати необхідні змінні
|
||||||
self.bump_markers = []
|
self.bump_markers: list[MapMarker] = []
|
||||||
self.pothole_markers = []
|
self.pothole_markers: list[MapMarker] = []
|
||||||
|
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
"""
|
"""
|
||||||
Встановлює необхідні маркери, викликає функцію для оновлення мапи
|
Встановлює необхідні маркери, викликає функцію для оновлення мапи
|
||||||
"""
|
"""
|
||||||
self.update()
|
self.update()
|
||||||
Clock.schedule_interval(self.update, 5)
|
Clock.schedule_interval(self.update, 0.1)
|
||||||
|
|
||||||
def update(self, *args):
|
def update(self, *args):
|
||||||
"""
|
"""
|
||||||
@@ -87,15 +100,17 @@ class MapViewApp(App):
|
|||||||
self.car_markers[user_id].lat = lat
|
self.car_markers[user_id].lat = lat
|
||||||
self.car_markers[user_id].lon = lon
|
self.car_markers[user_id].lon = lon
|
||||||
|
|
||||||
|
if user_id == 1:
|
||||||
self.mapview.center_on(lat, lon)
|
self.mapview.center_on(lat, lon)
|
||||||
|
|
||||||
def set_pothole_marker(self, point):
|
def map_lat_lon_to_pothole(self, lat: float, lon: float) -> MapMarker | None:
|
||||||
if isinstance(point, dict):
|
click_tolerance = self.mapview.zoom * 10
|
||||||
lat = point.get("lat")
|
flt = filter(lambda marker: abs(lat - marker.lat) + abs(lon - marker.lon) < click_tolerance,
|
||||||
lon = point.get("lon")
|
self.pothole_markers)
|
||||||
else:
|
return next(flt)
|
||||||
lat, lon = point
|
|
||||||
|
|
||||||
|
def set_pothole_marker(self, point):
|
||||||
|
lat, lon = get_lat_lon(point)
|
||||||
if lat is None or lon is None:
|
if lat is None or lon is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -109,12 +124,7 @@ class MapViewApp(App):
|
|||||||
self.pothole_markers.append(marker)
|
self.pothole_markers.append(marker)
|
||||||
|
|
||||||
def set_bump_marker(self, point):
|
def set_bump_marker(self, point):
|
||||||
if isinstance(point, dict):
|
lat, lon = get_lat_lon(point)
|
||||||
lat = point.get("lat")
|
|
||||||
lon = point.get("lon")
|
|
||||||
else:
|
|
||||||
lat, lon = point
|
|
||||||
|
|
||||||
if lat is None or lon is None:
|
if lat is None or lon is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -127,6 +137,16 @@ class MapViewApp(App):
|
|||||||
self.mapview.add_marker(marker)
|
self.mapview.add_marker(marker)
|
||||||
self.bump_markers.append(marker)
|
self.bump_markers.append(marker)
|
||||||
|
|
||||||
|
def delete_pothole_marker(self, point):
|
||||||
|
lat, lon = get_lat_lon(point)
|
||||||
|
if lat is None or lon is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
pothole = self.map_lat_lon_to_pothole(lat, lon)
|
||||||
|
if pothole:
|
||||||
|
self.mapview.remove_marker(pothole)
|
||||||
|
self.pothole_markers.pop(self.pothole_markers.index(pothole))
|
||||||
|
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Accelerometer:
|
class Accelerometer:
|
||||||
x: int
|
x: float
|
||||||
y: int
|
y: float
|
||||||
z: int
|
z: float
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class FileDatasource:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
acc_divisor: float,
|
||||||
accelerometer_filename: str,
|
accelerometer_filename: str,
|
||||||
gps_filename: str,
|
gps_filename: str,
|
||||||
park_filename: str,
|
park_filename: str,
|
||||||
@@ -34,6 +35,8 @@ class FileDatasource:
|
|||||||
|
|
||||||
self._started = False
|
self._started = False
|
||||||
|
|
||||||
|
self.acc_divisor = acc_divisor
|
||||||
|
|
||||||
def startReading(self, *args, **kwargs):
|
def startReading(self, *args, **kwargs):
|
||||||
"""Must be called before read()"""
|
"""Must be called before read()"""
|
||||||
if self._started:
|
if self._started:
|
||||||
@@ -160,15 +163,14 @@ class FileDatasource:
|
|||||||
|
|
||||||
return row
|
return row
|
||||||
|
|
||||||
@staticmethod
|
def _parse_acc(self, row: List[str]) -> Accelerometer:
|
||||||
def _parse_acc(row: List[str]) -> Accelerometer:
|
|
||||||
if len(row) < 3:
|
if len(row) < 3:
|
||||||
raise ValueError(f"Accelerometer row must have 3 values (x,y,z). Got: {row}")
|
raise ValueError(f"Accelerometer row must have 3 values (x,y,z). Got: {row}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
x = int(row[0])
|
x = int(row[0]) / self.acc_divisor
|
||||||
y = int(row[1])
|
y = int(row[1]) / self.acc_divisor
|
||||||
z = int(row[2])
|
z = int(row[2]) / self.acc_divisor
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ValueError(f"Invalid accelerometer values (expected integers): {row}") from e
|
raise ValueError(f"Invalid accelerometer values (expected integers): {row}") from e
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def run():
|
|||||||
# Prepare mqtt client
|
# Prepare mqtt client
|
||||||
client = connect_mqtt(config.MQTT_BROKER_HOST, config.MQTT_BROKER_PORT)
|
client = connect_mqtt(config.MQTT_BROKER_HOST, config.MQTT_BROKER_PORT)
|
||||||
# Prepare datasource
|
# Prepare datasource
|
||||||
datasource = FileDatasource("data/accelerometer.csv", "data/gps.csv", "data/parking.csv")
|
datasource = FileDatasource(16384.0, "data/accelerometer.csv", "data/gps.csv", "data/parking.csv")
|
||||||
# Infinity publish data
|
# Infinity publish data
|
||||||
publish(client, config.MQTT_TOPIC, datasource)
|
publish(client, config.MQTT_TOPIC, datasource)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ from marshmallow import Schema, fields
|
|||||||
|
|
||||||
|
|
||||||
class AccelerometerSchema(Schema):
|
class AccelerometerSchema(Schema):
|
||||||
x = fields.Int()
|
x = fields.Float()
|
||||||
y = fields.Int()
|
y = fields.Float()
|
||||||
z = fields.Int()
|
z = fields.Float()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from app.entities.agent_data import AgentData
|
from app.entities.agent_data import AgentData
|
||||||
from app.entities.processed_agent_data import ProcessedAgentData
|
from app.entities.processed_agent_data import ProcessedAgentData
|
||||||
|
|
||||||
|
_last_detection_state = {}
|
||||||
|
|
||||||
def process_agent_data(
|
def process_agent_data(
|
||||||
agent_data: AgentData,
|
agent_data: AgentData,
|
||||||
@@ -12,8 +13,24 @@ def process_agent_data(
|
|||||||
Returns:
|
Returns:
|
||||||
processed_data_batch (ProcessedAgentData): Processed data containing the classified state of the road surface and agent data.
|
processed_data_batch (ProcessedAgentData): Processed data containing the classified state of the road surface and agent data.
|
||||||
"""
|
"""
|
||||||
# Implement it
|
user_id = agent_data.user_id
|
||||||
|
road_state = "normal"
|
||||||
|
|
||||||
|
last_detection_state = _last_detection_state.get(user_id, False)
|
||||||
|
|
||||||
|
if (agent_data.accelerometer.z < 0.6):
|
||||||
|
road_state = "pothole"
|
||||||
|
elif (agent_data.accelerometer.z > 1.2):
|
||||||
|
road_state = "bump"
|
||||||
|
|
||||||
|
detection_happened = road_state != "normal"
|
||||||
|
|
||||||
|
if not (not last_detection_state and detection_happened):
|
||||||
|
road_state = "normal"
|
||||||
|
|
||||||
|
_last_detection_state[user_id] = detection_happened
|
||||||
|
|
||||||
return ProcessedAgentData(
|
return ProcessedAgentData(
|
||||||
road_state="normal",
|
road_state=road_state,
|
||||||
agent_data=agent_data
|
agent_data=agent_data
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ processed_agent_data = Table(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# WebSocket subscriptions
|
# WebSocket subscriptions
|
||||||
subscriptions: Dict[int, Set[WebSocket]] = {}
|
subscriptions: Set[WebSocket] = set()
|
||||||
|
|
||||||
|
|
||||||
# FastAPI WebSocket endpoint
|
# FastAPI WebSocket endpoint
|
||||||
@@ -41,10 +41,7 @@ subscriptions: Dict[int, Set[WebSocket]] = {}
|
|||||||
async def websocket_endpoint(websocket: WebSocket, user_id: int):
|
async def websocket_endpoint(websocket: WebSocket, user_id: int):
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
|
|
||||||
if user_id not in subscriptions:
|
subscriptions.add(websocket)
|
||||||
subscriptions[user_id] = set()
|
|
||||||
|
|
||||||
subscriptions[user_id].add(websocket)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# send already available data
|
# send already available data
|
||||||
@@ -55,20 +52,20 @@ async def websocket_endpoint(websocket: WebSocket, user_id: int):
|
|||||||
for i in jsonable_data:
|
for i in jsonable_data:
|
||||||
i['timestamp'] = i['timestamp'].strftime("%Y-%m-%dT%H:%M:%SZ")
|
i['timestamp'] = i['timestamp'].strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
|
||||||
await websocket.send_json(json.dumps(jsonable_data))
|
for i in jsonable_data:
|
||||||
|
await websocket.send_json(json.dumps([i]))
|
||||||
|
|
||||||
# receive forever
|
# receive forever
|
||||||
while True:
|
while True:
|
||||||
await websocket.receive_text()
|
await websocket.receive_text()
|
||||||
except WebSocketDisconnect:
|
except WebSocketDisconnect:
|
||||||
subscriptions[user_id].remove(websocket)
|
subscriptions.remove(websocket)
|
||||||
|
|
||||||
|
|
||||||
# Function to send data to subscribed users
|
# Function to send data to subscribed users
|
||||||
async def send_data_to_subscribers(user_id: int, data):
|
async def send_data_to_subscribers(data):
|
||||||
if user_id in subscriptions:
|
for websocket in subscriptions:
|
||||||
for websocket in subscriptions[user_id]:
|
await websocket.send_json(json.dumps([data]))
|
||||||
await websocket.send_json(json.dumps(data))
|
|
||||||
|
|
||||||
|
|
||||||
# FastAPI CRUDL endpoints
|
# FastAPI CRUDL endpoints
|
||||||
@@ -100,7 +97,7 @@ async def create_processed_agent_data(data: List[ProcessedAgentData], user_id: i
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
for record in created_records:
|
for record in created_records:
|
||||||
await send_data_to_subscribers(user_id, jsonable_encoder(record))
|
await send_data_to_subscribers(jsonable_encoder(record))
|
||||||
return created_records
|
return created_records
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
|
|||||||
Reference in New Issue
Block a user