431 lines
16 KiB
C#
431 lines
16 KiB
C#
namespace Xylem.Common.Ui.GenesisToolBox.Infrastructure
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.IO.Pipes;
|
|
using System.Linq;
|
|
using System.Timers;
|
|
|
|
public class CordonelTempLut
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int PcbId { get; set; }
|
|
|
|
public double RefTempC { get; set; }
|
|
|
|
public double AvgTof { get; set; }
|
|
|
|
public bool Active { get; set; }
|
|
|
|
public DateTime? Timestamp { get; set; }
|
|
}
|
|
|
|
public class RefTempRange
|
|
{
|
|
public Double Min { get; set; }
|
|
public Double Max { get; set; }
|
|
}
|
|
|
|
public class LutTemp
|
|
{
|
|
public int PcbId { get; set; }
|
|
|
|
public int FromTempC { get; set; }
|
|
|
|
public int ToTempC { get; set; }
|
|
|
|
public double? AvgRefTempC { get; set; }
|
|
|
|
public ICollection<double> ListOfRecordedTofs { get; set; } = new List<double>();
|
|
|
|
public double? AvgTofs { get; set; }
|
|
}
|
|
|
|
internal enum ProgrammState
|
|
{
|
|
Start,
|
|
Connected,
|
|
RecordRuning,
|
|
RecordStoped,
|
|
RecodCalculated,
|
|
}
|
|
|
|
internal struct Messurement
|
|
{
|
|
public double T { get; set; }
|
|
|
|
public double TOF { get; set; }
|
|
|
|
public DateTime DTS { get; set; }
|
|
}
|
|
|
|
internal class AutomaticTemperatureCalibration
|
|
{
|
|
private readonly int messurementsCount;
|
|
private readonly Func<double> tofSource;
|
|
private readonly Timer temperatureWatcher;
|
|
private readonly Timer messurementWatcher;
|
|
private readonly ICollection<Messurement> messurements;
|
|
|
|
private int messurmentN;
|
|
private double referenceT;
|
|
private bool waitingChanges;
|
|
private int temperatureChecks;
|
|
private int temperatureErrors;
|
|
private StreamReader streamReader;
|
|
private NamedPipeClientStream pipeStream;
|
|
private DateTime? timestamp;
|
|
|
|
public AutomaticTemperatureCalibration(Func<double> tofSource)
|
|
{
|
|
this.tofSource = tofSource;
|
|
// Instead of time tracking,
|
|
this.messurementsCount = Appsettings.MessurementDauerInMs / Appsettings.MessurementStep;
|
|
|
|
this.temperatureWatcher = new Timer(Appsettings.TemperatureCheckInMs);
|
|
this.temperatureWatcher.Elapsed += this.CheckTemperature;
|
|
|
|
this.messurementWatcher = new Timer(Appsettings.MessurementStep);
|
|
this.messurementWatcher.Elapsed += this.Messure;
|
|
|
|
this.waitingChanges = true;
|
|
this.messurements = new List<Messurement>();
|
|
}
|
|
|
|
public event Action Start;
|
|
public event Action Stop;
|
|
public event Action<string> InfoMessage;
|
|
public event Action<string> ErrorMessage;
|
|
public event Action<string> WarningMessage;
|
|
public event Action<double, double, DateTime> TemperatureMesured;
|
|
|
|
public void StartMessuring()
|
|
{
|
|
this.Info(string.Empty);
|
|
this.Info($"================================");
|
|
this.Info($"[INFO] Starting messurements ...");
|
|
|
|
if (this.tofSource != null)
|
|
{
|
|
try
|
|
{
|
|
this.pipeStream = new NamedPipeClientStream(".", "TempPipe", PipeDirection.In);
|
|
this.pipeStream.Connect(3000);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
this.Error(e.Message);
|
|
this.Stop?.Invoke();
|
|
}
|
|
|
|
if (this.pipeStream.IsConnected)
|
|
{
|
|
this.streamReader = new StreamReader(this.pipeStream);
|
|
this.StopMessurementWatcher();
|
|
this.StartTemperatureWatcher();
|
|
|
|
this.Start?.Invoke();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Stop?.Invoke();
|
|
this.Info($"\tTOF source is missing.");
|
|
}
|
|
}
|
|
|
|
public void StopMessuring()
|
|
{
|
|
this.Info(string.Empty);
|
|
|
|
if (this.tofSource != null)
|
|
{
|
|
this.StopMessurementWatcher();
|
|
this.StopTemperatureWatcher();
|
|
this.streamReader.Dispose();
|
|
this.pipeStream.Close();
|
|
}
|
|
|
|
this.Stop?.Invoke();
|
|
this.Info($"[INFO] Messurements canceled.");
|
|
this.Info($"=============================");
|
|
this.Info(string.Empty);
|
|
}
|
|
|
|
private void Info(string message)
|
|
=> this.InfoMessage?.Invoke(message);
|
|
|
|
private void Warning(string message)
|
|
=> this.WarningMessage?.Invoke(message);
|
|
|
|
private void Error(string message)
|
|
=> this.ErrorMessage?.Invoke(message);
|
|
|
|
private void StartTemperatureWatcher()
|
|
{
|
|
this.temperatureWatcher.AutoReset = true;
|
|
this.temperatureWatcher.Enabled = true;
|
|
this.temperatureWatcher.Start();
|
|
}
|
|
|
|
private void StopTemperatureWatcher()
|
|
{
|
|
this.temperatureWatcher.Stop();
|
|
this.temperatureWatcher.Enabled = false;
|
|
this.temperatureWatcher.AutoReset = false;
|
|
}
|
|
|
|
private void StartMessurementWatcher()
|
|
{
|
|
this.messurmentN = 1;
|
|
this.messurementWatcher.AutoReset = true;
|
|
this.messurementWatcher.Enabled = true;
|
|
this.messurementWatcher.Start();
|
|
}
|
|
|
|
private void StopMessurementWatcher()
|
|
{
|
|
this.messurmentN = 1;
|
|
this.messurementWatcher.Stop();
|
|
this.messurementWatcher.Enabled = false;
|
|
this.messurementWatcher.AutoReset = false;
|
|
}
|
|
|
|
private void CheckTemperature(object sender, ElapsedEventArgs args)
|
|
{
|
|
// Default start number.
|
|
const int start = 1;
|
|
|
|
// If reading of the reference temperature succeeds.
|
|
if (this.TryGetReferenceT(out var referenceT))
|
|
{
|
|
// Calculating the absolute difference to the previouse temperature.
|
|
var differenceT = Math.Abs(this.referenceT - referenceT);
|
|
|
|
if (this.waitingChanges && differenceT < Appsettings.TemperatureCheckInC)
|
|
{
|
|
// Reset counter to default.
|
|
this.temperatureChecks = 0;
|
|
|
|
this.Warning($"[CHECK] Temperature is not changed {this.referenceT,5:0.00} <=> {referenceT,5:0.00}°C difference {differenceT,5:0.00}°C {args.SignalTime:hh:mm:ss.fff}ms");
|
|
|
|
if (this.timestamp.HasValue && Math.Abs((args.SignalTime - this.timestamp.Value).TotalMinutes) > Appsettings.ExitWhenNotChangedInMin)
|
|
{
|
|
this.StopMessuring();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
this.timestamp = args.SignalTime;
|
|
|
|
// If difference is greater or equal than configured, reset counter and start tracking the new temperature.
|
|
if (differenceT >= Appsettings.TemperatureCheckInC)
|
|
{
|
|
if (this.temperatureChecks == 0)
|
|
{
|
|
this.Info(string.Empty);
|
|
this.Info($"[CHECK] Start counting: {Appsettings.TemperatureChecksCount} times for {referenceT:0.00}°C +/-{Appsettings.MessurementToleranceInC:0.00}°C");
|
|
}
|
|
|
|
this.waitingChanges = false;
|
|
// Start counting from 1.
|
|
this.temperatureChecks = start;
|
|
// Saving current temperature for fuhrter comparisons.
|
|
this.referenceT = referenceT;
|
|
|
|
var t = differenceT == referenceT ? 0 : differenceT;
|
|
this.Info($"[CHECK] {this.temperatureChecks, 2}. {referenceT, 5:0.00}°C difference {t, 5:0.00}°C {args.SignalTime:hh:mm:ss.fff}ms");
|
|
}
|
|
// If tolerance is smaller or equal than configured,
|
|
else if (differenceT <= Appsettings.MessurementToleranceInC)
|
|
{
|
|
// the temperature is same also continue counting.
|
|
this.temperatureChecks++;
|
|
|
|
this.Info($"[CHECK] {this.temperatureChecks, 2}. {referenceT, 5:0.00}°C difference {differenceT, 5:0.00}°C {args.SignalTime:hh:mm:ss.fff}ms");
|
|
}
|
|
|
|
// If counter reaches configured counts then start messurement for configured time.
|
|
if (this.temperatureChecks == Appsettings.TemperatureChecksCount)
|
|
{
|
|
this.waitingChanges = true;
|
|
// Reset counter to default.
|
|
this.temperatureChecks = 0;
|
|
// Next call breaks current method execution.
|
|
this.StopTemperatureWatcher();
|
|
// Next call starts new messurement as configured.
|
|
this.StartMessurementWatcher();
|
|
|
|
this.Info(string.Empty);
|
|
this.Info($"[INFO] Starting messurement - {args.SignalTime: yyyy-MM-dd hh:mm:ss.ffff}, {Appsettings.MessurementStep}ms for {Appsettings.MessurementDauerInMs}ms");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.waitingChanges = true;
|
|
// Reset counter to default.
|
|
this.temperatureChecks = 0;
|
|
// Reset temperature to default.
|
|
this.referenceT = default(double);
|
|
|
|
this.Info(string.Empty);
|
|
this.Warning($"[WARNING] Reading reference temperature failed, resseting state.");
|
|
}
|
|
}
|
|
|
|
private void Messure(object sender, ElapsedEventArgs args)
|
|
{
|
|
// If reading of the reference temperature succeeds ...
|
|
if (this.TryGetReferenceT(out var referenceT))
|
|
{
|
|
var differenceT = Math.Abs(this.referenceT - referenceT);
|
|
|
|
// If the absolute difference is in expected tolerance.
|
|
if (differenceT <= Appsettings.MessurementToleranceInC)
|
|
{
|
|
this.referenceT = referenceT;
|
|
|
|
var currentTOF = this.tofSource();
|
|
var hasValidTOF = true;
|
|
|
|
this.Info($"[INFO] {this.messurmentN, 2}. {currentTOF, 19:F18}, {referenceT, 5:0.00}°C, +/-{differenceT, 5:0.00}°C, {args.SignalTime:hh:mm:ss.fff}ms");
|
|
|
|
// For some LINQ expressions the collection should have at least 1 item in it.
|
|
if (this.messurements.Any())
|
|
{
|
|
// For instance if the tolerance is 30% => 30 / 100 = 0.3
|
|
var tolerance = Appsettings.MessurementTOFTolerance / 100;
|
|
// Calculating min tolerance: (if TOF = 1) * (1.00 - 0.3 = 0.7) = 0.7 is the min tolerance.
|
|
var min = this.messurements.Min(x => x.TOF) * (1.00 - tolerance);
|
|
// Calculating max tolerance: (if TOF = 1) * (1.00 + 0.3 = 1.3) = 1.3 is the max tolerance.
|
|
var max = this.messurements.Max(x => x.TOF) * (1.00 + tolerance);
|
|
|
|
// Determinates wheter current TOF is between min and max tolerance
|
|
hasValidTOF = currentTOF >= min && currentTOF <= max;
|
|
|
|
if (!hasValidTOF)
|
|
{
|
|
this.Error($"[ERROR] Invalid TOF: {currentTOF}");
|
|
}
|
|
}
|
|
|
|
// When current TOF is between min and max tolerance
|
|
if (hasValidTOF)
|
|
{
|
|
// New messurement record is created and added to the messurements.
|
|
this.messurements.Add(new Messurement
|
|
{
|
|
T = referenceT,
|
|
TOF = currentTOF,
|
|
DTS = args.SignalTime
|
|
});
|
|
}
|
|
}
|
|
// Else the absolute difference was greater than expected.
|
|
else if (differenceT >= Appsettings.TemperatureCheckInC)
|
|
{
|
|
// Sets the messurements to the calculated count.
|
|
// This should breack current method execution in the next step.
|
|
this.messurmentN = this.messurementsCount;
|
|
|
|
this.Warning($"[WARNING] {referenceT:0.00}°C overstepped the tolerance of +/-{Appsettings.TemperatureCheckInC:0.00}.");
|
|
}
|
|
else
|
|
{
|
|
this.temperatureErrors++;
|
|
|
|
this.Warning($"[WARNING] {referenceT:0.00}°C overstepped the tolerance of +/-{Appsettings.MessurementToleranceInC:0.00}.");
|
|
this.Warning($"[WARNING] Next time current messurment will be canceled.");
|
|
}
|
|
|
|
// In each case counts 1 more.
|
|
this.messurmentN++;
|
|
}
|
|
else
|
|
{
|
|
this.Warning($"[WARNING] Reading reference temperature failed.");
|
|
}
|
|
|
|
// If the messurements count is reached.
|
|
if (this.messurmentN > this.messurementsCount || this.temperatureErrors > 2)
|
|
{
|
|
// Calculates the time difference between first and last messurement times.
|
|
var timespan = this.messurements.LastOrDefault().DTS - this.messurements.FirstOrDefault().DTS;
|
|
|
|
// If there are enough messurements, more than the half of the Appsettings.MessurementDauerInMs.
|
|
if (timespan.TotalMilliseconds > Appsettings.MessurementDauerInMs / 2)
|
|
{
|
|
// Average temperature is calculated.
|
|
var tmp = this.messurements.Average(x => x.T);
|
|
// Average times od flights is calculated.
|
|
var tof = this.messurements.Average(x => x.TOF);
|
|
|
|
this.Info($"[INFO] Data collected over {timespan}, sending average values ...");
|
|
|
|
// Notifies whit the average values and a timestamp that the messurement is completed.
|
|
this.TemperatureMesured?.Invoke(tmp, tof, args.SignalTime);
|
|
// All existing messurements are cleared.
|
|
this.messurements.Clear();
|
|
|
|
this.Info($"[SENT] {tmp, 5} {tof, 19} {args.SignalTime}");
|
|
}
|
|
else
|
|
{
|
|
this.Error($"[ERROR] {timespan.Milliseconds}ms <= {Appsettings.MessurementDauerInMs / 2}");
|
|
}
|
|
|
|
this.messurmentN = 1;
|
|
|
|
// In each case, when the messuremens count is reached current method execution should be stopped.
|
|
this.StopMessurementWatcher();
|
|
// Next call starts seeking for new temperature change.
|
|
this.StartTemperatureWatcher();
|
|
|
|
this.Info($"[INFO] Messurement is done.");
|
|
this.Info(string.Empty);
|
|
}
|
|
}
|
|
|
|
private bool TryGetReferenceT(out double referenceT)
|
|
{
|
|
referenceT = 0;
|
|
|
|
var succeeded = false;
|
|
var readAttempts = 1;
|
|
var value = string.Empty;
|
|
|
|
try
|
|
{
|
|
do
|
|
{
|
|
lock (this.streamReader)
|
|
{
|
|
value = this.streamReader.ReadLine();
|
|
}
|
|
|
|
if (double.TryParse(value, NumberStyles.Any, new CultureInfo("de"), out referenceT)
|
|
|| double.TryParse(value, NumberStyles.Any, new CultureInfo("us"), out referenceT))
|
|
{
|
|
succeeded = true;
|
|
|
|
break;
|
|
}
|
|
|
|
System.Threading.Thread.Sleep(10);
|
|
|
|
} while (readAttempts++ <= 1000);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
this.Error(e.Message);
|
|
}
|
|
|
|
return succeeded;
|
|
}
|
|
}
|
|
}
|