70 lines
3.0 KiB
TypeScript
70 lines
3.0 KiB
TypeScript
import { Controller, Post, Get, Body, Query, Param, UseGuards, HttpException, HttpStatus } from '@nestjs/common';
|
|
import { ApiTags, ApiOperation, ApiQuery, ApiParam, ApiSecurity } from '@nestjs/swagger';
|
|
import { TelemetryService } from './telemetry.service';
|
|
import { DriverTelemetryDto, DriverTelemetryBatchDto } from './dto/driver-telemetry.dto';
|
|
import { ApiKeyGuard } from '../common/guards/api-key.guard';
|
|
|
|
@ApiTags('telemetry')
|
|
@ApiSecurity('x-api-key')
|
|
@Controller('telemetry')
|
|
@UseGuards(ApiKeyGuard)
|
|
export class TelemetryController {
|
|
constructor(private readonly telemetryService: TelemetryService) {}
|
|
|
|
@Post()
|
|
@ApiOperation({
|
|
summary: 'Ingest real-time driver telemetry with elevation & distance 📡⛰️',
|
|
description: 'Receives GPS position, speed, heading, distance, and AMSL elevation from driver app.',
|
|
})
|
|
async ingest(@Body() data: DriverTelemetryDto) {
|
|
if (!data.driver_id) {
|
|
throw new HttpException('Missing driver_id', HttpStatus.BAD_REQUEST);
|
|
}
|
|
return this.telemetryService.ingest(data);
|
|
}
|
|
|
|
@Post('batch')
|
|
@ApiOperation({
|
|
summary: 'Queue navigation telemetry batch 📦',
|
|
description: 'Acknowledges a navigation batch immediately; the server persists it from Redis in the background.',
|
|
})
|
|
async ingestBatch(@Body() body: DriverTelemetryBatchDto) {
|
|
if (!body || !Array.isArray(body.points)) {
|
|
throw new HttpException('Invalid payload: expected { points: [...] }', HttpStatus.BAD_REQUEST);
|
|
}
|
|
if (body.points.length > 100) {
|
|
throw new HttpException('A telemetry batch may contain at most 100 points', HttpStatus.BAD_REQUEST);
|
|
}
|
|
return this.telemetryService.enqueueBatch(body.points);
|
|
}
|
|
|
|
@Get('nearby')
|
|
@ApiOperation({ summary: 'Query active drivers within spatial radius with elevation & bearing 🚗' })
|
|
@ApiQuery({ name: 'lat', required: true, type: Number, description: 'Center latitude' })
|
|
@ApiQuery({ name: 'lng', required: true, type: Number, description: 'Center longitude' })
|
|
@ApiQuery({ name: 'radius', required: false, type: Number, description: 'Radius in meters (default: 5000m)' })
|
|
async getNearby(
|
|
@Query('lat') lat: number,
|
|
@Query('lng') lng: number,
|
|
@Query('radius') radius?: number,
|
|
) {
|
|
const latNum = Number(lat);
|
|
const lngNum = Number(lng);
|
|
if (isNaN(latNum) || isNaN(lngNum)) {
|
|
throw new HttpException('lat and lng must be valid numbers', HttpStatus.BAD_REQUEST);
|
|
}
|
|
return this.telemetryService.getRecentDrivers(latNum, lngNum, radius ? Number(radius) : 5000);
|
|
}
|
|
|
|
@Get('driver/:driverId/profile')
|
|
@ApiOperation({ summary: 'Get 3D elevation profile, climb, and terrain grade for a driver 📈' })
|
|
@ApiParam({ name: 'driverId', required: true, description: 'Driver unique ID' })
|
|
@ApiQuery({ name: 'hours', required: false, description: 'Window in hours (default: 24)' })
|
|
async getDriverElevationProfile(
|
|
@Param('driverId') driverId: string,
|
|
@Query('hours') hours?: number,
|
|
) {
|
|
return this.telemetryService.getElevationProfile(driverId, hours ? Number(hours) : 24);
|
|
}
|
|
}
|