/// <summary> /// Creates a new <see cref="DataCell"/> 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 DataCell(SerializationInfo info, StreamingContext context) : base(info, context) { // Deserialize data cell m_channelFlags = (ChannelFlags)info.GetValue("channelFlags", typeof(ChannelFlags)); m_reservedFlags = (ReservedFlags)info.GetValue("reservedFlags", typeof(ReservedFlags)); m_sampleNumber = info.GetUInt16("sampleNumber"); }
/// <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) { DataFrame parentFrame = Parent; DataFrameParsingState frameState = parentFrame.State; IDataCellParsingState state = State; RevisionNumber revision = parentFrame.ConfigurationFrame.RevisionNumber; int x, index = startIndex; byte analogs, digitals, phasors; // Read data buffer if using phasor data file format if (UsePhasorDataFileFormat && frameState.RemainingPdcBlockPmus == 0) { m_dataBuffer = BigEndian.ToUInt32(buffer, index); index += 4; } // Get data cell flags m_channelFlags = (ChannelFlags)buffer[index]; analogs = buffer[index + 1]; index += 2; // Parse PDCstream specific header image if (revision >= RevisionNumber.Revision2 && frameState.RemainingPdcBlockPmus == 0) { // Strip off reserved flags m_reservedFlags = (ReservedFlags)analogs & ~ReservedFlags.AnalogWordsMask; // Leave analog word count analogs &= (byte)ReservedFlags.AnalogWordsMask; } else { // Older revisions didn't allow analogs m_dataRate = analogs; analogs = 0; } if (frameState.RemainingPdcBlockPmus > 0) { // PDC Block PMU's contain exactly 2 phasors, 0 analogs and 1 digital phasors = 2; analogs = 0; digitals = 1; UsingPdcExchangeFormat = true; // Decrement remaining PDC block PMU's frameState.RemainingPdcBlockPmus--; } else { // Parse number of digitals and phasors for normal PMU cells digitals = buffer[index]; phasors = buffer[index + 1]; index += 2; if (revision >= RevisionNumber.Revision2) { // Strip off IEEE flags FormatFlags = (FormatFlags)digitals & ~FormatFlags.DigitalWordsMask; // Leave digital word count digitals &= (byte)FormatFlags.DigitalWordsMask; } // Check for PDC exchange format if (UsingPdcExchangeFormat) { // In cases where we are using PDC exchange the phasor count is the number of PMU's in the PDC block int pdcBlockPmus = phasors - 1; // <-- Current PMU counts as one frameState.RemainingPdcBlockPmus = pdcBlockPmus; frameState.CellCount += pdcBlockPmus; // PDC Block PMU's contain exactly 2 phasors, 0 analogs and 1 digital phasors = 2; analogs = 0; digitals = 1; // Get data cell flags for PDC block PMU m_channelFlags = (ChannelFlags)buffer[index]; UsingPdcExchangeFormat = true; index += 2; } else { // Parse PMU's sample number m_sampleNumber = BigEndian.ToUInt16(buffer, index); index += 2; } } // Algorithm Case: Determine best course of action when stream counts don't match counts defined in the // external INI based configuration file. Think about what *will* happen when new data appears in the // stream that's not in the config file - you could raise an event notifying consumer about the mismatch // instead of raising an exception - could even make a boolean property that would allow either case. // The important thing to consider is that to parse the cell images you have to have a defined // definition (see base class method "DataCellBase.ParseBodyImage"). If you have more items defined // in the stream than you do in the config file then you won't get the new value, too few items and you // don't have enough definitions to correctly interpret the data (that would be bad) - either way the // definitions won't line up with the appropriate data value and you won't know which one is missing or // added. I can't change the protocol so this is enough argument to just raise an error for config // file/stream mismatch. So for now we'll just throw an exception and deal with consequences :) // Note that this only applies to BPA PDCstream protocol because of external configuration. // Addendum: After running this with several protocol implementations I noticed that if a device wasn't // reporting, the phasor count dropped to zero even if there were phasors defined in the configuration // file, so the only time an exception is thrown is if there are more phasors defined in the the stream // than there are defined in the INI file... // At least this number of phasors should be already defined in BPA PDCstream configuration file if (phasors > ConfigurationCell.PhasorDefinitions.Count) { throw new InvalidOperationException( "Stream/Config File Mismatch: Phasor value count in stream (" + phasors + ") does not match defined count in configuration file (" + ConfigurationCell.PhasorDefinitions.Count + ") for " + ConfigurationCell.IDLabel); } // If analog values get a clear definition in INI file at some point, we can validate the number in the // stream to the number in the config file, in the mean time we dyanmically add analog definitions to // configuration cell as needed (they are only defined in data frame of BPA PDCstream) if (analogs > ConfigurationCell.AnalogDefinitions.Count) { for (x = ConfigurationCell.AnalogDefinitions.Count; x < analogs; x++) { ConfigurationCell.AnalogDefinitions.Add(new AnalogDefinition(ConfigurationCell, "Analog " + (x + 1), 1, 0.0D, AnalogType.SinglePointOnWave)); } } // If digital values get a clear definition in INI file at some point, we can validate the number in the // stream to the number in the config file, in the mean time we dyanmically add digital definitions to // configuration cell as needed (they are only defined in data frame of BPA PDCstream) if (digitals > ConfigurationCell.DigitalDefinitions.Count) { for (x = ConfigurationCell.DigitalDefinitions.Count; x < digitals; x++) { ConfigurationCell.DigitalDefinitions.Add(new DigitalDefinition(ConfigurationCell, "Digital Word " + (x + 1))); } } // Unlike most all other protocols the counts defined for phasors, analogs and digitals in the data frame // may not exactly match what's defined in the configuration frame as these values are defined in an external // INI file for BPA PDCstream. As a result, we manually assign the counts to the parsing state so that these // will be the counts used to parse values from data frame in the base class ParseBodyImage method state.PhasorCount = phasors; state.AnalogCount = analogs; state.DigitalCount = digitals; // Status flags and remaining data elements will parsed by base class in the ParseBodyImage method return(index - startIndex); }
/// <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) { DataFrame parentFrame = Parent; DataFrameParsingState frameState = parentFrame.State; IDataCellParsingState state = State; RevisionNumber revision = parentFrame.ConfigurationFrame.RevisionNumber; int x, index = startIndex; byte analogs, digitals, phasors; // Read data buffer if using phasor data file format if (UsePhasorDataFileFormat && frameState.RemainingPdcBlockPmus == 0) { m_dataBuffer = BigEndian.ToUInt32(buffer, index); index += 4; } // Get data cell flags m_channelFlags = (ChannelFlags)buffer[index]; analogs = buffer[index + 1]; index += 2; // Parse PDCstream specific header image if (revision >= RevisionNumber.Revision2 && frameState.RemainingPdcBlockPmus == 0) { // Strip off reserved flags m_reservedFlags = (ReservedFlags)analogs & ~ReservedFlags.AnalogWordsMask; // Leave analog word count analogs &= (byte)ReservedFlags.AnalogWordsMask; } else { // Older revisions didn't allow analogs m_dataRate = analogs; analogs = 0; } if (frameState.RemainingPdcBlockPmus > 0) { // PDC Block PMU's contain exactly 2 phasors, 0 analogs and 1 digital phasors = 2; analogs = 0; digitals = 1; UsingPdcExchangeFormat = true; // Decrement remaining PDC block PMU's frameState.RemainingPdcBlockPmus--; } else { // Parse number of digitals and phasors for normal PMU cells digitals = buffer[index]; phasors = buffer[index + 1]; index += 2; if (revision >= RevisionNumber.Revision2) { // Strip off IEEE flags FormatFlags = (FormatFlags)digitals & ~FormatFlags.DigitalWordsMask; // Leave digital word count digitals &= (byte)FormatFlags.DigitalWordsMask; } // Check for PDC exchange format if (UsingPdcExchangeFormat) { // In cases where we are using PDC exchange the phasor count is the number of PMU's in the PDC block int pdcBlockPmus = phasors - 1; // <-- Current PMU counts as one frameState.RemainingPdcBlockPmus = pdcBlockPmus; frameState.CellCount += pdcBlockPmus; // PDC Block PMU's contain exactly 2 phasors, 0 analogs and 1 digital phasors = 2; analogs = 0; digitals = 1; // Get data cell flags for PDC block PMU m_channelFlags = (ChannelFlags)buffer[index]; UsingPdcExchangeFormat = true; index += 2; } else { // Parse PMU's sample number m_sampleNumber = BigEndian.ToUInt16(buffer, index); index += 2; } } // Algorithm Case: Determine best course of action when stream counts don't match counts defined in the // external INI based configuration file. Think about what *will* happen when new data appears in the // stream that's not in the config file - you could raise an event notifying consumer about the mismatch // instead of raising an exception - could even make a boolean property that would allow either case. // The important thing to consider is that to parse the cell images you have to have a defined // definition (see base class method "DataCellBase.ParseBodyImage"). If you have more items defined // in the stream than you do in the config file then you won't get the new value, too few items and you // don't have enough definitions to correctly interpret the data (that would be bad) - either way the // definitions won't line up with the appropriate data value and you won't know which one is missing or // added. I can't change the protocol so this is enough argument to just raise an error for config // file/stream mismatch. So for now we'll just throw an exception and deal with consequences :) // Note that this only applies to BPA PDCstream protocol because of external configuration. // Addendum: After running this with several protocol implementations I noticed that if a device wasn't // reporting, the phasor count dropped to zero even if there were phasors defined in the configuration // file, so the only time an exception is thrown is if there are more phasors defined in the the stream // than there are defined in the INI file... // At least this number of phasors should be already defined in BPA PDCstream configuration file if (phasors > ConfigurationCell.PhasorDefinitions.Count) throw new InvalidOperationException( "Stream/Config File Mismatch: Phasor value count in stream (" + phasors + ") does not match defined count in configuration file (" + ConfigurationCell.PhasorDefinitions.Count + ") for " + ConfigurationCell.IDLabel); // If analog values get a clear definition in INI file at some point, we can validate the number in the // stream to the number in the config file, in the mean time we dyanmically add analog definitions to // configuration cell as needed (they are only defined in data frame of BPA PDCstream) if (analogs > ConfigurationCell.AnalogDefinitions.Count) { for (x = ConfigurationCell.AnalogDefinitions.Count; x < analogs; x++) { ConfigurationCell.AnalogDefinitions.Add(new AnalogDefinition(ConfigurationCell, "Analog " + (x + 1), 1, 0.0D, AnalogType.SinglePointOnWave)); } } // If digital values get a clear definition in INI file at some point, we can validate the number in the // stream to the number in the config file, in the mean time we dyanmically add digital definitions to // configuration cell as needed (they are only defined in data frame of BPA PDCstream) if (digitals > ConfigurationCell.DigitalDefinitions.Count) { for (x = ConfigurationCell.DigitalDefinitions.Count; x < digitals; x++) { ConfigurationCell.DigitalDefinitions.Add(new DigitalDefinition(ConfigurationCell, "Digital Word " + (x + 1))); } } // Unlike most all other protocols the counts defined for phasors, analogs and digitals in the data frame // may not exactly match what's defined in the configuration frame as these values are defined in an external // INI file for BPA PDCstream. As a result, we manually assign the counts to the parsing state so that these // will be the counts used to parse values from data frame in the base class ParseBodyImage method state.PhasorCount = phasors; state.AnalogCount = analogs; state.DigitalCount = digitals; // Status flags and remaining data elements will parsed by base class in the ParseBodyImage method return (index - startIndex); }