예제 #1
0
        /// <summary>
        /// Raises the <see cref="FrameParserBase{TypeIndentifier}.ReceivedConfigurationFrame"/> event.
        /// </summary>
        /// <param name="frame"><see cref="IConfigurationFrame"/> to send to <see cref="FrameParserBase{TypeIndentifier}.ReceivedConfigurationFrame"/> event.</param>
        protected override void OnReceivedConfigurationFrame(IConfigurationFrame frame)
        {
            // We override this method so we can cache configuration frame when it's received
            base.OnReceivedConfigurationFrame(frame);

            // Cache new configuration frame for parsing subsequent data frames...
            ConfigurationFrame configurationFrame = frame as ConfigurationFrame;

            if (configurationFrame != null)
            {
                m_configurationFrame = configurationFrame;
            }
        }
예제 #2
0
        // Static Methods

        // Attempts to cast given frame into a BPA PDCstream configuration frame - theoretically this will
        // allow the same configuration frame to be used for any protocol implementation
        internal static ConfigurationFrame CastToDerivedConfigurationFrame(IConfigurationFrame sourceFrame, string configurationFileName)
        {
            // See if frame is already a BPA PDCstream configuration frame (if so, we don't need to do any work)
            ConfigurationFrame derivedFrame = sourceFrame as ConfigurationFrame;

            if (derivedFrame == null)
            {
                // Create a new BPA PDCstream configuration frame converted from equivalent configuration information
                ConfigurationCell    derivedCell;
                IFrequencyDefinition sourceFrequency;

                derivedFrame = new ConfigurationFrame(sourceFrame.Timestamp, configurationFileName, 1, RevisionNumber.Revision2, StreamType.Compact);

                foreach (IConfigurationCell sourceCell in sourceFrame.Cells)
                {
                    // Create new derived configuration cell
                    derivedCell = new ConfigurationCell(derivedFrame, sourceCell.IDCode, sourceCell.NominalFrequency);

                    // Create equivalent derived phasor definitions
                    foreach (IPhasorDefinition sourcePhasor in sourceCell.PhasorDefinitions)
                    {
                        derivedCell.PhasorDefinitions.Add(new PhasorDefinition(derivedCell, sourcePhasor.Label, sourcePhasor.ScalingValue, sourcePhasor.Offset, sourcePhasor.PhasorType, null));
                    }

                    // Create equivalent derived frequency definition
                    sourceFrequency = sourceCell.FrequencyDefinition;

                    if (sourceFrequency != null)
                    {
                        derivedCell.FrequencyDefinition = new FrequencyDefinition(derivedCell, sourceFrequency.Label);
                    }

                    // Create equivalent derived analog definitions (assuming analog type = SinglePointOnWave)
                    foreach (IAnalogDefinition sourceAnalog in sourceCell.AnalogDefinitions)
                    {
                        derivedCell.AnalogDefinitions.Add(new AnalogDefinition(derivedCell, sourceAnalog.Label, sourceAnalog.ScalingValue, sourceAnalog.Offset, sourceAnalog.AnalogType));
                    }

                    // Create equivalent derived digital definitions
                    foreach (IDigitalDefinition sourceDigital in sourceCell.DigitalDefinitions)
                    {
                        derivedCell.DigitalDefinitions.Add(new DigitalDefinition(derivedCell, sourceDigital.Label));
                    }

                    // Add cell to frame
                    derivedFrame.Cells.Add(derivedCell);
                }
            }

            return(derivedFrame);
        }
