public static EventReport Parse(double systemFrequency, string[] lines, ref int index) { EventReport eventReport = new EventReport(); int firmwareIndex; // Parse the report header eventReport.Header = Header.Parse(lines, ref index); // Skip to the next nonblank line EventFile.SkipBlanks(lines, ref index); // Get the index of the line where // the firmware information is located firmwareIndex = index; // Parse the firmware and event number eventReport.Firmware = Firmware.Parse(lines, ref firmwareIndex); eventReport.EventNumber = ParseEventNumber(lines, ref index); index = Math.Max(firmwareIndex, index); // Skip to the next nonblank line EventFile.SkipBlanks(lines, ref index); // Parse the analog section of the report eventReport.AnalogSection = AnalogSection.Parse(eventReport.Header.EventTime, systemFrequency, lines, ref index); if (lines.Length < index) { // skip digitals for now while (!lines[index].ToLower().Contains("group settings") && index < lines.Length) { ++index; } ++index; // Parse Group settings eventReport.GroupSetting = Settings.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); // Parse Logic control equations ++index; EventFile.SkipBlanks(lines, ref index); eventReport.ControlEquations = ControlEquation.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); // Parse Global Settings eventReport.GlobalSetting = Settings.Parse(lines, ref index); } return(eventReport); }
private static AnalogSection ParseAnalogSection(DateTime eventTime, string[] lines, ref int index) { const string CycleHeader = @"^\[\d+\]$"; AnalogSection analogSection = new AnalogSection(); int headerLineIndex; int firstDataLineIndex; int dataLineIndex; int triggerLineIndex; string[] headers = null; string[] fields = null; int analogEndIndex = -1; double[] analogs; double analog = 0.0; Cycle <DateTime> currentTimeCycle = null; List <Cycle <double> > currentCycles = null; string currentLine; int sampleCount = 0; int eventSample = -1; int firstCycleCount; long timePerSample; // Scan forward to the first line of data firstDataLineIndex = index; while (firstDataLineIndex < lines.Length) { fields = Regex.Split(lines[firstDataLineIndex], @"\s|>|\*").Where(s => s != string.Empty).ToArray(); if (fields.Length > 0 && double.TryParse(fields[0], out analog)) { break; } firstDataLineIndex++; } if (firstDataLineIndex >= lines.Length) { return(analogSection); } // Scan backward to find the header line headerLineIndex = firstDataLineIndex - 1; while (headerLineIndex >= index) { headers = lines[headerLineIndex].Split((char[])null, StringSplitOptions.RemoveEmptyEntries); if (headers.Length == fields.Length) { break; } headerLineIndex--; } if (headerLineIndex < index) { return(analogSection); } // Scan forward to either the trigger or the // largest current, whichever comes first, // and use it to determine the analogEndIndex triggerLineIndex = firstDataLineIndex; while (firstDataLineIndex < lines.Length) { currentLine = lines[triggerLineIndex++]; // If the current line is empty or it matches the cycle header, skip it if (string.IsNullOrWhiteSpace(currentLine) || Regex.IsMatch(currentLine, CycleHeader)) { continue; } // If the length of the current line is not within one character // of the length of the first data line, assume we have reached // the end of the section and stop scanning lines if (Math.Abs(currentLine.Length - lines[firstDataLineIndex].Length) > 1) { break; } // Check if this is the trigger row analogEndIndex = currentLine.IndexOf('>'); if (analogEndIndex >= 0) { break; } // Check if this is the largest current row analogEndIndex = currentLine.IndexOf('*'); if (analogEndIndex >= 0) { break; } } // If analogEndIndex is valid, parse the header row again if (analogEndIndex >= 0 && analogEndIndex < lines[headerLineIndex].Length) { headers = lines[headerLineIndex].Remove(analogEndIndex).Split((char[])null, StringSplitOptions.RemoveEmptyEntries); } // Generate analog channels from header row analogSection.AnalogChannels = headers .Select(header => new Channel <double>() { Name = header }) .ToList(); // Scan through the lines of data dataLineIndex = firstDataLineIndex; while (dataLineIndex < lines.Length) { currentLine = lines[dataLineIndex++]; // Empty lines or cycle headers indicate start of next cycle if (string.IsNullOrWhiteSpace(currentLine) || Regex.IsMatch(currentLine, CycleHeader)) { // Two empty lines in a row indicates the end of the section if ((object)currentCycles == null) { break; } currentCycles = null; continue; } // If the line does not indicate the start of a cycle, // check the length to make sure we are still in the analog section if (Math.Abs(currentLine.Length - lines[firstDataLineIndex].Length) > 1) { break; } // Parse this line as a line of data if (analogEndIndex >= 0 && analogEndIndex < currentLine.Length) { currentLine = currentLine.Remove(analogEndIndex); } analogs = currentLine .Split((char[])null, StringSplitOptions.RemoveEmptyEntries) .TakeWhile(field => double.TryParse(field, out analog)) .Select(field => analog) .ToArray(); // Remove analog channels whose values cannot be parsed as doubles while (analogSection.AnalogChannels.Count > analogs.Length) { analogSection.AnalogChannels.RemoveAt(analogSection.AnalogChannels.Count - 1); } // Ensure the existence of a list of cycles to hold the analog values if ((object)currentCycles == null) { currentCycles = analogSection.AnalogChannels.Select(channel => new Cycle <double>()).ToList(); for (int i = 0; i < currentCycles.Count; i++) { analogSection.AnalogChannels[i].Cycles.Add(currentCycles[i]); } } // Add the analogs to their respective cycles for (int i = 0; i < analogs.Length && i < currentCycles.Count; i++) { currentCycles[i].Samples.Add(analogs[i]); } // Determine whether this line represents the sample that triggered the event if (currentLine.Contains('>') || (eventSample == -1 && currentLine.Contains('*'))) { eventSample = sampleCount; } sampleCount++; index = dataLineIndex; } // If we did not find any samples marked with the // event time, assume it is the first sample if (eventSample < 0) { eventSample = 0; } // Determine the time per sample, in ticks firstCycleCount = analogSection.AnalogChannels.First().Cycles.First().Samples.Count; timePerSample = TimeSpan.TicksPerSecond / (60L * firstCycleCount); for (int i = 0; i < sampleCount; i++) { if ((i % firstCycleCount) == 0) { currentTimeCycle = new Cycle <DateTime>(); analogSection.TimeChannel.Cycles.Add(currentTimeCycle); } // Null reference not possible since 0 % firstCycleCount is 0 currentTimeCycle.Samples.Add(eventTime + TimeSpan.FromTicks(timePerSample * (i - eventSample))); } return(analogSection); }
private static AnalogSection ParseAnalogSection(DateTime eventTime, string[] lines, ref int index) { const string CycleHeader = @"^\[\d+\]$"; AnalogSection analogSection = new AnalogSection(); int headerLineIndex; int firstDataLineIndex; int dataLineIndex; int triggerLineIndex; string[] headers = null; string[] fields = null; int analogEndIndex = -1; double[] analogs; double analog = 0.0; Cycle<DateTime> currentTimeCycle = null; List<Cycle<double>> currentCycles = null; string currentLine; int sampleCount = 0; int eventSample = -1; int firstCycleCount; long timePerSample; // Scan forward to the first line of data firstDataLineIndex = index; while (firstDataLineIndex < lines.Length) { fields = Regex.Split(lines[firstDataLineIndex], @"\s|>|\*").Where(s => s != string.Empty).ToArray(); if (fields.Length > 0 && double.TryParse(fields[0], out analog)) break; firstDataLineIndex++; } if (firstDataLineIndex >= lines.Length) return analogSection; // Scan backward to find the header line headerLineIndex = firstDataLineIndex - 1; while (headerLineIndex >= index) { headers = lines[headerLineIndex].Split((char[])null, StringSplitOptions.RemoveEmptyEntries); if (headers.Length == fields.Length) break; headerLineIndex--; } if (headerLineIndex < index) return analogSection; // Scan forward to either the trigger or the // largest current, whichever comes first, // and use it to determine the analogEndIndex triggerLineIndex = firstDataLineIndex; while (firstDataLineIndex < lines.Length) { currentLine = lines[triggerLineIndex++]; // If the current line is empty or it matches the cycle header, skip it if (string.IsNullOrWhiteSpace(currentLine) || Regex.IsMatch(currentLine, CycleHeader)) continue; // If the length of the current line is not within one character // of the length of the first data line, assume we have reached // the end of the section and stop scanning lines if (Math.Abs(currentLine.Length - lines[firstDataLineIndex].Length) > 1) break; // Check if this is the trigger row analogEndIndex = currentLine.IndexOf('>'); if (analogEndIndex >= 0) break; // Check if this is the largest current row analogEndIndex = currentLine.IndexOf('*'); if (analogEndIndex >= 0) break; } // If analogEndIndex is valid, parse the header row again if (analogEndIndex >= 0 && analogEndIndex < lines[headerLineIndex].Length) headers = lines[headerLineIndex].Remove(analogEndIndex).Split((char[])null, StringSplitOptions.RemoveEmptyEntries); // Generate analog channels from header row analogSection.AnalogChannels = headers .Select(header => new Channel<double>() { Name = header }) .ToList(); // Scan through the lines of data dataLineIndex = firstDataLineIndex; while (dataLineIndex < lines.Length) { currentLine = lines[dataLineIndex++]; // Empty lines or cycle headers indicate start of next cycle if (string.IsNullOrWhiteSpace(currentLine) || Regex.IsMatch(currentLine, CycleHeader)) { // Two empty lines in a row indicates the end of the section if ((object)currentCycles == null) break; currentCycles = null; continue; } // If the line does not indicate the start of a cycle, // check the length to make sure we are still in the analog section if (Math.Abs(currentLine.Length - lines[firstDataLineIndex].Length) > 1) break; // Parse this line as a line of data if (analogEndIndex >= 0 && analogEndIndex < currentLine.Length) currentLine = currentLine.Remove(analogEndIndex); analogs = currentLine .Split((char[])null, StringSplitOptions.RemoveEmptyEntries) .TakeWhile(field => double.TryParse(field, out analog)) .Select(field => analog) .ToArray(); // Remove analog channels whose values cannot be parsed as doubles while (analogSection.AnalogChannels.Count > analogs.Length) analogSection.AnalogChannels.RemoveAt(analogSection.AnalogChannels.Count - 1); // Ensure the existence of a list of cycles to hold the analog values if ((object)currentCycles == null) { currentCycles = analogSection.AnalogChannels.Select(channel => new Cycle<double>()).ToList(); for (int i = 0; i < currentCycles.Count; i++) analogSection.AnalogChannels[i].Cycles.Add(currentCycles[i]); } // Add the analogs to their respective cycles for (int i = 0; i < analogs.Length && i < currentCycles.Count; i++) currentCycles[i].Samples.Add(analogs[i]); // Determine whether this line represents the sample that triggered the event if (currentLine.Contains('>') || (eventSample == -1 && currentLine.Contains('*'))) eventSample = sampleCount; sampleCount++; index = dataLineIndex; } // If we did not find any samples marked with the // event time, assume it is the first sample if (eventSample < 0) eventSample = 0; // Determine the time per sample, in ticks firstCycleCount = analogSection.AnalogChannels.First().Cycles.First().Samples.Count; timePerSample = TimeSpan.TicksPerSecond / (60L * firstCycleCount); for (int i = 0; i < sampleCount; i++) { if ((i % firstCycleCount) == 0) { currentTimeCycle = new Cycle<DateTime>(); analogSection.TimeChannel.Cycles.Add(currentTimeCycle); } // Null reference not possible since 0 % firstCycleCount is 0 currentTimeCycle.Samples.Add(eventTime + TimeSpan.FromTicks(timePerSample * (i - eventSample))); } return analogSection; }