diff --git a/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj b/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj index 1e1da846984a0deb0a6269dfe253cacf53019a27..b59c4d3ae9ec3e5d8b2bed08d3335bd05039bdb9 100644 --- a/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj +++ b/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj @@ -226,7 +226,8 @@ <Compile Include="Attributes\DisplayTextAttribute.cs" /> <Compile Include="Attributes\PrivateAttribute.cs" /> <Compile Include="CodeGen.cs" /> - <Compile Include="Constants\ParameterMetaDataConstants.cs" /> + <Compile Include="Utilities\CollectionExtensions.cs" /> + <Compile Include="Utilities\Constants\ParameterMetaDataConstants.cs" /> <Compile Include="Controls\BackstageView\BackstageView.cs"> <SubType>UserControl</SubType> </Compile> diff --git a/Tools/ArdupilotMegaPlanner/Constants/ParameterMetaDataConstants.cs b/Tools/ArdupilotMegaPlanner/Constants/ParameterMetaDataConstants.cs deleted file mode 100644 index 73a226920b9d22142c11a136f3cc30910e3a57cb..0000000000000000000000000000000000000000 --- a/Tools/ArdupilotMegaPlanner/Constants/ParameterMetaDataConstants.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace ArdupilotMega.Constants -{ - public sealed class ParameterMetaDataConstants - { - public const string Delimeter = "@"; - public const string Param = "Param"; - public const string DisplayName = "DisplayName"; - public const string Description = "Description"; - public const string Units = "Units"; - public const string Range = "Range"; - } -} diff --git a/Tools/ArdupilotMegaPlanner/GCSViews/ConfigurationView/ConfigRawParams.cs b/Tools/ArdupilotMegaPlanner/GCSViews/ConfigurationView/ConfigRawParams.cs index 2ee33bf48ac4c85c03387a3e49ab1591135799ab..e49bf42a85da52f2e6959b72f3c6abc85500ea5d 100644 --- a/Tools/ArdupilotMegaPlanner/GCSViews/ConfigurationView/ConfigRawParams.cs +++ b/Tools/ArdupilotMegaPlanner/GCSViews/ConfigurationView/ConfigRawParams.cs @@ -1,20 +1,14 @@ using System; using System.Collections; -using System.Collections.Generic; using System.ComponentModel; -using System.Configuration; using System.Drawing; -using System.Data; using System.IO; -using System.Linq; using System.Text; -using System.Xml.Linq; using System.Windows.Forms; -using ArdupilotMega.Constants; using ArdupilotMega.Utilities; +using ArdupilotMega.Utilities.Constants; using log4net; using ArdupilotMega.Controls.BackstageView; -using ArdupilotMega.Controls; namespace ArdupilotMega.GCSViews.ConfigurationView { diff --git a/Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs b/Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..6b435af4025aabf2f92bf0bf975914ca637f78f5 --- /dev/null +++ b/Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ArdupilotMega.Utilities +{ + public static class CollectionExtensions + { + /// <summary> + /// Performs the specified <paramref name="action"/> on each element of the <paramref name="enumerable"/>. + /// + /// </summary> + /// <param name="enumerable">An enumerable instance. + /// </param><param name="action"/> + public static void ForEach(this IEnumerable enumerable, Action<object> action) + { + foreach (object obj in enumerable) + action(obj); + } + + /// <summary> + /// Performs the specified <paramref name="action"/> on each element of the <paramref name="enumerable"/>. + /// + /// </summary> + /// <param name="enumerable">An enumerable instance. + /// </param><param name="action"/> + public static void ForEach<T>(this IEnumerable enumerable, Action<T> action) + { + foreach (T obj in enumerable) + action(obj); + } + + /// <summary> + /// Performs the specified <paramref name="action"/> on each element of the <paramref name="enumerable"/>. + /// + /// </summary> + /// <typeparam name="T">The type contained in the <paramref name="enumerable"/>. + /// </typeparam><param name="enumerable"/><param name="action"/> + public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) + { + foreach (T obj in enumerable) + action(obj); + } + } +} diff --git a/Tools/ArdupilotMegaPlanner/Utilities/Constants/ParameterMetaDataConstants.cs b/Tools/ArdupilotMegaPlanner/Utilities/Constants/ParameterMetaDataConstants.cs new file mode 100644 index 0000000000000000000000000000000000000000..b8764f9a737d2d170fab3d31f90f0d6eccfc5a0f --- /dev/null +++ b/Tools/ArdupilotMegaPlanner/Utilities/Constants/ParameterMetaDataConstants.cs @@ -0,0 +1,24 @@ +namespace ArdupilotMega.Utilities.Constants +{ + public sealed class ParameterMetaDataConstants + { + #region Markers + + public const string ParamDelimeter = "@"; + public const string PathDelimeter = ","; + public const string Param = "Param"; + public const string Lib = "Lib"; + public const string Path = "Path"; + + #endregion + + #region Meta Keys + + public const string DisplayName = "DisplayName"; + public const string Description = "Description"; + public const string Units = "Units"; + public const string Range = "Range"; + + #endregion + } +} diff --git a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs index a737da4f5397ca983ab3ca956e5c5bd627f99771..1d6882537163edad19e94dd92d1771dc3f585d1b 100644 --- a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs +++ b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs @@ -7,14 +7,15 @@ using System.Net; using System.Text.RegularExpressions; using System.Windows.Forms; using System.Xml; -using ArdupilotMega.Constants; +using ArdupilotMega.Utilities.Constants; using log4net; namespace ArdupilotMega.Utilities { public static class ParameterMetaDataParser { - private static readonly Regex _paramMetaRegex = new Regex(String.Format("{0}(?<MetaKey>[^:]+):(?<MetaValue>.+)", ParameterMetaDataConstants.Delimeter)); + private static readonly Regex _paramMetaRegex = new Regex(String.Format("{0}(?<MetaKey>[^:]+):(?<MetaValue>.+)", ParameterMetaDataConstants.ParamDelimeter)); + private static readonly Regex _parentDirectoryRegex = new Regex("(?<ParentDirectory>[../]*)(?<Path>.+)"); private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); @@ -35,59 +36,85 @@ namespace ArdupilotMega.Utilities foreach (string parameterLocation in parameterLocations) { - log.Info(parameterLocation); + // Write the start element for this parameter location + objXmlTextWriter.WriteStartElement(parameterLocation); - var request = WebRequest.Create(parameterLocation); + // Read and parse the content. + string dataFromAddress = ReadDataFromAddress(parameterLocation); + ParseLibInformation(dataFromAddress, objXmlTextWriter, parameterLocation); + ParseParameterInformation(dataFromAddress, objXmlTextWriter); - // Plenty of timeout - request.Timeout = 10000; + // Write the end element for this parameter location + objXmlTextWriter.WriteEndElement(); - // Set the Method property of the request to GET. - request.Method = "GET"; - - // Get the response. - using (var response = request.GetResponse()) - { - // Display the status. - log.Info(((HttpWebResponse)response).StatusDescription); + } + + // Clear the stream + objXmlTextWriter.WriteEndDocument(); + objXmlTextWriter.Flush(); + objXmlTextWriter.Close(); + } + } + } - // Get the stream containing content returned by the server. - using (var dataStream = response.GetResponseStream()) - { - if (dataStream != null) + /// <summary> + /// Parses the lib information. + /// </summary> + /// <param name="fileContents">The file contents.</param> + /// <param name="objXmlTextWriter">The obj XML text writer.</param> + /// <param name="parameterLocation">The parameter location.</param> + private static void ParseLibInformation(string fileContents, XmlTextWriter objXmlTextWriter, string parameterLocation) + { + var parsedInformation = ParseKeyValuePairs(fileContents, ParameterMetaDataConstants.Lib); + if (parsedInformation != null && parsedInformation.Count > 0) + { + // node is the prefix of the parameter group here + parsedInformation.ForEach(node => + { + // node.Value is a nested dictionary containing the additional meta data + // In this case we are looking for the @Path key + if (node.Value != null && node.Value.Count > 0) + { + // Find the @Path key + node.Value + .Where(meta => meta.Key == ParameterMetaDataConstants.Path) + // We might have multiple paths to inspect, so break them out by the delimeter + .ForEach(path => path.Value.Split(new []{ ParameterMetaDataConstants.PathDelimeter }, StringSplitOptions.None) + .ForEach(separatedPath => { - // Open the stream using a StreamReader for easy access. - using (var reader = new StreamReader(dataStream)) + // Match based on the regex defined at the top of this class + Match pathMatch = _parentDirectoryRegex.Match(separatedPath); + if (pathMatch.Success && pathMatch.Groups["Path"] != null && !String.IsNullOrEmpty(pathMatch.Groups["Path"].Value)) { - // Write the start element for this parameter location - objXmlTextWriter.WriteStartElement(parameterLocation); + if (pathMatch.Groups["ParentDirectory"] != null && !String.IsNullOrEmpty(pathMatch.Groups["ParentDirectory"].Value)) + { + // How many directories from the original path do we have to move + int numberOfParentDirectoryMoves = pathMatch.Groups["ParentDirectory"].Value.Split(new string[] { "../" }, StringSplitOptions.None).Count(); - // Read and parse the content. - ParseParameterInformation(reader.ReadToEnd(), objXmlTextWriter); + // We need to remove the http:// or https:// prefix to build the new URL properly + string httpTest = parameterLocation.Substring(0, 7); + int trimHttpPrefixIdx = httpTest == "http://" ? 7 : 8; - // Write the end element for this parameter location - objXmlTextWriter.WriteEndElement(); + // Get the parts of the original URL + var parameterLocationParts = parameterLocation.Substring(trimHttpPrefixIdx, parameterLocation.Length - trimHttpPrefixIdx).Split(new char[] { '/' }); - // Close the reader - reader.Close(); - } + // Rebuild the new url taking into account the numberOfParentDirectoryMoves + string urlAfterParentDirectory = string.Empty; + for (int i = 0; i < parameterLocationParts.Length && i < parameterLocationParts.Length - numberOfParentDirectoryMoves; i++) + { + urlAfterParentDirectory += parameterLocationParts[i] + "/"; + } - // Close the datastream - dataStream.Close(); - } - } - - // Close the response - response.Close(); - } + // This is the URL of the file we need to parse for comments + string newPath = String.Format("{0}{1}{2}", parameterLocation.Substring(0, trimHttpPrefixIdx), urlAfterParentDirectory, pathMatch.Groups["Path"].Value); + // Parse the param info from the newly constructed URL + ParseParameterInformation(ReadDataFromAddress(newPath), objXmlTextWriter, node.Key); + } + } + })); } - - // Clear the stream - objXmlTextWriter.WriteEndDocument(); - objXmlTextWriter.Flush(); - objXmlTextWriter.Close(); - } + }); } } @@ -98,8 +125,50 @@ namespace ArdupilotMega.Utilities /// <param name="objXmlTextWriter">The obj XML text writer.</param> private static void ParseParameterInformation(string fileContents, XmlTextWriter objXmlTextWriter) { + ParseParameterInformation(fileContents, objXmlTextWriter, string.Empty); + } + + /// <summary> + /// Parses the parameter information. + /// </summary> + /// <param name="fileContents">The file contents.</param> + /// <param name="objXmlTextWriter">The obj XML text writer.</param> + /// <param name="parameterPrefix">The parameter prefix.</param> + private static void ParseParameterInformation(string fileContents, XmlTextWriter objXmlTextWriter, string parameterPrefix) + { + var parsedInformation = ParseKeyValuePairs(fileContents, ParameterMetaDataConstants.Param); + if(parsedInformation != null && parsedInformation.Count > 0) + { + parsedInformation.ForEach(node => + { + objXmlTextWriter.WriteStartElement(String.Format("{0}{1}", parameterPrefix, node.Key)); + if (node.Value != null && node.Value.Count > 0) + { + node.Value.ForEach(meta => + { + // Write the key value pair to XML + objXmlTextWriter.WriteStartElement(meta.Key); + objXmlTextWriter.WriteString(meta.Value); + objXmlTextWriter.WriteEndElement(); + }); + } + objXmlTextWriter.WriteEndElement(); + }); + } + } + + /// <summary> + /// Parses the parameter information. + /// </summary> + /// <param name="fileContents">The file contents.</param> + /// <param name="nodeKey">The node key.</param> + /// <returns></returns> + private static Dictionary<string, Dictionary<string, string>> ParseKeyValuePairs(string fileContents, string nodeKey) + { + var returnDict = new Dictionary<string, Dictionary<string, string>>(); + var indicies = new List<int>(); - GetIndexOfMarkers(ref indicies, fileContents, ParameterMetaDataConstants.Delimeter + ParameterMetaDataConstants.Param, 0); + GetIndexOfMarkers(ref indicies, fileContents, ParameterMetaDataConstants.ParamDelimeter + nodeKey, 0); if(indicies.Count > 0) { @@ -114,7 +183,7 @@ namespace ArdupilotMega.Utilities if(!String.IsNullOrEmpty(subStringToSearch)) { var metaIndicies = new List<int>(); - GetIndexOfMarkers(ref metaIndicies, subStringToSearch, ParameterMetaDataConstants.Delimeter, 0); + GetIndexOfMarkers(ref metaIndicies, subStringToSearch, ParameterMetaDataConstants.ParamDelimeter, 0); if(metaIndicies.Count > 0) { @@ -124,40 +193,43 @@ namespace ArdupilotMega.Utilities // Match based on the regex defined at the top of this class Match paramNameKeyMatch = _paramMetaRegex.Match(paramNameKey); - if (paramNameKeyMatch.Success && paramNameKeyMatch.Groups["MetaKey"].Value == ParameterMetaDataConstants.Param) + if (paramNameKeyMatch.Success && paramNameKeyMatch.Groups["MetaKey"].Value == nodeKey) { - objXmlTextWriter.WriteStartElement(paramNameKeyMatch.Groups["MetaValue"].Value.Trim(new char[] { ' ' })); - - // Loop through the indicies of the meta data found - for (int x = 1; x < metaIndicies.Count; x++) + string key = paramNameKeyMatch.Groups["MetaValue"].Value.Trim(new char[] {' '}); + var metaDict = new Dictionary<string, string>(); + if(!returnDict.ContainsKey(key)) { - // This is the end index for a substring to search for parameter attributes - // If we are on the last index in our collection, we will search to the end of the file - var stopMetaIdx = (x == metaIndicies.Count - 1) ? subStringToSearch.Length : metaIndicies[x + 1]; + // Loop through the indicies of the meta data found + for (int x = 1; x < metaIndicies.Count; x++) + { + // This is the end index for a substring to search for parameter attributes + // If we are on the last index in our collection, we will search to the end of the file + var stopMetaIdx = (x == metaIndicies.Count - 1) ? subStringToSearch.Length : metaIndicies[x + 1]; - // This meta param string - var metaString = subStringToSearch.Substring(metaIndicies[x], (stopMetaIdx - metaIndicies[x])); + // This meta param string + var metaString = subStringToSearch.Substring(metaIndicies[x], (stopMetaIdx - metaIndicies[x])); - // Match based on the regex defined at the top of this class - Match metaMatch = _paramMetaRegex.Match(metaString); + // Match based on the regex defined at the top of this class + Match metaMatch = _paramMetaRegex.Match(metaString); - // Test for success - if (metaMatch.Success) - { - // Write the key value pair to XML - objXmlTextWriter.WriteStartElement(metaMatch.Groups["MetaKey"].Value.Trim(new char[] { ' ' })); - objXmlTextWriter.WriteString(metaMatch.Groups["MetaValue"].Value.Trim(new char[] { ' ' })); - objXmlTextWriter.WriteEndElement(); + // Test for success + if (metaMatch.Success) + { + string metaKey = metaMatch.Groups["MetaKey"].Value.Trim(new char[] {' '}); + if(!metaDict.ContainsKey(metaKey)) + { + metaDict.Add(metaKey, metaMatch.Groups["MetaValue"].Value.Trim(new char[] { ' ' })); + } + } } } - - // End this parameter node - objXmlTextWriter.WriteEndElement(); + returnDict.Add(key, metaDict); } } } } } + return returnDict; } /// <summary> @@ -189,5 +261,58 @@ namespace ArdupilotMega.Utilities } } } + + /// <summary> + /// Reads the data from address. + /// </summary> + /// <param name="address">The address.</param> + /// <returns></returns> + private static string ReadDataFromAddress(string address) + { + string data = string.Empty; + + log.Info(address); + + var request = WebRequest.Create(address); + + // Plenty of timeout + request.Timeout = 10000; + + // Set the Method property of the request to GET. + request.Method = "GET"; + + // Get the response. + using (var response = request.GetResponse()) + { + // Display the status. + log.Info(((HttpWebResponse) response).StatusDescription); + + // Get the stream containing content returned by the server. + using (var dataStream = response.GetResponseStream()) + { + if (dataStream != null) + { + // Open the stream using a StreamReader for easy access. + using (var reader = new StreamReader(dataStream)) + { + // Store the data to return + data = reader.ReadToEnd(); + + // Close the reader + reader.Close(); + } + + // Close the datastream + dataStream.Close(); + } + } + + // Close the response + response.Close(); + } + + // Return the data + return data; + } } }