예제 #3
0
        /// <summary>
        /// Creates a new <see cref="CommonFrameHeader"/> from given <paramref name="buffer"/>.
        /// </summary>
        /// <param name="parseWordCountFromByte">Defines flag that interprets word count in packet header from a byte instead of a word.</param>
        /// <param name="usePhasorDataFileFormat">Defines flag that determines if source data is in the Phasor Data File Format (i.e., a DST file).</param>
        /// <param name="configFrame">Previously parsed configuration frame, if available.</param>
        /// <param name="buffer">Buffer that contains data to parse.</param>
        /// <param name="startIndex">Start index into buffer where valid data begins.</param>
        /// <param name="length">Maximum length of valid data from start index.</param>
        public CommonFrameHeader(bool parseWordCountFromByte, bool usePhasorDataFileFormat, ConfigurationFrame configFrame, byte[] buffer, int startIndex, int length)
        {
            uint secondOfCentury;

            // Determines if format is for DST file or streaming data
            m_usePhasorDataFileFormat = usePhasorDataFileFormat;

            if (m_usePhasorDataFileFormat)
            {
                // Handle phasor file format data protocol steps
                if (buffer[startIndex] == PhasorProtocols.Common.SyncByte && buffer[startIndex + 1] == Common.PhasorFileFormatFlag)
                {
                    // Bail out and leave frame length zero if there's not enough buffer to parse complete fixed portion of header
                    if (length >= DstHeaderFixedLength)
                    {
                        // Read full DST header
                        m_packetNumber = (byte)BPAPDCstream.FrameType.ConfigurationFrame;
                        m_fileType     = (FileType)buffer[startIndex + 2];
                        m_fileVersion  = (FileVersion)buffer[startIndex + 3];
                        m_sourceID     = Encoding.ASCII.GetString(buffer, startIndex + 4, 4);
                        uint headerLength = BigEndian.ToUInt32(buffer, startIndex + 8);
                        secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 12);

                        switch (m_fileType)
                        {
                        case FileType.PdcNtp:
                            RoughTimestamp = new NtpTimeTag(secondOfCentury, 0).ToDateTime().Ticks;
                            break;

                        case FileType.PdcUnix:
                            RoughTimestamp = new UnixTimeTag(secondOfCentury).ToDateTime().Ticks;
                            break;

                        default:
                            RoughTimestamp = 0;
                            break;
                        }

                        m_startSample    = BigEndian.ToUInt32(buffer, startIndex + 16);
                        m_sampleInterval = BigEndian.ToUInt16(buffer, startIndex + 20);
                        m_sampleRate     = BigEndian.ToUInt16(buffer, startIndex + 22);
                        m_rowLength      = BigEndian.ToUInt32(buffer, startIndex + 24);
                        m_totalRows      = BigEndian.ToUInt32(buffer, startIndex + 28);
                        secondOfCentury  = BigEndian.ToUInt32(buffer, startIndex + 32);

                        switch (m_fileType)
                        {
                        case FileType.PdcNtp:
                            m_triggerTime = new NtpTimeTag(secondOfCentury, 0).ToDateTime().Ticks;
                            break;

                        case FileType.PdcUnix:
                            m_triggerTime = new UnixTimeTag(secondOfCentury).ToDateTime().Ticks;
                            break;

                        default:
                            m_triggerTime = 0;
                            break;
                        }

                        m_triggerSample   = BigEndian.ToUInt32(buffer, startIndex + 36);
                        m_preTriggerRows  = BigEndian.ToUInt32(buffer, startIndex + 40);
                        m_triggerPMU      = BigEndian.ToUInt16(buffer, startIndex + 44);
                        m_triggerType     = BigEndian.ToUInt16(buffer, startIndex + 46);
                        m_userInformation = Encoding.ASCII.GetString(buffer, startIndex + 48, 80).Trim();
                        m_pmuCount        = BigEndian.ToUInt32(buffer, startIndex + 128);
                        FrameLength       = unchecked ((ushort)headerLength);
                    }
                }
                else
                {
                    // Must assume this is a data row if there are no sync bytes
                    m_packetNumber = (byte)BPAPDCstream.FrameType.DataFrame;
                    m_rowFlags     = BigEndian.ToUInt32(buffer, startIndex);

                    if (configFrame is null)
                    {
                        FrameLength = FixedLength;
                    }
                    else
                    {
                        uint sampleIndex = configFrame.SampleIndex;
                        CommonFrameHeader configFrameHeader = configFrame.CommonHeader;

                        if (configFrameHeader is null)
                        {
                            FrameLength = FixedLength;
                        }
                        else
                        {
                            // Assign row length to make sure parser knows how much data it needs
                            FrameLength = unchecked ((ushort)configFrameHeader.RowLength);

                            // Calculate timestamp as offset plus sample index * frame rate
                            RoughTimestamp = configFrameHeader.RoughTimestamp + Ticks.FromSeconds(sampleIndex * (1.0D / configFrameHeader.FrameRate));
                        }

                        // Increment sample index for next row
                        configFrame.SampleIndex = sampleIndex + 1;
                    }
                }
            }
            else
            {
                // Handle streaming data protocol steps
                if (buffer[startIndex] != PhasorProtocols.Common.SyncByte)
                {
                    throw new InvalidOperationException($"Bad data stream, expected sync byte 0xAA as first byte in BPA PDCstream frame, got 0x{buffer[startIndex].ToString("X").PadLeft(2, '0')}");
                }

                // Get packet number
                m_packetNumber = buffer[startIndex + 1];

                // Some older streams have a bad word count (e.g., some data streams have a 0x01 as the third byte
                // in the stream - this should be a 0x00 to make the word count come out correctly).  The following
                // compensates for this erratic behavior
                m_wordCount = parseWordCountFromByte ? buffer[startIndex + 3] : BigEndian.ToUInt16(buffer, startIndex + 2);

                // If this is a data frame get a rough timestamp down to the second (full parse will get accurate timestamp), this way
                // data frames that don't get fully parsed because configuration hasn't been received will still show a timestamp
                if (m_packetNumber > 0 && length > 8)
                {
                    secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 4);

                    // Until configuration is available, we make a guess at time tag type - this will just be
                    // used for display purposes until a configuration frame arrives.  If second of century
                    // is greater than 3155673600 (SOC value for NTP timestamp 1/1/2007), then this is likely
                    // an NTP time stamp (else this is a Unix time tag for the year 2069 - not likely).
                    RoughTimestamp = secondOfCentury > 3155673600 ?
                                     new NtpTimeTag(secondOfCentury, 0).ToDateTime().Ticks :
                                     new UnixTimeTag(secondOfCentury).ToDateTime().Ticks;
                }
            }
        }
