using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using DTS.Common.Base; using DTS.Common.Enums.DBExport; using DTS.Common.Utilities.Logging; using DTS.Common.Enums; using DTS.Common.Enums.Sensors; using DTS.Common.Classes.Sensors; using DTS.Common.Utils; using System.Xml; using DTS.Common.Interface.Sensors; using DTS.SensorDB; using System.Xml.Serialization; using System.IO; using EQX; using DTS.Common.DAS.Concepts; namespace DTS.Common.DataModel { /// /// handles exporting (to TDCSensorDatabase CSV format) /// couple of important notes. /// 1) uses current culture list separator (not necessarily a comma) /// 2) uses local culture decimal/number formats /// 3) doesn't specify an encoding for export file /// public class SensorDatabaseExport : BasePropertyChanged { // ReSharper disable once InconsistentNaming private bool _TDCOnly = false; public bool TDCOnly { get => _TDCOnly; set => SetProperty(ref _TDCOnly, value, "TDCOnly"); } private string _defaultFolder = DataModelSettings.DefaultTDCSensorDatabaseFolder; /// /// the default folder for the export, is initialed using default value in config file /// public string DefaultFolder { get => _defaultFolder; set => SetProperty(ref _defaultFolder, value, "DefaultFolder"); } private string _defaultFile = DataModelSettings.DefaultTDCSensorDatabaseFile; /// /// the filename to export to, by default is controlled by config file /// public string DefaultFile { get => _defaultFile; set => SetProperty(ref _defaultFile, value, "DefaultFile"); } /// /// sets the default file and folder for export /// /// full path to destination file. Can be a relative path, it will be resolved to absolute path. public void SetFilename(string fullPathToFile) { var fi = new FileInfo(fullPathToFile); DefaultFolder = Path.GetFullPath(fi.DirectoryName); DefaultFile = fi.Name; } /// /// controls what to do if there is an error during export /// an errored export will not finish and returns immediately /// /// public delegate void ErrorDelegate(string message); public event ErrorDelegate OnError; /// /// controls what to do when export finishes /// public delegate void DoneDelegate(); public event DoneDelegate OnDone; private SensorData[] _sensors = null; /// /// sensors to export, if null all sensors are exported /// public SensorData[] Sensors { get => _sensors; set => SetProperty(ref _sensors, value, "Sensors"); } /// /// whether the user wants to export first use dates or not /// 13065 Sensor "First Use" Date /// public bool ExportFirstUseDate { get; set; } = true; private List _modelStrings; public void ExportSLICEWare() { try { if (!Directory.Exists(DefaultFolder)) { Directory.CreateDirectory(DefaultFolder); } var di = new DirectoryInfo(DefaultFolder); var calFilename = Path.Combine(DefaultFolder, "Calibration.SensorDB.xml"); var dataFilename = Path.Combine(DefaultFolder, "Data.SensorDB.xml"); var modelFilename = Path.Combine(DefaultFolder, "Model.SensorDB.xml"); if (File.Exists(calFilename)) { BackupAndDelete(calFilename); } if (File.Exists(dataFilename)) { BackupAndDelete(dataFilename); } if (File.Exists(modelFilename)) { BackupAndDelete(modelFilename); } var sb = new StringBuilder(5000000); var sb2 = new StringBuilder(5000000); var sb3 = new StringBuilder(5000000); //stringbuilder always uses UTF-16, so we don't _really_ get a choice here. var xSet = new XmlWriterSettings() { Indent = true, CheckCharacters = true, Encoding = Encoding.Unicode }; var writerSensors = XmlWriter.Create(sb, xSet); var writerCals = XmlWriter.Create(sb2, xSet); var writerModels = XmlWriter.Create(sb3, xSet); writerSensors.WriteStartDocument(); writerCals.WriteStartDocument(); writerModels.WriteStartDocument(); writerCals.WriteStartElement("Calibrations"); writerSensors.WriteStartElement("SensorData"); writerSensors.WriteAttributeString("Software", System.Reflection.Assembly.GetEntryAssembly().GetName().Name); writerSensors.WriteAttributeString("SoftwareVersion", System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString(4)); writerModels.WriteStartElement("SensorModels"); _modelStrings = new List(); foreach (var s in Sensors) { // First Axis var thisSensor = new SensorData(s); WriteSLICEWareXML(thisSensor, ref writerSensors, ref writerCals, ref writerModels); } writerSensors.WriteEndElement();//SensorData writerSensors.WriteEndDocument(); writerCals.WriteEndElement();//calibrations writerCals.WriteEndDocument(); writerModels.WriteEndElement();//SensorModels writerModels.WriteEndDocument(); writerSensors.Close(); writerCals.Close(); writerModels.Close(); File.WriteAllText(dataFilename, sb.ToString(), xSet.Encoding); File.WriteAllText(calFilename, sb2.ToString(), xSet.Encoding); File.WriteAllText(modelFilename, sb3.ToString(), xSet.Encoding); OnDone?.Invoke(); } catch (Exception ex) { OnError?.Invoke(ex.Message); APILogger.Log(ex); } finally { _modelStrings = null; } } private void WriteElement(ref XmlWriter sensorWriter, string tag, string val) { sensorWriter.WriteStartElement(tag); sensorWriter.WriteString(val); sensorWriter.WriteEndElement(); } private SensorCalibration GetPreferredSensorCalibration(SensorData s) { return s.SupportedExcitation.Select(e => SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(s, e)).FirstOrDefault(sc => null != sc); } private SensorCalibration GetFirstSensorCalibration(SensorData sd) { var scPreferred = new SensorCalibration(); Array.ForEach(scPreferred.Records.Records, record => record.EngineeringUnits = sd.DisplayUnit); //FB16398: set units on all records, not just first try { scPreferred.Records.Records[0].Excitation = sd.SupportedExcitation[0]; } catch (Exception ex) { APILogger.Log(ex); } // If no SupportedExcitation, don't crash. Then, the sensor will not be exported below. return scPreferred; } private bool ShouldExport(SensorData sd, SensorCalibration scPreferred) { if (sd.IsSquib() || sd.IsDigitalInput() || sd.IsDigitalOutput() || scPreferred.NonLinear && scPreferred.IRTraccCalculationType == NonLinearStyles.IRTraccCalFactor) { return false; }//skip for now return true; } private string GetSanitizedSN(SensorData sd) { var serialNumber = sd.SerialNumber; if (1 < sd.NumberOfAxes || 0 < sd.AxisNumber) { // sanitize serialnumber string if (0 < sd.AxisNumber) { serialNumber = serialNumber.Remove(serialNumber.IndexOf("_axis_", StringComparison.Ordinal)); } // put back the axis designation serialNumber = string.Concat(serialNumber, "_axis_", sd.AxisNumber + 1); } return serialNumber; } private string GetSanitizedModel(SensorData sd) { var model = sd.Model; if (1 < sd.NumberOfAxes || 0 < sd.AxisNumber) { // sanitize model string if (0 < sd.AxisNumber) { model = model.Remove(model.IndexOf("_axis_", StringComparison.Ordinal)); } model = string.IsNullOrEmpty(model) ? string.Empty : string.Concat(model, "_axis_", sd.AxisNumber + 1); } return model; } private void WriteSLICEWareXML(SensorData sd, ref XmlWriter sensorWriter, ref XmlWriter calWriter, ref XmlWriter modelWriter) { var scPreferred = GetPreferredSensorCalibration(sd); //Handle the off chance that we have no valid calibration for this sensor at this excitation. if (null == scPreferred) { scPreferred = GetFirstSensorCalibration(sd); } if (!ShouldExport(sd, scPreferred)) { return; } foreach (var se in sd.SupportedExcitation) { var sc = SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sd, se); if (null == sc) continue; WriteSupportedExcitation(ref sensorWriter, sd, se, scPreferred, sc, ref modelWriter, ref calWriter); if (!sc.IsProportional) { break; }//this works for all excitation } } private void WriteSupportedExcitation(ref XmlWriter sensorWriter, SensorData sd, ExcitationVoltageOptions.ExcitationVoltageOption se, SensorCalibration scPreferred, SensorCalibration sc, ref XmlWriter modelWriter, ref XmlWriter calWriter) { sensorWriter.WriteStartElement("Sensor"); WriteElement(ref sensorWriter, "Version", "1"); var serialNumber = GetSanitizedSN(sd); var model = GetSanitizedModel(sd); if (1 < sd.SupportedExcitation.Length) { serialNumber = string.Concat(serialNumber, "_", se.ToString()); } WriteElement(ref sensorWriter, "SerialNumber", serialNumber); WriteElement(ref sensorWriter, "UserSerialNumber", sd.UserSerialNumber); WriteElement(ref sensorWriter, "DiagnosticsMode", sd.DiagnosticsMode.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "Model", model); WriteElement(ref sensorWriter, "Status", sd.Status.ToString()); WriteElement(ref sensorWriter, "MeasurementUnit", scPreferred.Records.Records[0].EngineeringUnits); WriteElement(ref sensorWriter, "ID", sd.EID); WriteElement(ref sensorWriter, "Capacity", $"{sd.Capacity:0}"); WriteElement(ref sensorWriter, "Comment", sd.Comment); WriteElement(ref sensorWriter, "IsPropotional", scPreferred.IsProportional.ToString().ToLower()); WriteElement(ref sensorWriter, "Bridge", sd.Bridge.ToString()); WriteElement(ref sensorWriter, "Shunt", sd.Shunt.ToString()); WriteElement(ref sensorWriter, "CalSignal", sd.CalSignal.ToString().ToLower()); WriteElement(ref sensorWriter, "InternalShuntResistance", sd.InternalShuntResistance.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "ExternalShuntResistance", sd.ExternalShuntResistance.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "BridgeResistance", sd.BridgeResistance.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "BridgeLegMode", sd.BridgeLegMode.ToString()); sensorWriter.WriteStartElement("OffsetTolerance-LowHigh"); sensorWriter.WriteAttributeString("Low", sd.OffsetToleranceLow.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteAttributeString("High", sd.OffsetToleranceHigh.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteEndElement(); WriteElement(ref sensorWriter, "Invert", sd.Invert.ToString().ToLower()); var removeOffset = scPreferred.RemoveOffset.ToString().ToLower(); if (scPreferred.NonLinear && scPreferred.Records.Records[0].Poly.NonLinearSliceWareStyle == NonLinearSLICEWareStyles.DiagnosticZeroMMmV) { //SLICEWare expects true for remove offset in this case removeOffset = "true"; } WriteElement(ref sensorWriter, "RemoveOffset", removeOffset); WriteElement(ref sensorWriter, "UserValue1", sd.UserValue1); WriteElement(ref sensorWriter, "UserValue2", sd.UserValue2); WriteElement(ref sensorWriter, "UserValue3", sd.UserValue3); sensorWriter.WriteStartElement("Range-SensorRange"); sensorWriter.WriteAttributeString("Low", sd.RangeLow.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteAttributeString("Medium", sd.RangeMedium.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteAttributeString("High", sd.RangeHigh.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteEndElement(); WriteElement(ref sensorWriter, "Excitation", se.ToString()); sensorWriter.WriteStartElement("Filter-FilterClass"); var fc = sd.Filter.FClass; var freq = sd.Filter.Frequency; if ( fc == FilterClassType.Unfiltered) { fc = FilterClassType.None; freq = 0; } sensorWriter.WriteAttributeString("Class", fc.ToString()); sensorWriter.WriteString(freq.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteEndElement(); sensorWriter.WriteStartElement("Zero-ZeroMethod");//TODO: linear/nonlinear sensorWriter.WriteAttributeString("Start", scPreferred.ZeroMethods.Methods[0].Start.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteAttributeString("End", scPreferred.ZeroMethods.Methods[0].End.ToString(CultureInfo.InvariantCulture)); sensorWriter.WriteString(scPreferred.ZeroMethods.Methods[0].Method.ToString()); sensorWriter.WriteEndElement(); WriteElement(ref sensorWriter, "InitialEU", SensorData.GetInitialEUValue(scPreferred, scPreferred.Records.Records[0].Excitation, scPreferred.InitialOffsets.Offsets.First()).ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "Created", sd.Created.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "TimesUsed", sd.TimesUsed.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "Zmo", "0"); WriteElement(ref sensorWriter, "ISOCode", sd.ISOCode); WriteElement(ref sensorWriter, "CheckOffset", sd.CheckOffset.ToString()); WriteElement(ref sensorWriter, "IRTraccExponent", scPreferred.Records.Records.First().Poly.LinearizationExponent.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "SIFFile", ""); WriteElement(ref sensorWriter, "SensorCategory", sd.SensorCategory.ToString(CultureInfo.InvariantCulture)); WriteElement(ref sensorWriter, "FilterOption", sd.ByPassFilter.ToString()); WriteElement(ref sensorWriter, "OriginalShuntMode", sd.Shunt.ToString()); WriteElement(ref sensorWriter, "OriginalZeroMethod", scPreferred.ZeroMethods.Methods.First().Method.ToString()); WriteElement(ref sensorWriter, "Manufacturer", sd.Manufacturer); WriteElement(ref sensorWriter, "CouplingMode", sd.CouplingMode.ToString()); WriteElement(ref sensorWriter, "IgnoreRange", sd.IgnoreRange.ToString()); WriteElement(ref sensorWriter, "UniPolar", sd.UniPolar.ToString().ToLower()); WriteElement(ref sensorWriter, "NonLinear", scPreferred.NonLinear.ToString().ToLower()); WriteElement(ref sensorWriter, "Dimension", sd.PhysicalDimension); WriteElement(ref sensorWriter, "PolyStyle", "IRTRacc"); WriteElement(ref sensorWriter, "IRTraccFormat", scPreferred.Records.Records.First().Poly.NonLinearSliceWareStyle.ToString()); sensorWriter.WriteEndElement();//Sensor WriteSensorModel(sd, ref modelWriter, scPreferred); WriteSensorCalibrations(sc, se, ref calWriter, serialNumber); } private void WriteSensorCalibrations(SensorCalibration sc, ExcitationVoltageOptions.ExcitationVoltageOption se, ref XmlWriter calWriter, string serialNumber) { foreach (var record in sc.Records.Records) { if (true == sc.IsProportional && record.Excitation != se) { continue; } calWriter.WriteStartElement("Calibration"); WriteElement(ref calWriter, "Version", "1"); WriteElement(ref calWriter, "SerialNumber", serialNumber); WriteElement(ref calWriter, "Date", sc.CalibrationDate.ToString(CultureInfo.InvariantCulture)); var SLICEWareSensitivity = record.Sensitivity; if (sc.IsProportional && SensorConstants.SensUnits.mVperEU == record.SensitivityUnits) { // Need to process the sensitivity in this case. SLICEWareSensitivity = record.Sensitivity / Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(se); } WriteElement(ref calWriter, "Sensitivity", SLICEWareSensitivity.ToString(CultureInfo.InvariantCulture)); WriteElement(ref calWriter, "Username", sc.Username); WriteElement(ref calWriter, "DocumentID", ""); WriteElement(ref calWriter, "MeasurementUnit", record.EngineeringUnits); WriteElement(ref calWriter, "Excitation", se.ToString()); WriteElement(ref calWriter, "Zmo", "0"); WriteElement(ref calWriter, "Poly", record.Poly.ToSLICEWareSerializeString()); calWriter.WriteEndElement(); } } private void WriteSensorModel(SensorData sd, ref XmlWriter modelWriter, SensorCalibration scPreferred) { if (!string.IsNullOrEmpty(sd.Manufacturer) && !_modelStrings.Contains(string.Concat(sd.Manufacturer, ",", sd.Model))) { _modelStrings.Add(string.Concat(sd.Manufacturer, ",", sd.Model)); modelWriter.WriteStartElement("SensorModel"); WriteElement(ref modelWriter, "Model", sd.Model); WriteElement(ref modelWriter, "Manufacturer", sd.Manufacturer); WriteElement(ref modelWriter, "UserPartNumber", ""); WriteElement(ref modelWriter, "Capacity", sd.Capacity.ToString(CultureInfo.InvariantCulture)); WriteElement(ref modelWriter, "OffsetTolerance", string.Concat(sd.OffsetToleranceLow, ",", sd.OffsetToleranceHigh)); WriteElement(ref modelWriter, "MeasurementUnit", sd.DisplayUnit); WriteElement(ref modelWriter, "SensorRange", string.Concat(sd.RangeLow, ",", sd.RangeMedium, ",", sd.RangeHigh)); WriteElement(ref modelWriter, "Sensitivity", scPreferred.Records.Records.First().Sensitivity.ToString(CultureInfo.InvariantCulture)); WriteElement(ref modelWriter, "Excitation", scPreferred.Records.Records.First().Excitation.ToString()); WriteElement(ref modelWriter, "IsProportional", scPreferred.IsProportional.ToString()); WriteElement(ref modelWriter, "Bridge", sd.Bridge.ToString()); WriteElement(ref modelWriter, "Shunt", sd.Shunt.ToString()); WriteElement(ref modelWriter, "BridgeResistance", sd.BridgeResistance.ToString(CultureInfo.InvariantCulture)); WriteElement(ref modelWriter, "RemoveOffset", scPreferred.RemoveOffset.ToString()); WriteElement(ref modelWriter, "FilterClass", sd.Filter.FClass.ToString()); WriteElement(ref modelWriter, "ZeroMethod", string.Concat(scPreferred.ZeroMethods.Methods.First().Method.ToString(), ",", scPreferred.ZeroMethods.Methods.First().Start, ",", scPreferred.ZeroMethods.Methods.First().End)); WriteElement(ref modelWriter, "InitialEU", scPreferred.InitialOffsets.Offsets.First().EU.ToString(CultureInfo.InvariantCulture)); WriteElement(ref modelWriter, "NonLinear", scPreferred.NonLinear.ToString()); WriteElement(ref modelWriter, "UniPolar", sd.UniPolar.ToString()); WriteElement(ref modelWriter, "PolynomialStyle", "Equation"); WriteElement(ref modelWriter, "IRTraccFormat", "DiagnosticZeroMMmV"); WriteElement(ref modelWriter, "LinearizationFormula", ""); WriteElement(ref modelWriter, "PhysicalDimension", sd.PhysicalDimension); WriteElement(ref modelWriter, "IgnoreRange", sd.IgnoreRange.ToString()); WriteElement(ref modelWriter, "Invert", sd.Invert.ToString()); WriteElement(ref modelWriter, "CouplingMode", sd.CouplingMode.ToString()); WriteElement(ref modelWriter, "CheckOffset", sd.CheckOffset.ToString()); modelWriter.WriteEndElement();//Model } } private void BackupAndDelete(string filename) { var backupFile = filename + ".bak"; if (File.Exists(backupFile)) { File.Delete(backupFile); } File.Move(filename, backupFile); } /// /// public void ExportTDC() { try { if (!Directory.Exists(DefaultFolder)) { Directory.CreateDirectory(DefaultFolder); } var di = new DirectoryInfo(DefaultFolder); var listSeparator = DataModelSettings.TDCSensorDatabaseExportUseCurrentLocale ? CultureInfo.CurrentCulture.TextInfo.ListSeparator : CultureInfo.InvariantCulture.TextInfo.ListSeparator; var ec = DataModelSettings.TDCSensorDatabaseExportUseCurrentLocale ? CultureInfo.CurrentCulture : CultureInfo.InvariantCulture; var fullPathToCSVFile = Path.Combine(di.FullName, DefaultFile); if (File.Exists(fullPathToCSVFile)) { BackupAndDelete(fullPathToCSVFile); } var fieldLookup = new Dictionary>(); var sb = new StringBuilder(); for (var i = CSVImportTags.MIN_VALID_VERSION; i <= CSVImportTags.MAX_VALID_VERSION; i++) { if (1 == i) { continue; }//1 only has test tags if (3 == i) { continue; }//3 is only group tags for tests ... if (i > 0 && TDCOnly) { continue; } //TDC only supports v0 var tags = CSVImportTags.GetVersionTags(i); fieldLookup[i] = new List(tags); foreach (var tag in tags) { if (tag == CSVImportTags.Tags.Unknown) { continue; } //skip if (sb.Length > 0) { sb.Append(listSeparator); } sb.Append(EscapeCSV(CSVImportTags.GetStringForTag(tag), false, listSeparator)); } } sb.AppendLine(); //done with headers, now go through all sensors, if no sensors are explicetly selected, use all sensors var currentNumber = 0; if (null == Sensors || Sensors.Length == 0) { Sensors = SensorsCollection.SensorsList.GetAllSensors(true); } ExportSensors(ref currentNumber, sb, fieldLookup, listSeparator, ec); //we may infact want to force an encoding, but for now just use the default... File.WriteAllText(fullPathToCSVFile, sb.ToString(), Encoding.GetEncoding(DataModelSettings.TDCSensorDatabaseImportEncoding)); OnDone?.Invoke(); Sensors = null; } catch (Exception ex) { OnError?.Invoke(ex.Message); APILogger.Log(ex); } } private void ExportSensors(ref int currentNumber, StringBuilder sb, IReadOnlyDictionary> columns, string listSeparator, CultureInfo ec) { foreach (var sd in Sensors) { ExportSensor(sd, sb, ref currentNumber, columns, listSeparator, ec); sb.AppendLine(); } } private void ExportSensor(SensorData sd, StringBuilder sb, ref int currentNumber, IReadOnlyDictionary> columns, string listSeparator, CultureInfo ec) { //for TDC sensitivities we use the preferred excitation record, there could be multiple records that are currently valid for different excitations //for this sensor ... but those are handled in the DataPRO only columns, if they are enabled SensorCalibration scPreferred = null; if (!sd.SupportedExcitation.Any()) { //Set supported excitation(s) based on any found calibration records var scPreferredList = SensorCalibrationList.GetCalibrationsBySerialNumber(sd); var supportedExcitationList = new List(); for (var i = 0; i < scPreferredList.Length; i++) { foreach (var calRecord in scPreferredList[i].Records.Records) { if (!supportedExcitationList.Contains(calRecord.Excitation)) { supportedExcitationList.Add(calRecord.Excitation); } } } if (!supportedExcitationList.Any()) { //There are no calibration records, so give up return; } sd.SupportedExcitation = supportedExcitationList.ToArray(); } scPreferred = SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sd, sd.SupportedExcitation.First()); //Handle the off chance that we have no valid calibration for this sensor at this excitation. if (null == scPreferred) { scPreferred = new SensorCalibration(); scPreferred.Records.Records.First().EngineeringUnits = sd.DisplayUnit; scPreferred.Records.Records.First().Excitation = sd.SupportedExcitation.First(); } if (TDCOnly && scPreferred.NonLinear && scPreferred.Records.Records.First().Poly.NonLinearStyle != NonLinearStyles.Polynomial) { //TDC can not consume this sensor type. lets give up. return; } currentNumber++; var euConversionFactor = MeasurementUnitList.GetMeasurementUnit(scPreferred.Records.Records.First().EngineeringUnits).GetScalerConversionFrom(sd.DisplayUnit); var bNeedComma = false; for (var i = CSVImportTags.MIN_VALID_VERSION; i <= CSVImportTags.MAX_VALID_VERSION; i++) { if (!columns.ContainsKey(i)) { continue; } var tags = columns[i]; if (0 == tags.Count) { continue; } foreach (var tag in tags) { ExportTag(sd, scPreferred, tag, sb, ref currentNumber, listSeparator, ref bNeedComma, euConversionFactor, i, ec); } } } private void ExportVersion0(SensorData sd, SensorCalibration scPreferred, CSVImportTags.Tags tag, StringBuilder sb, ref int currentNumber, string listSeparator, ref bool bNeedComma, double euConversionFactor, CultureInfo ec) { string sVal = null; var bTextField = false; switch (tag) { case CSVImportTags.Tags.Axis: sVal = ""; break; case CSVImportTags.Tags.BRIDGE: sVal = sd.Bridge == SensorConstants.BridgeType.HalfBridge ? "Half" : "Full"; break; case CSVImportTags.Tags.BridgeResistance: { if (double.IsNaN(sd.BridgeResistance) || sd.BridgeResistance < 0) { sVal = ""; } else { sVal = sd.BridgeResistance.ToString(ec); } } break; case CSVImportTags.Tags.BypassAAFilter: sVal = sd.ByPassFilter ? "Yes" : "No"; break; case CSVImportTags.Tags.Category: { if (scPreferred.NonLinear) { switch (scPreferred.Records.Records.First().Poly.NonLinearStyle) { case NonLinearStyles.Polynomial: sVal = ((int)SensorInformationFile.TDCSensorCategory.Polynomial).ToString(); break; default: sVal = ((int)SensorInformationFile.TDCSensorCategory.IRTracc).ToString(); break; } } else { sVal = ((int)SensorInformationFile.TDCSensorCategory.Normal).ToString(); } } break; case CSVImportTags.Tags.ChannelName: sVal = sd.Comment; bTextField = true; break; case CSVImportTags.Tags.CommentField: sVal = sd.UserValue1; bTextField = true; break; case CSVImportTags.Tags.CustomerCode: sVal = sd.UserValue2; bTextField = true; break; case CSVImportTags.Tags.DatabaseReferenceNumber: sVal = currentNumber.ToString(); break; case CSVImportTags.Tags.DesiredRange: sVal = (sd.Capacity * euConversionFactor).ToString(ec); break; case CSVImportTags.Tags.Dimension: sVal = ""; break; case CSVImportTags.Tags.Due: sVal = ""; break; case CSVImportTags.Tags.DueCal: sVal = sd.GetDueDate(scPreferred).ToString("d", ec); break; case CSVImportTags.Tags.EquivalentEUShuntResistor: sVal = ""; break; case CSVImportTags.Tags.EU: sVal = scPreferred.Records.Records.First().EngineeringUnits; bTextField = true; break; case CSVImportTags.Tags.Exc: { try { sVal = Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(sd.SupportedExcitation.First()).ToString(ec); } catch { sVal = string.Empty; } } break; case CSVImportTags.Tags.FilterClass: sVal = sd.Filter.GetFilterClassNumericValue().ToString(ec); break; case CSVImportTags.Tags.FullScale: sVal = (sd.Capacity * euConversionFactor).ToString(ec); break; case CSVImportTags.Tags.Group1: sVal = ""; break; case CSVImportTags.Tags.Group2: sVal = ""; break; case CSVImportTags.Tags.Group3: sVal = ""; break; case CSVImportTags.Tags.Group4: sVal = ""; break; case CSVImportTags.Tags.Group5: sVal = ""; break; case CSVImportTags.Tags.InvertSignal: sVal = sd.Invert ? "Yes" : "No"; break; case CSVImportTags.Tags.IRTRACCExponent: { if (scPreferred.NonLinear && scPreferred.Records.Records.First().Poly.NonLinearStyle != NonLinearStyles.Polynomial) { sVal = scPreferred.Records.Records.First().Poly.LinearizationExponent.ToString(ec); } else { sVal = ""; } } break; case CSVImportTags.Tags.LaboratoryCode: sVal = sd.UserValue3; bTextField = true; break; case CSVImportTags.Tags.LastCal: sVal = scPreferred.CalibrationDate.ToString("d", ec); break; case CSVImportTags.Tags.Location: sVal = ""; break; case CSVImportTags.Tags.Manufacturer: sVal = sd.Manufacturer ?? ""; bTextField = true; break; case CSVImportTags.Tags.Model: sVal = sd.Model ?? ""; bTextField = true; break; case CSVImportTags.Tags.OffsetToleranceHigh: sVal = sd.OffsetToleranceHigh.ToString(ec); break; case CSVImportTags.Tags.OffsetToleranceLow: sVal = sd.OffsetToleranceLow.ToString(ec); break; case CSVImportTags.Tags.OutputAtEXCFSmV: sVal = ""; break; case CSVImportTags.Tags.PartialISOCode: sVal = sd.ISOCode; bTextField = true; break; case CSVImportTags.Tags.Proportional: sVal = scPreferred.IsProportional ? "Yes" : "No"; break; case CSVImportTags.Tags.Received: sVal = ""; break; case CSVImportTags.Tags.RemoveOffset: sVal = scPreferred.RemoveOffset ? "Yes" : "No"; break; case CSVImportTags.Tags.Sensitivity: { sVal = "1"; if (scPreferred.NonLinear) { if (scPreferred.Records.Records.First().Poly.NonLinearStyle == NonLinearStyles.Polynomial) { sVal = scPreferred.Records.Records.First().Sensitivity.ToString(FormatScientificNotation(), ec); } else { sVal = scPreferred.Records.Records.First().Poly.MMPerV.ToString(ec); } } else { if (scPreferred.IsProportional) { foreach (var r in scPreferred.Records.Records) { try { if (r.Excitation != sd.SupportedExcitation.First()) continue; sVal = r.Sensitivity.ToString(ec); break; } catch { break; } } } else { sVal = scPreferred.Records.Records.First().Sensitivity.ToString(FormatScientificNotation(), ec); } } } break; case CSVImportTags.Tags.SensorID: sVal = sd.EID; bTextField = true; break; case CSVImportTags.Tags.SensorSN: sVal = sd.SerialNumber; bTextField = true; break; case CSVImportTags.Tags.SensorType: sVal = ""; break; case CSVImportTags.Tags.ShuntResistorValue: sVal = ""; break; case CSVImportTags.Tags.SoftwareZeroEquivalentEU: { bTextField = true; sVal = ""; switch (scPreferred.InitialOffsets.Offsets.First().Form) { case InitialOffsetTypes.EU: case InitialOffsetTypes.None: sVal = (scPreferred.InitialOffsets.Offsets.First().EU * euConversionFactor).ToString(ec); break; } } break; case CSVImportTags.Tags.SoftwareZeroReference: { switch (scPreferred.ZeroMethods.Methods.First().Method) { case ZeroMethodType.AverageOverTime: sVal = ZM_STRING_AVERAGE_OVER_TIME; break; case ZeroMethodType.None: sVal = ZM_STRING_NONE; break; case ZeroMethodType.UsePreEventDiagnosticsZero: sVal = ZM_STRING_DIAGNOSTICS; break; } } break; case CSVImportTags.Tags.Tech: sVal = scPreferred.Username; bTextField = true; break; case CSVImportTags.Tags.Unknown: break; case CSVImportTags.Tags.UseShuntCal: sVal = sd.Shunt == ShuntMode.Emulation ? "Yes" : "No"; break; } if (null == sVal) { return; } //if we have a null it's not a field we expect to process or know how to process, so we skip those if (bNeedComma) { sb.Append(listSeparator); } else { bNeedComma = true; } sb.Append(EscapeCSV(sVal, bTextField, listSeparator)); } private void ExportVersion1(SensorData sd, SensorCalibration scPreferred, CSVImportTags.Tags tag, StringBuilder sb, ref int currentNumber, string listSeparator, ref bool bNeedComma, double euConversionFactor, CultureInfo ec) { } private void ExportVersion2(SensorData sd, SensorCalibration scPreferred, CSVImportTags.Tags tag, StringBuilder sb, ref int currentNumber, string listSeparator, ref bool bNeedComma, double euConversionFactor, CultureInfo ec) { string sVal = null; var bTextField = false; switch (tag) { case CSVImportTags.Tags.FiveVoltExcSensitivity: { //if the default calibration is not propoprtional, it covers all supported excitations ... if (scPreferred.IsProportional && sd.SupportedExcitation.Contains(ExcitationVoltageOptions.ExcitationVoltageOption.Volt5)) { sVal = GetSensitivity(ExcitationVoltageOptions.ExcitationVoltageOption.Volt5, sd).ToString(ec); } else { sVal = ""; } } break; case CSVImportTags.Tags.TenVoltExcSensitivity: { if (scPreferred.IsProportional && sd.SupportedExcitation.Contains(ExcitationVoltageOptions.ExcitationVoltageOption.Volt10)) { sVal = GetSensitivity(ExcitationVoltageOptions.ExcitationVoltageOption.Volt10, sd).ToString(ec); } else { sVal = ""; } } break; case CSVImportTags.Tags.TwoVoltExcSensitivity: { if (scPreferred.IsProportional && sd.SupportedExcitation.Contains(ExcitationVoltageOptions.ExcitationVoltageOption.Volt2)) { sVal = GetSensitivity(ExcitationVoltageOptions.ExcitationVoltageOption.Volt2, sd).ToString(ec); } else { sVal = ""; } } break; case CSVImportTags.Tags.Unknown: break; case CSVImportTags.Tags.AxisNumber: sVal = sd.AxisNumber.ToString(); break; case CSVImportTags.Tags.BridgeLegMode: sVal = sd.BridgeLegMode.ToString(); bTextField = true; break; case CSVImportTags.Tags.BridgeType: sVal = sd.Bridge.ToString(); bTextField = true; break; case CSVImportTags.Tags.CalInterval: sVal = sd.CalInterval.ToString(); break; case CSVImportTags.Tags.CouplingMode: sVal = sd.CouplingMode.ToString(); bTextField = true; break; case CSVImportTags.Tags.Created: sVal = sd.Created.ToString("F", ec); break; case CSVImportTags.Tags.DelayMS: sVal = sd.DelayMS.ToString(ec); break; case CSVImportTags.Tags.DigitalOutputDelayMS: sVal = sd.DigitalOutputDelayMS.ToString(ec); break; case CSVImportTags.Tags.DigitalInputMode: sVal = sd.InputMode.ToString(); bTextField = true; break; case CSVImportTags.Tags.NonLinearCalibration: sVal = scPreferred.Records.Records.First().Poly.ToSerializeString(); bTextField = true; break; case CSVImportTags.Tags.AdditionalInitialOffsets: if (scPreferred.InitialOffsets.Offsets.Length > 1) { var additionalInitalOffsets = new InitialOffsets(scPreferred.InitialOffsets, scPreferred.InitialOffsets.Offsets.Length - 1); sVal = additionalInitalOffsets.ToSerializedString(); bTextField = true; } else { sVal = string.Empty; //In case there is no additional Initial Offsets } break; case CSVImportTags.Tags.AdditionalLinearSensitivity: if (scPreferred.NonLinear && scPreferred.LinearAdded) { if (scPreferred.IsProportional) { foreach (var r in scPreferred.LinearAddedRecords) { try { if (r.Excitation != sd.SupportedExcitation.First()) continue; sVal = r.Sensitivity.ToString(ec); break; } catch { break; } } } else { sVal = scPreferred.LinearAddedSensitivity.ToString(ec); } bTextField = true; } else { sVal = string.Empty; //In case there is no additional linear calibration (Sensitivity) } break; case CSVImportTags.Tags.AdditionalLinearZeroMethod: if (scPreferred.NonLinear && scPreferred.LinearAdded) { sVal = scPreferred.LinearAddedZeroMethodType.ToString(); bTextField = true; } else { sVal = string.Empty; //In case there is no additional linear calibration (Zero Method) } break; case CSVImportTags.Tags.AdditionalLinearZeroMethodEnd: if (scPreferred.NonLinear && scPreferred.LinearAdded && scPreferred.LinearAddedZeroMethodType == ZeroMethodType.AverageOverTime) { sVal = scPreferred.LinearAddedZeroMethodEnd.ToString(ec); } else { sVal = string.Empty; //In case there is no additional linear calibration (Zero Method End) } break; case CSVImportTags.Tags.AdditionalLinearZeroMethodStart: if (scPreferred.NonLinear && scPreferred.LinearAdded && scPreferred.LinearAddedZeroMethodType == ZeroMethodType.AverageOverTime) { sVal = scPreferred.LinearAddedZeroMethodStart.ToString(ec); } else { sVal = string.Empty; //In case there is no additional linear calibration (Zero Method Start) } break; case CSVImportTags.Tags.DigitalOutputMode: sVal = sd.DigitalOutputMode.ToString(); bTextField = true; break; case CSVImportTags.Tags.DigitalScaleMultiplier: sVal = sd.ScaleMultiplier.ToSerializeDbString(); bTextField = true; break; case CSVImportTags.Tags.Direction: sVal = sd.Direction; bTextField = true; break; case CSVImportTags.Tags.DisplayUnits: sVal = sd.DisplayUnit; bTextField = true; break; case CSVImportTags.Tags.DurationMS: sVal = sd.DurationMS.ToString(ec); break; case CSVImportTags.Tags.DigitalOutputDurationMS: sVal = sd.DigitalOutputDurationMS.ToString(ec); break; case CSVImportTags.Tags.InitialOffset: { // Need to export nominal EU here. Scale the EU of the InitialOffset before Serializing to String scPreferred.InitialOffsets.Offsets.First().EU = scPreferred.InitialOffsets.Offsets.First().EU * euConversionFactor; sVal = scPreferred.InitialOffsets.Offsets.First().ToDbSerializeString(); bTextField = true; break; } case CSVImportTags.Tags.LimitDuration: sVal = sd.LimitDuration ? "Yes" : "No"; break; case CSVImportTags.Tags.NonLinear: sVal = scPreferred.NonLinear ? "Yes" : "No"; break; case CSVImportTags.Tags.NumberOfAxes: sVal = sd.NumberOfAxes.ToString(); break; case CSVImportTags.Tags.PhysicalDimension: sVal = sd.PhysicalDimension; bTextField = true; break; case CSVImportTags.Tags.Polarity: sVal = sd.Polarity; bTextField = true; break; case CSVImportTags.Tags.RangeHigh: sVal = (sd.RangeHigh * euConversionFactor).ToString(ec); break; case CSVImportTags.Tags.RangeLow: sVal = (sd.RangeLow * euConversionFactor).ToString(ec); break; case CSVImportTags.Tags.RangeMedium: sVal = (sd.RangeMedium * euConversionFactor).ToString(ec); break; case CSVImportTags.Tags.SquibFireMode: sVal = sd.SquibFireMode.ToString(); bTextField = true; break; case CSVImportTags.Tags.SquibMeasurementType: sVal = sd.SquibMeasurementType.ToString(); bTextField = true; break; case CSVImportTags.Tags.SquibOutputCurrent: sVal = sd.SquibOutputCurrent.ToString(ec); break; case CSVImportTags.Tags.SupportedExcitation: sVal = sd.GetSerializedSupportedExcitation(); bTextField = true; break; case CSVImportTags.Tags.TimesUsed: sVal = sd.TimesUsed.ToString(); break; case CSVImportTags.Tags.Unipolar: sVal = sd.UniPolar ? "Yes" : "No"; break; case CSVImportTags.Tags.ZeroMethod: sVal = scPreferred.ZeroMethods.Methods.First().Method.ToString(); bTextField = true; break;//TODO: linear/nonlinear case CSVImportTags.Tags.ZeroMethodEnd: sVal = scPreferred.ZeroMethods.Methods.First().End.ToString(ec); break; case CSVImportTags.Tags.ZeroMethodStart: sVal = scPreferred.ZeroMethods.Methods.First().Start.ToString(ec); break; case CSVImportTags.Tags.CapacityOutputIsBasedOn: sVal = scPreferred.Records.Records.First().CapacityOutputIsBasedOn.ToString("N3"); break; case CSVImportTags.Tags.AtCapacity: sVal = scPreferred.Records.Records.First().AtCapacity ? "Yes" : "No"; break; case CSVImportTags.Tags.SensitivityUnits: sVal = scPreferred.Records.Records.First().SensitivityUnits.ToString(); break; case CSVImportTags.Tags.CheckOffset: sVal = sd.CheckOffset ? "Yes" : "No"; break; case CSVImportTags.Tags.Broken: sVal = sd.Broken ? "Yes" : "No"; break; case CSVImportTags.Tags.DoNotUse: sVal = sd.DoNotUse ? "Yes" : "No"; break; case CSVImportTags.Tags.ISOChannelName: sVal = sd.ISOChannelName; break; case CSVImportTags.Tags.UserCode: sVal = sd.UserCode; break; case CSVImportTags.Tags.UserChannelName: sVal = sd.UserChannelName; break; default: sVal = ""; break; } if (null == sVal) { return; } if (bNeedComma) { sb.Append(listSeparator); } else { bNeedComma = true; } sb.Append(EscapeCSV(sVal, bTextField, listSeparator)); } private void ExportVersion3(SensorData sd, SensorCalibration scPreferred, CSVImportTags.Tags tag, StringBuilder sb, ref int currentNumber, string listSeparator, ref bool bNeedComma, double euConversionFactor, CultureInfo ec) { } private void ExportVersion4(SensorData sd, SensorCalibration scPreferred, CSVImportTags.Tags tag, StringBuilder sb, ref int currentNumber, string listSeparator, ref bool bNeedComma, double euConversionFactor, CultureInfo ec) { var sVal = string.Empty; var bTextField = false; switch (tag) { case CSVImportTags.Tags.DASSerialNumber: break; case CSVImportTags.Tags.DASChannelIndex: break; case CSVImportTags.Tags.StreamProfile: sVal = sd.StreamOutUDPProfile.ToString(); bTextField = true; break; case CSVImportTags.Tags.UDPAddress: if (sd.IsStreamInput()) { sVal = sd.StreamInUDPAddress; } else { sVal = sd.StreamOutUDPAddress; } bTextField = true; break; case CSVImportTags.Tags.TimeChannelId: sVal = sd.StreamOutUDPTimeChannelId.ToString(ec); break; case CSVImportTags.Tags.DataChannelId: sVal = sd.StreamOutUDPDataChannelId.ToString(ec); break; case CSVImportTags.Tags.TmNSConfig: sVal = sd.StreamOutUDPTmNSConfig; bTextField = true; break; case CSVImportTags.Tags.IRIGTimeDataPacketIntervalMS: sVal = sd.StreamOutIRIGTimeDataPacketIntervalMs.ToString(ec); break; case CSVImportTags.Tags.TMATSIntervalMS: sVal = sd.StreamOutTMATSIntervalMs.ToString(ec); break; case CSVImportTags.Tags.BaudRate: sVal = sd.UartBaudRate.ToString(ec); bTextField = true; break; case CSVImportTags.Tags.DataBits: sVal = sd.UartDataBits.ToString(ec); bTextField = true; break; case CSVImportTags.Tags.StopBits: sVal = sd.UartStopBits.ToString(); bTextField = true; break; case CSVImportTags.Tags.Parity: sVal = sd.UartParity.ToString(); bTextField = true; break; case CSVImportTags.Tags.DataFormat: sVal = sd.UartDataFormat.ToString(); bTextField = true; break; case CSVImportTags.Tags.TestUserCode: sVal = sd.UserCode.ToString(); bTextField = true; break; case CSVImportTags.Tags.TestUserChannelName: sVal = sd.UserChannelName.ToString(); bTextField = true; break; case CSVImportTags.Tags.TestIsoCode: sVal = sd.ISOCode.ToString(); bTextField = true; break; case CSVImportTags.Tags.TestIsoChannelName: sVal = sd.ISOChannelName.ToString(); bTextField = true; break; } if (bNeedComma) { sb.Append(listSeparator); } else { bNeedComma = true; } sb.Append(EscapeCSV(sVal, bTextField, listSeparator)); } private void ExportTag(SensorData sd, SensorCalibration scPreferred, CSVImportTags.Tags tag, StringBuilder sb, ref int currentNumber, string listSeparator, ref bool bNeedComma, double euConversionFactor, int version, CultureInfo ec) { switch (version) { case 0: ExportVersion0(sd, scPreferred, tag, sb, ref currentNumber, listSeparator, ref bNeedComma, euConversionFactor, ec); break; case 1: ExportVersion1(sd, scPreferred, tag, sb, ref currentNumber, listSeparator, ref bNeedComma, euConversionFactor, ec); break; case 2: ExportVersion2(sd, scPreferred, tag, sb, ref currentNumber, listSeparator, ref bNeedComma, euConversionFactor, ec); break; case 3: ExportVersion3(sd, scPreferred, tag, sb, ref currentNumber, listSeparator, ref bNeedComma, euConversionFactor, ec); break; case 4: ExportVersion4(sd, scPreferred, tag, sb, ref currentNumber, listSeparator, ref bNeedComma, euConversionFactor, ec); break; } } //FB 29478 Format that is used by ToString method from Scientific Notation if applicable to double //https://stackoverflow.com/questions/1546113/double-to-string-conversion-without-scientific-notation/36204442#36204442 private string FormatScientificNotation() => $"0.{new string('#', 339)}"; // ReSharper disable once InconsistentNaming public void ExportDataPRO() { try { if (!Directory.Exists(DefaultFolder)) { Directory.CreateDirectory(DefaultFolder); } var di = new DirectoryInfo(DefaultFolder); var fullPathToDataProFile = Path.Combine(di.FullName, DefaultFile); if (File.Exists(fullPathToDataProFile)) { BackupAndDelete(fullPathToDataProFile); } var includedSensors = new Dictionary(); var includedSensorModels = new Dictionary(); var includedCalibrations = new Dictionary(); var includedChanges = new Dictionary>(); foreach (var sd in Sensors) { var scs = SensorCalibration.GetCalibrationsBySerialNumber(sd); if (null == sd) { continue; } var analog = sd.IsAnalog(); if (analog && (null == scs || !scs.Any())) { continue; } if (null != scs && scs.Any()) { includedCalibrations.Add(sd.SerialNumber, scs); } if (analog && sd.SupportedExcitation.Length == 0) { //Set supported excitation(s) based on any found calibration records var supportedExcitationList = new List(); for (var i = 0; i < scs.Length; i++) { foreach (var calRecord in scs[i].Records.Records) { if (!supportedExcitationList.Contains(calRecord.Excitation)) { supportedExcitationList.Add(calRecord.Excitation); } } } sd.SupportedExcitation = supportedExcitationList.ToArray(); } includedSensors.Add(sd.SerialNumber, sd); includedChanges.Add(sd, new List()); includedChanges[sd].AddRange(SensorChangeTypeHelper.GetAllSensorChanges(sd)); if (!analog) { continue; } //only care about sensormodel for analog sensors .. var sm = SensorModelCollection.SensorModelList.GetSensorModel(sd.Manufacturer, sd.Model); if (null == sm) continue; var key = $"{sm.Manufacturer}x_x{sm.Model}"; if (!includedSensorModels.ContainsKey(key)) { includedSensorModels.Add(key, sm); } } ExportToFile(includedSensors, includedSensorModels, includedCalibrations, fullPathToDataProFile, includedChanges, ExportFirstUseDate); OnDone?.Invoke(); } catch (Exception ex) { OnError?.Invoke(ex.Message); APILogger.Log(ex); } } public void ExportEQX() { try { if (!Directory.Exists(DefaultFolder)) { Directory.CreateDirectory(DefaultFolder); } var di = new DirectoryInfo(DefaultFolder); var fullPathToDataProFile = Path.Combine(di.FullName, DefaultFile); if (File.Exists(fullPathToDataProFile)) { BackupAndDelete(fullPathToDataProFile); } var includedSensors = new Dictionary(); var includedCalibrations = new Dictionary(); foreach (var sd in Sensors) { var scs = SensorCalibration.GetCalibrationsBySerialNumber(sd); var sc = SensorCalibration.GetLatestCalibrationBySerialNumber(sd); if (null == sd || null == scs || null == sc || scs.Length <= 0 || sd.IsDigitalOutput() || (sc.NonLinear && sc.IRTraccCalculationType != NonLinearStyles.Polynomial && sc.IRTraccCalculationType != NonLinearStyles.IRTraccCalFactor && sc.IRTraccCalculationType != NonLinearStyles.IRTraccAverageOverTime) //also export these types of non linear sensors? //http://manuscript.dts.local/f/cases/edit/36885/EQX-requested-changes ) { //Should warn user that these sensors will not be exported continue; } includedCalibrations.Add(sd.SerialNumber, scs); if (sd.SupportedExcitation.Length == 0) { //Set supported excitation(s) based on any found calibration records var supportedExcitationList = new List(); for (var i = 0; i < scs.Length; i++) { foreach (var calRecord in scs[i].Records.Records) { if (!supportedExcitationList.Contains(calRecord.Excitation)) { supportedExcitationList.Add(calRecord.Excitation); } } } sd.SupportedExcitation = supportedExcitationList.ToArray(); } includedSensors.Add(sd.SerialNumber, sd); } ExportToEQXFile(includedSensors, includedCalibrations, fullPathToDataProFile); OnDone?.Invoke(); } catch (Exception ex) { OnError?.Invoke(ex.Message); APILogger.Log(ex); } } private static void ExportToEQXFile(Dictionary includedSensors, Dictionary includedCalibration, string exportFile) { EqxSensors eqxSensors = GetEQXSensorsFromSensorData(includedSensors, includedCalibration); if (null == eqxSensors) { return; } var serializer = new XmlSerializer(typeof(EqxSensors)); if (File.Exists(exportFile)) { File.Delete(exportFile); } using (var fs = new FileStream(exportFile, FileMode.CreateNew)) { using (var writer = new StreamWriter(fs, Encoding.Unicode)) { serializer.Serialize(writer, eqxSensors); writer.Close(); } fs.Close(); } } private static EqxSensors GetEQXSensorsFromSensorData(Dictionary includedSensors, Dictionary includedCalibration) { EqxSensors rv = new EqxSensors(); List eqxSensors = new List(); foreach (var kvp in includedSensors) { var sd = kvp.Value as SensorData; var scList = includedCalibration[kvp.Key]; // 34420: Add case for equal cal dates, to be then ordered by modify date var sc = scList.Aggregate((curMax, x) => curMax == null || x.CalibrationDate > curMax.CalibrationDate || (x.CalibrationDate == curMax.CalibrationDate && x.ModifyDate > curMax.ModifyDate) ? x : curMax); var eqxScalingMethod = GetEqxScalingMethod(sc); var eqxSensor = new EqxSensorGroup { Name = kvp.Value.SerialNumber, SerialNumber = kvp.Value.SerialNumber, UUID = string.IsNullOrEmpty(kvp.Value.UUID) ? Guid.NewGuid().ToString() : kvp.Value.UUID, CalDate = includedCalibration[kvp.Key].First().CalibrationDate, Sensor = new EqxSensorAxis[1] { GetSensorAxis(sd, scList, sc, eqxScalingMethod) }, }; SetSensitivities(sc, eqxSensor.Sensor[0], eqxScalingMethod); eqxSensors.Add(eqxSensor); } rv.SensorGroup = eqxSensors.ToArray(); rv.FileInfo = new EqxFileInfo(); rv.FileInfo.DataFormatEdition = "1.5"; rv.FileInfo.Software = System.Reflection.Assembly.GetEntryAssembly().GetName().Name; rv.FileInfo.SoftwareVersion = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString(4); rv.FileInfo.CreationTime = DateTime.UtcNow; return rv; } private static EqxSensorAxis GetSensorAxis(SensorData sd, SensorCalibration [] scList, SensorCalibration sc, EqxScalingMethod eqxScalingMethod) { var eqx = new EqxSensorAxis { Name = sd.SerialNumber, ElectricalMethodSpecified = true, ElectricalMethod = GetEqxElectricalMethod(sd.Bridge, scList.First().IsProportional), SWFilterClassTypeSpecified = true, SWFilterClassType = GetEqxSWFilterCLass(sd.FilterClass.FClass), Supplier = sd.Manufacturer, PhysicalUnit = sd.DisplayUnit, Model = sd.Model, IDModuleString = sd.EID, LocationCode = sd.ISOCode, LocationLongname = sd.ISOChannelName, }; if (sd.IsDigitalInput()) { SetDigitalInputProperties(eqx, sd); } else if (sd.IsSquib()) { SetSquibProperties(eqx, sd); } else if (sd.IsAnalog()) { SetAnalogProperties(eqx, sd, eqxScalingMethod, sc); } return eqx; } private static void SetAnalogProperties(EqxSensorAxis eqx, SensorData sd, EqxScalingMethod eqxScalingMethod, SensorCalibration sc) { eqx.ShuntResistanceSpecified = true; eqx.ShuntResistance = (float)sd.BridgeResistance; eqx.OffsetTolSpecified = true; eqx.OffsetTol = (float)sd.OffsetToleranceHigh; eqx.CalDateSpecified = true; eqx.CalDate = sc.CalibrationDate; eqx.CalPeriodSpecified = true; eqx.CalPeriod = sd.CalInterval; eqx.CalPerson = sc.Username; eqx.MaxRangeSpecified = true; eqx.MaxRange = (float)sd.RangeHigh; eqx.MinRangeSpecified = true; eqx.MinRange = (float)sd.RangeLow; eqx.PreferredRangeSpecified = true; eqx.PreferredRange = (float)sd.Capacity; eqx.OffsetCheckSpecified = true; eqx.OffsetCheck = sd.CheckOffset; eqx.Remark = sd.Comment; eqx.CompanyLongname = sd.Manufacturer; eqx.ExcitationVoltageSpecified = true; eqx.ExcitationVoltage = (float)Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(sc.Records.Records.First().Excitation); eqx.SensitivityVoltageSpecified = true; eqx.SensitivityVoltage = sc.IsProportional ? 1F : (float)Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(sc.Records.Records.First().Excitation); eqx.SWOffsetFixValueSpecified = true; eqx.SWOffsetFixValue = (float)sc.InitialOffsets.Offsets.First().EU; eqx.MountingPolaritySpecified = true; eqx.MountingPolarity = sd.Invert ? EqxPolarity.Negative : EqxPolarity.Positive; eqx.OffsetCompensationSpecified = true; eqx.OffsetCompensation = sc.RemoveOffset; eqx.ScalingMethodSpecified = true; eqx.ScalingMethod = eqxScalingMethod; eqx.SerialNumber = sd.SerialNumber; eqx.ShuntCheckPosSpecified = true; eqx.ShuntCheckPos = sd.PerformShuntEmulation; eqx.SWOffsetCompensation = sc.RemoveOffset; eqx.SWOffsetCompensationSpecified = true; eqx.SWOffsetCompensationType = GetSWOffsetCompensationType(sc.ZeroMethods.Methods[0].Method); eqx.SWOffsetCompensationSpecified = true; if (sc.ZeroMethods.Methods[0].Method == ZeroMethodType.AverageOverTime) { eqx.SWOffsetCalculationStartSec = Convert.ToSingle(sc.ZeroMethods.Methods[0].Start); eqx.SWOffsetCalculationStartSecSpecified = true; eqx.SWOffsetCalculationEndSec = Convert.ToSingle(sc.ZeroMethods.Methods[0].End); eqx.SWOffsetCalculationEndSecSpecified = true; } } private static EqxSWOffsetCompensationType GetSWOffsetCompensationType(ZeroMethodType zeroMethod) { switch(zeroMethod) { case ZeroMethodType.AverageOverTime: return EqxSWOffsetCompensationType.Averagecalculation; case ZeroMethodType.None: return EqxSWOffsetCompensationType.Fixvalue; case ZeroMethodType.UsePreEventDiagnosticsZero: return EqxSWOffsetCompensationType.Pretestmeasurement; default: throw new InvalidCastException($"Invalid offset type: {zeroMethod}"); } } private static void SetDigitalInputProperties(EqxSensorAxis eqx, SensorData sd) { eqx.InputMode = GetEqxInputMode(sd.InputMode); eqx.InputModeSpecified = true; } private static void SetSquibProperties(EqxSensorAxis eqx, SensorData sd) { eqx.FiringModeSpecified = true; eqx.FiringMode = GetEqxFiringMode(sd.SquibFireMode); eqx.FiringDelaySpecified = true; eqx.FiringDelay = Convert.ToSingle(sd.SquibFireDelayMS); if (sd.LimitSquibFireDuration) { eqx.FiringDurationSpecified = true; eqx.FiringDuration = Convert.ToInt32(sd.SquibFireDurationMS*1000); } eqx.FiringVoltageLimitSpecified = false; eqx.FiringCurrentLimitSpecified = true; eqx.FiringCurrentLimit = Convert.ToInt32(sd.SquibOutputCurrent*1000); } private static EqxFiringMode GetEqxFiringMode(SquibFireMode mode) { switch(mode) { case SquibFireMode.AC: return EqxFiringMode.AC; case SquibFireMode.CAP: return EqxFiringMode.CapacitorDischarge; case SquibFireMode.CONSTANT: return EqxFiringMode.ConstantCurrent; case SquibFireMode.NONE: return EqxFiringMode.CapacitorDischarge; default: throw new InvalidCastException($"Unknown squib firing mode: {mode}"); } } private static EqxInputModes GetEqxInputMode(DigitalInputModes inputMode) { switch(inputMode) { case DigitalInputModes.CCNC: return EqxInputModes.CCNC; case DigitalInputModes.CCNO: return EqxInputModes.CCNO; case DigitalInputModes.THL: return EqxInputModes.THL; case DigitalInputModes.TLH: return EqxInputModes.TLH; default: throw new InvalidCastException($"Unknown input mode: {inputMode}"); } } private static void SetSensitivities(SensorCalibration sc, EqxSensorAxis axis, EqxScalingMethod eqxScalingMethod) { if (eqxScalingMethod == EqxScalingMethod.IRTRACC) { if (sc.IRTraccCalculationType == NonLinearStyles.IRTraccCalFactor) { SetSensitivitiesCalFactor(sc, axis, eqxScalingMethod); } else { SetSensitivitiesIRTRACC(sc, axis); } } else { SetSensitivitiesAllTheRest(sc, axis, eqxScalingMethod); } } private static void SetSensitivitiesIRTRACC(SensorCalibration sc, EqxSensorAxis axis) { var record = sc.Records.Records.FirstOrDefault(); if (null == record) { return; } //set EU to mV, set Sensitivity1 to 1000/Cal factor, set sensitivity 3 to (Intercept/calfactor)*1000? axis.Sensitivity = Convert.ToSingle(record.Poly.MMPerV); axis.SensitivitySpecified = true; axis.Sensitivity2Specified = true; axis.Sensitivity2 = record.Poly.LinearizationExponent; axis.EngineeringUnit = EqxEngineeringUnit.mV; } private static void SetSensitivitiesCalFactor(SensorCalibration sc, EqxSensorAxis axis, EqxScalingMethod eqxScalingMethod) { var record = sc.Records.Records.FirstOrDefault(); if (null == record) { return; } //set EU to mV, set Sensitivity1 to 1000/Cal factor, set sensitivity 3 to (Intercept/calfactor)*1000? axis.Sensitivity = Convert.ToSingle(1000D / record.Poly.CalibrationFactor); axis.SensitivitySpecified = true; axis.Sensitivity2Specified = true; axis.Sensitivity2 = record.Poly.LinearizationExponent; axis.Sensitivity3Specified = true; axis.Sensitivity3 = 1000D * (record.Poly.ZeroPositionIntercept / record.Poly.CalibrationFactor); axis.Sensitivity4Specified = false; axis.Sensitivity5Specified = false; axis.EngineeringUnit = EqxEngineeringUnit.mV; } private static void SetSensitivitiesAllTheRest(SensorCalibration sc, EqxSensorAxis axis, EqxScalingMethod eqxScalingMethod) { axis.SensitivitySpecified = true; axis.Sensitivity = (float)GetEqxSensitivity(sc, 1); axis.Sensitivity2Specified = eqxScalingMethod != EqxScalingMethod.Linear; axis.Sensitivity2 = GetEqxSensitivity(sc, 2); axis.Sensitivity3Specified = eqxScalingMethod != EqxScalingMethod.Linear; axis.Sensitivity3 = GetEqxSensitivity(sc, 3); axis.Sensitivity4Specified = eqxScalingMethod == EqxScalingMethod.CubicPolynomial; axis.Sensitivity4 = GetEqxSensitivity(sc, 4); axis.Sensitivity5Specified = eqxScalingMethod == EqxScalingMethod.CubicPolynomial; axis.Sensitivity5 = GetEqxSensitivity(sc, 5); } private static double GetEqxSensitivity(SensorCalibration sc, int sensitivityIndex) { if (sensitivityIndex > 5) { return 0D; } switch (GetEqxScalingMethod(sc)) { case EqxScalingMethod.CubicPolynomial: return sensitivityIndex >= 2 ? sc.Records.Records.First().Poly.PolynomialCoefficients[sensitivityIndex - 2] : 0D; case EqxScalingMethod.IRTRACC: case EqxScalingMethod.Linear: return sc.Records.Records.First().Sensitivity; default: return 0D; } } private static EqxScalingMethod GetEqxScalingMethod(SensorCalibration sc) { if (sc.NonLinear) { if (sc.Records.Records.First().Poly.NonLinearStyle == NonLinearStyles.Polynomial) { return EqxScalingMethod.CubicPolynomial; } return EqxScalingMethod.IRTRACC; } return EqxScalingMethod.Linear; } private static EqxFilterClassType GetEqxSWFilterCLass(FilterClassType filterClass) { switch (filterClass) { case FilterClassType.CFC10: return EqxFilterClassType.CFC10; case FilterClassType.CFC1000: return EqxFilterClassType.CFC1000; case FilterClassType.CFC180: return EqxFilterClassType.CFC180; case FilterClassType.CFC60: return EqxFilterClassType.CFC60; case FilterClassType.CFC600: return EqxFilterClassType.CFC600; case FilterClassType.AdHoc: return EqxFilterClassType.AdHoc; default: return EqxFilterClassType.None; } } private static EqxElectricalMethod GetEqxElectricalMethod(SensorConstants.BridgeType bridgeType, bool isProportional) { switch (bridgeType) { case SensorConstants.BridgeType.IEPE: return EqxElectricalMethod.PiezoInput; case SensorConstants.BridgeType.FullBridge: if (isProportional) { return EqxElectricalMethod.FullBridge; } else { return EqxElectricalMethod.ActiveSensor; } case SensorConstants.BridgeType.HalfBridge: if (isProportional) { return EqxElectricalMethod.HalfBridge; } else { return EqxElectricalMethod.HalfBridgeActive; } case SensorConstants.BridgeType.QuarterBridge: if (isProportional) { return EqxElectricalMethod.QuarterBridge; } else { return EqxElectricalMethod.QuarterBridgeActive; } default: return EqxElectricalMethod.FullBridge; } } private static void ExportToFile(Dictionary includedSensors, Dictionary includedSensorModels, Dictionary includedCalibration, string exportFile, Dictionary> includedChanges = null, bool bExportFirstUseDate = true) { using (TextWriter writer = new StreamWriter(exportFile, false)) { XmlWriter xmlWriter = null; try { xmlWriter = new XmlTextWriter(writer); xmlWriter.WriteStartDocument(); xmlWriter.WriteStartElement("ExportFile"); xmlWriter.WriteAttributeString("Version", FileUtils.CurrentXmlVersion.ToString(CultureInfo.InvariantCulture)); var fields = Enum.GetValues(typeof(TopLevelFields)).Cast().ToArray(); foreach (var f in fields) { switch (f) { case TopLevelFields.Calibrations: if (includedCalibration.Count > 0) { xmlWriter.WriteStartElement(f.ToString()); foreach (var c in includedCalibration) { foreach (var sc in c.Value) { xmlWriter.Flush(); sc.WriteXML(ref xmlWriter); xmlWriter.Flush(); } } xmlWriter.WriteEndElement(); } break; case TopLevelFields.SensorModels: if (includedSensorModels.Count > 0) { xmlWriter.WriteStartElement(f.ToString()); foreach (var sm in includedSensorModels) { xmlWriter.Flush(); sm.Value.WriteXML(ref xmlWriter); xmlWriter.Flush(); } xmlWriter.WriteEndElement(); } break; case TopLevelFields.Sensors: if (includedSensors.Count > 0) { xmlWriter.Flush(); xmlWriter.WriteStartElement(f.ToString()); foreach (var sd in includedSensors) { xmlWriter.Flush(); sd.Value.WriteXML(ref xmlWriter, bExportFirstUseDate); xmlWriter.Flush(); } xmlWriter.WriteEndElement(); } break; case TopLevelFields.SensorChangeHistory: if (null != includedChanges && includedChanges.Any()) { WriteIncludedChanges(ref xmlWriter, includedChanges); } break; } } xmlWriter.WriteEndElement(); } finally { if (xmlWriter != null) { xmlWriter.Flush(); xmlWriter.Close(); } } } } /// /// writes all sensor changes to xml /// private static void WriteIncludedChanges(ref XmlWriter writer, IReadOnlyDictionary> changes) { writer.WriteStartElement("SensorChangeHistory"); using (var eSensor = changes.GetEnumerator()) { while (eSensor.MoveNext()) { WriteIncludedChanges(ref writer, eSensor.Current.Key, eSensor.Current.Value); } } writer.WriteEndElement(); } /// /// writes all changes for a given sensor to xml /// private static void WriteIncludedChanges(ref XmlWriter writer, ISensorData sensor, IReadOnlyList changes) { writer.WriteStartElement("Sensor"); writer.WriteAttributeString("SensorId", sensor.DatabaseId.ToString(CultureInfo.InvariantCulture)); foreach (var change in changes) { writer.WriteStartElement("Change"); writer.WriteAttributeString("RecordId", change.RecordId.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("ChangeType", change.ChangeType.ToString()); writer.WriteAttributeString("Timestamp", change.TimeStamp.ToString("O", CultureInfo.InvariantCulture)); writer.WriteAttributeString("Username", change.UserName); writer.WriteAttributeString("Value1", change.Value1); writer.WriteAttributeString("Value2", change.Value2); writer.WriteAttributeString("Value3", change.Value3); writer.WriteAttributeString("Value4", change.Value4); writer.WriteEndElement(); } writer.WriteEndElement(); } public const string ZM_STRING_AVERAGE_OVER_TIME = "Avg"; public const string ZM_STRING_DIAGNOSTICS = "Pre"; public const string ZM_STRING_NONE = "0mV"; /// /// gets the latest sensitivity (as a double) for a given sensor and an excitation /// /// /// /// private double GetSensitivity(ExcitationVoltageOptions.ExcitationVoltageOption option, SensorData sd) { var sc = SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sd, option); if (null == sc) return double.NaN; foreach (var r in sc.Records.Records) { if (r.Excitation == option) { return r.Sensitivity; } } return double.NaN; } /// /// escape a string to CSV friendly string /// /// /// whether the field is intended to be text and may need to be escaped (example, sensor serial of 6-1) /// /// public static string EscapeCSV(string str, bool bTextField, string listSeparator) { //find out if we need to escape string, if so var quote = str.Contains(listSeparator) || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"); if (!string.IsNullOrWhiteSpace(str) && bTextField && char.IsDigit(str[0])) { quote = true; } if (!quote) return str; //then go character by character escaping as you go var sb = new StringBuilder(); sb.Append("\""); foreach (var nextChar in str) { sb.Append(nextChar); if (nextChar == '"') { sb.Append("\""); } } sb.Append("\""); str = sb.ToString(); return str; } public static string UnEscapeCSV(string str) { if (!str.StartsWith("\"") || !str.EndsWith("\"")) { return str; } str = str.Substring(1, str.Length - 2); var sb = new StringBuilder(); for (var i = 0; i < str.Length; i++) { var c = str[i]; if (i == str.Length - 2) { sb.Append(c); } else { if (c == '"' && str[i + 1] == '"') { sb.Append(c); i++; } } } str = sb.ToString(); return str; } } }