Пример #1
0
        private int ReadTimestamp(byte[] buffer)
        {
            int index = 0;

            // Read sample index
            uint sample = LittleEndian.ToUInt32(buffer, index);

            index += 4;

            // Get timestamp of this record
            Timestamp = DateTime.MinValue;

            // If sample rates are defined, this is the preferred method for timestamp resolution
            if (InferTimeFromSampleRates && m_schema.SampleRates.Length > 0)
            {
                // Find rate for given sample
                SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);

                if (sampleRate.Rate > 0.0D)
                {
                    Timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);
                }
            }

            // Read microsecond timestamp
            uint microseconds = LittleEndian.ToUInt32(buffer, index);

            index += 4;

            // Fall back on specified microsecond time
            if (Timestamp == DateTime.MinValue)
            {
                Timestamp = new DateTime(Ticks.FromMicroseconds(microseconds * m_schema.TimeFactor) + m_schema.StartTime.Value);
            }

            // Apply timestamp offset to restore UTC timezone
            if (AdjustToUTC)
            {
                TimeOffset offset = Schema.TimeCode ?? new TimeOffset();
                Timestamp = new DateTime(Timestamp.Ticks + offset.TickOffset, DateTimeKind.Utc);
            }

            return(index);
        }
Пример #2
0
        /// <summary>
        /// Creates a new COMTRADE configuration <see cref="Schema"/>.
        /// </summary>
        /// <param name="metadata">Schema <see cref="ChannelMetadata"/> records.</param>
        /// <param name="stationName">Station name for the schema.</param>
        /// <param name="deviceID">Device ID for the schema.</param>
        /// <param name="dataStartTime">Data start time.</param>
        /// <param name="sampleCount">Total data samples (i.e., total number of rows).</param>
        /// <param name="isBinary">Determines if data file should be binary or ASCII - defaults to <c>true</c> for binary.</param>
        /// <param name="timeFactor">Time factor to use in schema - defaults to 1000.</param>
        /// <param name="samplingRate">Desired sampling rate - defaults to 33.3333Hz.</param>
        /// <param name="nominalFrequency">Nominal frequency - defaults to 60Hz.</param>
        /// <param name="includeFracSecDefinition">Determines if the FRACSEC word digital definitions should be included - defaults to <c>true</c>.</param>
        /// <returns>New COMTRADE configuration <see cref="Schema"/>.</returns>
        /// <remarks>
        /// This function is primarily intended to create a configuration based on synchrophasor data
        /// (see Annex H: Schema for Phasor Data 2150 Using the COMTRADE File Standard in IEEE C37.111-2010),
        /// it may be necessary to manually create a schema object for other COMTRADE needs. You can call
        /// the <see cref="Schema.FileImage"/> property to return a string that that can be written to a file
        /// that will be the contents of the configuration file.
        /// </remarks>
        public static Schema CreateSchema(IEnumerable<ChannelMetadata> metadata, string stationName, string deviceID, Ticks dataStartTime, int sampleCount, bool isBinary = true, double timeFactor = 1.0D, double samplingRate = 30.0D, double nominalFrequency = 60.0D, bool includeFracSecDefinition = true)
        {
            Schema schema = new Schema();

            schema.StationName = stationName;
            schema.DeviceID = deviceID;

            SampleRate samplingFrequency = new SampleRate();
            samplingFrequency.Rate = samplingRate;
            samplingFrequency.EndSample = sampleCount;

            schema.SampleRates = new[] { samplingFrequency };

            Timestamp startTime;
            startTime.Value = dataStartTime;
            schema.StartTime = startTime;
            schema.TriggerTime = startTime;

            schema.FileType = isBinary ? FileType.Binary : FileType.Ascii;
            schema.TimeFactor = timeFactor;

            List<AnalogChannel> analogChannels = new List<AnalogChannel>();
            List<DigitalChannel> digitalChannels = new List<DigitalChannel>();
            int analogIndex = 1;
            int digitalIndex = 1;

            if (includeFracSecDefinition)
            {
                // Add default time quality digitals for IEEE C37.118 FRACSEC word. Note that these flags, as
                // defined in Annex H of the IEEE C37.111-2010 standard, assume full export was all from one
                // source device. This a poor assumption since data can be exported from historical data for any
                // number of points which could have come from any number of devices all with different FRACSEC
                // values. Regardless there is only one FRACSEC definition defined and, if included, it must
                // come as the first set of digitals in the COMTRADE configuration.
                for (int i = 0; i < 4; i++)
                {
                    digitalChannels.Add(new DigitalChannel
                    {
                        Index = digitalIndex,
                        Name = "TQ_CNT" + i,
                        PhaseID = "T" + digitalIndex++
                    });
                }

                digitalChannels.Add(new DigitalChannel
                {
                    Index = digitalIndex,
                    Name = "TQ_LSPND",
                    PhaseID = "T" + digitalIndex++
                });


                digitalChannels.Add(new DigitalChannel
                {
                    Index = digitalIndex,
                    Name = "TQ_LSOCC",
                    PhaseID = "T" + digitalIndex++
                });

                digitalChannels.Add(new DigitalChannel
                {
                    Index = digitalIndex,
                    Name = "TQ_LSDIR",
                    PhaseID = "T" + digitalIndex++
                });

                digitalChannels.Add(new DigitalChannel
                {
                    Index = digitalIndex,
                    Name = "RSV",
                    PhaseID = "T" + digitalIndex++
                });

                for (int i = 1; i < 9; i++)
                {
                    digitalChannels.Add(new DigitalChannel
                    {
                        Index = digitalIndex,
                        Name = "RESV" + i,
                        PhaseID = "T" + digitalIndex++
                    });
                }
            }

            // Add meta data for selected points sorted analogs followed by status flags then digitals
            foreach (ChannelMetadata record in metadata.OrderBy(m => m, ChannelMetadataSorter.Default))
            {
                if (record.IsDigital)
                {
                    // Every synchrophasor digital is 16-bits
                    for (int i = 0; i < 16; i++)
                    {
                        digitalChannels.Add(new DigitalChannel
                        {
                            Index = digitalIndex++,
                            Name = record.Name,
                            PhaseID = "B" + i.ToString("X")
                        });
                    }
                }
                else
                {
                    switch (record.SignalType)
                    {
                        case SignalType.IPHM: // Current Magnitude
                            analogChannels.Add(new AnalogChannel
                            {
                                Index = analogIndex++,
                                Name = record.Name,
                                PhaseID = "Pm",
                                Units = "A",
                                Multiplier = 0.05D
                            });
                            break;
                        case SignalType.VPHM: // Voltage Magnitude
                            analogChannels.Add(new AnalogChannel
                            {
                                Index = analogIndex++,
                                Name = record.Name,
                                PhaseID = "Pm",
                                Units = "V",
                                Multiplier = 5.77362D
                            });
                            break;
                        case SignalType.IPHA: // Current Phase Angle
                        case SignalType.VPHA: // Voltage Phase Angle
                            analogChannels.Add(new AnalogChannel
                            {
                                Index = analogIndex++,
                                Name = record.Name,
                                PhaseID = "Pa",
                                Units = "Rads",
                                Multiplier = 1.0E-4D
                            });
                            break;
                        case SignalType.FREQ: // Frequency
                            analogChannels.Add(new AnalogChannel
                            {
                                Index = analogIndex++,
                                Name = record.Name,
                                PhaseID = "F",
                                Units = "Hz",
                                Adder = (double)nominalFrequency,
                                Multiplier = 0.001D
                            });
                            break;
                        case SignalType.DFDT: // Frequency Delta (dF/dt)
                            analogChannels.Add(new AnalogChannel
                            {
                                Index = analogIndex++,
                                Name = record.Name,
                                PhaseID = "dF",
                                Units = "Hz/s",
                                Multiplier = 0.01D
                            });
                            break;
                        case SignalType.FLAG: // Status flags
                            // Add synchrophasor status flag specific digitals
                            int statusIndex = 0;

                            for (int i = 1; i < 5; i++)
                            {
                                digitalChannels.Add(new DigitalChannel
                                {
                                    Index = digitalIndex++,
                                    Name = record.Name + ":TRG" + i,
                                    PhaseID = "S" + statusIndex++.ToString("X")
                                });
                            }

                            for (int i = 1; i < 3; i++)
                            {
                                digitalChannels.Add(new DigitalChannel
                                {
                                    Index = digitalIndex++,
                                    Name = record.Name + ":UNLK" + i,
                                    PhaseID = "S" + statusIndex++.ToString("X")
                                });
                            }

                            for (int i = 1; i < 5; i++)
                            {
                                digitalChannels.Add(new DigitalChannel
                                {
                                    Index = digitalIndex++,
                                    Name = record.Name + ":SEC" + i,
                                    PhaseID = "S" + statusIndex++.ToString("X")
                                });
                            }

                            digitalChannels.Add(new DigitalChannel
                            {
                                Index = digitalIndex++,
                                Name = record.Name + ":CFGCH",
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });

                            digitalChannels.Add(new DigitalChannel
                            {
                                Index = digitalIndex++,
                                Name = record.Name + ":PMUTR",
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });

                            digitalChannels.Add(new DigitalChannel
                            {
                                Index = digitalIndex++,
                                Name = record.Name + ":SORT",
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });

                            digitalChannels.Add(new DigitalChannel
                            {
                                Index = digitalIndex++,
                                Name = record.Name + ":SYNC",
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });

                            digitalChannels.Add(new DigitalChannel
                            {
                                Index = digitalIndex++,
                                Name = record.Name + ":PMUERR",
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });

                            digitalChannels.Add(new DigitalChannel
                            {
                                Index = digitalIndex++,
                                Name = record.Name + ":DTVLD",
                                PhaseID = "S" + statusIndex.ToString("X")
                            });
                            break;
                        default:     // All other signals assumed to be analog values
                            analogChannels.Add(new AnalogChannel
                            {
                                Index = analogIndex++,
                                Name = record.Name,
                                PhaseID = ""
                            });
                            break;
                    }
                }
            }

            schema.AnalogChannels = analogChannels.ToArray();
            schema.DigitalChannels = digitalChannels.ToArray();
            schema.NominalFrequency = nominalFrequency;

            return schema;
        }
