//****************************************************************************************************** // MetadataRecord.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/03/2006 - Pinal C. Patel // Generated original version of code based on DatAWare system specifications by Brian B. Fox, GSF. // 04/20/2009 - Pinal C. Patel // Converted to C#. // 09/15/2009 - Stephen C. Wills // Added new header and license agreement. // 10/11/2010 - Mihir Brahmbhatt // Updated header and license agreement. // 11/30/2011 - J. Ritchie Carroll // Modified to support buffer optimized ISupportBinaryImage. // 12/14/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.IO; using System.Text; using GSF.Parsing; namespace GSF.Historian.Files { /// /// Represents a record in the that contains the various attributes associates to a . /// /// /// /// /// /// /// /// /// /// public class MetadataRecord : ISupportBinaryImage, IComparable { // ************************************************************************************************** // * Binary Structure * // ************************************************************************************************** // * # Of Bytes Byte Index Data Type Property Name * // * ---------- ---------- ---------- --------------------------------------------------------------* // * 512 0-511 Char(512) Remarks * // * 512 512-1023 Char(512) HardwareInfo * // * 512 1024-1535 Char(512) EmailAddresses * // * 80 1536-1615 Char(80) Description * // * 80 1616-1695 Char(80) CurrentData * // * 40 1696-1735 Char(40) Name * // * 40 1736-1775 Char(40) Synonym1 * // * 40 1776-1815 Char(40) Synonym2 * // * 40 1816-1855 Char(40) Synonym3 * // * 40 1856-1895 Char(40) PagerNumbers * // * 40 1896-1935 Char(40) PhoneNumbers * // * 24 1936-1959 Char(24) PlantCode * // * 24 1960-1983 Char(24) System * // * 40 1984-2023 Char(40) EmailTime * // * 40 2024-2063 Char(40) [Spare string field 1] * // * 40 2064-2103 Char(40) [Spare string field 2] * // * 4 2104-2107 Single ScanRate * // * 4 2108-2111 Int32 UnitNumber * // * 4 2112-2115 Int32 SecurityFlags * // * 4 2116-2119 Int32 GeneralFlags * // * 4 2120-2123 Int32 AlarmFlags * // * 4 2124-2127 Int32 CompressionMinTime * // * 4 2128-2131 Int32 CompressionMaxTime * // * 4 2132-2135 Int32 SourceID * // * 4 2136-2139 Int32 [Spare 32-bit field 1] * // * 4 2140-2143 Int32 [Spare 32-bit field 2] * // * 4 2144-2147 Int32 [Spare 32-bit field 3] * // * 4 2148-2151 Int32 [Spare 32-bit field 4] * // * 512 2152-2663 Byte(512) (Analog | Digital| Composed |Constant)Fields * // ************************************************************************************************** #region [ Members ] // Constants /// /// Specifies the number of bytes in the binary image of . /// public const int FixedLength = 2664; // Fields private string m_remarks; private string m_hardwareInfo; private string m_emailAddresses; private string m_description; private string m_currentData; private string m_name; private string m_synonym1; private string m_synonym2; private string m_synonym3; private string m_pagerNumbers; private string m_phoneNumbers; private string m_plantCode; private string m_system; private string m_emailTime; private MetadataRecordSecurityFlags m_securityFlags; private MetadataRecordGeneralFlags m_generalFlags; private MetadataRecordAlarmFlags m_alarmFlags; private int m_sourceID; private MetadataRecordAnalogFields m_analogFields; private MetadataRecordDigitalFields m_digitalFields; private MetadataRecordComposedFields m_composedFields; private MetadataRecordConstantFields m_constantFields; private readonly MetadataFileLegacyMode m_legacyMode; #endregion #region [ Constructors ] /// /// Initializes a new instance of the class. /// /// Historian identifier of . /// Legacy mode of . public MetadataRecord(int historianID, MetadataFileLegacyMode legacyMode) { if (historianID < 0) throw new ArgumentOutOfRangeException(nameof(historianID)); HistorianID = historianID; m_remarks = string.Empty; m_hardwareInfo = string.Empty; m_emailAddresses = string.Empty; m_description = string.Empty; m_currentData = string.Empty; m_name = string.Empty; m_synonym1 = string.Empty; m_synonym2 = string.Empty; m_synonym3 = string.Empty; m_pagerNumbers = string.Empty; m_phoneNumbers = string.Empty; m_plantCode = string.Empty; m_system = string.Empty; m_emailTime = string.Empty; m_securityFlags = new MetadataRecordSecurityFlags(); m_generalFlags = new MetadataRecordGeneralFlags(); m_alarmFlags = new MetadataRecordAlarmFlags(); m_analogFields = new MetadataRecordAnalogFields(legacyMode); m_digitalFields = new MetadataRecordDigitalFields(legacyMode); m_composedFields = new MetadataRecordComposedFields(legacyMode); m_constantFields = new MetadataRecordConstantFields(); m_legacyMode = legacyMode; } /// /// Initializes a new instance of the class. /// /// Historian identifier of . /// Legacy mode of . /// Binary image to be used for initializing . /// 0-based starting index of initialization data in the . /// Valid number of bytes in from . public MetadataRecord(int historianID, MetadataFileLegacyMode legacyMode, byte[] buffer, int startIndex, int length) : this(historianID, legacyMode) { ParseBinaryImage(buffer, startIndex, length); } internal MetadataRecord(BinaryReader reader) { HistorianID = reader.ReadInt32(); m_remarks = reader.ReadString(); m_hardwareInfo = reader.ReadString(); m_emailAddresses = reader.ReadString(); m_description = reader.ReadString(); m_currentData = reader.ReadString(); m_name = reader.ReadString(); m_synonym1 = reader.ReadString(); m_synonym2 = reader.ReadString(); m_synonym3 = reader.ReadString(); m_pagerNumbers = reader.ReadString(); m_phoneNumbers = reader.ReadString(); m_plantCode = reader.ReadString(); m_system = reader.ReadString(); m_emailTime = reader.ReadString(); ScanRate = reader.ReadSingle(); UnitNumber = reader.ReadInt32(); m_securityFlags = new MetadataRecordSecurityFlags { Value = reader.ReadInt32() }; m_generalFlags = new MetadataRecordGeneralFlags { Value = reader.ReadInt32() }; m_alarmFlags = new MetadataRecordAlarmFlags { Value = reader.ReadInt32() }; CompressionMinTime = reader.ReadInt32(); CompressionMaxTime = reader.ReadInt32(); m_sourceID = reader.ReadInt32(); switch (m_generalFlags.DataType) { case DataType.Analog: m_analogFields = new MetadataRecordAnalogFields(reader); m_digitalFields = new MetadataRecordDigitalFields(MetadataFileLegacyMode.Disabled); m_composedFields = new MetadataRecordComposedFields(MetadataFileLegacyMode.Disabled); m_constantFields = new MetadataRecordConstantFields(); break; case DataType.Digital: m_digitalFields = new MetadataRecordDigitalFields(reader); m_analogFields = new MetadataRecordAnalogFields(MetadataFileLegacyMode.Disabled); m_composedFields = new MetadataRecordComposedFields(MetadataFileLegacyMode.Disabled); m_constantFields = new MetadataRecordConstantFields(); break; case DataType.Composed: m_composedFields = new MetadataRecordComposedFields(reader); m_analogFields = new MetadataRecordAnalogFields(MetadataFileLegacyMode.Disabled); m_digitalFields = new MetadataRecordDigitalFields(MetadataFileLegacyMode.Disabled); m_constantFields = new MetadataRecordConstantFields(); break; case DataType.Constant: m_constantFields = new MetadataRecordConstantFields(reader); m_analogFields = new MetadataRecordAnalogFields(MetadataFileLegacyMode.Disabled); m_digitalFields = new MetadataRecordDigitalFields(MetadataFileLegacyMode.Disabled); m_composedFields = new MetadataRecordComposedFields(MetadataFileLegacyMode.Disabled); break; default: throw new ArgumentOutOfRangeException(); } } #endregion #region [ Properties ] /// /// Gets or sets any remarks associated with the . /// /// /// Maximum length for is 512 characters. /// /// The value being assigned is a null string. public string Remarks { get => m_remarks; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_remarks = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(512) : value; } } /// /// Gets or sets hardware information associated with the . /// /// /// Maximum length for is 512 characters. /// /// The value being assigned is a null string. public string HardwareInfo { get => m_hardwareInfo; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_hardwareInfo = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(512) : value; } } /// /// Gets or sets a comma-separated list of email addresses that will receive alarm notification email messages based /// on the settings for the . /// /// /// Maximum length for is 512 characters. /// /// The value being assigned is a null string. public string AlarmEmails { get => m_emailAddresses; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_emailAddresses = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(512) : value; } } /// /// Gets or sets the description associated with the . /// /// /// Maximum length for is 80 characters. /// /// The value being assigned is a null string. public string Description { get => m_description; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_description = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(80) : value; } } /// /// Gets or sets the time, value and quality of the most current data received for the . /// /// /// Maximum length for is 80 characters. /// /// The value being assigned is a null string. public string CurrentData { get => m_currentData; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_currentData = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(80) : value; } } /// /// Gets or sets a alpha-numeric name for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string Name { get => m_name; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_name = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } } /// /// Gets or sets an alternate for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string Synonym1 { get => m_synonym1; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_synonym1 = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } } /// /// Gets or sets an alternate for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string Synonym2 { get => m_synonym2; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_synonym2 = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } } /// /// Gets or sets an alternate for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string Synonym3 { get => m_synonym3; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_synonym3 = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } } /// /// Gets or sets a comma-separated list of pager numbers that will receive alarm notification text messages based /// on the settings for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string AlarmPagers { get => m_pagerNumbers; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_pagerNumbers = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } } /// /// Gets or sets a comma-separated list of phone numbers that will receive alarm notification voice messages based /// on the settings for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string AlarmPhones { get => m_phoneNumbers; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_phoneNumbers = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } } /// /// Gets or sets the name of the plant to which the is associated. /// /// /// Maximum length for is 24 characters. /// /// The value being assigned is a null string. public string PlantCode { get => m_plantCode; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_plantCode = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(24) : value; } } /// /// Gets or sets the alpha-numeric system identifier for the . /// /// /// Maximum length for is 24 characters. /// /// The value being assigned is a null string. public string SystemName { get => m_system; set { if (value is null) throw new ArgumentNullException(nameof(value)); m_system = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(24) : value; } } /// /// Gets or sets the data and time when an alarm notification is sent based on the settings for the . /// /// /// Maximum length for is 40 characters. /// /// The value being assigned is a null string. public string EmailTime { get => m_emailTime; set => m_emailTime = m_legacyMode == MetadataFileLegacyMode.Enabled ? value.TruncateRight(40) : value; } /// /// Gets or sets the rate at which the source device scans and sends data for the . /// /// /// is used by data acquisition components for polling data from the actual device. /// public float ScanRate { get; set; } /// /// Gets or sets the unit (i.e. generator) to which the is associated. /// public int UnitNumber { get; set; } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordSecurityFlags SecurityFlags { get => m_securityFlags; set => m_securityFlags = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordGeneralFlags GeneralFlags { get => m_generalFlags; set => m_generalFlags = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordAlarmFlags AlarmFlags { get => m_alarmFlags; set => m_alarmFlags = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the minimum allowable time (in seconds) between archived data for the . /// /// /// is useful for limiting archived data for noisy s. /// public int CompressionMinTime { get; set; } /// /// Gets or sets the maximum time (in seconds) after which data is to be archived for the . /// /// /// ensures that archived data exist every "n" seconds for the , /// which would otherwise be omitted due to compression. /// public int CompressionMaxTime { get; set; } /// /// Gets or sets the numeric identifier of the data source for the . /// /// /// is used for the determination of "global time" when that client option is in effect. /// When "global time" is in effect, the historian returns the current data time for a /// based on the latest time received for all s with the same . /// /// The value being assigned is not positive or zero. public int SourceID { get => m_sourceID; set { if (value < 0) throw new ArgumentException("Value must be positive or zero"); m_sourceID = value; } } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordAnalogFields AnalogFields { get => m_analogFields; set => m_analogFields = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordDigitalFields DigitalFields { get => m_digitalFields; set => m_digitalFields = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordComposedFields ComposedFields { get => m_composedFields; set => m_composedFields = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the associated with the . /// /// The value being assigned is null. public MetadataRecordConstantFields ConstantFields { get => m_constantFields; set => m_constantFields = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets the historian identifier of . /// public int HistorianID { get; } /// /// Gets the object for . /// public MetadataRecordSummary Summary => new MetadataRecordSummary(this); /// /// Gets the length of the . /// public int BinaryLength => FixedLength; #endregion #region [ Methods ] /// /// Initializes from the specified . /// /// Binary image to be used for initializing . /// 0-based starting index of initialization data in the . /// Valid number of bytes in from . /// Number of bytes used from the for initializing . public int ParseBinaryImage(byte[] buffer, int startIndex, int length) { if (length >= FixedLength) { // Binary image has sufficient data. Remarks = Encoding.ASCII.GetString(buffer, startIndex, 512).Trim(); HardwareInfo = Encoding.ASCII.GetString(buffer, startIndex + 512, 512).Trim(); AlarmEmails = Encoding.ASCII.GetString(buffer, startIndex + 1024, 512).Trim(); Description = Encoding.ASCII.GetString(buffer, startIndex + 1536, 80).Trim(); CurrentData = Encoding.ASCII.GetString(buffer, startIndex + 1616, 80).Trim(); Name = Encoding.ASCII.GetString(buffer, startIndex + 1696, 40).Trim(); Synonym1 = Encoding.ASCII.GetString(buffer, startIndex + 1736, 40).Trim(); Synonym2 = Encoding.ASCII.GetString(buffer, startIndex + 1776, 40).Trim(); Synonym3 = Encoding.ASCII.GetString(buffer, startIndex + 1816, 40).Trim(); AlarmPagers = Encoding.ASCII.GetString(buffer, startIndex + 1856, 40).Trim(); AlarmPhones = Encoding.ASCII.GetString(buffer, startIndex + 1896, 40).Trim(); PlantCode = Encoding.ASCII.GetString(buffer, startIndex + 1936, 24).Trim(); SystemName = Encoding.ASCII.GetString(buffer, startIndex + 1960, 24).Trim(); EmailTime = Encoding.ASCII.GetString(buffer, startIndex + 1984, 40).Trim(); ScanRate = LittleEndian.ToSingle(buffer, startIndex + 2104); UnitNumber = LittleEndian.ToInt32(buffer, startIndex + 2108); SecurityFlags.Value = LittleEndian.ToInt32(buffer, startIndex + 2112); GeneralFlags.Value = LittleEndian.ToInt32(buffer, startIndex + 2116); AlarmFlags.Value = LittleEndian.ToInt32(buffer, startIndex + 2120); CompressionMinTime = LittleEndian.ToInt32(buffer, startIndex + 2124); CompressionMaxTime = LittleEndian.ToInt32(buffer, startIndex + 2128); SourceID = LittleEndian.ToInt32(buffer, startIndex + 2132); switch (GeneralFlags.DataType) { case DataType.Analog: m_analogFields.ParseBinaryImage(buffer, startIndex + 2152, length - 2152); break; case DataType.Digital: m_digitalFields.ParseBinaryImage(buffer, startIndex + 2152, length - 2152); break; case DataType.Composed: m_composedFields.ParseBinaryImage(buffer, startIndex + 2152, length - 2152); break; case DataType.Constant: m_constantFields.ParseBinaryImage(buffer, startIndex + 2152, length - 2152); break; } return FixedLength; } // Binary image does not have sufficient data. return 0; } /// /// Generates binary image of the and copies it into the given buffer, for bytes. /// /// Buffer used to hold generated binary image of the source object. /// 0-based starting index in the to start writing. /// The number of bytes written to the . /// is null. /// /// or is less than 0 -or- /// and will exceed length. /// public virtual int GenerateBinaryImage(byte[] buffer, int startIndex) { int length = BinaryLength; buffer.ValidateParameters(startIndex, length); // Construct the binary IP buffer for this event Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_remarks.PadRight(512).TruncateRight(512)), 0, buffer, startIndex, 512); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_hardwareInfo.PadRight(512).TruncateRight(512)), 0, buffer, startIndex + 512, 512); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_emailAddresses.PadRight(512).TruncateRight(512)), 0, buffer, startIndex + 1024, 512); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_description.PadRight(80).TruncateRight(80)), 0, buffer, startIndex + 1536, 80); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_currentData.PadRight(80).TruncateRight(80)), 0, buffer, startIndex + 1616, 80); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_name.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1696, 40); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_synonym1.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1736, 40); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_synonym2.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1776, 40); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_synonym3.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1816, 40); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_pagerNumbers.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1856, 40); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_phoneNumbers.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1896, 40); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_plantCode.PadRight(24).TruncateRight(24)), 0, buffer, startIndex + 1936, 24); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_system.PadRight(24).TruncateRight(24)), 0, buffer, startIndex + 1960, 24); Buffer.BlockCopy(Encoding.ASCII.GetBytes(m_emailTime.PadRight(40).TruncateRight(40)), 0, buffer, startIndex + 1984, 40); Buffer.BlockCopy(LittleEndian.GetBytes(ScanRate), 0, buffer, startIndex + 2104, 4); Buffer.BlockCopy(LittleEndian.GetBytes(UnitNumber), 0, buffer, startIndex + 2108, 4); Buffer.BlockCopy(LittleEndian.GetBytes(m_securityFlags.Value), 0, buffer, startIndex + 2112, 4); Buffer.BlockCopy(LittleEndian.GetBytes(m_generalFlags.Value), 0, buffer, startIndex + 2116, 4); Buffer.BlockCopy(LittleEndian.GetBytes(m_alarmFlags.Value), 0, buffer, startIndex + 2120, 4); Buffer.BlockCopy(LittleEndian.GetBytes(CompressionMinTime), 0, buffer, startIndex + 2124, 4); Buffer.BlockCopy(LittleEndian.GetBytes(CompressionMaxTime), 0, buffer, startIndex + 2128, 4); Buffer.BlockCopy(LittleEndian.GetBytes(m_sourceID), 0, buffer, startIndex + 2132, 4); switch (m_generalFlags.DataType) { case DataType.Analog: m_analogFields.GenerateBinaryImage(buffer, startIndex + 2152); break; case DataType.Digital: m_digitalFields.GenerateBinaryImage(buffer, startIndex + 2152); break; case DataType.Composed: m_composedFields.GenerateBinaryImage(buffer, startIndex + 2152); break; case DataType.Constant: m_constantFields.GenerateBinaryImage(buffer, startIndex + 2152); break; } return length; } /// /// Compares the current object to . /// /// Object against which the current object is to be compared. /// /// Negative value if the current object is less than , /// Zero if the current object is equal to , /// Positive value if the current object is greater than . /// public virtual int CompareTo(object obj) => !(obj is MetadataRecord other) ? 1 : HistorianID.CompareTo(other.HistorianID); /// /// Determines whether the current object is equal to . /// /// Object against which the current object is to be compared for equality. /// true if the current object is equal to ; otherwise false. public override bool Equals(object obj) => CompareTo(obj) == 0; /// /// Returns the text representation of object. /// /// A value. public override string ToString() => $"ID={HistorianID}; Name={m_name}"; /// /// Returns the hash code for the current object. /// /// A 32-bit signed integer value. public override int GetHashCode() => HistorianID.GetHashCode(); internal void WriteImage(BinaryWriter writer) { writer.Write(HistorianID); writer.Write(m_remarks); writer.Write(m_hardwareInfo); writer.Write(m_emailAddresses); writer.Write(m_description); writer.Write(m_currentData); writer.Write(m_name); writer.Write(m_synonym1); writer.Write(m_synonym2); writer.Write(m_synonym3); writer.Write(m_pagerNumbers); writer.Write(m_phoneNumbers); writer.Write(m_plantCode); writer.Write(m_system); writer.Write(m_emailTime); writer.Write(ScanRate); writer.Write(UnitNumber); writer.Write(m_securityFlags.Value); writer.Write(m_generalFlags.Value); writer.Write(m_alarmFlags.Value); writer.Write(CompressionMinTime); writer.Write(CompressionMaxTime); writer.Write(m_sourceID); switch (m_generalFlags.DataType) { case DataType.Analog: m_analogFields.WriteImage(writer); break; case DataType.Digital: m_digitalFields.WriteImage(writer); break; case DataType.Composed: m_composedFields.WriteImage(writer); break; case DataType.Constant: m_constantFields.WriteImage(writer); break; default: throw new ArgumentOutOfRangeException(); } } #endregion } }