예제 #4
0
 /// <summary>
 /// Creates a new <see cref="ConfigurationCell"/> from specified parameters.
 /// </summary>
 /// <param name="parent">The reference to parent <see cref="ConfigurationFrame"/> of this <see cref="ConfigurationCell"/>.</param>
 /// <param name="idCode">The numeric ID code for this <see cref="ConfigurationCell"/>.</param>
 /// <param name="nominalFrequency">The nominal <see cref="LineFrequency"/> of the <see cref="FrequencyDefinition"/> of this <see cref="ConfigurationCell"/>.</param>
 public ConfigurationCell(ConfigurationFrame parent, ushort idCode, LineFrequency nominalFrequency = LineFrequency.Hz60)
     : this(parent)
 {
     IDCode           = idCode;
     NominalFrequency = nominalFrequency;
 }
예제 #5
0
 /// <summary>
 /// Creates a new <see cref="DataFrame"/> from specified parameters.
 /// </summary>
 /// <param name="timestamp">The exact timestamp, in <see cref="Ticks"/>, of the data represented by this <see cref="DataFrame"/>.</param>
 /// <param name="configurationFrame">The <see cref="ConfigurationFrame"/> associated with this <see cref="DataFrame"/>.</param>
 /// <param name="packetNumber">Packet number for this <see cref="DataFrame"/>.</param>
 /// <param name="sampleNumber">Sample number for this <see cref="DataFrame"/>.</param>
 /// <remarks>
 /// This constructor is used by a consumer to generate a BPA PDCstream data frame.
 /// </remarks>
 public DataFrame(Ticks timestamp, ConfigurationFrame configurationFrame, byte packetNumber, ushort sampleNumber)
     : base(new DataCellCollection(), timestamp, configurationFrame)
 {
     PacketNumber   = packetNumber;
     m_sampleNumber = sampleNumber;
 }