Пример #3
0
        /// <summary>
        /// Creates a new COMTRADE configuration <see cref="Schema"/>.
        /// </summary>
        /// <param name="metadata">Schema <see cref="ChannelMetadata"/> records.</param>
        /// <param name="stationName">Station name for the schema.</param>
        /// <param name="deviceID">Device ID for the schema.</param>
        /// <param name="dataStartTime">Data start time.</param>
        /// <param name="sampleCount">Total data samples (i.e., total number of rows).</param>
        /// <param name="version">Target schema version - defaults to 1999.</param>
        /// <param name="fileType">Determines the data file type for the schema.</param>
        /// <param name="timeFactor">Time factor to use in schema - defaults to 1000.</param>
        /// <param name="samplingRate">Desired sampling rate - defaults to 33.3333Hz.</param>
        /// <param name="nominalFrequency">Nominal frequency - defaults to 60Hz.</param>
        /// <param name="includeFracSecDefinition">Determines if the FRACSEC word digital definitions should be included - defaults to <c>true</c>.</param>
        /// <returns>New COMTRADE configuration <see cref="Schema"/>.</returns>
        /// <remarks>
        /// <para>
        /// This function is primarily intended to create a configuration based on synchrophasor data
        /// (see Annex H: Schema for Phasor Data 2150 Using the COMTRADE File Standard in IEEE C37.111-2010),
        /// it may be necessary to manually create a schema object for other COMTRADE needs. You can call
        /// the <see cref="Schema.FileImage"/> property to return a string that can be written to a file
        /// that will be the contents of the configuration file.
        /// </para>
        /// <para>
        /// Linear scaling factors for analog channels, i.e., adders and multipliers, will be set to reasonable
        /// values based on the channel type. These should be adjusted as needed based on actual channel value
        /// ranges. Note that for <see cref="FileType.Float32"/> the multiplier will be <c>1.0</c> and the adder
        /// will be <c>0.0</c> for all analog values.
        /// </para>
        /// </remarks>
        public static Schema CreateSchema(IEnumerable <ChannelMetadata> metadata, string stationName, string deviceID, Ticks dataStartTime, int sampleCount, int version = 1999, FileType fileType = FileType.Binary, double timeFactor = 1.0D, double samplingRate = 30.0D, double nominalFrequency = 60.0D, bool includeFracSecDefinition = true)
        {
            Schema schema = new Schema
            {
                StationName = stationName,
                DeviceID    = deviceID,
                Version     = version
            };

            SampleRate samplingFrequency = new SampleRate
            {
                Rate      = samplingRate,
                EndSample = sampleCount
            };

            schema.SampleRates = new[] { samplingFrequency };

            Timestamp startTime;

            startTime.Value    = dataStartTime;
            schema.StartTime   = startTime;
            schema.TriggerTime = startTime;

            schema.FileType   = fileType;
            schema.TimeFactor = timeFactor;

            List <AnalogChannel>  analogChannels  = new List <AnalogChannel>();
            List <DigitalChannel> digitalChannels = new List <DigitalChannel>();
            bool targetFloatingPoint = fileType == FileType.Float32;

            int analogIndex  = 1;
            int digitalIndex = 1;

            if (includeFracSecDefinition)
            {
                // Add default time quality digitals for IEEE C37.118 FRACSEC word. Note that these flags, as
                // defined in Annex H of the IEEE C37.111-2010 standard, assume full export was all from one
                // source device. This a poor assumption since data can be exported from historical data for any
                // number of points which could have come from any number of devices all with different FRACSEC
                // values. Regardless there is only one FRACSEC definition defined and, if included, it must
                // come as the first set of digitals in the COMTRADE configuration.
                for (int i = 0; i < 4; i++)
                {
                    digitalChannels.Add(new DigitalChannel(schema.Version)
                    {
                        Index   = digitalIndex,
                        Name    = "TQ_CNT" + i,
                        PhaseID = "T" + digitalIndex++
                    });
                }

                digitalChannels.Add(new DigitalChannel(schema.Version)
                {
                    Index   = digitalIndex,
                    Name    = "TQ_LSPND",
                    PhaseID = "T" + digitalIndex++
                });


                digitalChannels.Add(new DigitalChannel(schema.Version)
                {
                    Index   = digitalIndex,
                    Name    = "TQ_LSOCC",
                    PhaseID = "T" + digitalIndex++
                });

                digitalChannels.Add(new DigitalChannel(schema.Version)
                {
                    Index   = digitalIndex,
                    Name    = "TQ_LSDIR",
                    PhaseID = "T" + digitalIndex++
                });

                digitalChannels.Add(new DigitalChannel(schema.Version)
                {
                    Index   = digitalIndex,
                    Name    = "RSV",
                    PhaseID = "T" + digitalIndex++
                });

                for (int i = 1; i < 9; i++)
                {
                    digitalChannels.Add(new DigitalChannel(schema.Version)
                    {
                        Index   = digitalIndex,
                        Name    = "RESV" + i,
                        PhaseID = "T" + digitalIndex++
                    });
                }
            }

            // Add meta data for selected points sorted analogs followed by status flags then digitals
            foreach (ChannelMetadata record in metadata.OrderBy(m => m, ChannelMetadataSorter.Default))
            {
                if (record.IsDigital)
                {
                    // Every synchrophasor digital is 16-bits
                    for (int i = 0; i < 16; i++)
                    {
                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name,
                            PhaseID = "B" + i.ToString("X")
                        });
                    }
                }
                else
                {
                    switch (record.SignalType)
                    {
                    case SignalType.IPHM:     // Current Magnitude
                        analogChannels.Add(new AnalogChannel(schema.Version, targetFloatingPoint)
                        {
                            Index            = analogIndex++,
                            Name             = record.Name,
                            Units            = record.Units ?? "A",
                            PhaseID          = "Pm",
                            CircuitComponent = record.CircuitComponent,
                            Multiplier       = targetFloatingPoint ? 1.0D : AnalogChannel.DefaultCurrentMagnitudeMultiplier
                        });
                        break;

                    case SignalType.VPHM:     // Voltage Magnitude
                        analogChannels.Add(new AnalogChannel(schema.Version, targetFloatingPoint)
                        {
                            Index            = analogIndex++,
                            Name             = record.Name,
                            Units            = record.Units ?? "V",
                            PhaseID          = "Pm",
                            CircuitComponent = record.CircuitComponent,
                            Multiplier       = targetFloatingPoint ? 1.0D : AnalogChannel.DefaultVoltageMagnitudeMultiplier
                        });
                        break;

                    case SignalType.IPHA:     // Current Phase Angle
                    case SignalType.VPHA:     // Voltage Phase Angle
                        analogChannels.Add(new AnalogChannel(schema.Version, targetFloatingPoint)
                        {
                            Index            = analogIndex++,
                            Name             = record.Name,
                            Units            = record.Units ?? "Rads",
                            PhaseID          = "Pa",
                            CircuitComponent = record.CircuitComponent,
                            Multiplier       = targetFloatingPoint ? 1.0D : AnalogChannel.DefaultPhaseAngleMultiplier
                        });
                        break;

                    case SignalType.FREQ:     // Frequency
                        analogChannels.Add(new AnalogChannel(schema.Version, targetFloatingPoint)
                        {
                            Index            = analogIndex++,
                            Name             = record.Name,
                            Units            = record.Units ?? "Hz",
                            PhaseID          = "F",
                            CircuitComponent = record.CircuitComponent,
                            Adder            = targetFloatingPoint ? 0.0D : nominalFrequency,
                            Multiplier       = targetFloatingPoint ? 1.0D : AnalogChannel.DefaultFrequencyMultiplier
                        });
                        break;

                    case SignalType.DFDT:     // Frequency Delta (dF/dt)
                        analogChannels.Add(new AnalogChannel(schema.Version, targetFloatingPoint)
                        {
                            Index            = analogIndex++,
                            Name             = record.Name,
                            Units            = record.Units ?? "Hz/s",
                            PhaseID          = "dF",
                            CircuitComponent = record.CircuitComponent,
                            Multiplier       = targetFloatingPoint ? 1.0D : AnalogChannel.DefaultDfDtMultiplier
                        });
                        break;

                    case SignalType.FLAG:     // Status flags
                        // Add synchrophasor status flag specific digitals
                        int statusIndex = 0;

                        for (int i = 1; i < 5; i++)
                        {
                            digitalChannels.Add(new DigitalChannel(schema.Version)
                            {
                                Index   = digitalIndex++,
                                Name    = record.Name + ":TRG" + i,
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });
                        }

                        for (int i = 1; i < 3; i++)
                        {
                            digitalChannels.Add(new DigitalChannel(schema.Version)
                            {
                                Index   = digitalIndex++,
                                Name    = record.Name + ":UNLK" + i,
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });
                        }

                        for (int i = 1; i < 5; i++)
                        {
                            digitalChannels.Add(new DigitalChannel(schema.Version)
                            {
                                Index   = digitalIndex++,
                                Name    = record.Name + ":SEC" + i,
                                PhaseID = "S" + statusIndex++.ToString("X")
                            });
                        }

                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name + ":CFGCH",
                            PhaseID = "S" + statusIndex++.ToString("X")
                        });

                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name + ":PMUTR",
                            PhaseID = "S" + statusIndex++.ToString("X")
                        });

                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name + ":SORT",
                            PhaseID = "S" + statusIndex++.ToString("X")
                        });

                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name + ":SYNC",
                            PhaseID = "S" + statusIndex++.ToString("X")
                        });

                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name + ":PMUERR",
                            PhaseID = "S" + statusIndex++.ToString("X")
                        });

                        digitalChannels.Add(new DigitalChannel(schema.Version)
                        {
                            Index   = digitalIndex++,
                            Name    = record.Name + ":DTVLD",
                            PhaseID = "S" + statusIndex.ToString("X")
                        });
                        break;

                    default:         // All other signals assumed to be analog values
                        analogChannels.Add(new AnalogChannel(schema.Version, targetFloatingPoint)
                        {
                            Index            = analogIndex++,
                            Name             = record.Name,
                            PhaseID          = "",
                            Units            = record.Units,
                            CircuitComponent = record.CircuitComponent,
                            Multiplier       = targetFloatingPoint ? 1.0D : AnalogChannel.DefaultAnalogMultipler
                        });
                        break;
                    }
                }
            }

            schema.AnalogChannels   = analogChannels.ToArray();
            schema.DigitalChannels  = digitalChannels.ToArray();
            schema.NominalFrequency = nominalFrequency;

            return(schema);
        }
