Beispiel #1
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
 public bool Remove(CommaSeparatedEventReport commaSeparatedEventReport)
 {
     return(m_sections.Remove(commaSeparatedEventReport));
 }
Beispiel #6
0
 public void Add(CommaSeparatedEventReport commaSeparatedEventReport)
 {
     m_sections.Add(commaSeparatedEventReport);
 }
Beispiel #7
0
        // 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);
        }
Beispiel #8
0
        // 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);
        }
Beispiel #10
0
 public bool Remove(CommaSeparatedEventReport commaSeparatedEventReport)
 {
     return m_sections.Remove(commaSeparatedEventReport);
 }
Beispiel #11
0
 public void Add(CommaSeparatedEventReport commaSeparatedEventReport)
 {
     m_sections.Add(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;
        }