diff --git a/.gitignore b/.gitignore index 61f2dc9..04ea32d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/__pycache__/ +save/ diff --git a/api/Dockerfile b/api/Dockerfile index 36d042c..123fa86 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -13,4 +13,4 @@ EXPOSE 5000 ENV FLASK_APP=api.py # run Gunicorn instead of the default Flask development server -CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--timeout", "60", "main:app"] +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--timeout", "60", "api:app"] diff --git a/api/main.py b/api/api.py similarity index 66% rename from api/main.py rename to api/api.py index d708ac1..8a7d770 100644 --- a/api/main.py +++ b/api/api.py @@ -9,21 +9,32 @@ import os import requests import time import asyncio +import yaml from common.Message import Message app = Flask(__name__) +# init config file +try: + with open('config/config.yml', 'r') as file: + config = yaml.safe_load(file) + radar_data = config['radar'] +except FileNotFoundError: + print("Error: Configuration file not found.") +except yaml.YAMLError as e: + print("Error reading YAML configuration:", e) + # store state data -servers = [ - {"name": "radar4", "url": "radar4.30hours.dev"}, - {"name": "radar5", "url": "radar5.30hours.dev"}, - {"name": "radar6", "url": "radar6.30hours.dev"} -] +servers = [] +for radar in radar_data: + if radar['name'] and radar['url']: + servers.append({'name': radar['name'], 'url': radar['url']}) + associators = [ {"name": "ADSB Associator", "id": "adsb-associator"} ] -# todo: ellipse conic int (analytic), SX, arc length + localisations = [ {"name": "Ellipse Parametric (Mean)", "id": "ellipse-parametric-mean"}, {"name": "Ellipse Parametric (Min)", "id": "ellipse-parametric-min"}, @@ -31,11 +42,19 @@ localisations = [ {"name": "Ellipsoid Parametric (Min)", "id": "ellipsoid-parametric-min"}, {"name": "Spherical Intersection", "id": "spherical-intersection"} ] + adsbs = [ {"name": "adsb.30hours.dev", "url": "adsb.30hours.dev"}, {"name": "None", "url": ""} ] +# store valid ids +valid = {} +valid['servers'] = [item['url'] for item in servers] +valid['associators'] = [item['id'] for item in associators] +valid['localisations'] = [item['id'] for item in localisations] +valid['adsbs'] = [item['url'] for item in adsbs] + # message received callback async def callback_message_received(msg): print(f"Callback: Received message in main.py: {msg}", flush=True) @@ -58,6 +77,20 @@ def serve_static(file): @app.route("/api") def api(): api = request.query_string.decode('utf-8') + # input protection + servers_api = request.args.getlist('server') + associators_api = request.args.getlist('associator') + localisations_api = request.args.getlist('localisation') + adsbs_api = request.args.getlist('adsb') + if not all(item in valid['servers'] for item in servers_api): + return 'Invalid server' + if not all(item in valid['associators'] for item in associators_api): + return 'Invalid associator' + if not all(item in valid['localisations'] for item in localisations_api): + return 'Invalid localisation' + if not all(item in valid['adsbs'] for item in adsbs_api): + return 'Invalid ADSB'] + # send to event handler try: reply_chunks = message_api_request.send_message(api) reply = ''.join(reply_chunks) diff --git a/api/map/main.js b/api/map/main.js index 05fd504..c471fd4 100644 --- a/api/map/main.js +++ b/api/map/main.js @@ -179,6 +179,27 @@ if (cesiumCredit) { return pointEntity; } +function is_localhost(ip) { + + if (ip === 'localhost') { + return true; + } + + ip = ip.replace(/^https?:\/\//, ""); + + const localRanges = ['127.0.0.1', '192.168.0.0/16', '10.0.0.0/8', '172.16.0.0/12']; + + const ipToInt = ip => ip.split('.').reduce((acc, octet) => (acc << 8) + +octet, 0) >>> 0; + + return localRanges.some(range => { + const [rangeStart, rangeSize = 32] = range.split('/'); + const start = ipToInt(rangeStart); + const end = (start | (1 << (32 - +rangeSize))) >>> 0; + return ipToInt(ip) >= start && ipToInt(ip) <= end; + }); + +} + // global vars var adsb_url; var adsbEntities = {}; @@ -193,13 +214,14 @@ window.addEventListener('load', function () { // add radar points const radar_names = new URLSearchParams( window.location.search).getAll('server'); - console.log(radar_names); var radar_config_url = radar_names.map( url => `http://${url}/api/config`); - if (this.window.location.protocol === "https:") { - radar_config_url = radar_config_url.map( + radar_config_url.forEach(function(url) { + if (!is_localhost(url)) { + radar_config_url = radar_config_url.map( url => url.replace(/^http:/, 'https:')); - } + } + }); var style_radar = {}; style_radar.color = 'rgba(0, 0, 0, 1.0)'; style_radar.pointSize = 10; @@ -241,19 +263,16 @@ window.addEventListener('load', function () { } }) .catch(error => { - // Handle errors during fetch console.error('Error during fetch:', error); }); }); - // get detection data URL - // get truth URL adsb_url = new URLSearchParams( window.location.search).get('adsb').split('&'); adsb_url = adsb_url.map( url => `http://${url}/data/aircraft.json`); - if (this.window.location.protocol === "https:") { + if (!is_localhost(adsb_url[0])) { adsb_url = adsb_url.map( url => url.replace(/^http:/, 'https:')); } diff --git a/api/requirements.txt b/api/requirements.txt index 353faa9..ed1e1a1 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,3 +1,4 @@ Flask==3.0.1 gunicorn==21.2.0 requests==2.31.0 +PyYAML==6.0.1 \ No newline at end of file diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000..d4a7958 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,26 @@ +radar: + - name: radar4 + url: radar4.30hours.dev + - name: radar5 + url: radar5.30hours.dev + - name: radar6 + url: radar6.30hours.dev + +associate: + adsb: + tar1090: 'adsb.30hours.dev' + tDelete: 5 + +localisation: + ellipse: + nSamples: 100 + threshold: 500 + nDisplay: 50 + ellipsoid: + nSamples: 60 + threshold: 500 + nDisplay: 50 + +3lips: + save: true + tDelete: 60 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 54d295a..9975db8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: networks: - 3lips volumes: + - ./config:/app/config - ./common:/app/common container_name: 3lips-api @@ -28,6 +29,7 @@ services: networks: - 3lips volumes: + - ./config:/app/config - ./common:/app/common - ./test:/app/test - ./save:/app/save @@ -41,4 +43,4 @@ services: image: cesium-apache networks: - 3lips - container_name: 3lips-cesium \ No newline at end of file + container_name: 3lips-cesium diff --git a/event/algorithm/localisation/EllipseParametric.py b/event/algorithm/localisation/EllipseParametric.py index 0ab2acb..a2a7d73 100644 --- a/event/algorithm/localisation/EllipseParametric.py +++ b/event/algorithm/localisation/EllipseParametric.py @@ -49,15 +49,11 @@ class EllipseParametric: for target in assoc_detections: - print(target, flush=True) target_samples = {} target_samples[target] = {} for radar in assoc_detections[target]: - print(radar["radar"], flush=True) - print(radar["delay"], flush=True) - # create ellipsoid for radar ellipsoid = next(( item for item in self.ellipsoids diff --git a/event/algorithm/localisation/EllipsoidParametric.py b/event/algorithm/localisation/EllipsoidParametric.py index 588edd6..b52f143 100644 --- a/event/algorithm/localisation/EllipsoidParametric.py +++ b/event/algorithm/localisation/EllipsoidParametric.py @@ -46,15 +46,11 @@ class EllipsoidParametric: for target in assoc_detections: - print(target, flush=True) target_samples = {} target_samples[target] = {} for radar in assoc_detections[target]: - print(radar["radar"], flush=True) - print(radar["delay"], flush=True) - # create ellipsoid for radar ellipsoid = next(( item for item in self.ellipsoids @@ -142,9 +138,6 @@ class EllipsoidParametric: output[target] = {} output[target]["points"] = [] - print('test', flush=True) - print(samples_intersect, flush=True) - for i in range(len(samples_intersect)): samples_intersect[i] = Geometry.ecef2lla( samples_intersect[i][0], diff --git a/event/event.py b/event/event.py index da6f067..c01bd06 100644 --- a/event/event.py +++ b/event/event.py @@ -13,6 +13,7 @@ import copy import json import hashlib import os +import yaml from algorithm.associator.AdsbAssociator import AdsbAssociator from algorithm.localisation.EllipseParametric import EllipseParametric @@ -23,19 +24,36 @@ from common.Message import Message from data.Ellipsoid import Ellipsoid from algorithm.geometry.Geometry import Geometry +# init config file +try: + with open('config/config.yml', 'r') as file: + config = yaml.safe_load(file) + nSamplesEllipse = config['localisation']['ellipse']['nSamples'] + thresholdEllipse = config['localisation']['ellipse']['threshold'] + nDisplayEllipse = config['localisation']['ellipse']['nDisplay'] + nSamplesEllipsoid = config['localisation']['ellipsoid']['nSamples'] + thresholdEllipsoid = config['localisation']['ellipsoid']['threshold'] + nDisplayEllipsoid = config['localisation']['ellipsoid']['nDisplay'] + tDeleteAdsb = config['associate']['adsb']['tDelete'] + save = config['3lips']['save'] + tDelete = config['3lips']['tDelete'] +except FileNotFoundError: + print("Error: Configuration file not found.") +except yaml.YAMLError as e: + print("Error reading YAML configuration:", e) + # init event loop api = [] # init config -tDelete = 60 +tDelete = tDelete adsbAssociator = AdsbAssociator() -ellipseParametricMean = EllipseParametric("mean", 150, 500) -ellipseParametricMin = EllipseParametric("min", 150, 500) -ellipsoidParametricMean = EllipsoidParametric("mean", 60, 800) -ellipsoidParametricMin = EllipsoidParametric("min", 60, 800) +ellipseParametricMean = EllipseParametric("mean", nSamplesEllipse, thresholdEllipse) +ellipseParametricMin = EllipseParametric("min", nSamplesEllipse, thresholdEllipse) +ellipsoidParametricMean = EllipsoidParametric("mean", nSamplesEllipsoid, thresholdEllipsoid) +ellipsoidParametricMin = EllipsoidParametric("min", nSamplesEllipsoid, thresholdEllipsoid) sphericalIntersection = SphericalIntersection() -adsbTruth = AdsbTruth(5) -save = True +adsbTruth = AdsbTruth(tDeleteAdsb) saveFile = '/app/save/' + str(int(time.time())) + '.ndjson' async def event(): @@ -139,6 +157,9 @@ async def event(): for key, value in associated_dets.items() if isinstance(value, list) and len(value) >= 3 } + if associated_dets_3_radars: + print('Detections from 3 or more radars availble.') + print(associated_dets_3_radars) associated_dets_2_radars = { key: value for key, value in associated_dets.items() @@ -176,7 +197,7 @@ async def event(): [x_rx, y_rx, z_rx], radar["radar"] ) - points = localisation.sample(ellipsoid, radar["delay"]*1000, 50) + points = localisation.sample(ellipsoid, radar["delay"]*1000, nDisplayEllipse) for i in range(len(points)): lat, lon, alt = Geometry.ecef2lla(points[i][0], points[i][1], points[i][2]) if item["localisation"] == "ellipsoid-parametric-mean" or \ @@ -239,8 +260,6 @@ def short_hash(input_string, length=10): # message received callback async def callback_message_received(msg): - #print(f"Callback: Received message in event.py: {msg}", flush=True) - timestamp = int(time.time()*1000) # update timestamp if API entry exists diff --git a/event/requirements.txt b/event/requirements.txt index fcdfabb..70a1fc1 100644 --- a/event/requirements.txt +++ b/event/requirements.txt @@ -1,2 +1,3 @@ numpy==1.26.4 -requests==2.31.0 \ No newline at end of file +requests==2.31.0 +PyYAML==6.0.1 \ No newline at end of file