예제 #6
0
        /// <summary>
        /// Creates a new <see cref="PhasorDefinition"/> from specified parameters.
        /// </summary>
        /// <param name="parent">The <see cref="ConfigurationCell"/> parent of this <see cref="PhasorDefinition"/>.</param>
        /// <param name="index">Index of phasor within INI based configuration file.</param>
        /// <param name="entryValue">The entry value from the INI based configuration file.</param>
        public PhasorDefinition(ConfigurationCell parent, int index, string entryValue)
            : base(parent)
        {
            string[]         entry     = entryValue.Split(',');
            string           entryType = entry[0].Trim().Substring(0, 1).ToUpper();
            PhasorDefinition defaultPhasor;
            double           dValue;

            if (parent != null)
            {
                ConfigurationFrame configFile = this.Parent.Parent;

                if (entryType == "V")
                {
                    PhasorType    = PhasorType.Voltage;
                    defaultPhasor = configFile.DefaultPhasorV;
                }
                else if (entryType == "I")
                {
                    PhasorType    = PhasorType.Current;
                    defaultPhasor = configFile.DefaultPhasorI;
                }
                else
                {
                    PhasorType    = PhasorType.Voltage;
                    defaultPhasor = configFile.DefaultPhasorV;
                }
            }
            else
            {
                defaultPhasor = new PhasorDefinition(null as ConfigurationCell);
            }

            if (entry.Length > 1 && double.TryParse(entry[1].Trim(), out dValue))
            {
                Ratio = dValue;
            }
            else
            {
                Ratio = defaultPhasor.Ratio;
            }

            if (entry.Length > 2 && double.TryParse(entry[2].Trim(), out dValue))
            {
                CalFactor = dValue;
            }
            else
            {
                CalFactor = defaultPhasor.CalFactor;
            }

            if (entry.Length > 3 && double.TryParse(entry[3].Trim(), out dValue))
            {
                Offset = dValue;
            }
            else
            {
                Offset = defaultPhasor.Offset;
            }

            if (entry.Length > 4 && double.TryParse(entry[4].Trim(), out dValue))
            {
                Shunt = dValue;
            }
            else
            {
                Shunt = defaultPhasor.Shunt;
            }

            if (entry.Length > 5 && double.TryParse(entry[5].Trim(), out dValue))
            {
                VoltageReferenceIndex = (int)dValue;
            }
            else
            {
                VoltageReferenceIndex = defaultPhasor.VoltageReferenceIndex;
            }

            if (entry.Length > 6)
            {
                Label = entry[6].Trim();
            }
            else
            {
                Label = defaultPhasor.Label;
            }

            this.Index = index;
        }
예제 #7
0
        /// <summary>
        /// Creates a new <see cref="PhasorDefinition"/> from specified parameters.
        /// </summary>
        /// <param name="parent">The <see cref="ConfigurationCell"/> parent of this <see cref="PhasorDefinition"/>.</param>
        /// <param name="index">Index of phasor within INI based configuration file.</param>
        /// <param name="entryValue">The entry value from the INI based configuration file.</param>
        public PhasorDefinition(ConfigurationCell parent, int index, string entryValue)
            : base(parent)
        {
            string[]         entry     = entryValue.Split(',');
            string           entryType = entry[0].Trim().Substring(0, 1).ToUpper();
            PhasorDefinition defaultPhasor;

            if (parent is null)
            {
                defaultPhasor = new PhasorDefinition(null);
            }
            else
            {
                ConfigurationFrame configFile = Parent.Parent;

                switch (entryType)
                {
                case "V":
                    PhasorType    = PhasorType.Voltage;
                    defaultPhasor = configFile.DefaultPhasorV;
                    break;

                case "I":
                    PhasorType    = PhasorType.Current;
                    defaultPhasor = configFile.DefaultPhasorI;
                    break;

                default:
                    PhasorType    = PhasorType.Voltage;
                    defaultPhasor = configFile.DefaultPhasorV;
                    break;
                }
            }

            if (entry.Length > 1 && double.TryParse(entry[1].Trim(), out double dValue))
            {
                Ratio = dValue;
            }
            else
            {
                Ratio = defaultPhasor.Ratio;
            }

            if (entry.Length > 2 && double.TryParse(entry[2].Trim(), out dValue))
            {
                CalFactor = dValue;
            }
            else
            {
                CalFactor = defaultPhasor.CalFactor;
            }

            if (entry.Length > 3 && double.TryParse(entry[3].Trim(), out dValue))
            {
                Offset = dValue;
            }
            else
            {
                Offset = defaultPhasor.Offset;
            }

            if (entry.Length > 4 && double.TryParse(entry[4].Trim(), out dValue))
            {
                Shunt = dValue;
            }
            else
            {
                Shunt = defaultPhasor.Shunt;
            }

            if (entry.Length > 5 && double.TryParse(entry[5].Trim(), out dValue))
            {
                VoltageReferenceIndex = (int)dValue;
            }
            else
            {
                VoltageReferenceIndex = defaultPhasor.VoltageReferenceIndex;
            }

            Label = entry.Length > 6 ? entry[6].Trim() : defaultPhasor.Label;

            Index = index;
        }
