kopia lustrzana https://github.com/dl2alf/AirScout
563 wiersze
32 KiB
C#
563 wiersze
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.ComponentModel;
|
|
using System.Windows;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.IO;
|
|
using AirScout.Core;
|
|
using AirScout.Aircrafts;
|
|
using AirScout.AircraftPositions;
|
|
using AirScout.PlaneFeeds.Plugin;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using ScoutBase.Core;
|
|
using System.Diagnostics;
|
|
using AirScout.PlaneFeeds.Plugin.MEFContract;
|
|
|
|
namespace AirScout.PlaneFeeds
|
|
{
|
|
|
|
// enum for ReportProgress messages
|
|
public enum PROGRESS
|
|
{
|
|
ERROR = -1,
|
|
STATUS = 0,
|
|
PLANES = 1,
|
|
FINISHED = 100
|
|
}
|
|
|
|
// enum for PlaneFeed status
|
|
public enum STATUS
|
|
{
|
|
ERROR = -1,
|
|
OK = 0
|
|
}
|
|
|
|
[System.ComponentModel.DesignerCategory("")]
|
|
[DefaultPropertyAttribute("Name")]
|
|
public class PlaneFeed : BackgroundWorker
|
|
{
|
|
private PlaneInfoCache PlanePositions = new PlaneInfoCache();
|
|
private PlaneFeedWorkEventArgs Arguments;
|
|
|
|
public PlaneFeed()
|
|
: base ()
|
|
{
|
|
WorkerSupportsCancellation = true;
|
|
WorkerReportsProgress = true;
|
|
}
|
|
|
|
public STATUS Status;
|
|
|
|
protected LogWriter Log = LogWriter.Instance;
|
|
|
|
protected override void OnDoWork(DoWorkEventArgs e)
|
|
{
|
|
Log.WriteMessage("Started.");
|
|
Arguments = (PlaneFeedWorkEventArgs)e.Argument;
|
|
if (String.IsNullOrEmpty(Thread.CurrentThread.Name))
|
|
Thread.CurrentThread.Name = this.GetType().Name;
|
|
|
|
// use PlaneInfoConverter for plausibility check
|
|
PlaneInfoConverter C = new PlaneInfoConverter();
|
|
|
|
// check boundaries
|
|
if ((Arguments.MaxLat <= Arguments.MinLat) || (Arguments.MaxLon <= Arguments.MinLon))
|
|
{
|
|
Status = STATUS.ERROR;
|
|
this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!");
|
|
Log.WriteMessage("Area boundaries mismatch. Check your Covered Area parameters!", LogLevel.Error);
|
|
}
|
|
else
|
|
{
|
|
if (Arguments.Feed == null)
|
|
{
|
|
Status = STATUS.ERROR;
|
|
this.ReportProgress((int)PROGRESS.ERROR, "Plane feed plugin not found. Check your settings!");
|
|
Log.WriteMessage("Plane feed plugin not found. Check your settings!", LogLevel.Error);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
try
|
|
{
|
|
Status = STATUS.OK;
|
|
int interval = Arguments.Interval;
|
|
// build arguments
|
|
PlaneFeedPluginArgs feedargs = new PlaneFeedPluginArgs();
|
|
feedargs.AppDirectory = Arguments.AppDirectory;
|
|
feedargs.AppDataDirectory = Arguments.AppDataDirectory;
|
|
feedargs.LogDirectory = Arguments.LogDirectory;
|
|
feedargs.TmpDirectory = Arguments.TmpDirectory;
|
|
feedargs.DatabaseDirectory = Arguments.DatabaseDirectory;
|
|
feedargs.PlanePositionsDirectory = Arguments.PlanePositionsDirectory;
|
|
feedargs.MaxLat = Arguments.MaxLat;
|
|
feedargs.MinLon = Arguments.MinLon;
|
|
feedargs.MinLat = Arguments.MinLat;
|
|
feedargs.MaxLon = Arguments.MaxLon;
|
|
feedargs.MyLat = Arguments.MyLat;
|
|
feedargs.MyLon = Arguments.MyLon;
|
|
feedargs.DXLat = Arguments.DXLat;
|
|
feedargs.DXLon = Arguments.DXLon;
|
|
feedargs.MinAlt = Arguments.MinAlt;
|
|
feedargs.MaxAlt = Arguments.MaxAlt;
|
|
feedargs.KeepHistory = Arguments.KeepHistory;
|
|
feedargs.InstanceID = Arguments.InstanceID;
|
|
feedargs.SessionKey = Arguments.SessionKey;
|
|
feedargs.GetKeyURL = Arguments.GetKeyURL;
|
|
feedargs.LogPlanePositions = Arguments.LogPlanePositions;
|
|
|
|
// do start procedure
|
|
Arguments.Feed.Start(feedargs);
|
|
// run inner loop
|
|
do
|
|
{
|
|
// call plugin's interface to get the planes
|
|
try
|
|
{
|
|
Stopwatch st = new Stopwatch();
|
|
st.Start();
|
|
// get plane raw data and do addtional checks
|
|
PlaneFeedPluginPlaneInfoList acs = Arguments.Feed.GetPlanes(feedargs);
|
|
PlaneInfoList planes = new PlaneInfoList();
|
|
PlaneInfoList invalids = new PlaneInfoList();
|
|
int total = acs.Count;
|
|
int count = 0;
|
|
int errors = 0;
|
|
double track = 0;
|
|
double dist = 0;
|
|
|
|
foreach (PlaneFeedPluginPlaneInfo ac in acs)
|
|
{
|
|
// skip without error when on ground
|
|
if (ac.Ground)
|
|
continue;
|
|
// copy raw data to new PlaneInfo object
|
|
PlaneInfo plane = new PlaneInfo();
|
|
plane.Hex = ac.Hex;
|
|
plane.Lat = ac.Lat;
|
|
plane.Lon = ac.Lon;
|
|
plane.Alt = ac.Alt;
|
|
plane.Call = ac.Call;
|
|
plane.Reg = ac.Reg;
|
|
plane.Track = ac.Track;
|
|
plane.Speed = ac.Speed;
|
|
plane.Time = ac.Time;
|
|
plane.From = ac.From;
|
|
plane.To = ac.To;
|
|
plane.VSpeed = ac.VSpeed;
|
|
try
|
|
{
|
|
plane.Category = (PLANECATEGORY)ac.Category;
|
|
}
|
|
catch
|
|
{
|
|
plane.Category = PLANECATEGORY.NONE;
|
|
}
|
|
plane.Type = ac.Type;
|
|
plane.Model = ac.Model;
|
|
plane.Manufacturer = ac.Manufacturer;
|
|
// start checks
|
|
// assuming that at least a timestamp is set!
|
|
// do basic check on hex --> is strictly needed as identifier
|
|
if (!PlaneInfoChecker.Check_Hex(plane.Hex))
|
|
{
|
|
// try to fill hex from reg
|
|
if (!PlaneInfoChecker.Check_Reg(plane.Reg))
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Hex]: " + plane.Hex, LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
AircraftDesignator ad = AircraftData.Database.AircraftFindByReg(plane.Reg);
|
|
if (ad == null)
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Reg]: " + plane.Reg, LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
plane.Hex = ad.Hex;
|
|
}
|
|
// check latitude
|
|
if (!PlaneInfoChecker.Check_Lat(plane.Lat))
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Lat]: " + plane.Lat.ToString("F8", CultureInfo.InvariantCulture), LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
// skip without error when latitude is out of scope
|
|
if ((plane.Lat < Arguments.MinLat) || (plane.Lat > Arguments.MaxLat))
|
|
continue;
|
|
// check longitude
|
|
if (!PlaneInfoChecker.Check_Lon(plane.Lon))
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Lon]: " + plane.Lon.ToString("F8", CultureInfo.InvariantCulture), LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
// skip without error when longitude is out of scope
|
|
if ((plane.Lon < Arguments.MinLon) || (plane.Lon > Arguments.MaxLon))
|
|
continue;
|
|
// check altitude
|
|
if (!PlaneInfoChecker.Check_Alt(plane.Alt))
|
|
{
|
|
// try to recover altitude from previuos messages
|
|
PlaneInfo info = null;
|
|
if (PlanePositions.TryGetValue(plane.Hex, out info))
|
|
{
|
|
plane.Alt = info.Alt;
|
|
}
|
|
else
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Alt]: " + plane.Alt.ToString("F8", CultureInfo.InvariantCulture), LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
}
|
|
// skip without error when altitude_ is out of bounds
|
|
if ((plane.Alt_m < Arguments.MinAlt) || (plane.Alt_m > Arguments.MaxAlt))
|
|
{
|
|
continue;
|
|
}
|
|
// check call
|
|
if (!PlaneInfoChecker.Check_Call(plane.Call))
|
|
{
|
|
// try to recover from cache if check fails or set it to [unknown]
|
|
PlaneInfo info = null;
|
|
if (PlanePositions.TryGetValue(plane.Hex, out info))
|
|
{
|
|
plane.Call = info.Call;
|
|
}
|
|
else
|
|
plane.Call = "[unknown]";
|
|
}
|
|
// still unknown call --> try to recover last known call from database
|
|
if (!PlaneInfoChecker.Check_Call(plane.Call))
|
|
{
|
|
AircraftDesignator ad = AircraftData.Database.AircraftFindByHex(plane.Hex);
|
|
if (ad != null)
|
|
{
|
|
plane.Call = ad.Call;
|
|
}
|
|
else
|
|
plane.Call = "[unknown]";
|
|
}
|
|
// check registration
|
|
if (!PlaneInfoChecker.Check_Reg(plane.Reg))
|
|
{
|
|
// try to recover from cache if check fails or set it to [unknown]
|
|
PlaneInfo info = null;
|
|
if (PlanePositions.TryGetValue(plane.Hex, out info))
|
|
{
|
|
plane.Reg = info.Reg;
|
|
}
|
|
else
|
|
plane.Reg = "[unknown]";
|
|
}
|
|
// still unknown --> try to recover last known reg from database
|
|
if (!PlaneInfoChecker.Check_Reg(plane.Reg))
|
|
{
|
|
AircraftDesignator ad = AircraftData.Database.AircraftFindByHex(plane.Hex);
|
|
if (ad != null)
|
|
{
|
|
plane.Reg = ad.Reg;
|
|
}
|
|
else
|
|
plane.Reg = "[unknown]";
|
|
}
|
|
// check speed
|
|
if (!PlaneInfoChecker.Check_Track(plane.Track))
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Track]: " + plane.Track.ToString("F8", CultureInfo.InvariantCulture), LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
// check speed
|
|
if (!PlaneInfoChecker.Check_Speed(plane.Speed))
|
|
{
|
|
// try to recover speed from previous messages
|
|
PlaneInfo info = null;
|
|
if (PlanePositions.TryGetValue(plane.Hex, out info))
|
|
{
|
|
plane.Speed = info.Speed;
|
|
}
|
|
else
|
|
{
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft data received [Speed]: " + plane.Speed.ToString("F8", CultureInfo.InvariantCulture), LogLevel.Warning);
|
|
invalids.Add(plane);
|
|
errors++;
|
|
continue;
|
|
}
|
|
}
|
|
// check type
|
|
if (!PlaneInfoChecker.Check_Type(plane.Type))
|
|
{
|
|
AircraftDesignator ad = AircraftData.Database.AircraftFindByHex(plane.Hex);
|
|
if (ad != null)
|
|
{
|
|
plane.Type = ad.TypeCode;
|
|
// getrest of info later later
|
|
}
|
|
else
|
|
{
|
|
// set all type info to unknown
|
|
plane.Type = "[unknown]";
|
|
plane.Model = "[unknown]";
|
|
plane.Manufacturer = "[unknown]";
|
|
plane.Category = PLANECATEGORY.NONE;
|
|
}
|
|
}
|
|
// try to recover type info from database if check fails or unknown
|
|
if (!PlaneInfoChecker.Check_Manufacturer(plane.Manufacturer) || !PlaneInfoChecker.Check_Model(plane.Model) ||
|
|
(plane.Manufacturer == "[unkonwn]") || (plane.Model == "[unknown]"))
|
|
{
|
|
AircraftTypeDesignator td = AircraftData.Database.AircraftTypeFindByICAO(plane.Type);
|
|
if (td != null)
|
|
{
|
|
plane.Manufacturer = td.Manufacturer;
|
|
plane.Model = td.Model;
|
|
plane.Category = td.Category;
|
|
}
|
|
else
|
|
{
|
|
plane.Manufacturer = "[unknown]";
|
|
plane.Model = "[unknown]";
|
|
plane.Category = PLANECATEGORY.NONE;
|
|
}
|
|
}
|
|
// remove manufacturer info if part of model description
|
|
if (plane.Model.StartsWith(plane.Manufacturer))
|
|
plane.Model = plane.Model.Remove(0, plane.Manufacturer.Length).Trim();
|
|
|
|
// check position against estimated position from last konwn if possible
|
|
PlaneInfo estplane = null;
|
|
PlaneInfo lastplane = null;
|
|
if (PlanePositions.ContainsKey(plane.Hex))
|
|
{
|
|
lastplane = PlanePositions[plane.Hex];
|
|
if ((plane.Time - lastplane.Time).TotalSeconds > 300)
|
|
lastplane = null;
|
|
estplane = PlanePositions.Get(plane.Hex, plane.Time, 5);
|
|
}
|
|
if (Arguments.ExtendedPlausibilityCheck_Enable && (lastplane != null) && (estplane != null))
|
|
{
|
|
// estimate the track value from location change
|
|
track = LatLon.Bearing(lastplane.Lat, lastplane.Lon, plane.Lat, plane.Lon);
|
|
|
|
// estimate the distance between estimated position and reported position
|
|
dist = LatLon.Distance(estplane.Lat, estplane.Lon, plane.Lat, plane.Lon);
|
|
|
|
// Check track
|
|
if (Math.Abs(((track <= 180) ? track : 360 - track) - ((plane.Track <= 180) ? plane.Track : 360 - plane.Track)) > 45)
|
|
{
|
|
|
|
plane.Track = track;
|
|
|
|
// report error
|
|
if (Arguments.LogErrors)
|
|
Log.WriteMessage("Incorrect aircraft track received [(" + lastplane.Track.ToString("F0") + "<>" + plane.Track.ToString("F0"), LogLevel.Warning);
|
|
|
|
invalids.Add(plane);
|
|
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
// check distance
|
|
if (Math.Abs(dist) > Arguments.ExtendedPlausiblityCheck_MaxErrorDist * (plane.Time - lastplane.Time).TotalMinutes)
|
|
{
|
|
Console.WriteLine("[Error " + errors + "] : Distance " + dist);
|
|
// report error
|
|
if (Arguments.LogErrors)
|
|
{
|
|
invalids.Add(plane);
|
|
Log.WriteMessage("Incorrect aircraft position received [(" + lastplane.Lat.ToString("F8") + "," + lastplane.Lon.ToString("F8") + ")<" + dist.ToString("F0") + "km>(" + plane.Lat.ToString("F8") + "," + plane.Lon.ToString("F8") + ")]: " + plane.ToString(), LogLevel.Warning);
|
|
}
|
|
|
|
invalids.Add(plane);
|
|
|
|
errors++;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
if (feedargs.LogPlanePositions)
|
|
{
|
|
// extended logging
|
|
string filename = Path.Combine(feedargs.PlanePositionsDirectory, plane.Hex + ".csv");
|
|
|
|
if (!File.Exists(filename))
|
|
{
|
|
File.WriteAllText(filename, "Time;TimeD;Hex;Lat;LatD;Lon;LonD;Alt;AltD;Track;TrackD;Speed;SpeedD;Call;Reg;From;To;VSpeed;CalcTrack;CalcDist" + Environment.NewLine);
|
|
}
|
|
try
|
|
{
|
|
double timed = 0;
|
|
double latd = 0;
|
|
double lond = 0;
|
|
double altd = 0;
|
|
double spdd = 0;
|
|
double trkd = 0;
|
|
|
|
if (lastplane != null)
|
|
{
|
|
timed = (lastplane.Time - plane.Time).TotalSeconds;
|
|
latd = lastplane.Lat - plane.Lat;
|
|
lond = lastplane.Lon - plane.Lon;
|
|
altd = lastplane.Alt - plane.Alt;
|
|
spdd = lastplane.Speed - plane.Speed;
|
|
trkd = lastplane.Track - plane.Track;
|
|
}
|
|
|
|
string csv = plane.Time + ";" +
|
|
timed + ";" +
|
|
plane.Hex + ";" +
|
|
plane.Lat + ";" +
|
|
latd + ";" +
|
|
plane.Lon + ";" +
|
|
lond + ";" +
|
|
plane.Alt + ";" +
|
|
altd + ";" +
|
|
plane.Track + ";" +
|
|
trkd + ";" +
|
|
plane.Speed + ";" +
|
|
spdd + ";" +
|
|
plane.Call + ";" +
|
|
plane.Reg + ";" +
|
|
plane.From + ";" +
|
|
plane.To + ";" +
|
|
plane.VSpeed + ";" +
|
|
track + ";" +
|
|
dist +
|
|
Environment.NewLine;
|
|
|
|
File.AppendAllText(filename, csv);
|
|
}
|
|
catch
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
// all checks successfully done --> add plane to list
|
|
planes.Add(plane);
|
|
count++;
|
|
// cancel thread if requested
|
|
if (this.CancellationPending)
|
|
return;
|
|
}
|
|
// update local cache
|
|
this.PlanePositions.BulkInsertOrUpdateIfNewer(planes);
|
|
// report planes to main program
|
|
this.ReportProgress((int)PROGRESS.PLANES, planes);
|
|
// update global database
|
|
AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(this, planes);
|
|
// update position database if enabled
|
|
if (Arguments.KeepHistory)
|
|
AircraftPositionData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes);
|
|
st.Stop();
|
|
string msg = "[" + DateTime.UtcNow.ToString("HH:mm:ss") + "] " +
|
|
total.ToString() + " Positions updated from " + Arguments.Feed.Name + ", " +
|
|
st.ElapsedMilliseconds.ToString() + " ms. OK: " + count.ToString() + ", Errors: " + errors.ToString();
|
|
this.ReportProgress((int)PROGRESS.STATUS, msg);
|
|
// write all planes to file
|
|
try
|
|
{
|
|
// simple logging
|
|
using (StreamWriter sw = new StreamWriter(Path.Combine(Arguments.TmpDirectory, "planes.csv")))
|
|
{
|
|
sw.WriteLine("Time;Hex;Lat;Lon;Alt;Track;Speed;Call;Reg;From;To;VSpeed");
|
|
foreach (PlaneInfo plane in planes)
|
|
{
|
|
sw.WriteLine(plane.Time + ";" +
|
|
plane.Hex + ";" +
|
|
plane.Lat + ";" +
|
|
plane.Lon + ";" +
|
|
plane.Alt + ";" +
|
|
plane.Track + ";" +
|
|
plane.Speed + ";" +
|
|
plane.Call + ";" +
|
|
plane.Reg + ";" +
|
|
plane.From + ";" +
|
|
plane.To + ";" +
|
|
plane.VSpeed);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// do nothing
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Status = STATUS.ERROR;
|
|
this.ReportProgress((int)PROGRESS.ERROR, "Plane Feed Execption: " + ex.Message);
|
|
Log.WriteMessage(ex.ToString(), LogLevel.Error);
|
|
}
|
|
// wait for next execution
|
|
int i = 0;
|
|
while (!CancellationPending && (i < interval))
|
|
{
|
|
Thread.Sleep(1000);
|
|
i++;
|
|
}
|
|
}
|
|
while (!CancellationPending);
|
|
// do stop procedure
|
|
Arguments.Feed.Stop(feedargs);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Status = STATUS.ERROR;
|
|
this.ReportProgress((int)PROGRESS.ERROR, "Plane Feed Execption: " + ex.Message);
|
|
Log.WriteMessage(ex.ToString(), LogLevel.Error);
|
|
Console.WriteLine("Plane Feed Execption: " + ex.ToString(), LogLevel.Error);
|
|
}
|
|
}
|
|
while (!this.CancellationPending);
|
|
}
|
|
}
|
|
this.ReportProgress((int)PROGRESS.FINISHED);
|
|
Log.WriteMessage("Finished.");
|
|
}
|
|
|
|
protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
|
|
{
|
|
// do stop procedure
|
|
try
|
|
{
|
|
Arguments.Feed.Stop(null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.WriteMessage(ex.ToString());
|
|
}
|
|
|
|
base.OnRunWorkerCompleted(e);
|
|
}
|
|
|
|
}
|
|
|
|
}
|