diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs index c3d6b28b7..8b40ee43a 100644 --- a/TBF/Properties/AssemblyInfo.cs +++ b/TBF/Properties/AssemblyInfo.cs @@ -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")] diff --git a/TBF/Rig/ControlBoard/Uni/FlyingStartStopTestOp.cs b/TBF/Rig/ControlBoard/Uni/FlyingStartStopTestOp.cs index 0b6487c15..a8870d0d5 100644 --- a/TBF/Rig/ControlBoard/Uni/FlyingStartStopTestOp.cs +++ b/TBF/Rig/ControlBoard/Uni/FlyingStartStopTestOp.cs @@ -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: diff --git a/TBF/Rig/ControlBoard/Uni/StandingStartStopTestOp.cs b/TBF/Rig/ControlBoard/Uni/StandingStartStopTestOp.cs index f2732345c..edfa9c3b7 100644 --- a/TBF/Rig/ControlBoard/Uni/StandingStartStopTestOp.cs +++ b/TBF/Rig/ControlBoard/Uni/StandingStartStopTestOp.cs @@ -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: diff --git a/TBF/Rig/DataEntry/Uni/CycleBgEnForm.cs b/TBF/Rig/DataEntry/Uni/CycleBgEnForm.cs index f069293f7..8ad674378 100644 --- a/TBF/Rig/DataEntry/Uni/CycleBgEnForm.cs +++ b/TBF/Rig/DataEntry/Uni/CycleBgEnForm.cs @@ -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; + } /// @@ -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(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(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 storeUIForUpdate = new LinkedHashMap(); + 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 SerialNumberRead; + public event EventHandler 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 regReaders) + { + log.Debug("Reading serial numbers from register readers..."); + + try + { + var groups = regReaders + .OfType() + .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)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!"); + } } } diff --git a/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs b/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs index 2b5b1635e..a51cadcf0 100644 --- a/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs +++ b/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs @@ -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; } + + /// /// 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 storeUIForUpdate = new LinkedHashMap(); + 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 ctValues = isEnd + ? new List { Ct.EndState, Ct.EndStateAux } + : new List { 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}"); + } + } + } + } + } + } + } + /// /// 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 /// /// Show selected image in the picture box. @@ -1249,5 +1369,118 @@ namespace TBF.Rig.DataEntry.Uni } #endregion + + public event EventHandler VolumeStartReadbyRegReader; + public event EventHandler 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 regReaders) + { + log.Debug("Reading serial numbers from register readers..."); + + try + { + var groups = regReaders + .OfType() + .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)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!"); + } } } diff --git a/TBF/Rig/GenericDevices/IRegReaderSmart.cs b/TBF/Rig/GenericDevices/IRegReaderSmart.cs new file mode 100644 index 000000000..5133990d5 --- /dev/null +++ b/TBF/Rig/GenericDevices/IRegReaderSmart.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; + +namespace TBF.Rig.GenericDevices +{ + public interface IRegReaderSmart + { + Task DataEntry_ReadSerialNumber(); + Task DataEntry_ReadBeginVolume(); + Task DataEntry_ReadEndVolume(); + + int Group { get; } + int MuxBoardNrOrGroup14 { get; } + + } +} \ No newline at end of file diff --git a/TBF/Rig/Sequences/MainSeq.cs b/TBF/Rig/Sequences/MainSeq.cs index 4ef2273c4..70faa1c57 100644 --- a/TBF/Rig/Sequences/MainSeq.cs +++ b/TBF/Rig/Sequences/MainSeq.cs @@ -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; } } diff --git a/TBF/Rig/TestMethods/iPerlCommunication/TestMethod.cs b/TBF/Rig/TestMethods/iPerlCommunication/TestMethod.cs index 66e6f0c36..4fd359817 100644 --- a/TBF/Rig/TestMethods/iPerlCommunication/TestMethod.cs +++ b/TBF/Rig/TestMethods/iPerlCommunication/TestMethod.cs @@ -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"); diff --git a/TBF/Rig/TestMethods/iPerlCommunication/communication/OptoHeadTest.cs b/TBF/Rig/TestMethods/iPerlCommunication/communication/OptoHeadTest.cs index 4f0ace63a..2d33dc278 100644 --- a/TBF/Rig/TestMethods/iPerlCommunication/communication/OptoHeadTest.cs +++ b/TBF/Rig/TestMethods/iPerlCommunication/communication/OptoHeadTest.cs @@ -34,7 +34,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication this.iperlHead = iperlHead; } - private void CloseConnection() + public void CloseConnection() { if (serialDriver != null) serialDriver.CloseConnection(); diff --git a/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs b/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs index fc65a72fb..767dc30e9 100644 --- a/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs +++ b/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs @@ -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; } diff --git a/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs b/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs index 7d34840b6..e8b7c746a 100644 --- a/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs +++ b/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs @@ -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 /// /// This component = instance of this class is a placeholder for a combined main watermeter /// - 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 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 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 DataEntry_ReadBeginVolume() + { + log.Debug("called DataEntry_ReadBeginVolumer()"); + + Task readedVolume = DataEntry_BeginVolumeAsync(); + + return readedVolume; + } + + public Task DataEntry_ReadEndVolume() + { + log.Debug("called DataEntry_ReadBeginVolumer()"); + + Task readedVolume = DataEntry_EndVolumeAsync(); + + return readedVolume; + } + public async Task 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 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 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; + }); + } } } diff --git a/TBF/Rig/Uni/RegValve/SetFlowOp.cs b/TBF/Rig/Uni/RegValve/SetFlowOp.cs index 02f301cdb..c0721842d 100644 --- a/TBF/Rig/Uni/RegValve/SetFlowOp.cs +++ b/TBF/Rig/Uni/RegValve/SetFlowOp.cs @@ -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(); diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index f8ce60b82..795381d59 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -633,6 +633,7 @@ +