Files
laatzen/Common/Ui/GenesisToolBox/Infrastructure/TempMeterModels.cs
T
2023-08-07 09:33:16 +02:00

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