552 lines
20 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|