using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Globalization; using System.Linq; using DTS.Common.Enums; using DTS.Common.Interface.Sensors; using DTS.Common.Storage; using DTS.Common.Utilities.Logging; namespace DTS.SensorDB { public class SensorCalibrationList { private static List _cachedCalibrations = null; public static void SetCachedCalibrations(SensorCalibration[] cachedCals) { _cachedCalibrations = null == cachedCals ? null : new List(cachedCals); } public static void ClearCachedCalibrations() { _cachedCalibrations = null; _calibrationList?._calibrations?.Clear(); } // I'm not sure we ever want to call this constructor? we probably always want to get all? //22287 Calibration error in Edit Test Setup when DataPRO is initiated and Sensors tab is not clicked first. //private SensorCalibrationList(string sensorSerialNumber) //{ // _calibrations = new Dictionary>(); // var hr = DbOperations.SensorCalibrationsGet(null, sensorSerialNumber, out var records); // if( 0 == hr && null != records && records.Any()) // { // foreach( var record in records) // { // var sc = new SensorCalibration(record); // if (!_calibrations.ContainsKey(sc.SerialNumber)) // { // _calibrations.Add(sc.SerialNumber, new List()); // } // _calibrations[sc.SerialNumber].Add(sc); // } // } //} protected SensorCalibrationList(ISensorCalDbRecord[] records) { _calibrations = new Dictionary>(); if (null != records && records.Any()) { foreach (var record in records) { var sc = new SensorCalibration(record); if (!_calibrations.ContainsKey(sc.SerialNumber)) { _calibrations.Add(sc.SerialNumber, new List()); } _calibrations[sc.SerialNumber].Add(sc); } } } private readonly Dictionary> _calibrations; private static readonly object LOCK = new object(); private static SensorCalibrationList _calibrationList; public static void Reload() { lock (LOCK) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } } public static void Reload(ISensorCalDbRecord[] records) { lock (LOCK) { _calibrationList = new SensorCalibrationList(records); } } public static ISensorCalDbRecord[] GetSensorCalibrationsFromDb() { var hr = DbOperations.SensorCalibrationsGet(null, null, out var records); if (hr == 0) { return records; } return new ISensorCalDbRecord[0]; } public static SensorCalibration GetLatestCalibrationBySerialNumber(SensorData sd, ISensorCalDbRecord[] sensorCalDbRecords = null) { if (null == sd) { return null; } if (sd.IsDigitalInput() || sd.IsSquib() || sd.IsDigitalOutput()) { return SensorCalibration.NewDigitalSC(sd.IsDigitalInput() ? sd.DIUnits : "V"); } if (null != _cachedCalibrations) { var matches = from sc in _cachedCalibrations where sc.SerialNumber == sd.SerialNumber select sc; var sensorCalibrations = matches as SensorCalibration[] ?? matches.ToArray(); if (sensorCalibrations.Any()) { return ValidateAndGetSensorCalibrationFromCache(sensorCalibrations); } } lock (LOCK) { AssignCalibrationListFromFb(ref sensorCalDbRecords); if (_calibrationList._calibrations.ContainsKey(sd.SerialNumber) && _calibrationList._calibrations[sd.SerialNumber].Count > 0) { return ValidateAndGetSensorCalibration(sd); } return null; } } private static SensorCalibration ValidateAndGetSensorCalibrationFromCache(SensorCalibration[] sensorCalibrations) { SensorCalibration cal = null; foreach (var sc in sensorCalibrations) { if (null == cal || cal.CalibrationDate < sc.CalibrationDate || cal.CalibrationDate == sc.CalibrationDate && cal.ModifyDate < sc.ModifyDate) { cal = sc; } } return cal; } private static void AssignCalibrationListFromFb(ref ISensorCalDbRecord[] sensorCalDbRecords) { if ((null == _calibrationList) || (_calibrationList._calibrations == null) || (!_calibrationList._calibrations.Any())) { if (sensorCalDbRecords == null) { sensorCalDbRecords = GetSensorCalibrationsFromDb(); } _calibrationList = new SensorCalibrationList(sensorCalDbRecords); } } private static SensorCalibration ValidateAndGetSensorCalibration(SensorData sd) { try { var item = _calibrationList._calibrations[sd.SerialNumber].Aggregate((i1, i2) => { if (i1.CalibrationDate > i2.CalibrationDate) { return i1; } else { return i1.CalibrationDate == i2.CalibrationDate && i1.ModifyDate > i2.ModifyDate ? i1 : i2; } }); return new SensorCalibration(item);//for safety reasons, don't return the original } catch (Exception ex) { APILogger.Log(ex); } return null; } public static SensorCalibration NewEmbeddedSC(string units) { return SensorCalibration.NewEmbeddedSC(units); } public static SensorCalibration GetLatestCalibrationBySerialNumberAndExcitation(SensorData sd, ExcitationVoltageOptions.ExcitationVoltageOption exc) { if (null == sd) { return null; } if (sd.IsDigitalInput() || sd.IsSquib() || sd.IsDigitalOutput()) { return SensorCalibration.NewDigitalSC(sd.IsDigitalInput() ? sd.DIUnits : "V"); } if (sd.IsTestSpecificEmbedded) { return SensorCalibration.NewEmbeddedSC(sd.Calibration?.EngineeringUnits ?? "V"); } //TODO: REMOVE THIS HACK when we have proper get cal functions if (sd.IsTestSpecificThermo) { return SensorCalibration.NewEmbeddedSC(sd.Calibration?.EngineeringUnits ?? "C"); } if (null != _cachedCalibrations && _cachedCalibrations.Any()) { var matches = from sc in _cachedCalibrations where sc.SerialNumber == sd.SerialNumber select sc; var sensorCalibrations = matches as SensorCalibration[] ?? matches.ToArray(); if (sensorCalibrations.Any()) { SensorCalibration cal = null; foreach (var sc in sensorCalibrations) { if (sc.IsProportional) { var bOk = Array.Exists(sc.Records.Records, record => record.Excitation == exc); if (!bOk) { continue; } } if (null == cal) { cal = sc; } else if (sc.CalibrationDate > cal.CalibrationDate) { cal = sc; } else if (sc.CalibrationDate == cal.CalibrationDate && sc.ModifyDate > cal.ModifyDate) { cal = sc; } } if (null != cal) { return cal; } } } lock (LOCK) { if (null == _calibrationList || 0 == _calibrationList._calibrations.Count) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } if (!_calibrationList._calibrations.ContainsKey(sd.SerialNumber) || _calibrationList._calibrations[sd.SerialNumber].Count <= 0) return null; try { var list = _calibrationList._calibrations[sd.SerialNumber]; list.Sort(); foreach (var sc in list) { if (!sc.IsProportional) { return new SensorCalibration(sc); } if (Array.Exists(sc.Records.Records, record => record.Excitation == exc)) { return new SensorCalibration(sc); } } } catch (Exception ex) { APILogger.Log(ex); } return null; } } public static SensorCalibration GetLatestCalibrationsBySerialNumberAndCalDate(string ser, DateTime calDate) { if (null == _calibrationList) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } if (!_calibrationList._calibrations.ContainsKey(ser) || _calibrationList._calibrations[ser].Count <= 0) return null; try { var list = new List(_calibrationList._calibrations[ser]); for (var i = list.Count - 1; i >= 0; i--) { if (list[i].CalibrationDate != calDate) { list.RemoveAt(i); } } if (list.Count <= 0) return null; list.Sort(); return new SensorCalibration(list[0]); } catch (Exception ex) { APILogger.Log(ex); } return null; } public static SensorCalibration GetLatestCalibrationsBySerialNumberCalDateAndModifyDate(string ser, DateTime calDate, DateTime modifyDate) { if (null == _calibrationList) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } if (_calibrationList._calibrations.ContainsKey(ser) && _calibrationList._calibrations[ser].Count > 0) { try { var list = new List(_calibrationList._calibrations[ser]); for (var i = list.Count - 1; i >= 0; i--) { if (list[i].CalibrationDate != calDate) { list.RemoveAt(i); } else { //12488 Import Test Setup adds new sensor calibration entries every time //note that modify date as datetime can have more deviation than cal dates, which are just date //so we have to consider the minimum varation between time, which I set to 1 second here var delta = list[i].ModifyDate.Subtract(modifyDate); if (Math.Abs(delta.TotalSeconds) >= 1) { list.RemoveAt(i); } } } if (list.Count <= 0) return null; list.Sort(); return new SensorCalibration(list[0]); } catch (Exception ex) { APILogger.Log(ex); } return null; } return null; } public static SensorCalibration[] GetCalibrationsBySerialNumber(SensorData sd) { if (null == sd) { return new SensorCalibration[0]; } if (sd.IsDigitalInput() || sd.IsSquib() || sd.IsDigitalOutput()) { return new[] { SensorCalibration.NewDigitalSC(sd.IsDigitalInput() ? sd.DIUnits : "V") }; } if (null != _cachedCalibrations) { var matches = from sc in _cachedCalibrations where sd.SerialNumber == sc.SerialNumber select sc; var sensorCalibrations = matches as SensorCalibration[] ?? matches.ToArray(); if (sensorCalibrations.Any()) { return sensorCalibrations.ToArray(); } } lock (LOCK) { if (null == _calibrationList || 0 == _calibrationList._calibrations.Count) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } if (!_calibrationList._calibrations.ContainsKey(sd.SerialNumber)) return new SensorCalibration[0]; var list = new List(_calibrationList._calibrations[sd.SerialNumber].Count); list.AddRange(_calibrationList._calibrations[sd.SerialNumber].Select(sc => new SensorCalibration(sc))); return list.ToArray(); } } /// /// commits a sensor to the db /// /// /// /// /// whether to set the calibration id on the sensor to the sensor calibration when the calibration is committed public static void Commit(SensorCalibration sc, bool bChangeModifyDate, SensorData sd, bool bSetLatestCalId = false) { try { if (sd.IsDigitalInput() || sd.IsDigitalOutput() || sd.IsSquib() || sd.IsStreamOutput() || sd.IsStreamInput() || sd.IsUart()) { return; } if (null == sc) { return; } SensorCalibration scExisting = null; if (!bChangeModifyDate) { scExisting = GetLatestCalibrationsBySerialNumberCalDateAndModifyDate(sc.SerialNumber, sc.CalibrationDate, sc.ModifyDate); } if (null != scExisting && sc.Equals(scExisting)) { return; }//no update needed if (string.IsNullOrEmpty(sc.SerialNumber)) { return; }//don't commit a calibration without a serialnumber //correct units capitalization if needed foreach (var record in sc.Records.Records) { var units = MeasurementUnitList.GetMeasurementUnit(record.EngineeringUnits); if (null != units && units.MainDisplayUnit != record.EngineeringUnits) { record.EngineeringUnits = units.MainDisplayUnit; } } sc.Username = string.Empty; if (null != DbOperations.CurrentUserDbRecord) { sc.Username = DbOperations.CurrentUserDbRecord.UserName; } sc.Insert(bChangeModifyDate, sd, bSetLatestCalId); if (!_calibrationList._calibrations.ContainsKey(sc.SerialNumber)) { _calibrationList._calibrations.Add(sc.SerialNumber, new List()); } _calibrationList._calibrations[sc.SerialNumber].Add(new SensorCalibration(sc)); } // ReSharper disable once PossibleNullReferenceException catch (Exception ex) { APILogger.Log("Failed to write sensor calibration", sc.SerialNumber, ex); } } /// /// deletes all calibration data /// originally created so TDM imports could clear all tables except DAS tables /// public static void DeleteAll() { try { var hr = DbOperations.SensorCalibrationsDelete(null, null, null); if (0 != hr) { return; } lock (LOCK) { if (null == _calibrationList) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } _calibrationList._calibrations.Clear(); } } catch (Exception ex) { APILogger.Log("Failed to delete sensor calibrations ", ex); } } /// /// Deletes Calibration records from the database and Calibration List dictionary that match the serialNumber param /// /// public static void DeleteCalsBySerialNumber(string serialNumber) { // 6853 - Sensitivities are lost when importing SLICEWare sensors. try { var hr = DbOperations.SensorCalibrationsDelete(serialNumber, null, null); if (0 != hr) { APILogger.Log("Failed to delete sensor calibration ", serialNumber, hr); return; } lock (LOCK) { if (null == _calibrationList) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } if (!_calibrationList._calibrations.ContainsKey(serialNumber)) return; for (var i = _calibrationList._calibrations[serialNumber].Count - 1; i >= 0; i--) { _calibrationList._calibrations[serialNumber].RemoveAt(i); } } } catch (Exception ex) { APILogger.Log("Failed to delete sensor calibration ", serialNumber, ex); } } public static void Delete(SensorCalibration sc) { try { var hr = DbOperations.SensorCalibrationsDelete(sc.SerialNumber, sc.CalibrationDate.Date, sc.ModifyDate); if (0 != hr) { APILogger.Log("Failed to delete sensor calibration ", sc.SerialNumber, sc.CalibrationDate.ToShortDateString(), sc.ModifyDate.ToString(CultureInfo.InvariantCulture), hr); return; } lock (LOCK) { if (null == _calibrationList) { _calibrationList = new SensorCalibrationList(GetSensorCalibrationsFromDb()); } if (!_calibrationList._calibrations.ContainsKey(sc.SerialNumber)) return; for (var i = _calibrationList._calibrations[sc.SerialNumber].Count - 1; i >= 0; i--) { if (_calibrationList._calibrations[sc.SerialNumber][i].CalibrationDate != sc.CalibrationDate || _calibrationList._calibrations[sc.SerialNumber][i].ModifyDate != sc.ModifyDate) continue; _calibrationList._calibrations[sc.SerialNumber].RemoveAt(i); break; } } } catch (Exception ex) { APILogger.Log("Failed to delete sensor calibration ", sc.SerialNumber, sc.CalibrationDate.ToShortDateString(), sc.ModifyDate.ToString(CultureInfo.InvariantCulture), ex); } } } }