Compare commits

..

5 commits

Author SHA1 Message Date
Vasilisk9812
7f70ebdd96 telemetry packets limit increased 2026-06-17 00:22:48 +09:00
Vasilisk9812
80e0177be0 websocket fix 2026-05-27 15:07:42 +09:00
Vasilisk9812
0261b41a71 resolve merge conflicts 2026-05-25 20:57:44 +09:00
Vasilisk9812
5eb8a4a4e2 resolve merge conflicts 2026-05-25 20:56:26 +09:00
Vasilisk9812
1850608f96 init commit 2026-05-25 20:50:43 +09:00
7 changed files with 78 additions and 17 deletions

View file

@ -45,6 +45,7 @@ MEDIA_ROOT = os.getenv('MEDIA_ROOT', os.path.join(BASE_DIR, 'media')) # Куд
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'daphne',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -103,7 +104,7 @@ ASGI_APPLICATION = 'stratoflights.asgi.application'
# Database # Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases # https://docs.djangoproject.com/en/4.2/ref/settings/#databases
if PRODUCTION: if not PRODUCTION:
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
@ -167,7 +168,7 @@ AUTH_USER_MODEL = 'stratoflights_api.User'
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100, 'PAGE_SIZE': 1000,
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
@ -196,9 +197,9 @@ CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost:5173,
CHANNEL_LAYERS = { CHANNEL_LAYERS = {
"default": { "default": {
"BACKEND": "channels_redis.core.RedisChannelLayer", "BACKEND": "channels.layers.InMemoryChannelLayer",
"CONFIG": { # "CONFIG": {
"hosts": [("redis", 6379)], # "hosts": [("redis", 6379)],
}, # },
}, },
} }

View file

@ -4,7 +4,7 @@ from rest_framework.response import Response
class CustomLimitOffsetPagination(LimitOffsetPagination): class CustomLimitOffsetPagination(LimitOffsetPagination):
limit_query_param = 'limit' limit_query_param = 'limit'
offset_query_param = 'skip' offset_query_param = 'skip'
max_limit = 100 max_limit = 1000
default_limit = 10 default_limit = 10

View file

@ -0,0 +1,24 @@
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stratoflights_api', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='telemetrypacket',
name='user',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -39,7 +39,7 @@ class Satellite(models.Model):
class TelemetryPacket(models.Model): class TelemetryPacket(models.Model):
user = models.ForeignKey( user = models.ForeignKey(
get_user_model(), on_delete=models.CASCADE, default=0) get_user_model(), on_delete=models.SET_NULL, null=True, blank=True)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
satellite = models.ForeignKey( satellite = models.ForeignKey(
Satellite, on_delete=models.CASCADE, related_name="telemetry") Satellite, on_delete=models.CASCADE, related_name="telemetry")
@ -50,6 +50,10 @@ class TelemetryPacket(models.Model):
payload = models.JSONField(blank=True, default=dict) payload = models.JSONField(blank=True, default=dict)
raw_data = models.JSONField(blank=True, default=dict) raw_data = models.JSONField(blank=True, default=dict)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"packet {self.satellite} {self.lat} {self.lon} {self.alt} {self.timestamp}"
class Meta:
ordering = ["-timestamp"]
class PreditctionTemplate(models.Model): class PreditctionTemplate(models.Model):

View file

@ -124,6 +124,7 @@ class TelemetryPacketSerializer(serializers.ModelSerializer):
model = TelemetryPacket model = TelemetryPacket
fields = ['id', 'timestamp', 'lat', 'lon', 'alt', 'payload'] fields = ['id', 'timestamp', 'lat', 'lon', 'alt', 'payload']
read_only_fields = ['id'] read_only_fields = ['id']
extra_kwargs = {'timestamp': {'required': False}}
class SavedPointSerializer(serializers.ModelSerializer): class SavedPointSerializer(serializers.ModelSerializer):

View file

@ -6,7 +6,7 @@ from zoneinfo import ZoneInfo
from collections import OrderedDict from collections import OrderedDict
class TawhiriClient: class TawhiriClient:
BASE_URL = "https://fly.stratonautica.ru/api/v2/" BASE_URL = "http://127.0.0.1:8080/api/v1/prediction"
TIMEOUT = 15 TIMEOUT = 15
@staticmethod @staticmethod

View file

@ -156,16 +156,47 @@ class TelemetryListCreateView(generics.ListCreateAPIView):
if not serializer.is_valid(): if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
try:
satellite = Satellite.objects.get(id=pk)
except Satellite.DoesNotExist:
return Response({"detail": "Satellite not found"}, status=status.HTTP_404_NOT_FOUND)
validated_data = serializer.validated_data validated_data = serializer.validated_data
TelemetryPacket.objects.create(timestamp=time.time(), packet = TelemetryPacket.objects.create(
satellite=Satellite.objects.get(id=pk), satellite=satellite,
lat=validated_data["lat"], user=request.user if request.user.is_authenticated else None,
lon=validated_data["lon"], timestamp=validated_data.get('timestamp', int(time.time())),
alt=validated_data["alt"], lat=validated_data['lat'],
payload=validated_data['payload'], lon=validated_data['lon'],
alt=validated_data['alt'],
payload=validated_data.get('payload', {}),
) )
return Response(serializer.errors, status=status.HTTP_201_CREATED)
# Broadcast to WebSocket subscribers so the tracking page updates live
try:
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from .consumers import SatelliteTelemetryConsumer
channel_layer = get_channel_layer()
if channel_layer is not None:
async_to_sync(SatelliteTelemetryConsumer.broadcast_to_satellite_group)(
str(pk),
{
'id': str(packet.id),
'timestamp': packet.timestamp,
'lat': packet.lat,
'lon': packet.lon,
'alt': packet.alt,
'payload': packet.payload,
'raw_data': {},
},
channel_layer,
)
except Exception:
pass # WS broadcast is best-effort; don't fail the REST response
return Response({'id': str(packet.id)}, status=status.HTTP_201_CREATED)
class SessionView(APIView): class SessionView(APIView):