@mytec: iter1.5 ready for testing

This commit is contained in:
2026-01-31 01:34:51 +02:00
parent 5bd9302dd8
commit 3c92fdbb90
10 changed files with 564 additions and 109 deletions

View File

@@ -0,0 +1,133 @@
/**
* Backend API client for RFCP coverage calculation
*/
const API_BASE = import.meta.env.VITE_API_URL || 'https://api.rfcp.eliah.one';
// === Request types ===
export interface ApiSiteParams {
lat: number;
lon: number;
height: number;
power: number; // dBm
gain: number; // dBi
frequency: number; // MHz
azimuth?: number;
beamwidth?: number;
}
export interface ApiCoverageSettings {
radius: number; // meters
resolution: number; // meters
min_signal: number; // dBm
preset?: 'fast' | 'standard' | 'detailed' | 'full';
use_terrain?: boolean;
use_buildings?: boolean;
use_materials?: boolean;
use_dominant_path?: boolean;
use_street_canyon?: boolean;
use_reflections?: boolean;
}
export interface CoverageRequest {
sites: ApiSiteParams[];
settings: ApiCoverageSettings;
}
// === Response types ===
export interface ApiCoveragePoint {
lat: number;
lon: number;
rsrp: number;
distance: number;
has_los: boolean;
terrain_loss: number;
building_loss: number;
reflection_gain: number;
}
export interface ApiCoverageStats {
min_rsrp: number;
max_rsrp: number;
avg_rsrp: number;
los_percentage: number;
points_with_buildings: number;
points_with_terrain_loss: number;
points_with_reflection_gain: number;
}
export interface CoverageResponse {
points: ApiCoveragePoint[];
count: number;
settings: ApiCoverageSettings;
stats: ApiCoverageStats;
computation_time: number;
models_used: string[];
}
export interface Preset {
description: string;
use_terrain: boolean;
use_buildings: boolean;
use_materials: boolean;
use_dominant_path: boolean;
use_street_canyon: boolean;
use_reflections: boolean;
estimated_speed: string;
}
// === API Client ===
class ApiService {
private abortController: AbortController | null = null;
async getPresets(): Promise<Record<string, Preset>> {
const response = await fetch(`${API_BASE}/api/coverage/presets`);
if (!response.ok) throw new Error('Failed to fetch presets');
const data = await response.json();
return data.presets;
}
async calculateCoverage(request: CoverageRequest): Promise<CoverageResponse> {
// Cancel previous request if running
if (this.abortController) {
this.abortController.abort();
}
this.abortController = new AbortController();
const response = await fetch(`${API_BASE}/api/coverage/calculate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
signal: this.abortController.signal,
});
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: 'Coverage calculation failed' }));
throw new Error(error.detail || 'Coverage calculation failed');
}
this.abortController = null;
return response.json();
}
cancelCalculation() {
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
}
async getElevation(lat: number, lon: number): Promise<number> {
const response = await fetch(
`${API_BASE}/api/terrain/elevation?lat=${lat}&lon=${lon}`
);
if (!response.ok) return 0;
const data = await response.json();
return data.elevation;
}
}
export const api = new ApiService();