/// <summary> /// Look recursively for probability (PROBnn) attributes and embed a new evolution object one level deeper for each /// </summary> /// <param name="evolution"></param> /// <param name="remaining_evo"></param> /// <param name="decodedTaf"></param> /// <returns></returns> private string ProbabilityChunkDecoder(Evolution evolution, string chunk, DecodedTaf decodedTaf) { var found = Regex.Matches(chunk, ProbabilityPattern).Cast <Match>().ToList(); if (found.Count < 1) { return(chunk); } var probability = found[0].Groups[1].Value.Trim(); var type = found[0].Groups[2].Value.Trim(); var period = found[0].Groups[3].Value.Trim(); var remaining = found[0].Groups[4].Value.Trim(); if (probability.StartsWith("PROB")) { evolution.Probability = probability; var embeddedEvolutionPeriodArray = period.Split('/'); var embeddedEvolution = new Evolution() { Type = !string.IsNullOrEmpty(type) ? type : Probability, FromDay = Convert.ToInt32(embeddedEvolutionPeriodArray[0].Substring(0, 2)), FromTime = embeddedEvolutionPeriodArray[0].Substring(2, 2) + ":00 UTC", ToDay = Convert.ToInt32(embeddedEvolutionPeriodArray[1].Substring(0, 2)), ToTime = embeddedEvolutionPeriodArray[1].Substring(2, 2) + ":00 UTC", }; evolution.Evolutions.Add(embeddedEvolution); // recurse on the remaining chunk to extract the weather elements it contains chunk = ParseEntitiesChunk(evolution, remaining, decodedTaf); } return(string.Empty); }
private void AddEvolution(DecodedTaf decodedTaf, Evolution evolution, Dictionary <string, object> result, string entityName) { // clone the evolution entity var newEvolution = evolution.Clone() as Evolution; // add the new entity to it newEvolution.Entity = result[entityName]; if (entityName == VisibilityChunkDecoder.VisibilityParameterName && _withCavok) { newEvolution.Cavok = true; } // get the original entity from the decoded taf or a new one decoded taf doesn't contain it yet var decodedEntity = typeof(DecodedTaf).GetProperty(entityName).GetValue(decodedTaf) as AbstractEntity; if (decodedEntity == null || entityName == CloudChunkDecoder.CloudsParameterName || entityName == WeatherChunkDecoder.WeatherPhenomenonParameterName) { // that entity is not in the decoded_taf yet, or it's a cloud layer which is a special case decodedEntity = InstantiateEntity(entityName); } // add the new evolution to that entity decodedEntity.Evolutions.Add(newEvolution); // update the decoded taf's entity or add the new one to it switch (entityName) { case CloudChunkDecoder.CloudsParameterName: decodedTaf.Clouds.Add(decodedEntity as CloudLayer); break; case WeatherChunkDecoder.WeatherPhenomenonParameterName: decodedTaf.WeatherPhenomenons.Add(decodedEntity as WeatherPhenomenon); break; case VisibilityChunkDecoder.VisibilityParameterName: decodedTaf.Visibility = decodedEntity as Visibility; break; case SurfaceWindChunkDecoder.SurfaceWindParameterName: decodedTaf.SurfaceWind = decodedEntity as SurfaceWind; break; case TemperatureChunkDecoder.MaximumTemperatureParameterName: decodedTaf.MaximumTemperature = decodedEntity as Temperature; break; case TemperatureChunkDecoder.MinimumTemperatureParameterName: decodedTaf.MinimumTemperature = decodedEntity as Temperature; break; default: throw new TafChunkDecoderException(TafChunkDecoderException.Messages.UnknownEntity + decodedEntity.ToString()); } }
public void TestParseErrors(Tuple <string, Type, string> source) { // launch decoding DecodedTaf decodedTaf = TafDecoder.ParseNotStrict(source.Item1); // check the error triggered Assert.NotNull(decodedTaf); Assert.False(decodedTaf.IsValid, "DecodedTaf should be invalid."); var errors = decodedTaf.DecodingExceptions; Assert.AreEqual(source.Item2, errors.FirstOrDefault().ChunkDecoder.GetType(), "ChunkDecoder type is incorrect."); Assert.AreEqual(source.Item3, errors.FirstOrDefault().RemainingTaf, "RemainingTaf is incorrect."); decodedTaf.ResetDecodingExceptions(); Assert.AreEqual(0, decodedTaf.DecodingExceptions.Count, "DecodingExceptions should be empty."); }
public void Parse(string remainingTaf, DecodedTaf decodedTaf) { string newRemainingTaf; var found = Consume(remainingTaf, out newRemainingTaf); if (found.Count <= 1) { // the first chunk didn't match anything, so we remove it to avoid an infinite loop Remaining = ConsumeOneChunk(remainingTaf); return; } var evolutionType = found[1].Value.Trim(); var evolutionPeriod = found[2].Value.Trim(); var remaining = found[3].Value; var evolution = new Evolution() { Type = evolutionType }; if (newRemainingTaf.StartsWith("PROB")) { // if the line started with PROBnn it won't have been consumed and we'll find it in remaining evolution.Probability = newRemainingTaf.Trim(); } // period if (evolutionType == "BECMG" || evolutionType == "TEMPO") { var evolutionPeriodArray = evolutionPeriod.Split('/'); evolution.FromDay = Convert.ToInt32(evolutionPeriodArray[0].Substring(0, 2)); evolution.FromTime = evolutionPeriodArray[0].Substring(2, 2) + ":00 UTC"; evolution.ToDay = Convert.ToInt32(evolutionPeriodArray[1].Substring(0, 2)); evolution.ToTime = evolutionPeriodArray[1].Substring(2, 2) + ":00 UTC"; } else { evolution.FromDay = Convert.ToInt32(evolutionPeriod.Substring(0, 2)); evolution.FromTime = evolutionPeriod.Substring(2, 2) + ':' + evolutionPeriod.Substring(4, 2) + " UTC"; } remaining = ParseEntitiesChunk(evolution, remaining, decodedTaf); }
/// <summary> /// Extract the weather elements (surface winds, visibility, etc) between 2 evolution tags (BECMG, TEMPO or FM) /// </summary> /// <param name="evolution"></param> /// <param name="remaining"></param> /// <param name="decodedTaf"></param> /// <returns></returns> private string ParseEntitiesChunk(Evolution evolution, string chunk, DecodedTaf decodedTaf) { // For each value we detect, we'll clone the evolution object, complete the clone, // and add it to the corresponding entity of the decoded taf var remainingEvolutions = chunk; var tries = 0; // call each decoder in the chain and use results to populate the decoded taf foreach (var chunk_decoder in _decoderChain) { try { // we check for probability in each loop, as it can be anywhere remainingEvolutions = ProbabilityChunkDecoder(evolution, remainingEvolutions, decodedTaf); // reset cavok _withCavok = false; // try to parse the chunk with the current chunk decoder var decoded = chunk_decoder.Parse(remainingEvolutions, _withCavok); // map the obtained fields (if any) to a original entity in the decoded_taf var result = decoded[TafDecoder.ResultKey] as Dictionary <string, object>; var entityName = result.Keys.FirstOrDefault(); if (entityName == VisibilityChunkDecoder.CavokParameterName) { if ((bool)result[entityName]) { _withCavok = true; } entityName = VisibilityChunkDecoder.VisibilityParameterName; } var entity = result.Count > 0 ? result[entityName] : null; if (entity == null && entityName != VisibilityChunkDecoder.VisibilityParameterName) { // visibility will be null if cavok is true but we still want to add the evolution throw new TafChunkDecoderException(chunk, remainingEvolutions, TafChunkDecoderException.Messages.WeatherEvolutionBadFormat, this); } if (entityName == TemperatureChunkDecoder.MaximumTemperatureParameterName || entityName == TemperatureChunkDecoder.MinimumTemperatureParameterName) { AddEvolution(decodedTaf, evolution, result, TemperatureChunkDecoder.MaximumTemperatureParameterName); AddEvolution(decodedTaf, evolution, result, TemperatureChunkDecoder.MinimumTemperatureParameterName); } else { AddEvolution(decodedTaf, evolution, result, entityName); } // update remaining evo for the next round remainingEvolutions = (string)decoded[TafDecoder.RemainingTafKey]; } catch (Exception) { if (++tries == _decoderChain.Count) { if (IsStrict) { throw new TafChunkDecoderException(chunk, remainingEvolutions, TafChunkDecoderException.Messages.EvolutionInformationBadFormat, this); } else { // we tried all the chunk decoders on the first chunk and none of them got a match, // so we drop it remainingEvolutions = ConsumeOneChunk(remainingEvolutions); } } } } return(remainingEvolutions); }
/// <summary> /// Decode a full taf string into a complete taf object. /// </summary> /// <param name="rawTaf"></param> /// <returns></returns> public static DecodedTaf ParseWithMode(string rawTaf, bool isStrict = false) { // prepare decoding inputs/outputs: (trim, remove linefeeds and returns, no more than one space) var cleanTaf = rawTaf.Trim(); cleanTaf = Regex.Replace(cleanTaf, "\n+", string.Empty); cleanTaf = Regex.Replace(cleanTaf, "\r+", string.Empty); cleanTaf = Regex.Replace(cleanTaf, "[ ]{2,}", " ") + " "; cleanTaf = cleanTaf.ToUpper(); var remainingTaf = cleanTaf; if (!cleanTaf.Contains("CNL")) { // appending END to it is necessary to detect the last line of evolution // but only when the TAF wasn't cancelled (CNL) remainingTaf = cleanTaf.Trim() + " END"; } else { remainingTaf = cleanTaf; } var decodedTaf = new DecodedTaf(cleanTaf); var withCavok = false; // call each decoder in the chain and use results to populate decoded taf foreach (var chunkDecoder in _decoderChain) { try { // try to parse a chunk with current chunk decoder var decodedData = chunkDecoder.Parse(remainingTaf, withCavok); // map obtained fields (if any) to the final decoded object if (decodedData.ContainsKey(ResultKey) && decodedData[ResultKey] is Dictionary <string, object> ) { var result = decodedData[ResultKey] as Dictionary <string, object>; foreach (var obj in result) { if (obj.Value != null) { typeof(DecodedTaf).GetProperty(obj.Key).SetValue(decodedTaf, obj.Value); } } } // update remaining taf for next round remainingTaf = decodedData[RemainingTafKey] as string; } catch (TafChunkDecoderException tafChunkDecoderException) { // log error in decoded taf and abort decoding if in strict mode decodedTaf.AddDecodingException(tafChunkDecoderException); // abort decoding if strict mode is activated, continue otherwise if (isStrict) { break; } // update remaining taf for next round remainingTaf = tafChunkDecoderException.RemainingTaf; } // hook for CAVOK decoder, keep CAVOK information in memory if (chunkDecoder is VisibilityChunkDecoder) { withCavok = decodedTaf.Cavok; } } // weather evolutions var evolutionDecoder = new EvolutionChunkDecoder(isStrict, withCavok); while (!string.IsNullOrEmpty(remainingTaf) && remainingTaf.Trim() != "END") { evolutionDecoder.Parse(remainingTaf, decodedTaf); remainingTaf = evolutionDecoder.Remaining; } return(decodedTaf); }