521 lines
20 KiB
Plaintext
521 lines
20 KiB
Plaintext
|
|
using DTS.Common.Interface.Sensors;
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Text;
|
||
|
|
using DTS.Common.Enums;
|
||
|
|
using DTS.Common.Enums.Sensors;
|
||
|
|
using DTS.Common.Utilities.Logging;
|
||
|
|
using System.Globalization;
|
||
|
|
|
||
|
|
namespace DTS.Common.Classes.Sensors
|
||
|
|
{
|
||
|
|
public class CalibrationRecords : ICalibrationRecords
|
||
|
|
{
|
||
|
|
public ICalibrationRecord[] Records { get; set; } = new CalibrationRecord[] { new CalibrationRecord() };
|
||
|
|
|
||
|
|
public CalibrationRecords(ICalibrationRecords copy)
|
||
|
|
{
|
||
|
|
var records = new CalibrationRecord[copy.Records.Length];
|
||
|
|
for (var i = 0; i < copy.Records.Length; i++)
|
||
|
|
{
|
||
|
|
records[i] = new CalibrationRecord(copy.Records[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
Records = records;
|
||
|
|
}
|
||
|
|
|
||
|
|
public CalibrationRecords()
|
||
|
|
{
|
||
|
|
Records = new CalibrationRecord[] { new CalibrationRecord() };
|
||
|
|
}
|
||
|
|
|
||
|
|
public CalibrationRecords(string records)
|
||
|
|
{
|
||
|
|
FromSerializedString(records);
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool IsEqual(object obj, ISensorCalibration sc)
|
||
|
|
{
|
||
|
|
if (obj is CalibrationRecords r)
|
||
|
|
{
|
||
|
|
if (r.Records.Length != Records.Length)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (var i = 0; i < r.Records.Length; i++)
|
||
|
|
{
|
||
|
|
if (!r.Records[i].IsEqual(Records[i], sc))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return base.Equals(obj);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void FromSerializedString(string s)
|
||
|
|
{
|
||
|
|
var tokens = s.Split(new string[] { MySeparator }, StringSplitOptions.None);
|
||
|
|
for (var i = 0; i < tokens.Length; i++)
|
||
|
|
{
|
||
|
|
tokens[i] = tokens[i].Replace(MySeparatorBackup, MySeparator);
|
||
|
|
}
|
||
|
|
|
||
|
|
var records = new List<CalibrationRecord>();
|
||
|
|
foreach (string token in tokens)
|
||
|
|
{
|
||
|
|
records.Add(new CalibrationRecord(token));
|
||
|
|
}
|
||
|
|
|
||
|
|
Records = records.ToArray();
|
||
|
|
}
|
||
|
|
|
||
|
|
private const string MySeparator = "__x__";
|
||
|
|
private const string MySeparatorBackup = "___xx___";
|
||
|
|
|
||
|
|
public string ToSerializedString(ISensorCalDbRecord sc)
|
||
|
|
{
|
||
|
|
var records = new List<string>();
|
||
|
|
|
||
|
|
foreach (var r in Records)
|
||
|
|
{
|
||
|
|
records.Add(r.ToSerializedString(sc));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (var i = 0; i < records.Count; i++)
|
||
|
|
{
|
||
|
|
System.Diagnostics.Trace.Assert(!records[i].Contains(MySeparatorBackup));
|
||
|
|
records[i] = records[i].Replace(MySeparator, MySeparatorBackup);
|
||
|
|
}
|
||
|
|
|
||
|
|
return string.Join(MySeparator, records.ToArray());
|
||
|
|
}
|
||
|
|
|
||
|
|
public string ToDisplayString(ISensorCalibration sc, ISensorCalibration previous, string linearFormat, string nonlinearFormat)
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
|
||
|
|
for (var i = 0; i < Records.Length; i++)
|
||
|
|
{
|
||
|
|
if (i > 0)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
var r = Records[i];
|
||
|
|
ICalibrationRecord r2 = null;
|
||
|
|
|
||
|
|
if (null != previous)
|
||
|
|
{
|
||
|
|
if (i < previous.Records.Records.Length)
|
||
|
|
{
|
||
|
|
r2 = previous.Records.Records[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var s = r.ToDisplayString(sc, r2, previous, linearFormat, nonlinearFormat);
|
||
|
|
if (!string.IsNullOrEmpty(s))
|
||
|
|
{
|
||
|
|
sb.Append(s);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class CalibrationRecord : ICalibrationRecord
|
||
|
|
{
|
||
|
|
public double Sensitivity { get; set; }
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// ZeroPoint is used to hold the calibration certificate field for 2D/3D IR-TRACC cal certs
|
||
|
|
/// it is used to zero the IR-TRACC and POT data prior to being fed into the 3D equations
|
||
|
|
/// </summary>
|
||
|
|
private double _zeroPoint = 0D;
|
||
|
|
|
||
|
|
public double ZeroPoint
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
if (false == Equals(Poly.CalibrationFactor, 0.0))
|
||
|
|
{
|
||
|
|
// This field is always calculated. Do not return stored value unless we are unable to calculate
|
||
|
|
return Poly.ZeroPositionIntercept / Poly.CalibrationFactor;
|
||
|
|
}
|
||
|
|
|
||
|
|
return _zeroPoint;
|
||
|
|
}
|
||
|
|
set => _zeroPoint = value;
|
||
|
|
}
|
||
|
|
|
||
|
|
public LinearizationFormula Poly { get; set; }
|
||
|
|
|
||
|
|
public bool AtCapacity { get; set; } = false;
|
||
|
|
|
||
|
|
public string EngineeringUnits { get; set; } = "g";
|
||
|
|
|
||
|
|
public SensorConstants.SensUnits SensitivityUnits { get; set; } = SensorConstants.SensUnits.NONE;
|
||
|
|
|
||
|
|
public ExcitationVoltageOptions.ExcitationVoltageOption Excitation { get; set; } = ExcitationVoltageOptions.ExcitationVoltageOption.Volt5;
|
||
|
|
|
||
|
|
public double CapacityOutputIsBasedOn { get; set; } = 1.000;
|
||
|
|
|
||
|
|
public InitialOffsetTypes InitialOffsetMethod { get; set; } = InitialOffsetTypes.EU;
|
||
|
|
|
||
|
|
public string ISOCode { get; set; } = String.Empty;
|
||
|
|
|
||
|
|
private enum Fields
|
||
|
|
{
|
||
|
|
Sensitivity,
|
||
|
|
Poly,
|
||
|
|
AtCapacity,
|
||
|
|
EngineeringUnits,
|
||
|
|
Excitation,
|
||
|
|
CapacityOutputIsBasedOn,
|
||
|
|
SensitivityUnits,
|
||
|
|
ZeroPoint,
|
||
|
|
ISOCode
|
||
|
|
};
|
||
|
|
|
||
|
|
public string ToSerializedString(ISensorCalDbRecord parentCal)
|
||
|
|
{
|
||
|
|
var tokens = new List<string>();
|
||
|
|
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().ToArray();
|
||
|
|
foreach (var field in fields)
|
||
|
|
{
|
||
|
|
switch (field)
|
||
|
|
{
|
||
|
|
case Fields.AtCapacity:
|
||
|
|
tokens.Add(AtCapacity.ToString());
|
||
|
|
break;
|
||
|
|
case Fields.EngineeringUnits:
|
||
|
|
tokens.Add(EngineeringUnits);
|
||
|
|
break;
|
||
|
|
case Fields.Excitation:
|
||
|
|
tokens.Add(Excitation.ToString());
|
||
|
|
break;
|
||
|
|
case Fields.Poly:
|
||
|
|
Poly.MarkValid(parentCal.NonLinear);
|
||
|
|
if (parentCal.LinearAdded)
|
||
|
|
{
|
||
|
|
//We have a mixed-sensitivity sensor. Mark the first CR valid, kill the rest
|
||
|
|
for (var i = 0; i < parentCal.Records.Records.Length; i++)
|
||
|
|
{
|
||
|
|
parentCal.Records.Records[i].Poly.MarkValid(i == 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
tokens.Add(Poly.ToSerializeString());
|
||
|
|
break;
|
||
|
|
case Fields.Sensitivity:
|
||
|
|
tokens.Add(Sensitivity.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
|
break;
|
||
|
|
case Fields.CapacityOutputIsBasedOn:
|
||
|
|
tokens.Add(CapacityOutputIsBasedOn.ToString());
|
||
|
|
break;
|
||
|
|
case Fields.SensitivityUnits:
|
||
|
|
tokens.Add(SensitivityUnits.ToString());
|
||
|
|
break;
|
||
|
|
case Fields.ZeroPoint:
|
||
|
|
tokens.Add(ZeroPoint.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
|
break;
|
||
|
|
case Fields.ISOCode:
|
||
|
|
if (!string.IsNullOrWhiteSpace(ISOCode)) tokens.Add(ISOCode);
|
||
|
|
break;
|
||
|
|
default: throw new NotSupportedException("unknown CalibrationRecord field: " + field.ToString());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (var i = 0; i < tokens.Count; i++)
|
||
|
|
{
|
||
|
|
if (null == tokens[i])
|
||
|
|
{
|
||
|
|
tokens[i] = "";
|
||
|
|
}
|
||
|
|
|
||
|
|
tokens[i] = tokens[i].Replace(System.Globalization.CultureInfo.InvariantCulture.TextInfo.ListSeparator,
|
||
|
|
"x_Separator_x");
|
||
|
|
}
|
||
|
|
|
||
|
|
return string.Join(System.Globalization.CultureInfo.InvariantCulture.TextInfo.ListSeparator,
|
||
|
|
tokens.ToArray());
|
||
|
|
}
|
||
|
|
|
||
|
|
public string ToDisplayString(ISensorCalibration sc, ICalibrationRecord previous, ISensorCalibration sc2, string linearFormat, string nonlinearFormat)
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().ToArray();
|
||
|
|
foreach (var field in fields)
|
||
|
|
{
|
||
|
|
switch (field)
|
||
|
|
{
|
||
|
|
case Fields.AtCapacity:
|
||
|
|
if (null == previous || AtCapacity != previous.AtCapacity)
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_AtCapacity, AtCapacity);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.EngineeringUnits:
|
||
|
|
if (null == previous || false == EngineeringUnits.Equals(previous.EngineeringUnits))
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_EngineeringUnits, EngineeringUnits);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.Excitation:
|
||
|
|
if (null == previous || sc.IsProportional != sc2.IsProportional || Excitation != previous.Excitation)
|
||
|
|
{
|
||
|
|
if (sc.IsProportional)
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1:0.0}", Strings.Strings.SensorFields_Excitation,
|
||
|
|
GetExcitationVoltageMagnitudeFromEnum(Excitation));
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.Poly:
|
||
|
|
if (null == previous || sc.NonLinear || sc.NonLinear != sc2.NonLinear)
|
||
|
|
{
|
||
|
|
if (sc.NonLinear)
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_NonLinearFormat,
|
||
|
|
Poly.ToDisplayString(nonlinearFormat));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.Sensitivity:
|
||
|
|
if (null == previous || Sensitivity != previous.Sensitivity)
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_Sensitivity,
|
||
|
|
Sensitivity.ToString(linearFormat));
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.CapacityOutputIsBasedOn:
|
||
|
|
if (false == AtCapacity)
|
||
|
|
{
|
||
|
|
if (null == previous || CapacityOutputIsBasedOn != previous.CapacityOutputIsBasedOn)
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_CapacityOutputIsBasedOn,
|
||
|
|
CapacityOutputIsBasedOn);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.SensitivityUnits:
|
||
|
|
if (null == previous || false == SensitivityUnits.Equals(previous.SensitivityUnits))
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_SensitivityUnits, SensitivityUnits);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case Fields.ZeroPoint:
|
||
|
|
if (null == previous || ZeroPoint != previous.ZeroPoint)
|
||
|
|
{
|
||
|
|
if (sb.Length > 1)
|
||
|
|
{
|
||
|
|
sb.AppendLine();
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.AppendFormat("{0}: {1}", Strings.Strings.SensorFields_ZeroPoint, ZeroPoint);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
public bool IsEqual(object obj, ISensorCalibration sc)
|
||
|
|
{
|
||
|
|
if (obj is CalibrationRecord r)
|
||
|
|
{
|
||
|
|
return r.ToSerializedString(sc) == ToSerializedString(sc);
|
||
|
|
}
|
||
|
|
|
||
|
|
return base.Equals(obj);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void FromString(string s)
|
||
|
|
{
|
||
|
|
var tokens = s.Split(new string[] { CultureInfo.InvariantCulture.TextInfo.ListSeparator },
|
||
|
|
StringSplitOptions.None);
|
||
|
|
|
||
|
|
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().ToArray();
|
||
|
|
|
||
|
|
for (var i = 0; i < tokens.Length && i < fields.Length; i++)
|
||
|
|
{
|
||
|
|
var token = tokens[i].Replace("x_Separator_x",
|
||
|
|
CultureInfo.InvariantCulture.TextInfo.ListSeparator);
|
||
|
|
switch (fields[i])
|
||
|
|
{
|
||
|
|
case Fields.AtCapacity:
|
||
|
|
AtCapacity = Convert.ToBoolean(token);
|
||
|
|
break;
|
||
|
|
case Fields.EngineeringUnits:
|
||
|
|
EngineeringUnits = token;
|
||
|
|
break;
|
||
|
|
case Fields.Excitation:
|
||
|
|
if (Enum.TryParse(token, out ExcitationVoltageOptions.ExcitationVoltageOption excitation))
|
||
|
|
{
|
||
|
|
Excitation = excitation;
|
||
|
|
}
|
||
|
|
else { APILogger.Log($"failed to parse excitation: {token} from {s}"); }
|
||
|
|
break;
|
||
|
|
case Fields.Poly:
|
||
|
|
Poly = new LinearizationFormula();
|
||
|
|
Poly.FromSerializeString(token);
|
||
|
|
break;
|
||
|
|
case Fields.Sensitivity:
|
||
|
|
Sensitivity = Convert.ToDouble(token, System.Globalization.CultureInfo.InvariantCulture);
|
||
|
|
break;
|
||
|
|
case Fields.CapacityOutputIsBasedOn:
|
||
|
|
CapacityOutputIsBasedOn = Convert.ToDouble(token);
|
||
|
|
break;
|
||
|
|
case Fields.SensitivityUnits:
|
||
|
|
if (Enum.TryParse(token, out SensorConstants.SensUnits unit))
|
||
|
|
{
|
||
|
|
SensitivityUnits = unit;
|
||
|
|
}
|
||
|
|
else { APILogger.Log($"failed to parse sensitivity units: {token} from {s}"); }
|
||
|
|
break;
|
||
|
|
case Fields.ZeroPoint:
|
||
|
|
ZeroPoint = Convert.ToDouble(token, System.Globalization.CultureInfo.InvariantCulture);
|
||
|
|
break;
|
||
|
|
case Fields.ISOCode:
|
||
|
|
ISOCode = token;
|
||
|
|
break;
|
||
|
|
default: throw new NotSupportedException("unknown CalibrationRecord field: " + fields.ToArray());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
public CalibrationRecord(string s)
|
||
|
|
{
|
||
|
|
FromString(s);
|
||
|
|
}
|
||
|
|
|
||
|
|
public CalibrationRecord(ICalibrationRecord copy)
|
||
|
|
{
|
||
|
|
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().ToArray();
|
||
|
|
foreach (var field in fields)
|
||
|
|
{
|
||
|
|
switch (field)
|
||
|
|
{
|
||
|
|
case Fields.AtCapacity:
|
||
|
|
AtCapacity = copy.AtCapacity;
|
||
|
|
break;
|
||
|
|
case Fields.EngineeringUnits:
|
||
|
|
EngineeringUnits = copy.EngineeringUnits;
|
||
|
|
break;
|
||
|
|
case Fields.Excitation:
|
||
|
|
Excitation = copy.Excitation;
|
||
|
|
break;
|
||
|
|
case Fields.Poly:
|
||
|
|
Poly = new LinearizationFormula(copy.Poly);
|
||
|
|
break;
|
||
|
|
case Fields.Sensitivity:
|
||
|
|
Sensitivity = copy.Sensitivity;
|
||
|
|
break;
|
||
|
|
case Fields.CapacityOutputIsBasedOn:
|
||
|
|
CapacityOutputIsBasedOn = copy.CapacityOutputIsBasedOn;
|
||
|
|
break;
|
||
|
|
case Fields.SensitivityUnits:
|
||
|
|
SensitivityUnits = copy.SensitivityUnits;
|
||
|
|
break;
|
||
|
|
case Fields.ZeroPoint:
|
||
|
|
ZeroPoint = copy.ZeroPoint;
|
||
|
|
break;
|
||
|
|
case Fields.ISOCode:
|
||
|
|
ISOCode = copy.ISOCode;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
throw new NotSupportedException("unknown calibrationrecord field: " + field.ToString());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public CalibrationRecord()
|
||
|
|
{
|
||
|
|
Poly = new LinearizationFormula();
|
||
|
|
}
|
||
|
|
|
||
|
|
//helpers moved from DAS.Concepts
|
||
|
|
public static double GetExcitationVoltageMagnitudeFromEnum(ExcitationVoltageOptions.ExcitationVoltageOption target)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return new ExcitationVoltageOptions.VoltageMagnitudeAttributeCoder().DecodeAttributeValue(target);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem attempting to get excitation voltage magnitude from enum", ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static ExcitationVoltageOptions.ExcitationVoltageOption GetExcitationVoltageEnumFromMagnitude(double magnitude)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return new ExcitationVoltageOptions.VoltageMagnitudeAttributeCoder().EncodeAttributeValue(magnitude);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log("encountered problem attempting to get excitation voltage enum from magnitude", ex);
|
||
|
|
return ExcitationVoltageOptions.ExcitationVoltageOption.Undefined;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|