2623 lines · 117.4 KB · C#
RawDownload
//******************************************************************************************************//  DataPublisher.cs - Gbtc////  Copyright © 2010, 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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php////  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://  ----------------------------------------------------------------------------------------------------//  08/20/2010 - J. Ritchie Carroll//       Generated original version of source code.//  11/15/2010 - Mehulbhai P Thakkar//       Fixed bug when DataSubscriber tries to resubscribe by setting subscriber. Initialized manually //       in ReceiveClientDataComplete event handler.//  12/02/2010 - J. Ritchie Carroll//       Fixed an issue for when DataSubcriber dynamically resubscribes with a different//       synchronization method (e.g., going from unsynchronized to synchronized)//  05/26/2011 - J. Ritchie Carroll//       Implemented subscriber authentication model.//  02/07/2012 - Mehulbhai Thakkar//       Modified m_metadataTables to include filter expression and made the list ";" seperated.////****************************************************************************************************** using GSF.Communication;//using GSF.Data;//using GSF.Security.Cryptography;using GSF.TimeSeries.Adapters;//using GSF.TimeSeries.Statistics;using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Globalization;using System.IO;using System.IO.Compression;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Text.RegularExpressions;using System.Threading;using System.Xml; namespace GSF.TimeSeries.Transport{    #region [ Enumerations ]     /// <summary>    /// Server commands received by <see cref="DataPublisher"/> and sent by <see cref="DataSubscriber"/>.    /// </summary>    /// <remarks>    /// Solicited server commands will receive a <see cref="ServerResponse.Succeeded"/> or <see cref="ServerResponse.Failed"/>    /// response code along with an associated success or failure message. Message type for successful responses will be based    /// on server command - for example, server response for a successful MetaDataRefresh command will return a serialized    /// <see cref="DataSet"/> of the available server metadata. Message type for failed responses will always be a string of    /// text respresenting the error message.    /// </remarks>    public enum ServerCommand : byte    {        /// <summary>        /// Authenticate command.        /// </summary>        /// <remarks>        /// Requests that server authenticate client with the following encrypted authentication packet.        /// Successful return message type will be string indicating client ID was validated.        /// Client should not attempt further steps if authentication fails, user will need to take action.        /// Client should send MetaDataRefresh and Subscribe commands upon successful authentication.        /// If server is setup to not require authentication, authentication step can be skipped - however        /// it will be up to the implementation to know if authentication is required - the server cannot be        /// queried to determine if authentication is required.        /// It is expected that use of this protocol between gateways over a shared network (i.e., the lower        /// security zone) will require authentication.        /// It is expected that use of this protocol between gateway and other systems (e.g., a PDC) in a        /// private network (i.e., the higher security zone) can be setup to not require authentication.        /// </remarks>        Authenticate = 0x00,        /// <summary>        /// Meta data refresh command.        /// </summary>        /// <remarks>        /// Requests that server send an updated set of metadata so client can refresh its point list.        /// Successful return message type will be a <see cref="DataSet"/> containing server device and measurement metadata.        /// Received device list should be defined as children of the "parent" server device connection similar to the way        /// PMUs are defined as children of a parent PDC device connection.        /// Devices and measurements contain unique Guids that should be used to key metadata updates in local repository.        /// </remarks>        MetaDataRefresh = 0x01,        /// <summary>        /// Subscribe command.        /// </summary>        /// <remarks>        /// Requests a subscription of streaming data from server based on connection string that follows.        /// It will not be necessary to stop an existing subscription before requesting a new one.        /// Successful return message type will be string indicating total number of allowed points.        /// Client should wait for UpdateSignalIndexCache and UpdateBaseTime response codes before attempting        /// to parse data when using the compact measurement format.        /// </remarks>        Subscribe = 0x02,        /// <summary>        /// Unsubscribe command.        /// </summary>        /// <remarks>        /// Requests that server stop sending streaming data to the client and cancel the current subscription.        /// </remarks>        Unsubscribe = 0x03,        /// <summary>        /// Rotate cipher keys.        /// </summary>        /// <remarks>        /// Manually requests that server send a new set of cipher keys for data packet encryption.        /// </remarks>        RotateCipherKeys = 0x04,        /// <summary>        /// Update processing interval.        /// </summary>        /// <remarks>        /// Manually requests server to update the processing interval with the following specified value.        /// </remarks>        UpdateProcessingInterval = 0x05,        /// <summary>        /// Define operational modes for subscriber connection.        /// </summary>        /// <remarks>        /// As soon as connection is established, requests that server set operational        /// modes that affect how the subscriber and publisher will communicate.        /// </remarks>        DefineOperationalModes = 0x06    }     /// <summary>    /// Server responses sent by <see cref="DataPublisher"/> and received by <see cref="DataSubscriber"/>.    /// </summary>    public enum ServerResponse : byte    {        // Although the server commands and responses will be on two different paths, the response enumeration values        // are defined as distinct from the command values to make it easier to identify codes from a wire analysis.         /// <summary>        /// Command succeeded response.        /// </summary>        /// <remarks>        /// Informs client that its solicited server command succeeded, original command and success message follow.        /// </remarks>        Succeeded = 0x80,        /// <summary>        /// Command failed response.        /// </summary>        /// <remarks>        /// Informs client that its solicited server command failed, original command and failure message follow.        /// </remarks>        Failed = 0x81,        /// <summary>        /// Data packet response.        /// </summary>        /// <remarks>        /// Unsolicited response informs client that a data packet follows.        /// </remarks>        DataPacket = 0x82,        /// <summary>        /// Update signal index cache response.        /// </summary>        /// <remarks>        /// Unsolicited response requests that client update its runtime signal index cache with the one that follows.        /// </remarks>        UpdateSignalIndexCache = 0x83,        /// <summary>        /// Update runtime base-timestamp offsets response.        /// </summary>        /// <remarks>        /// Unsolicited response requests that client update its runtime base-timestamp offsets with those that follow.        /// </remarks>        UpdateBaseTimes = 0x84,        /// <summary>        /// Update runtime cipher keys response.        /// </summary>        /// <remarks>        /// Response, solicited or unsolicited, requests that client update its runtime data cipher keys with those that follow.        /// </remarks>        UpdateCipherKeys = 0x85,        /// <summary>        /// Data start time response packet.        /// </summary>        /// <remarks>        /// Unsolicited response provides the start time of data being processed from the first measurement.        /// </remarks>        DataStartTime = 0x86,        /// <summary>        /// Processing complete notification.        /// </summary>        /// <remarks>        /// Unsolicited response provides notification that input processing has completed, typically via temporal constraint.        /// </remarks>        ProcessingComplete = 0x87,        /// <summary>        /// No operation keep-alive ping.        /// </summary>        /// <remarks>        /// The command channel can remain quiet for some time, this command allows a period test of client connectivity.        /// </remarks>        NoOP = 0xFF    }     /// <summary>    /// <see cref="DataPublisher"/> data packet flags.    /// </summary>    [Flags()]    public enum DataPacketFlags : byte    {        /// <summary>        /// Determines if data packet is synchronized.        /// </summary>        /// <remarks>        /// Bit set = synchronized, bit clear = unsynchronized.        /// </remarks>        Synchronized = (byte)Bits.Bit00,        /// <summary>        /// Determines if serialized measurement is compact.        /// </summary>        /// <remarks>        /// Bit set = compact, bit clear = full fidelity.        /// </remarks>        Compact = (byte)Bits.Bit01,        /// <summary>        /// Detemines which cipher index to use when encrypting data packet.        /// </summary>        /// <remarks>        /// Bit set = use odd cipher index (i.e., 1), bit clear = use even cipher index (i.e., 0).        /// </remarks>        CipherIndex = (byte)Bits.Bit02,        /// <summary>        /// No flags set.        /// </summary>        /// <remarks>        /// This would represent unsynchronized, full fidelity measurement data packets.        /// </remarks>        NoFlags = (Byte)Bits.Nil    }     /// <summary>    /// Operational modes that affect how <see cref="DataPublisher"/> and <see cref="DataSubscriber"/> communicate.    /// </summary>    /// <remarks>    /// Operational modes only apply to fundamental protocol control.    /// </remarks>    [Flags()]    public enum OperationalModes : uint    {        /// <summary>        /// Mask to get version number of protocol.        /// </summary>        /// <remarks>        /// Version number is currently set to 0.        /// </remarks>        VersionMask = (uint)(Bits.Bit04 | Bits.Bit03 | Bits.Bit02 | Bits.Bit01 | Bits.Bit00),        /// <summary>        /// Mask to get mode of compression.        /// </summary>        /// <remarks>        /// Compression mode is currently not supported.        /// These bits are simply reserved for different compression modes.        /// </remarks>        CompressionModeMask = (uint)(Bits.Bit07 | Bits.Bit06 | Bits.Bit05),        /// <summary>        /// Mask to get character encoding used when exchanging messages between publisher and subscriber.        /// </summary>        /// <remarks>        /// 00 = UTF-16 little endian<br/>        /// 01 = UTF-16 big endian<br/>        /// 10 = UTF-8<br/>        /// 11 = ANSI        /// </remarks>        EncodingMask = (uint)(Bits.Bit09 | Bits.Bit08),        /// <summary>        /// Determines type of serialization to use when exchanging signal index cache and metadata.        /// </summary>        /// <remarks>        /// Bit set = common serialization format, bit clear = .NET serialization format        /// </remarks>        UseCommonSerializationFormat = (uint)Bits.Bit24,        /// <summary>        /// Determines whether the signal index cache is compressed when exchanging between publisher and subscriber.        /// </summary>        /// <remarks>        /// Bit set = compress, bit clear = no compression        /// </remarks>        CompressSignalIndexCache = (uint)Bits.Bit30,        /// <summary>        /// Determines whether is compressed when exchanging between publisher and subscriber.        /// </summary>        /// <remarks>        /// Bit set = compress, bit clear = no compression        /// </remarks>        CompressMetadata = (uint)Bits.Bit31,        /// <summary>        /// No flags set.        /// </summary>        /// <remarks>        /// This would represent protocol version 0,        /// UTF-16 little endian character encoding,        /// .NET serialization and no compression.        /// </remarks>        NoFlags = (uint)Bits.Nil    }     /// <summary>    /// Enumeration for character encodings supported by the Gateway Exchange Protocol.    /// </summary>    public enum OperationalEncoding : uint    {        /// <summary>        /// UTF-16 little endian        /// </summary>        Unicode = (uint)Bits.Nil,        /// <summary>        /// UTF-16 bit endian        /// </summary>        BigEndianUnicode = (uint)Bits.Bit08,        /// <summary>        /// UTF-8        /// </summary>        UTF8 = (uint)Bits.Bit09,        /// <summary>        /// ANSI        /// </summary>        ANSI = (uint)(Bits.Bit09 | Bits.Bit08)    }     /// <summary>    /// Enumeration for compression modes supported by the Gateway Exchange Protocol.    /// </summary>    public enum GatewayCompressionMode : uint    {        /// <summary>        /// GZip compression        /// </summary>        GZip = (uint)Bits.Bit05,        /// <summary>        /// No compression        /// </summary>        None = (uint)Bits.Nil    }     #endregion     /// <summary>    /// Represents a data publishing server that allows multiple connections for data subscriptions.    /// </summary>    [Description("DataPublisher: server component that allows gateway-style subscription connections.")]    [EditorBrowsable(EditorBrowsableState.Always)]    public class DataPublisher : ActionAdapterCollection    {        #region [ Members ]         // Nested Types         private class LatestMeasurementCache : FacileActionAdapterBase        {            public LatestMeasurementCache(string connectionString)            {                ConnectionString = connectionString;            }             public override string Name            {                get                {                    return "LatestMeasurementCache";                }                set                {                    base.Name = value;                }            }             public override bool SupportsTemporalProcessing            {                get                {                    return false;                }            }             public override string GetShortStatus(int maxLength)            {                return "LatestMeasurementCache happily exists. :)";            }        }         // Events         /// <summary>        /// Indicates that a new client has connected to the publisher.        /// </summary>        /// <remarks>        /// <see cref="EventArgs{T1, T2, T3}.Argument1"/> is the <see cref="Guid"/> based subscriber ID.<br/>        /// <see cref="EventArgs{T1, T2, T3}.Argument2"/> is the connection identification (e.g., IP and DNS name, if available).<br/>        /// <see cref="EventArgs{T1, T2, T3}.Argument3"/> is the subscriber information as reported by the client.        /// </remarks>        public event EventHandler<EventArgs<Guid, string, string>> ClientConnected;         /// <summary>        /// Indicates to the host that processing for an input adapter (via temporal session) has completed.        /// </summary>        /// <remarks>        /// This event is expected to only be raised when an input adapter has been designed to process        /// a finite amount of data, e.g., reading a historical range of data during temporal procesing.        /// </remarks>        public event EventHandler ProcessingComplete;         // Constants         /// <summary>        /// Default value for <see cref="RequireAuthentication"/>.        /// </summary>        public const bool DefaultRequireAuthentication = false;         /// <summary>        /// Default value for <see cref="EncryptPayload"/>.        /// </summary>        public const bool DefaultEncryptPayload = false;         /// <summary>        /// Default value for <see cref="SharedDatabase"/>.        /// </summary>        public const bool DefaultSharedDatabase = false;         /// <summary>        /// Default value for <see cref="AllowSynchronizedSubscription"/>.        /// </summary>        public const bool DefaultAllowSynchronizedSubscription = true;         /// <summary>        /// Default value for <see cref="UseBaseTimeOffsets"/>.        /// </summary>        public const bool DefaultUseBaseTimeOffsets = false;         /// <summary>        /// Default value for <see cref="CipherKeyRotationPeriod"/>.        /// </summary>        public const double DefaultCipherKeyRotationPeriod = 60000.0D;         // Maximum packet size before software fragmentation of UDP payload        internal const int MaxPacketSize = ushort.MaxValue / 2;         // Length of random salt prefix        internal const int CipherSaltLength = 8;         // Fields        private ManualResetEvent m_initializeWaitHandle;        private TcpServer m_commandChannel;        private ConcurrentDictionary<Guid, ClientConnection> m_clientConnections;        private ConcurrentDictionary<Guid, IServer> m_clientPublicationChannels;        private ConcurrentDictionary<MeasurementKey, Guid> m_signalIDCache;        private System.Timers.Timer m_commandChannelRestartTimer;        private System.Timers.Timer m_cipherKeyRotationTimer;        private RoutingTables m_routingTables;        private IAdapterCollection m_parent;        private string m_metadataTables;        private bool m_requireAuthentication;        private bool m_encryptPayload;        private bool m_sharedDatabase;        private bool m_allowSynchronizedSubscription;        private bool m_useBaseTimeOffsets;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new <see cref="DataPublisher"/>.        /// </summary>        public DataPublisher()            : this(null)        {            // When collection is spawned as an adapter, it needs a parameterless constructor        }         /// <summary>        /// Creates a new <see cref="DataPublisher"/>.        /// </summary>        /// <param name="waitHandles">Wait handle dictionary.</param>        public DataPublisher(ConcurrentDictionary<string, AutoResetEvent> waitHandles)            : base(waitHandles)        {            base.Name = "Data Publisher Collection";            base.DataMember = "[internal]";             m_initializeWaitHandle = new ManualResetEvent(false);

Lines 1–500 of 2623.

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