lots of changes
This commit is contained in:
Binary file not shown.
Binary file not shown.
27
database.py
Normal file
27
database.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from dqlalchemy.orm import sessionmaker, declaritive_base
|
||||||
|
|
||||||
|
DATABASE_URL = "sqlite:///./locations.db"
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
DATABASE_URL, connect_engine("check_same_thread": false}
|
||||||
|
)
|
||||||
|
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
"""Dependency for getting database session."""
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
def create_all_tables():
|
||||||
|
"""Creates all tables defined with Base in the database."""
|
||||||
|
# Note: import models before calling create_all_tables()
|
||||||
|
Base.metadata_create_all(bind=engine)
|
||||||
|
print("Database and tables created.")
|
||||||
|
|
||||||
58
db_models.py
Normal file
58
db_models.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String, Float, ForeignKey
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from .database import Base
|
||||||
|
|
||||||
|
|
||||||
|
class Location(Base):
|
||||||
|
"""
|
||||||
|
SQLAlchemy model for the 'locations' table.
|
||||||
|
"""
|
||||||
|
__tablename__ = "locations"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
name = Column(String, unique=False, index=True)
|
||||||
|
address = Column(String, unique=False, index=True)
|
||||||
|
latitude = Column(Float, unique=False, index=True)
|
||||||
|
longitude = Column(Float, unique=False, index=True)
|
||||||
|
is_favorite = Column(Boolean, unique=False, index=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Location(name='{self.name}', address='{self.address}', latitude='{self.latitude}', longitude='{self.longitude}', is_favorite='{self.is_favorite}')>"
|
||||||
|
|
||||||
|
class Route(Base):
|
||||||
|
"""
|
||||||
|
SQLAlchemy model for the 'routes' table.
|
||||||
|
"""
|
||||||
|
__tablename__ = "routes"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
name = Column(String, index=True)
|
||||||
|
|
||||||
|
# Start and Endpoints (One-to-Many relationship)
|
||||||
|
start_location_id = Column(Integer, ForeignKey('locations.id'))
|
||||||
|
end_location_id = Column(Integer, ForeignKey('locations.id'))
|
||||||
|
|
||||||
|
start_location = relationship("Location", foreign_keys=[start_id])
|
||||||
|
end_location = relationship("Location", foreign_keys=[end_id])
|
||||||
|
|
||||||
|
# Relationship to get waypoints ordered
|
||||||
|
waypoints = relationship("Waypoint", order_by="Waypoint.order", back_populates="route")
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Route(name='{self.name}', start='{self.start_location.name}', end='{self.end_location.name}')>"
|
||||||
|
|
||||||
|
# Association Table for Many-to-Many relationsjip (Routes <-> Waypoints)
|
||||||
|
class Waypoint(Base):
|
||||||
|
"""
|
||||||
|
SQLAlchemy model for the 'waypoints' table.
|
||||||
|
"""
|
||||||
|
__tablename__ = 'waypoints'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
route_id = Column(Integer, ForeignKey('routes.id'))
|
||||||
|
location_id = Column(Integer, ForeignKey('locations.id'))
|
||||||
|
order = Column(Integer, nullable=False)
|
||||||
|
|
||||||
|
route = relationship("Route", back_populates="waypoints")
|
||||||
|
location = relationship("Location")
|
||||||
88
icloud.py
Normal file
88
icloud.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pyicloud import PyiCloudService
|
||||||
|
from pyicloud.exceptions import PyiCloud2FARequiredException
|
||||||
|
|
||||||
|
class FindMyMonitor:
|
||||||
|
def __init__(self, username, password, queue: asyncio.Queue, token_file="icloud_token.txt"):
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.token_file = token_file
|
||||||
|
self.queue = queue
|
||||||
|
self.api = None
|
||||||
|
self.device = None
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
async def authenticate(self):
|
||||||
|
"""Authenticates with iCloud, handling 2FA and token storage."""
|
||||||
|
if os.path.exists(self.token_file):
|
||||||
|
print("Loading stored session...")
|
||||||
|
self.api = PyiCloudService(self.username, cookie_directory="./cookies")
|
||||||
|
else:
|
||||||
|
print("No stored session. Authenticating...")
|
||||||
|
self.api = PyiCloudService(self.username, self.password, cookie_directory="./cookies")
|
||||||
|
|
||||||
|
if self.api.requires_2fa:
|
||||||
|
print("Two-factor authentication required.")
|
||||||
|
code = input("Enter the code you received: ")
|
||||||
|
result = self.api.validate_2fa_code(code)
|
||||||
|
print(f"Code validation result: {result}")
|
||||||
|
if not result:
|
||||||
|
print("Failed to verify 2FA code")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Trust the session
|
||||||
|
self.api.trust_session()
|
||||||
|
|
||||||
|
print("Successfully authenticated.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def get_location(self):
|
||||||
|
"""Fetches the latest latitude and longitude."""
|
||||||
|
if not self.api:
|
||||||
|
await self.authenticate()
|
||||||
|
|
||||||
|
# Refresh API data
|
||||||
|
self.api.refresh_client()
|
||||||
|
|
||||||
|
# Find the device (modify name to match your iPhone name in iCloud)
|
||||||
|
if not self.device:
|
||||||
|
# Assuming you have devices, pick the first or match by name
|
||||||
|
self.device = self.api.devices[0]
|
||||||
|
print(f"Monitoring device: {self.device.name()}")
|
||||||
|
|
||||||
|
location = self.device.location()
|
||||||
|
if location:
|
||||||
|
return location['latitude'], location['longitude'], location['timeStamp']
|
||||||
|
return None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
async def run_monitor(self, interval=60):
|
||||||
|
"""Runs the monitor loop."""
|
||||||
|
if not await self.authenticate():
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.running:
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
lat, lon, ts = await self.get_location()
|
||||||
|
print(f"[{ts}] Location: {lat}, {lon}")
|
||||||
|
# Add your logic to update database/API here
|
||||||
|
await self.queue.put(lat, lng, ts)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
# Re-authenticate if session expired
|
||||||
|
await self.authenticate()
|
||||||
|
|
||||||
|
await asyncio.sleep(interval)
|
||||||
|
|
||||||
|
|
||||||
323
server.py
323
server.py
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from icloud import FindMyMonitor
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
@@ -9,11 +10,13 @@ import signal
|
|||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
import random
|
import random
|
||||||
|
from operator import truediv
|
||||||
|
|
||||||
from pydantic import BaseModel, RootModel
|
from pydantic import BaseModel, RootModel
|
||||||
import socketio
|
import socketio
|
||||||
from contextlib import asynccontextmanager, suppress
|
from contextlib import asynccontextmanager, suppress
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional, Dict
|
||||||
|
|
||||||
from pymobiledevice3.services.dvt.instruments.location_simulation_base import LocationSimulationBase
|
from pymobiledevice3.services.dvt.instruments.location_simulation_base import LocationSimulationBase
|
||||||
|
|
||||||
@@ -129,24 +132,37 @@ class SimulationRequestResponse(BaseModel):
|
|||||||
status: bool
|
status: bool
|
||||||
data: Optional[SimulationRequestResponseData]
|
data: Optional[SimulationRequestResponseData]
|
||||||
|
|
||||||
|
class SimulationQueueDict(BaseModel):
|
||||||
|
location_id: Dict[str, SimulationRequestResponseData]
|
||||||
|
|
||||||
|
class iCloudLocationData(BaseModel):
|
||||||
|
latitude: number
|
||||||
|
longitude: number
|
||||||
|
timestamp: string
|
||||||
|
|
||||||
|
|
||||||
class LocationSimulationState:
|
class LocationSimulationState:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.current_location: Optional[Dict[str, SimulationRequestResponseData]] = None
|
||||||
|
self.next_location: Optional[Dict[str, SimulationRequestResponseData]] = None
|
||||||
|
self.loc_id: Optional[str] = None
|
||||||
self.latitude: Optional[float] = None
|
self.latitude: Optional[float] = None
|
||||||
self.longitude: Optional[float] = None
|
self.longitude: Optional[float] = None
|
||||||
self.next_move: Optional[float] = None
|
self.next_move: Optional[float] = None
|
||||||
self.udid: Optional[str] = None
|
self.udid: Optional[str] = None
|
||||||
self.simulation_active: bool = False
|
self.simulation_active: bool = False
|
||||||
self.loc_id: Optional[str] = None
|
|
||||||
self.set_location_enabled: bool = True
|
self.set_location_enabled: bool = True
|
||||||
self.queue: asyncio.Queue = asyncio.Queue()
|
self.queue: asyncio.Queue = asyncio.Queue()
|
||||||
self.queue_list: list[SimulationRequestResponseData] = []
|
self.queue_order: list[str] = []
|
||||||
|
self.queue_data: Dict = {}
|
||||||
self.queue_status: Optional[asyncio.Event] = asyncio.Event()
|
self.queue_status: Optional[asyncio.Event] = asyncio.Event()
|
||||||
self.queue_state: str = "STOPPED"
|
self.queue_state: str = "STOPPED"
|
||||||
self.test_mode: bool = True
|
self.test_mode: bool = False
|
||||||
self.simulation_task: Optional[asyncio.Task] = None
|
self.simulation_task: Optional[asyncio.Task] = None
|
||||||
self.sio: socketio.AsyncServer = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
|
self.sio: socketio.AsyncServer = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
|
||||||
self.tunnel: Optional[RemoteServiceDiscoveryService] = None
|
self.tunnel: Optional[RemoteServiceDiscoveryService] = None
|
||||||
|
self.fmf_queue: asyncio.Queue = asyncio.Queue
|
||||||
|
self.fmf_location: Optional[iCloudLocationData] = None
|
||||||
|
|
||||||
|
|
||||||
class TunneldRunnerSio:
|
class TunneldRunnerSio:
|
||||||
@@ -325,10 +341,7 @@ class TunneldRunnerSio:
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info("Simulation worker: acquiring tunnel (udid=%s)", self.context.udid)
|
logger.info("Simulation worker: acquiring tunnel (udid=%s)", self.context.udid)
|
||||||
# tun = await asyncio.wait_for(
|
|
||||||
# get_tun(self.context.udid),
|
|
||||||
# timeout=TUNNEL_ACQUIRE_TIMEOUT_SECONDS,
|
|
||||||
# )
|
|
||||||
tun = await get_tun(self.context.udid)
|
tun = await get_tun(self.context.udid)
|
||||||
logger.info("Simulation worker: tunnel acquired, connecting DVT provider")
|
logger.info("Simulation worker: tunnel acquired, connecting DVT provider")
|
||||||
dvt_provider = DvtProvider(tun)
|
dvt_provider = DvtProvider(tun)
|
||||||
@@ -369,6 +382,19 @@ class TunneldRunnerSio:
|
|||||||
self.context.simulation_active = False
|
self.context.simulation_active = False
|
||||||
self.context.simulation_task = None
|
self.context.simulation_task = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def start_icloud_monitor()
|
||||||
|
"""Start Apple iCloud Find My Monitor to retreive actual reported device location"""
|
||||||
|
monitor = FindMyMonitor(apple_id, apple_pw, self.context.fmf_queue)
|
||||||
|
monitor_task = asyncio.create_task(monitor.run_monitor(interval=30))
|
||||||
|
while True:
|
||||||
|
updated_location = await self.context.fmf_queue.get()
|
||||||
|
if self.context.fmf_location !== updated_location:
|
||||||
|
self.context.fmf_location = update_location
|
||||||
|
self.context.sio.emit("fmf_update", updated_location, namespace="/",)
|
||||||
|
|
||||||
|
|
||||||
async def pause_simulation_queue():
|
async def pause_simulation_queue():
|
||||||
"""Pauses asyncio.Queue playback"""
|
"""Pauses asyncio.Queue playback"""
|
||||||
self.context.queue_state = "PAUSED"
|
self.context.queue_state = "PAUSED"
|
||||||
@@ -390,23 +416,95 @@ class TunneldRunnerSio:
|
|||||||
except asyncio.QueueEmpty:
|
except asyncio.QueueEmpty:
|
||||||
break
|
break
|
||||||
|
|
||||||
async def end_simulation_queue() -> str:
|
def add_item(item_id, payload):
|
||||||
|
self.context.queue_data[item_id] = payload
|
||||||
|
self.context.queue_order.append(item_id)
|
||||||
|
|
||||||
|
def remove_item(item_id):
|
||||||
|
if item_id in self.context.queue_order:
|
||||||
|
self.context.queue_order.remove(item_id)
|
||||||
|
|
||||||
|
def get_item(item_id):
|
||||||
|
return self.context.queue_data[item_id]
|
||||||
|
|
||||||
|
def update_item(item_id, **updates):
|
||||||
|
if item_id in self.context.queue_data:
|
||||||
|
self.context.queue_data[item_id].update(updates)
|
||||||
|
|
||||||
|
def get_item_index(item_id):
|
||||||
|
return self.context.queue_order.index(item_id)
|
||||||
|
|
||||||
|
def get_item_id_by_index(index):
|
||||||
|
return self.context.queue_order[index]
|
||||||
|
|
||||||
|
def get_items_in_order():
|
||||||
|
return [self.context.queue_data[i] for i in self.context.queue_order]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def end_simulation_queue() -> bool:
|
||||||
"""Ends asyncio.Queue playback and closes tunnel"""
|
"""Ends asyncio.Queue playback and closes tunnel"""
|
||||||
logger.info("End location simulation request from %s", sid)
|
logger.info("End location simulation request from %s", sid)
|
||||||
if self.context.simulation_task is not None and not self.context.simulation_task.done():
|
try:
|
||||||
q = self.context.queue
|
if self.context.test_mode:
|
||||||
if q.qsize() > 0:
|
q = self.context.queue
|
||||||
await empty_simulation_queue()
|
if q.qsize() > 0:
|
||||||
while q.empty() and q.qsize() == 0:
|
self.context.set_location_enabled = False
|
||||||
|
while not q.empty():
|
||||||
|
try:
|
||||||
|
item = q.get_nowait()
|
||||||
|
q.task_done()
|
||||||
|
logger.info("Discarding item from queue: %s", item)
|
||||||
|
except asyncio.QueueEmpty:
|
||||||
|
break
|
||||||
|
|
||||||
await q.join()
|
await q.join()
|
||||||
with suppress(asyncio.CancelledError):
|
# with suppress(asyncio.CancelledError):
|
||||||
await self.context.simulation_task
|
# await self.context.simulation_task
|
||||||
if self.context.tunnel is not None:
|
self.context.simulation_active = False
|
||||||
async with DvtProvider(self.context.tunnel) as dvt, LocationSimulation(dvt) as locate_simulation:
|
self.context.queue_state = "SHUTDOWN"
|
||||||
await locate_simulation.clear()
|
return True
|
||||||
self.context.simulation_active = False
|
if not self.context.test_mode:
|
||||||
self.context.queue_state = "SHUTDOWN"
|
if self.context.simulation_task is not None and not self.context.simulation_task.done():
|
||||||
return "ended"
|
q = self.context.queue
|
||||||
|
if q.qsize() > 0:
|
||||||
|
await empty_simulation_queue()
|
||||||
|
while q.empty() and q.qsize() == 0:
|
||||||
|
await q.join()
|
||||||
|
with suppress(asyncio.CancelledError):
|
||||||
|
await self.context.simulation_task
|
||||||
|
if self.context.tunnel is not None:
|
||||||
|
async with DvtProvider(self.context.tunnel) as dvt, LocationSimulation(dvt) as locate_simulation:
|
||||||
|
await locate_simulation.clear()
|
||||||
|
self.context.simulation_active = False
|
||||||
|
self.context.queue_state = "SHUTDOWN"
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error ending simulation queue: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_status():
|
||||||
|
data = {
|
||||||
|
"current_location": self.context.current_location,
|
||||||
|
"next_location": self.context.next_location,
|
||||||
|
"latitude": self.context.latitude,
|
||||||
|
"longitude": self.context.longitude,
|
||||||
|
"next_move": self.context.next_move,
|
||||||
|
"udid": self.context.udid,
|
||||||
|
"simulation_active": self.context.simulation_active,
|
||||||
|
"loc_id": self.context.loc_id,
|
||||||
|
"set_location_enable": self.context.set_location_enabled,
|
||||||
|
"queue_length": self.context.queue.qsize() if self.context.queue else 0,
|
||||||
|
"queue_state": self.context.queue_state,
|
||||||
|
"queue_order": self.context.queue_order,
|
||||||
|
"queue_data": self.context.queue_data,
|
||||||
|
"queue_status": self.context.queue_status.is_set() if self.context.queue_status else False,
|
||||||
|
"test_mode": self.context.test_mode,
|
||||||
|
"simulation_task": self.context.simulation_task.get_name() if self.context.simulation_task else None,
|
||||||
|
"tunnel": self.context.tunnel.service.address[0] if self.context.tunnel else None,
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
""" FastAPI HTTP Functions"""
|
""" FastAPI HTTP Functions"""
|
||||||
def generate_http_response(
|
def generate_http_response(
|
||||||
@@ -646,27 +744,22 @@ class TunneldRunnerSio:
|
|||||||
|
|
||||||
@self._app.get("/context-status")
|
@self._app.get("/context-status")
|
||||||
async def app_context_status() -> fastapi.Response:
|
async def app_context_status() -> fastapi.Response:
|
||||||
data = {
|
data = get_status()
|
||||||
"latitude": self.context.latitude,
|
|
||||||
"longitude": self.context.longitude,
|
|
||||||
"next_more": self.context.next_move,
|
|
||||||
"udid": self.context.udid,
|
|
||||||
"simulation_active": self.context.simulation_active,
|
|
||||||
"loc_id": self.context.loc_id,
|
|
||||||
"set_location_enable": self.context.set_location_enabled,
|
|
||||||
"queue_state": self.context.queue_state,
|
|
||||||
"queue_list": self.context.queue_list
|
|
||||||
}
|
|
||||||
return generate_http_response(data)
|
return generate_http_response(data)
|
||||||
|
|
||||||
""" Socket.IO Functions"""
|
""" Socket.IO Functions"""
|
||||||
|
|
||||||
|
async def sio_send_status(sid):
|
||||||
|
""" Send Current Status"""
|
||||||
|
await self.context.sio.emit("status", get_status(), namespace="/", to=sid)
|
||||||
|
|
||||||
"""Socket.IO Connection Events"""
|
"""Socket.IO Connection Events"""
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def connect(sid, environ):
|
async def connect(sid, environ):
|
||||||
"""Client connection event handler."""
|
"""Client connection event handler."""
|
||||||
logger.info("Client connected: %s", sid)
|
logger.info("Client connected: %s", sid)
|
||||||
return('%s connected' % sid)
|
await sio_send_status(sid)
|
||||||
|
return '%s connected' % sid
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def disconnect(sid):
|
async def disconnect(sid):
|
||||||
@@ -680,8 +773,9 @@ class TunneldRunnerSio:
|
|||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def message(sid, data):
|
async def message(sid, data):
|
||||||
logger.info("Received message from %s: %s", data, sid)
|
logger.info("Received message from %s: %s", sid, data)
|
||||||
await self.context.sio.emit("message", f"Received message from {sid}: {data}", namespace="/")
|
return True, "Message received"
|
||||||
|
# await self.context.sio.emit("message", f"Received message from {sid}: {data}", namespace="/")
|
||||||
|
|
||||||
""" Device Control"""
|
""" Device Control"""
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
@@ -708,83 +802,86 @@ class TunneldRunnerSio:
|
|||||||
return { "command": command, "status": "error", "message": f"Invalid command: {command}" }
|
return { "command": command, "status": "error", "message": f"Invalid command: {command}" }
|
||||||
|
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
async def simulate_control(sid, data):
|
async def simulation_control(sid, data):
|
||||||
""" Simulation Control """
|
""" Simulation Control """
|
||||||
command = data.get("command") if isinstance(data, dict) else getattr(data, "command", None)
|
command = data.get("command") if isinstance(data, dict) else getattr(data, "command", None)
|
||||||
logger.info("Simulation Control command: %s requested from %s", command, sid)
|
logger.info("Simulation Control command: %s requested from %s", command, sid)
|
||||||
match command:
|
try:
|
||||||
case "add":
|
match command:
|
||||||
""" Add a location to the simulation queue"""
|
case "add":
|
||||||
loc_id = str(uuid.uuid4())
|
""" Add a location to the simulation queue"""
|
||||||
latitude = data.get("latitude") if isinstance(data, dict) else getattr(data, "latitude", None)
|
loc_id = str(uuid.uuid4())
|
||||||
longitude = data.get("longitude") if isinstance(data, dict) else getattr(data, "longitude", None)
|
latitude = data.get("latitude") if isinstance(data, dict) else getattr(data, "latitude", None)
|
||||||
delay = data.get("delay", 0) if isinstance(data, dict) else getattr(data, "delay", 0)
|
longitude = data.get("longitude") if isinstance(data, dict) else getattr(data, "longitude", None)
|
||||||
delay = 0 if delay is None else delay
|
delay = data.get("delay", 0) if isinstance(data, dict) else getattr(data, "delay", 0)
|
||||||
if latitude is not None and longitude is not None:
|
delay = 0 if delay is None else delay
|
||||||
logger.info("Adding location %s (%s, %s) with %s delay to the queue", loc_id, latitude, longitude,
|
if latitude is not None and longitude is not None:
|
||||||
delay)
|
logger.info("Adding location %s (%s, %s) with %s delay to the queue", loc_id, latitude, longitude,
|
||||||
await self.context.queue.put((loc_id, latitude, longitude, delay))
|
delay)
|
||||||
if delay == 0:
|
accrued_delay = 0
|
||||||
start_time = datetime.now(timezone.utc).isoformat()
|
if self.context.queue_data:
|
||||||
else:
|
accrued_delay = sum(item.get('delay', 0) for item in self.context.queue_data.values())
|
||||||
now_time = datetime.now(timezone.utc)
|
now_time = datetime.now(timezone.utc)
|
||||||
new_time = now_time + timedelta(seconds=delay)
|
new_time = now_time + timedelta(seconds=accrued_delay) + timedelta(seconds=delay)
|
||||||
start_time = new_time.isoformat()
|
start_time = new_time.isoformat()
|
||||||
|
location_item = {
|
||||||
location_item = {
|
|
||||||
loc_id: {
|
|
||||||
"loc_id": loc_id,
|
"loc_id": loc_id,
|
||||||
"latitude": latitude,
|
"latitude": latitude,
|
||||||
"longitude": longitude,
|
"longitude": longitude,
|
||||||
"delay": delay,
|
"delay": delay,
|
||||||
"start": start_time
|
"start": start_time
|
||||||
}
|
}
|
||||||
}
|
ack = {
|
||||||
ack = {
|
"command": command,
|
||||||
"command": command,
|
"status": "added",
|
||||||
"status": "added",
|
"message": f"Location {loc_id} added to the queue",
|
||||||
"message": f"Location {loc_id} added to the queue",
|
"item": location_item
|
||||||
"item": location_item
|
}
|
||||||
}
|
await self.context.queue.put(loc_id)
|
||||||
self.context.queue_list.append(location_item)
|
add_item(loc_id, location_item)
|
||||||
logger.info("Location %s added to the queue", loc_id)
|
logger.info("Location %s added to the queue", loc_id)
|
||||||
return ack
|
return ack
|
||||||
else:
|
else:
|
||||||
logger.warning("Invalid location data received from %s: %s", sid, data)
|
logger.warning("Invalid location data received from %s: %s", sid, data)
|
||||||
return {"command": command, "status": "error", "message": "Invalid location data"}
|
return {"command": command, "status": "error", "message": "Invalid location data", "data": location_item}
|
||||||
case "clear":
|
case "clear":
|
||||||
""" Clear the simulation queue"""
|
""" Clear the simulation queue"""
|
||||||
await empty_simulation_queue()
|
await empty_simulation_queue()
|
||||||
return {"command": command, "status": "cleared", "message": "Simulation cleared"}
|
return {"command": command, "status": "cleared", "message": "Simulation cleared"}
|
||||||
case "pause":
|
case "pause":
|
||||||
""" Pause the simulation queue"""
|
""" Pause the simulation queue"""
|
||||||
await pause_simulation_queue()
|
await pause_simulation_queue()
|
||||||
return {"command": command, "status": "paused", "message": "Simulation paused"}
|
return {"command": command, "status": "paused", "message": "Simulation paused"}
|
||||||
case "resume":
|
case "resume":
|
||||||
""" Resume the simulation queue"""
|
""" Resume the simulation queue"""
|
||||||
await resume_simulation_queue()
|
await resume_simulation_queue()
|
||||||
return {"command": command, "status": "resumed", "message": "Simulation resumed"}
|
return {"command": command, "status": "resumed", "message": "Simulation resumed"}
|
||||||
case "end":
|
case "end":
|
||||||
""" End the simulation queue"""
|
""" End the simulation queue"""
|
||||||
logger.info("End location simulation request from %s", sid)
|
logger.info("End location simulation request from %s", sid)
|
||||||
end_task = asyncio.create_task(end_simulation_queue(), name="end-simulation-worker")
|
end_task = asyncio.create_task(end_simulation_queue(), name="end-simulation-worker")
|
||||||
result = await end_task
|
result = await end_task
|
||||||
return {"command": command, "status": result, "message": "Simulation ended"}
|
simstatus = not result
|
||||||
case "start":
|
return {"command": command, "status": simstatus, "message": "Simulation ended"}
|
||||||
""" Start the simulation queue"""
|
case "start":
|
||||||
logger.info("Start location simulation request from %s", sid)
|
""" Start the simulation queue"""
|
||||||
if self.context.simulation_task is None or self.context.simulation_task.done():
|
logger.info("Start location simulation request from %s", sid)
|
||||||
self.context.simulation_active = True
|
if self.context.simulation_task is None or self.context.simulation_task.done():
|
||||||
self.context.simulation_task = asyncio.create_task(
|
self.context.simulation_active = True
|
||||||
start_simulation_queue(),
|
self.context.queue_state = "RUNNING"
|
||||||
name="location-simulation-worker",
|
self.context.simulation_task = asyncio.create_task(
|
||||||
)
|
start_simulation_queue(),
|
||||||
return {"command": command, "status": "started", "message": "Simulation started"}
|
name="location-simulation-worker",
|
||||||
else:
|
)
|
||||||
return {"command": command, "status": "error", "message": "Simulation already running"}
|
return {"command": command, "status": self.context.queue_state, "message": "Simulation started"}
|
||||||
case _:
|
else:
|
||||||
logger.warning("Invalid command received from %s: %s", sid, command)
|
return {"command": command, "status": "error", "message": "Simulation already running"}
|
||||||
return {"status": "error", "message": "Invalid command"}
|
case _:
|
||||||
|
logger.warning("Invalid command received from %s: %s", sid, command)
|
||||||
|
return {"status": "error", "message": "Invalid command"}
|
||||||
|
finally:
|
||||||
|
await sio_send_status(sid)
|
||||||
|
|
||||||
|
|
||||||
""" Tunnel Control """
|
""" Tunnel Control """
|
||||||
@self.context.sio.event
|
@self.context.sio.event
|
||||||
@@ -798,7 +895,7 @@ class TunneldRunnerSio:
|
|||||||
try:
|
try:
|
||||||
self._tunneld_core.start()
|
self._tunneld_core.start()
|
||||||
logger.info("Tunneld started successfully")
|
logger.info("Tunneld started successfully")
|
||||||
return {"status": "started", "message": "Tunneld started successfully"}
|
return {"status": "running", "message": "Tunneld started successfully"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error starting tunneld: %s", e)
|
logger.error("Error starting tunneld: %s", e)
|
||||||
return {"command": command, "status": "error", "message": f"Error starting tunneld: {e}"}
|
return {"command": command, "status": "error", "message": f"Error starting tunneld: {e}"}
|
||||||
@@ -855,9 +952,15 @@ class LocationSimulationQueue(LocationSimulation):
|
|||||||
continue
|
continue
|
||||||
if self.context.queue_state == "SHUTDOWN":
|
if self.context.queue_state == "SHUTDOWN":
|
||||||
break
|
break
|
||||||
loc_id, latitude, longitude, delay = await self.context.queue.get()
|
loc_id = await self.context.queue.get()
|
||||||
if (loc_id, latitude, longitude, delay) == (None, None, None, None):
|
if loc_id == None:
|
||||||
break
|
break
|
||||||
|
location_item = self.context.queue_data.get(loc_id)
|
||||||
|
latitude = location_item.get("latitude")
|
||||||
|
longitude = location_item.get("longitude")
|
||||||
|
delay = location_item.get("delay")
|
||||||
|
delay = 0 if delay is None else delay
|
||||||
|
start_time = location_item.get("start_time")
|
||||||
if self.context.set_location_enabled:
|
if self.context.set_location_enabled:
|
||||||
if delay > 0 and not disable_sleep:
|
if delay > 0 and not disable_sleep:
|
||||||
if timing_randomness_range > 0:
|
if timing_randomness_range > 0:
|
||||||
@@ -941,9 +1044,14 @@ class LocationSimulationTestQueue(LocationSimulationBase):
|
|||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
if self.context.queue_state == "SHUTDOWN":
|
if self.context.queue_state == "SHUTDOWN":
|
||||||
break
|
break
|
||||||
loc_id, latitude, longitude, delay = await self.context.queue.get()
|
loc_id = await self.context.queue.get()
|
||||||
if (loc_id, latitude, longitude, delay) == (None, None, None, None):
|
if loc_id == None:
|
||||||
break
|
break
|
||||||
|
location_item = self.context.queue_data.get(loc_id)
|
||||||
|
latitude = location_item.get("latitude")
|
||||||
|
longitude = location_item.get("longitude")
|
||||||
|
delay = location_item.get("delay")
|
||||||
|
start_time = location_item.get("start_time")
|
||||||
if self.context.set_location_enabled:
|
if self.context.set_location_enabled:
|
||||||
if delay > 0 and not disable_sleep:
|
if delay > 0 and not disable_sleep:
|
||||||
if timing_randomness_range > 0:
|
if timing_randomness_range > 0:
|
||||||
@@ -969,6 +1077,7 @@ class LocationSimulationTestQueue(LocationSimulationBase):
|
|||||||
self.context.longitude = longitude
|
self.context.longitude = longitude
|
||||||
self.context.loc_id = loc_id
|
self.context.loc_id = loc_id
|
||||||
await self.context.sio.emit(
|
await self.context.sio.emit(
|
||||||
|
|
||||||
"simulation_status",
|
"simulation_status",
|
||||||
{
|
{
|
||||||
"status": self.context.simulation_active,
|
"status": self.context.simulation_active,
|
||||||
|
|||||||
1074
server_recover.py
Normal file
1074
server_recover.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user