You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
16 KiB
349 lines
16 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Configuration; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Net; |
|
using System.Text.RegularExpressions; |
|
using System.Windows.Forms; |
|
using System.Xml; |
|
using ArdupilotMega.Utilities; |
|
using log4net; |
|
|
|
namespace ArdupilotMega.Utilities |
|
{ |
|
public static class ParameterMetaDataParser |
|
{ |
|
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); |
|
|
|
public static void GetParameterInformation() |
|
{ |
|
string parameterLocationsString = ConfigurationManager.AppSettings["ParameterLocations"]; |
|
|
|
if(!String.IsNullOrEmpty(parameterLocationsString)) |
|
{ |
|
var parameterLocations = parameterLocationsString.Split(';').ToList(); |
|
parameterLocations.RemoveAll(String.IsNullOrEmpty); |
|
|
|
string sStartupPath = Application.StartupPath; |
|
using (var objXmlTextWriter = new XmlTextWriter(String.Format("{0}{1}{2}", Application.StartupPath, Path.DirectorySeparatorChar, ConfigurationManager.AppSettings["ParameterMetaDataXMLFileName"]), null)) |
|
{ |
|
objXmlTextWriter.Formatting = Formatting.Indented; |
|
objXmlTextWriter.WriteStartDocument(); |
|
|
|
objXmlTextWriter.WriteStartElement("Params"); |
|
|
|
foreach (string parameterLocation in parameterLocations) |
|
{ |
|
string element = "none"; |
|
|
|
if (parameterLocation.ToLower().Contains("arducopter")) { |
|
element = MainV2.Firmwares.ArduCopter2.ToString(); |
|
} else if (parameterLocation.ToLower().Contains("arduplane")) { |
|
element = MainV2.Firmwares.ArduPlane.ToString(); |
|
} else if (parameterLocation.ToLower().Contains("rover")) { |
|
element = MainV2.Firmwares.ArduRover.ToString(); |
|
} |
|
|
|
// Write the start element for this parameter location |
|
objXmlTextWriter.WriteStartElement(element); |
|
|
|
// Read and parse the content. |
|
string dataFromAddress = ReadDataFromAddress(parameterLocation); |
|
ParseGroupInformation(dataFromAddress, objXmlTextWriter, parameterLocation); |
|
ParseParameterInformation(dataFromAddress, objXmlTextWriter); |
|
|
|
// Write the end element for this parameter location |
|
objXmlTextWriter.WriteEndElement(); |
|
|
|
} |
|
|
|
objXmlTextWriter.WriteEndElement(); |
|
|
|
// Clear the stream |
|
objXmlTextWriter.WriteEndDocument(); |
|
objXmlTextWriter.Flush(); |
|
objXmlTextWriter.Close(); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Parses the group parameter 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 ParseGroupInformation(string fileContents, XmlTextWriter objXmlTextWriter, string parameterLocation) |
|
{ |
|
var parsedInformation = ParseKeyValuePairs(fileContents, ParameterMetaDataConstants.Group); |
|
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 => |
|
{ |
|
log.Info("Process "+ node.Key + " : " + separatedPath); |
|
// 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)) |
|
{ |
|
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(); |
|
|
|
// 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; |
|
|
|
// Get the parts of the original URL |
|
var parameterLocationParts = parameterLocation.Substring(trimHttpPrefixIdx, parameterLocation.Length - trimHttpPrefixIdx).Split(new char[] { '/' }); |
|
|
|
// 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] + "/"; |
|
} |
|
|
|
// 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); |
|
} |
|
} |
|
})); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Parses the parameter information. |
|
/// </summary> |
|
/// <param name="fileContents">The file contents.</param> |
|
/// <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.ParamDelimeter + nodeKey, 0); |
|
|
|
if(indicies.Count > 0) |
|
{ |
|
// Loop through the indicies of the parameter comments found |
|
for(int i = 0; i < indicies.Count; i++) |
|
{ |
|
// 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 stopIdx = (i == indicies.Count - 1) ? fileContents.Length : indicies[i + 1] + 1; |
|
|
|
string subStringToSearch = fileContents.Substring(indicies[i], (stopIdx - indicies[i])); |
|
if(!String.IsNullOrEmpty(subStringToSearch)) |
|
{ |
|
var metaIndicies = new List<int>(); |
|
GetIndexOfMarkers(ref metaIndicies, subStringToSearch, ParameterMetaDataConstants.ParamDelimeter, 0); |
|
|
|
if(metaIndicies.Count > 0) |
|
{ |
|
// This meta param key |
|
var paramNameKey = subStringToSearch.Substring(metaIndicies[0], (metaIndicies[1] - metaIndicies[0])); |
|
|
|
// Match based on the regex defined at the top of this class |
|
Match paramNameKeyMatch = _paramMetaRegex.Match(paramNameKey); |
|
|
|
if (paramNameKeyMatch.Success && paramNameKeyMatch.Groups["MetaKey"].Value == nodeKey) |
|
{ |
|
string key = paramNameKeyMatch.Groups["MetaValue"].Value.Trim(new char[] {' '}); |
|
var metaDict = new Dictionary<string, string>(); |
|
if(!returnDict.ContainsKey(key)) |
|
{ |
|
// 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] + 1; |
|
|
|
// 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); |
|
|
|
// 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[] { ' ' })); |
|
} |
|
} |
|
} |
|
} |
|
if (!returnDict.ContainsKey(key)) |
|
{ |
|
returnDict.Add(key, metaDict); |
|
} |
|
else |
|
{ |
|
log.Error("Duplicate Key " + key); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return returnDict; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the index of param markers. |
|
/// </summary> |
|
/// <param name="indicies">The indicies.</param> |
|
/// <param name="inspectThis">The string to be inspected for a parameter.</param> |
|
/// <param name="delimeter">The delimeter.</param> |
|
/// <param name="prevIdx">The prev idx.</param> |
|
private static void GetIndexOfMarkers(ref List<int> indicies, string inspectThis, string delimeter, int prevIdx) |
|
{ |
|
// Find the index of the start of a parameter comment |
|
int idx = inspectThis.IndexOf(delimeter, prevIdx, StringComparison.InvariantCultureIgnoreCase); |
|
|
|
// If we can't find one we stop here |
|
if(idx != -1) |
|
{ |
|
// Add the index we found |
|
indicies.Add(idx); |
|
|
|
// Move the index after the parameter delimeter |
|
int newIdx = idx + delimeter.Length; |
|
|
|
// If we have more string to inspect |
|
if(newIdx < inspectThis.Length) |
|
{ |
|
// Recursively search for the next index |
|
GetIndexOfMarkers(ref indicies, inspectThis, delimeter, newIdx); |
|
} |
|
} |
|
} |
|
|
|
/// <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); |
|
|
|
// Make sure we don't blow up if the user is not connected or the endpoint is not available |
|
try |
|
{ |
|
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; |
|
} |
|
catch (WebException ex) |
|
{ |
|
log.Error(String.Format("The request to {0} failed.", address), ex); |
|
} |
|
return string.Empty; |
|
} |
|
} |
|
}
|
|
|