예제 #8
0
파일: DataFrame.cs 프로젝트: xj0229/gsf
        /// <summary>
        /// Parses the binary header image.
        /// </summary>
        /// <param name="buffer">Binary image to parse.</param>
        /// <param name="startIndex">Start index into <paramref name="buffer"/> to begin parsing.</param>
        /// <param name="length">Length of valid data within <paramref name="buffer"/>.</param>
        /// <returns>The length of the data that was parsed.</returns>
        protected override int ParseHeaderImage(byte[] buffer, int startIndex, int length)
        {
            IDataFrameParsingState state = State;
            ConfigurationFrame     configurationFrame = state.ConfigurationFrame as ConfigurationFrame;

            // Check for unlikely occurrence of unexpected configuration frame type
            if ((object)configurationFrame == null)
            {
                throw new InvalidOperationException("Unexpected configuration frame encountered - BPA PDCstream configuration frame expected, cannot parse data frame.");
            }

            if (m_usePhasorDataFileFormat)
            {
                // Because in cases where PDCxchng is being used the data cell count will be smaller than the
                // configuration cell count - we save this count to calculate the offsets later
                state.CellCount = unchecked ((int)configurationFrame.CommonHeader.PmuCount);

                if (state.CellCount > configurationFrame.Cells.Count)
                {
                    throw new InvalidOperationException("Stream/Config File Mismatch: PMU count (" + state.CellCount + ") in stream does not match defined count in configuration file (" + configurationFrame.Cells.Count + ")");
                }

                return(CommonFrameHeader.FixedLength);
            }

            // Only need to parse what wasn't already parsed in common frame header
            int index = startIndex + CommonFrameHeader.FixedLength;

            // Parse frame timestamp
            uint secondOfCentury = BigEndian.ToUInt32(buffer, index);

            m_sampleNumber = BigEndian.ToUInt16(buffer, index + 4);
            index         += 6;

            if (configurationFrame.RevisionNumber == RevisionNumber.Revision0)
            {
                Timestamp = (new NtpTimeTag(secondOfCentury, 0)).ToDateTime().Ticks + (long)((m_sampleNumber - 1) * configurationFrame.TicksPerFrame);
            }
            else
            {
                Timestamp = (new UnixTimeTag(secondOfCentury)).ToDateTime().Ticks + (long)((m_sampleNumber - 1) * configurationFrame.TicksPerFrame);
            }

            // Because in cases where PDCxchng is being used the data cell count will be smaller than the
            // configuration cell count - we save this count to calculate the offsets later
            state.CellCount = BigEndian.ToUInt16(buffer, index);
            index          += 2;

            if (state.CellCount > configurationFrame.Cells.Count)
            {
                throw new InvalidOperationException("Stream/Config File Mismatch: PMU count (" + state.CellCount + ") in stream does not match defined count in configuration file (" + configurationFrame.Cells.Count + ")");
            }

            // We'll at least retrieve legacy labels if defined (might be useful for debugging dynamic changes in data-stream)
            if (configurationFrame.StreamType == StreamType.Legacy)
            {
                m_legacyLabels = new string[state.CellCount];

                for (int x = 0; x < state.CellCount; x++)
                {
                    m_legacyLabels[x] = Encoding.ASCII.GetString(buffer, index, 4);
                    // We don't need offsets, so we skip them...
                    index += 8;
                }
            }

            return(index - startIndex);
        }