Пример #4
0
        // Handle binary file read
        private bool ReadNextBinary()
        {
            FileStream currentFile  = m_fileStreams[m_streamIndex];
            int        recordLength = m_schema.BinaryRecordLength;

            byte[] buffer = new byte[recordLength];

            // Read next record from file
            int bytesRead = currentFile.Read(buffer, 0, recordLength);

            // See if we have reached the end of this file
            if (bytesRead == 0)
            {
                m_streamIndex++;

                // There is more to read if there is another file
                return(m_streamIndex < m_fileStreams.Length && ReadNext());
            }

            if (bytesRead == recordLength)
            {
                int index = 0;

                // Read sample index
                uint sample = LittleEndian.ToUInt32(buffer, index);
                index += 4;

                // Get timestamp of this record
                m_timestamp = DateTime.MinValue;

                // If sample rates are defined, this is the preferred method for timestamp resolution
                if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0)
                {
                    // Find rate for given sample
                    SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);

                    if (sampleRate.Rate > 0.0D)
                    {
                        m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);
                    }
                }

                // Read microsecond timestamp
                uint microseconds = LittleEndian.ToUInt32(buffer, index);
                index += 4;

                // Fall back on specified microsecond time
                if (m_timestamp == DateTime.MinValue)
                {
                    m_timestamp = new DateTime(Ticks.FromMicroseconds(microseconds * m_schema.TimeFactor) + m_schema.StartTime.Value);
                }

                // Parse all analog record values
                for (int i = 0; i < m_schema.AnalogChannels.Length; i++)
                {
                    // Read next value
                    m_values[i] = LittleEndian.ToInt16(buffer, index) * m_schema.AnalogChannels[i].Multiplier + m_schema.AnalogChannels[i].Adder;
                    index      += 2;
                }

                int    valueIndex   = m_schema.AnalogChannels.Length;
                int    digitalWords = m_schema.DigitalWords;
                ushort digitalWord;

                for (int i = 0; i < digitalWords; i++)
                {
                    // Read next digital word
                    digitalWord = LittleEndian.ToUInt16(buffer, index);
                    index      += 2;

                    // Distribute each bit of digital word through next 16 digital values
                    for (int j = 0; j < 16 && valueIndex < m_values.Length; j++, valueIndex++)
                    {
                        m_values[valueIndex] = digitalWord.CheckBits(BitExtensions.BitVal(j)) ? 1.0D : 0.0D;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Failed to read enough bytes from COMTRADE file for a record as defined by schema - possible schema/data file mismatch or file corruption.");
            }

            return(true);
        }
