public static EventFile Parse(string filename, double systemFrequency) { string[] lineSeparators = { "\r\n", "\n\r", "\r", "\n" }; EventFile parsedFile = new EventFile(); string fileText = File.ReadAllText(filename); int firstLineSeparatorIndex = lineSeparators.Select(separator => fileText.IndexOf(separator)).Where(index => index >= 0).Min(); string lineSeparator = lineSeparators.First(separator => fileText.IndexOf(separator) == firstLineSeparatorIndex); string[] lines = fileText .Split(new string[] { lineSeparator }, StringSplitOptions.None) .Select(line => line.RemoveControlCharacters()) .ToArray(); int lineIndex = 0; string command; EventReport parsedEventReport; EventHistory parsedEventHistory; CommaSeparatedEventReport parsedCommaSeparatedEventReport; while (lineIndex < lines.Length) { // Parse next command from the file command = ParseCommand(lines, ref lineIndex); // Skip to the next nonblank line SkipBlanks(lines, ref lineIndex); if (command.ToUpper().Contains("EVE")) { parsedEventReport = EventReport.Parse(systemFrequency, lines, ref lineIndex); parsedEventReport.Command = command; parsedFile.Add(parsedEventReport); } else if (command.ToUpper().Contains("CEV")) { parsedCommaSeparatedEventReport = CommaSeparatedEventReport.Parse(lines, ref lineIndex); parsedCommaSeparatedEventReport.Command = (command.ToUpper().Contains("FID") ? command.Split('\"')[0] : command); parsedFile.Add(parsedCommaSeparatedEventReport); } else if (command.ToUpper().Contains("HIS")) { parsedEventHistory = EventHistory.Parse(lines, ref lineIndex); parsedEventHistory.Command = command; parsedFile.Add(parsedEventHistory); } // Skip to the next nonblank line SkipBlanks(lines, ref lineIndex); } return(parsedFile); }
// Static Methods public static CommaSeparatedEventReport Parse(string[] lines, ref int index) { CommaSeparatedEventReport commaSeparatedEventReport = new CommaSeparatedEventReport(); commaSeparatedEventReport.Firmware = new Firmware(); commaSeparatedEventReport.Header = new Header(); int triggerIndex = 0; foreach (string line in lines) { if (line.Split(',').Length > 11 && line.Split(',')[11].Contains(">")) commaSeparatedEventReport.TriggerIndex = triggerIndex; ++triggerIndex; } while (!lines[index].ToUpper().Contains("SEL")) ++index; // Parse the report firmware id and checksum commaSeparatedEventReport.Firmware.ID = lines[index].Split(',')[0].Split('=')[1].Split('"')[0]; commaSeparatedEventReport.Firmware.Checksum = Convert.ToInt32(lines[index].Split(',')[1].Replace("\"", ""), 16); while (!lines[index].ToUpper().Contains("MONTH")) ++index; // Parse the date commaSeparatedEventReport.Header.EventTime = new DateTime(Convert.ToInt32(lines[++index].Split(',')[2]), Convert.ToInt32(lines[index].Split(',')[0]), Convert.ToInt32(lines[index].Split(',')[1]), Convert.ToInt32(lines[index].Split(',')[3]), Convert.ToInt32(lines[index].Split(',')[4]), Convert.ToInt32(lines[index].Split(',')[5])); // Set rest of header commaSeparatedEventReport.Header.SerialNumber = 0; commaSeparatedEventReport.Header.RelayID = ""; commaSeparatedEventReport.Header.StationID = ""; while (!lines[index].ToUpper().Contains("FREQ")) ++index; commaSeparatedEventReport.AverageFrequency = Convert.ToDouble(lines[++index].Split(',')[0]); commaSeparatedEventReport.SamplesPerCycleAnalog = Convert.ToInt32(lines[index].Split(',')[1]); commaSeparatedEventReport.SamplesPerCycleDigital = Convert.ToInt32(lines[index].Split(',')[2]); commaSeparatedEventReport.NumberOfCycles = Convert.ToInt32(lines[index].Split(',')[3]); commaSeparatedEventReport.Event = lines[index].Split(',')[4].Replace("\"", ""); while (!lines[index].ToUpper().Contains("IA")) ++index; commaSeparatedEventReport.AnalogSection = new AnalogSection(); string[] fields = lines[index].Split(','); foreach (string name in fields) { if(name.Length < 10 && ( name.ToUpper().Contains("IA") || name.ToUpper().Contains("IB") || name.ToUpper().Contains("IC") || name.ToUpper().Contains("IN") || (!name.ToUpper().Contains("TRIG") && name.ToUpper().Contains("IG")) || name.ToUpper().Contains("VA") || name.ToUpper().Contains("VB") || name.ToUpper().Contains("VC") || name.ToUpper().Contains("VS") || name.ToUpper().Contains("VDC") || name.ToUpper().Contains("FREQ") )) { commaSeparatedEventReport.AnalogSection.AnalogChannels.Add(new Channel<double>()); commaSeparatedEventReport.AnalogSection.AnalogChannels[commaSeparatedEventReport.AnalogSection.AnalogChannels.Count - 1].Name = name.Split('(')[0].Trim('"'); } } foreach(string channel in fields[12].Split(' ')) { if(channel != "\"") { commaSeparatedEventReport.AnalogSection.DigitalChannels.Add(new Channel<bool>()); commaSeparatedEventReport.AnalogSection.DigitalChannels[commaSeparatedEventReport.AnalogSection.DigitalChannels.Count - 1].Name = channel.Trim('"'); } } commaSeparatedEventReport.InitialReadingIndex = ++index; int timeStepTicks = Convert.ToInt32(Math.Round(10000000.0 / commaSeparatedEventReport.AverageFrequency / commaSeparatedEventReport.SamplesPerCycleAnalog)); while (!lines[index].ToUpper().Contains("SETTINGS")) { int diff = commaSeparatedEventReport.TriggerIndex - index - commaSeparatedEventReport.InitialReadingIndex; commaSeparatedEventReport.AnalogSection.TimeChannel.Samples.Add(commaSeparatedEventReport.Header.EventTime.AddTicks(-1*timeStepTicks * diff)); commaSeparatedEventReport.AnalogSection.AnalogChannels[0].Samples.Add(Convert.ToDouble(lines[index].Split(',')[0])*(fields[0].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[1].Samples.Add(Convert.ToDouble(lines[index].Split(',')[1])*(fields[1].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[2].Samples.Add(Convert.ToDouble(lines[index].Split(',')[2])*(fields[2].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[3].Samples.Add(Convert.ToDouble(lines[index].Split(',')[3])*(fields[3].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[4].Samples.Add(Convert.ToDouble(lines[index].Split(',')[4])*(fields[4].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[5].Samples.Add(Convert.ToDouble(lines[index].Split(',')[5])*(fields[5].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[6].Samples.Add(Convert.ToDouble(lines[index].Split(',')[6])*(fields[6].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[7].Samples.Add(Convert.ToDouble(lines[index].Split(',')[7])*(fields[7].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[8].Samples.Add(Convert.ToDouble(lines[index].Split(',')[8])*(fields[8].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[9].Samples.Add(Convert.ToDouble(lines[index].Split(',')[9])*(fields[9].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[10].Samples.Add(Convert.ToDouble(lines[index].Split(',')[10])); string digitals = lines[index].Split(',')[12].Replace("\"", ""); int forEachIndex = 0; foreach (Channel<bool> channel in commaSeparatedEventReport.AnalogSection.DigitalChannels) { channel.Samples.Add(Convert.ToString(Convert.ToInt32(digitals[forEachIndex/4].ToString(), 16), 2).PadLeft(4, '0')[forEachIndex%4] == '1'); ++forEachIndex; } ++index; } //skip "SETTINGS" AND " LINES if(lines[index].ToUpper() == "SETTINGS") ++index; if (lines[index] == "\"") ++index; EventFile.SkipBlanks(lines, ref index); // SKIP GROUP 1 AND GROUP SETTINGS lINES if (lines[index].ToUpper() == "GROUP 1") ++index; if (lines[index].ToUpper() == "GROUP SETTINGS:") ++index; commaSeparatedEventReport.GroupSetting = Settings.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); ++index; commaSeparatedEventReport.ControlEquations = ControlEquation.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); commaSeparatedEventReport.GlobalSetting = Settings.Parse(lines, ref index); return commaSeparatedEventReport; }
// Static Methods public static CommaSeparatedEventReport Parse(string[] lines, ref int index) { CommaSeparatedEventReport commaSeparatedEventReport = new CommaSeparatedEventReport(); commaSeparatedEventReport.Firmware = new Firmware(); commaSeparatedEventReport.Header = new Header(); int triggerIndex = 0; foreach (string line in lines) { if (line.Split(',').Length > 11 && line.Split(',')[11].Contains(">")) { commaSeparatedEventReport.TriggerIndex = triggerIndex; } ++triggerIndex; } while (!lines[index].ToUpper().Contains("SEL")) { ++index; } // Parse the report firmware id and checksum commaSeparatedEventReport.Firmware.ID = lines[index].Split(',')[0].Split('=')[1].Split('"')[0]; commaSeparatedEventReport.Firmware.Checksum = Convert.ToInt32(lines[index].Split(',')[1].Replace("\"", ""), 16); while (!lines[index].ToUpper().Contains("MONTH")) { ++index; } // Parse the date commaSeparatedEventReport.Header.EventTime = new DateTime(Convert.ToInt32(lines[++index].Split(',')[2]), Convert.ToInt32(lines[index].Split(',')[0]), Convert.ToInt32(lines[index].Split(',')[1]), Convert.ToInt32(lines[index].Split(',')[3]), Convert.ToInt32(lines[index].Split(',')[4]), Convert.ToInt32(lines[index].Split(',')[5])); // Set rest of header commaSeparatedEventReport.Header.SerialNumber = 0; commaSeparatedEventReport.Header.RelayID = ""; commaSeparatedEventReport.Header.StationID = ""; while (!lines[index].ToUpper().Contains("FREQ")) { ++index; } List <string> sampleStatHeader = lines[index].Split(',').ToList(); List <string> sampleStats = lines[++index].Split(',').ToList(); commaSeparatedEventReport.AverageFrequency = Convert.ToDouble(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("FREQ"))]); commaSeparatedEventReport.SamplesPerCycleAnalog = Convert.ToInt32(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("SAM/CYC_A"))]); commaSeparatedEventReport.SamplesPerCycleDigital = Convert.ToInt32(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("SAM/CYC_D"))]); commaSeparatedEventReport.NumberOfCycles = Convert.ToInt32(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("NUM_OF_CYC"))]); commaSeparatedEventReport.Event = sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("EVENT"))].Replace("/", ""); while (!lines[index].ToUpper().Contains("IA")) { ++index; } commaSeparatedEventReport.AnalogSection = new AnalogSection(); string[] fields = lines[index].Split(','); foreach (string name in fields) { if (name.Length < 10 && ( name.ToUpper().Contains("IA") || name.ToUpper().Contains("IB") || name.ToUpper().Contains("IC") || name.ToUpper().Contains("IN") || (!name.ToUpper().Contains("TRIG") && name.ToUpper().Contains("IG")) || name.ToUpper().Contains("VA") || name.ToUpper().Contains("VB") || name.ToUpper().Contains("VC") || name.ToUpper().Contains("VS") || name.ToUpper().Contains("VDC") || name.ToUpper().Contains("FREQ") )) { commaSeparatedEventReport.AnalogSection.AnalogChannels.Add(new Channel <double>()); commaSeparatedEventReport.AnalogSection.AnalogChannels[commaSeparatedEventReport.AnalogSection.AnalogChannels.Count - 1].Name = name.Split('(')[0].Trim('"'); } } foreach (string channel in fields[12].Split(' ')) { if (channel != "\"") { commaSeparatedEventReport.AnalogSection.DigitalChannels.Add(new Channel <bool>()); commaSeparatedEventReport.AnalogSection.DigitalChannels[commaSeparatedEventReport.AnalogSection.DigitalChannels.Count - 1].Name = channel.Trim('"'); } } commaSeparatedEventReport.InitialReadingIndex = ++index; int timeStepTicks = Convert.ToInt32(Math.Round(10000000.0 / commaSeparatedEventReport.AverageFrequency / commaSeparatedEventReport.SamplesPerCycleAnalog)); while (!lines[index].ToUpper().Contains("SETTINGS")) { int diff = commaSeparatedEventReport.TriggerIndex - index - commaSeparatedEventReport.InitialReadingIndex; commaSeparatedEventReport.AnalogSection.TimeChannel.Samples.Add(commaSeparatedEventReport.Header.EventTime.AddTicks(-1 * timeStepTicks * diff)); commaSeparatedEventReport.AnalogSection.AnalogChannels[0].Samples.Add(Convert.ToDouble(lines[index].Split(',')[0]) * (fields[0].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[1].Samples.Add(Convert.ToDouble(lines[index].Split(',')[1]) * (fields[1].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[2].Samples.Add(Convert.ToDouble(lines[index].Split(',')[2]) * (fields[2].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[3].Samples.Add(Convert.ToDouble(lines[index].Split(',')[3]) * (fields[3].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[4].Samples.Add(Convert.ToDouble(lines[index].Split(',')[4]) * (fields[4].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[5].Samples.Add(Convert.ToDouble(lines[index].Split(',')[5]) * (fields[5].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[6].Samples.Add(Convert.ToDouble(lines[index].Split(',')[6]) * (fields[6].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[7].Samples.Add(Convert.ToDouble(lines[index].Split(',')[7]) * (fields[7].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[8].Samples.Add(Convert.ToDouble(lines[index].Split(',')[8]) * (fields[8].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[9].Samples.Add(Convert.ToDouble(lines[index].Split(',')[9]) * (fields[9].ToUpper().Contains("KV")? 1000 : 1)); commaSeparatedEventReport.AnalogSection.AnalogChannels[10].Samples.Add(Convert.ToDouble(lines[index].Split(',')[10])); string digitals = lines[index].Split(',')[12].Replace("\"", ""); int forEachIndex = 0; foreach (Channel <bool> channel in commaSeparatedEventReport.AnalogSection.DigitalChannels) { channel.Samples.Add(Convert.ToString(Convert.ToInt32(digitals[forEachIndex / 4].ToString(), 16), 2).PadLeft(4, '0')[forEachIndex % 4] == '1'); ++forEachIndex; } ++index; } //skip "SETTINGS" AND " LINES if (lines[index].ToUpper() == "SETTINGS") { ++index; } if (lines[index] == "\"") { ++index; } EventFile.SkipBlanks(lines, ref index); // SKIP GROUP 1 AND GROUP SETTINGS lINES if (lines[index].ToUpper() == "GROUP 1") { ++index; } if (lines[index].ToUpper() == "GROUP SETTINGS:") { ++index; } commaSeparatedEventReport.GroupSetting = Settings.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); ++index; commaSeparatedEventReport.ControlEquations = ControlEquation.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); commaSeparatedEventReport.GlobalSetting = Settings.Parse(lines, ref index); return(commaSeparatedEventReport); }
public static EventFile Parse(string filename, double systemFrequency) { string[] lineSeparators = { "\r\n", "\n\r", "\r", "\n" }; EventFile parsedFile = new EventFile(); string fileText = File.ReadAllText(filename); int firstLineSeparatorIndex = lineSeparators.Select(separator => fileText.IndexOf(separator)).Where(index => index >= 0).Min(); string lineSeparator = lineSeparators.First(separator => fileText.IndexOf(separator) == firstLineSeparatorIndex); string[] lines = fileText .Split(new string[] { lineSeparator }, StringSplitOptions.None) .Select(line => line.RemoveControlCharacters()) .ToArray(); int lineIndex = 0; string command; EventReport parsedEventReport; EventHistory parsedEventHistory; CommaSeparatedEventReport parsedCommaSeparatedEventReport; bool parsed = false; while (lineIndex < lines.Length && !parsed) { // Parse next command from the file command = ParseCommand(lines, ref lineIndex); // Skip to the next nonblank line SkipBlanks(lines, ref lineIndex); if (command.ToUpper().Contains("EVE")) { parsedEventReport = EventReport.Parse(systemFrequency, lines, ref lineIndex); parsedEventReport.Command = command; parsedFile.Add(parsedEventReport); parsed = true; } else if (command.ToUpper().Contains("CEV") || filename.ToUpper().Contains(".CEV")) { parsedCommaSeparatedEventReport = CommaSeparatedEventReport.Parse(lines, ref lineIndex); parsedCommaSeparatedEventReport.Command = (command.ToUpper().Contains("FID") ? command.Split('\"')[0] : command); parsedFile.Add(parsedCommaSeparatedEventReport); parsed = true; } else if (command.ToUpper().Contains("HIS")) { parsedEventHistory = EventHistory.Parse(lines, ref lineIndex); parsedEventHistory.Command = command; parsedFile.Add(parsedEventHistory); parsed = true; } else { // We do not know what type of file we are reading because the console command may be missing from the top of the file. // We can attempt to glean if it is a cev by checking if the 3rd line can be split by commas. if (lines[2].Split(',').Count() > 1) { parsedCommaSeparatedEventReport = CommaSeparatedEventReport.Parse(lines, ref lineIndex); parsedCommaSeparatedEventReport.Command = (command.ToUpper().Contains("FID") ? command.Split('\"')[0] : command); parsedFile.Add(parsedCommaSeparatedEventReport); parsed = true; } else if (!lines.Where(x => x.Contains("FID")).Contains(",")) { parsedEventReport = EventReport.Parse(systemFrequency, lines, ref lineIndex); parsedEventReport.Command = command; parsedFile.Add(parsedEventReport); parsed = true; } } // Skip to the next nonblank line SkipBlanks(lines, ref lineIndex); } return(parsedFile); }
public bool Remove(CommaSeparatedEventReport commaSeparatedEventReport) { return(m_sections.Remove(commaSeparatedEventReport)); }
public void Add(CommaSeparatedEventReport commaSeparatedEventReport) { m_sections.Add(commaSeparatedEventReport); }
// Static Methods /// <summary> /// Parses CEV files. /// </summary> /// <param name="lines">The string array of SEL.cev lines to process</param> /// <param name="fileIdentifier">For error logging, an identifier of the file being processed -- typically the filename.</param> /// <param name="maxFileDuration">Set to a positive value limit the number of data records processed.</param> /// <remarks>Removed lineIndex since file must be processed in sequence. </remarks> /// <returns>Data model representing the comma separated event report.</returns> public static CommaSeparatedEventReport Parse(string[] lines, string fileIdentifier = "", double maxFileDuration = 0.0D) { //OnDebugMessage(string.Format("Parsing SEL CEV file: {0}", fileIdentifier)); if (lines == null || lines.Length == 0) { OnDebugMessage(string.Format("SEL CEV Parse aborted. Nothing to do. Sent line array from file {0} is null or empty.", fileIdentifier)); return(null); } CommaSeparatedEventReport commaSeparatedEventReport = new CommaSeparatedEventReport(); commaSeparatedEventReport.Firmware = new Firmware(); commaSeparatedEventReport.Header = new Header(); int lineIndex = 0; //relative to the first line in the file string inString = string.Empty; int headerRecordNumber = 1; string[] headerFields = null; string[] lastHeaderFields = null; //advance to first header record while (lineIndex < lines.Length) { headerFields = StringParser.ParseStandardCSV(lines[lineIndex]); if (headerFields != null && headerFields[0].ToUpper().Contains("FID")) { break; } lineIndex++; } if (lineIndex >= lines.Length) { OnDebugMessage(string.Format("No SEL CEV data found. Nothing to do processing file {0} of length {1}", fileIdentifier, lines.Length.ToString())); return(null); } //Header Section -- 7 records expected //It's reasonable to assume that for a file to be valid it must contain the correct number of headers in the proper order //Returns null if header is significantly malformed. //However, it will try to survive bad bytesum checks while (headerRecordNumber < 8) { inString = lines[lineIndex].Trim(); if (!string.IsNullOrEmpty(inString)) { ByteSum byteSum = new ByteSum(); headerFields = StringParser.ParseStandardCSV(inString); switch (headerRecordNumber) { case 1: //field names for headerRecord 2 -- already verified that field 0 contains 'FID' byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } if (headerFields.Length < 2) { OnDebugMessage(string.Format("Processing SEL CEV header record 1 for SEL CEV file: {0} Expected at least 2 fields and {1} were found.", fileIdentifier, headerFields.Length)); } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 2: //The FID and Firmware Version Number byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } if (headerFields.Length < 2) { OnDebugMessage(string.Format("Processing SEL CEV header record 2 for SEL CEV file: {0} Expected at least 2 fields and {1} were found.", fileIdentifier, headerFields.Length)); } if (!headerFields[0].Contains("SEL")) { OnDebugMessage(string.Format("Processing field 0 for header record 2 for file {0} Expected string to contain 'SEL' and '{1}' was found.", fileIdentifier, headerFields[0])); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } if (headerFields.Length > 2) { commaSeparatedEventReport.Firmware.ID = headerFields[1].Trim(); } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 3: //The headers for the date data byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } string[] expectedFieldNames3 = { "MONTH", "DAY", "YEAR", "HOUR", "MIN", "SEC", "MSEC" }; if (!StringParser.ExpectedFieldNamesMatch(expectedFieldNames3, headerFields, true, 6)) { OnDebugMessage("Processing SEL CEV header record 3, field names for date header. The expected values for the date labels did not match."); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 4: //The file date values byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } if (headerFields.Length < 6) { OnDebugMessage(string.Format("Processing SEL CEV header record 4 for SEL CEV file: {0} Expected at least 6 fields and {1} were found.", fileIdentifier, headerFields.Length)); } else { int[] values; if (!TryConvertInt32(headerFields, out values, headerFields.Length - 1)) { OnDebugMessage(string.Format("One or more date fields in header record 4 did not parse to integers. Event time not set in SEL CEV File: {0}", fileIdentifier)); } else { commaSeparatedEventReport.Header.EventTime = new DateTime(values[2], values[0], values[1], values[3], values[4], values[5]); if (commaSeparatedEventReport.Header.EventTime.CompareTo(Convert.ToDateTime("01/01/2000")) < 0) { OnDebugMessage(string.Format("The event time of {0} is prior to January 1, 2000.", commaSeparatedEventReport.Header.EventTime.ToShortDateString())); } } } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 5: //The headers for the summary data - fields can appear in any order byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } if (StringParser.FindIndex("FREQ", headerFields) < 0 || StringParser.FindIndex("EVENT", headerFields) < 0) //just check a couple { OnDebugMessage("Processing header record 5 and the minimum expected values of 'FREQ' and 'EVENT' were not found."); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } headerRecordNumber++; lastHeaderFields = headerFields; break; case 6: //The summary data - no try-parse data tests performed since there is confirmation of the availability of specific fields byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } if (headerFields.Length != lastHeaderFields.Length) { OnDebugMessage(string.Format("Processing header record 6 -- expected {0} values and {1} were found", lastHeaderFields.Length, headerFields.Length)); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } //For completeness, not needed commaSeparatedEventReport.Header.SerialNumber = 0; commaSeparatedEventReport.Header.RelayID = ""; commaSeparatedEventReport.Header.StationID = ""; //set key class properties //nominal frequency is based on average found. commaSeparatedEventReport.FrequencyAverage = Convert.ToDouble(headerFields[StringParser.FindIndex("FREQ", lastHeaderFields)]); if (commaSeparatedEventReport.FrequencyAverage > 48D && commaSeparatedEventReport.FrequencyAverage < 52D) { commaSeparatedEventReport.FrequencyNominal = 50D; } else { commaSeparatedEventReport.FrequencyNominal = 60D; } commaSeparatedEventReport.Event = headerFields[StringParser.FindIndex("EVENT", lastHeaderFields)]; int labelIndex = StringParser.FindIndex("SAM/CYC_A", lastHeaderFields); if (labelIndex > 0) { commaSeparatedEventReport.SamplesPerCycleAnalog = Convert.ToDouble(headerFields[labelIndex]); } labelIndex = StringParser.FindIndex("SAM/CYC_D", lastHeaderFields); if (labelIndex > 0) { commaSeparatedEventReport.SamplesPerCycleDigital = Convert.ToDouble(headerFields[labelIndex]); } labelIndex = StringParser.FindIndex("NUM_OF_CYC", lastHeaderFields); if (labelIndex > 0) { commaSeparatedEventReport.NumberOfCycles = Convert.ToDouble(headerFields[labelIndex]); } labelIndex = StringParser.FindIndex("NUM_CH_A", lastHeaderFields); if (labelIndex > 0) { commaSeparatedEventReport.ExpectedAnalogCount = Convert.ToInt32(headerFields[labelIndex]); } labelIndex = StringParser.FindIndex("NUM_CH_D", lastHeaderFields); if (labelIndex > 0) { commaSeparatedEventReport.ExpectedDigitalCount = Convert.ToInt32(headerFields[labelIndex]); } headerRecordNumber++; lastHeaderFields = headerFields; break; case 7: //The header the data records - assume valid if all three phase currents are present //Note for some files, this record spans multiple lines, will keep reading lines until the quotes match. StringBuilder sb = new StringBuilder(lines[lineIndex]); //find all the data field names - spanning multiple lines while (sb.ToString().CharCount('\"') % 2 == 1) { sb.Append(lines[++lineIndex]); if (lineIndex >= lines.Length) { OnDebugMessage(string.Format("Only partial CEV header data found. Processing of SEL CEV file: {0} aborted at line {1}", fileIdentifier, lineIndex.ToString())); return(null); } } headerFields = StringParser.ParseStandardCSV(sb.ToString().Trim()); byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage(string.Format("ByteSum check failed for header record {0} in SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } if (StringParser.FindIndex("IA", headerFields, false, true) < 0 || StringParser.FindIndex("IB", headerFields, false, true) < 0 || StringParser.FindIndex("IC", headerFields, false, true) < 0) { OnDebugMessage("Processing header record 7, the field names for the data records, and did not find the minimum set of 'IA', 'IB' and 'IC'"); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } headerRecordNumber++; lastHeaderFields = headerFields; break; default: break; } } else { OnDebugMessage(string.Format("Unexpected empty header reader in advance of header record number {0} for SEL CEV file: {1}", headerRecordNumber, fileIdentifier)); } lineIndex++; if (lineIndex >= lines.Length) { OnDebugMessage(string.Format("Only partial CEV header data found. Processing of SEL CEV file: {0} aborted at line {1}", fileIdentifier, lineIndex.ToString())); return(null); } //else // OnDebugMessage(string.Format("Successfully parsed header record {0} in SEL CEV file: {1}", headerRecordNumber - 1, fileIdentifier)); } commaSeparatedEventReport.InitialReadingIndex = lineIndex; //determine the number of analog data fields based on the position of "TRIG" (the trigger field name) and setup up Analog Section int triggerFieldPosition = Array.FindIndex(headerFields, x => x.ToUpper().Contains(m_triggerFieldName)); if (triggerFieldPosition < 0) //not found { OnDebugMessage(string.Format("Processing header record 8, the field names for data, searching for {0} as the analog/digital data field separator. It was not found within the values of {1}.", m_triggerFieldName, headerFields.ToString())); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } if (headerFields.Length < triggerFieldPosition + 2) //too few field names past separator { OnDebugMessage(string.Format("Processing header record 8, the field names for data, too few field names found past {0} (the analog/digital data field separator) within the values of {1}.", m_triggerFieldName, headerFields.ToString())); OnDebugMessage(string.Format("Processing TERMINATED for SEL CEV file: {0}", fileIdentifier)); return(null); } commaSeparatedEventReport.AnalogSection = new AnalogSection(); //loop through the expected analog fields, add all the fields but "TRIG" (the trigger field name) if (commaSeparatedEventReport.ExpectedAnalogCount <= 0) { commaSeparatedEventReport.ExpectedAnalogCount = triggerFieldPosition; } //expected value count = analogs + trigger + digitals + bytesum (analogs plus 3) commaSeparatedEventReport.ExpectedDataRecordValueCount = commaSeparatedEventReport.ExpectedAnalogCount + 3; //for speed, the scaling factors, if any are used, are pre-positioned double[] scalingFactors = new double[commaSeparatedEventReport.ExpectedAnalogCount]; bool scalingRequired = false; for (int fieldIndex = 0; fieldIndex < triggerFieldPosition; fieldIndex++) { commaSeparatedEventReport.AnalogSection.AnalogChannels.Add(new Channel <double>()); commaSeparatedEventReport.AnalogSection.AnalogChannels[fieldIndex].Name = headerFields[fieldIndex]; if (headerFields[fieldIndex].ToUpper().Contains("KV")) { scalingFactors[fieldIndex] = 1000D; scalingRequired = true; } else { scalingFactors[fieldIndex] = 1D; } } //loop through the digital channels int digitalChannelCount = 0; foreach (string channel in headerFields[triggerFieldPosition + 1].QuoteUnwrap().RemoveDuplicateWhiteSpace().Trim().Split(' ')) { commaSeparatedEventReport.AnalogSection.DigitalChannels.Add(new Channel <bool?>()); commaSeparatedEventReport.AnalogSection.DigitalChannels[commaSeparatedEventReport.AnalogSection.DigitalChannels.Count - 1].Name = channel; digitalChannelCount++; } if (commaSeparatedEventReport.ExpectedDigitalCount <= 0) { commaSeparatedEventReport.ExpectedDigitalCount = digitalChannelCount; } else if (digitalChannelCount != commaSeparatedEventReport.ExpectedDigitalCount) { OnDebugMessage(string.Format("Processing SEL CEV header record 8, the field names for data, the {0} digital channel names found does not match the expected number of {1}", commaSeparatedEventReport.ExpectedDigitalCount, digitalChannelCount)); } //find the trigger record within the data section, Carry on if none found. int triggerIndexRelative = 0; //relative to the first data line for (lineIndex = commaSeparatedEventReport.InitialReadingIndex; lineIndex < lines.Length; lineIndex++) { if (string.IsNullOrEmpty(lines[lineIndex]) || lines[lineIndex].Trim().Length == 0) { OnDebugMessage(string.Format("Null or empty data record was found at line {0} in file {1} and was skipped in the determination of the trigger record time.", lineIndex, fileIdentifier)); //this condition logged at Info level later continue; //skip this line to be consistent with data parsing logic. } string[] s = lines[lineIndex].Split(','); //use the split function for speed if (s.Length > triggerFieldPosition && s[triggerFieldPosition].Trim().Length > 0) { commaSeparatedEventReport.TriggerIndex = triggerIndexRelative; break; } if (s.Length > 0 && s[0].ToUpper().Contains("SETTINGS")) //we're done with data and no trigger was found. { triggerIndexRelative = 0; OnDebugMessage(string.Format("No trigger index found in SEL CEV file: {0}", fileIdentifier)); break; } ++triggerIndexRelative; } if (lineIndex >= lines.Length) //we've looped through all the lines, no trigger && no SETTINGS { OnDebugMessage(string.Format("No SETTINGS line terminator and No trigger index found in SEL CEV file: {0}", fileIdentifier)); //this condition logged at the Info level later triggerIndexRelative = 0; } //Log significant info about the file //OnDebugMessage(string.Format("Found {0} analog channels and {1} digital channels to process within the SEL CEV file: {2} with an event time of {3} and a relative trigger index of {4}", // commaSeparatedEventReport.AnalogSection.AnalogChannels.Count, commaSeparatedEventReport.AnalogSection.DigitalChannels.Count, fileIdentifier, // commaSeparatedEventReport.Header.EventTime.ToLongDateString(), triggerIndexRelative.ToString())); int timeStepTicks = Convert.ToInt32(Math.Round(10000000.0 / commaSeparatedEventReport.FrequencyNominal / commaSeparatedEventReport.SamplesPerCycleAnalog)); //Time (in ticks) is relative to the trigger line (record). //Negative in advance of the trigger record, Zero at the trigger record, Positive following the trigger recored. int lineTicks = -1 * triggerIndexRelative * timeStepTicks; //Log significant time-based info //OnDebugMessage(string.Format("Starting line tics: {0} Incremental tics per record: {1}", lineTicks, timeStepTicks)); //set data record limit int dataRecordLimit = (int)Math.Round(maxFileDuration * commaSeparatedEventReport.FrequencyNominal * commaSeparatedEventReport.SamplesPerCycleAnalog); if (dataRecordLimit > 0) { dataRecordLimit = ((lines.Length - commaSeparatedEventReport.InitialReadingIndex) > dataRecordLimit) ? dataRecordLimit : lines.Length - commaSeparatedEventReport.InitialReadingIndex; } else { dataRecordLimit = lines.Length - commaSeparatedEventReport.InitialReadingIndex; } //------------------------------------------------ THE DATA -------------------------------------------------------- //Now loop through the lines to get the data //Empty lines are ignored (i.e., time is not incremented) [OnDebugMessage] //For radically malformed lines time is incremented and all analogs are set to NaN and digitals set to null [OnDebugMessage] //Data field order and type are set by the header and do not vary with the data region int dataRecordCount = 0; for (lineIndex = commaSeparatedEventReport.InitialReadingIndex; lineIndex < commaSeparatedEventReport.InitialReadingIndex + dataRecordLimit; lineIndex++) { string[] data = StringParser.ParseStandardCSV(lines[lineIndex]); dataRecordCount++; if (data == null || data.Length == 0) { OnDebugMessage(string.Format("Data record {0} in SEL CEV file: {1} was empty and was skipped.", dataRecordCount, fileIdentifier)); continue; //get next line } if (data.Length > 0 && data[0].ToUpper().Contains("SETTINGS")) //we're done with the data { break; } //increment time commaSeparatedEventReport.AnalogSection.TimeChannel.Samples.Add(commaSeparatedEventReport.Header.EventTime.AddTicks(lineTicks)); lineTicks += timeStepTicks; if (data.Length != commaSeparatedEventReport.ExpectedDataRecordValueCount) { OnDebugMessage(string.Format("Data record {0} in SEL CEV file: {1} did not contain the anticipated values.", dataRecordCount, fileIdentifier)); //OnDebugMessage(string.Format("Data record {0} in SEL CEV file: {1} did not contain the anticipated {2) values. Setting all analog and digital values to NaN or null and continuing.", // dataRecordCount.ToString(), fileIdentifier, Convert.ToString(commaSeparatedEventReport.ExpetedDataRecordValueCount))); //let's try to survive it. foreach (var analogChannel in commaSeparatedEventReport.AnalogSection.AnalogChannels) { analogChannel.Samples.Add(Double.NaN); //what are the consequences here?? } foreach (Channel <bool?> channel in commaSeparatedEventReport.AnalogSection.DigitalChannels) { channel.Samples.Add(null); } continue; //get next line } //check bytesum ByteSum byteSum = new ByteSum(); byteSum.Check(lines[lineIndex], fileIdentifier); if (!byteSum.Match) { //todo: Append AnalogSection to include data quality OnDebugMessage(string.Format("Byte sum does not match for data record {0} in SEL CEV file {1}. This record processed as if it is valid.", dataRecordCount, fileIdentifier)); } //LOAD ANALOG DATA (overall record tests above are sufficient to verify expected number of values) int channelIndex = 0; foreach (var analogChannel in commaSeparatedEventReport.AnalogSection.AnalogChannels) { double value = 0D; if (!double.TryParse(data[channelIndex], out value)) { analogChannel.Samples.Add(Double.NaN); } else if (scalingRequired) { analogChannel.Samples.Add(Convert.ToDouble(data[channelIndex]) * scalingFactors[channelIndex]); } else { analogChannel.Samples.Add(Convert.ToDouble(data[channelIndex])); } channelIndex++; //analogChannel.Samples.Add(Convert.ToDouble(lines[lineIndex].Split(',')[channelIndex]) * (lineFields[channelIndex++].ToUpper().Contains("KV") ? 1000 : 1)); } //if (dataRecordCount == 1) //log the first recored for debug // OnDebugMessage("Data record 1: " + lines[lineIndex]); //LOAD DIGITAL DATA char[] hexDigitals = data[commaSeparatedEventReport.AnalogSection.AnalogChannels.Count + 1].QuoteUnwrap().Trim().ToCharArray(); //digitals always on the other side of "TRIG" if (hexDigitals.Length == 0) { continue; } if (hexDigitals.Length * 4 < commaSeparatedEventReport.AnalogSection.DigitalChannels.Count) { OnDebugMessage(string.Format("The expected {0} digital channels were not found for data record {1} in SEL CEV file {2}. {3} were found. Setting digitals to null and continuing.", hexDigitals.Length * 4, dataRecordCount, fileIdentifier, commaSeparatedEventReport.AnalogSection.DigitalChannels.Count)); foreach (Channel <bool?> channel in commaSeparatedEventReport.AnalogSection.DigitalChannels) { channel.Samples.Add(null); } continue; //get next line } channelIndex = 0; int hexCharIndex = 0; if (commaSeparatedEventReport.AnalogSection.DigitalChannels.Count > 0) { foreach (Channel <bool?> channel in commaSeparatedEventReport.AnalogSection.DigitalChannels) //loop through the channels and add the values { hexCharIndex = channelIndex / 4; if (hexDigitals[hexCharIndex].IsHex()) { BitArray ba = hexDigitals[hexCharIndex].ConvertHexToBitArray(); //OnDebugMessage(string.Format("dig channel:{0} hex:{1}, position:{2}, value:{3}", channelIndex, hexDigitals[hexCharIndex], channelIndex % 4, ba[channelIndex % 4].ToString())); //validation of correct digital logic channel.Samples.Add(ba[channelIndex % 4]); } else { channel.Samples.Add(null); } channelIndex++; } } } //------------------------------ END DATA SECTION ----------------------------------------- if (lineIndex >= commaSeparatedEventReport.InitialReadingIndex + dataRecordLimit) //we've looped through all the lines, no trigger && no SETTINGS { OnDebugMessage(string.Format("Reached data record limit of {0} prior to finding SETTINGS as data section terminator in SEL CEV file: {1}", dataRecordLimit, fileIdentifier)); } commaSeparatedEventReport.ExpectedSampleCount = dataRecordCount; //OnDebugMessage(string.Format("Successfully processed {0} data records in SEL CEV file: {1}", dataRecordCount, fileIdentifier)); if (lineIndex >= lines.Length) { return(commaSeparatedEventReport); //we're done. } //advance to 'SETTINGS' if we're not there already if (!lines[lineIndex].Contains("SETTINGS")) { while (lineIndex < lines.Length) { string[] temp = StringParser.ParseStandardCSV(lines[lineIndex]); if (temp != null && string.Equals(temp[0].ToUpper(), "SETTINGS")) { break; } lineIndex++; } } if (lineIndex >= lines.Length) //we've looped through all the lines no settings found to add { OnDebugMessage(string.Format("No settings were found following the SETTINGS line terminator was found at end of data section in SEL CEV file: {0}", fileIdentifier)); //we're done return(commaSeparatedEventReport); } //advance to the first non-null settings line lineIndex++; while (lineIndex < lines.Length && (lines[lineIndex].Trim().Length == 0 || lines[lineIndex].Trim() == "\"")) { lineIndex++; } List <SectionDefinition> settingsRegions = new List <SectionDefinition>(); string sectionName = "Settings"; //always first int startLine = lineIndex; while (lineIndex < lines.Length) { string test = lines[lineIndex].ToUpper().Trim(); if (test.Contains("SETTINGS") || test.Contains("VARIABLES") || test.Contains("EQUATIONS")) { settingsRegions.Add(new SectionDefinition(sectionName, startLine, lineIndex - startLine)); int p = lines[lineIndex].IndexOf(':'); if (p < 0) { sectionName = lines[lineIndex].RemoveCharacters(char.IsWhiteSpace); //no terminating colon, assume just the name. lines[lineIndex] = string.Empty; } else { int q = lines[lineIndex].IndexOf(":="); if (q < 0) { q = lines[lineIndex].IndexOf('='); } if (q < 0) //no data this line. { sectionName = lines[lineIndex].Substring(0, p).RemoveCharacters(char.IsWhiteSpace); lines[lineIndex] = string.Empty; } else { lines[lineIndex] = lines[lineIndex].Substring(p + 1); } } startLine = lineIndex; } lineIndex++; } settingsRegions.Add(new SectionDefinition(sectionName, startLine, lines.Length - startLine)); //handle the last one. //Now build the dictionary of settings. //The key for settings takes the form "RegionName:Settings" -- with all white spaces removed from RegionNames string[] regions = new string[settingsRegions.Count]; Dictionary <string, string> settingValues = new Dictionary <string, string>(); int i = 0; foreach (SectionDefinition sd in settingsRegions) { for (int j = sd.StartLine; j < sd.StartLine + sd.Length; ++j) { string test = lines[j].Trim().RemoveDuplicateWhiteSpace(); if (test.Length == 0) { continue; //get the next line } while (test.Contains("=")) { int p, q, r, s = 0; string value = string.Empty; p = test.IndexOf(":="); q = p + 2; if (p < 0) { p = test.IndexOf("="); q = p + 1; } string key = string.Concat(sd.Name, ":", test.Substring(0, p).Trim()); if (q < test.Length) { r = test.IndexOf(":=", q); //find the next one if (r < 0) { r = test.IndexOf('=', q); } if (r > 0) { s = test.IndexOfPrevious(char.IsWhiteSpace, r - 2); //go back past the space if (s < q) { value = "0"; } else { value = test.Substring(q, s - q).Trim(); } } else //we're at the end of the line { value = test.Substring(q).Trim(); test = ""; } if (settingValues.ContainsKey(key)) { OnDebugMessage(string.Format("Settings already contains key:{0}", key)); } else { settingValues.Add(key, value); } } test = test.Substring(s); } } regions[i++] = sd.Name; } //OnDebugMessage(string.Format("Successfully found {0} settings groups and a total of {1} settings.", regions.Length, settingValues.Count)); commaSeparatedEventReport.SettingsRegions = regions; commaSeparatedEventReport.Settings = settingValues; return(commaSeparatedEventReport); }
// Static Methods /// <summary> /// Parses CEV files. /// </summary> /// <param name="lines">The string array of SEL.cev lines to process</param> /// <param name="processSettings">Set to TRUE to process settings block</param> /// <param name="processDigitals">Set to TRUE to process digital values block</param> /// <param name="fileIdentifier">For error logging, an identifier of the file being processed -- typically the filename.</param> /// <param name="maxFileDuration">Set to a positive value limit the number of data records processed.</param> /// <remarks>Removed lineIndex since file must be processed in sequence. </remarks> /// <returns>Data model representing the comma separated event report.</returns> public static CommaSeparatedEventReport Parse(string[] lines, bool processDigitals = true, bool processSettings = false, string fileIdentifier = "", double maxFileDuration = 0.0D) { //OnDebugMessage(string.Format("Parsing SEL CEV file: {0}", fileIdentifier)); if (lines == null || lines.Length == 0) { OnDebugMessage($"SEL CEV Parse aborted. Nothing to do. Sent line array from file {fileIdentifier} is null or empty."); return(null); } CommaSeparatedEventReport cSER = new CommaSeparatedEventReport(); cSER.Firmware = new Firmware(); cSER.Header = new Header(); cSER.ProcessSettings = processSettings; cSER.ProcessDigitals = processDigitals; int lineIndex = 0; //relative to the first line in the file string inString; int headerRecordNumber = 1; string[] headerFields = null; string[] lastHeaderFields = null; //------------------------------------------------ THE HEADER BLOCK -------------------------------------------------------- //Header Section -- 7 records expected //It's reasonable to assume that for a file to be valid it must contain the correct number of headers in the proper order //Returns null if header is significantly malformed. //However, it will try to survive bad bytesum checks while (lineIndex < lines.Length) { headerFields = StringParser.ParseStandardCSV(lines[lineIndex]); if (headerFields != null && headerFields[0].ToUpper().Contains("FID")) { break; } lineIndex++; } if (lineIndex >= lines.Length) { OnDebugMessage($"No SEL CEV data found. Nothing to do processing file {fileIdentifier} of length {lines.Length.ToString()}"); return(null); } while (headerRecordNumber < 8) { inString = lines[lineIndex].Trim(); if (!string.IsNullOrEmpty(inString)) { ByteSum byteSum = new ByteSum(); headerFields = StringParser.ParseStandardCSV(inString); switch (headerRecordNumber) { case 1: //field names for headerRecord 2 -- already verified that field 0 contains 'FID' byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage($"ByteSum check failed for header record {headerRecordNumber} in SEL CEV file: {fileIdentifier}"); } if (headerFields.Length < 2) { OnDebugMessage($"Processing SEL CEV header record 1 for SEL CEV file: {fileIdentifier} Expected at least 2 fields and {headerFields.Length} were found."); } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 2: //The FID and Firmware Version Number byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage($"ByteSum check failed for header record {headerRecordNumber} in SEL CEV file: {fileIdentifier}"); } if (headerFields.Length < 2) { OnDebugMessage($"Processing SEL CEV header record 2 for SEL CEV file: {fileIdentifier} Expected at least 2 fields and {headerFields.Length} were found."); } if (!headerFields[0].Contains("SEL")) { OnDebugMessage($"Processing field 0 for header record 2 for file {fileIdentifier} Expected string to contain 'SEL' and '{headerFields[0]}' was found."); OnDebugMessage($"Processing TERMINATED for SEL CEV file: {fileIdentifier}"); return(null); } if (headerFields.Length > 2) { cSER.Firmware.ID = headerFields[1].Trim(); } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 3: //The headers for the date data byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage($"ByteSum check failed for header record {headerRecordNumber} in SEL CEV file: {fileIdentifier}"); } string[] expectedFieldNames3 = { "MONTH", "DAY", "YEAR", "HOUR", "MIN", "SEC", "MSEC" }; if (!StringParser.ExpectedFieldNamesMatch(expectedFieldNames3, headerFields, true, 6)) { OnDebugMessage("Processing SEL CEV header record 3, field names for date header. The expected values for the date labels did not match."); OnDebugMessage($"Processing TERMINATED for SEL CEV file: {fileIdentifier}"); return(null); } headerRecordNumber++; //lastHeaderFields = headerFields; break; case 4: //The file date values byteSum.Check(inString, fileIdentifier); if (!byteSum.Match) //moved inside case switch due to case 7 { OnDebugMessage($"ByteSum check failed for header record {headerRecordNumber} in SEL CEV file: {fileIdentifier}"); } if (headerFields.Length < 6) { OnDebugMessage($"Processing SEL CEV header record 4 for SEL CEV file: {fileIdentifier} Expected at least 6 fields and {headerFields.Length} were found."); } else { if (!TryConvertInt32(headerFields, out int[] values, headerFields.Length - 1))
public static CommaSeparatedEventReport Parse(double systemFrequency, string[] lines, ref int index) { CommaSeparatedEventReport commaSeparatedEventReport = new CommaSeparatedEventReport(); commaSeparatedEventReport.Firmware = new Firmware(); commaSeparatedEventReport.Header = new Header(); int triggerIndex = 0; foreach (string line in lines) { if (line.Split(',').Length > 11 && line.Split(',')[11].Contains(">")) { commaSeparatedEventReport.TriggerIndex = triggerIndex; } ++triggerIndex; } while (!lines[index].ToUpper().Contains("SEL")) { ++index; } // Parse the report firmware id and checksum commaSeparatedEventReport.Firmware.ID = lines[index].Split(',')[0].Split('=')[1].Split('"')[0]; //commaSeparatedEventReport.Firmware.Checksum = Convert.ToInt32(lines[index].Split(',')[1].Replace("\"", ""), 16); while (!lines[index].ToUpper().Contains("MONTH")) { ++index; } // Parse the date commaSeparatedEventReport.Header.EventTime = new DateTime(Convert.ToInt32(lines[++index].Split(',')[2]), Convert.ToInt32(lines[index].Split(',')[0]), Convert.ToInt32(lines[index].Split(',')[1]), Convert.ToInt32(lines[index].Split(',')[3]), Convert.ToInt32(lines[index].Split(',')[4]), Convert.ToInt32(lines[index].Split(',')[5])); // Set rest of header commaSeparatedEventReport.Header.SerialNumber = 0; commaSeparatedEventReport.Header.RelayID = ""; commaSeparatedEventReport.Header.StationID = ""; while (!lines[index].ToUpper().Contains("FREQ")) { ++index; } List <string> sampleStatHeader = lines[index].Split(',').ToList(); List <string> sampleStats = lines[++index].Split(',').ToList(); commaSeparatedEventReport.AverageFrequency = Convert.ToDouble(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("FREQ"))]); commaSeparatedEventReport.SamplesPerCycleAnalog = Convert.ToDouble(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("SAM/CYC_A"))]); commaSeparatedEventReport.SamplesPerCycleDigital = Convert.ToDouble(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("SAM/CYC_D"))]); commaSeparatedEventReport.NumberOfCycles = Convert.ToDouble(sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("NUM_OF_CYC"))]); commaSeparatedEventReport.Event = sampleStats[sampleStatHeader.FindIndex(x => x.ToUpper().Contains("EVENT"))].Replace("/", ""); while (!lines[index].ToUpper().Contains("IA")) { ++index; } commaSeparatedEventReport.AnalogSection = new AnalogSection(); string[] fields = lines[index].Split(','); int fieldIndex = 0; while (fields[fieldIndex].Trim('"').ToUpper() != "TRIG") { commaSeparatedEventReport.AnalogSection.AnalogChannels.Add(new Channel <double>()); commaSeparatedEventReport.AnalogSection.AnalogChannels[commaSeparatedEventReport.AnalogSection.AnalogChannels.Count - 1].Name = fields[fieldIndex++].Split('(')[0].Trim('"'); } foreach (string channel in fields[++fieldIndex].Split(' ')) { if (channel != "\"") { commaSeparatedEventReport.AnalogSection.DigitalChannels.Add(new Channel <bool?>()); commaSeparatedEventReport.AnalogSection.DigitalChannels[commaSeparatedEventReport.AnalogSection.DigitalChannels.Count - 1].Name = channel.Trim('"'); } } commaSeparatedEventReport.InitialReadingIndex = ++index; int timeStepTicks = Convert.ToInt32(Math.Round(10000000.0 / systemFrequency / commaSeparatedEventReport.SamplesPerCycleAnalog)); while (!lines[index].ToUpper().Contains("SETTINGS") && lines[index].ToUpper() != string.Empty) { int diff = commaSeparatedEventReport.TriggerIndex - index - commaSeparatedEventReport.InitialReadingIndex; commaSeparatedEventReport.AnalogSection.TimeChannel.Samples.Add(commaSeparatedEventReport.Header.EventTime.AddTicks(-1 * timeStepTicks * diff)); int channelIndex = 0; foreach (var analogChannel in commaSeparatedEventReport.AnalogSection.AnalogChannels) { analogChannel.Samples.Add(Convert.ToDouble(lines[index].Split(',')[channelIndex]) * (fields[channelIndex++].ToUpper().Contains("KV") ? 1000 : 1)); } string digitals = lines[index].Split(',')[++channelIndex].Replace("\"", ""); int forEachIndex = 0; foreach (Channel <bool?> channel in commaSeparatedEventReport.AnalogSection.DigitalChannels) { if (digitals == "" || !Regex.IsMatch(digitals, @"\A\b[0-9a-fA-F]+\b\Z")) { channel.Samples.Add(null); } else { channel.Samples.Add(Convert.ToString(Convert.ToInt32(digitals[forEachIndex / 4].ToString(), 16), 2).PadLeft(4, '0')[forEachIndex % 4] == '1'); } ++forEachIndex; } ++index; } try { //skip "SETTINGS" AND " LINES if (lines[index].ToUpper() == "SETTINGS") { ++index; } if (lines[index] == "\"") { ++index; } EventFile.SkipBlanks(lines, ref index); // SKIP GROUP 1 AND GROUP SETTINGS lINES if (lines[index].ToUpper() == "GROUP 1") { ++index; } if (lines[index].ToUpper() == "GROUP SETTINGS:") { ++index; } commaSeparatedEventReport.GroupSetting = Settings.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); ++index; commaSeparatedEventReport.ControlEquations = ControlEquation.Parse(lines, ref index); EventFile.SkipBlanks(lines, ref index); commaSeparatedEventReport.GlobalSetting = Settings.Parse(lines, ref index); } catch (IndexOutOfRangeException) { if (index < lines.Length) { throw; } } return(commaSeparatedEventReport); }
public bool Remove(CommaSeparatedEventReport commaSeparatedEventReport) { return m_sections.Remove(commaSeparatedEventReport); }
private ParsedChannel MakeParsedChannel(CommaSeparatedEventReport report, Channel<double> channel) { List<DateTime> timeSamples = report.AnalogSection.TimeChannel.Samples.ToList(); List<object> xValues = timeSamples .Select(time => time - timeSamples[0]) .Select(timeSpan => timeSpan.TotalSeconds) .Cast<object>() .ToList(); ParsedChannel parsedChannel = new ParsedChannel() { Name = string.Format("({0}) {1}", report.Command, channel.Name), TimeValues = timeSamples, XValues = xValues, YValues = channel.Samples.Cast<object>().ToList() }; return parsedChannel; }
private Channel MakeParsedDigital(CommaSeparatedEventReport report, int channelIndex) { Channel channel = new Channel(); Series series = new Series(); Channel<bool> digitalChannel = report.AnalogSection.DigitalChannels[channelIndex]; channel.Name = digitalChannel.Name; channel.HarmonicGroup = 0; channel.MeasurementType = new MeasurementType(); channel.MeasurementType.Name = "Digital"; channel.MeasurementCharacteristic = new MeasurementCharacteristic(); channel.MeasurementCharacteristic.Name = "Instantaneous"; channel.Phase = new Phase(); channel.Phase.Name = "None"; series.Channel = channel; series.SeriesType = new SeriesType(); series.SeriesType.Name = "Values"; series.SourceIndexes = channelIndex.ToString(); channel.MeasurementType.Description = channel.MeasurementType.Name; channel.MeasurementCharacteristic.Description = channel.MeasurementCharacteristic.Name; channel.Phase.Description = "No phase"; series.SeriesType.Description = series.SeriesType.Name; return channel; }
private Channel MakeParsedAnalog(CommaSeparatedEventReport report, int channelIndex) { Channel channel = new Channel(); Series series = new Series(); Channel<double> analogChannel = report.AnalogSection.AnalogChannels[channelIndex]; channel.Name = analogChannel.Name; channel.HarmonicGroup = 0; channel.MeasurementType = new MeasurementType(); channel.MeasurementCharacteristic = new MeasurementCharacteristic(); channel.MeasurementCharacteristic.Name = "Instantaneous"; channel.Phase = new Phase(); series.Channel = channel; series.SeriesType = new SeriesType(); series.SeriesType.Name = "Values"; series.SourceIndexes = channelIndex.ToString(); switch (analogChannel.Name) { case "VA": case "VB": case "VC": case "VS": case "VDC": case "Freq": channel.MeasurementType.Name = "Voltage"; break; case "IA": case "IB": case "IC": case "IN": case "IG": case "IR": channel.MeasurementType.Name = "Current"; break; default: channel.MeasurementType.Name = "Unknown"; break; } switch (analogChannel.Name) { case "VA": case "IA": case "Freq": channel.Phase.Name = "AN"; channel.Phase.Description = "A-phase to neutral"; break; case "VB": case "IB": channel.Phase.Name = "BN"; channel.Phase.Description = "B-phase to neutral"; break; case "VC": case "IC": channel.Phase.Name = "CN"; channel.Phase.Description = "C-phase to neutral"; break; case "IN": channel.Phase.Name = "NG"; channel.Phase.Description = "Neutral to ground"; break; case "IG": channel.Phase.Name = "Ground"; channel.Phase.Description = "Ground"; break; case "IR": channel.Phase.Name = "RES"; channel.Phase.Description = "Residual"; break; default: case "VS": case "VDC": channel.Phase.Name = "Unknown"; channel.Phase.Description = "Unknown"; break; } channel.MeasurementType.Description = channel.MeasurementType.Name; channel.MeasurementCharacteristic.Description = channel.MeasurementCharacteristic.Name; series.SeriesType.Description = series.SeriesType.Name; return channel; }