Exemplo n.º 1
0
        /// <summary>
        /// Casts the parsed <see cref="IChannelFrame"/> to its specific implementation (i.e., <see cref="DataFrame"/>, <see cref="ConfigurationFrame"/> or <see cref="CommandFrame"/>).
        /// </summary>
        /// <param name="frame"><see cref="IChannelFrame"/> that was parsed by <see cref="FrameImageParserBase{TTypeIdentifier,TOutputType}"/> that implements protocol specific common frame header interface.</param>
        protected override void OnReceivedChannelFrame(IChannelFrame frame)
        {
            // Raise abstract channel frame events as a priority (i.e., IDataFrame, IConfigurationFrame, etc.)
            base.OnReceivedChannelFrame(frame);

            // Raise IEC 61850-90-5 specific channel frame events, if any have been subscribed
            if (frame != null && (ReceivedDataFrame != null || ReceivedConfigurationFrame != null || ReceivedCommandFrame != null))
            {
                DataFrame dataFrame = frame as DataFrame;

                if (dataFrame != null)
                {
                    if (ReceivedDataFrame != null)
                    {
                        ReceivedDataFrame(this, new EventArgs <DataFrame>(dataFrame));
                    }
                }
                else
                {
                    ConfigurationFrame configFrame = frame as ConfigurationFrame;

                    if (configFrame != null)
                    {
                        if (ReceivedConfigurationFrame != null)
                        {
                            ReceivedConfigurationFrame(this, new EventArgs <ConfigurationFrame>(configFrame));
                        }
                    }
                    else
                    {
                        CommandFrame commandFrame = frame as CommandFrame;

                        if (commandFrame != null)
                        {
                            if (ReceivedCommandFrame != null)
                            {
                                ReceivedCommandFrame(this, new EventArgs <CommandFrame>(commandFrame));
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Creates a new <see cref="CommonFrameHeader"/> from specified parameters.
        /// </summary>
        /// <param name="configurationFrame">IEC 61850-90-5 <see cref="ConfigurationFrame"/> if available.</param>
        /// <param name="typeID">The IEC 61850-90-5 specific frame type of this frame.</param>
        /// <param name="idCode">The ID code of this frame.</param>
        /// <param name="timestamp">The timestamp of this frame.</param>
        /// <param name="msvID">MSVID to use for this frame, if any.</param>
        /// <param name="asduCount">ASDU count.</param>
        /// <param name="configurationRevision">Configuration revision.</param>
        public CommonFrameHeader(ConfigurationFrame configurationFrame, FrameType typeID, ushort idCode, Ticks timestamp, string msvID = null, int asduCount = 1, uint configurationRevision = 1)
        {
            m_frameType             = typeID;
            m_idCode                = idCode;
            m_timestamp             = timestamp;
            m_version               = 1;
            m_timebase              = Common.Timebase;
            m_msvID                 = msvID;
            m_asduCount             = asduCount;
            m_configurationRevision = configurationRevision;

            m_securityAlgorithm  = SecurityAlgorithm.None;
            m_signatureAlgorithm = SignatureAlgorithm.None;

            if ((object)configurationFrame != null)
            {
                // Hang on to configured frame rate and ticks per frame
                m_framesPerSecond = configurationFrame.FrameRate;
                m_ticksPerFrame   = Ticks.PerSecond / (double)m_framesPerSecond;
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Creates a new <see cref="CommonFrameHeader"/> from given <paramref name="buffer"/>.
        /// </summary>
        /// <param name="configurationFrame">IEC 61850-90-5 <see cref="ConfigurationFrame"/> if already parsed.</param>
        /// <param name="useETRConfiguration">Determines if system should find associated ETR file using MSVID with same name for configuration.</param>
        /// <param name="guessConfiguration">Determines if system should try to guess at a possible configuration given payload size.</param>
        /// <param name="parseRedundantASDUs">Determines if system should expose redundantly parsed ASDUs.</param>
        /// <param name="ignoreSignatureValidationFailures">Determines if system should ignore checksum signature validation errors.</param>
        /// <param name="ignoreSampleSizeValidationFailures">Determines if system should ignore sample size validation errors.</param>
        /// <param name="angleFormat">Allows customization of the angle parsing format.</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 offset.</param>
        // ReSharper disable once UnusedParameter.Local
        public CommonFrameHeader(ConfigurationFrame configurationFrame, bool useETRConfiguration, bool guessConfiguration, bool parseRedundantASDUs, bool ignoreSignatureValidationFailures, bool ignoreSampleSizeValidationFailures, AngleFormat angleFormat, byte[] buffer, int startIndex, int length)
        {
            const byte VersionNumberMask = (byte)IEC61850_90_5.FrameType.VersionNumberMask;

            // Cache behavioral connection parameters
            m_useETRConfiguration = useETRConfiguration;
            m_guessConfiguration  = guessConfiguration;
            m_parseRedundantASDUs = parseRedundantASDUs;
            m_ignoreSignatureValidationFailures  = ignoreSignatureValidationFailures;
            m_ignoreSampleSizeValidationFailures = ignoreSampleSizeValidationFailures;
            m_angleFormat = angleFormat;

            // Ignore the time base from configuration frame if available.  The timebase is not adjustable for 61850.
            m_timebase = Common.Timebase;

            // See if frame is for a common IEEE C37.118 frame (e.g., for configuration or command)
            if (buffer[startIndex] == PhasorProtocols.Common.SyncByte)
            {
                // Strip out frame type and version information...
                m_frameType = (FrameType)(buffer[startIndex + 1] & ~VersionNumberMask);
                m_version   = (byte)(buffer[startIndex + 1] & VersionNumberMask);

                m_frameLength = BigEndian.ToUInt16(buffer, startIndex + 2);
                m_idCode      = BigEndian.ToUInt16(buffer, startIndex + 4);

                uint secondOfCentury  = BigEndian.ToUInt32(buffer, startIndex + 6);
                uint fractionOfSecond = BigEndian.ToUInt32(buffer, startIndex + 10);
                long ticksBeyondSecond;

                // Without timebase, the best timestamp you can get is down to the whole second
                m_timestamp = (new UnixTimeTag((double)secondOfCentury)).ToDateTime().Ticks;

                // "Actual fractional seconds" are obtained by taking fractionOfSecond and dividing by timebase.
                // Since we are converting to ticks, we need to multiply by Ticks.PerSecond.
                // We do the multiplication first so that the whole operation can be done using integer arithmetic.
                // m_timebase / 2L is added before dividing by timebase in order to round the result.
                ticksBeyondSecond = (fractionOfSecond & ~Common.TimeQualityFlagsMask) * Ticks.PerSecond;
                m_timestamp      += (ticksBeyondSecond + m_timebase / 2L) / m_timebase;

                if ((object)configurationFrame != null)
                {
                    // Hang on to configured frame rate and ticks per frame
                    m_framesPerSecond = configurationFrame.FrameRate;
                    m_ticksPerFrame   = Ticks.PerSecond / (double)m_framesPerSecond;
                }

                m_timeQualityFlags = fractionOfSecond & Common.TimeQualityFlagsMask;
            }
            else if (buffer[startIndex + 1] == Common.CltpTag)
            {
                // Make sure there is enough data to parse session header from frame
                if (length > Common.SessionHeaderSize)
                {
                    // Manually assign frame type - this is an IEC 61850-90-5 data frame
                    m_frameType = IEC61850_90_5.FrameType.DataFrame;

                    // Calculate CLTP tag length
                    int cltpTagLength = buffer[startIndex] + 1;

                    // Initialize buffer parsing index starting past connectionless transport protocol header
                    int index = startIndex + cltpTagLength;

                    // Start calculating total frame length
                    int frameLength = cltpTagLength;

                    // Get session type (Goose, sampled values, etc.)
                    SessionType sessionType = (SessionType)buffer[index++];

                    // Make sure session type is sampled values
                    if (sessionType == SessionType.SampledValues)
                    {
                        byte headerSize = buffer[index];

                        // Make sure header size is standard
                        if (headerSize == Common.SessionHeaderSize)
                        {
                            // Skip common header tag
                            index += 3;

                            // Get SPDU length
                            m_spduLength = BigEndian.ToUInt32(buffer, index);
                            index       += 4;

                            // Add SPDU length to total frame length (updated as of 10/3/2012 to accommodate extra 6 bytes)
                            frameLength += (int)m_spduLength + 8;

                            // Make sure full frame of data is available - cannot calculate full frame length needed for check sum
                            // without the entire frame since signature algorithm calculation length varies by type and size
                            if (length > m_spduLength + 13)
                            {
                                // Get SPDU packet number
                                m_packetNumber = BigEndian.ToUInt32(buffer, index);

                                // Get security algorithm type
                                m_securityAlgorithm = (SecurityAlgorithm)buffer[index + 12];

                                // Get signature algorithm type
                                m_signatureAlgorithm = (SignatureAlgorithm)buffer[index + 13];

                                // Get current key ID
                                m_keyID = BigEndian.ToUInt32(buffer, index + 14);

                                // Add signature calculation result length to total frame length
                                switch (m_signatureAlgorithm)
                                {
                                case SignatureAlgorithm.None:
                                    break;

                                case SignatureAlgorithm.Sha80:
                                    frameLength += 11;
                                    break;

                                case SignatureAlgorithm.Sha128:
                                case SignatureAlgorithm.Aes128:
                                    frameLength += 17;
                                    break;

                                case SignatureAlgorithm.Sha256:
                                    frameLength += 33;
                                    break;

                                case SignatureAlgorithm.Aes64:
                                    frameLength += 9;
                                    break;

                                default:
                                    throw new InvalidOperationException("Invalid IEC 61850-90-5 signature algorithm detected: 0x" + buffer[index].ToString("X").PadLeft(2, '0'));
                                }

                                // Check signature algorithm packet checksum here, this step is skipped in data frame parsing due to non-standard location...
                                if (m_signatureAlgorithm != SignatureAlgorithm.None)
                                {
                                    int packetIndex = startIndex + cltpTagLength;
                                    int hmacIndex   = (int)(packetIndex + m_spduLength + 2);

                                    // Check for signature tag
                                    if (buffer[hmacIndex++] == 0x85)
                                    {
                                        // KeyID is technically a lookup into derived rotating keys, but all these are using dummy key for now
                                        HMAC hmac   = m_signatureAlgorithm <= SignatureAlgorithm.Sha256 ? (HMAC)(new ShaHmac(Common.DummyKey)) : (HMAC)(new AesHmac(Common.DummyKey));
                                        int  result = 0;

                                        switch (m_signatureAlgorithm)
                                        {
                                        case SignatureAlgorithm.None:
                                            break;

                                        case SignatureAlgorithm.Aes64:
                                            m_sourceHash     = buffer.BlockCopy(hmacIndex, 8);
                                            m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 8);
                                            result           = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 8);
                                            break;

                                        case SignatureAlgorithm.Sha80:
                                            m_sourceHash     = buffer.BlockCopy(hmacIndex, 10);
                                            m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 10);
                                            result           = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 10);
                                            break;

                                        case SignatureAlgorithm.Sha128:
                                        case SignatureAlgorithm.Aes128:
                                            m_sourceHash     = buffer.BlockCopy(hmacIndex, 16);
                                            m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 16);
                                            result           = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 16);
                                            break;

                                        case SignatureAlgorithm.Sha256:
                                            m_sourceHash     = buffer.BlockCopy(hmacIndex, 32);
                                            m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 32);
                                            result           = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 32);
                                            break;

                                        default:
                                            throw new NotSupportedException(string.Format("IEC 61850-90-5 signature algorithm \"{0}\" is not currently supported: ", m_signatureAlgorithm));
                                        }

                                        if (result != 0 && !m_ignoreSignatureValidationFailures)
                                        {
                                            throw new CrcException("Invalid binary image detected - IEC 61850-90-5 check sum does not match.");
                                        }
                                    }
                                    else
                                    {
                                        throw new CrcException("Invalid binary image detected - expected IEC 61850-90-5 check sum does not exist.");
                                    }
                                }

                                // Get payload length
                                index       += 18;
                                m_dataLength = (ushort)BigEndian.ToUInt32(buffer, index);
                                index       += 4;

                                // Confirm payload type tag is sampled values
                                if (buffer[index] != 0x82)
                                {
                                    throw new InvalidOperationException("Encountered a payload that is not tagged 0x82 for sampled values: 0x" + buffer[index].ToString("X").PadLeft(2, '0'));
                                }

                                index++;

                                // Get simulated bit value
                                m_simulatedData = buffer[index++] != 0;

                                // Get application ID
                                m_applicationID = BigEndian.ToUInt16(buffer, index);
                                index          += 2;

                                // Get ASDU payload size
                                m_payloadSize = BigEndian.ToUInt16(buffer, index);
                                index        += 2;

                                // Validate sampled value PDU tag exists and skip past it
                                buffer.ValidateTag(SampledValueTag.SvPdu, ref index);

                                // Parse number of ASDUs tag
                                m_asduCount = buffer.ParseByteTag(SampledValueTag.AsduCount, ref index);

                                if (m_asduCount == 0)
                                {
                                    throw new InvalidOperationException("Total number of ADSUs must be greater than zero.");
                                }

                                // Validate sequence of ASDU tag exists and skip past it
                                buffer.ValidateTag(SampledValueTag.SequenceOfAsdu, ref index);

                                // Set header length
                                m_headerLength = (ushort)(index - startIndex);

                                // Set calculated frame length
                                m_frameLength = (ushort)frameLength;
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException("Bad data stream, encountered an invalid session header size: " + headerSize);
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(string.Format("This library can only parse IEC 61850-90-5 sampled value sessions, type \"{0}\" is not supported.", sessionType));
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Bad data stream, expected sync byte 0xAA or 0x01 as first byte in IEC 61850-90-5 frame, got 0x" + buffer[startIndex].ToString("X").PadLeft(2, '0'));
            }
        }
Exemplo n.º 4
0
        // Static Methods

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

            if (derivedFrame == null)
            {
                // Create a new IEC 61850-90-5 configuration frame converted from equivalent configuration information
                ConfigurationCell    derivedCell;
                IFrequencyDefinition sourceFrequency;

                // Assuming timebase = 16777216
                derivedFrame = new ConfigurationFrame(Common.Timebase, sourceFrame.IDCode, sourceFrame.Timestamp, sourceFrame.FrameRate);

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

                    string stationName = sourceCell.StationName;
                    string idLabel     = sourceCell.IDLabel;

                    if (!string.IsNullOrWhiteSpace(stationName))
                    {
                        derivedCell.StationName = stationName.TruncateLeft(derivedCell.MaximumStationNameLength);
                    }

                    if (!string.IsNullOrWhiteSpace(idLabel))
                    {
                        derivedCell.IDLabel = idLabel.TruncateLeft(derivedCell.IDLabelLength);
                    }

                    // 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, 0, 0));
                    }

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

            return(derivedFrame);
        }
Exemplo n.º 5
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)
     : this(parent)
 {
     IDCode           = idCode;
     NominalFrequency = nominalFrequency;
 }