AirScout/AirScout.PlaneFeeds.Plugin..../FlexJSON.cs

1776 wiersze
61 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel.Composition;
using System.ComponentModel;
using System.Globalization;
using AirScout.PlaneFeeds.Plugin.MEFContract;
using System.Diagnostics;
using System.Net;
using System.IO;
using System.Web.Script.Serialization;
using System.Xml.Serialization;
using System.Windows.Forms;
using AirScout.PlaneFeeds.Plugin;
using System.Xml;
using System.Xml.Linq;
namespace AirScout.PlaneFeeds.Plugin.FlexJSON
{
[Serializable]
public class FlexJSONSettings
{
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for UTC [OPTIONAL]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_UTC { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Hex [OPTIONAL]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Hex { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Call [OPTIONAL]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Call { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Latitude [deg] [MANDATORY]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Lat { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Longitde [deg] [MANDATORY]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Lon { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Altitude [ft, m] [MANDATORY]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Alt { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Units for Altitude [ft, m]")]
[DefaultValue("ft")]
public string Units_Alt { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Speed [kts, km/h] [MANDATORY]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Speed { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Units for Speed [kts, km/h]")]
[DefaultValue("kts")]
public string Units_Speed { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Track in [deg] [MANDATORY]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Track { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Type [OPTIONAL]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Type { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Index for Registration [OPTIONAL]. Use -1 for detect, 255 for not used.")]
[DefaultValue(-1)]
public int Index_Reg { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Minimum Count of Elements per Plane Position Data Set")]
[DefaultValue(10)]
public int Min_Elements { get; set; }
[CategoryAttribute("Data Field Properties")]
[DescriptionAttribute("Minium Count of Plane Position Data Sets")]
[DefaultValue(50)]
public int Min_Planes { get; set; }
[Browsable(false)]
[DefaultValue("")]
[XmlIgnore]
public string DisclaimerAccepted { get; set; }
[CategoryAttribute("Web Feed")]
[DescriptionAttribute("Save downloaded JSON to file")]
[DefaultValue(false)]
[XmlIgnore]
public bool SaveToFile { get; set; }
[CategoryAttribute("Web Feed")]
[DescriptionAttribute("Base URL for website.")]
[DefaultValue("")]
public string URL { get; set; }
[CategoryAttribute("Web Feed")]
[DescriptionAttribute("Timeout for loading the site.")]
[DefaultValue(30)]
[XmlIgnore]
public int Timeout { get; set; }
public FlexJSONSettings()
{
Default();
Load(true);
}
/// <summary>
/// Sets all properties to their default value according to the [DefaultValue=] attribute
/// </summary>
public void Default()
{
// set all properties to their default values according to definition in [DeafultValue=]
foreach (var p in this.GetType().GetProperties())
{
try
{
// initialize all properties with default value if set
if (Attribute.IsDefined(p, typeof(DefaultValueAttribute)))
{
p.SetValue(this, ((DefaultValueAttribute)Attribute.GetCustomAttribute(
p, typeof(DefaultValueAttribute)))?.Value, null);
}
}
catch (Exception ex)
{
Console.WriteLine("[" + this.GetType().Name + "]: Cannot set default value of: " + p.Name + ", " + ex.Message);
}
}
}
/// <summary>
/// Loads settings from a XML-formatted configuration file into settings.
/// </summary>
/// <param name="loadall">If true, ignore the [XmlIgnore] attribute, e.g. load all settings available in the file.<br>If false, load only settings without [XmlIgore] attrbute.</br></param>
/// <param name="filename">The filename of the settings file.</param>
public void Load(bool loadall, string filename = "")
{
// use standard filename if empty
// be careful because Linux file system is case sensitive
if (String.IsNullOrEmpty(filename))
filename = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase.Replace(".dll", ".cfg").Replace(".DLL", ".CFG")).LocalPath;
// do nothing if file not exists
if (!File.Exists(filename))
return;
try
{
string xml = "";
using (StreamReader sr = new StreamReader(File.OpenRead(filename)))
{
xml = sr.ReadToEnd();
}
XDocument xdoc = XDocument.Parse(xml);
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
if (!loadall)
{
// check on XmlIgnore attribute, skip if set
object[] attr = p.GetCustomAttributes(typeof(XmlIgnoreAttribute), false);
if (attr.Length > 0)
continue;
}
try
{
// get matching element
XElement typenode = xdoc.Element(this.GetType().Name);
if (typenode != null)
{
XElement element = typenode.Element(p.Name);
if (element != null)
p.SetValue(this, Convert.ChangeType(element.Value, p.PropertyType), null);
}
}
catch (Exception ex)
{
Console.WriteLine("[" + this.GetType().Name + "]: Error while loading property[" + p.Name + " from " + filename + ", " + ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine("[" + this.GetType().Name + "]: Cannot load settings from " + filename + ", " + ex.Message);
}
}
/// <summary>
/// Saves settings from settings into a XML-formatted configuration file
/// </summary>
/// <param name="saveall">If true, ignore the [XmlIgnore] attribute, e.g. save all settings.<br>If false, save only settings without [XmlIgore] attrbute.</param>
/// <param name="filename">The filename of the settings file.</param>
public void Save(bool saveall, string filename = "")
{
// use standard filename if empty
// be careful because Linux file system is case sensitive
if (String.IsNullOrEmpty(filename))
filename = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase.Replace(".dll", ".cfg").Replace(".DLL",".CFG")).LocalPath;
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
if (saveall)
{
// ovverride the XmlIgnore attributes to get all serialized
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
XmlAttributes attribs = new XmlAttributes { XmlIgnore = false };
overrides.Add(this.GetType(), p.Name, attribs);
}
}
try
{
using (StreamWriter sw = new StreamWriter(File.Create(filename)))
{
XmlSerializer s = new XmlSerializer(this.GetType(), overrides);
s.Serialize(sw, this);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot save settings to " + filename + ", " + ex.Message);
}
}
}
[Export(typeof(IPlaneFeedPlugin))]
[ExportMetadata("Name", "PlaneFeedPlugin")]
public class FlexJSONPlugin : IPlaneFeedPlugin
{
//********************************** START OF INTERFACE ******************************************
public string Name
{
get
{
return "[WebFeed] Flexible JSON";
}
}
public string Info
{
get
{
return "Web feed with flexible JSON column assignment. Use it for webfeed with unknown content.\n" +
"(c) AirScout(www.airscout.eu)\n" +
"This feed tries to read unknown JSON data into an array of plane information.\n" +
"If this is successful, a \"forensic\" algorithm tries to find out which information is on which index.\n" +
"You can manually enter indices as well.See documentation for further details.";
}
}
public string Version
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
public bool HasSettings
{
get
{
return true;
}
}
public bool CanImport
{
get
{
return true;
}
}
public bool CanExport
{
get
{
return true;
}
}
public string Disclaimer
{
get
{
return "This plane feed might fetch data from an Internet server via Deep Link\n" +
"technology(see http://en.wikipedia.org/wiki/Deep_link)\n." +
"The use is probably not intended by the website owners and could be changed in URL and data format frequently and without further notice.\n" +
"Furthermore, it might cause legal issues in some countries.\n" +
"By clicking on \"Accept\" you understand that you are\n\n" +
"DOING THAT ON YOUR OWN RISK\n\n" +
"The auhor of this software will not be responsible in any case.";
}
}
public string DisclaimerAccepted
{
get
{
return Settings.DisclaimerAccepted;
}
set
{
Settings.DisclaimerAccepted = value;
}
}
public void ResetSettings()
{
Settings.Default();
}
public void LoadSettings()
{
Settings.Load(true);
}
public void SaveSettings()
{
Settings.Save(true);
}
public object GetSettings()
{
return this.Settings;
}
public void ImportSettings()
{
OpenFileDialog Dlg = new OpenFileDialog();
Dlg.FileName = "*.feed";
Dlg.DefaultExt = "feed";
Dlg.Filter = "Plane Feeds | .feed";
Dlg.CheckFileExists = true;
if (Dlg.ShowDialog() == DialogResult.OK)
{
try
{
Settings.Load(false, Dlg.FileName);
}
catch (Exception ex)
{
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot import from " + Dlg.FileName + ", " + ex.Message);
}
}
}
public void ExportSettings()
{
SaveFileDialog Dlg = new SaveFileDialog();
Dlg.DefaultExt = "feed";
Dlg.Filter = "Plane Feeds | .feed";
Dlg.OverwritePrompt = true;
if (Dlg.ShowDialog() == DialogResult.OK)
{
try
{
Settings.Save(false, Dlg.FileName);
}
catch (Exception ex)
{
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot export to " + Dlg.FileName + ", " + ex.Message);
}
}
}
public void Start(PlaneFeedPluginArgs args)
{
// keep the TmpDirectory
TmpDirectory = args.TmpDirectory;
}
public PlaneFeedPluginPlaneInfoList GetPlanes(PlaneFeedPluginArgs args)
{
// intialize variables
VarConverter VC = new VarConverter();
VC.AddVar("APPDIR", args.AppDirectory);
VC.AddVar("DATADIR", args.AppDataDirectory);
VC.AddVar("LOGDIR", args.LogDirectory);
VC.AddVar("DATABASEDIR", args.DatabaseDirectory);
VC.AddVar("MINLAT", args.MinLat);
VC.AddVar("MAXLAT", args.MaxLat);
VC.AddVar("MINLON", args.MinLon);
VC.AddVar("MAXLON", args.MaxLon);
VC.AddVar("MINALTM", args.MinAlt);
VC.AddVar("MAXALTM", args.MaxAlt);
VC.AddVar("MINALTFT", (int)UnitConverter.m_ft((double)args.MinAlt));
VC.AddVar("MAXALTFT", (int)UnitConverter.m_ft((double)args.MaxAlt));
// initialize plane info list
PlaneFeedPluginPlaneInfoList planes = new PlaneFeedPluginPlaneInfoList();
string json = "";
// calculate url and get json
String url = VC.ReplaceAllVars(Settings.URL);
// check parameters
if (String.IsNullOrEmpty(url))
throw new ArgumentException("The URL is empty.");
if (Settings.Timeout <= 0)
throw new ArgumentException("The value for Timout is invalid: " + Settings.Timeout.ToString());
Console.WriteLine("[" + this.GetType().Name + "]: Creating web request: " + url);
// check url and choose the according download mode
if (!url.ToLowerInvariant().StartsWith("https://"))
{
HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url);
webrequest.Referer = "";
webrequest.Timeout = (int)Settings.Timeout * 1000;
webrequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0";
webrequest.Accept = "application/json, text/javascript, */*;q=0.01";
webrequest.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip;
Console.WriteLine("[" + this.GetType().Name + "]: Getting web response");
HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();
Console.WriteLine("[" + this.GetType().Name + "]: Reading stream");
using (StreamReader sr = new StreamReader(webresponse.GetResponseStream()))
{
json = sr.ReadToEnd();
}
}
else
{
json = TlsClient.DownloadFile(url, Settings.Timeout * 1000);
}
// save raw data to file if enabled
if (Settings.SaveToFile)
{
using (StreamWriter sw = new StreamWriter(args.TmpDirectory + Path.DirectorySeparatorChar + this.GetType().Name + "_" + DateTime.UtcNow.ToString("yyyy-MM-dd HH_mm_ss") + ".json"))
{
sw.WriteLine(json);
}
}
Console.WriteLine("[" + this.GetType().Name + "]: Analyzing data");
JavaScriptSerializer js = new JavaScriptSerializer();
dynamic root = js.Deserialize<dynamic>(json);
// get all planeinfos in an array
GetAllPlaneInfos(root);
// try to find all indices automatically if not set
// mind the search order!
if (Settings.Index_Hex < 0)
{
FindHexIndex();
Settings.Save(true);
}
if (Settings.Index_UTC < 0)
{
FindUTCIndex();
Settings.Save(true);
}
if (Settings.Index_Type < 0)
{
FindTypeIndex();
Settings.Save(true);
}
if (Settings.Index_Lat < 0)
{
FindLatIndex();
Settings.Save(true);
}
if (Settings.Index_Lon < 0)
{
FindLonIndex();
Settings.Save(true);
}
if (Settings.Index_Alt < 0)
{
FindAltIndex();
Settings.Save(true);
}
if (Settings.Index_Track < 0)
{
FindTrackIndex();
Settings.Save(true);
}
if (Settings.Index_Speed < 0)
{
FindSpeedIndex();
Settings.Save(true);
}
if (Settings.Index_Reg < 0)
{
FindRegIndex();
Settings.Save(true);
}
// all must produce/have some mandatory data
if (Settings.Index_Lat < 0)
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot get planes, Lat index not found/not set!");
if (Settings.Index_Lon < 0)
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot get planes, Lon index not found/not set!");
if (Settings.Index_Alt < 0)
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot get planes, Alt index not found/not set!");
if (Settings.Index_Track < 0)
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot get planes, Track index not found/not set!");
if (Settings.Index_Speed < 0)
throw new InvalidOperationException("[" + this.GetType().Name + "]: Cannot get planes, Speed index not found/not set!");
// fill plane info
foreach (string[] info in PlaneList)
{
PlaneFeedPluginPlaneInfo plane = new PlaneFeedPluginPlaneInfo();
if ((Settings.Index_Hex > 0) && (Settings.Index_Hex < info.Length))
plane.Hex = To_Hex(info[Settings.Index_Hex]);
else
plane.Hex = "";
if ((Settings.Index_UTC > 0) && (Settings.Index_UTC < info.Length))
plane.Time = To_UTC(info[Settings.Index_UTC]);
else
plane.Time = DateTime.UtcNow;
if ((Settings.Index_Lat > 0) && (Settings.Index_Lat < info.Length))
plane.Lat = To_Lat(info[Settings.Index_Lat]);
else
plane.Lat = double.MinValue;
if ((Settings.Index_Lon > 0) && (Settings.Index_Lon < info.Length))
plane.Lon = To_Lon(info[Settings.Index_Lon]);
else
plane.Lon = double.MinValue;
if ((Settings.Index_Alt > 0) && (Settings.Index_Alt < info.Length))
plane.Alt = To_Alt(info[Settings.Index_Alt]);
else
plane.Alt = double.MinValue;
if ((Settings.Index_Speed > 0) && (Settings.Index_Speed < info.Length))
plane.Speed = To_Speed(info[Settings.Index_Speed]);
else
plane.Speed = int.MinValue;
if ((Settings.Index_Track > 0) && (Settings.Index_Track < info.Length))
plane.Track = To_Track(info[Settings.Index_Track]);
else
plane.Track = int.MinValue;
if ((Settings.Index_Call > 0) && (Settings.Index_Call < info.Length))
plane.Call = To_Call(info[Settings.Index_Call]);
else
plane.Call = "";
if ((Settings.Index_Type > 0) && (Settings.Index_Type < info.Length))
plane.Type = To_Type(info[Settings.Index_Type]);
else
plane.Type = "";
if ((Settings.Index_Reg > 0) && (Settings.Index_Reg < info.Length))
plane.Reg = To_Reg(info[Settings.Index_Reg]);
else
plane.Reg = "";
planes.Add(plane);
}
Console.WriteLine("[" + this.GetType().Name + "]: Returning planes");
return planes;
}
public void Stop(PlaneFeedPluginArgs args)
{
// save settings
Settings.Save(true);
}
// **************************** END OF INTERFACE *********************************
private string TmpDirectory;
private FlexJSONSettings Settings = new FlexJSONSettings();
public FlexJSONPlugin()
{
}
// *********************** Plane info converters **********************************
#region PlaneInfoCheckers
// checks for int and long values
private bool IsInt(string s)
{
s = s.Trim();
// int should contain only these chars
string allowed = "-01234567890";
int i;
for (i = 0; i < s.Length; i++)
{
if (!allowed.Contains(s[i]))
return false;
}
long l;
// try to convert to int
if (!long.TryParse(s, out l))
return false;
// all checks paased
return true;
}
private bool IsDouble(string s)
{
s = s.Trim();
// double must contain at least a decimal separator
if (!s.Contains(".") && !s.Contains(","))
return false;
// try to convert to double
double d;
// try to convert with regional settings
if (!double.TryParse(s, out d))
{
// try invariant
if (!double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out d))
return false;
}
// all checks passed
return true;
}
private bool IsHex(string s)
{
bool b;
s = s.Trim();
if (s.Length != 6)
return false;
long l;
if (!long.TryParse(s, System.Globalization.NumberStyles.HexNumber, null, out l))
return false;
// all checks passed
return true;
}
private bool IsUTC(string s)
{
// get current time in all three formats
DateTime now = DateTime.UtcNow;
int now_int = (Int32)(now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
long now_long = (long)now_int * 1000;
s = s.Trim();
// to short for a UTC value
if (s.Length < 10)
return false;
if (!IsInt(s))
{
// maybe DateTime formatted
if (!s.ToCharArray().Any(c => "-_:".Contains(c)))
return false;
}
// assuming int
long l = 0;
// assuming integer values
if (s.Length < 13)
{
// assuming UNIX time in sec
if (!long.TryParse(s, out l))
return false;
if (Math.Abs(now_int - l) > 1000)
return false;
}
// assuming UNIX time in msec
if (!long.TryParse(s, out l))
return false;
if (Math.Abs(now_long - l) > 1000000)
return false;
// all checks passed
return true;
}
private bool IsLat(string s)
{
bool b;
s = s.Trim();
if (!IsDouble(s))
return false;
double d = To_Double(s);
if (d < -90)
return false;
if (d > 90)
return false;
// all checks passed
return true;
}
private bool IsLon(string s)
{
bool b;
s = s.Trim();
if (!IsDouble(s))
return false;
double d = To_Double(s);
if (d < -180)
return false;
if (d > 180)
return false;
// all checks passed
return true;
}
private bool IsAlt(string s)
{
bool b;
s = s.Trim();
if (!IsInt(s) && !IsDouble(s))
return false;
double d = To_Double(s);
// don't accept negative values though they might occur
if (d < 0)
return false;
if ((Settings.Units_Alt.ToLower() == "m ") && (d > 30000))
return false;
// assuming altitude in feet
if (d > 100000)
return false;
// all checks passed
return true;
}
private bool IsTrack(string s)
{
bool b;
s = s.Trim();
if (!IsInt(s) && !IsDouble(s))
return false;
double d = To_Double(s);
if (d < 0)
return false;
if (d > 360)
return false;
// all checks passed
return true;
}
private bool IsSpeed(string s)
{
bool b;
s = s.Trim();
if (!IsInt(s) && !IsDouble(s))
return false;
double d = To_Double(s);
if (d < 0)
return false;
// don't accept higher values though they might occur
if ((Settings.Units_Speed.ToLower() == "km/h ") && (d > 1000))
return false;
// assuming speed in kts
if (d > 550)
return false;
// all checks passed
return true;
}
private bool IsReg(string s)
{
bool b;
s = s.Trim();
// Registration should contain a "-" or start with "N"
if (!s.Contains("-"))
{
if (!s.StartsWith("N"))
return false;
}
return true;
}
private bool IsType(string s)
{
bool b;
s = s.Trim();
if (s.Length != 4)
return false;
// type should contain at least on number
for (int i = 0; i < s.Length; i++)
{
if (char.IsNumber(s[i]))
return true;
}
return false; ;
}
#endregion
#region PlaneInfoConverters
public int To_Int(string s)
{
int i;
if (int.TryParse(s, out i))
return i;
return int.MinValue;
}
public long To_Long(string s)
{
long l;
if (long.TryParse(s, out l))
return l;
return long.MinValue;
}
public double To_Double(string s)
{
double d;
if (double.TryParse(s, out d))
return d;
if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out d))
return d;
return double.MinValue;
}
public DateTime To_UTC(string s)
{
if (String.IsNullOrEmpty(s))
return DateTime.MinValue;
// remove qoutes and spaces, if any
s = s.Replace("\"", String.Empty).Trim();
// UTC could be either:
// a 32bit integer containing seconds since 1970-01-01
// a 64bit ticks containing ticks[ms] since 1970-01-01
// a valid DateTime string, like "yyyy-MM-dd HH:mm:ss"
// try to convert UNIX times first
if ((s.Length >= 10) && IsInt(s))
{
try
{
if (s.Length > 10)
{
// try to convert to milliseconds
// try to convert to seconds
long l = System.Convert.ToInt64(s);
DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
timestamp = timestamp.AddMilliseconds(l);
return timestamp;
}
else
{
// try to convert to seconds
long l = System.Convert.ToInt64(s);
DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
timestamp = timestamp.AddSeconds(l);
return timestamp;
}
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
}
// check for Standard DateTime notation
try
{
// try to convert to UTC timestamp
DateTime timestamp;
if (DateTime.TryParse(s, out timestamp))
return timestamp;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return DateTime.MinValue;
}
public string To_Hex(string s)
{
if (String.IsNullOrEmpty(s))
return null;
// remove qoutes and spaces, if any
s = s.Replace("\"", String.Empty).ToUpper().Trim();
// check if Hex
if (!IsHex(s))
return null;
try
{
// try to convert to Hex value
long hex = System.Convert.ToInt64(s, 16);
// check boundaries
if ((hex < 0) || (hex > 16777215))
return null;
return s;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return null;
}
public double To_Lat(string s)
{
s = s.Replace("\"", String.Empty).Trim();
if (s.Length < 3)
return double.MinValue;
// double Lon must contain a decimal separator
if (s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) < 0)
return double.MinValue;
try
{
// try to convert to double
double d = System.Convert.ToDouble(s);
// check bounds
if ((d >= -90.0) && (d <= 90.0))
return d;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return double.MinValue;
}
public double To_Lon(string s)
{
s = s.Replace("\"", String.Empty).Trim();
if (s.Length < 3)
return double.MinValue;
// double Lon must contain a decimal separator
if (s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) < 0)
return double.MinValue;
try
{
// try to convert to double
double d = System.Convert.ToDouble(s);
// check bounds
if ((d >= -180.0) && (d <= 180.0))
return d;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return double.MinValue;
}
public int To_Alt(string s)
{
s = s.Replace("\"", String.Empty).Trim();
try
{
// try to convert to integer
long alt = System.Convert.ToInt64(s);
// convert m to ft
if (Settings.Units_Alt.ToLower() == "m")
alt = (int)((double)alt * 3.28084);
// check bounds
if ((alt < 0) || (alt > 100000))
return int.MinValue;
return (int)alt;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return int.MinValue;
}
public int To_Track(string s)
{
s = s.Replace("\"", String.Empty).Trim();
try
{
// try to convert to integer
long track = System.Convert.ToInt64(s);
// check bounds
if ((track < 0) || (track > 360))
return int.MinValue;
return (int)track;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return int.MinValue;
}
public int To_Speed(string s)
{
s = s.Replace("\"", String.Empty).Trim();
try
{
// try to convert to integer
long speed = System.Convert.ToInt64(s);
// convert km/h to kts
if (Settings.Units_Speed.ToLower() == "km/h")
return (int)((double)speed / 1.852);
if ((speed < 0) || (speed > 550))
return int.MinValue;
return (int)speed;
}
catch (Exception ex)
{
// Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s);
}
return int.MinValue;
}
public string To_Call(string s)
{
if (String.IsNullOrEmpty(s))
return null;
// remove qoutes and spaces, if any
s = s.Replace("\"", String.Empty).ToUpper().Trim();
return s;
}
public string To_Type(string s)
{
if (String.IsNullOrEmpty(s))
return null;
// remove qoutes and spaces, if any
s = s.Replace("\"", String.Empty).ToUpper().Trim();
return s;
}
public string To_Reg(string s)
{
if (String.IsNullOrEmpty(s))
return null;
// remove qoutes and spaces, if any
s = s.Replace("\"", String.Empty).ToUpper().Trim();
return s;
}
#endregion
#region IndexFinders
private void FindHexIndex()
{
if (PlaneList.Count <= 0)
return;
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsHexColumns.Length; i++)
{
if (!DetectedColumns[i])
{
if (IsHexColumns[i] > maxcount)
{
maxcount = IsHexColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Hex = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindUTCIndex()
{
if (PlaneList.Count <= 0)
return;
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsUTCColumns.Length; i++)
{
if (!DetectedColumns[i])
{
if (IsUTCColumns[i] > maxcount)
{
maxcount = IsUTCColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_UTC = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindLatIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsLatColumns.Length; i++)
{
if (!DetectedColumns[i])
{
if (IsLatColumns[i] > maxcount)
{
maxcount = IsLatColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Lat = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindLonIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsLonColumns.Length; i++)
{
if (!DetectedColumns[i])
{
if (IsLonColumns[i] > maxcount)
{
maxcount = IsLonColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Lon = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindTrackIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsTrackColumns.Length; i++)
{
if (!DetectedColumns[i] && (MaxColumns[i] <= 360))
{
if (IsTrackColumns[i] > maxcount)
{
maxcount = IsTrackColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Track = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindSpeedIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsSpeedColumns.Length; i++)
{
if (!DetectedColumns[i] && (MaxColumns[i] > 360))
{
if (IsSpeedColumns[i] > maxcount)
{
maxcount = IsSpeedColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Speed = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindAltIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsAltColumns.Length; i++)
{
if (!DetectedColumns[i] && (MeanColumns[i] > 5000))
{
if (IsAltColumns[i] > maxcount)
{
maxcount = IsAltColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Alt = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindTypeIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsTypeColumns.Length; i++)
{
if (!DetectedColumns[i])
{
if (IsTypeColumns[i] > maxcount)
{
maxcount = IsTypeColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Type = maxindex;
DetectedColumns[maxindex] = true;
}
}
private void FindRegIndex()
{
int maxindex = -1;
int maxcount = 0;
for (int i = 0; i < IsRegColumns.Length; i++)
{
if (!DetectedColumns[i])
{
if (IsRegColumns[i] > maxcount)
{
maxcount = IsRegColumns[i];
maxindex = i;
}
}
}
if (maxcount > (int)(0.9 * (double)PlaneList.Count))
{
Settings.Index_Reg = maxindex;
DetectedColumns[maxindex] = true;
}
}
#endregion
// *********************** Parsing elements ************************************
private class ElementInfo
{
public string Key;
public string TypeName;
public int Length;
public dynamic Element;
public ElementInfo(string key, string typename, int length, dynamic element)
{
Key = key;
TypeName = typename;
Length = length;
Element = element;
}
}
private List<ElementInfo> Elements = new List<ElementInfo>();
private Dictionary<int, int> ElementLengths = new Dictionary<int, int>();
private List<string[]> PlaneList = new List<string[]>();
private double[] MaxColumns;
private double[] MinColumns;
private double[] MeanColumns;
private int[] IsDoubleColumns;
private int[] IsIntColumns;
private int[] IsHexColumns;
private int[] IsUTCColumns;
private int[] IsLatColumns;
private int[] IsLonColumns;
private int[] IsAltColumns;
private int[] IsTrackColumns;
private int[] IsSpeedColumns;
private int[] IsTypeColumns;
private int[] IsRegColumns;
private bool[] DetectedColumns;
private void AddElement(string key, dynamic el)
{
string typename = el.GetType().Name;
if (typename.ToLower().Contains("dictionary"))
{
// dictionary found
// count the length and maintain element lengths
int i = 0;
int c = el.Values.Count;
if (ElementLengths.TryGetValue(c, out i))
{
ElementLengths[c]++;
}
else
{
ElementLengths.Add(c, 1);
}
// add element to elements list
ElementInfo info = new ElementInfo(key, typename, c, el);
Elements.Add(info);
// recurse element, key ist dictionary key
foreach (dynamic e in el.Keys)
{
AddElement(e, el[e]);
}
}
else if (typename.ToLower().Contains("[]"))
{
// array found
// add element to elements list
// count the length and maintain element lengths
int i = 0;
int c = el.Length;
if (ElementLengths.TryGetValue(c, out i))
{
ElementLengths[c]++;
}
else
{
ElementLengths.Add(c, 1);
}
ElementInfo info = new ElementInfo(key, typename, c, el);
Elements.Add(info);
// recurse elements, key is a unique ID
foreach (dynamic e in el)
{
AddElement(Guid.NewGuid().ToString(), e);
}
}
else if (typename.ToLower().Contains("arraylist"))
{
// ArrayList found
// add element to elements list
// count the length and maintain element lengths
int i = 0;
int c = el.Count;
if (ElementLengths.TryGetValue(c, out i))
{
ElementLengths[c]++;
}
else
{
ElementLengths.Add(c, 1);
}
ElementInfo info = new ElementInfo(key, typename, c, el);
Elements.Add(info);
Console.WriteLine("Element found: " + info.Key + "; " + info.TypeName + "; " + c.ToString() + "; ", el.ToString());
// recurse elements, key is a unique ID
foreach (dynamic e in el)
{
AddElement(Guid.NewGuid().ToString(), e);
}
}
}
private string[] GetPlaneInfo(ElementInfo el)
{
string[] info = new string[el.Length + 1];
// set key
info[0] = el.Key;
// grab info
if (el.TypeName.ToLower().Contains("dictionary"))
{
// dictionary found
int count = 1;
foreach (dynamic e in el.Element.Values)
{
if (e.GetType().IsPrimitive || (e.GetType().Name.ToLower() == "string"))
{
info[count] = e.ToString();
}
else
{
info[count] = "";
}
count++;
}
}
else if (el.TypeName.ToLower().Contains("[]"))
{
// array found
int count = 1;
foreach (dynamic e in el.Element)
{
string typename = e.GetType().Name;
if (e.GetType().IsPrimitive || (typename.ToLower() == "string") || (typename.ToLower() == "decimal"))
{
info[count] = e.ToString();
}
else
{
info[count] = "";
}
count++;
}
}
else if (el.TypeName.ToLower().Contains("arraylist"))
{
// ArrayList found
int count = 1;
foreach (dynamic e in el.Element)
{
string typename = e.GetType().Name;
if (e.GetType().IsPrimitive || (typename.ToLower() == "string") || (typename.ToLower() == "decimal"))
{
info[count] = e.ToString();
}
else
{
info[count] = "";
}
count++;
}
}
return info;
}
private void GetColumnInfo()
{
double[] sum = new double[MinColumns.Length];
for (int i = 0; i < sum.Length; i++)
sum[i] = 0;
foreach (string[] info in PlaneList)
{
if (info == null)
continue;
for (int i = 0; i < info.Length; i++)
{
if (IsDouble(info[i]))
{
IsDoubleColumns[i]++;
double d = To_Double(info[i]);
if (d != double.MinValue)
{
if (d < MinColumns[i])
MinColumns[i] = d;
else if (d > MaxColumns[i])
MaxColumns[i] = d;
}
sum[i] = sum[i] + d;
}
if (IsInt(info[i]))
{
IsIntColumns[i]++;
long l = To_Long(info[i]);
if (l != long.MinValue)
{
if (l < MinColumns[i])
MinColumns[i] = l;
else if (l > MaxColumns[i])
MaxColumns[i] = l;
}
sum[i] = sum[i] + l;
}
if (IsHex(info[i]))
IsHexColumns[i]++;
if (IsUTC(info[i]))
IsUTCColumns[i]++;
if (IsLat(info[i]))
IsLatColumns[i]++;
if (IsLon(info[i]))
IsLonColumns[i]++;
if (IsAlt(info[i]))
IsAltColumns[i]++;
if (IsTrack(info[i]))
IsTrackColumns[i]++;
if (IsSpeed(info[i]))
IsSpeedColumns[i]++;
if (IsType(info[i]))
IsTypeColumns[i]++;
if (IsReg(info[i]))
IsRegColumns[i]++;
}
}
for (int i = 0; i < sum.Length; i++)
MeanColumns[i] = sum[i] / PlaneList.Count();
}
private void GetAllPlaneInfos(dynamic obj)
{
// get all elements in a linear list and count the lengths
Elements.Clear();
ElementLengths.Clear();
// start at the root element
AddElement("", obj);
// find the majority of element lengths --> assuming that these are the plane elements
int maxcount = 0;
int maxkey = 0;
foreach (int key in ElementLengths.Keys)
{
if (ElementLengths[key] > maxcount)
{
maxcount = ElementLengths[key];
maxkey = key;
}
}
// get all plane infos in a clean list
PlaneList.Clear();
foreach (ElementInfo el in Elements)
{
// add matching elements to planes list
if (el.Length == maxkey)
{
string[] info = GetPlaneInfo(el);
PlaneList.Add(info);
}
}
// store the plane i nfo table as CSV, so that a user can determine which columns at which indices are needed
// catch the exception here to continue grabbing planes if user left thge file open :-)
string csvfilename = Path.Combine(TmpDirectory, "FlexJSON.csv");
try
{
using (StreamWriter sw = new StreamWriter(csvfilename))
{
foreach (string[] infos in PlaneList)
{
foreach (string info in infos)
sw.Write("\"" + info + "\"" + ";");
sw.WriteLine();
}
}
}
catch (Exception ex)
{
// do nothing
Console.WriteLine("[" + this.GetType().Name + "]: Cannot write CSV-table " + csvfilename + ", " + ex.ToString());
}
// clear all info arrays
MaxColumns = new double[maxkey + 1];
MinColumns = new double[maxkey + 1];
MeanColumns = new double[maxkey + 1];
IsIntColumns = new int[maxkey + 1];
IsDoubleColumns = new int[maxkey + 1];
IsHexColumns = new int[maxkey + 1];
IsUTCColumns = new int[maxkey + 1];
IsLatColumns = new int[maxkey + 1];
IsLonColumns = new int[maxkey + 1];
IsAltColumns = new int[maxkey + 1];
IsTrackColumns = new int[maxkey + 1];
IsSpeedColumns = new int[maxkey + 1];
IsTypeColumns = new int[maxkey + 1];
IsRegColumns = new int[maxkey + 1];
DetectedColumns = new bool[maxkey + 1];
for (int i = 0; i < maxkey + 1; i++)
{
MaxColumns[i] = 0;
MinColumns[i] = 0;
MeanColumns[i] = 0;
IsIntColumns[i] = 0;
IsDoubleColumns[i] = 0;
IsHexColumns[i] = 0;
IsUTCColumns[i] = 0;
IsLatColumns[i] = 0;
IsAltColumns[i] = 0;
IsTrackColumns[i] = 0;
IsSpeedColumns[i] = 0;
IsTypeColumns[i] = 0;
IsRegColumns[i] = 0;
DetectedColumns[i] = false;
}
// initially set all indices already found
PropertyInfo[] properties = typeof(PlaneFeedPluginSettings).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo p in properties)
{
// find all index properties
if (p.Name.StartsWith("Index_") && (p.PropertyType == typeof(int)))
{
int index = (int)p.GetValue(Settings, null);
// mark detected column as true
if ((index >= 0) && (index < DetectedColumns.Length))
DetectedColumns[index] = true;
}
}
// get min/max info per column
GetColumnInfo();
}
[System.Diagnostics.DebuggerNonUserCode]
private string ReadPropertyString(dynamic o, string propertyname)
{
string s = null;
try
{
s = o[propertyname];
}
catch
{
}
return s;
}
[System.Diagnostics.DebuggerNonUserCode]
private int ReadPropertyInt(dynamic o, string propertyname)
{
int i = int.MinValue;
double d = ReadPropertyDouble(o, propertyname);
if ((d != double.MinValue) && (d >= int.MinValue) && (d <= int.MaxValue))
i = (int)d;
return i;
}
[System.Diagnostics.DebuggerNonUserCode]
private double ReadPropertyDouble(dynamic o, string propertyname)
{
double d = double.MinValue;
try
{
string s = o[propertyname].ToString(CultureInfo.InvariantCulture);
d = double.Parse(s, CultureInfo.InvariantCulture);
}
catch
{
// do nothing if something went wrong
}
return d;
}
[System.Diagnostics.DebuggerNonUserCode]
private long ReadPropertyLong(dynamic o, string propertyname)
{
long l = long.MinValue;
try
{
l = o[propertyname];
}
catch
{
// do nothing if something went wrong
}
return l;
}
[System.Diagnostics.DebuggerNonUserCode]
private bool ReadPropertyBool(dynamic o, string propertyname)
{
bool b = false;
try
{
string s = o[propertyname].ToString();
b = s.ToLower() == "true";
}
catch
{
// do nothing if something went wrong
}
return b;
}
}
/// <summary>
/// //////////////////////////////////////////// Helpers ////////////////////////////////////////////
/// </summary>
public static class UnitConverter
{
public static double ft_m(double feet)
{
return feet / 3.28084;
}
public static double m_ft(double m)
{
return m * 3.28084;
}
public static double kts_kmh(double kts)
{
return kts * 1.852;
}
public static double kmh_kts(double kmh)
{
return kmh / 1.852;
}
public static double km_mi(double km)
{
return km * 1.609;
}
public static double mi_km(double mi)
{
return mi / 1.609;
}
}
public class VarConverter : Dictionary<string, object>
{
public readonly char VarSeparator = '%';
public void AddVar(string var, object value)
{
// adds a new var<>value pair to dictionary
object o;
if (this.TryGetValue(var, out o))
{
// item found --> update value
o = value;
}
else
{
// item not found --> add new
this.Add(var, value);
}
}
public object GetValue(string var)
{
// finds a var in dictionary and returns its value
object o;
if (this.TryGetValue(var, out o))
{
// item found --> return value
return o;
}
// item not found --> return null
return null;
}
public string ReplaceAllVars(string s)
{
// check for var separotors first
if (s.Contains(VarSeparator))
{
// OK, string is containing vars --> crack the string first and replace vars
try
{
string[] a = s.Split(VarSeparator);
// as we are always using a pair of separators the length of a[] must be odd
if (a.Length % 2 == 0)
throw new ArgumentException("Number of separators is not an even number.");
// create new string and replace all vars (on odd indices)
s = "";
for (int i = 0; i < a.Length; i++)
{
if (i % 2 == 0)
{
// cannot be not a var on that position
s = s + a[i];
}
else
{
// var identifier: upper the string and try to convert
a[i] = a[i].ToUpper();
object o;
if (this.TryGetValue(a[i], out o))
{
// convert floating points with invariant culture info
if (o.GetType() == typeof(double))
s = s + ((double)o).ToString(CultureInfo.InvariantCulture);
else if (o.GetType() == typeof(float))
s = s + ((float)o).ToString(CultureInfo.InvariantCulture);
else
s = s + o.ToString();
}
else
{
throw new ArgumentException("Var identifier not found: " + a[i]);
}
}
}
}
catch (Exception ex)
{
// throw an excecption
throw new ArgumentException("Error while parsing string for variables [" + ex.Message + "]: " + s);
}
}
return s;
}
}
}