436 lines · 16.4 KB · C#
RawDownload
//******************************************************************************************************//  SignalIndexCache.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://  ----------------------------------------------------------------------------------------------------//  05/15/2011 - J. Ritchie Carroll//       Generated original version of source code.//  12/20/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.Data;using System.Linq;using System.Text;using GSF.Collections;using GSF.Parsing; #pragma warning disable 618 namespace GSF.TimeSeries.Transport{    /// <summary>    /// Represents a serializable <see cref="Guid"/> signal ID to <see cref="ushort"/> index cross reference.    /// </summary>    /// <remarks>    /// This class is used to create a runtime index to be used for data exchange so that a 16-bit integer    /// is exchanged in the data packets for signal identification instead of the 128-bit Guid signal ID    /// to reduce bandwidth required for signal exchange. This means the total number of unique signal    /// IDs that could be exchanged using this method in a single session is 65,535. This number seems    /// reasonable for the currently envisioned use cases, however, multiple sessions each with their own    /// runtime signal index cache could be established if this is a limitation for a given data set.    /// </remarks>    [Serializable]    public class SignalIndexCache : ISupportBinaryImage    {        #region [ Members ]         // Fields        private Guid m_subscriberID;         // Since measurement keys are statically cached as a global system optimization and the keys        // can be different between two parties exchanging data, the raw measurement key elements are        // cached and exchanged instead of actual measurement key values        private ConcurrentDictionary<ushort, MeasurementKey> m_reference;        private Guid[] m_unauthorizedSignalIDs;         /// <summary>        /// Lookups MeasurementKey.RuntimeID and returns ushort SignalIndex. -1 means it does not exist.        /// </summary>        [NonSerialized] // SignalID reverse lookup runtime cache (used to speed deserialization)        private IndexedArray<int> m_signalIDCache;         [NonSerialized]        private Encoding m_encoding;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new <see cref="SignalIndexCache"/> instance.        /// </summary>        public SignalIndexCache()        {            m_reference = new ConcurrentDictionary<ushort, MeasurementKey>();            m_signalIDCache = new IndexedArray<int>(-1);        }         /// <summary>        /// Creates a new local system cache from one that was received remotely.        /// </summary>        /// <param name="dataSource"><see cref="DataSet"/> based data source used to interpret local measurement keys.</param>        /// <param name="remoteCache">Deserialized remote signal index cache.</param>        public SignalIndexCache(DataSet dataSource, SignalIndexCache remoteCache)        {            m_subscriberID = remoteCache.SubscriberID;             // If active measurements are defined, interpret signal cache in context of current measurement key definitions            if (dataSource != null && dataSource.Tables.Contains("ActiveMeasurements"))            {                DataTable activeMeasurements = dataSource.Tables["ActiveMeasurements"];                m_reference = new ConcurrentDictionary<ushort, MeasurementKey>();                 foreach (KeyValuePair<ushort, MeasurementKey> signalIndex in remoteCache.Reference)                {                    Guid signalID = signalIndex.Value.SignalID;                    DataRow[] filteredRows = activeMeasurements.Select("SignalID = '" + signalID.ToString() + "'");                     if (filteredRows.Length > 0)                    {                        DataRow row = filteredRows[0];                        MeasurementKey key = MeasurementKey.LookUpOrCreate(signalID, row["ID"].ToNonNullString(MeasurementKey.Undefined.ToString()));                        m_reference.TryAdd(signalIndex.Key, key);                    }                }                 m_unauthorizedSignalIDs = remoteCache.UnauthorizedSignalIDs;            }            else            {                // Just use remote signal index cache as-is if no local configuration exists                m_reference = remoteCache.Reference;                m_unauthorizedSignalIDs = remoteCache.UnauthorizedSignalIDs;            }        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the <see cref="Guid"/> based subscriber ID of this <see cref="SignalIndexCache"/>.        /// </summary>        public Guid SubscriberID        {            get            {                return m_subscriberID;            }            set            {                m_subscriberID = value;            }        }         /// <summary>        /// Gets or sets integer signal index cross reference dictionary.        /// </summary>        public ConcurrentDictionary<ushort, MeasurementKey> Reference        {            get            {                return m_reference;            }            set            {                m_reference = value;                IndexedArray<int> signalIDCache = new IndexedArray<int>(-1);                foreach (var pair in value)                {                    signalIDCache[pair.Value.RuntimeID] = pair.Key;                }                 m_signalIDCache = signalIDCache;            }        }         /// <summary>        /// Gets reference to array of requested input measurement signal IDs that were authorized.        /// </summary>        public Guid[] AuthorizedSignalIDs        {            get            {                return m_reference.Select(kvp => kvp.Value.SignalID).ToArray();            }        }         /// <summary>        /// Gets or sets reference to array of requested input measurement signal IDs that were unauthorized.        /// </summary>        public Guid[] UnauthorizedSignalIDs        {            get            {                return m_unauthorizedSignalIDs;            }            set            {                m_unauthorizedSignalIDs = value;            }        }         /// <summary>        /// Gets the current maximum integer signal index.        /// </summary>        public ushort MaximumIndex        {            get            {                if (m_reference.Count == 0)                    return 0;                 return (ushort)(m_reference.Max(kvp => kvp.Key) + 1);            }        }         /// <summary>        /// Gets or sets character encoding used to convert strings to binary.        /// </summary>        public Encoding Encoding        {            get            {                return m_encoding;            }            set            {                m_encoding = value;            }        }         /// <summary>        /// Gets the length of the binary image.        /// </summary>        public int BinaryLength        {            get            {                int binaryLength = 0;                 if ((object)m_encoding == null)                    throw new InvalidOperationException("Attempt to get binary length of signal index cache without setting a character encoding.");                 // Byte size of cache                binaryLength += 4;                 // Subscriber ID                binaryLength += 16;                 // Number of references                binaryLength += 4;                 // Each reference                binaryLength += m_reference.Sum(kvp => 2 + 16 + 4 + m_encoding.GetByteCount(kvp.Value.Source) + 4);                 // Number of unauthorized IDs                binaryLength += 4;                 // Each unauthorized ID                binaryLength += 16 * (m_unauthorizedSignalIDs ?? new Guid[0]).Length;                 return binaryLength;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Gets runtime signal index for given <see cref="Guid"/> signal ID.        /// </summary>        /// <param name="key">The <see cref="MeasurementKey"/> used to lookup associated runtime signal index.</param>        /// <returns>Runtime signal index for given <see cref="MeasurementKey"/> <paramref name="key"/>.</returns>        public ushort GetSignalIndex(MeasurementKey key)        {            int value = m_signalIDCache[key.RuntimeID];            if (value < 0)                return ushort.MaxValue;             return (ushort)value;        }         /// <summary>        /// Generates binary image of the <see cref="SignalIndexCache"/> and copies it into the given buffer, for <see cref="BinaryLength"/> bytes.        /// </summary>        /// <param name="buffer">Buffer used to hold generated binary image of the source object.</param>        /// <param name="startIndex">0-based starting index in the <paramref name="buffer"/> to start writing.</param>        /// <returns>The number of bytes written to the <paramref name="buffer"/>.</returns>        public int GenerateBinaryImage(byte[] buffer, int startIndex)        {            Guid[] unauthorizedSignalIDs = m_unauthorizedSignalIDs ?? new Guid[0];             int binaryLength = BinaryLength;            int offset = startIndex;            byte[] bigEndianBuffer;            byte[] unicodeBuffer;             if ((object)m_encoding == null)                throw new InvalidOperationException("Attempt to generate binary image of signal index cache without setting a character encoding.");             buffer.ValidateParameters(startIndex, binaryLength);             // Byte size of cache            bigEndianBuffer = BigEndian.GetBytes(binaryLength);            Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);            offset += bigEndianBuffer.Length;             // Subscriber ID            bigEndianBuffer = EndianOrder.BigEndian.GetBytes(m_subscriberID);            Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);            offset += bigEndianBuffer.Length;             // Number of references            bigEndianBuffer = BigEndian.GetBytes(m_reference.Count);            Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);            offset += bigEndianBuffer.Length;             foreach (KeyValuePair<ushort, MeasurementKey> kvp in m_reference)            {                // Signal index                bigEndianBuffer = BigEndian.GetBytes(kvp.Key);                Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);                offset += bigEndianBuffer.Length;                 // Signal ID                bigEndianBuffer = EndianOrder.BigEndian.GetBytes(kvp.Value.SignalID);                Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);                offset += bigEndianBuffer.Length;                 // Source                unicodeBuffer = m_encoding.GetBytes(kvp.Value.Source);                bigEndianBuffer = BigEndian.GetBytes(unicodeBuffer.Length);                Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);                offset += bigEndianBuffer.Length;                Buffer.BlockCopy(unicodeBuffer, 0, buffer, offset, unicodeBuffer.Length);                offset += unicodeBuffer.Length;                 // ID                bigEndianBuffer = BigEndian.GetBytes((uint)kvp.Value.ID);                Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);                offset += bigEndianBuffer.Length;            }             // Number of unauthorized IDs            bigEndianBuffer = BigEndian.GetBytes(unauthorizedSignalIDs.Length);            Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);            offset += bigEndianBuffer.Length;             foreach (Guid signalID in unauthorizedSignalIDs)            {                // Unauthorized ID                bigEndianBuffer = EndianOrder.BigEndian.GetBytes(signalID);                Buffer.BlockCopy(bigEndianBuffer, 0, buffer, offset, bigEndianBuffer.Length);                offset += bigEndianBuffer.Length;            }             return binaryLength;        }         /// <summary>        /// Initializes the <see cref="SignalIndexCache"/> by parsing the specified <paramref name="buffer"/> containing a binary image.        /// </summary>        /// <param name="buffer">Buffer containing binary image to parse.</param>        /// <param name="startIndex">0-based starting index in the <paramref name="buffer"/> to start parsing.</param>        /// <param name="length">Valid number of bytes within <paramref name="buffer"/> to read from <paramref name="startIndex"/>.</param>        /// <returns>The number of bytes used for initialization in the <paramref name="buffer"/> (i.e., the number of bytes parsed).</returns>        public int ParseBinaryImage(byte[] buffer, int startIndex, int length)        {            int binaryLength;            int offset = startIndex;             int referenceCount;            ushort signalIndex;            Guid signalID;            int sourceSize;            string source;            uint id;             int unauthorizedIDCount;             if ((object)m_encoding == null)                throw new InvalidOperationException("Attempt to parse binary image of signal index cache without setting a character encoding.");             buffer.ValidateParameters(startIndex, length);             if (length < 4)                return 0;             // Byte size of cache            binaryLength = BigEndian.ToInt32(buffer, offset);            offset += 4;             if (length < binaryLength)                return 0;             // We know we have enough data so we can empty the reference cache            m_reference.Clear();             // Subscriber ID            m_subscriberID = EndianOrder.BigEndian.ToGuid(buffer, offset);            offset += 16;             // Number of references            referenceCount = BigEndian.ToInt32(buffer, offset);            offset += 4;             for (int i = 0; i < referenceCount; i++)            {                // Signal index                signalIndex = BigEndian.ToUInt16(buffer, offset);                offset += 2;                 // Signal ID                signalID = EndianOrder.BigEndian.ToGuid(buffer, offset);                offset += 16;                 // Source                sourceSize = BigEndian.ToInt32(buffer, offset);                offset += 4;                source = m_encoding.GetString(buffer, offset, sourceSize);                offset += sourceSize;                 // ID                id = BigEndian.ToUInt32(buffer, offset);                offset += 4;                 m_reference[signalIndex] = MeasurementKey.LookUpOrCreate(signalID, source, id);            }             // Number of unauthorized IDs            unauthorizedIDCount = BigEndian.ToInt32(buffer, offset);            m_unauthorizedSignalIDs = new Guid[unauthorizedIDCount];            offset += 4;             for (int i = 0; i < unauthorizedIDCount; i++)            {                // Unauthorized ID                m_unauthorizedSignalIDs[i] = EndianOrder.BigEndian.ToGuid(buffer, offset);                offset += 16;            }             return binaryLength;        }         #endregion    }}

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