Пример #5
0
        // Handle ASCII file read
        private bool ReadNextAscii()
        {
            if ((object)m_fileReaders == null)
            {
                m_fileReaders = new StreamReader[m_fileStreams.Length];

                for (int i = 0; i < m_fileStreams.Length; i++)
                {
                    m_fileReaders[i] = new StreamReader(m_fileStreams[i]);
                }
            }

            // Read next line of record values
            StreamReader reader = m_fileReaders[m_streamIndex];
            string       line   = reader.ReadLine();

            string[] elems = ((object)line != null) ? line.Split(',') : null;

            // See if we have reached the end of this file
            if ((object)elems == null || elems.Length != m_values.Length + 2)
            {
                if (reader.EndOfStream)
                {
                    m_streamIndex++;

                    // There is more to read if there is another file
                    return(m_streamIndex < m_fileStreams.Length && ReadNext());
                }

                throw new InvalidOperationException("COMTRADE schema does not match number of elements found in ASCII data file.");
            }

            // Parse row of data
            uint sample = uint.Parse(elems[0]);

            // Get timestamp of this record
            m_timestamp = DateTime.MinValue;

            // If sample rates are defined, this is the preferred method for timestamp resolution
            if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0)
            {
                // Find rate for given sample
                SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);

                if (sampleRate.Rate > 0.0D)
                {
                    m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);
                }
            }

            // Fall back on specified microsecond time
            if (m_timestamp == DateTime.MinValue)
            {
                m_timestamp = new DateTime(Ticks.FromMicroseconds(uint.Parse(elems[1]) * m_schema.TimeFactor) + m_schema.StartTime.Value);
            }

            // Parse all record values
            for (int i = 0; i < m_values.Length; i++)
            {
                m_values[i] = double.Parse(elems[i + 2]);

                if (i < m_schema.AnalogChannels.Length)
                {
                    m_values[i] *= m_schema.AnalogChannels[i].Multiplier;
                    m_values[i] += m_schema.AnalogChannels[i].Adder;
                }
            }

            return(true);
        }
