using System.Collections.Generic; using System.Linq; using DTS.Common.Utilities.Logging; using DTS.Slice.Users; namespace DTS.SensorDB { /// /// A class that handles merging of sensor calibrations /// public class SensorMerge { public List SensorDataMergeList { get; private set; } = new List(); public Dictionary> SensorCalibrationMergeList { get; private set; } = new Dictionary>(); public Dictionary OldSensorDatabaseIdToNew { get; private set; } = new Dictionary(); private readonly User _currentUser = null; private readonly object _lock = new object(); private Dictionary _mergeStatuses = new Dictionary(); public MergeStatus GetMergeStatus(string serialNumber) { lock (_lock) { return _mergeStatuses.ContainsKey(serialNumber) ? _mergeStatuses[serialNumber] : null; } } public List GetMergeStatuses(List serialNumbers) { var rv = new List(); foreach (var sn in serialNumbers) { rv.Add(GetMergeStatus(sn)); } return rv; } public List GetAllMergeStatuses() { var rv = new List(); lock (_lock) { rv.AddRange(_mergeStatuses.Values); } return rv; } public class MergeStatus { /// /// Gets or sets the string for IsSensorInUse for the sensor /// public string BrokenDoNotUse { get; set; } /// /// Gets or sets the bool for a sensor with an EID that matches another sensor in the database /// public bool ConflictedEID { get; set; } /// /// Gets or sets the bool for a sensor with an EID that matches another sensor in the database /// public bool WarnConflictedEID { get; set; } /// /// Gets or sets the bool for a sensor that may match serial number and cal date but have different calibration information. /// public bool ConflictedSensor { get; set; } /// /// If true, the sensor did not get committed because of an error /// public bool Errored => ConflictedEID && ConflictedSensor && WarnConflictedEID || ErrorCommitingCal || MismatchedModelOrBridgeType; /// /// indicates that there was an error with the cal or the cal failed to be committed /// public bool ErrorCommitingCal { get; set; } = false; /// /// If true, the sensor did not get committed because there was newer/same information in the database. /// public bool Ignored { get; set; } /// /// If true, the sensor was committed or updated. /// public bool Committed { get; set; } /// /// If true, the input file had a mismatch in the Model or BridgeType column /// public bool MismatchedModelOrBridgeType { get; set; } } public SensorMerge(User currentUser) { _currentUser = currentUser; } public SensorMerge(User currentUser, List sensorDataToMerge, Dictionary> sensorCalibrationsToMerge) { SensorDataMergeList = sensorDataToMerge; SensorCalibrationMergeList = sensorCalibrationsToMerge; _currentUser = currentUser; } public SensorMerge(User currentUser, List sensorDataToMerge, Dictionary> sensorCalibrationsToMerge, Dictionary oldSensorDatabaseIdToNew) { SensorDataMergeList = sensorDataToMerge; SensorCalibrationMergeList = sensorCalibrationsToMerge; OldSensorDatabaseIdToNew = oldSensorDatabaseIdToNew; _currentUser = currentUser; } public void PerformWork() { lock (_lock) { _mergeStatuses = new Dictionary(); // Verify that SensorDataMergeList Contains the Calibration entries. // If it doesn't, add from the DB, if they exist there. foreach (var scList in SensorCalibrationMergeList) { var sn = scList.Value.FirstOrDefault()?.SerialNumber; if (string.IsNullOrEmpty(sn)) continue; if (SensorDataMergeList.Any(x => string.Equals(x.SerialNumber, sn))) continue; var existing = SensorsCollection.SensorsList.GetSensorBySerialNumber(sn); if (existing != null) { SensorDataMergeList.Add(existing); } } //handle broken [true] before do not use //handle do not use [true] before regular. SensorDataMergeList.Sort((a, b) => { if (a.Equals(b)) { return 0; } if (a.Broken == b.Broken) { if (a.DoNotUse == b.DoNotUse) { return a.CompareTo(b); } return a.DoNotUse ? -1 : 1; } return a.Broken ? -1 : 1; }); foreach (var sd in SensorDataMergeList) { try { _mergeStatuses[sd.SerialNumber] = new MergeStatus(); var oldId = sd.DatabaseId; if (sd.Broken || sd.DoNotUse) { _mergeStatuses[sd.SerialNumber].BrokenDoNotUse = DTS.SensorDB.SensorsCollection.SensorsList.IsSensorInUse(sd); } if (sd.IsSquib() || sd.IsDigitalInput() || sd.IsDigitalOutput()) { var existing = DTS.SensorDB.SensorsCollection.SensorsList.GetSensorBySerialNumber(sd.SerialNumber); if (null == existing) { if (DTS.SensorDB.SensorsCollection.SensorsList.SensorIdExists(sd.EID)) { //just fine, note it and don't commit it _mergeStatuses[sd.SerialNumber].WarnConflictedEID = true; } else { CommitToSensorList(sd, oldId); _mergeStatuses[sd.SerialNumber].Committed = true; } } else if (!existing.SimpleEquals(sd)) { // Setting does not match. Update it. CommitToSensorList(sd, oldId); _mergeStatuses[sd.SerialNumber].Committed = true; } else { _mergeStatuses[sd.SerialNumber].Ignored = true; //just fine, note and move on (don't commit } } else { if (!SensorCalibrationMergeList.ContainsKey(sd.UUID)) continue; var scNew = SensorCalibrationMergeList[sd.UUID].FirstOrDefault(); var scOld = DTS.SensorDB.SensorCalibrationList.GetLatestCalibrationBySerialNumber(sd); var bCommit = false; if (null == scOld) { bCommit = true; if (DTS.SensorDB.SensorsCollection.SensorsList.SensorIdExists(sd.EID) && (!sd.Broken || !sd.DoNotUse)) { bCommit = false; _mergeStatuses[sd.SerialNumber].ConflictedEID = true; //ERROR } } else if (scOld.CalibrationDate.Date < scNew.CalibrationDate || sd.Broken || sd.DoNotUse) { bCommit = true; } else if (scOld.CalibrationDate == scNew.CalibrationDate) { bCommit = false; var sdOld = DTS.SensorDB.SensorsCollection.SensorsList.GetSensorBySerialNumber(sd.SerialNumber, false); if (null == sdOld || !sdOld.SimpleEquals(sd)) { //if the cal dates are the same but the cal info is not, we've got an error //if the ids are the same, the cal dates are the same, and broken and do not use are all the same, //we've got nothing to update, so we've got an error //if broken/donotuse/id change do the update if (null != sdOld && (!scOld.SimpleEquals(scNew) || sdOld.EID.Equals(sd.EID) && sdOld.Broken == sd.Broken && sd.DoNotUse == sdOld.DoNotUse)) { _mergeStatuses[sd.SerialNumber].ConflictedSensor = true; //ERROR } else { //http://fogbugz/fogbugz/default.asp?10391 //allow updating if the cal info is the same, and ids have changed //we might want to check that just the id changed ... bCommit = true; } } else { //just fine, note it and don't commit it _mergeStatuses[sd.SerialNumber].Ignored = true; } } if (!bCommit) continue; try { CommitToSensorList(sd, oldId); } catch (System.Exception ex) { //flag all 3 problems otherwise "Errored" isn't true with current rules _mergeStatuses[sd.SerialNumber].ConflictedEID = true; _mergeStatuses[sd.SerialNumber].ConflictedSensor = true; _mergeStatuses[sd.SerialNumber].WarnConflictedEID = true; APILogger.Log(ex); continue; } if (null != scNew && !scNew.SimpleEquals(scOld)) { try { //avoid any dates which are invalid according to ms sql if (scNew.CalibrationDate.Year <= 1900) { _mergeStatuses[sd.SerialNumber].ErrorCommitingCal = true; APILogger.Log($"Invalid date: {scNew.CalibrationDate.ToShortDateString()} - {sd.SerialNumber}"); continue; } SensorCalibrationList.Commit(scNew, true, sd); } catch (System.Exception ex) { _mergeStatuses[sd.SerialNumber].ErrorCommitingCal = true; APILogger.Log(ex); continue; } } _mergeStatuses[sd.SerialNumber].Committed = true; } } catch (System.Exception ex) { if (ex.Message.Contains("can't change sensor to")) { _mergeStatuses[sd.SerialNumber].MismatchedModelOrBridgeType = true; } else { _mergeStatuses[sd.SerialNumber].ErrorCommitingCal = true; } APILogger.Log(ex); continue; } } } } private void CommitToSensorList(SensorData sd, int oldId) { SensorsCollection.SensorsList.Commit(_currentUser.UserName, sd, false); if (sd.DatabaseId != oldId) { OldSensorDatabaseIdToNew[oldId] = sd.DatabaseId; } } } }