using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO.Ports; using System.IO; using System.Text.RegularExpressions; using KMLib; using KMLib.Feature; using KMLib.Geometry; using Core.Geometry; using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Checksums; using ICSharpCode.SharpZipLib.Core; namespace ArdupilotMega { public partial class Log : Form { ICommsSerial comPort; int logcount = 0; serialstatus status = serialstatus.Connecting; byte[] buffer = new byte[4000]; StreamWriter sw; int currentlog = 0; bool threadrun = true; string logfile = ""; int receivedbytes = 0; List flightdata = new List(); //List orientation = new List(); Model runmodel = new Model(); Object thisLock = new Object(); DateTime start = DateTime.Now; public struct Data { public Model model; public string[] ntun; public string[] ctun; } enum serialstatus { Connecting, Createfile, Closefile, Reading, Waiting, Done } public Log() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; // so can update display from another thread } private void Log_Load(object sender, EventArgs e) { status = serialstatus.Connecting; comPort = MainV2.comPort.BaseStream; //comPort.ReceivedBytesThreshold = 50; //comPort.ReadBufferSize = 1024 * 1024; try { comPort.DtrEnable = false; System.Threading.Thread.Sleep(100); comPort.DtrEnable = true; //comPort.Open(); } catch (Exception) { MessageBox.Show("Error opening comport"); } System.Threading.Thread t11 = new System.Threading.Thread(delegate() { DateTime start = DateTime.Now; threadrun = true; while (threadrun) { try { updateDisplay(); System.Threading.Thread.Sleep(10); if (!comPort.IsOpen) break; while (comPort.BytesToRead >= 4) { comPort_DataReceived((object)null, (SerialDataReceivedEventArgs)null); } } catch (Exception ex) { Console.WriteLine("crash in comport reader "+ex.ToString()); } // cant exit unless told to } Console.WriteLine("Comport thread close"); }); t11.Name = "comport reader"; t11.Start(); MainV2.threads.Add(t11); // doesnt seem to work on mac //comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived); } void genchkcombo(int logcount) { MethodInvoker m = delegate() { CHK_logs.Items.Clear(); for (int a = 1; a <= logcount; a++) { CHK_logs.Items.Add(a); } }; try { BeginInvoke(m); } catch { } } void updateDisplay() { if (start.Second != DateTime.Now.Second) { TXT_status.Text = status.ToString() + " " + receivedbytes + " " + comPort.BytesToRead; start = DateTime.Now; } } void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { while (comPort.BytesToRead > 0 && threadrun) { updateDisplay(); string line = ""; comPort.ReadTimeout = 500; try { line = comPort.ReadLine(); //readline(comPort); if (!line.Contains("\n")) line = line + "\n"; } catch { line = comPort.ReadExisting(); //byte[] data = readline(comPort); //line = Encoding.ASCII.GetString(data, 0, data.Length); } receivedbytes += line.Length; //string line = Encoding.ASCII.GetString(data, 0, data.Length); switch (status) { case serialstatus.Connecting: if (line.Contains("reset to FLY") || line.Contains("interactive setup")) { comPort.Write("logs\r"); } if (line.Contains("logs")) { Regex regex2 = new Regex(@"^([0-9]+)", RegexOptions.IgnoreCase); if (regex2.IsMatch(line)) { MatchCollection matchs = regex2.Matches(line); logcount = int.Parse(matchs[0].Groups[0].Value); genchkcombo(logcount); status = serialstatus.Done; } } if (line.Contains("No logs")) { status = serialstatus.Done; } break; case serialstatus.Closefile: sw.Close(); TextReader tr = new StreamReader(logfile); while (tr.Peek() != -1) { processLine(tr.ReadLine()); } tr.Close(); writeKML(logfile + ".kml"); status = serialstatus.Done; break; case serialstatus.Createfile: receivedbytes = 0; Directory.CreateDirectory(Path.GetDirectoryName(Application.ExecutablePath)+ Path.DirectorySeparatorChar +@"logs"); logfile = Path.GetDirectoryName(Application.ExecutablePath) + Path.DirectorySeparatorChar + @"logs" + Path.DirectorySeparatorChar + DateTime.Now.ToString("yyyy-MM-dd hh-mm") + " " + currentlog + ".log"; sw = new StreamWriter(logfile); status = serialstatus.Waiting; lock (thisLock) { TXT_seriallog.Clear(); } //if (line.Contains("Dumping Log")) { status = serialstatus.Reading; } break; case serialstatus.Done: // break; case serialstatus.Reading: if (line.Contains("packets read") || line.Contains("Done") || line.Contains("logs enabled")) { status = serialstatus.Closefile; break; } sw.Write(line); continue; case serialstatus.Waiting: if (line.Contains("Dumping Log") || line.Contains("GPS:") || line.Contains("NTUN:") || line.Contains("CTUN:") || line.Contains("PM:")) { status = serialstatus.Reading; } break; } lock (thisLock) { this.BeginInvoke((System.Threading.ThreadStart)delegate() { Console.Write(line); TXT_seriallog.AppendText(line); // auto scroll if (TXT_seriallog.TextLength >= 10000) { TXT_seriallog.Text = TXT_seriallog.Text.Substring(TXT_seriallog.TextLength / 2); } TXT_seriallog.SelectionStart = TXT_seriallog.Text.Length; TXT_seriallog.ScrollToCaret(); TXT_seriallog.Refresh(); }); } } Console.WriteLine("exit while"); } catch (Exception ex) { MessageBox.Show("Error reading data" + ex.ToString()); } } string lastline = ""; string[] ctunlast = new string[] { "", "", "", "", "", "", "", "", "", "", "", "", "", "" }; string[] ntunlast = new string[] { "", "", "", "", "", "", "", "", "", "", "", "", "", "" }; List cmd = new List(); Point3D oldlastpos = new Point3D(); Point3D lastpos = new Point3D(); private void processLine(string line) { try { line = line.Replace(", ", ","); line = line.Replace(": ", ":"); string[] items = line.Split(',',':'); if (items[0].Contains("CMD")) { if (flightdata.Count == 0) { if (int.Parse(items[2]) <= (int)MAVLink.MAV_CMD.LAST) // wps { PointLatLngAlt temp = new PointLatLngAlt(double.Parse(items[5],new System.Globalization.CultureInfo("en-US")) / 10000000, double.Parse(items[6],new System.Globalization.CultureInfo("en-US")) / 10000000, double.Parse(items[4],new System.Globalization.CultureInfo("en-US")) / 100, items[1].ToString()); cmd.Add(temp); } } } if (items[0].Contains("MOD")) { positionindex++; modelist.Add(""); // i cant be bothered doing this properly modelist.Add(""); modelist[positionindex] = (items[1]); } if (items[0].Contains("GPS") && items[2] == "1" && items[4] != "0" && items[4] != "-1" && lastline != line) // check gps line and fixed status { if (position[positionindex] == null) position[positionindex] = new List(); double alt = double.Parse(items[6], new System.Globalization.CultureInfo("en-US")); if (items.Length == 11 && items[6] == "0.0000") alt = double.Parse(items[7], new System.Globalization.CultureInfo("en-US")); position[positionindex].Add(new Point3D(double.Parse(items[5],new System.Globalization.CultureInfo("en-US")), double.Parse(items[4],new System.Globalization.CultureInfo("en-US")), alt)); oldlastpos = lastpos; lastpos = (position[positionindex][position[positionindex].Count - 1]); lastline = line; } if (items[0].Contains("CTUN")) { ctunlast = items; } if (items[0].Contains("NTUN")) { ntunlast = items; line = "ATT:" + double.Parse(ctunlast[3], new System.Globalization.CultureInfo("en-US")) * 100 + "," + double.Parse(ctunlast[6], new System.Globalization.CultureInfo("en-US")) * 100 + "," + double.Parse(items[1], new System.Globalization.CultureInfo("en-US")) * 100; items = line.Split(',', ':'); } if (items[0].Contains("ATT")) { try { if (lastpos.X != 0 && oldlastpos != lastpos) { Data dat = new Data(); runmodel = new Model(); runmodel.Location.longitude = lastpos.X; runmodel.Location.latitude = lastpos.Y; runmodel.Location.altitude = lastpos.Z; runmodel.Orientation.roll = double.Parse(items[1], new System.Globalization.CultureInfo("en-US")) / -100; runmodel.Orientation.tilt = double.Parse(items[2], new System.Globalization.CultureInfo("en-US")) / -100; runmodel.Orientation.heading = double.Parse(items[3], new System.Globalization.CultureInfo("en-US")) / 100; dat.model = runmodel; dat.ctun = ctunlast; dat.ntun = ntunlast; flightdata.Add(dat); } } catch { } } } catch (Exception) { // if items is to short or parse fails.. ignore } } List modelist = new List(); List[] position = new List[200]; int positionindex = 0; private void writeKML(string filename) { Color[] colours = { Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Blue, Color.Indigo, Color.Violet, Color.Pink }; AltitudeMode altmode = AltitudeMode.absolute; if (MainV2.cs.firmware == MainV2.Firmwares.ArduCopter2) { altmode = AltitudeMode.relativeToGround; // because of sonar, this is both right and wrong. right for sonar, wrong in terms of gps as the land slopes off. } KMLRoot kml = new KMLRoot(); Folder fldr = new Folder("Log"); Style style = new Style(); style.Id = "yellowLineGreenPoly"; style.Add(new LineStyle(HexStringToColor("7f00ffff"), 4)); PolyStyle pstyle = new PolyStyle(); pstyle.Color = HexStringToColor("7f00ff00"); style.Add(pstyle); kml.Document.AddStyle(style); int stylecode = 0xff; int g = -1; foreach (List poslist in position) { g++; if (poslist == null) continue; LineString ls = new LineString(); ls.AltitudeMode = altmode; ls.Extrude = true; //ls.Tessellate = true; Coordinates coords = new Coordinates(); coords.AddRange(poslist); ls.coordinates = coords; Placemark pm = new Placemark(); string mode = ""; if (g < modelist.Count) mode = modelist[g]; pm.name = g + " Flight Path " + mode; pm.styleUrl = "#yellowLineGreenPoly"; pm.LineString = ls; stylecode = colours[g % (colours.Length - 1)].ToArgb(); Style style2 = new Style(); Color color = Color.FromArgb(0xff, (stylecode >> 16) & 0xff,(stylecode >> 8) & 0xff,(stylecode >> 0) & 0xff); Console.WriteLine("colour " + color.ToArgb().ToString("X") + " "+ color.ToKnownColor().ToString()); style2.Add(new LineStyle(color, 4)); pm.AddStyle(style2); fldr.Add(pm); } Folder planes = new Folder(); planes.name = "Planes"; fldr.Add(planes); Folder waypoints = new Folder(); waypoints.name = "Waypoints"; fldr.Add(waypoints); LineString lswp = new LineString(); lswp.AltitudeMode = AltitudeMode.relativeToGround; lswp.Extrude = true; Coordinates coordswp = new Coordinates(); foreach (PointLatLngAlt p1 in cmd) { coordswp.Add(new Point3D(p1.Lng, p1.Lat, p1.Alt)); } lswp.coordinates = coordswp; Placemark pmwp = new Placemark(); pmwp.name = "Waypoints"; //pm.styleUrl = "#yellowLineGreenPoly"; pmwp.LineString = lswp; waypoints.Add(pmwp); int a = 0; int l = -1; Model lastmodel = null; foreach (Data mod in flightdata) { l++; if (mod.model.Location.latitude == 0) continue; if (lastmodel != null) { if (lastmodel.Location.Equals(mod.model.Location)) { continue; } } Placemark pmplane = new Placemark(); pmplane.name = "Plane " + a; pmplane.visibility = false; Model model = mod.model; model.AltitudeMode = altmode; model.Scale.x = 2; model.Scale.y = 2; model.Scale.z = 2; try { pmplane.description = @" Roll: " + model.Orientation.roll + @" Pitch: " + model.Orientation.tilt + @" Yaw: " + model.Orientation.heading + @" WP dist " + mod.ntun[2] + @" tar bear " + mod.ntun[3] + @" nav bear " + mod.ntun[4] + @" alt error " + mod.ntun[5] + @" ]]>"; } catch { } pmplane.Point = new KmlPoint((float)model.Location.longitude, (float)model.Location.latitude, (float)model.Location.altitude); pmplane.Point.AltitudeMode = altmode; Link link = new Link(); link.href = "block_plane_0.dae"; model.Link = link; pmplane.Model = model; planes.Add(pmplane); lastmodel = mod.model; a++; } kml.Document.Add(fldr); kml.Save(filename); // create kmz - aka zip file FileStream fs = File.Open(filename.Replace(".log.kml",".kmz"),FileMode.Create); ZipOutputStream zipStream = new ZipOutputStream(fs); zipStream.SetLevel(9); //0-9, 9 being the highest level of compression zipStream.UseZip64 = UseZip64.Off; // older zipfile // entry 1 string entryName = ZipEntry.CleanName(Path.GetFileName(filename)); // Removes drive from name and fixes slash direction ZipEntry newEntry = new ZipEntry(entryName); newEntry.DateTime = DateTime.Now; zipStream.PutNextEntry(newEntry); // Zip the file in buffered chunks // the "using" will close the stream even if an exception occurs byte[] buffer = new byte[4096]; using (FileStream streamReader = File.OpenRead(filename)) { StreamUtils.Copy(streamReader, zipStream, buffer); } zipStream.CloseEntry(); File.Delete(filename); filename = Path.GetDirectoryName(Application.ExecutablePath) + Path.DirectorySeparatorChar + "block_plane_0.dae"; // entry 2 entryName = ZipEntry.CleanName(Path.GetFileName(filename)); // Removes drive from name and fixes slash direction newEntry = new ZipEntry(entryName); newEntry.DateTime = DateTime.Now; zipStream.PutNextEntry(newEntry); // Zip the file in buffered chunks // the "using" will close the stream even if an exception occurs buffer = new byte[4096]; using (FileStream streamReader = File.OpenRead(filename)) { StreamUtils.Copy(streamReader, zipStream, buffer); } zipStream.CloseEntry(); zipStream.IsStreamOwner = true; // Makes the Close also Close the underlying stream zipStream.Close(); positionindex = 0; modelist.Clear(); flightdata.Clear(); position = new List[200]; cmd.Clear(); } private void Log_FormClosing(object sender, FormClosingEventArgs e) { threadrun = false; System.Threading.Thread.Sleep(500); if (comPort.IsOpen) { //comPort.Close(); } System.Threading.Thread.Sleep(500); } private void CHK_logs_Click(object sender, EventArgs e) { ListBox lb = sender as ListBox; } private void BUT_DLall_Click(object sender, EventArgs e) { if (status == serialstatus.Done) { System.Threading.Thread t11 = new System.Threading.Thread(delegate() { downloadthread(1, logcount); }); t11.Name = "Log Download All thread"; t11.Start(); MainV2.threads.Add(t11); } } private void downloadthread(int startlognum,int endlognum) { for (int a = startlognum; a <= endlognum; a++) { currentlog = a; System.Threading.Thread.Sleep(500); comPort.Write("dump "); System.Threading.Thread.Sleep(100); comPort.Write(a.ToString() + "\r"); status = serialstatus.Createfile; while (status != serialstatus.Done) { System.Threading.Thread.Sleep(100); } } } private void downloadsinglethread() { for (int i = 0; i < CHK_logs.CheckedItems.Count; ++i) { int a = (int)CHK_logs.CheckedItems[i]; { currentlog = a; System.Threading.Thread.Sleep(500); comPort.Write("dump "); System.Threading.Thread.Sleep(100); comPort.Write(a.ToString()+"\r"); status = serialstatus.Createfile; while (status != serialstatus.Done) { System.Threading.Thread.Sleep(100); } } } } private void BUT_DLthese_Click(object sender, EventArgs e) { if (status == serialstatus.Done) { System.Threading.Thread t11 = new System.Threading.Thread(delegate() { downloadsinglethread(); }); t11.Name = "Log download single thread"; t11.Start(); MainV2.threads.Add(t11); } } private void BUT_clearlogs_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(500); comPort.Write("erase\r"); System.Threading.Thread.Sleep(100); status = serialstatus.Done; } private void BUT_redokml_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.Filter = "*.log|*.log"; openFileDialog1.FilterIndex = 2; openFileDialog1.RestoreDirectory = true; openFileDialog1.Multiselect = true; try { openFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath) + Path.DirectorySeparatorChar + @"logs"+ Path.DirectorySeparatorChar ; } catch { } // incase dir doesnt exist if (openFileDialog1.ShowDialog() == DialogResult.OK) { foreach (string logfile in openFileDialog1.FileNames) { TXT_seriallog.AppendText("\n\nProcessing " + logfile + "\n"); this.Refresh(); TextReader tr = new StreamReader(logfile); while (tr.Peek() != -1) { processLine(tr.ReadLine()); } tr.Close(); writeKML(logfile + ".kml"); TXT_seriallog.AppendText("Done\n"); } } } public static Color HexStringToColor(string hexColor) { string hc = (hexColor); if (hc.Length != 8) { // you can choose whether to throw an exception //throw new ArgumentException("hexColor is not exactly 6 digits."); return Color.Empty; } string a = hc.Substring(0, 2); string r = hc.Substring(6, 2); string g = hc.Substring(4, 2); string b = hc.Substring(2, 2); Color color = Color.Empty; try { int ai = Int32.Parse(a, System.Globalization.NumberStyles.HexNumber); int ri = Int32.Parse(r, System.Globalization.NumberStyles.HexNumber); int gi = Int32.Parse(g, System.Globalization.NumberStyles.HexNumber); int bi = Int32.Parse(b, System.Globalization.NumberStyles.HexNumber); color = Color.FromArgb(ai, ri, gi, bi); } catch { // you can choose whether to throw an exception //throw new ArgumentException("Conversion failed."); return Color.Empty; } return color; } private void BUT_firstperson_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.Filter = "*.log|*.log"; openFileDialog1.FilterIndex = 2; openFileDialog1.RestoreDirectory = true; openFileDialog1.Multiselect = true; try { openFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath) + Path.DirectorySeparatorChar + @"logs" + Path.DirectorySeparatorChar; } catch { } // incase dir doesnt exist if (openFileDialog1.ShowDialog() == DialogResult.OK) { foreach (string logfile in openFileDialog1.FileNames) { TXT_seriallog.AppendText("\n\nProcessing " + logfile+"\n"); this.Refresh(); try { TextReader tr = new StreamReader(logfile); while (tr.Peek() != -1) { processLine(tr.ReadLine()); } tr.Close(); } catch (Exception ex) { MessageBox.Show("Error processing log. Is it still downloading? " + ex.Message); continue; } writeKMLFirstPerson(logfile + ".kml"); TXT_seriallog.AppendText("Done\n"); } } } private void writeKMLFirstPerson(string filename) { StreamWriter stream = new StreamWriter(File.Open(filename, FileMode.Create)); System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); string header = "\n Paths Path\n \n "; stream.Write(header); StringBuilder kml = new StringBuilder(); StringBuilder data = new StringBuilder(); double lastlat = 0; double lastlong = 0; int gpspackets = 0; int lastgpspacket = 0; foreach (Data mod in flightdata) { if (mod.model.Location.latitude == 0) continue; gpspackets++; if (lastlat == mod.model.Location.latitude && lastlong == mod.model.Location.longitude) continue; // double speed 0.05 - assumeing 10hz in log file // 1 speed = 0.1 10 / 1 = 0.1 data.Append(@" " + ((gpspackets - lastgpspacket) * 0.1) + @" smooth " + mod.model.Location.longitude.ToString(new System.Globalization.CultureInfo("en-US")) + @" " + mod.model.Location.latitude.ToString(new System.Globalization.CultureInfo("en-US")) + @" " + mod.model.Location.altitude.ToString(new System.Globalization.CultureInfo("en-US")) + @" " + mod.model.Orientation.roll.ToString(new System.Globalization.CultureInfo("en-US")) + @" " + (90 - mod.model.Orientation.tilt).ToString(new System.Globalization.CultureInfo("en-US")) + @" " + mod.model.Orientation.heading.ToString(new System.Globalization.CultureInfo("en-US")) + @" absolute "); lastlat = mod.model.Location.latitude; lastlong = mod.model.Location.longitude; lastgpspacket = gpspackets; } kml.Append(@" Flight Flight Do " + data + @" "); stream.Write(kml.ToString()); stream.Close(); // create kmz - aka zip file FileStream fs = File.Open(filename.Replace(".log.kml", ".kmz"), FileMode.Create); ZipOutputStream zipStream = new ZipOutputStream(fs); zipStream.SetLevel(9); //0-9, 9 being the highest level of compression zipStream.UseZip64 = UseZip64.Off; // older zipfile // entry 1 string entryName = ZipEntry.CleanName(Path.GetFileName(filename)); // Removes drive from name and fixes slash direction ZipEntry newEntry = new ZipEntry(entryName); newEntry.DateTime = DateTime.Now; zipStream.PutNextEntry(newEntry); // Zip the file in buffered chunks // the "using" will close the stream even if an exception occurs byte[] buffer = new byte[4096]; using (FileStream streamReader = File.OpenRead(filename)) { StreamUtils.Copy(streamReader, zipStream, buffer); } zipStream.CloseEntry(); File.Delete(filename); filename = Path.GetDirectoryName(Application.ExecutablePath) + Path.DirectorySeparatorChar + "block_plane_0.dae"; // entry 2 entryName = ZipEntry.CleanName(Path.GetFileName(filename)); // Removes drive from name and fixes slash direction newEntry = new ZipEntry(entryName); newEntry.DateTime = DateTime.Now; zipStream.PutNextEntry(newEntry); // Zip the file in buffered chunks // the "using" will close the stream even if an exception occurs buffer = new byte[4096]; using (FileStream streamReader = File.OpenRead(filename)) { StreamUtils.Copy(streamReader, zipStream, buffer); } zipStream.CloseEntry(); zipStream.IsStreamOwner = true; // Makes the Close also Close the underlying stream zipStream.Close(); positionindex = 0; modelist.Clear(); flightdata.Clear(); position = new List[200]; cmd.Clear(); } } }