/// <summary> /// Creates a new <see cref="DataFrame"/> from serialization parameters. /// </summary> /// <param name="info">The <see cref="SerializationInfo"/> with populated with data.</param> /// <param name="context">The source <see cref="StreamingContext"/> for this deserialization.</param> protected DataFrame(SerializationInfo info, StreamingContext context) : base(info, context) { // Deserialize data frame m_frameHeader = (CommonFrameHeader)info.GetValue("frameHeader", typeof(CommonFrameHeader)); m_sampleNumber = info.GetUInt16("sampleNumber"); }
/// <summary> /// Parses a common header instance that implements <see cref="ICommonHeader{TTypeIdentifier}"/> for the output type represented /// in the binary image. /// </summary> /// <param name="buffer">Buffer containing data to parse.</param> /// <param name="offset">Offset index into buffer that represents where to start parsing.</param> /// <param name="length">Maximum length of valid data from offset.</param> /// <returns>The <see cref="ICommonHeader{TTypeIdentifier}"/> which includes a type ID for the <see cref="Type"/> to be parsed.</returns> /// <remarks> /// <para> /// Derived classes need to provide a common header instance (i.e., class that implements <see cref="ICommonHeader{TTypeIdentifier}"/>) /// for the output types; this will primarily include an ID of the <see cref="Type"/> that the data image represents. This parsing is /// only for common header information, actual parsing will be handled by output type via its <see cref="ISupportBinaryImage.ParseBinaryImage"/> /// method. This header image should also be used to add needed complex state information about the output type being parsed if needed. /// </para> /// <para> /// If there is not enough buffer available to parse common header (as determined by <paramref name="length"/>), return null. Also, if /// the protocol allows frame length to be determined at the time common header is being parsed and there is not enough buffer to parse /// the entire frame, it will be optimal to prevent further parsing by returning null. /// </para> /// </remarks> protected override ICommonHeader <FrameType> ParseCommonHeader(byte[] buffer, int offset, int length) { // See if there is enough data in the buffer to parse the common frame header. if (length >= CommonFrameHeader.FixedLength) { // Parse common frame header CommonFrameHeader parsedFrameHeader = new CommonFrameHeader(m_parseWordCountFromByte, m_usePhasorDataFileFormat, m_configurationFrame, buffer, offset, length); // As an optimization, we also make sure entire frame buffer image is available to be parsed - by doing this // we eliminate the need to validate length on all subsequent data elements that comprise the frame if (length >= parsedFrameHeader.FrameLength) { // Expose the frame buffer image in case client needs this data for any reason OnReceivedFrameBufferImage(parsedFrameHeader.FrameType, buffer, offset, parsedFrameHeader.FrameLength); // Handle special parsing states switch (parsedFrameHeader.TypeID) { case FrameType.DataFrame: // Assign data frame parsing state parsedFrameHeader.State = new DataFrameParsingState(parsedFrameHeader.FrameLength, m_configurationFrame, DataCell.CreateNewCell, TrustHeaderLength, ValidateDataFrameCheckSum); break; case FrameType.ConfigurationFrame: // Assign configuration frame parsing state parsedFrameHeader.State = new ConfigurationFrameParsingState(parsedFrameHeader.FrameLength, m_configurationFileName, ConfigurationCell.CreateNewCell, TrustHeaderLength, ValidateConfigurationFrameCheckSum); break; } return(parsedFrameHeader); } } return(null); }
/// <summary> /// Creates a new <see cref="ConfigurationFrame"/> from serialization parameters. /// </summary> /// <param name="info">The <see cref="SerializationInfo"/> with populated with data.</param> /// <param name="context">The source <see cref="StreamingContext"/> for this deserialization.</param> protected ConfigurationFrame(SerializationInfo info, StreamingContext context) : base(info, context) { // Deserialize configuration frame m_frameHeader = (CommonFrameHeader)info.GetValue("frameHeader", typeof(CommonFrameHeader)); m_packetsPerSample = info.GetUInt16("packetsPerSample"); m_streamType = (StreamType)info.GetValue("streamType", typeof(StreamType)); m_revisionNumber = (RevisionNumber)info.GetValue("revisionNumber", typeof(RevisionNumber)); m_iniFile = new IniFile(info.GetString("configurationFileName")); // The usePhasorDataFileFormat flag and other new elements did not exist in prior versions so we protect against possible deserialization failures try { UsePhasorDataFileFormat = info.GetBoolean("usePhasorDataFileFormat"); m_rowLength = info.GetUInt32("rowLength"); } catch (SerializationException) { UsePhasorDataFileFormat = false; m_rowLength = 0; } Refresh(false); }
/// <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; } } }