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:
Michal Buzik 2026-02-25 21:29:15 +01:00
parent 7983463e8d
commit ef4d7b1abd
13 changed files with 820 additions and 60 deletions

View File

@ -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")]

View File

@ -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:

View File

@ -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:

View File

@ -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!");
}
}
}

View File

@ -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!");
}
}
}

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

View File

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

View File

@ -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");

View File

@ -34,7 +34,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication
this.iperlHead = iperlHead;
}
private void CloseConnection()
public void CloseConnection()
{
if (serialDriver != null)
serialDriver.CloseConnection();

View File

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

View File

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

View File

@ -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();

View File

@ -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" />