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); } /// /// Sets all properties to their default value according to the [DefaultValue=] attribute /// 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); } } } /// /// Loads settings from a XML-formatted configuration file into settings. /// /// If true, ignore the [XmlIgnore] attribute, e.g. load all settings available in the file.
If false, load only settings without [XmlIgore] attrbute.
/// The filename of the settings file. 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); } } /// /// Saves settings from settings into a XML-formatted configuration file /// /// If true, ignore the [XmlIgnore] attribute, e.g. save all settings.
If false, save only settings without [XmlIgore] attrbute. /// The filename of the settings file. 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(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 Elements = new List(); private Dictionary ElementLengths = new Dictionary(); private List PlaneList = new List(); 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; } } /// /// //////////////////////////////////////////// Helpers //////////////////////////////////////////// /// 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 { 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; } } }