using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using DTS.Common.DAS.Concepts; using DTS.Common.Enums; using DTS.Common.Enums.DASFactory; using DTS.Common.Interface.DASFactory; using DTS.Common.Interface.DASFactory.Config; using DTS.Common.Utils; namespace DTS.DASLib.Service { /// /// This represents one Module on a DAS unit. A DAS unit can have numerous Modules and a Module can have /// numerous channels. The channels that are connected to this module are in the /// array property. See . Another property, /// is the index of this object in any array of Modules in the corresponding /// however it may also be addressed by it's Device ID. The first module in a DAS will have a ModuleArrayIndex /// of 0 but a DeviceID of 1. See .MapDASChannelNumber2ModuleDeviceID and /// .MapModuleDeviceIDAndChannelNum2DASChannel /// [Serializable] public class DASModule : IDASModule, IXmlSerializable { /// /// An array of objects representing the channels attatched to this /// module, indexable by ModuleChannelNumber of the Channel. /// public IDASChannel[] Channels { get; set; } /// /// EID's for the module corresponding to the sensors on each of the channels. /// [XmlIgnore] public IEID[] IDs { get; set; } /// /// Index of this Module in any array of Modules. The first module in a DAS unit will have a /// ModuleArrayIndex of 0. /// public int ModuleArrayIndex { get; set; } /// /// For use only with Circular-Buffer mode. /// See . /// The number of requested seconds for this Module to sample data before a 'Trigger' event; the number of /// seconds to sample BEFORE time-zero. /// public double RequestedPreTriggerSeconds { get; set; } /// /// For use with all recording modes. /// See . /// The number of requested seconds to sample data from sensors after time-zero, that is after /// a trigger or start event. /// public double RequestedPostTriggerSeconds { get; set; } /// /// For use only with Circular-Buffer mode. /// See . /// The number of seconds for this Module to sample data before a 'Trigger' event; the number of /// seconds to sampel BEFORE time-zero. /// public double PreTriggerSeconds { get; set; } /// /// For use with all recording modes. /// See . /// The number of seconds to sample data from sensors after time-zero, that is after /// a trigger or start event. /// public double PostTriggerSeconds { get; set; } /// /// The number of events to collect before disarming. /// public int NumberOfEvents { get; set; } /// /// The number of seconds of inactivity before going back to sleep. /// public int WakeUpMotionTimeout { get; set; } /// /// The version of the firmware on a module. /// public string FirmwareVersion { get; set; } /// /// A string that describes the module, for example, to know if it was created for the /// purposes of adding it to the .dts file during download of Slice6 Distributor attributes. /// public string Description { get; set; } /// /// The maximum storage space for a module. /// public UInt64? MaxEventStorageSpaceInBytes { get; set; } /// /// This is set after a recording session and is the total number of samples this Module /// captured during the last data acquisition run. /// public UInt64 NumberOfSamples { get; set; } /// /// An array of sample numbers where a trigger was activated. /// [XmlIgnore] public UInt64[] TriggerSampleNumbers { get; set; } public int GetLevelTriggerT0AdjustmentSamplesAutoApplied() { try { //temporary hack/workaround to correct leveltriggert0adjustmentsamples if (OwningDAS is IDASReconfigure) { //we do qualifications +1 at request of Loc/Tim, see them for details var channelsWithAdjustments = Channels.Where(c => c.LevelTriggerSeen); return channelsWithAdjustments.Count() > 0 ? channelsWithAdjustments.First().QualificationSamples : 0; } else { // // Figure out what the number of samples that would have been auto-applied to // this module is. // var channelsWithAdjustments = Channels.Where(c => null != c.LevelTriggerT0AdjustmentSamples); var channelsWithMaxAdjustment = channelsWithAdjustments.Max(c => c.LevelTriggerT0AdjustmentSamples); return channelsWithAdjustments.Count() > 0 ? (int)(channelsWithAdjustments.First().LevelTriggerT0AdjustmentSamples) : 0; } } catch (System.Exception ex) { throw new ApplicationException( "encountered problem getting number of level trigger T0 adjustment samples already applied to module " + (null != OwningDAS && null != OwningDAS.SerialNumber ? "\"" + OwningDAS.SerialNumber + "\"" : "") + ", module " + ModuleArrayIndex.ToString(), ex); } } /// /// The sample number where recording started. /// public ulong StartRecordSampleNumber { get; set; } /// /// /// public uint StartRecordTimestampSec { get; set; } /// /// /// public uint TriggerTimestampSec { get; set; } /// /// /// public uint StartRecordTimestampNanoSec { get; set; } /// /// /// public uint TriggerTimestampNanoSec { get; set; } /// /// /// public bool PTPMasterSync { get; set; } /// /// /// public double TiltSensorAxisXDegreesPre { get; set; } /// /// /// public double TiltSensorAxisYDegreesPre { get; set; } /// /// /// public double TiltSensorAxisZDegreesPre { get; set; } /// /// /// public double TiltSensorAxisXDegreesPost { get; set; } /// /// /// public double TiltSensorAxisYDegreesPost { get; set; } /// /// /// public double TiltSensorAxisZDegreesPost { get; set; } public float TemperatureLocation1Pre { get; set; } = float.NaN; public float TemperatureLocation2Pre { get; set; } = float.NaN; public float TemperatureLocation3Pre { get; set; } = float.NaN; public float TemperatureLocation4Pre { get; set; } = float.NaN; public float TemperatureLocation1Post { get; set; } = float.NaN; public float TemperatureLocation2Post { get; set; } = float.NaN; public float TemperatureLocation3Post { get; set; } = float.NaN; public float TemperatureLocation4Post { get; set; } = float.NaN; /// /// The sample rate in Hz at which the Module will sample sensor data. /// public uint SampleRateHz { get; set; } //FB 25558 /// /// The actual sample rate in Hz currently used in realtime for TSR AIR /// public uint ActualSampleRateHz { get; set; } /// /// The sample rate in Hz at which any Embedded Modules will sample sensor data. /// public uint[] EmbeddedSampleRateHz { get; set; } /// /// xml string tag for AAFilterRateHz /// public const string AAFilterRateHzTag = "AAFilterRateHz"; /// /// Hardware anti-alias filter rate. /// public float AAFilterRateHz { get; set; } /// /// What type of recording mode is this Module in? /// See . /// public DFConstantsAndEnums.RecordingMode RecordingMode { get; set; } public DateTime ScheduledStartTime { get; set; } public int RecordingInterval { get; set; } /// /// FB15388: The UDP streaming profile set (for supporting DAS). /// [XmlIgnore] public UDPStreamProfile StreamProfile { get; set; } #region Slice 6 Tilt Feature /// /// Defines which 2 axis will be used for the bubble level feature for Slice 6. /// public DFConstantsAndEnums.TiltAxes TiltAxes { get; set; } public double TargetAxisOne { get; set; } public double TargetAxisTwo { get; set; } public float TargetAngleAxisX { get; set; } public float TargetAngleAxisY { get; set; } public float TargetAngleAxisZ { get; set; } public double MountOffsetAxisOne { get; set; } public double MountOffsetAxisTwo { get; set; } public string SystemLocation { get; set; } public string SystemID { get; set; } public int AxisIgnored { get; set; } public byte TiltID { get; set; } public string TiltSerialNumber { get; set; } public double LevelTolerance { get; set; } public bool UseForTiltCalculation { get; set; } public double InputVoltage { get; set; } public double BatteryVoltage { get; set; } #endregion [XmlIgnore] public IDASCommunication OwningDAS { get; set; } /// /// Count how many channels are configured in this Module. /// See . /// /// Number of configured channels public int NumberOfConfiguredChannels() { if (Channels == null || Channels.Length == 0) return 0; var configuredChannels = from ch in Channels.AsParallel() where ch.IsConfigured() select ch; if (configuredChannels.Count() > 0) { var channels = configuredChannels.ToArray(); } return configuredChannels.Count(); } /// /// Count how many TOM channels are configured in this module /// /// Number of configured TOM channels public int NumberOfConfiguredTOMChannels() { if (Channels == null || Channels.Length == 0) { return 0; } var configuredTOMChannels = from ch in Channels.AsParallel() where (ch.IsConfigured() && ch is OutputSquibChannel) select ch; return configuredTOMChannels.Count(); } public bool IsDummyArmed() { int numConfiguredChannels = NumberOfConfiguredChannels(); return numConfiguredChannels < 1; } /// /// Count how many channels this module has (regardless if they are configured /// or not). /// /// Total number of channels public int NumberOfChannels() { if (Channels == null || Channels.Length == 0) return 0; return Channels.Length; } /// /// Retrieve the serial number from DASInfo /// /// The serial number of this module public string SerialNumber() { if (OwningDAS == null) { throw new NullReferenceException("DASModule: Trying to get serialnumber but OwningDAS is null"); } if (OwningDAS.DASInfo == null) { throw new NullReferenceException("DASModule: Trying to get serialnumber but OwningDAS.DASInfo is null"); } if (OwningDAS.DASInfo.Modules == null) { throw new NullReferenceException("DASModule: Trying to get serialnumber but OwningDAS.DASInfo.Modules is null"); } for (int i = 0; i < OwningDAS.DASInfo.Modules.Length; i++) { if (OwningDAS.DASInfo.Modules[i]?.ModuleArrayIndex == ModuleArrayIndex) { var sn = OwningDAS.DASInfo.Modules[i].SerialNumber; if (string.IsNullOrEmpty(sn)) { throw new NullReferenceException("DASModule: Trying to get serialnumber but it's null or empty"); } else { return sn; } } } throw new NullReferenceException("DASModule: Trying to get serialnumber but ModuleArrayIndex is invalid"); } /// /// Retrieve the module type from DASInfo /// /// The type of this module public DFConstantsAndEnums.ModuleType ModuleType() { if (OwningDAS == null) { throw new NullReferenceException("DASModule: Trying to get module type but OwningDAS is null"); } if (OwningDAS.DASInfo == null) { throw new NullReferenceException("DASModule: Trying to get module type but OwningDAS.DASInfo is null"); } if (OwningDAS.DASInfo.Modules == null) { throw new NullReferenceException("DASModule: Trying to get module type but OwningDAS.DASInfo.Modules is null"); } foreach (InfoResult.Module module in OwningDAS.DASInfo.Modules) { if (module.ModuleArrayIndex != ModuleArrayIndex) continue; var dasType = module.TypeOfModule; return dasType; } throw new NullReferenceException("DASModule: Trying to get module type but ModuleArrayIndex is invalid"); } /// /// Retrieve the module type from DASInfo and return whether or not it's a clock type /// /// Whether the type of this module is a clock public bool IsClock() { DFConstantsAndEnums.ModuleType moduleType = ModuleType(); return moduleType == DFConstantsAndEnums.ModuleType.EmbeddedClockNanosAndPad || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedClockSecondsAndMarker; } /// /// Retrieve the module type from DASInfo and return whether or not it's a uart type /// /// Whether the type of this module is a uart public bool IsUart() { DFConstantsAndEnums.ModuleType moduleType = ModuleType(); return moduleType == DFConstantsAndEnums.ModuleType.UART; } /// /// Retrieve the module type from DASInfo and return whether or not it's a stream output type /// /// Whether the type of this module is a stream output public bool IsStreamOut() { DFConstantsAndEnums.ModuleType moduleType = ModuleType(); return moduleType == DFConstantsAndEnums.ModuleType.StreamOut; } /// /// Retrieve the module type from DASInfo and return whether or not it's a stream input type /// /// Whether the type of this module is a stream output public bool IsStreamIn() { DFConstantsAndEnums.ModuleType moduleType = ModuleType(); return moduleType == DFConstantsAndEnums.ModuleType.StreamIn; } /// /// Retrieve the module type from DASInfo and return whether or not it's an embedded type /// /// Whether the type of this module is embedded public bool IsEmbedded() { DFConstantsAndEnums.ModuleType moduleType = ModuleType(); return moduleType == DFConstantsAndEnums.ModuleType.EmbeddedLinearAccelLowG || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedLinearAccelHighG || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedAngularRate || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedAngularAccel || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedAtmospheric || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedMagnetInput || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedMagnetometer || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedMicrophone || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedOptical || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedClockNanosAndPad || moduleType == DFConstantsAndEnums.ModuleType.EmbeddedClockSecondsAndMarker; } public bool IsThermocoupler() { DFConstantsAndEnums.ModuleType moduleType = ModuleType(); return moduleType == DFConstantsAndEnums.ModuleType.Thermocoupler; } /// /// Constructor for a DASModule that initializes the object with a /// ModuleArrayIndex and a parent DAS as an IDASCommunication. /// /// /// public DASModule(int moduleArrayIdx, IDASCommunication _OwningDAS) { Channels = null; IDs = null; ModuleArrayIndex = moduleArrayIdx; PreTriggerSeconds = 0; PostTriggerSeconds = 0; NumberOfSamples = 0; TriggerSampleNumbers = null; StartRecordSampleNumber = 0; SampleRateHz = 10000; AAFilterRateHz = 0; RecordingMode = DFConstantsAndEnums.RecordingMode.RecorderMode; OwningDAS = _OwningDAS; } /// /// Default constructor for serialization methods compliance. /// public DASModule() { } public virtual void WriteXmlCRC32(XmlWriter writer) { writer.WriteStartElement("DASModule"); // Channels writer.WriteStartElement("Channels"); if (Channels != null) { foreach (var channel in Channels) { channel.WriteElementStart(writer); channel.WriteXmlCRC32(writer); channel.WriteElementEnd(writer); } } writer.WriteEndElement(); // ModuleArrayIndex XMLHelper.PutInt(writer, "ModuleArrayIndex", ModuleArrayIndex); // PreTriggerSeconds //XMLHelper.PutDouble(writer, "PreTriggerSeconds", PreTriggerSeconds); // PostTriggerSeconds //XMLHelper.PutDouble(writer, "PostTriggerSeconds", PostTriggerSeconds); // NumberOfSamples //XMLHelper.PutUInt64(writer, "NumberOfSamples", NumberOfSamples); // StartRecordSampleNumber //XMLHelper.PutUInt64(writer, "StartRecordSampleNumber", StartRecordSampleNumber); // SampleRateHz //XMLHelper.PutUInt(writer, "SampleRateHz", SampleRateHz); // AAFilterRateHz, we write this one as a double //XMLHelper.PutDouble(writer, AAFilterRateHzTag, (double)AAFilterRateHz); // RecordingMode //XMLHelper.PutString(writer, "RecordingMode", RecordingMode.ToString()); writer.WriteEndElement(); } public virtual void WriteXml(XmlWriter writer) { writer.WriteStartElement("DASModule"); // Channels writer.WriteStartElement("Channels"); if (Channels != null) { foreach (var channel in Channels) { channel.WriteElementStart(writer); channel.WriteXml(writer); channel.WriteElementEnd(writer); } } writer.WriteEndElement(); // ModuleArrayIndex XMLHelper.PutInt(writer, "ModuleArrayIndex", ModuleArrayIndex); // PreTriggerSeconds XMLHelper.PutDouble(writer, "PreTriggerSeconds", PreTriggerSeconds); // PostTriggerSeconds XMLHelper.PutDouble(writer, "PostTriggerSeconds", PostTriggerSeconds); // NumberOfSamples XMLHelper.PutUInt64(writer, "NumberOfSamples", NumberOfSamples); // StartRecordSampleNumber XMLHelper.PutUInt64(writer, "StartRecordSampleNumber", StartRecordSampleNumber); // SampleRateHz XMLHelper.PutUInt(writer, "SampleRateHz", SampleRateHz); // AAFilterRateHz, we write this one as a double XMLHelper.PutDouble(writer, AAFilterRateHzTag, AAFilterRateHz); // RecordingMode XMLHelper.PutString(writer, "RecordingMode", RecordingMode.ToString()); // ScheduledStartTime XMLHelper.PutString(writer, "ScheduledStartTime", ScheduledStartTime.ToString(System.Globalization.CultureInfo.InvariantCulture)); // RecordingInterval XMLHelper.PutString(writer, "RecordingInterval", RecordingInterval.ToString()); // Slice 6 Tilt variables XMLHelper.PutString(writer, "TiltAxes", TiltAxes.ToString()); XMLHelper.PutString(writer, "SystemID", SystemID); XMLHelper.PutString(writer, "SystemLocation", SystemLocation); XMLHelper.PutDouble(writer, "TargetAxisOne", TargetAxisOne); XMLHelper.PutDouble(writer, "TargetAxisTwo", TargetAxisTwo); XMLHelper.PutDouble(writer, "MountOffsetAxisOne", MountOffsetAxisOne); XMLHelper.PutDouble(writer, "MountOffsetAxisTwo", MountOffsetAxisTwo); XMLHelper.PutDouble(writer, "LevelTolerance", LevelTolerance); XMLHelper.PutInt(writer, "AxisIgnored", AxisIgnored); XMLHelper.PutBool(writer, "UseForTiltCalculation", UseForTiltCalculation); XMLHelper.PutDouble(writer, "InputVoltage", InputVoltage); XMLHelper.PutDouble(writer, "BatteryVoltage", BatteryVoltage); // FB 26980 Write NumberOfEvents to XML XMLHelper.PutInt(writer, "NumberOfEvents", NumberOfEvents); XMLHelper.PutInt(writer, "WakeUpMotionTimeout", WakeUpMotionTimeout); writer.WriteEndElement(); } private void ReadChannels(XmlReader reader) { var myChannels = new List(); do { if (reader.NodeType == XmlNodeType.Element && reader.Name == "DASChannel") { var chanType = "DTS.DASLib.Service." + reader.GetAttribute("xsi:type"); var channelClassType = Type.GetType(chanType); var deserializedChannel = Activator.CreateInstance(channelClassType) as DASChannel; deserializedChannel.ReadXml(reader); myChannels.Add(deserializedChannel); } //if we are at break out now, don't want to do a read() if (reader.Name == "Channels" && (reader.NodeType == XmlNodeType.EndElement || reader.IsEmptyElement)) { break; } //if we are at we don't want to do a read, otherwise we do if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "DASChannel")) { if (!reader.Read()) { break; } } } while (!(reader.Name == "Channels" && reader.NodeType == XmlNodeType.EndElement)); Channels = myChannels.ToArray(); } private const string CHANNELS_TAG = "Channels"; private const string MODULEARRAYINDEX_TAG = "ModuleArrayIndex"; private const string PRETRIGGERSECONDS_TAG = "PreTriggerSeconds"; private const string POSTTRIGGERSECONDS_TAG = "PostTriggerSeconds"; private const string NUMBEROFSECONDS_TAG = "NumberOfSamples"; private const string TRIGGERSAMPLENUMBERS_TAG = "TriggerSampleNumbers"; private const string STARTRECORDSAMPLENUMBER_TAG = "StartRecordSampleNumber"; private const string SAMPLERATEHZ_TAG = "SampleRateHz"; private const string AAFILTERRATEHZ_TAG = "AAFilterRateHz"; private const string RECORDINGMODE_TAG = "RecordingMode"; private const string SCHEDULEDSTARTTIME_TAG = "ScheduledStartTime"; private const string RECORDINGINTERVAL_TAG = "RecordingInterval"; private const string TILT_AXES_TAG = "TiltAxes"; private const string SYSTEMID_TAG = "SystemID"; private const string SYSTEMLOCATION_TAG = "SystemLocation"; private const string MOUNTOFFSETAXISONE_TAG = "MountOffsetAxisOne"; private const string MOUNTOFFSETAXISTWO_TAG = "MountOffsetAxisTwo"; private const string TARGETAXISONE_TAG = "TargetAxisOne"; private const string TARGETAXISTWO_TAG = "TargetAxisTwo"; private const string LEVELTOLERANCE_TAG = "LevelTolerance"; private const string AXISIGNORED_TAG = "AxisIgnored"; private const string USEFORTILTCALCULATION_TAG = "UseForTiltCalculation"; private const string INPUTVOLTAGE_TAG = "InputVoltage"; private const string BATTERYVOLTAGE_TAG = "BatteryVoltage"; private const string NUMBEROFEVENTS_TAG = "NumberOfEvents"; private const string WAKEUPTIMEOUT_TAG = "WakeUpTimeout"; protected virtual void HandleElement(XmlReader reader) { switch (reader.Name) { case CHANNELS_TAG: ReadChannels(reader); break; case MODULEARRAYINDEX_TAG: ModuleArrayIndex = XMLHelper.GetInt(reader); break; case PRETRIGGERSECONDS_TAG: PreTriggerSeconds = XMLHelper.GetDouble(reader); break; case POSTTRIGGERSECONDS_TAG: PostTriggerSeconds = XMLHelper.GetDouble(reader); break; case NUMBEROFSECONDS_TAG: NumberOfSamples = XMLHelper.GetUInt64(reader); break; case TRIGGERSAMPLENUMBERS_TAG: // UInt64[] throw new XmlException("DASModule.ReadXml: Unexpected TriggerSampleNumbers tag in data"); case STARTRECORDSAMPLENUMBER_TAG: StartRecordSampleNumber = XMLHelper.GetUInt64(reader); break; case SAMPLERATEHZ_TAG: SampleRateHz = XMLHelper.GetUInt(reader); break; case AAFilterRateHzTag: AAFilterRateHz = XMLHelper.GetFloat(reader); break; case RECORDINGMODE_TAG: var value = XMLHelper.GetString(reader); if (!Enum.TryParse(value, out DFConstantsAndEnums.RecordingMode mode)) { if (value.Equals("ActiveMode")) { RecordingMode = DFConstantsAndEnums.RecordingMode.Aerospace; } } else { RecordingMode = mode; } break; case SCHEDULEDSTARTTIME_TAG: var scheduledStartTime = XMLHelper.GetString(reader); ScheduledStartTime = DateTime.Parse(scheduledStartTime, System.Globalization.CultureInfo.InvariantCulture); break; case RECORDINGINTERVAL_TAG: RecordingInterval = XMLHelper.GetInt(reader); break; case TILT_AXES_TAG: var ta = XMLHelper.GetString(reader); TiltAxes = (DFConstantsAndEnums.TiltAxes)Enum.Parse(typeof(DFConstantsAndEnums.TiltAxes), ta); break; case SYSTEMID_TAG: SystemID = XMLHelper.GetString(reader); break; case SYSTEMLOCATION_TAG: SystemLocation = XMLHelper.GetString(reader); break; case MOUNTOFFSETAXISONE_TAG: MountOffsetAxisOne = XMLHelper.GetDouble(reader); break; case MOUNTOFFSETAXISTWO_TAG: MountOffsetAxisTwo = XMLHelper.GetDouble(reader); break; case TARGETAXISONE_TAG: TargetAxisOne = XMLHelper.GetDouble(reader); break; case TARGETAXISTWO_TAG: TargetAxisTwo = XMLHelper.GetDouble(reader); break; case LEVELTOLERANCE_TAG: LevelTolerance = XMLHelper.GetDouble(reader); break; case AXISIGNORED_TAG: AxisIgnored = XMLHelper.GetInt(reader); break; case USEFORTILTCALCULATION_TAG: UseForTiltCalculation = XMLHelper.GetBool(reader); break; case INPUTVOLTAGE_TAG: InputVoltage = XMLHelper.GetDouble(reader); break; case BATTERYVOLTAGE_TAG: BatteryVoltage = XMLHelper.GetDouble(reader); break; //FB 26980 case NUMBEROFEVENTS_TAG: NumberOfEvents = XMLHelper.GetInt(reader); break; case WAKEUPTIMEOUT_TAG: WakeUpMotionTimeout = XMLHelper.GetInt(reader); break; default: // let child handle it break; } } public virtual void ReadXml(XmlReader reader) { // it must be an Element if (reader.NodeType != XmlNodeType.Element) { throw new XmlException("DASModule.ReadXml: Unknown input: " + reader.NodeType.ToString()); } // remove our start tag if (reader.Name == "DASModule") { reader.Read(); } do { if (reader.NodeType != XmlNodeType.EndElement) { HandleElement(reader); } if (!reader.Read()) { break; } } while (!(reader.Name.Equals("DASModule") && reader.NodeType == XmlNodeType.EndElement)); // we're going to end with an EndElement, so clean up if (reader.NodeType == XmlNodeType.EndElement && reader.Name.Equals("DASModule")) { reader.Read(); } } public XmlSchema GetSchema() { return null; } public virtual ushort GetCRC32() { var sb = new StringBuilder(); using (var writer = XmlWriter.Create(sb)) { WriteXmlCRC32(writer); writer.Flush(); } var lData = new List(Encoding.UTF8.GetBytes(sb.ToString())); if (0 != lData.Count % 2) { lData.Add(0x00); } byte[] data = lData.ToArray(); ushort crc = 0xFFFF; for (int i = 0; i < data.Length; i += 2) { crc = Utils.Math_DoCRC16Step(BitConverter.ToUInt16(data, i), crc); } return crc; } } }