PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/Projects/OSRR.Parsing/NR2003Parser.cs

https://github.com/tjeuten/OSRR
C# | 1464 lines | 992 code | 335 blank | 137 comment | 201 complexity | 904e3bf80ba4d9e0304c248cee8ed93d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using OSRR.Core;
  6. using System.IO;
  7. using System.Xml;
  8. using System.Xml.Linq;
  9. using OSRR.Parsing.NR2003;
  10. using OSRR.Parsing.Auxiliary;
  11. using System.Data;
  12. using System.Collections;
  13. using System.Globalization;
  14. using System.Reflection;
  15. namespace OSRR.Parsing
  16. {
  17. public class NR2003Parser : IResultParser
  18. {
  19. #region Class members
  20. private OSRRDocument _osrrDoc;
  21. private string _HTMLFileName;
  22. private string _RAXMLFileName;
  23. #endregion
  24. #region Helper members
  25. private string HTMLFileContents;
  26. private List<string> excludedCarNumbers;
  27. #endregion
  28. #region Properties
  29. /// <summary>
  30. /// Gets the generated OSRR document
  31. /// </summary>
  32. public OSRRDocument OSRRDoc
  33. {
  34. get { return this._osrrDoc; }
  35. }
  36. /// <summary>
  37. /// Gets or sets the filepath and name of the HTML result file
  38. /// </summary>
  39. public string HTMLFileName
  40. {
  41. get { return this._HTMLFileName; }
  42. set { this._HTMLFileName = value; }
  43. }
  44. /// <summary>
  45. /// Gets or sets the filepath and name of the Replay Analyzer XML result file
  46. /// </summary>
  47. public string RAXMLFileName
  48. {
  49. get { return this._RAXMLFileName; }
  50. set { this._RAXMLFileName = value; }
  51. }
  52. #endregion
  53. #region Constructors
  54. /// <summary>
  55. /// Default parameterless constructor
  56. /// </summary>
  57. public NR2003Parser()
  58. {
  59. this.excludedCarNumbers = new List<string>();
  60. this.excludedCarNumbers.Add("-1");
  61. }
  62. /// <summary>
  63. /// Constructor providing the location of the HTML and/or Replay Analyzer XML export result(s). You can specify a parameter as null if not applicable
  64. /// </summary>
  65. /// <param name="htmlFileName">The filepath and name of the HTML result file. Pass null if not applicable</param>
  66. /// <param name="xmlFileName">The filepath and name of the Replay Analyzer XML result file. Pass null if not applicable</param>
  67. /// <param name="serverCarNumber">In combination with the Replay Analyzer XML file, specify the car number of the server. This car will then be excluded from scoring</par
  68. public NR2003Parser(string HTMLFileName, string RAXMLFileName, string serverCarNumber) : this()
  69. {
  70. this._HTMLFileName = HTMLFileName;
  71. this._RAXMLFileName = RAXMLFileName;
  72. if(serverCarNumber != null)
  73. this.excludedCarNumbers.Add(serverCarNumber);
  74. }
  75. #endregion
  76. #region Public methods
  77. /// <summary>
  78. /// Parses the HTML result and/or the Replay Analyzer XML result
  79. /// </summary>
  80. /// <returns>A OSRR object containing the results, combined from HTML and XML if both were specified</returns>
  81. public OSRRDocument Parse()
  82. {
  83. //PARSE THE HTML RESULT
  84. NR2003HTMLResult htmlResult = this.ParseHTML();
  85. //LOAD THE RA XML RESULT
  86. XDocument RAXML = this.LoadRAXML();
  87. //CREATE THE EVENTS
  88. Event evHtml = this.CreateEventFromHTMLResult(htmlResult);
  89. Event evRAXml = this.CreateEventFromXMLResult(RAXML);
  90. //FOR TESTING, SERIALIZE
  91. //if (evHtml != null)
  92. // evHtml.SaveToFile(@"D:\Test\OSRR_Event_HTML.xml");
  93. //if (evRAXml != null)
  94. // evRAXml.SaveToFile(@"D:\Test\OSRR_Event_RA.xml");
  95. //ASSIGN THE EVENT TO THE OSRR DOCUMENT
  96. this._osrrDoc = new OSRRDocument();
  97. this._osrrDoc.Event = this.CombineEvents(evHtml, evRAXml);
  98. return this._osrrDoc;
  99. }
  100. /// <summary>
  101. /// Adds a carNumber to the list of carnumbers to be excluded from scoring
  102. /// </summary>
  103. /// <param name="carNumber">The carNumber to exclude</param>
  104. public void AddExcludedCarNumber(string carNumber)
  105. {
  106. if (this.excludedCarNumbers != null && !this.excludedCarNumbers.Contains(carNumber))
  107. {
  108. this.excludedCarNumbers.Add(carNumber);
  109. }
  110. }
  111. #endregion
  112. #region Private Methods
  113. /// <summary>
  114. /// This method is primarily used to combine the Event objects of HTML output and RA XML output together. In normal circumstances, the HTML file should take priority over the RA XML file.
  115. /// </summary>
  116. /// <param name="primary">This Event will serve as base for the combination. This is in most cases the Event generated from the HTML output</param>
  117. /// <param name="secondary">This Event will be used to provide additional information to the primary Event. This is in most cases the Event generated from the RA XML output</param>
  118. /// <returns></returns>
  119. private Event CombineEvents(Event primary, Event secondary)
  120. {
  121. Event returnEvent = null;
  122. if (primary != null && secondary != null)
  123. {
  124. returnEvent = Event.Deserialize(primary.Serialize());
  125. this.CombineObjects(returnEvent, secondary);
  126. //COMBINE TIMEDSESSIONS
  127. if (returnEvent.TimedSessions != null && secondary.TimedSessions != null)
  128. {
  129. //MERGE SAME SESSIONS
  130. foreach(Core.TimedSession timedSession in returnEvent.TimedSessions)
  131. {
  132. var q = from s in secondary.TimedSessions
  133. where s.Name.Equals(timedSession.Name)
  134. select s;
  135. if(q.Count() > 0)
  136. {
  137. Core.TimedSession secondaryTimedSession = q.First();
  138. this.CombineObjects(timedSession, secondaryTimedSession);
  139. //MERGE SAME SESSIONRESULTS
  140. foreach (Core.TimedSessionResult timedSessionResult in timedSession.Results)
  141. {
  142. var q2 = from sr in secondaryTimedSession.Results
  143. where sr.CarNumber.Equals(timedSessionResult.CarNumber)
  144. select sr;
  145. if (q2.Count() > 0)
  146. {
  147. Core.TimedSessionResult secondaryTimedSessionResult = q2.First();
  148. this.CombineObjects(timedSessionResult, secondaryTimedSessionResult);
  149. Driver driver = timedSessionResult.Drivers[0];
  150. Driver secondaryDriver = secondaryTimedSessionResult.Drivers[0];
  151. if (secondaryDriver.LastName.Contains(driver.LastName) || driver.LastName.Contains(secondaryDriver.LastName))
  152. {
  153. if (secondaryDriver.FirstName.StartsWith(driver.FirstName))
  154. {
  155. driver.FirstName = secondaryDriver.FirstName;
  156. }
  157. }
  158. }
  159. }
  160. //ADD SESSIONRESULTS THAT EXIST IN SECONDARY BUT NOT IN PRIMARY
  161. var q3 = from sr in secondaryTimedSession.Results
  162. where timedSession.Results.Find(res => res.CarNumber == sr.CarNumber) == null
  163. orderby sr.Position ascending
  164. select sr;
  165. foreach (Core.TimedSessionResult res in q3)
  166. {
  167. timedSession.Results.Add(res);
  168. }
  169. }
  170. }
  171. //ADD SESSIONS FROM SECONDARY THAT DO NOT EXIST IN PRIMARY
  172. //todo: to be determined if necessary
  173. }
  174. //COMBINE RACESESSIONS
  175. if (returnEvent.RaceSessions != null && secondary.RaceSessions != null)
  176. {
  177. //MERGE SAME SESSIONS
  178. foreach (Core.RaceSession raceSession in returnEvent.RaceSessions)
  179. {
  180. var q = from s in secondary.RaceSessions
  181. where s.Name.Equals(raceSession.Name)
  182. select s;
  183. if (q.Count() > 0)
  184. {
  185. Core.RaceSession secondaryRaceSession = q.First();
  186. this.CombineObjects(raceSession, secondaryRaceSession);
  187. //MERGE SAME SESSIONRESULTS
  188. foreach (Core.RaceSessionResult raceSessionResult in raceSession.Results)
  189. {
  190. var q2 = from sr in secondaryRaceSession.Results
  191. where sr.CarNumber.Equals(raceSessionResult.CarNumber)
  192. select sr;
  193. if (q2.Count() > 0)
  194. {
  195. Core.RaceSessionResult secondaryRaceSessionResult = q2.First();
  196. this.CombineObjects(raceSessionResult, secondaryRaceSessionResult);
  197. Driver driver = raceSessionResult.Drivers[0];
  198. Driver secondaryDriver = secondaryRaceSessionResult.Drivers[0];
  199. if (secondaryDriver.LastName.Contains(driver.LastName) || driver.LastName.Contains(secondaryDriver.LastName))
  200. {
  201. if (secondaryDriver.FirstName.StartsWith(driver.FirstName))
  202. {
  203. driver.FirstName = secondaryDriver.FirstName;
  204. }
  205. }
  206. }
  207. }
  208. //ADD SESSIONRESULTS THAT EXIST IN SECONDARY BUT NOT IN PRIMARY
  209. var q3 = from sr in secondaryRaceSession.Results
  210. where raceSession.Results.Find(res => res.CarNumber == sr.CarNumber) == null
  211. orderby sr.Position ascending
  212. select sr;
  213. foreach (Core.RaceSessionResult res in q3)
  214. {
  215. raceSession.Results.Add(res);
  216. }
  217. }
  218. }
  219. //ADD SESSIONS FROM SECONDARY THAT DO NOT EXIST IN PRIMARY
  220. //todo: to be determined if necessary
  221. }
  222. }
  223. return returnEvent;
  224. }
  225. /// <summary>
  226. /// Normalizes the positions in a list of SessionResult object. This is to avoid "gaps" in the position sequence, induced by for instance excluding certain carnumbers from scoring
  227. /// </summary>
  228. /// <typeparam name="T">A type inheriting of OSRR.Core.SessionResult (either a TimedSessionResult or a RaceSessionResult)</typeparam>
  229. /// <param name="sessionResults">The list of SessionResult objects to normalize</param>
  230. private void NormalizePositions<T>(List<T> sessionResults) where T : Core.SessionResult
  231. {
  232. if (sessionResults != null)
  233. {
  234. sessionResults.OrderBy(res => res.Position);
  235. int pos = 1;
  236. foreach (Core.SessionResult result in sessionResults)
  237. {
  238. result.Position = pos;
  239. pos++;
  240. }
  241. }
  242. }
  243. /// <summary>
  244. /// A general method used to combine the properties of two objects, using reflection. This method is mainly called from the CombineEvents method.
  245. /// The target's property value will be set from the source's, only if the target's is null
  246. /// </summary>
  247. /// <typeparam name="T">Can be any class</typeparam>
  248. /// <param name="target">The target object to modify</param>
  249. /// <param name="source">The source object to take data from</param>
  250. private void CombineObjects<T>(T target, T source) where T : class
  251. {
  252. if (target != null & source != null)
  253. {
  254. Type objectType = target.GetType();
  255. foreach (PropertyInfo pi in objectType.GetProperties())
  256. {
  257. Console.WriteLine(objectType.Name + " - " + pi.Name + " - " + pi.PropertyType.FullName);
  258. try
  259. {
  260. if (pi.GetValue(target, null) == null)
  261. {
  262. Console.WriteLine("Setting " + pi.Name + " to " + pi.GetValue(source, null).ToString());
  263. pi.SetValue(target, pi.GetValue(source, null), null);
  264. //For correctly serializing to XML, we need to set any boolean property with this propertyName + "Specified" to true
  265. PropertyInfo piSpecified = objectType.GetProperty(pi.Name + "Specified", typeof(bool));
  266. if (piSpecified != null)
  267. {
  268. if (pi.GetValue(source, null) != null)
  269. {
  270. Console.WriteLine("Setting " + piSpecified.Name + " to " + piSpecified.GetValue(source, null).ToString());
  271. piSpecified.SetValue(target, piSpecified.GetValue(source, null), null);
  272. }
  273. }
  274. }
  275. else if (!pi.PropertyType.IsValueType)
  276. {
  277. this.CombineObjects(pi.GetValue(target, null), pi.GetValue(source, null));
  278. }
  279. }
  280. catch { }
  281. }
  282. }
  283. }
  284. /// <summary>
  285. /// Creates an OSRR Event object from a given NR2003HTMLResult
  286. /// </summary>
  287. /// <param name="htmlResult">The NR2003HTMLResult object to be processed</param>
  288. /// <returns>An OSRR Event based on the NR2003HTMLResult object</returns>
  289. private Event CreateEventFromHTMLResult(NR2003HTMLResult htmlResult)
  290. {
  291. Event ev = new Event();
  292. ev.IsMultiClass = false;
  293. ev.IsMultiDriver = false;
  294. ev.SpeedUnitSpecified = true;
  295. ev.SpeedUnit = SpeedUnit.mph;
  296. //HOLD ALL DIFFERENT DRIVERS IN A SEPARATE DICTIONARY. THE KEY OF THE DICTIONARY SHOULD BE SOMETHING WE CAN USE TO UNIQUELY IDENTIFY A DRIVER IN THE CURRENT CONTEXT.
  297. //IN THE CASE OF NR2003, THIS IS THE CARNUMBER, WHICH IS A STRING.
  298. Dictionary<string, Core.Driver> drivers = new Dictionary<string, Driver>();
  299. if (htmlResult != null)
  300. {
  301. ev.Schedule = new Schedule();
  302. ev.Schedule.BeginDateSpecified = true;
  303. ev.Schedule.BeginDate = htmlResult.General.Date;
  304. ev.Track = new Track();
  305. ev.Track.Name = htmlResult.General.Track;
  306. //PROCESS THE TIMEDSESSIONS
  307. Dictionary<string, NR2003.TimedSession> nr2003TimedSessions = new Dictionary<string, NR2003.TimedSession>();
  308. nr2003TimedSessions.Add("Practice", htmlResult.PracticeSession);
  309. nr2003TimedSessions.Add("Qualifying", htmlResult.QualifyingSession);
  310. nr2003TimedSessions.Add("Happy Hour", htmlResult.HappyHourSession);
  311. foreach (KeyValuePair<string, NR2003.TimedSession> nr2003TimedSessionKVP in nr2003TimedSessions)
  312. {
  313. NR2003.TimedSession nr2003TimedSession = nr2003TimedSessionKVP.Value;
  314. if (nr2003TimedSession != null)
  315. {
  316. Core.TimedSession timedSession = null;
  317. timedSession = new Core.TimedSession();
  318. if (ev.TimedSessions == null)
  319. ev.TimedSessions = new List<Core.TimedSession>();
  320. timedSession.Name = nr2003TimedSessionKVP.Key;
  321. timedSession.Schedule = ev.Schedule;
  322. timedSession.Weather = new Core.Weather();
  323. if (nr2003TimedSession.Weather != null)
  324. {
  325. timedSession.Weather.Conditions = nr2003TimedSession.Weather.Conditions;
  326. timedSession.Weather.Temperature = nr2003TimedSession.Weather.Temperature;
  327. timedSession.Weather.WindDirection = nr2003TimedSession.Weather.WindDirection;
  328. timedSession.Weather.WindSpeed = nr2003TimedSession.Weather.WindSpeed;
  329. }
  330. if (nr2003TimedSession.Results != null)
  331. {
  332. if (timedSession.Results == null)
  333. timedSession.Results = new List<Core.TimedSessionResult>();
  334. foreach (NR2003.TimedSessionResult nr2003Result in nr2003TimedSession.Results)
  335. {
  336. if (!this.excludedCarNumbers.Contains(nr2003Result.CarNumber))
  337. {
  338. Driver driver;
  339. if (!drivers.Keys.Contains(nr2003Result.CarNumber)) //CREATE THE DRIVER IF ITS CARNUMBER DOES NOT YET EXIST IN THE DICTIONARY
  340. {
  341. driver = new Driver();
  342. int driverSpacePosition = nr2003Result.Driver.IndexOf(" "); //THE FIRST SPACE SPLITS THE FIRSTNAME FROM THE LASTNAME
  343. if (driverSpacePosition == -1) //NO SPACE, SO PUT EVERYTING IN THE LASTNAME
  344. {
  345. driver.LastName = nr2003Result.Driver.Trim();
  346. }
  347. else
  348. {
  349. driver.FirstName = nr2003Result.Driver.Substring(0, driverSpacePosition).Trim();
  350. driver.LastName = nr2003Result.Driver.Substring(driverSpacePosition + 1).Trim();
  351. }
  352. drivers.Add(nr2003Result.CarNumber, driver);
  353. }
  354. else //IF THE CARNUMBER ALREADY EXISTS IN THE DICTIONARY, RETRIEVE THE DRIVER FROM THERE
  355. {
  356. driver = drivers[nr2003Result.CarNumber];
  357. }
  358. Core.TimedSessionResult result = new Core.TimedSessionResult();
  359. result.Drivers = new List<Driver>();
  360. result.Drivers.Add(driver);
  361. result.Position = nr2003Result.Position;
  362. result.CarNumber = nr2003Result.CarNumber;
  363. if (nr2003Result.Time.HasValue)
  364. {
  365. result.BestLapTimeSpecified = true;
  366. result.BestLapTime = nr2003Result.Time;
  367. }
  368. if (nr2003Result.Speed.HasValue)
  369. {
  370. result.BestLapSpeedSpecified = true;
  371. result.BestLapSpeed = nr2003Result.Speed;
  372. }
  373. timedSession.Results.Add(result);
  374. }
  375. }
  376. }
  377. this.NormalizePositions(timedSession.Results);
  378. ev.TimedSessions.Add(timedSession);
  379. }
  380. }
  381. //PROCESS THE RACESESSION
  382. NR2003.RaceSession nr2003RaceSession = htmlResult.RaceSession;
  383. if (nr2003RaceSession != null)
  384. {
  385. Core.RaceSession raceSession = null;
  386. raceSession = new Core.RaceSession();
  387. if (ev.RaceSessions == null)
  388. ev.RaceSessions = new List<Core.RaceSession>();
  389. raceSession.Name = "Race";
  390. raceSession.Schedule = ev.Schedule;
  391. raceSession.Weather = new Core.Weather();
  392. if (nr2003RaceSession.Weather != null)
  393. {
  394. raceSession.Weather.Conditions = nr2003RaceSession.Weather.Conditions;
  395. raceSession.Weather.Temperature = nr2003RaceSession.Weather.Temperature;
  396. raceSession.Weather.WindDirection = nr2003RaceSession.Weather.WindDirection;
  397. raceSession.Weather.WindSpeed = nr2003RaceSession.Weather.WindSpeed;
  398. }
  399. if (nr2003RaceSession.CautionFlags.HasValue)
  400. {
  401. raceSession.CautionsSpecified = true;
  402. raceSession.Cautions = nr2003RaceSession.CautionFlags;
  403. }
  404. if (nr2003RaceSession.CautionLaps.HasValue)
  405. {
  406. raceSession.CautionLapsSpecified = true;
  407. raceSession.CautionLaps = nr2003RaceSession.CautionLaps;
  408. }
  409. if (nr2003RaceSession.LeadChanges.HasValue)
  410. {
  411. raceSession.LeadChangesSpecified = true;
  412. raceSession.LeadChanges = nr2003RaceSession.LeadChanges;
  413. }
  414. if (nr2003RaceSession.LeadDrivers.HasValue)
  415. {
  416. raceSession.LeadDriversSpecified = true;
  417. raceSession.LeadDrivers = nr2003RaceSession.LeadDrivers;
  418. }
  419. if (nr2003RaceSession.Results != null)
  420. {
  421. if (raceSession.Results == null)
  422. raceSession.Results = new List<Core.RaceSessionResult>();
  423. foreach (NR2003.RaceSessionResult nr2003Result in nr2003RaceSession.Results)
  424. {
  425. if (!this.excludedCarNumbers.Contains(nr2003Result.CarNumber))
  426. {
  427. Driver driver;
  428. if (!drivers.Keys.Contains(nr2003Result.CarNumber)) //CREATE THE DRIVER IF ITS CARNUMBER DOES NOT YET EXIST IN THE DICTIONARY
  429. {
  430. driver = new Driver();
  431. int driverSpacePosition = nr2003Result.Driver.IndexOf(" "); //THE FIRST SPACE SPLITS THE FIRSTNAME FROM THE LASTNAME
  432. if (driverSpacePosition == -1) //NO SPACE, SO PUT EVERYTING IN THE LASTNAME
  433. {
  434. driver.LastName = nr2003Result.Driver.Trim();
  435. }
  436. else
  437. {
  438. driver.FirstName = nr2003Result.Driver.Substring(0, driverSpacePosition).Trim();
  439. driver.LastName = nr2003Result.Driver.Substring(driverSpacePosition + 1).Trim();
  440. }
  441. drivers.Add(nr2003Result.CarNumber, driver);
  442. }
  443. else //IF THE CARNUMBER ALREADY EXISTS IN THE DICTIONARY, RETRIEVE THE DRIVER FROM THERE
  444. {
  445. driver = drivers[nr2003Result.CarNumber];
  446. }
  447. Core.RaceSessionResult result = new Core.RaceSessionResult();
  448. result.Drivers = new List<Driver>();
  449. result.Drivers.Add(driver);
  450. result.Position = nr2003Result.Position;
  451. result.CarNumber = nr2003Result.CarNumber;
  452. result.Status = nr2003Result.Status;
  453. if (result.Status.Equals("Running"))
  454. result.IsRunning = true;
  455. result.LapsSpecified = true;
  456. result.Laps = nr2003Result.Laps;
  457. result.LapsLedSpecified = true;
  458. result.LapsLed = nr2003Result.LapsLed;
  459. if (result.Laps.HasValue && result.Laps == 0) //SET RESULTS WITH ZERO LAPS TO NOT RUNNING AND STATUS DNS
  460. {
  461. result.IsRunning = false;
  462. result.Status = "DNS";
  463. }
  464. if (result.Position != 1) //SET INTERVAL TO WINNER
  465. {
  466. decimal interval;
  467. bool intervalParsable = Decimal.TryParse(nr2003Result.Interval.Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator), out interval);
  468. if (intervalParsable)
  469. {
  470. result.IntervalToWinnerSpecified = true;
  471. result.IntervalToWinner = Math.Abs(interval);
  472. }
  473. }
  474. else //ON 1ST POSITION, THE AVERAGE SPEED IS INDICATED
  475. {
  476. decimal avgSpeed;
  477. bool avgSpeedParsable = Decimal.TryParse(nr2003Result.Interval.Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator), out avgSpeed);
  478. if (avgSpeedParsable)
  479. {
  480. result.AverageLapSpeedSpecified = true;
  481. result.AverageLapSpeed = avgSpeed;
  482. }
  483. }
  484. //GET THE PENALTIES ASSOCIATED WITH THIS DRIVER
  485. if (htmlResult.Penalties != null)
  486. {
  487. var q = from p in htmlResult.Penalties
  488. where p.CarNumber.Equals(result.CarNumber)
  489. select p;
  490. foreach (NR2003.Penalty nr2003Penalty in q)
  491. {
  492. if (result.Penalties == null)
  493. result.Penalties = new List<Core.Penalty>();
  494. Core.Penalty penalty = new Core.Penalty();
  495. penalty.LapSpecified = true;
  496. penalty.Lap = nr2003Penalty.Lap;
  497. penalty.Infraction = nr2003Penalty.Infraction;
  498. penalty.PenaltyDescription = nr2003Penalty.PenaltyDescription;
  499. result.Penalties.Add(penalty);
  500. }
  501. }
  502. raceSession.Results.Add(result);
  503. }
  504. }
  505. }
  506. this.NormalizePositions(raceSession.Results);
  507. ev.RaceSessions.Add(raceSession);
  508. }
  509. }
  510. return ev;
  511. }
  512. /// <summary>
  513. /// Creates an OSRR Event object from a given Replay Analyzer XML XDocument
  514. /// </summary>
  515. /// <param name="RAXML">The RA XDocument to be processed</param>
  516. /// <returns>An OSRR Event based on the Replay Analyzer XDocument object</returns>
  517. private Event CreateEventFromXMLResult(XDocument RAXML)
  518. {
  519. Event ev = new Event();
  520. ev.IsMultiClass = false;
  521. ev.IsMultiDriver = false;
  522. ev.SpeedUnitSpecified = true;
  523. ev.SpeedUnit = SpeedUnit.mph;
  524. //HOLD ALL DIFFERENT DRIVERS IN A SEPARATE DICTIONARY. THE KEY OF THE DICTIONARY SHOULD BE SOMETHING WE CAN USE TO UNIQUELY IDENTIFY A DRIVER IN THE CURRENT CONTEXT.
  525. //IN THE CASE OF NR2003, THIS IS THE CARNUMBER, WHICH IS A STRING.
  526. Dictionary<string, Core.Driver> drivers = new Dictionary<string, Driver>();
  527. if (RAXML != null)
  528. {
  529. ev.Track = new Track();
  530. ev.Track.Name = RAXML.Root.Element("general").Element("track").Value;
  531. IEnumerable<XElement> qPracticeResults = null;
  532. IEnumerable<XElement> qQualifyingResults = null;
  533. IEnumerable<XElement> qHappyHourResults = null;
  534. IEnumerable<XElement> qRaceResults = null;
  535. try
  536. {
  537. qPracticeResults = from res in RAXML.Root.Element("practice").Elements("practice-time")
  538. select res;
  539. }
  540. catch { }
  541. try
  542. {
  543. qQualifyingResults = from res in RAXML.Root.Element("qualify").Elements("qualify-time")
  544. select res;
  545. }
  546. catch { }
  547. try
  548. {
  549. qHappyHourResults = from res in RAXML.Root.Element("happy-hour").Elements("happy-hour-time")
  550. select res;
  551. }
  552. catch { }
  553. try
  554. {
  555. qRaceResults = from res in RAXML.Root.Element("race").Element("race-results").Elements("race-result")
  556. select res;
  557. }
  558. catch { }
  559. //PROCESS THE SESSIONRESULTS
  560. Dictionary<string, IEnumerable<XElement>> RAXMLSessionResultsList = new Dictionary<string, IEnumerable<XElement>>();
  561. RAXMLSessionResultsList.Add("Practice", qPracticeResults);
  562. RAXMLSessionResultsList.Add("Qualifying", qQualifyingResults);
  563. RAXMLSessionResultsList.Add("Happy Hour", qHappyHourResults);
  564. RAXMLSessionResultsList.Add("Race", qRaceResults);
  565. foreach(KeyValuePair<string, IEnumerable<XElement>> RAXMLSessionResultsKVP in RAXMLSessionResultsList)
  566. {
  567. IEnumerable<XElement> RAXMLSessionResults = RAXMLSessionResultsKVP.Value;
  568. if (RAXMLSessionResults != null && RAXMLSessionResults.Count() > 0)
  569. {
  570. if (!RAXMLSessionResultsKVP.Key.Equals("Race") && ev.TimedSessions == null)
  571. ev.TimedSessions = new List<Core.TimedSession>();
  572. else if (RAXMLSessionResultsKVP.Key.Equals("Race") && ev.RaceSessions == null)
  573. ev.RaceSessions = new List<Core.RaceSession>();
  574. Core.Session session;
  575. if (RAXMLSessionResultsKVP.Key.Equals("Race"))
  576. {
  577. session = new Core.RaceSession();
  578. ((Core.RaceSession)(session)).Results = new List<Core.RaceSessionResult>();
  579. }
  580. else
  581. {
  582. session = new Core.TimedSession();
  583. ((Core.TimedSession)(session)).Results = new List<Core.TimedSessionResult>();
  584. }
  585. session.Name = RAXMLSessionResultsKVP.Key;
  586. foreach (XElement RAXMLSessionResult in RAXMLSessionResults)
  587. {
  588. string carNumber = RAXMLSessionResult.Element("car-number").Value.Trim();
  589. if (!this.excludedCarNumbers.Contains(carNumber))
  590. {
  591. string driverFullName = RAXMLSessionResult.Element("driver").Attribute("name").Value.Trim();
  592. Driver driver;
  593. if (!drivers.Keys.Contains(carNumber)) //CREATE THE DRIVER IF ITS CARNUMBER DOES NOT YET EXIST IN THE DICTIONARY
  594. {
  595. driver = new Driver();
  596. int driverSpacePosition = driverFullName.IndexOf(" "); //THE FIRST SPACE SPLITS THE FIRSTNAME FROM THE LASTNAME
  597. if (driverSpacePosition == -1) //NO SPACE, SO PUT EVERYTING IN THE LASTNAME
  598. {
  599. driver.LastName = driverFullName;
  600. }
  601. else
  602. {
  603. driver.FirstName = driverFullName.Substring(0, driverSpacePosition).Trim();
  604. driver.LastName = driverFullName.Substring(driverSpacePosition + 1).Trim();
  605. }
  606. drivers.Add(carNumber, driver);
  607. }
  608. else //IF THE CARNUMBER ALREADY EXISTS IN THE DICTIONARY, RETRIEVE THE DRIVER FROM THERE
  609. {
  610. driver = drivers[carNumber];
  611. }
  612. int position;
  613. decimal time;
  614. bool timeParsable;
  615. decimal speed;
  616. bool speedParsable;
  617. int laps;
  618. bool lapsParsable;
  619. Int32.TryParse(RAXMLSessionResult.Attribute("position").Value.Trim(), out position);
  620. timeParsable = Decimal.TryParse(RAXMLSessionResult.Element("time").Value.Trim().Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator), out time);
  621. speedParsable = Decimal.TryParse(RAXMLSessionResult.Element("speed").Value.Trim().Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator), out speed);
  622. lapsParsable = Int32.TryParse(RAXMLSessionResult.Element("laps").Value.Trim().Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator), out laps);
  623. Core.SessionResult result;
  624. if (session is Core.TimedSession)
  625. result = new Core.TimedSessionResult();
  626. else
  627. result = new Core.RaceSessionResult();
  628. result.Drivers = new List<Driver>();
  629. result.Drivers.Add(driver);
  630. result.Position = position;
  631. result.CarNumber = carNumber;
  632. if (timeParsable)
  633. {
  634. if (result is Core.TimedSessionResult)
  635. {
  636. result.BestLapTimeSpecified = true;
  637. result.BestLapTime = time;
  638. }
  639. else
  640. {
  641. ((Core.RaceSessionResult)(result)).TotalRaceTimeSpecified = true;
  642. ((Core.RaceSessionResult)(result)).TotalRaceTime = time;
  643. }
  644. }
  645. if (speedParsable)
  646. {
  647. if (result is Core.TimedSessionResult)
  648. {
  649. result.BestLapSpeedSpecified = true;
  650. result.BestLapSpeed = speed;
  651. }
  652. else
  653. {
  654. result.AverageLapSpeedSpecified = true;
  655. result.AverageLapSpeed = speed;
  656. }
  657. }
  658. if (lapsParsable)
  659. {
  660. result.LapsSpecified = true;
  661. result.Laps = laps;
  662. }
  663. if (session is Core.RaceSession)
  664. {
  665. string problem = RAXMLSessionResult.Element("problem").Value.Trim();
  666. if (problem == null || problem.Equals("") || problem.ToLower().Equals("running"))
  667. {
  668. ((Core.RaceSessionResult)(result)).IsRunning = true;
  669. result.Status = "Running";
  670. }
  671. else
  672. {
  673. ((Core.RaceSessionResult)(result)).IsRunning = false;
  674. result.Status = problem;
  675. }
  676. if (result.Laps.HasValue && result.Laps == 0) //SET RESULTS WITH ZERO LAPS TO NOT RUNNING AND STATUS DNS
  677. {
  678. ((Core.RaceSessionResult)(result)).IsRunning = false;
  679. result.Status = "DNS";
  680. }
  681. }
  682. if(session is Core.TimedSession)
  683. ((Core.TimedSession)(session)).Results.Add((Core.TimedSessionResult)(result));
  684. else
  685. ((Core.RaceSession)(session)).Results.Add((Core.RaceSessionResult)(result));
  686. }
  687. }
  688. if (session is Core.TimedSession)
  689. {
  690. this.NormalizePositions(((Core.TimedSession)(session)).Results);
  691. ev.TimedSessions.Add((Core.TimedSession)(session));
  692. }
  693. else
  694. {
  695. this.NormalizePositions(((Core.RaceSession)(session)).Results);
  696. ev.RaceSessions.Add((Core.RaceSession)(session));
  697. }
  698. }
  699. }
  700. }
  701. return ev;
  702. }
  703. /// <summary>
  704. /// Parses the NR2003 exported HTML result file associated with this NR2003Parser object
  705. /// </summary>
  706. /// <returns>A NR2003HTMLResult object</returns>
  707. private NR2003HTMLResult ParseHTML()
  708. {
  709. NR2003HTMLResult nresult = null;
  710. if (this._HTMLFileName != null && File.Exists(this._HTMLFileName))
  711. {
  712. this.HTMLFileContents = File.ReadAllText(this._HTMLFileName, Encoding.UTF8);
  713. if (!this.HTMLFileContents.Contains("NASCAR Racing 2003 Season EXPORTED STANDINGS"))
  714. {
  715. throw new ArgumentException("HTML file is not a valid NR2003 result file");
  716. }
  717. else
  718. {
  719. nresult = new NR2003HTMLResult();
  720. nresult.General = new GeneralInfo();
  721. //string lineBreak = "\n";
  722. //TRACK
  723. int trackStart = this.HTMLFileContents.IndexOf("Track:");
  724. if (trackStart != -1)
  725. {
  726. trackStart += 7;
  727. int trackEnd = this.HTMLFileContents.IndexOf("</", trackStart);
  728. string trackName = this.HTMLFileContents.Substring(trackStart, trackEnd - trackStart).Trim();
  729. nresult.General.Track = trackName;
  730. }
  731. //DATE
  732. int dateStart = this.HTMLFileContents.IndexOf("Date:");
  733. if (dateStart != -1)
  734. {
  735. dateStart += 6;
  736. int dateEnd = this.HTMLFileContents.IndexOf("</", dateStart);
  737. string date = this.HTMLFileContents.Substring(dateStart, dateEnd - dateStart).Trim();
  738. try
  739. {
  740. nresult.General.Date = DateTime.ParseExact(date, "MM/dd/yy", CultureInfo.InvariantCulture);
  741. }
  742. catch (Exception ex)
  743. {
  744. Console.WriteLine("Could not parse date " + date + ": " + ex.ToString());
  745. }
  746. }
  747. //PRACTICE SESSION
  748. int practiceStart = this.HTMLFileContents.IndexOf("Session: Practice");
  749. if (practiceStart != -1)
  750. {
  751. nresult.PracticeSession = new NR2003.TimedSession();
  752. DataTable dtPractice = null;
  753. int practiceTableStart = this.HTMLFileContents.IndexOf("<table", practiceStart, StringComparison.InvariantCultureIgnoreCase);
  754. if (practiceTableStart != -1)
  755. {
  756. int practiceTableEnd = this.HTMLFileContents.IndexOf("</table", practiceTableStart, StringComparison.InvariantCultureIgnoreCase);
  757. if (practiceTableEnd != -1)
  758. {
  759. dtPractice = this.NR2003HTMLTableToDatatable(this.HTMLFileContents.Substring(practiceTableStart, practiceTableEnd - practiceTableStart).Trim());
  760. }
  761. }
  762. //DATATABLE TO SESSIONRESULT
  763. nresult.PracticeSession.Results = this.DataTableToTimedSessionsResults(dtPractice);
  764. //WEATHER
  765. int weatherStart = this.HTMLFileContents.IndexOf("Weather:", practiceStart);
  766. if (weatherStart != -1)
  767. {
  768. weatherStart += 9;
  769. int weatherEnd = this.HTMLFileContents.IndexOf("</h3", weatherStart, StringComparison.InvariantCultureIgnoreCase);
  770. string weather = this.HTMLFileContents.Substring(weatherStart, weatherEnd - weatherStart).Trim();
  771. nresult.PracticeSession.Weather = this.GetWeatherFromString(weather);
  772. }
  773. }
  774. //QUALIFYING SESSION
  775. int qualifyingStart = this.HTMLFileContents.IndexOf("Session: Qualifying");
  776. if (qualifyingStart != -1)
  777. {
  778. nresult.QualifyingSession = new NR2003.TimedSession();
  779. DataTable dtQualifying = null;
  780. int qualifyingTableStart = this.HTMLFileContents.IndexOf("<table", qualifyingStart, StringComparison.InvariantCultureIgnoreCase);
  781. if (qualifyingTableStart != -1)
  782. {
  783. int qualifyingTableEnd = this.HTMLFileContents.IndexOf("</table", qualifyingTableStart, StringComparison.InvariantCultureIgnoreCase);
  784. if (qualifyingTableEnd != -1)
  785. {
  786. dtQualifying = this.NR2003HTMLTableToDatatable(this.HTMLFileContents.Substring(qualifyingTableStart, qualifyingTableEnd - qualifyingTableStart).Trim());
  787. }
  788. }
  789. //DATATABLE TO SESSIONRESULT
  790. nresult.QualifyingSession.Results = this.DataTableToTimedSessionsResults(dtQualifying);
  791. //WEATHER
  792. int weatherStart = this.HTMLFileContents.IndexOf("Weather:", qualifyingStart);
  793. if (weatherStart != -1)
  794. {
  795. weatherStart += 9;
  796. int weatherEnd = this.HTMLFileContents.IndexOf("</h3", weatherStart, StringComparison.InvariantCultureIgnoreCase);
  797. string weather = this.HTMLFileContents.Substring(weatherStart, weatherEnd - weatherStart).Trim();
  798. string[] weatherSplit = weather.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
  799. nresult.QualifyingSession.Weather = this.GetWeatherFromString(weather);
  800. }
  801. }
  802. //HAPPYHOUR SESSION
  803. int happyhourStart = this.HTMLFileContents.IndexOf("Session: Happy Hour");
  804. if (happyhourStart != -1)
  805. {
  806. nresult.HappyHourSession = new NR2003.TimedSession();
  807. DataTable dtHappyHour = null;
  808. int happyhourTableStart = this.HTMLFileContents.IndexOf("<table", happyhourStart, StringComparison.InvariantCultureIgnoreCase);
  809. if (happyhourTableStart != -1)
  810. {
  811. int happyhourTableEnd = this.HTMLFileContents.IndexOf("</table", happyhourTableStart, StringComparison.InvariantCultureIgnoreCase);
  812. if (happyhourTableEnd != -1)
  813. {
  814. dtHappyHour = this.NR2003HTMLTableToDatatable(this.HTMLFileContents.Substring(happyhourTableStart, happyhourTableEnd - happyhourTableStart).Trim());
  815. }
  816. }
  817. //DATATABLE TO SESSIONRESULT
  818. nresult.HappyHourSession.Results = this.DataTableToTimedSessionsResults(dtHappyHour);
  819. //WEATHER
  820. int weatherStart = this.HTMLFileContents.IndexOf("Weather:", happyhourStart);
  821. if (weatherStart != -1)
  822. {
  823. weatherStart += 9;
  824. int weatherEnd = this.HTMLFileContents.IndexOf("</h3", weatherStart, StringComparison.InvariantCultureIgnoreCase);
  825. string weather = this.HTMLFileContents.Substring(weatherStart, weatherEnd - weatherStart).Trim();
  826. string[] weatherSplit = weather.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
  827. nresult.HappyHourSession.Weather = this.GetWeatherFromString(weather);
  828. }
  829. }
  830. //RACE SESSION
  831. int raceStart = this.HTMLFileContents.IndexOf("Session: Race");
  832. if (raceStart != -1)
  833. {
  834. nresult.RaceSession = new NR2003.RaceSession();
  835. DataTable dtRace = null;
  836. int raceTableStart = this.HTMLFileContents.IndexOf("<table", raceStart, StringComparison.InvariantCultureIgnoreCase);
  837. if (raceTableStart != -1)
  838. {
  839. int raceTableEnd = this.HTMLFileContents.IndexOf("</table", raceTableStart, StringComparison.InvariantCultureIgnoreCase);
  840. if (raceTableEnd != -1)
  841. {
  842. dtRace = th

Large files files are truncated, but you can click here to view the full file