Uni DataEntry support to Iperl ASIC as SMART METER
Add async methods for iPerl communication and update simulation logic: - Extend `IperlHead` with async methods for reading opto data and volumes (`DataEntry_ReadSerialNumberAsync`, `DataEntry_BeginVolumeAsync`, `DataEntry_EndVolumeAsync`). - Add support for async serial number reading in `CycleBgEnForm`, implementing real-time updates for `regReaders`. - Enhance `FlyingStartStopTestOp` and `StandingStartStopTestOp` with simulation timers for processing time (25 seconds). - Improve exception handling and debug logs across modules (`DataEntry_ReadSerialNumber`, `ReadOptoDataWithTimeoutAsync`).
This commit is contained in:
parent
7983463e8d
commit
ef4d7b1abd
@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.2148.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.2148.1")]
|
||||
[assembly: AssemblyVersion("3.9.2205.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.2005.1")]
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using Common;
|
||||
using log4net;
|
||||
using TBF.Rig.Sequences;
|
||||
|
||||
@ -11,6 +12,10 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(FlyingStartStopTestOp));
|
||||
public override string ToString() { return string.Format("FlyingStartStopTestOp()"); }
|
||||
|
||||
private DateTime startTimeForSimulation;
|
||||
private bool simulationTimerStarted = false;
|
||||
|
||||
|
||||
/// Arguments of the constructor
|
||||
readonly UniCB uniCB;
|
||||
@ -123,6 +128,28 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
log.DebugFormat("Op.Run() opState={0}", opState);
|
||||
|
||||
//Simulation of processing time 25 seconds
|
||||
if (uniCB.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
if (opState == OpState.StartingTest)
|
||||
{
|
||||
startTimeForSimulation = DateTime.Now;
|
||||
simulationTimerStarted = false;
|
||||
}
|
||||
if (opState == OpState.TestInProgress)
|
||||
{
|
||||
if (!simulationTimerStarted)
|
||||
{
|
||||
startTimeForSimulation = DateTime.Now;
|
||||
simulationTimerStarted = true;
|
||||
}
|
||||
else if (DateTime.Now - startTimeForSimulation > TimeSpan.FromSeconds(25))
|
||||
{
|
||||
return Event.TestCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (opState)
|
||||
{
|
||||
case OpState.StartingTest:
|
||||
|
||||
@ -5,6 +5,7 @@ using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using Common;
|
||||
using log4net;
|
||||
using TBF.Rig.GenericDevices;
|
||||
|
||||
@ -24,6 +25,9 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
readonly TBF.Rig.Uni.Diverter.Diverter diverter;
|
||||
readonly int pulsesCount; /// Number of reference pulses for a complete test
|
||||
readonly bool withDiverter; /// Test with diverter (and scale)
|
||||
|
||||
private DateTime startTimeForSimulation;
|
||||
private bool simulationTimerStarted = false;
|
||||
|
||||
///
|
||||
/// Internal state of this operation
|
||||
@ -183,6 +187,28 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
log.DebugFormat("Run() opState={0}", opState);
|
||||
|
||||
//Simulation of processing time 25 seconds
|
||||
if (uniCB.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
if (opState == OpState.StartingTest)
|
||||
{
|
||||
startTimeForSimulation = DateTime.Now;
|
||||
simulationTimerStarted = false;
|
||||
}
|
||||
if (opState == OpState.TestInProgress)
|
||||
{
|
||||
if (!simulationTimerStarted)
|
||||
{
|
||||
startTimeForSimulation = DateTime.Now;
|
||||
simulationTimerStarted = true;
|
||||
}
|
||||
else if (DateTime.Now - startTimeForSimulation > TimeSpan.FromSeconds(25))
|
||||
{
|
||||
return Event.TestCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (opState)
|
||||
{
|
||||
case OpState.StartingTest:
|
||||
|
||||
@ -5,8 +5,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using log4net;
|
||||
using NHibernate.Util;
|
||||
using Results.Entities;
|
||||
using TBF.Resources;
|
||||
using TBF.Rig.GenericDevices;
|
||||
@ -428,6 +430,7 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
: this(waterMeters, title, myCfgBgTitle, myCfgBgSize, myCfgBgIsLrOrder, myCfgBgIsCameraPicture, myCfgBgFormCloseKeys, getItems, getColumns, b)
|
||||
{
|
||||
this.regReaders = regReaders;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -516,6 +519,9 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
case Ac.Set:
|
||||
commonComboBoxes[ix].Text = Strings.yes;
|
||||
break;
|
||||
case Ac.RegReader:
|
||||
commonComboBoxes[ix].Text = "--";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,56 +703,81 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
}
|
||||
|
||||
char[] digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
|
||||
///
|
||||
/// Find a column with serial numbers
|
||||
///
|
||||
for (int k = 0; k < colItems.Count; k++)
|
||||
|
||||
if (readSerialNoByRegisterReader)
|
||||
{
|
||||
if ((colItems[k].Content == Ct.SerialNr || colItems[k].Content == Ct.SerialNrAux) && colItems[k].Action != Ac.LoadReadOnly)
|
||||
log.Debug("Reading serial numbers from register readers...");
|
||||
this.SerialNumberRead += (s, eArgs) =>
|
||||
{
|
||||
///
|
||||
/// Column with serial numbers found => perform an auto s/n assignment
|
||||
///
|
||||
string firstSN = comboBoxes[k, firstIx].Text;
|
||||
log.Debug("Serial updated: " + eArgs.SerialNumber);
|
||||
UpdateSomethingBySerial(eArgs.Reader, eArgs.SerialNumber);
|
||||
};
|
||||
|
||||
|
||||
|
||||
if (readSerialNoByRegisterReader)
|
||||
{
|
||||
//TODO read serail number from register reader
|
||||
log.Debug("Read serial number from register reader!");
|
||||
|
||||
}
|
||||
|
||||
int firstSnNr;
|
||||
int startIx = firstSN.IndexOfAny(digits);
|
||||
if (startIx >= 0)
|
||||
{
|
||||
int lastDigitPosPlus1 = startIx + 1;
|
||||
var listOfDigits = new List<char>(digits);
|
||||
while (lastDigitPosPlus1 < firstSN.Length && listOfDigits.Contains(firstSN[lastDigitPosPlus1]))
|
||||
{
|
||||
lastDigitPosPlus1++;
|
||||
}
|
||||
int digitsCount = lastDigitPosPlus1 - startIx;
|
||||
BeforeUpdate();
|
||||
this.DoneUpdateBySerial += (s, eArgs) =>
|
||||
{
|
||||
log.Debug("Serial updated DONE!");
|
||||
DoneUpdate();
|
||||
};
|
||||
|
||||
if (int.TryParse(firstSN.Substring(startIx, digitsCount), out firstSnNr) && firstSnNr >= 0)
|
||||
ReadSerialNumbersAsync(regReaders);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
///
|
||||
/// Find a column with serial numbers
|
||||
///
|
||||
for (int k = 0; k < colItems.Count; k++)
|
||||
{
|
||||
if ((colItems[k].Content == Ct.SerialNr || colItems[k].Content == Ct.SerialNrAux) &&
|
||||
colItems[k].Action != Ac.LoadReadOnly)
|
||||
{
|
||||
///
|
||||
/// Column with serial numbers found => perform an auto s/n assignment
|
||||
///
|
||||
string firstSN = comboBoxes[k, firstIx].Text;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int firstSnNr;
|
||||
int startIx = firstSN.IndexOfAny(digits);
|
||||
if (startIx >= 0)
|
||||
{
|
||||
for (int ix = firstIx + 1; ix < wmsCount; ix++)
|
||||
int lastDigitPosPlus1 = startIx + 1;
|
||||
var listOfDigits = new List<char>(digits);
|
||||
while (lastDigitPosPlus1 < firstSN.Length &&
|
||||
listOfDigits.Contains(firstSN[lastDigitPosPlus1]))
|
||||
{
|
||||
if (checkBoxes[ix].Checked)
|
||||
lastDigitPosPlus1++;
|
||||
}
|
||||
|
||||
int digitsCount = lastDigitPosPlus1 - startIx;
|
||||
|
||||
if (int.TryParse(firstSN.Substring(startIx, digitsCount), out firstSnNr) &&
|
||||
firstSnNr >= 0)
|
||||
{
|
||||
for (int ix = firstIx + 1; ix < wmsCount; ix++)
|
||||
{
|
||||
firstSnNr++;
|
||||
string newSN = firstSnNr.ToString();
|
||||
int len = newSN.Length;
|
||||
if (len <= digitsCount)
|
||||
if (checkBoxes[ix].Checked)
|
||||
{
|
||||
comboBoxes[k, ix].Text = firstSN.Substring(0, startIx + digitsCount - len) + newSN + firstSN.Substring(startIx + digitsCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
comboBoxes[k, ix].Text = firstSN.Substring(0, startIx) + newSN + firstSN.Substring(startIx + digitsCount); ;
|
||||
firstSnNr++;
|
||||
string newSN = firstSnNr.ToString();
|
||||
int len = newSN.Length;
|
||||
if (len <= digitsCount)
|
||||
{
|
||||
comboBoxes[k, ix].Text =
|
||||
firstSN.Substring(0, startIx + digitsCount - len) + newSN +
|
||||
firstSN.Substring(startIx + digitsCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
comboBoxes[k, ix].Text = firstSN.Substring(0, startIx) + newSN +
|
||||
firstSN.Substring(startIx + digitsCount);
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -778,6 +809,74 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSomethingBySerial(IRegReader eReader, string eSerialNumber)
|
||||
{
|
||||
log.Debug($"RegReader name: {eReader.Name}, Serial No updated: " + eSerialNumber);
|
||||
PopulateComboBoxWithSerialNumbers(eReader, eSerialNumber);
|
||||
}
|
||||
|
||||
LinkedHashMap<string, bool> storeUIForUpdate = new LinkedHashMap<string, bool>();
|
||||
private Cursor _previousCursor;
|
||||
private bool GetStoredOrDefault(string key)
|
||||
{
|
||||
bool value;
|
||||
if (storeUIForUpdate.TryGetValue(key, out value))
|
||||
return value;
|
||||
|
||||
return true; // default if nothing stored
|
||||
}
|
||||
|
||||
private void BeforeUpdate()
|
||||
{
|
||||
log.Debug("BeforeUpdate");
|
||||
|
||||
storeUIForUpdate["okButton"] = this.okButton.Enabled;
|
||||
storeUIForUpdate["clearButton"] = this.clearButton.Enabled;
|
||||
storeUIForUpdate["multiPurposeButton"] = this.multiPurposeButton.Enabled;
|
||||
|
||||
this.okButton.Enabled = false;
|
||||
this.clearButton.Enabled = false;
|
||||
this.multiPurposeButton.Enabled = false;
|
||||
|
||||
_previousCursor = Cursor.Current;
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
}
|
||||
private void DoneUpdate()
|
||||
{
|
||||
log.Debug("DoneUpdate");
|
||||
this.okButton.Enabled = GetStoredOrDefault("okButton");
|
||||
this.clearButton.Enabled = GetStoredOrDefault("clearButton");
|
||||
this.multiPurposeButton.Enabled = GetStoredOrDefault("multiPurposeButton");
|
||||
Cursor.Current = _previousCursor;
|
||||
}
|
||||
|
||||
private void PopulateComboBoxWithSerialNumbers( IRegReader eReader, string eSerialNumber)
|
||||
{
|
||||
if (regReaders == null || comboBoxes == null)
|
||||
return;
|
||||
|
||||
int regReadersLength = regReaders.Length;
|
||||
int comboRows = comboBoxes.GetLength(0);
|
||||
int comboCols = comboBoxes.GetLength(1);
|
||||
|
||||
for (int k = 0; k < colItems.Count && k < comboRows; k++)
|
||||
{
|
||||
for (int ix = 0; ix < wmsCount && ix < comboCols; ix++)
|
||||
{
|
||||
int regIndex = ix + wmsCount*k;
|
||||
|
||||
if (regIndex >= 0 && regIndex < regReadersLength)
|
||||
{
|
||||
var combo = comboBoxes[k, ix];
|
||||
if (combo != null && regReaders[regIndex] == eReader)
|
||||
{
|
||||
combo.Text = eSerialNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void comboBox_SelectedIndexChanged(object sndr, EventArgs e)
|
||||
{
|
||||
if (!isHandlersEnabled) return;
|
||||
@ -924,5 +1023,115 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event EventHandler<SerialNumberReadEventArgs> SerialNumberRead;
|
||||
public event EventHandler<SerialNumberReadDoneEventArgs> DoneUpdateBySerial;
|
||||
|
||||
public sealed class SerialNumberReadEventArgs : EventArgs
|
||||
{
|
||||
public IRegReader Reader { get; private set; }
|
||||
public string SerialNumber { get; private set; }
|
||||
|
||||
public SerialNumberReadEventArgs(IRegReader reader, string serialNumber)
|
||||
{
|
||||
Reader = reader;
|
||||
SerialNumber = serialNumber;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SerialNumberReadDoneEventArgs : EventArgs
|
||||
{
|
||||
public SerialNumberReadDoneEventArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnSerialNumberRead(IRegReader reader, string serial)
|
||||
{
|
||||
var handler = SerialNumberRead; // copy for thread-safety
|
||||
if (handler == null) return;
|
||||
if (IsHandleCreated && InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action(() =>
|
||||
handler(this, new SerialNumberReadEventArgs(reader, serial))));
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, new SerialNumberReadEventArgs(reader, serial));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnReadDone()
|
||||
{
|
||||
var handler = DoneUpdateBySerial; // copy for thread-safety
|
||||
if (handler == null) return;
|
||||
|
||||
if (IsHandleCreated && InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action(() =>
|
||||
handler(this, new SerialNumberReadDoneEventArgs())));
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, new SerialNumberReadDoneEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
public async void ReadSerialNumbersAsync(IEnumerable<IRegReader> regReaders)
|
||||
{
|
||||
log.Debug("Reading serial numbers from register readers...");
|
||||
|
||||
try
|
||||
{
|
||||
var groups = regReaders
|
||||
.OfType<IRegReaderSmart>()
|
||||
.GroupBy(r => r.Group)
|
||||
.OrderBy(g => g.Key);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var subGroups = group
|
||||
.GroupBy(r => r.MuxBoardNrOrGroup14)
|
||||
.OrderBy(sg => sg.Key);
|
||||
|
||||
foreach (var subGroup in subGroups)
|
||||
{
|
||||
log.Debug(string.Format("Processing Group {0}, SubGroup {1}", group.Key, subGroup.Key));
|
||||
|
||||
// Start tasks in parallel inside subgroup
|
||||
var tasks = subGroup.Select(async r =>
|
||||
{
|
||||
var serial = await r.DataEntry_ReadSerialNumber().ConfigureAwait(false);
|
||||
return new KeyValuePair<IRegReader, string>((IRegReader)r, serial);
|
||||
}).ToList();
|
||||
|
||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
foreach (var kv in results)
|
||||
{
|
||||
var reader = kv.Key;
|
||||
var serial = kv.Value;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(serial))
|
||||
continue;
|
||||
|
||||
// Notify for each successful read
|
||||
OnSerialNumberRead(reader, serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error("Error reading serial numbers from register readers", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnReadDone();
|
||||
}
|
||||
|
||||
|
||||
log.Debug("Reading serial numbers DONE!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ using TBF.Rig.GenericDevices;
|
||||
using TBF.Resources;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using NHibernate.Util;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
namespace TBF.Rig.DataEntry.Uni
|
||||
@ -463,6 +465,8 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
okButton.Text = Strings.OkBtnText;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialize combo boxes state defined by related 'Action'.
|
||||
/// Clear check boxes.
|
||||
@ -493,6 +497,7 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
textBoxes[k, i].Enabled = (isStartBoxAlwaysEn || !isEnd) && waterMeters[i] != null && !waterMeters[i].Disabled
|
||||
&& rrIx < regReaders.Length && regReaders[rrIx] != null;
|
||||
}
|
||||
|
||||
}
|
||||
else if (colItems[k].Content == Ct.StartStateAux)
|
||||
{
|
||||
@ -564,10 +569,121 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_readDataFromRegReaders)
|
||||
{
|
||||
log.Debug($"Reading serial numbers from register readers... IsEnd: {isEnd}");
|
||||
this.VolumeStartReadbyRegReader += (s, eArgs) =>
|
||||
{
|
||||
log.Debug("Volume updated: " + eArgs.Volume);
|
||||
UpdateVolume(eArgs.Reader, eArgs.Volume);
|
||||
};
|
||||
|
||||
BeforeUpdate();
|
||||
this.DoneUpdateByRegReader += (s, eArgs) =>
|
||||
{
|
||||
log.Debug("Volume updated DONE!");
|
||||
DoneUpdate();
|
||||
};
|
||||
|
||||
ReadVolumeAsync(regReaders);
|
||||
}
|
||||
|
||||
isHandlersEnabled = true;
|
||||
}
|
||||
|
||||
LinkedHashMap<string, bool> storeUIForUpdate = new LinkedHashMap<string, bool>();
|
||||
private Cursor _previousCursor;
|
||||
private bool GetStoredOrDefault(string key)
|
||||
{
|
||||
bool value;
|
||||
if (storeUIForUpdate.TryGetValue(key, out value))
|
||||
return value;
|
||||
|
||||
return true; // default if nothing stored
|
||||
}
|
||||
|
||||
private void BeforeUpdate()
|
||||
{
|
||||
log.Debug("BeforeUpdate");
|
||||
|
||||
storeUIForUpdate["okButton"] = this.okButton.Enabled;
|
||||
storeUIForUpdate["largeTextBox"] = this.largeTextBox.Enabled;
|
||||
storeUIForUpdate["largeExclamationLabel"] = this.largeExclamationLabel.Enabled;
|
||||
storeUIForUpdate["unitComboBox"] = this.unitComboBox.Enabled;
|
||||
|
||||
this.okButton.Enabled = false;
|
||||
this.largeTextBox.Enabled = false;
|
||||
this.largeExclamationLabel.Enabled = false;
|
||||
this.unitComboBox.Enabled = false;
|
||||
|
||||
_previousCursor = Cursor.Current;
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
}
|
||||
|
||||
private void DoneUpdate()
|
||||
{
|
||||
log.Debug("DoneUpdate");
|
||||
this.okButton.Enabled = GetStoredOrDefault("okButton");
|
||||
this.largeTextBox.Enabled = GetStoredOrDefault("largeTextBox");
|
||||
this.largeExclamationLabel.Enabled = GetStoredOrDefault("largeExclamationLabel");
|
||||
this.unitComboBox.Enabled = GetStoredOrDefault("unitComboBox");
|
||||
Cursor.Current = _previousCursor;
|
||||
}
|
||||
|
||||
private void UpdateVolume(IRegReader eArgsReader, double eArgsVolume)
|
||||
{
|
||||
log.Debug($"RegReader name: {eArgsReader.Name}, Serial No updated: " + eArgsVolume);
|
||||
PopulateVolume(eArgsReader, eArgsVolume);
|
||||
}
|
||||
|
||||
private void PopulateVolume(IRegReader eArgsReader, double eArgsVolume)
|
||||
{
|
||||
|
||||
if (regReaders == null || Double.IsNaN(eArgsVolume) || textBoxes == null)
|
||||
return;
|
||||
|
||||
int regReadersLength = regReaders.Length;
|
||||
int comboRows = textBoxes.GetLength(0);
|
||||
int comboCols = textBoxes.GetLength(1);
|
||||
|
||||
//just combine Start and End - switch column to write
|
||||
List<Ct> ctValues = isEnd
|
||||
? new List<Ct> { Ct.EndState, Ct.EndStateAux }
|
||||
: new List<Ct> { Ct.StartState, Ct.StartStateAux };
|
||||
|
||||
for (int k = 0; k < colItems.Count && k < comboRows; k++)
|
||||
{
|
||||
Ct current = (Ct)colItems[k].Content;
|
||||
if (ctValues.Contains(current)) // it define if is start or end
|
||||
{
|
||||
for (int ix = 0; ix < wmsCount && ix < comboCols; ix++)
|
||||
{
|
||||
int regIndex = ix;
|
||||
|
||||
if (regIndex >= 0 && regIndex < regReadersLength)
|
||||
{
|
||||
var textBox = textBoxes[k, ix];
|
||||
if (textBox != null && regReaders[regIndex] == eArgsReader)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (VolumeUnit == Unit.None)
|
||||
VolumeUnit = Unit.l;
|
||||
Double convertTo = Units.ConvertTo(VolumeUnit, eArgsVolume);
|
||||
textBox.Text = convertTo.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error($"Error converting volume to {VolumeUnit}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When one combo box is updated using drop-down menu, all combo boxes
|
||||
/// are updated by this function.
|
||||
@ -1178,7 +1294,11 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
Brush lightBrush = new SolidBrush(Color.LightGreen);
|
||||
Brush darkBrush = new SolidBrush(Color.Green);
|
||||
Font largeFont = new Font("arial", 24.0F, FontStyle.Bold); /// normal image
|
||||
Font smallFont = new Font("arial", 12.0F, FontStyle.Bold); /// zoomed image
|
||||
Font smallFont = new Font("arial", 12.0F, FontStyle.Bold);
|
||||
|
||||
private bool _readDataFromRegReaders = true;
|
||||
|
||||
/// zoomed image
|
||||
|
||||
/// <summary>
|
||||
/// Show selected image in the picture box.
|
||||
@ -1249,5 +1369,118 @@ namespace TBF.Rig.DataEntry.Uni
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event EventHandler<VolumeReadEventArgs> VolumeStartReadbyRegReader;
|
||||
public event EventHandler<VolumeReadDoneEventArgs> DoneUpdateByRegReader;
|
||||
|
||||
public sealed class VolumeReadEventArgs : EventArgs
|
||||
{
|
||||
public IRegReader Reader { get; private set; }
|
||||
public double Volume { get; private set; }
|
||||
|
||||
public VolumeReadEventArgs(IRegReader reader, double volume)
|
||||
{
|
||||
Reader = reader;
|
||||
Volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class VolumeReadDoneEventArgs : EventArgs
|
||||
{
|
||||
public VolumeReadDoneEventArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnReadVolume(IRegReader reader, double volume)
|
||||
{
|
||||
var handler = VolumeStartReadbyRegReader; // copy for thread-safety
|
||||
if (handler == null) return;
|
||||
if (IsHandleCreated && InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action(() =>
|
||||
handler(this, new VolumeReadEventArgs(reader, volume))));
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, new VolumeReadEventArgs(reader, volume));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnReadDone()
|
||||
{
|
||||
var handler = DoneUpdateByRegReader; // copy for thread-safety
|
||||
if (handler == null) return;
|
||||
|
||||
if (IsHandleCreated && InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action(() =>
|
||||
handler(this, new VolumeReadDoneEventArgs())));
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, new VolumeReadDoneEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
public async void ReadVolumeAsync(IEnumerable<IRegReader> regReaders)
|
||||
{
|
||||
log.Debug("Reading serial numbers from register readers...");
|
||||
|
||||
try
|
||||
{
|
||||
var groups = regReaders
|
||||
.OfType<IRegReaderSmart>()
|
||||
.GroupBy(r => r.Group)
|
||||
.OrderBy(g => g.Key);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var subGroups = group
|
||||
.GroupBy(r => r.MuxBoardNrOrGroup14)
|
||||
.OrderBy(sg => sg.Key);
|
||||
|
||||
foreach (var subGroup in subGroups)
|
||||
{
|
||||
log.Debug(string.Format("Processing Group {0}, SubGroup {1}", group.Key, subGroup.Key));
|
||||
|
||||
// Start tasks in parallel inside subgroup
|
||||
var tasks = subGroup.Select(async r =>
|
||||
{
|
||||
double volume = Double.NaN;
|
||||
if (isEnd)
|
||||
{
|
||||
volume = await r.DataEntry_ReadEndVolume().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
volume = await r.DataEntry_ReadBeginVolume().ConfigureAwait(false);
|
||||
}
|
||||
return new KeyValuePair<IRegReader, double>((IRegReader)r, volume);
|
||||
}).ToList();
|
||||
|
||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
foreach (var kv in results)
|
||||
{
|
||||
var reader = kv.Key;
|
||||
var volume = kv.Value;
|
||||
|
||||
// Notify for each successful read
|
||||
OnReadVolume(reader, volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error("Error reading Strat Volume from register readers", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnReadDone();
|
||||
}
|
||||
log.Debug("Reading Start Volume DONE!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
TBF/Rig/GenericDevices/IRegReaderSmart.cs
Normal file
16
TBF/Rig/GenericDevices/IRegReaderSmart.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TBF.Rig.GenericDevices
|
||||
{
|
||||
public interface IRegReaderSmart
|
||||
{
|
||||
Task<string> DataEntry_ReadSerialNumber();
|
||||
Task<double> DataEntry_ReadBeginVolume();
|
||||
Task<double> DataEntry_ReadEndVolume();
|
||||
|
||||
int Group { get; }
|
||||
int MuxBoardNrOrGroup14 { get; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -508,21 +508,35 @@ namespace TBF.Rig.Sequences
|
||||
/// This is a normal batch (not a restored one) => Display 'Cycle Begin Form'
|
||||
///
|
||||
/// Open iPerlCommunicationForm
|
||||
if (simultWithPurgingTests.Count > 0)
|
||||
bool hasValidReader = ProcessData.RegisterReaders != null &&
|
||||
ProcessData.RegisterReaders.Any(r => r != null);
|
||||
|
||||
if (!hasValidReader)
|
||||
{
|
||||
ProcessData.RegisterReaders = StateMachine.GetMetersPath(simultWithPurgingTests[0]).RegisterReaders;
|
||||
}
|
||||
else
|
||||
{
|
||||
//get throw test instances and if Smart Reader SerialNo found use it
|
||||
for (int i = 0; i < StateMachine.TestInstances.Length; i++)
|
||||
if (simultWithPurgingTests.Count > 0)
|
||||
{
|
||||
Test test = StateMachine.TestInstances[i].Test;
|
||||
if (test is ITestMethodSmart)
|
||||
ProcessData.RegisterReaders =
|
||||
StateMachine.GetMetersPath(simultWithPurgingTests[0]).RegisterReaders;
|
||||
}
|
||||
else
|
||||
{
|
||||
//get throw test instances and if Smart Reader SerialNo found use it
|
||||
Test first = null;
|
||||
for (int i = 0; i < StateMachine.TestInstances.Length; i++)
|
||||
{
|
||||
ProcessData.RegisterReaders = StateMachine.GetMetersPath(test).RegisterReaders;
|
||||
break;
|
||||
Test test = StateMachine.TestInstances[i].Test;
|
||||
ITestMethod tm = TbfComponents.FindComponent(test.Method) as ITestMethod;
|
||||
if (first == null) first = test;
|
||||
if (tm is ITestMethodSmart || tm is TBF.Rig.TestMethods.iPerlCommunication.TestMethod)
|
||||
{
|
||||
ProcessData.RegisterReaders = StateMachine.GetMetersPath(test).RegisterReaders;
|
||||
first = null;//we found what we need
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first != null)
|
||||
ProcessData.RegisterReaders = StateMachine.GetMetersPath(first).RegisterReaders;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ using TBF.UiBridge;
|
||||
|
||||
namespace TBF.Rig.TestMethods.iPerlCommunication
|
||||
{
|
||||
public class TestMethod : ComponentBase, ISimultTestMethod, ISequenceCondition, ISessionDataMngmnt
|
||||
public class TestMethod : ComponentBase, ISimultTestMethod, ISequenceCondition, ISessionDataMngmnt, ITestMethodSmart
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(TestMethod));
|
||||
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
|
||||
|
||||
@ -34,7 +34,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication
|
||||
this.iperlHead = iperlHead;
|
||||
}
|
||||
|
||||
private void CloseConnection()
|
||||
public void CloseConnection()
|
||||
{
|
||||
if (serialDriver != null)
|
||||
serialDriver.CloseConnection();
|
||||
|
||||
@ -945,7 +945,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
|
||||
|
||||
if (ihead.OptoHeadTest.SetTestMode())
|
||||
{
|
||||
log.Debug("test mode activated now");
|
||||
log.Debug($" Iperl:{ihead.Name}, test mode activated now");
|
||||
}
|
||||
|
||||
|
||||
@ -953,6 +953,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
|
||||
{
|
||||
/// Already in the correct test mode
|
||||
resultStr = "Already " + ihead.ConfigStruct.ToString(1);
|
||||
log.Debug($" Iperl:{ihead.Name}, status: {resultStr}");
|
||||
return CommErr.None;
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ using Sensus.iPerl.NfcHandler;
|
||||
using NHibernate;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using TBF.Rig.TestMethods.iPerlCommunication.communication; // This line is correct and does not need to be changed.
|
||||
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
|
||||
@ -28,7 +29,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
/// <summary>
|
||||
/// This component = instance of this class is a placeholder for a combined main watermeter
|
||||
/// </summary>
|
||||
public class IperlHead : ComponentBase, IDevice,IRegReaderDatastream, ISessionDataMngmnt, IOperation
|
||||
public class IperlHead : ComponentBase, IDevice,IRegReaderDatastream, ISessionDataMngmnt, IOperation, IRegReaderSmart
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
|
||||
private static readonly ILog logStream = LogManager.GetLogger("StreamData");
|
||||
@ -1083,6 +1084,51 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
||||
public async Task<string> ReadOptoDataWithTimeoutAsync(int timeoutMs = 5000)
|
||||
{
|
||||
if (optoSerialPort == null)
|
||||
return string.Empty;
|
||||
|
||||
var readTask = Task.Run(() =>
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (optoSerialPort== null) return string.Empty;
|
||||
|
||||
string line = optoSerialPort.ReadLine();
|
||||
byte[] bytes = optoSerialPort.Encoding.GetBytes(line);
|
||||
string received = HexFormatter.ToSerialHex(bytes);
|
||||
|
||||
log.Debug("RX ← " + received);
|
||||
return line;
|
||||
}
|
||||
});
|
||||
|
||||
var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs));
|
||||
|
||||
if (completedTask == readTask)
|
||||
{
|
||||
return await readTask; // completed successfully
|
||||
}
|
||||
|
||||
log.Debug("ReadOptoData timeout after " + timeoutMs + " ms");
|
||||
return string.Empty; // timeout case
|
||||
}
|
||||
|
||||
public string ReadOptoDataWithTimeout(int timeoutMs = 5000)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ReadOptoDataWithTimeoutAsync(timeoutMs)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OptoTelegramReceived(int currentIx, bool async, double volumeRawExt, double timestampRawExt)
|
||||
@ -1525,7 +1571,191 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> DataEntry_ReadSerialNumber()
|
||||
{
|
||||
log.Debug("called DataEntry_ReadSerialNumber()");
|
||||
if (!string.IsNullOrEmpty(SerialNr)) return SerialNr;
|
||||
|
||||
//need to find serial number
|
||||
SerialNr = await DataEntry_ReadSerialNumberAsync();
|
||||
|
||||
return SerialNr;
|
||||
}
|
||||
|
||||
public Task<double> DataEntry_ReadBeginVolume()
|
||||
{
|
||||
log.Debug("called DataEntry_ReadBeginVolumer()");
|
||||
|
||||
Task<double> readedVolume = DataEntry_BeginVolumeAsync();
|
||||
|
||||
return readedVolume;
|
||||
}
|
||||
|
||||
public Task<double> DataEntry_ReadEndVolume()
|
||||
{
|
||||
log.Debug("called DataEntry_ReadBeginVolumer()");
|
||||
|
||||
Task<double> readedVolume = DataEntry_EndVolumeAsync();
|
||||
|
||||
return readedVolume;
|
||||
}
|
||||
|
||||
|
||||
public async Task<double> DataEntry_EndVolumeAsync()
|
||||
{
|
||||
|
||||
if (optoSerialPort == null || !optoSerialPort.IsOpen)
|
||||
{
|
||||
StartDataStreamProcessing();
|
||||
if (optoSerialPort == null || !optoSerialPort.IsOpen)
|
||||
{
|
||||
log.Error($"optoSerialPort COM: {this.OptoComPortNr} is not open - DataEntry_EndVolumeAsync()");
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}");
|
||||
volumeLtr = Double.NaN;
|
||||
|
||||
int counter = 0;
|
||||
while (Double.IsNaN(volumeLtr) && counter < 2)
|
||||
{
|
||||
counter++;
|
||||
try
|
||||
{
|
||||
string readOptoDataWithTimeout = ReadOptoDataWithTimeout(2000);
|
||||
if (!string.IsNullOrEmpty(readOptoDataWithTimeout))
|
||||
{
|
||||
try
|
||||
{
|
||||
DiagnosticLedState4Data data =
|
||||
(DiagnosticLedState4Data)parser.ParseLine(readOptoDataWithTimeout, false);
|
||||
volumeLtr = data.RawVolume;
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Warn($"Parsing DiagnosticLedState4Data: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr}");
|
||||
if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort();
|
||||
|
||||
if (!Double.IsNaN(volumeLtr))
|
||||
{
|
||||
endWMState = volumeLtr;
|
||||
ReadPulses();
|
||||
return endWMState;
|
||||
}
|
||||
//}
|
||||
|
||||
log.Warn("Default NaN value returned! Data Opto stream reading failed!");
|
||||
return Double.NaN;
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
public async Task<double> DataEntry_BeginVolumeAsync()
|
||||
{
|
||||
if (ConfigStruct == null)
|
||||
{
|
||||
log.Debug("ConfigStruct is null - created new in ReadSerialNr()");
|
||||
ConfigStruct = new ConfigStruct();
|
||||
}
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
|
||||
log.Debug($"Try get Start Volume! COM: {this.OptoComPortNr}");
|
||||
|
||||
Start();
|
||||
|
||||
volumeLtr0 = Double.NaN;
|
||||
int counter = 0;
|
||||
while (Double.IsNaN(volumeLtr0) && counter < 10)
|
||||
{
|
||||
counter++;
|
||||
try
|
||||
{
|
||||
string readOptoDataWithTimeout = ReadOptoDataWithTimeout(5000);
|
||||
if (!string.IsNullOrEmpty(readOptoDataWithTimeout))
|
||||
{
|
||||
try
|
||||
{
|
||||
DiagnosticLedState4Data data =
|
||||
(DiagnosticLedState4Data)parser.ParseLine(readOptoDataWithTimeout, false);
|
||||
volumeLtr0 = data.RawVolume;
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Warn($"Parsing DiagnosticLedState4Data: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug($"Try get Start Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr0}");
|
||||
if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort();
|
||||
|
||||
if (!Double.IsNaN(volumeLtr0))
|
||||
{
|
||||
beginWMState = volumeLtr0;
|
||||
return beginWMState;
|
||||
}
|
||||
//}
|
||||
|
||||
log.Warn("Default NaN value returned! Data Opto stream reading failed!");
|
||||
return Double.NaN;
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<string> DataEntry_ReadSerialNumberAsync()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SerialNr))
|
||||
return SerialNr;
|
||||
|
||||
if (ConfigStruct == null)
|
||||
{
|
||||
log.Debug("ConfigStruct is null - created new in ReadSerialNr()");
|
||||
ConfigStruct = new ConfigStruct();
|
||||
}
|
||||
|
||||
if (CommFailed || ConfigStruct == null)
|
||||
return CommErr.CommFailed.ToString();
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
|
||||
|
||||
|
||||
log.Debug($"Try get ReadSerialNr! COM: {this.RfidComPortNr}");
|
||||
if (OptoHeadTest.ReadSerialNr())
|
||||
{
|
||||
SerialNr = this.ConfigStruct.PCBNumberString;
|
||||
log.Debug("ReadSerialNr successful");
|
||||
}
|
||||
|
||||
//optoHeadTest.CloseConnection();
|
||||
|
||||
return SerialNr;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Common;
|
||||
using log4net;
|
||||
using Config.Entities;
|
||||
using TBF.Rig.ControlBoard.Uni;
|
||||
@ -211,6 +212,8 @@ namespace TBF.Rig.Uni.RegValve
|
||||
|
||||
if (opState == OpState.Wait4FlowMsrmntAndSendRVMove)
|
||||
{
|
||||
if (flowMeter.DebugLevel == DebugMode.Simulate) return Event.FlowReached;
|
||||
|
||||
if (!flowMeter.MsrmntAvailable) return Event.Starting;
|
||||
|
||||
double flow1 = flowMeter.ReadFlow();
|
||||
|
||||
@ -633,6 +633,7 @@
|
||||
<Compile Include="Rig\GenericDevices\IParallelOutput.cs" />
|
||||
<Compile Include="Rig\GenericDevices\IReceivesDataFromRadio.cs" />
|
||||
<Compile Include="Rig\GenericDevices\IRegReaderPulses.cs" />
|
||||
<Compile Include="Rig\GenericDevices\IRegReaderSmart.cs" />
|
||||
<Compile Include="Rig\GenericDevices\IRestAPIAdapter.cs" />
|
||||
<Compile Include="Rig\GenericDevices\IResultsProcessor.cs" />
|
||||
<Compile Include="Rig\GenericDevices\ISessionDataMngmnt.cs" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user