laatzen/Common/Ui/GenesisToolBox/Infrastructure/TempMeterModels.cs
Stoyan Zlatev e4fab4a491 errors fix
2024-02-09 08:49:32 +01:00

721 lines
25 KiB
C#

namespace Xylem.Common.Ui.GenesisToolBox.Infrastructure
{
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Timers;
using System.Windows;
using SystemThreadingThread = System.Threading.Thread;
internal interface ITempMeterForm
{
void AddMessage(string message);
double AvgTOF();
IAsyncResult BeginInvoke(Delegate @delegate);
void StartRecording();
}
partial class AutomaticTemperatureCalibration
{
private const string BASE_ADDRESS = "http://sla12iis01.emea.sensus.net/LaaProductionWeb";
// private const string BASE_ADDRESS = "http://localhost:52822/LaaProductionWeb";
private const string WORKING_DIR = @"temperature_calibrations";
private const string MESSUREMENTS_FILE = @"_messurements.txt";
private const string AGGREGATIONS_FILE = @"_aggregations.txt";
private const string BACKWARDS_FILE = @"_backwards.txt";
private readonly string pcbId;
private readonly string messurementsFile;
private readonly string aggregationsFile;
private readonly string backwardsFile;
private readonly ITempMeterForm tempMeterForm;
private TemperatureSource temperatureSource;
private Timer longTimer;
private Timer shortTimer;
private Timer stopTimer;
private bool messuring;
public AutomaticTemperatureCalibration(string pcbId, ITempMeterForm genesisMeter)
{
this.pcbId = pcbId;
this.tempMeterForm = genesisMeter;
if (int.TryParse($"{this.pcbId}", out int pcb))
{
this.messurementsFile = this.GetOrCreateFile(this.pcbId, MESSUREMENTS_FILE);
this.aggregationsFile = this.GetOrCreateFile(this.pcbId, AGGREGATIONS_FILE);
this.backwardsFile = this.GetOrCreateFile(this.pcbId, BACKWARDS_FILE);
}
this.temperatureSource = new TemperatureSource();
}
public void StartMessuring()
{
this.messuring = false;
this.temperatureSource = new TemperatureSource();
this.shortTimer = new Timer(Appsettings.MessurementStepMs)
{
Enabled = true,
AutoReset = true
};
this.shortTimer.Elapsed += this.ShortTimerElapsed;
this.shortTimer.Stop();
this.longTimer = new Timer(Appsettings.MessurementDauerMs)
{
Enabled = true,
AutoReset = true
};
this.longTimer.Elapsed += this.LongTimerElapsed;
this.LongTimerElapsed(null, null);
this.longTimer.Start();
if (Appsettings.MessurementStopHours > 0)
{
this.stopTimer = new Timer(Appsettings.MessurementStopHours * 60 * 60 * 1000)
{
Enabled = true,
AutoReset = true
};
this.stopTimer.Elapsed += (sender, args) => this.StopMessuring();
this.stopTimer.Start();
}
File.AppendAllText(this.aggregationsFile, Environment.NewLine);
}
public void StopMessuring()
{
this.shortTimer?.Stop();
this.shortTimer?.Dispose();
this.shortTimer = null;
this.longTimer?.Stop();
this.longTimer?.Dispose();
this.longTimer = null;
this.stopTimer?.Stop();
this.stopTimer?.Dispose();
this.stopTimer = null;
this.temperatureSource?.Dispose();
this.temperatureSource = null;
}
private void LongTimerElapsed(Object sender, ElapsedEventArgs _)
{
this.messuring = !this.messuring;
SystemThreadingThread.Sleep(1_000);
if (this.messuring)
{
this.shortTimer.Start();
}
else
{
this.shortTimer.Stop();
this.ParseMessurements();
}
}
private void ShortTimerElapsed(Object sender, ElapsedEventArgs args)
{
lock (this.shortTimer)
{
this.tempMeterForm.BeginInvoke(new Action(() =>
{
var messurement = !this.TryGetTOFandT(out double currentTOF, out double currentT)
? $"[{args.SignalTime:yyyy-MM-dd hh:mm:ss}]\t invalid record (TOF: {currentTOF} & T: {currentT})"
: $"[{args.SignalTime:yyyy-MM-dd hh:mm:ss}]\t{this.pcbId,9}\t{currentT,5:00.00}\t{currentTOF,14:0.000000000000}";
if (this.messuring)
{
this.tempMeterForm.AddMessage(messurement);
File.AppendAllLines(this.messurementsFile, new[] { messurement });
}
}));
}
}
private String GetOrCreateFile(string pcbId, string fileBase)
{
Directory.CreateDirectory(WORKING_DIR);
var filePath = Path.Combine(".", WORKING_DIR, $"{pcbId}{fileBase}");
if (!File.Exists(filePath))
{
File.WriteAllText(filePath, string.Empty);
}
return filePath;
}
private void ParseMessurements()
{
var messurementLines = File.ReadAllLines(this.messurementsFile);
var messurements = new Messurements(this.pcbId);
var skipCount = 2;
File.WriteAllText(this.messurementsFile, string.Empty);
foreach (var messurementLine in messurementLines)
{
if (string.IsNullOrWhiteSpace(messurementLine))
{
continue;
}
if (skipCount > 0)
{
skipCount--;
continue;
}
var tokens = messurementLine
.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length != 4)
{
continue;
}
var dateString = tokens[0];
var pcbString = tokens[1];
var tString = tokens[2];
var tofString = tokens[3];
var currentT = default(double);
var currentTOF = default(double);
try
{
var tParsed = tString.TryParse(out currentT);
var tofParsed = tofString.TryParse(out currentTOF);
if (!tParsed || !tofParsed)
{
continue;
}
}
catch
{
continue;
}
messurements.Add(currentT, currentTOF);
}
File.AppendAllLines(this.aggregationsFile, new[] { messurements.ToString() });
}
internal IEnumerable<Messurement> AggregationsFor(string pcb)
{
var aggregations = new List<Messurement>();
var aggregationsFile = $"{WORKING_DIR}\\{pcb}{AGGREGATIONS_FILE}";
var existingAggregations = this.RequestAggregations(pcb);
var aggregationsLines = new List<string>();
if (File.Exists(aggregationsFile))
{
aggregationsLines = File
.ReadAllLines(aggregationsFile)
.Skip(1)
.ToList();
}
if (aggregationsLines.Count <= 0)
{
aggregations = existingAggregations.ToList();
aggregations.ForEach(x => x.Selected = true);
}
foreach (var line in aggregationsLines)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var tokens = line.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length != 10)
{
continue;
}
var pcbString = tokens[0] ?? string.Empty;
var avgTString = tokens[3] ?? string.Empty;
var difTString = tokens[7] ?? string.Empty;
var avgTOFString = tokens[4] ?? string.Empty;
var difTOFString = tokens[8] ?? string.Empty;
var secondsString = tokens[9] ?? string.Empty;
var pcbId = default(int);
var avgT = default(double);
var difT = default(double);
var avgTOF = default(double);
var difTOF = default(double);
var seconds = default(int);
var pcbParsed = int.TryParse(pcbString, out pcbId);
var tParsed = avgTString.TryParse(out avgT);
var dtParsed = difTString.TryParse(out difT);
var secParsed = int.TryParse(secondsString, out seconds) || int.TryParse(secondsString, out seconds);
var tofParsed = avgTOFString.TryParse(out avgTOF);
var dtofParsed = difTOFString.TryParse(out difTOF);
if (!pcbParsed || !tParsed || !tofParsed || !dtParsed || !dtofParsed)
{
continue;
}
aggregations.Add(new Messurement
{
PcbId = pcbId,
AvgT = avgT,
DiffT = difT,
AvgTOF = avgTOF,
DiffTOF = difTOF,
Seconds = seconds,
Selected = existingAggregations.Any(x => x.AvgT == avgT)
});
}
return aggregations
.OrderBy(x => x.PcbId)
.ThenBy(x => x.AvgT)
.ThenBy(x => x.AvgTOF)
.Select((messurement, index) =>
{
messurement.Id = index + 1;
return messurement;
})
.ToList();
}
internal IEnumerable<Messurement> RequestAggregations(string pcb)
{
var aggregations = Array.Empty<Messurement>();
try
{
using (var httpClient = new HttpClient())
{
using (var httpRequestMessage = new HttpRequestMessage())
{
httpRequestMessage.Method = HttpMethod.Get;
httpRequestMessage.Headers.Authorization = Software.AuthorizationHeader;
httpRequestMessage.RequestUri = new Uri($"{BASE_ADDRESS}/API/Production/TempMeter/{pcb}");
using (var httpResponseMessage = httpClient.SendAsync(httpRequestMessage).Result)
{
if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
{
var content = httpResponseMessage
.Content
.ReadAsStringAsync()
.Result;
aggregations = JsonConvert.DeserializeObject<Messurement[]>($"{content}");
}
else
{
MessageBox.Show($"HTTP! {(int)httpResponseMessage.StatusCode} - {httpResponseMessage.ReasonPhrase}.");
}
}
}
}
}
catch (Exception e)
{
MessageBox.Show($"Failed! {e.Message}");
}
return aggregations;
}
internal async void UpdateAggregations(IEnumerable<Messurement> messurements)
{
var message = "Succeeded!";
try
{
using (var httpClient = new HttpClient())
{
using (var httpRequestMessage = new HttpRequestMessage())
{
httpRequestMessage.Method = HttpMethod.Post;
httpRequestMessage.Headers.Authorization = Software.AuthorizationHeader;
httpRequestMessage.RequestUri = new Uri($"{BASE_ADDRESS}/API/Production/TempMeter/Update");
httpRequestMessage.Content = new StringContent(JsonConvert.SerializeObject(messurements));
httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage))
{
if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
{
var added = await httpResponseMessage.Content.ReadAsStringAsync();
message = $"Succeeded! {added} records added.";
}
else if (httpResponseMessage.StatusCode == HttpStatusCode.NotModified)
{
message = $"Failed! No messurements to add.";
}
else if (httpResponseMessage.StatusCode == HttpStatusCode.Conflict)
{
message = $"Failed! More than 1 pcb's sent.";
}
else
{
message = $"HTTP! {(int)httpResponseMessage.StatusCode} - {httpResponseMessage.ReasonPhrase}.";
}
}
}
}
}
catch (Exception e)
{
message = $"Failed! {e.Message}";
}
MessageBox.Show(message);
}
internal IEnumerable<string> AvailableAggregationPcbs()
{
var existingPcbs = this.GetAllPcbs();
var localPcbs = Directory
.CreateDirectory(WORKING_DIR)
.GetFiles($"*{AGGREGATIONS_FILE}")
.Select(x => x.Name
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
.FirstOrDefault());
return existingPcbs
.Union(localPcbs)
.OrderBy(x => x)
.ToHashSet();
}
private IEnumerable<string> GetAllPcbs()
{
var pcbs = Array.Empty<string>();
try
{
using (var httpClient = new HttpClient())
{
using (var httpRequestMessage = new HttpRequestMessage())
{
httpRequestMessage.Method = HttpMethod.Get;
httpRequestMessage.Headers.Authorization = Software.AuthorizationHeader;
httpRequestMessage.RequestUri = new Uri($"{BASE_ADDRESS}/API/Production/TempMeter/Pcbs");
using (var httpResponseMessage = httpClient.SendAsync(httpRequestMessage)
.ConfigureAwait(true)
.GetAwaiter()
.GetResult())
{
if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
{
var content = httpResponseMessage
.Content
.ReadAsStringAsync()
.ConfigureAwait(true)
.GetAwaiter()
.GetResult();
pcbs = JsonConvert
.DeserializeObject<int[]>($"{content}")
?.Select(x => x.ToString())
?.ToArray()
?? pcbs;
}
else
{
MessageBox.Show($"HTTP! {(int)httpResponseMessage.StatusCode} - {httpResponseMessage.ReasonPhrase}.");
}
}
}
}
}
catch (Exception e)
{
MessageBox.Show($"Failed! {e.Message}");
}
return pcbs;
}
internal void StartBackward()
{
this.temperatureSource = new TemperatureSource();
this.shortTimer = new Timer(Appsettings.BackwardsStepMs)
{
Enabled = true,
AutoReset = true
};
this.shortTimer.Elapsed += this.BackwardsElapsed;
this.shortTimer.Start();
}
private void BackwardsElapsed(object sender, ElapsedEventArgs args)
{
this.tempMeterForm.BeginInvoke(new Action(() =>
{
var messurement = !this.TryGetTOFandT(out double currentTOF, out double currentT)
? $"[{args.SignalTime:yyyy-MM-dd hh:mm:ss}]\t invalid record (TOF: {currentTOF} & T: {currentT})"
: $"[{args.SignalTime:yyyy-MM-dd hh:mm:ss}]\t{this.pcbId,9}\t{currentT,5:00.00}\t{currentTOF,14:0.000000000000}";
this.tempMeterForm.AddMessage(messurement);
File.AppendAllLines(this.backwardsFile, new[] { messurement });
}));
}
private bool TryGetTOFandT(out double actualTOF, out double actualT)
{
actualTOF = this.tempMeterForm.AvgTOF();
actualT = this.temperatureSource.GetTemperature();
this.tempMeterForm.StartRecording();
if (actualTOF >= 1 || actualT <= double.MinValue)
{
return false;
}
return true;
}
internal IEnumerable<string> AvailableCheckPcbs()
=> Directory
.CreateDirectory(WORKING_DIR)
.GetFiles($"*{BACKWARDS_FILE}")
.Select(x => x.Name
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
.FirstOrDefault())
.OrderBy(x => x)
.ToHashSet();
internal IEnumerable<InterpolationResult> TemperatureChecksFor(String pcb)
{
var backwardsFile = Path.Combine(".", WORKING_DIR, $"{pcb}{BACKWARDS_FILE}");
if (!File.Exists(backwardsFile))
{
return Array.Empty<InterpolationResult>();
}
var backwardsMessurements = new List<Messurement>();
foreach (var line in File.ReadAllLines(backwardsFile))
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var tokens = line.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length != 4)
{
continue;
}
var pcbString = tokens[1] ?? string.Empty;
var avgTString = tokens[2] ?? string.Empty;
var avgTOFString = tokens[3] ?? string.Empty;
var pcbId = default(int);
var avgT = default(double);
var avgTOF = default(double);
var pcbParsed = int.TryParse(pcbString, out pcbId);
var tParsed = avgTString.TryParse(out avgT);
var tofParsed = avgTOFString.TryParse(out avgTOF);
if (!pcbParsed || !tParsed || !tofParsed)
{
continue;
}
backwardsMessurements.Add(new Messurement
{
PcbId = pcbId,
AvgT = avgT,
AvgTOF = avgTOF
});
}
var localAggregations = this.AggregationsFor(pcb) ?? Array.Empty<Messurement>();
var temperatureLUT = localAggregations.OrderBy(x => x.AvgTOF).ToArray();
var temperatureChecks = new List<InterpolationResult>();
foreach (var check in backwardsMessurements)
{
var interpolatedResult = this.InterpolateTOF2T(check.AvgTOF, temperatureLUT);
interpolatedResult.PcbId = check.PcbId;
interpolatedResult.CurrentT = check.AvgT;
temperatureChecks.Add(interpolatedResult);
}
return temperatureChecks;
}
private InterpolationResult InterpolateTOF2T(double currentTOF, params Messurement[] temperatureLUT)
{
var interpolationResult = new InterpolationResult
{
CalculatedT = 1,
CurrentTOF = currentTOF
};
var length = temperatureLUT?.Length ?? 0;
var last = length - 1;
if (last <= 0 || temperatureLUT[0].AvgTOF > currentTOF || temperatureLUT[last].AvgTOF < currentTOF)
{
return interpolationResult;
}
var prevTOF = default(double);
var prevT = default(double);
var nextTOF = default(double);
var nextT = default(double);
for (int i = 1; i < length; i++)
{
if (temperatureLUT[i].AvgTOF >= currentTOF)
{
prevTOF = temperatureLUT[i - 1].AvgTOF;
prevT = temperatureLUT[i - 1].AvgT;
nextTOF = temperatureLUT[i].AvgTOF;
nextT = temperatureLUT[i].AvgT;
break;
}
}
var currentT = (nextT - prevT) / (nextTOF - prevTOF) * (currentTOF - prevTOF) + prevT;
interpolationResult.CalculatedT = currentT;
interpolationResult.PrevTOF = prevTOF;
interpolationResult.PrevT = prevT;
interpolationResult.NextTOF = nextTOF;
interpolationResult.NextT = nextT;
return interpolationResult;
}
}
class InterpolationResult
{
public int PcbId { get; set; }
public double PrevT { get; set; }
public double PrevTOF { get; set; }
public double CurrentT { get; set; }
public double CalculatedT { get; set; }
public double CurrentTOF { get; set; }
public double NextT { get; set; }
public double NextTOF { get; set; }
public double DiffT => Math.Abs(this.CurrentT - this.CalculatedT);
}
class Messurement
{
public int PcbId { get; set; }
public int Id { get; set; }
public double AvgT { get; set; }
public double AvgTOF { get; set; }
public double DiffT { get; set; }
public double DiffTOF { get; set; }
public int Seconds { get; set; }
public bool Selected { get; set; }
}
class Messurements
{
private readonly string pcbId;
// _______________________________ t _____ tof _________________
private readonly ICollection<Tuple<double, double>> messurements
= new List<Tuple<double, double>>();
public Messurements(string pcbId)
=> this.pcbId = pcbId;
public double MinT => this.messurements.Min(x => x.Item1);
public double MinTOF => this.messurements.Min(x => x.Item2);
public double AvgT => this.messurements.Average(x => x.Item1);
public double AvgTOF => this.messurements.Average(x => x.Item2);
public double MaxT => this.messurements.Max(x => x.Item1);
public double MaxTOF => this.messurements.Max(x => x.Item2);
public double DifT => Math.Abs(this.MinT - this.MaxT);
public double DifTOF => Math.Abs(this.MinTOF - this.MaxTOF);
public int Count => this.messurements.Count;
public override String ToString()
=> $"{this.pcbId}"
+ $"\t{this.MinT:0.00}"
+ $"\t{this.MinTOF:0.0000000000}"
+ $"\t{this.AvgT:0.00}"
+ $"\t{this.AvgTOF:0.0000000000}"
+ $"\t{this.MaxT:0.00}"
+ $"\t{this.MaxTOF:0.0000000000}"
+ $"\t{this.DifT:0.00}"
+ $"\t{this.DifTOF:0.0000000000}"
+ $"\t{this.Count}";
public void Add(double t, double tof)
=> this.messurements.Add(new Tuple<double, double>(t, tof));
public static string Header
=> $"Pcb Id\tMin °C\tMin TOF\tAvg °C\tAvg TOF\tMax °C\tMax TOF\tDif °C\tDif TOF\tCount";
}
}