3731 lines · 168.0 KB · C#
RawDownload
//******************************************************************************************************//  MultiProtocolFrameParser.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  03/16/2006 - J. Ritchie Carroll//       Initial version of source generated.//  06/26/2006 - Pinal C. Patel//       Changed out the socket code with TcpClient and UdpClient components from GSF.Communication.//  01/31/2007 - J. Ritchie Carroll//       Added TCP "server" support to allow listening connections from devices that act as data//       clients, e.g., F-NET devices.//  05/23/2007 - Pinal C. Patel//       Added member variable 'm_clientConnectionAttempts' to track the number of attempts made for//       connecting to the server since this information is no longer provided by the event raised by//       any of the Communication Client components.//  07/05/2007 - J. Ritchie Carroll//       Wrapped all event raising for frame parsing in Try/Catch so that any exceptions thrown in//       consumer event handlers won't have a negative effect on continuous data parsing - exceptions//       in consumer event handlers are duly noted and raised through the ParsingException event.//  09/28/2007 - J. Ritchie Carroll//       Implemented new disconnect overload on communications client that allows timeout on socket//       close to fix an issue related non-responsive threads that "lock-up" after sending connection//       commands that attempt to close the socket for remotely connected devices.//  12/14/2007 - J. Ritchie Carroll//       Implemented simulated timestamp injection for published frames to allow for real-time//       data simulations from archived sample data.//  10/28/2008 - J. Ritchie Carroll//       Added support for SEL's UDP_T and UDP_U protocol implementations (UDP_S was already supported),//       implementation handled by allowing definition of a "CommandChannel" in the connection string.//  04/27/2009 - J. Ritchie Carroll//       Added support for SEL Fast Message protocol.//  02/12/2010 - Pinal C. Patel//       Modified to start the IFrameParser object in InitializeFrameParser() instead of Start().//  03/20/2010 - J. Ritchie Carroll//       Added property "SkipDisableRealTimeData" to allow consumer to bypass sending the command to//       turn off the real-time data stream when automatically starting the data parsing sequence. This//       is useful when using UDP multicast that may have many listeners, in these cases you don't want//       to disable the stream on startup or shutdown since other applications may be subscribed to the//       real-time stream.//  03/21/2010 - J. Ritchie Carroll//       Added parsing exception threshold settings and consumer event to handle situation.//  06/13/2010 - J. Ritchie Carroll//       Added several more run-time statistics to the frame parser (e.g., missing frames, CRC errors).//  08/10/2010 - J. Ritchie Carroll//       Added code to handle high-resolution input timing to support accurate input simulations.//  05/06/2010- Jian (Ryan) Zuo//       Updated to exclude non-data frames from frame counts and injected waiting periods.//  05/19/2011 - Ritchie//       Added DST file support.//  12/17/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.IO.Ports;using System.Net;using System.Net.Sockets;using System.Text;using System.Text.RegularExpressions;using System.Threading;using GSF.Communication;using GSF.Diagnostics;using GSF.IO;using GSF.Parsing;using GSF.PhasorProtocols.IEEEC37_118;using GSF.PhasorProtocols.Macrodyne;using GSF.PhasorProtocols.SelFastMessage;using GSF.Threading;using GSF.TimeSeries;using GSF.Units;using GSF.Units.EE;using TcpClient = GSF.Communication.TcpClient;using UdpClient = GSF.Communication.UdpClient; // Ignore warnings about unused events that are required by IClient#pragma warning disable 67 namespace GSF.PhasorProtocols{    #region [ Enumerations ]     /// <summary>    /// Phasor data protocols enumeration.    /// </summary>    [Serializable]    public enum PhasorProtocol    {        /// <summary>        /// IEEE C37.118.2-2011 protocol.        /// </summary>        IEEEC37_118V2,        /// <summary>        /// IEEE C37.118-2005 protocol.        /// </summary>        IEEEC37_118V1,        /// <summary>        /// IEEE C37.118, draft 6 protocol.        /// </summary>        IEEEC37_118D6,        /// <summary>        /// IEEE 1344-1995 protocol.        /// </summary>        IEEE1344,        /// <summary>        /// BPA PDCstream protocol.        /// </summary>        BPAPDCstream,        /// <summary>        /// UTK F-NET protocol.        /// </summary>        FNET,        /// <summary>        /// SEL Fast Message protocol.        /// </summary>        SelFastMessage,        /// <summary>        /// Macrodyne protocol.        /// </summary>        Macrodyne,        /// <summary>        /// IEC 61850-90-5 protocol.        /// </summary>        IEC61850_90_5    }     #endregion     /// <summary>    /// Protocol independent frame parser.    /// </summary>    /// <remarks>    /// This class takes all protocol frame parsing implementations and reduces them to a single simple-to-use class exposing all    /// data through abstract interfaces (e.g., IConfigurationFrame, IDataFrame, etc.) - this way new protocol implementations can    /// be added without adversely affecting consuming code. Additionally, this class implements a variety of transport options    /// (e.g., TCP, UDP, Serial, etc.) and hides the complexities of this connectivity and internally pushes all data received from    /// the selected transport protocol to the selected phasor parsing protocol.    /// </remarks>    public sealed class MultiProtocolFrameParser : IFrameParser    {        #region [ Members ]         // Nested Types         /// <summary>        /// Creates an instance of a TCP server that can be shared for multiple devices (e.g., an F-NET server)        /// </summary>        private sealed class SharedTcpServerReference : IServer        {            #region [ Members ]             // Events             /// <summary>            /// Occurs when the server is started.            /// </summary>            public event EventHandler ServerStarted;             /// <summary>            /// Occurs when the server is stopped.            /// </summary>            public event EventHandler ServerStopped;             /// <summary>            /// Occurs when a client connects to the server.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T}.Argument"/> is the ID of the client that connected to the server.            /// </remarks>            public event EventHandler<EventArgs<Guid>> ClientConnected;             /// <summary>            /// Occurs when a client disconnects from the server.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T}.Argument"/> is the ID of the client that disconnected from the server.            /// </remarks>            public event EventHandler<EventArgs<Guid>> ClientDisconnected;             /// <summary>            /// Occurs when an exception is encountered while a client is connecting.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T}.Argument"/> is the <see cref="Exception"/> encountered when connecting to the client.            /// </remarks>            public event EventHandler<EventArgs<Exception>> ClientConnectingException;             /// <summary>            /// Occurs when data is being sent to a client.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T}.Argument"/> is the ID of the client to which the data is being sent.            /// </remarks>            public event EventHandler<EventArgs<Guid>> SendClientDataStart;             /// <summary>            /// Occurs when data has been sent to a client.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T}.Argument"/> is the ID of the client to which the data has been sent.            /// </remarks>            public event EventHandler<EventArgs<Guid>> SendClientDataComplete;             /// <summary>            /// Occurs when an <see cref="Exception"/> is encountered when sending data to a client.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T1,T2}.Argument1"/> is the ID of the client to which the data was being sent.<br/>            /// <see cref="EventArgs{T1,T2}.Argument2"/> is the <see cref="Exception"/> encountered when sending data to a client.            /// </remarks>            public event EventHandler<EventArgs<Guid, Exception>> SendClientDataException;             /// <summary>            /// Occurs when unprocessed data has been received from a client.            /// </summary>            /// <remarks>            /// <para>            /// This event can be used to receive a notification that client data has arrived. The <see cref="Read"/> method can then be used            /// to copy data to an existing buffer. In many cases it will be optimal to use an existing buffer instead of subscribing to the            /// <see cref="ReceiveClientDataComplete"/> event, however, the data that is available after calling the <see cref="Read"/> method            /// will be the original unprocessed data received by the client, i.e., not optionally decrypted or decompressed data.            /// </para>            /// <para>            /// <see cref="EventArgs{T1,T2}.Argument1"/> is the ID of the client from which data is received.<br/>            /// <see cref="EventArgs{T1,T2}.Argument2"/> is the number of bytes received in the buffer from the client.            /// </para>            /// </remarks>            public event EventHandler<EventArgs<Guid, int>> ReceiveClientData;             /// <summary>            /// Occurs when data received from a client has been processed and is ready for consumption.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T1,T2,T3}.Argument1"/> is the ID of the client from which data is received.<br/>            /// <see cref="EventArgs{T1,T2,T3}.Argument2"/> is a new buffer containing post-processed data received from the client starting at index zero.<br/>            /// <see cref="EventArgs{T1,T2,T3}.Argument3"/> is the number of post-processed bytes received in the buffer from the client.            /// </remarks>            public event EventHandler<EventArgs<Guid, byte[], int>> ReceiveClientDataComplete;             /// <summary>            /// Occurs when an <see cref="Exception"/> is encountered when receiving data from a client.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T1,T2}.Argument1"/> is the ID of the client from which the data was being received.<br/>            /// <see cref="EventArgs{T1,T2}.Argument2"/> is the <see cref="Exception"/> encountered when receiving data from a client.            /// </remarks>            public event EventHandler<EventArgs<Guid, Exception>> ReceiveClientDataException;             /// <summary>            /// Occurs when an <see cref="Exception"/> is encountered in a user-defined function via an event dispatch.            /// </summary>            /// <remarks>            /// <see cref="EventArgs{T}.Argument"/> is the <see cref="Exception"/> thrown by the user-defined function.            /// </remarks>            public event EventHandler<EventArgs<Exception>> UnhandledUserException;             /// <summary>            /// Raised after the source object has been properly disposed.            /// </summary>            public event EventHandler Disposed;             // Fields            private TcpServer m_tcpServer;            private IPAddress m_remoteAddress;            private Guid m_clientID;             #endregion             #region [ Properties ]             /// <summary>            /// Gets the name of the object providing status information.            /// </summary>            public string Name => m_tcpServer?.Name;             /// <summary>            /// Gets the current status details about object providing status information.            /// </summary>            public string Status => m_tcpServer?.Status ?? string.Empty;             /// <summary>            /// Gets or sets the data required by the server to initialize.            /// </summary>            public string ConfigurationString { get; set; }             /// <summary>            /// Gets or sets the maximum number of clients that can connect to the server.            /// </summary>            public int MaxClientConnections            {                get => m_tcpServer?.MaxClientConnections ?? ServerBase.DefaultMaxClientConnections;                set { } // Ignore this setting since we need to share            }             /// <summary>            /// Gets or sets the size of the buffer used by the client for receiving data from the server.            /// </summary>            public int SendBufferSize            {                get => m_tcpServer?.SendBufferSize ?? ServerBase.DefaultSendBufferSize;                set { } // Ignore this setting since we need to share            }             /// <summary>            /// Gets or sets the size of the buffer used by the server for receiving data from the clients.            /// </summary>            public int ReceiveBufferSize { get; set; }             /// <summary>            /// Gets or sets the <see cref="Encoding"/> to be used for the text sent to the connected clients.            /// </summary>            public Encoding TextEncoding            {                get => m_tcpServer?.TextEncoding;                set { } // Ignore this setting since we need to share            }             /// <summary>            /// Gets the current <see cref="ServerState"/>.            /// </summary>            public ServerState CurrentState => m_tcpServer?.CurrentState ?? ServerState.NotRunning;             /// <summary>            /// Gets the <see cref="TransportProtocol"/> used by the server for the transportation of data with the clients.            /// </summary>            public TransportProtocol TransportProtocol => TransportProtocol.Tcp;             /// <summary>            /// Gets the server's ID.            /// </summary>            public Guid ServerID => m_tcpServer?.ServerID ?? Guid.Empty;             /// <summary>            /// Gets the IDs of clients connected to the server.            /// </summary>            public Guid[] ClientIDs => m_tcpServer?.ClientIDs ?? Array.Empty<Guid>();             /// <summary>            /// Gets the <see cref="Time"/> for which the server has been running.            /// </summary>            public Time RunTime => m_tcpServer?.RunTime ?? 0.0D;             /// <summary>            /// Gets or sets a boolean value that indicates whether the object is enabled.            /// </summary>            public bool Enabled            {                get => m_tcpServer?.Enabled ?? false;                // ReSharper disable once ValueParameterNotUsed                set                {                    // value parameter not used here intentionally                    if (m_tcpServer is not null)                        m_tcpServer.Enabled = true;                }            }             /// <summary>            /// Gets a flag that indicates whether the object has been disposed.            /// </summary>            public bool IsDisposed { get; private set; }             #endregion             #region [ Methods ]             /// <summary>            /// Initializes the state of the object.            /// </summary>            public void Initialize()            {                Dictionary<string, string> settings = ConfigurationString.ParseKeyValuePairs();                 if (!settings.TryGetValue("receiveFrom", out string receiveFromSetting))                    return;                 IPStack ipStack = Transport.GetInterfaceIPStack(settings);                m_remoteAddress = Transport.CreateEndPoint(receiveFromSetting, 0, ipStack).Address;            }             /// <summary>            /// Starts the server.            /// </summary>            public void Start() => GetSharedServer();             /// <summary>            /// Stops the server.            /// </summary>            public void Stop() => ReturnSharedServer();             /// <summary>            /// Disconnects a connected client.            /// </summary>            /// <param name="clientID">ID of the client to be disconnected.</param>            public void DisconnectOne(Guid clientID)            {                if (clientID == m_clientID)                    m_tcpServer?.DisconnectOne(clientID);            }             /// <summary>            /// Disconnects all of the connected clients.            /// </summary>            public void DisconnectAll() => m_tcpServer?.DisconnectOne(m_clientID);             /// <summary>            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.            /// </summary>            public void Dispose()            {                if (IsDisposed)                    return;                 try                {                    ReturnSharedServer();                }                finally                {                    IsDisposed = true;                }            }             /// <summary>            /// Sends data to the specified client synchronously.            /// </summary>            /// <param name="clientID">ID of the client to which the data is to be sent.</param>            /// <param name="data">The buffer that contains the binary data to be sent.</param>            /// <param name="offset">The zero-based position in the <paramref name="data"/> at which to begin sending data.</param>            /// <param name="length">The number of bytes to be sent from <paramref name="data"/> starting at the <paramref name="offset"/>.</param>            public void SendTo(Guid clientID, byte[] data, int offset, int length) => SendToAsync(m_clientID, data, offset, length).WaitOne();             /// <summary>            /// Sends data to all of the connected clients synchronously.            /// </summary>            /// <param name="data">The buffer that contains the binary data to be sent.</param>            /// <param name="offset">The zero-based position in the <paramref name="data"/> at which to begin sending data.</param>            /// <param name="length">The number of bytes to be sent from <paramref name="data"/> starting at the <paramref name="offset"/>.</param>            public void Multicast(byte[] data, int offset, int length) => WaitHandle.WaitAll(MulticastAsync(data, offset, length));             /// <summary>            /// Sends data to the specified client asynchronously.            /// </summary>            /// <param name="clientID">ID of the client to which the data is to be sent.</param>            /// <param name="data">The buffer that contains the binary data to be sent.</param>            /// <param name="offset">The zero-based position in the <paramref name="data"/> at which to begin sending data.</param>            /// <param name="length">The number of bytes to be sent from <paramref name="data"/> starting at the <paramref name="offset"/>.</param>            /// <returns><see cref="WaitHandle"/> for the asynchronous operation.</returns>            public WaitHandle SendToAsync(Guid clientID, byte[] data, int offset, int length) => m_tcpServer?.SendToAsync(clientID, data, offset, length) ?? new ManualResetEvent(true);             /// <summary>            /// Sends data to all of the connected clients asynchronously.            /// </summary>            /// <param name="data">The buffer that contains the binary data to be sent.</param>            /// <param name="offset">The zero-based position in the <paramref name="data"/> at which to begin sending data.</param>            /// <param name="length">The number of bytes to be sent from <paramref name="data"/> starting at the <paramref name="offset"/>.</param>            /// <returns>Array of <see cref="WaitHandle"/> for the asynchronous operation.</returns>            public WaitHandle[] MulticastAsync(byte[] data, int offset, int length) => m_tcpServer is null ? Array.Empty<WaitHandle>() : new[] { m_tcpServer.SendToAsync(m_clientID, data, offset, length) };             /// <summary>            /// Reads a number of bytes from the current received data buffer and writes those bytes into a byte array at the specified offset.            /// </summary>            /// <param name="clientID">ID of the client from which data buffer should be read.</param>            /// <param name="buffer">Destination buffer used to hold copied bytes.</param>            /// <param name="startIndex">0-based starting index into destination <paramref name="buffer"/> to begin writing data.</param>            /// <param name="length">The number of bytes to read from current received data buffer and write into <paramref name="buffer"/>.</param>            /// <returns>The number of bytes read.</returns>            /// <remarks>            /// This function should only be called from within the <see cref="ReceiveClientData"/> event handler. Calling this method outside this            /// event will have unexpected results.            /// </remarks>            public int Read(Guid clientID, byte[] buffer, int startIndex, int length) => m_tcpServer?.Read(clientID, buffer, startIndex, length) ?? 0;             /// <summary>            /// Gets a reference to the shared server listening            /// on this server's local end point.            /// </summary>            /// <returns>A reference to a shared server.</returns>            private void GetSharedServer()            {                const string ConfigurationMismatchError = "Configuration mismatch detected between parsers using shared TCP server: {0}";                 lock (s_sharedServers)

Lines 1–500 of 3731.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t