/// <summary> /// Gets raw logic analyzer data (edges) and does complete decoding. /// </summary> /// <param name="list">Edge list</param> /// <param name="frequency">Communication frequency (equal to speed, 4Mbps = 4MHz for Mechatrolink-I and 10MHz for M-II)</param> /// <param name="littleEndian">Publicly available bits of docs are confusing on the subject of command body endianess. /// Experience suggests that body bytes on their own are transmitted as big-endian, /// though multibyte data pieces inside the body might be encoded as little-endian.</param> /// <returns></returns> public static EncoderCommunication Parse(SortedList <int, bool> list, int frequency, bool littleEndian = false) { var tempDecoded = HDLCManchesterDecoder.Decode(list, frequency, 0.25); //DataReporter.ReportProgress("Manchester layer decoded..."); var packets = HDLCManchesterDecoder.SeparatePackets(tempDecoded); tempDecoded.Clear(); //Not needed anymore tempDecoded.TrimExcess(); GC.Collect(); DataReporter.ReportProgress("HDLC layer decoded..."); var decoded = new EncoderPacket[packets.Length]; for (int i = 0; i < packets.Length; i++) { byte[] temp = HDLCManchesterDecoder.PackIntoBytes( HDLCManchesterDecoder.DestuffZeroes(packets[i])).Values.ToArray(); if (temp.Length == 0) { continue; } //DataReporter.ReportProgress(string.Format("Packet {0} out of {1} packed...", i + 1, packets.Length)); decoded[i] = EncoderPacket.Parse(temp, packets[i].Keys.First(), littleEndian); //DataReporter.ReportProgress(string.Format("Packet {0} out of {1} decoded...", i + 1, packets.Length)); } DataReporter.ReportProgress("Packets parsed..."); return(new EncoderCommunication(decoded, (int)Math.Round(1E8 / frequency))); }
private static void DoneReportErrors() { if (DataReporter.EnableProgressOutput && ErrorListener.Exceptions.Any()) { Console.WriteLine(Environment.NewLine + "Warnings:"); Console.WriteLine(ErrorListener.ToString()); } DataReporter.ReportProgress("Done." + Environment.NewLine); }
private static bool CallInstance(List <Process> running, ProcessStartInfo psi) { try { running.Add(Process.Start(psi)); DataReporter.ReportProgress("Started processing: " + psi.Arguments); } catch (Exception e) { DataReporter.ReportProgress(string.Format(@"Failed to start process with arguments: {0} Error details: {1}", psi.Arguments, e.ToString())); return(false); } return(true); }
/// <summary> /// Invokes multiple MechatrolinkParser instances /// Spins multiple threads for efficiency (if UseParallelComputation is set to true) /// Tries to use all available cores first, then reduces thread pool size if OutOfMemory exceptions are raised /// Retries to parse files that failed with OutOfMemory exception (once) /// </summary> /// <param name="directory">Input directory (all inner directories are included into file search)</param> /// <param name="limit">Ordinary command line argument: time limit</param> /// <param name="freq">Ordinary command line argument: communication frequency</param> /// <param name="search">Limited to ? and * wildcards, for example "*.txt"</param> /// <param name="flags">String of command line switches that go after limit and frequency, separated with whitespaces</param> /// <returns>Success == true</returns> public static bool ProcessDirectory(string directory, int limit, int freq, string search, string flags) { bool success = true; //Get list of all suitable files string[] files = Directory.GetFiles(directory, search, SearchOption.AllDirectories); files = files.Where(x => x != NameModifier(x)).ToArray(); //Skip reports, in case their extensions coincide with data files string args = string.Format("/c \"{0} \"{{0}}\" {1} {2} {3} > \"{{1}}\"\"", string.Format("\"{0}\"", System.Reflection.Assembly.GetExecutingAssembly().Location), limit, freq, flags); int maxInstances = UseParallelComputation ? Environment.ProcessorCount : 1; List <Process> running = new List <Process>(maxInstances); List <ProcessStartInfo> retry = new List <ProcessStartInfo>(); for (int i = 0; i < files.Length; i += maxInstances) { running.Clear(); for (int j = 0; j < maxInstances; j++) { if (i + j >= files.Length) { break; } if (!CallInstance(running, files[i + j], args)) { success = false; } } WaitForThreadPool(running); foreach (var item in running) { if (item.ExitCode != 0) { DataReporter.ReportProgress( string.Format("Parser process returned an error {0}, arguments: {1}", item.ExitCode, item.StartInfo.Arguments)); if (item.ExitCode == (int)Program.ExitCodes.OutOfMemory && maxInstances > 1) { retry.Add(item.StartInfo); if (maxInstances > 1) { maxInstances = (int)Math.Floor(maxInstances / 2d); //Adaptive parallelism } i += maxInstances; //Compensate for maxInstances change } else { success = false; } } } DataReporter.ReportProgress("Finished processing of the file (or group of files)."); } running.Clear(); if (retry.Any()) { DataReporter.ReportProgress("Retrying to parse files that caused OutOfMemory exceptions..."); } foreach (var item in retry) { if (!CallInstance(running, item)) { success = false; } WaitForThreadPool(running); if (running.First().ExitCode != 0) { success = false; } } DataReporter.ReportProgress("Finished processing of all the files."); return(success); }
public static int Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; if (args.Length < 1) { Console.WriteLine(@"Not enough arguments. Usage: MechatrolinkParser.exe <Exported text file path> <Line/time limit> <Frequency, Hz> [<Options>] Limit and frequency are optional only if <Options> is omitted, defaults: 20000 and 4000000. Use limit = 0 to disable the limit. Options: [-e] = packet body endianess swap (default is 'big-endian'), usually not needed [-s] = silent (switch off progress reports/warnings) [-x] = full exception info (defaults to distinct exception output) [-stm] = transcode the file into LogicSnifferSTM format [-f] = filter output (exclude nonsensical commands, e.g. with Control field equal to neither 01h nor 03h) [-b] = bulk (folder) processing, <file path> argument field becomes <""directory path|search string"">, other argument fields are not changed [-p] = parallel computation in bulk mode [-k] = keep console windows open (Console.ReadKey()) [-i] = invert raw data (to correct receiver polarity) [-pq:X] = set request preamble length to X (defaults to 16) [-ps:X] = set response preamble length to X (defaults to 16) [-n] = encoder mode (sets suitable preamble lengths, enables inversion, changes database and reporter engine - for CC-Link/Mechatrolink encoder communications) [-c:X] = set Kingst LA channel index (i.e. CSV column index) to X (defaults to 1) [-o:X] = start parsing from time offset X (w.r. to 0), units are x10nS [-imax:X] = max request-response delay (defaults to 1000x10nS) [-imin:X] = min request-response delay (defaults to 50x10nS) [-t] = use time limit instead of line limit [-dt] = disable timestamp output (useful for output file comparison during analysis) Returns: OK = 0, CommandLineError = 1, ParserError = 2, TranscoderError = 3, EmptyDataset = 4, BulkProcessingFailed = 5, OutOfMemory = 6 "); Console.ReadLine(); return((int)ExitCodes.CommandLineError); } //DataReporter.ReportProgress("Parsing command line..."); int limit = 20000; int freq = 4000000; int column = 1; int startOffset = int.MinValue; bool swap = false; bool transcode = false; bool invert = false; bool encoder = false; try { args[0] = args[0].Trim('"'); if (args.Length > 1) { limit = int.Parse(args[1]); if (limit == 0) { limit = int.MaxValue; } if (args.Length > 2) { freq = int.Parse(args[2]); } } DataReporter.FilterOutput = args.Contains("-f"); //DataReporter, BulkProcessing and ErrorListener setup goes first DataReporter.EnableProgressOutput = !args.Contains("-s"); DataReporter.DisableTimestamp = args.Contains("-dt"); BulkProcessing.UseParallelComputation = args.Contains("-p"); ErrorListener.PrintOnlyDistinctExceptions = !args.Contains("-x"); if (args.Contains("-b")) //Possible bulk processing goes second { return(BulkMain(args, limit, freq)); } LogicAnalyzerData.UseTimeLimit = args.Contains("-t"); invert = args.Contains("-i"); //And local vars are the last swap = args.Contains("-e"); transcode = args.Contains("-stm"); if (args.Contains("-n")) //Mode switches override on/off switches { //invert = true; encoder = true; HDLCManchesterDecoder.RequestPreambleLength = 15; HDLCManchesterDecoder.ResponsePreambleLength = 4; } //Switches with arguments override mode switches ArgumentHelper("-pq:", args, (int i) => { HDLCManchesterDecoder.RequestPreambleLength = i; }); ArgumentHelper("-ps:", args, (int i) => { HDLCManchesterDecoder.ResponsePreambleLength = i; }); ArgumentHelper("-c:", args, (int i) => { column = i; }); ArgumentHelper("-imin:", args, (int i) => { HDLCManchesterDecoder.MinRequestResponseDelay = i; }); ArgumentHelper("-imax:", args, (int i) => { HDLCManchesterDecoder.MaxRequestResponseDelay = i; }); ArgumentHelper("-o:", args, (int i) => { startOffset = i; }); } catch (Exception e) { Console.WriteLine("Unable to parse command line:"); Console.WriteLine(e.ToString()); return(ReturnHelper(ExitCodes.CommandLineError, args)); } DataReporter.ReportProgress(string.Format("{3} limit: {0}, frequency: {1}, endianess swap: {2}.", limit, freq, swap, LogicAnalyzerData.UseTimeLimit ? "Time" : "Line")); DataReporter.ReportProgress("Loading data..."); LogicAnalyzerData data; try { if (LogicAnalyzerData.DetectFormat(args[0]) == LogicAnalyzerData.Format.LogicSnifferSTM) { data = LogicAnalyzerData.CreateFromLogicSnifferSTM(args[0], limit); } else { data = LogicAnalyzerData.CreateFromKingst(args[0], column, limit); } if (data.Count == 0) { Console.WriteLine("Given current time constraints, the dataset empty!"); return(ReturnHelper(ExitCodes.EmptyDataset, args)); } if (invert) { data = data.Invert(); } data = data.SkipUntil(startOffset); GC.Collect(); DataReporter.ReportProgress("Parsing data..."); if (encoder) { var parsed = EncoderCommunication.Parse(data, freq, swap); DoneReportErrors(); Console.WriteLine(DataReporter.CreateEncoderReportString(parsed)); } else { var parsed = MechatrolinkCommunication.Parse(data, freq, swap); DoneReportErrors(); Console.WriteLine(DataReporter.CreateMechatrolinkReportString(parsed)); } } catch (OutOfMemoryException) { return(ReturnHelper(ExitCodes.OutOfMemory, args)); } catch (Exception e) { Console.WriteLine("Unable to parse the data:"); Console.WriteLine(e.ToString()); return(ReturnHelper(ExitCodes.ParserError, args)); } if (transcode) { try { MakeBackupCopy(args[0]); LogicAnalyzerData.WriteToLogicSnifferSTM(data, args[0]); } catch (Exception e) { Console.WriteLine("Unable to transcode the file:"); Console.WriteLine(e.ToString()); return(ReturnHelper(ExitCodes.TranscoderError, args)); } } return(ReturnHelper(0, args)); }