/* Test.cs Copyright © 2008 Diversified Technical Systems, Inc. All Rights Reserved */ using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using DTS.Common.Utilities; using DTS.Common.Utilities.DotNetProgrammingConstructs; using DTS.Common.Utilities.Logging; using DTS.Common.Utilities.Xml; namespace DTS.Serialization { /// /// Representation of a serializable test information. /// [XmlSerializationTag("Test")] public partial class Test : Exceptional, IXmlSerializable { private readonly Property _software = new Property(typeof(Test).Namespace + ".Test.Software", "DataPRO", true); [XmlSerializationTag("Software")] public string Software { get => _software.Value; set => _software.Value = value; } private readonly Property _softwareVersion = new Property(typeof(Test).Namespace + ".Test.SoftwareVersion", "", false); [XmlSerializationTag("SoftwareVersion")] public string SoftwareVersion { get => _softwareVersion.Value; set => _softwareVersion.Value = value; } /// /// clears all extended fault flags /// http://manuscript.dts.local/f/cases/39223/ /// public void ClearExtendedFaultFlags() { ExtendedFaultFlags1 = 0; ExtendedFaultFlags2 = 0; ExtendedFaultFlags3 = 0; ExtendedFaultFlags4 = 0; } public Test() { TryGetChannelOrder(); // // Note that the parameterless constructor for this object will leave // the Id and Description paramters } // public Test(string dtsfile) { } /// /// Initialize an instance of the Test class. /// /// /// /// The ID of this test. /// /// /// /// The description of this test. /// /// public Test(string id, string description) : this(id, description, 0) { } /// /// Initialize an instance of the Test class. /// /// /// /// The ID of this test. /// /// /// /// The description of this test. /// /// public Test(string id, string description, int eventNumber) { TryGetChannelOrder(); try { Id = id; Description = description; EventNumber = eventNumber; } catch (System.Exception ex) { throw new Exception("encountered problem constructing Test object (Id: " + (!string.IsNullOrEmpty(id) ? id : "<>") + "Description: " + (!string.IsNullOrEmpty(description) ? description : "<>") + ")", ex); } } /// /// Get the date of this object's serialization's creation (if applicable). /// public DateTime InceptionDate { get => _InceptionDate.Value; set => _InceptionDate.Value = value; } private readonly Property _InceptionDate = new Property( typeof(Test).Namespace + ".Test.InceptionDate", DateTime.Now, false ); private readonly Dictionary _channelOrder = new Dictionary(); private static string GetID(Module.Channel channel) { if (!string.IsNullOrEmpty(channel.SensorID)) { return channel.SensorID; } return channel.ChannelDescriptionString; } public class ChannelOrderComparor : IComparer { private readonly IDictionary _dictionary; public ChannelOrderComparor(IDictionary dictionary) { _dictionary = dictionary; } public int Compare(Module.Channel a, Module.Channel b) { var keyA = GetID(a); var keyB = GetID(b); var iA = _dictionary.ContainsKey(keyA) ? _dictionary[keyA] : int.MaxValue; var iB = _dictionary.ContainsKey(keyB) ? _dictionary[keyB] : int.MaxValue; return iA.CompareTo(iB); } } public void TryGetChannelOrder() { try { _channelOrder.Clear(); if (System.IO.File.Exists("ChannelOrder.txt")) { using (var sr = new System.IO.StreamReader("ChannelOrder.txt")) { string line; var i = 0; while ((line = sr.ReadLine()) != null) { _channelOrder.Add(line, i++); } } } } catch (System.Exception ex) { APILogger.Log("Exception getting channel order", ex); } } /// /// Get a named-DAS/numbered-channel accessor to this Event's channels. /// public List Channels { get { try { if (_channelOrder.Count < 1) { TryGetChannelOrder(); } var allChannels = new List(); foreach (var testModule in Modules) { allChannels.AddRange(testModule.Channels); allChannels.AddRange(testModule.CalculatedChannels); } allChannels.Sort(new Comparison(CompareChannels)); if (_channelOrder.Count > 0) { allChannels.Sort(new ChannelOrderComparor(_channelOrder)); } return allChannels; } catch (System.Exception ex) { throw new Exception("encountered problem getting all test channels", ex); } } } private int CompareChannels(Module.Channel left, Module.Channel right) { if (left == right) { return 0; } if (null == left) { return -1; } if (null == right) { return 1; } var ret = left.AbsoluteDisplayOrder.CompareTo(right.AbsoluteDisplayOrder); if (0 == ret) { ret = left.ParentModule.Number.CompareTo(right.ParentModule.Number); } if (0 == ret) { return left.Number.CompareTo(right.Number); } return ret; } /// /// The string ID of this test. /// [XmlSerializationTag("Id")] public string Id { get => _Id.Value; set => _Id.Value = value; } private readonly Property _Id = new Property(typeof(Test).Namespace + ".Test.Id", "", false); /// /// The string description of this test. /// [XmlSerializationTag("Description")] public string Description { get => _Description.Value; set => _Description.Value = value; } private readonly Property _Description = new Property(typeof(Test).Namespace + ".Test.Description", "", false); //FB 18312 Added event number /// /// The event number of this test. /// [XmlSerializationTag("EventNumber")] public int EventNumber { get => _eventNumber.Value; set => _eventNumber.Value = value; } private readonly Property _eventNumber = new Property(typeof(Test).Namespace + ".Test.EventNumber", 0, false); /// /// The globally unique identification string for this test. /// [XmlSerializationTag("Guid")] public Guid Guid { get => _Guid.Value; set => _Guid.Value = value; } private readonly Property _Guid = new Property(typeof(Test).Namespace + ".Test.Guid", new Guid("00000000-0000-0000-0000-000000000000"), false); /// /// The globally unique identification string for this test. /// [XmlSerializationTag("FaultFlags")] public ushort FaultFlags { get => _FaultFlags.Value; set => _FaultFlags.Value = value; } private readonly Property _FaultFlags = new Property(typeof(Test).Namespace + ".Test.FaultFlags", 0, false); [XmlSerializationTag("ExtendedFaultFlags1")] public uint ExtendedFaultFlags1 { get => _ExtendedFaultFlags1.Value; set => _ExtendedFaultFlags1.Value = value; } private readonly Property _ExtendedFaultFlags1 = new Property(typeof(Test).Namespace + ".Test.ExtendedFaultFlags1", 0, true); [XmlSerializationTag("ExtendedFaultFlags2")] public uint ExtendedFaultFlags2 { get => _ExtendedFaultFlags2.Value; set => _ExtendedFaultFlags2.Value = value; } private readonly Property _ExtendedFaultFlags2 = new Property(typeof(Test).Namespace + ".Test.ExtendedFaultFlags2", 0, true); [XmlSerializationTag("ExtendedFaultFlags3")] public uint ExtendedFaultFlags3 { get => _ExtendedFaultFlags3.Value; set => _ExtendedFaultFlags3.Value = value; } private readonly Property _ExtendedFaultFlags3 = new Property(typeof(Test).Namespace + ".Test.ExtendedFaultFlags3", 0, true); [XmlSerializationTag("ExtendedFaultFlags4")] public uint ExtendedFaultFlags4 { get => _ExtendedFaultFlags4.Value; set => _ExtendedFaultFlags4.Value = value; } private readonly Property _ExtendedFaultFlags4 = new Property(typeof(Test).Namespace + ".Test.ExtendedFaultFlags4", 0, true); /// /// Get/set inline serialized data switch. /// [XmlSerializationTag("InlineSerializedData")] public bool InlineSerializedData { get => _InlineSerializedData.Value; set { try { _InlineSerializedData.Value = value; foreach (var module in Modules) module.InlineSerializedData = value; } catch (System.Exception ex) { throw new Exception("encountered problem setting test InlineSerializedData state", ex); } } } private readonly Property _InlineSerializedData = new Property(typeof(Test).Namespace + ".Test.InlineSerializedData", false, true); /// /// The list of modules in this test. /// [XmlSerializationTag("Modules")] public List Modules { get => _Modules.Value; set => _Modules.Value = value; } private readonly Property> _Modules = new Property>(typeof(Test).Namespace + ".Test.Modules", new List(), true); /// /// The list of das timestamps in this test. /// [XmlSerializationTag("DasTimestamps")] public List DasTimestamps { get => _DasTimestamps.Value; set => _DasTimestamps.Value = value; } private readonly Property> _DasTimestamps = new Property>(typeof(Test).Namespace + ".Test.DasTimestamps", new List(), true); /// /// Write XML serialization for this object to the specified writer. /// /// /// /// The to which this object's XML serialization /// will be written. /// /// public void WriteXml(XmlWriter writer) { try { var attributeExtractor = new AttributeExtractor(); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Id").Value, Id); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Description").Value, Description); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "EventNumber").Value, EventNumber.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "InlineSerializedData").Value, InlineSerializedData.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Guid").Value, Guid.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "FaultFlags").Value, FaultFlags.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "ExtendedFaultFlags1").Value, ExtendedFaultFlags1.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "ExtendedFaultFlags2").Value, ExtendedFaultFlags2.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "ExtendedFaultFlags3").Value, ExtendedFaultFlags3.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "ExtendedFaultFlags4").Value, ExtendedFaultFlags4.ToString()); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Software").Value, Software?.ToString() ?? "UNKNOWN"); writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "SoftwareVersion").Value, SoftwareVersion?.ToString() ?? "UNKNOWN"); writer.WriteStartElement(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Modules").Value); foreach (var module in Modules) { module.WriteXml(writer); } writer.WriteEndElement(); writer.WriteStartElement(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "DasTimestamps").Value); foreach (var stamp in DasTimestamps) { stamp.WriteXml(writer); } writer.WriteEndElement(); } catch (System.Exception ex) { throw new Exception("encountered problem converting DTS.Serialization.Test object to XML", ex); } } /// /// retrieves an attribute if present as an UINT, if not present returns a default value /// private uint GetUintSafe(string tag, uint defaultValue, XmlReader reader, AttributeExtractor attributeExtractor) { try { var attr = attributeExtractor.ExtractAttachedAttributeFromProperty(this, tag).Value; if (string.IsNullOrEmpty(attr)) { return defaultValue; } attr = reader.GetAttribute(attr); if (string.IsNullOrEmpty(attr)) { return defaultValue; } return Convert.ToUInt32(attr); } catch (Exception ex) { APILogger.Log($"Failed to extract attribute: {tag}", ex); } return defaultValue; } /// /// Read XML serialization for this object from the specified reader. /// /// /// /// The from which this object's XML serialization /// will be read. /// /// public void ReadXml(XmlReader reader) { try { var attributeExtractor = new AttributeExtractor(); if (reader.IsStartElement("Test")) { Id = reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Id").Value); Description = reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Description").Value); EventNumber = Convert.ToInt32(reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "EventNumber").Value)); InlineSerializedData = bool.Parse(reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "InlineSerializedData").Value)); try { Guid = new Guid(reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Guid").Value)); } catch (System.Exception) { Guid = new Guid("00000000-0000-0000-0000-000000000000"); } try { FaultFlags = Convert.ToUInt16(reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "FaultFlags").Value)); } catch (System.Exception) { FaultFlags = 0; } ExtendedFaultFlags1 = GetUintSafe("ExtendedFaultFlags1", 0, reader, attributeExtractor); ExtendedFaultFlags2 = GetUintSafe("ExtendedFaultFlags2", 0, reader, attributeExtractor); ExtendedFaultFlags3 = GetUintSafe("ExtendedFaultFlags3", 0, reader, attributeExtractor); ExtendedFaultFlags4 = GetUintSafe("ExtendedFaultFlags4", 0, reader, attributeExtractor); try { Software = reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "Software").Value); } catch (System.Exception) { Software = "unknown"; } try { SoftwareVersion = reader.GetAttribute(attributeExtractor.ExtractAttachedAttributeFromProperty(this, "SoftwareVersion").Value); } catch (System.Exception) { SoftwareVersion = "unknown"; } Modules.Clear(); if (reader.ReadToDescendant("Modules")) { if (reader.ReadToDescendant("Module")) { do { var deserializedModule = new Module(this); deserializedModule.InlineSerializedData = InlineSerializedData; //Give the deserializer a separate reader so that it can't go amuck and read past the current module. deserializedModule.ReadXml(reader.ReadSubtree()); Modules.Add(deserializedModule); } while (reader.ReadToNextSibling("Module")); } } DasTimestamps.Clear(); if (reader.ReadToDescendant("DasTimestamps")) { if (reader.ReadToDescendant("DasTimestamp")) { do { var deserializedDasTimestamp = new DasTimestamp(this); //deserializedDasTimestamp.InlineSerializedData = InlineSerializedData; //Give the deserializer a separate reader so that it can't go amuck and read past the current timestamp. deserializedDasTimestamp.ReadXml(reader.ReadSubtree()); DasTimestamps.Add(deserializedDasTimestamp); } while (reader.ReadToNextSibling("DasTimestamp")); } } } } catch (System.Exception ex) { throw new Exception("encountered problem converting XML to DTS.Serialization.Test object", ex); } } /// /// Should normally return a schema representing the form of the XML /// generated/consumed by WriteXml/ReadXml, but it never called during /// the serialization process so ours just returns null. /// /// /// /// Null reference, always. /// /// public XmlSchema GetSchema() { // This method is never invoked during XML object serialization. return null; } /// /// Test the specified object for equality with this object. /// /// /// /// The to be tested for equality. /// /// /// /// true if the specified object has memeberwise equality with /// this object; false otherwise. /// /// public override bool Equals(object obj) { try { if (!(obj is Test that)) { return false; } return Id.Equals(that.Id) && Description.Equals(that.Description) && ModulesEquals(that.Modules); } catch (System.Exception ex) { throw new Exception("encountered problem equality testing object " + (null != obj ? "\"" + obj.ToString() + "\"" : "<>"), ex); } } /// /// Test the specified object's module list for equality with this object's /// module list. /// /// /// /// The of object to be /// compared for equality with this test's equivalent. /// /// /// /// true if the two lists contain equivalent-valued members; /// false otherwise. /// /// private bool ModulesEquals(List thoseModules) { try { if (Modules.Count != thoseModules.Count) return false; for (var i = 0; i < thoseModules.Count; i++) if (!Modules[i].Equals(thoseModules[i])) return false; return true; } catch (System.Exception ex) { throw new Exception("encountered problem equality-testing module list", ex); } } /// /// Return the hash code for this object. /// /// /// /// The hash code for this object. /// /// public override int GetHashCode() { return base.GetHashCode(); } public void SaveTest(string directory, string testId, int defaultEncoding, bool includeGroupNameInISOExport) { try { //Back up .dts file if necessary var dtsFilePath = Path.Combine(directory, testId + ".dts"); var dtsBackupFilePath = dtsFilePath + ".bak"; //Back up dts file only if a backup file does not exist. This should guarantee that the //original file is the only one that is preserved. var backupExisted = System.IO.File.Exists(dtsBackupFilePath); if (!backupExisted && System.IO.File.Exists(dtsFilePath)) { System.IO.File.Move(dtsFilePath, dtsBackupFilePath); } //Spit out portion var f = new SliceRaw.File(); //Write the SLICEWare-compatible info so that SLICEWare can display the data if desired. f.DefaultEncoding = defaultEncoding; f.Exporter.Write(directory, testId, this, false, includeGroupNameInISOExport, 0D, 0); //Append the DataPRO info to the end of the existing .dts file which already contains SLICEWare-compatible info using (var writer = new StringWriter()) { Encoding encoder; try { //force UTF-16 for the dts file, it contains "UTF-16" in the xml by default and isn't consumed by anything that requires //codepage exports (CSV/excel) encoder = Encoding.Unicode; //UTF-16 } catch (Exception ex) { APILogger.Log("Problem getting encoder", ex); encoder = Encoding.Default; } using (var fileWriter = new StreamWriter(dtsFilePath, true, encoder)) { fileWriter.Write(fileWriter.NewLine + writer); //FB9374: successful save without error. if we created a backup for this save, remove it if (!backupExisted) { System.IO.File.Delete(dtsBackupFilePath); } } } } catch (Exception ex) { APILogger.Log(ex); } } } }