/// <summary> /// Read AR3 file. See http://confluence.ash.ads.org/display/EHP/Autologic+ar3+route+file+format.+V2 /// </summary> /// <param name="filename">ar3 filename</param> /// <returns>ar3 route</returns> public static Route ReadAr3(string filename) { #region FIELDS // // The distance table contains the relation between LatLng and distance. // Filled during Read Gps markers. Filled with unique latlng and sorted distance. // var distanceTable = new Dictionary <PointLatLng, int>(); var line = string.Empty; var version = string.Empty; var startPoint = new PointLatLng(); var lastDistance = -1; var route = new Route() { FileName = filename }; var random = new Random(); var previousChangeMarker = new ChangeMarker(); errors = new int[5]; // used in unittesting. #endregion using (TextReader reader = new StreamReader(filename)) { while ((line = reader.ReadLine()) != null) { try { var s = line.Split(':', ','); // #region READ HEADER // if (line.StartsWith("Ar3")) { version = s[1]; } else if (line.StartsWith("MachineType")) { route.MachineType = My.GetEnum <MachineTypes>(s[1]); } #endregion // #region READ GPS MARKERS // else if (line.StartsWith("WayPoint[")) { var distance = (int)My.Val(s[3]); if (distance < lastDistance) { My.Log($"{++errors[0]} {line} has descending distance and will be ignored."); } else if (distance == lastDistance) { My.Log($"{++errors[1]} Duplicated line {line}"); } else { var point = Unique(new PointLatLng(My.Val(s[2]), My.Val(s[1])), distance); route.GpsMarkers.Add(new GpsMarker(point)); if (startPoint.IsEmpty) { startPoint = point; latLngFirstNav = Unique(startPoint, 20); // Sort dictionary 4 holten_cityjet.ar3 } } lastDistance = distance; } #endregion // #region READ NAVIGATION MARKERS // else if (line.StartsWith("Instruction[")) { PointLatLng latlng; if (s[1] == "0") // Set first Navigation marker not at distance zero. This is reserved for Change marker. { latlng = latLngFirstNav; route.GpsMarkers.Insert(1, new GpsMarker(latlng)); } else { latlng = FindLatLng(s[1]); } var marker = new NavigationMarker(latlng); if (s[2] == "1007") // Get custom message. { marker.Message = (s[6] != "") ? s[6] : Path.GetFileNameWithoutExtension(s[5]); } else { marker.Message = NavigationMessage(s[2]); } // // Get roundabout index. // marker.RoundAboutIndex = Math.Max((int)My.Val(s[3]), 0); if (marker.Message == "-") { My.Log($"{++errors[2]} {line} Unkown navigation type {s[2]}"); } marker.SoundFile = My.ValidateFilename((s[5] != "") ? s[5] : (marker.Message + ".wav")); // Todo create soundfile. route.NavigationMarkers.Add(marker); } #endregion // #region READ CHANGE MARKERS // else if (line.StartsWith("ChangePoint[")) { var marker = new ChangeMarker(FindLatLng(s[1])); if (route.MachineType == MachineTypes.StreetWasher) { // 1=DistanceFromStart, ActivityState, LeftNozzleIsActive, LeftNozzlePosition, RightNozzleIsActive, RightNozzlePosition, waterpressure,Marked, Message // If this code is changed then modify also methode DisplayOnForm. // // Handle empty strings; use previous marker value. marker.SpreadingOnOff = My.Bool(s[2], previousChangeMarker.SpreadingOnOff); marker.Hopper1OnOff = My.Bool(s[3], previousChangeMarker.Hopper1OnOff); marker.SpreadingWidthLeft = My.Val(s[4], previousChangeMarker.SpreadingWidthLeft); marker.Hopper2OnOff = My.Bool(s[5], previousChangeMarker.Hopper2OnOff); marker.SpreadingWidthRight = My.Val(s[6], previousChangeMarker.SpreadingWidthRight); marker.Dosage = My.Val(s[7], previousChangeMarker.Dosage); // // Set default pressure according spec. // if (marker.Dosage < 5) { marker.Dosage = 5; } } else { // 1=DistanceFromStartInCm, SpreadSprayOnOff, SprayModeOnOff, Max, SecMat,Dosage, WidthLeft, WidthRight, SecDos, WidthLeftSpraying, WidthRightSpraying, CombiPercentage, HopperSelection, Marked, Message marker.SpreadingOnOff = My.Bool(s[2], previousChangeMarker.SpreadingOnOff); marker.SprayingOnOff = My.Bool(s[3], previousChangeMarker.SprayingOnOff); marker.MaxOnOff = My.Bool(s[4], previousChangeMarker.MaxOnOff); marker.SecMatOnOff = My.Bool(s[5], previousChangeMarker.SecMatOnOff); marker.Dosage = My.Val(s[6], previousChangeMarker.Dosage * 100) / 100; marker.SpreadingWidthLeft = My.Val(s[7], previousChangeMarker.SpreadingWidthLeft * 100) / 100; marker.SpreadingWidthRight = My.Val(s[8], previousChangeMarker.SpreadingWidthRight * 100) / 100; marker.DosageLiquid = My.Val(s[9], previousChangeMarker.DosageLiquid * 100) / 100; marker.SprayingWidthLeft = My.Val(s[10], previousChangeMarker.SprayingWidthLeft * 100) / 100; marker.SprayingWidthRight = My.Val(s[11], previousChangeMarker.SprayingWidthRight * 100) / 100; marker.PersentageLiquid = My.Val(s[12], previousChangeMarker.PersentageLiquid); } // // First change marker should have distance 0. // if (route.ChangeMarkers.Count == 0 && s[1] != "0") { route.ChangeMarkers.Add(new ChangeMarker(startPoint)); } route.ChangeMarkers.Add(marker); previousChangeMarker = marker; } #endregion } catch (Exception ee) { Log($"{++errors[3]} Error in {line} {ee.Message} {ee.StackTrace}"); } } // // First change marker should have distance 0. // if (route.ChangeMarkers.Count == 0) { route.ChangeMarkers.Add(new ChangeMarker(startPoint)); } } My.Log("End of requirement analyze."); return(route); // // Make unique LatLng point. See Software Design Document. // PointLatLng Unique(PointLatLng point, int distance) { while (distanceTable.ContainsKey(point)) { var r = random.NextDouble() / 100000; point = new PointLatLng(point.Lat + r, point.Lng + r); } distanceTable.Add(point, distance); return(point); } PointLatLng FindLatLng(string sdistance) { var distance = (int)My.Val(sdistance); var last = default(KeyValuePair <PointLatLng, int>); var idx = 0; foreach (var item in distanceTable) { ++idx; if (item.Value < distance) { last = item; } else if (item.Value == distance) { last = item; break; } else if (item.Value > distance) // No corresponding Gps marker (orphan). { if (last.Key.IsEmpty) { last = item; } if (item.Value - distance < distance - last.Value) { last = item; } if (distance - last.Value < 200) { break; // Don't mark as orphan when marker is within 200 cm of current or previous marker. } if (item.Value - distance < 200) { break; } ++errors[4]; break; } } distanceTable.Remove(last.Key); // Use distance only one's so that both Navigation- and Change marker can't be added to one gps marker. return(last.Key); } }