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;
}
}
}
}