Files
laatzen/Common/OmniPlus/OmniPlusConnectionsPool.cs
T

552 lines
20 KiB
C#

using OmniPlus.Enums;
using OmniPlus.Features;
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using static OmniPlus.OmniPlusConstants;
namespace OmniPlus
{
public class OmniPlusConnectionsPool : IDisposable
{
public event Action Done;
public event OmniPlusStateChanged StateChanged;
private readonly ConcurrentDictionary<int, OmniPlusConnection> connections;
private readonly ConcurrentDictionary<int, OmniPlusMeter> meters;
private readonly CancellationTokenSource cancellationTokenSource;
private readonly CancellationToken cancellationToken;
private readonly SignalRConnection signalRConnection;
private readonly OmniPlusDatabase database;
private readonly OmniPlusType omniType;
private byte size;
private string variant;
private BackgroundWorker background;
public OmniPlusConnectionsPool(string dbConnectionString, string signalRURL)
{
this.connections = new ConcurrentDictionary<int, OmniPlusConnection>();
this.meters = new ConcurrentDictionary<int, OmniPlusMeter>();
this.cancellationTokenSource = new CancellationTokenSource();
this.signalRConnection = new SignalRConnection(signalRURL);
this.database = new OmniPlusDatabase(dbConnectionString);
this.omniType = new OmniPlusType();
this.size = 5;
this.variant = "C2";
this.cancellationToken = this.cancellationTokenSource.Token;
this.cancellationToken.Register(this.CancellationCallback);
}
public void Dispose()
{
this.signalRConnection.Dispose();
// Disposes all OmniPlus connections.
foreach (var connectionKVP in this.connections)
{
connectionKVP.Value?.Disconnect();
this.StateChanged?.Invoke(connectionKVP.Key, -1, CONNECTED, X);
}
// Clears collected connections and their meters.
this.connections.Clear();
this.meters.Clear();
// Releases all connected event handlers to the StateChanged event.
if (this.StateChanged != null)
{
foreach (var callback in this.StateChanged.GetInvocationList())
{
if (callback is OmniPlusStateChanged stateChangedCallback)
{
this.StateChanged -= stateChangedCallback;
}
}
}
// TODO: Save state.!?
}
public void InitializeInspection(params string[] portNames)
{
this.signalRConnection.Connect();
OmniPlusConnection connection;
OmniPlusMeter meter;
int position = 1;
bool connected;
foreach (var portName in portNames)
{
connection = new OmniPlusConnection(portName);
connected = connection.ConnectIrda();
if (connected)
{
meter = new OmniPlusMeter
{
Connected = connected
};
}
else
{
connection.Disconnect();
connection = default(OmniPlusConnection);
meter = default(OmniPlusMeter);
}
this.meters.TryAdd(position, meter);
this.connections.TryAdd(position, connection);
this.StateChanged?.Invoke(position, 1, SLOT_NR, position.ToString());
this.StateChanged?.Invoke(position, connected ? 1 : -1, CONNECTED, connected ? OK : null);
position++;
}
this.database.LoadOmniPlusType(this.size, this.variant, this.omniType);
}
public void CancelExecution()
=> this.cancellationTokenSource.Cancel(false);
//public void StartInspection()
//{
// foreach (var position in this.connections.Keys)
// {
// this.StartOmniInitialization(position);
// //foreach (var point in this.omniType.TestPoints)
// //{
// // this.StartOmniInspection(position, point);
// //}
// //this.CalculateCorrectionFactor(position);
// this.FinishOmniInspection(position);
// }
//}
public void StartInspection()
{
this.background = new BackgroundWorker();
this.background.DoWork += this.StartInspection;
this.background.RunWorkerAsync();
}
private void StartInspection(object sender, EventArgs args)
{
var connectionsCount = this.connections.Count;
var tasks = new Task[connectionsCount];
foreach (var position in this.connections.Keys)
{
tasks[position - 1] = Task.Factory.StartNew(() => this.StartOmniInitialization(position), this.cancellationToken);
}
tasks.ContinueWhenAll();
//foreach (var point in this.omniType.TestPoints)
//{
// // TODO: 600 = 10min * 60sec - change for production.
// if (!this.signalRConnection.SetQAndWaitUntilReached(point.VolumeM3H))
// {
// this.CancelExecution();
// break;
// }
// tasks = new Task[connectionsCount];
// foreach (var position in this.connections.Keys)
// {
// tasks[position - 1] = Task.Factory.StartNew(() => this.StartOmniInspection(position, point), this.cancellationToken);
// }
// tasks.ContinueWhenAll();
// var volume = this.signalRConnection.RequestRefV();
// foreach (var position in this.connections.Keys)
// {
// this.SetPointDifference(position, point, volume);
// }
//}
foreach (var position in this.connections.Keys)
{
this.CalculateCorrectionFactor(position);
}
tasks = new Task[connectionsCount];
foreach (var position in this.connections.Keys)
{
tasks[position - 1] = Task.Factory.StartNew(() => this.FinishOmniInspection(position), this.cancellationToken);
}
tasks.ContinueWhenAll();
this.Done?.Invoke();
}
private void StartOmniInitialization(int position)
{
if (this.TryGetOmni(position, out var connection, out var meter))
{
this.StateChanged?.Invoke(position, 0, FIRMWARE, TBD);
this.StateChanged?.Invoke(position, 0, BUILD_INFO, TBD);
var systemParameters = connection.ViewSystemParameters();
meter.AddSystemParameters(systemParameters);
if (meter.Initialized)
{
this.database.AddOrUpdate(position, systemParameters);
}
meter.CanCalibrate = connection.Set(this.omniType.InputVolume).Succeeded;
this.StateChanged?.Invoke(position, meter.FirmwareInRange ? 1 : -1, FIRMWARE, meter.Firmware);
this.StateChanged?.Invoke(position, !string.IsNullOrWhiteSpace(meter.BuildInfo) ? 1 : -1, BUILD_INFO, meter.BuildInfo);
}
else
{
this.StateChanged?.Invoke(position, 0, FIRMWARE, null);
this.StateChanged?.Invoke(position, 0, BUILD_INFO, null);
}
}
private void StartOmniInspection(int position, OmniPlusTestPoint point)
{
var header = $"{point.VolumeM3H:0.0000}m³/h";
if (this.TryGetOmni(position, out var connection, out var meter) /*&& meter.Initialized && meter.CanCalibrate*/)
{
this.StateChanged?.Invoke(position, 0, header, TBD);
var activationResponse = connection.StartChamberCalibration(point.Rotations, point.Duration);
if (activationResponse.Succeeded)
{
var calibrationResult = new ChamberCalibrationResponse();
var duration = point.Duration + 3;
this.NotifyWhileThreadSleep(position, header, point.Duration + 3);
for (int i = 0; i < 3; i++)
{
calibrationResult = connection.ViewChamberCalibration();
if (calibrationResult.Succeeded)
{
break;
}
Thread.Sleep(1000);
}
if (!calibrationResult.Succeeded)
{
this.StateChanged?.Invoke(position, -1, header, $"CC: {calibrationResult.Status.ToString()}");
}
else if (calibrationResult.Control != ChamberCalibrationResponse.TEST_COMPLETED)
{
var control = calibrationResult.Control == ChamberCalibrationResponse.TEST_ACTIVATED
? nameof(ChamberCalibrationResponse.TEST_ACTIVATED)
: nameof(ChamberCalibrationResponse.TEST_FAILED);
this.StateChanged?.Invoke(position, -1, header, $"CC: {control}");
}
else // process calibration
{
var volume = meter.AddCalibrationResult(point, calibrationResult);
this.StateChanged?.Invoke(position, 0, header, $"{volume:0.0000}m³");
}
}
else // activation failed
{
this.StateChanged?.Invoke(position, -1, header, $"AC: {activationResponse.Status}");
}
}
else // not connected
{
this.StateChanged?.Invoke(position, 0, header, null);
}
}
private void SetPointDifference(int position, OmniPlusTestPoint point, double volume)
{
var header = $"{point.VolumeM3H:0.0000}m³/h";
if (this.TryGetOmni(position, out var _, out var meter) && meter.Initialized && meter.CanCalibrate)
{
var difference = meter.AddCalibrationVolume(point, volume);
this.StateChanged?.Invoke(position, 0, header, $"{difference:0.00}%");
}
else // not connected
{
this.StateChanged?.Invoke(position, 0, header, null);
}
}
private void CalculateCorrectionFactor(int position)
{
if (this.TryGetOmni(position, out var _, out var meter) && meter.Initialized && meter.CanCalibrate)
{
var correctionFactor = meter.GetCorrectionFactor();
this.StateChanged?.Invoke(position, meter.CanCalibrate ? 1 : -1, CORRECTION, $"{correctionFactor:0.00}%");
}
else // not connected
{
this.StateChanged?.Invoke(position, 0, CORRECTION, null);
}
}
private void FinishOmniInspection(int position)
{
if (this.TryGetOmni(position, out var connection, out var meter)/* && meter.Calibrated*/)
{
var configurationParameters = connection.ViewConfigurationParameters();
var canContinue = configurationParameters.Succeeded;
if (canContinue)
{
meter.AddConfigurationParameters(configurationParameters);
this.database.AddOrUpdate(position, configurationParameters);
}
if (canContinue)
{
// TODO: Uncomment in production
//var systemTimeReset = connection.SetSystemTime();
//canContinue = systemTimeReset.Succeeded;
}
if (canContinue)
{
var registerSettings = new RegisterSettings();
registerSettings.IntPart = this.omniType.IntPart;
registerSettings.Fraction = this.omniType.Fraction;
registerSettings.Size = this.omniType.Size;
registerSettings.Range = this.omniType.Range;
registerSettings.VolumePerPulseUnit = 0x00;
registerSettings.MeterUnits = (byte)MessurementUnits.USGallons;
registerSettings.FlowUnits = (byte)MessurementUnits.USGallons;
registerSettings.FlowRate = (byte)TimeUnits.Minutes;
registerSettings.FactoryCorrection = meter.FactoryCorrection;
registerSettings.FieldCorrection = 0x00;
registerSettings.PulseWeight = 0x01;
registerSettings.PulseWidth = 0x02;
registerSettings.PulseUpdate = 0x00;
var response = connection.Set(registerSettings);
canContinue = response.Succeeded;
if (canContinue)
{
meter.AddRegisterSettings(registerSettings);
}
}
if (canContinue)
{
var configurationParametersSet = new ConfigurationParametersSet();
configurationParametersSet.ProgrammableID = "816232411983";
configurationParametersSet.FactoryID = "816232411983";
configurationParametersSet.NumberOfReadingDigits = 8; // meter.ReadingDigits;
configurationParametersSet.ReadingPreset = "000000000";
configurationParametersSet.OptionalUniDirFields = 1; // meter.OptionalUniDirFields;
configurationParametersSet.AlarmPercistancePeriodDays = 31;
configurationParametersSet.DataLogIntervalMinutes = 60;
configurationParametersSet.ManifacturingTime = Epoch20000101.UTCNow - 30000; // meter.ManifacturingTime;
configurationParametersSet.SystemTime = Epoch20000101.UTCNow;
configurationParametersSet.RebootCount = 0;
configurationParametersSet.BuildInfo = "816232411983"; // meter.BuildInfo;
// configurationParametersSet.DeviceID = "OMNI-01";
var response = connection.Set(configurationParametersSet);
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.ClearDisplay();
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.Set(new LeakAlarm
{
FlowRateGal = 0.25F, // 0.25 gpm
TimeLimit = 24 * 60 * 60 // 24 hours => 86400sec
});
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.Set(new HighFlowAlarm
{
FlowRateGal = 25F, // 25 gpm
TimeLimit = 1 * 60 * 60 // 1 hours => 86400sec
});
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.Set(new ReverseFlowAlarm
{
FlowRateGal = -1F, // -1 gpm
TimeLimit = 60 // 1 hours => 86400sec
});
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.ClearAlarms();
canContinue = response.Succeeded;
}
if (canContinue)
{
var alarmsState = connection.ViewAlarmsState();
canContinue = alarmsState.Succeeded;
//&& alarmsState.UsrEnabled
//&& alarmsState.PreEnabledInactive;
//TODO: Check alarms
}
if (canContinue)
{
var response = connection.EnableNFC();
canContinue = response.Succeeded;
}
if (canContinue)
{
var registerSettingsAfter = connection.ViewRegisterSettings();
canContinue = registerSettingsAfter.Succeeded
&& meter.Compare(registerSettingsAfter);
}
if (canContinue)
{
var systemParametersAfter = connection.ViewSystemParameters();
canContinue = systemParametersAfter.Succeeded;
//&& meter.Compare(systemParametersAfter);
}
if (canContinue)
{
var configurationParametersAfter = connection.ViewConfigurationParameters();
canContinue = configurationParametersAfter.Succeeded;
//&& meter.Compare(configurationParametersAfter);
}
if (canContinue)
{
var alarmsState = connection.ViewAlarmsState();
canContinue = alarmsState.Succeeded;
//&& alarmsState.UsrEnabled
//&& alarmsState.PreEnabledInactive;
}
if (canContinue)
{
var response = connection.ViewAlarmSettings(0x01);
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.ViewAlarmSettings(0x02);
canContinue = response.Succeeded;
}
if (canContinue)
{
var response = connection.ViewAlarmSettings(0x04);
canContinue = response.Succeeded;
}
if (canContinue)
{
//connection.Set(new FactorySeal
//{
// Sealed = true,
//});
//var factorySeal = connection.ViewFactorySeal();
//canContinue = factorySeal.Succeeded && factorySeal.Sealed;
}
}
else // meter or connection not in use
{
}
}
/// <summary>
/// Returns only connected connections and meters.
/// </summary>
/// <param name="position">The position of connection and meter.</param>
/// <param name="connection"><see cref="OmniPlusConnection"/></param>
/// <param name="meter"><see cref="OmniPlusMeter"/></param>
/// <returns>Returns true if connection and meter are found and are not null and the meter is connected, otherwise false.</returns>
private bool TryGetOmni(int position, out OmniPlusConnection connection, out OmniPlusMeter meter)
{
var connectionFound = this.connections.TryGetValue(position, out connection);
var meterFound = this.meters.TryGetValue(position, out meter);
return connectionFound
&& connection != null
&& meterFound
&& meter != null
&& meter.Connected;
}
private void NotifyWhileThreadSleep(int position, string header, int duration)
{
var seconds = 0;
var timer = new System.Timers.Timer(1000)
{
Enabled = true,
AutoReset = true,
};
timer.Elapsed += (_, _1) =>
{
this.StateChanged?.Invoke(position, 0, header, $"{seconds++}/{duration}");
};
timer.Start();
Thread.Sleep(duration * 1000);
timer.Stop();
}
private void CancellationCallback()
{
// TODO: Update database.
this.Dispose();
}
}
}