Пример #6
0
        // Handle ASCII file read
        private bool ReadNextAscii()
        {
            // For ASCII files, we wrap file streams with file readers
            if ((object)m_fileReaders == null)
            {
                m_fileReaders = new StreamReader[m_fileStreams.Length];

                for (int i = 0; i < m_fileStreams.Length; i++)
                {
                    m_fileReaders[i] = new StreamReader(m_fileStreams[i]);
                }
            }

            // Read next line of record values
            StreamReader reader = m_fileReaders[m_streamIndex];
            string       line   = reader.ReadLine();

            string[] elems = ((object)line != null) ? line.Split(',') : null;

            // See if we have reached the end of this file
            if ((object)elems == null || elems.Length != Values.Length + 2)
            {
                if (reader.EndOfStream)
                {
                    return(ReadNextFile());
                }

                throw new InvalidOperationException("COMTRADE schema does not match number of elements found in ASCII data file.");
            }

            // Parse row of data
            uint sample = uint.Parse(elems[0]);

            // Get timestamp of this record
            Timestamp = DateTime.MinValue;

            // If sample rates are defined, this is the preferred method for timestamp resolution
            if (InferTimeFromSampleRates && m_schema.SampleRates.Length > 0)
            {
                // Find rate for given sample
                SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);

                if (sampleRate.Rate > 0.0D)
                {
                    Timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);
                }
            }

            // Fall back on specified microsecond time
            if (Timestamp == DateTime.MinValue)
            {
                Timestamp = new DateTime(Ticks.FromMicroseconds(uint.Parse(elems[1]) * m_schema.TimeFactor) + m_schema.StartTime.Value);
            }

            // Apply timestamp offset to restore UTC timezone
            if (AdjustToUTC)
            {
                TimeOffset offset = Schema.TimeCode ?? new TimeOffset();
                Timestamp = new DateTime(Timestamp.Ticks + offset.TickOffset, DateTimeKind.Utc);
            }

            // Parse all record values
            for (int i = 0; i < Values.Length; i++)
            {
                Values[i] = double.Parse(elems[i + 2]);

                if (i < m_schema.AnalogChannels.Length)
                {
                    Values[i] = AdjustValue(Values[i], i);
                }
            }

            return(true);
        }