diff --git a/TBF/Rig/DataEntry/Uni/EntryFormCfg.cs b/TBF/Rig/DataEntry/Uni/EntryFormCfg.cs index dc0a6a9bc..b4c16cfdd 100644 --- a/TBF/Rig/DataEntry/Uni/EntryFormCfg.cs +++ b/TBF/Rig/DataEntry/Uni/EntryFormCfg.cs @@ -43,6 +43,7 @@ namespace TBF.Rig.DataEntry.Uni public string BgFormCloseKeys; /// 7 public bool BgIsAutoReadingSerialNo;/// 8 - new public int BgAutoCloseGap; /// 9 - new + public int DataEntryReadTimeoutSec; public bool EnShowForm; /// 8 + 2 public string EnTitle; /// 9 @@ -347,6 +348,7 @@ namespace TBF.Rig.DataEntry.Uni BgFormCloseKeys = string.Empty; BgIsAutoReadingSerialNo = false; BgAutoCloseGap = 5; + DataEntryReadTimeoutSec = 5; EnShowForm = false; EnTitle = "Enter water meter data"; @@ -403,6 +405,7 @@ namespace TBF.Rig.DataEntry.Uni "Beginning: Keys to close the form", "Beginning: Read Automatic Serial No from watermeter", //8 "All: Auto-continue delay after automatic reading [s; <=0=off]", //9 + "All: Timeout for reading parameters [s; <=0=5]", //10 (stored internally as parameter 28) "End: Show form",//8+2 "End: Form title", @@ -424,6 +427,16 @@ namespace TBF.Rig.DataEntry.Uni "Test start/end: End picture name", "Test start/end: Keys to close the form", }; + + // Keep the original internal parameter indexes for backward compatibility, + // but display the timeout directly after the auto-continue parameter. + int MapDisplayedParameterIndex(int i) + { + if (i == 10) return 28; + if (i > 10 && i <= 28) return i - 1; + return i; + } + public string ParamName(int i) { if (i < paramNames.Length) return paramNames[i]; @@ -505,6 +518,8 @@ namespace TBF.Rig.DataEntry.Uni public ICollection ParamValues(int i) { + i = MapDisplayedParameterIndex(i); + var list = new List(); if (i < paramNames.Length) @@ -590,6 +605,7 @@ namespace TBF.Rig.DataEntry.Uni return string.Format("Name={0}, Show cycle beginning form={1}, Show cycle end form={2}", Name, BgShowForm, EnShowForm); } + i = MapDisplayedParameterIndex(i); if (i < paramNames.Length) { @@ -625,6 +641,7 @@ namespace TBF.Rig.DataEntry.Uni case 25: return TestStartPicName; case 26: return TestEndPicName; case 27: return TestFormCloseKeys; + case 28: return DataEntryReadTimeoutSec.ToString(); default: return string.Empty; } @@ -702,6 +719,8 @@ namespace TBF.Rig.DataEntry.Uni public CfgUpdateFlags UpdateParam(int i, string str) { + i = MapDisplayedParameterIndex(i); + if (i < paramNames.Length) { switch (i) @@ -763,6 +782,14 @@ namespace TBF.Rig.DataEntry.Uni case 25: TestStartPicName = str; return CfgUpdateFlags.RestartRqrd; case 26: TestEndPicName = str; return CfgUpdateFlags.RestartRqrd; case 27: TestFormCloseKeys = str; return CfgUpdateFlags.RestartRqrd; + case 28: + int timeoutSec; + if (!Int32.TryParse(str, out timeoutSec)) + { + return CfgUpdateFlags.None; + } + DataEntryReadTimeoutSec = timeoutSec; + return CfgUpdateFlags.RestartRqrd; default: return CfgUpdateFlags.None; @@ -884,35 +911,39 @@ namespace TBF.Rig.DataEntry.Uni case 5: case 6: case 8://Auto close - yes/No - case 10://8+2 - case 12: - case 15: + case 11://8+2 + case 13: case 16: - case 18: - case 20: - case 22: + case 17: + case 19: + case 21: case 23: case 24: + case 25: message = string.Empty; if (ParamValues(i).Contains(str)) return true; break; case 3: case 4: case 9: //Gap AutoClose - case 13: case 14: - case 21: + case 15: + case 22: + message = string.Empty; + if (int.TryParse(str, out idummy)) return true; + break; + case 10: // Timeout; <= 0 uses the default timeout. message = string.Empty; if (int.TryParse(str, out idummy)) return true; break; case 1: case 7: - case 11: - case 17: - case 19: - case 25: + case 12: + case 18: + case 20: case 26: case 27: + case 28: message = string.Empty; return true; default: @@ -957,6 +988,7 @@ namespace TBF.Rig.DataEntry.Uni prms.BgFormCloseKeys = BgFormCloseKeys; prms.BgIsAutoReadingSerialNo = BgIsAutoReadingSerialNo; prms.BgAutoCloseGap = BgAutoCloseGap; + prms.DataEntryReadTimeoutSec = DataEntryReadTimeoutSec; prms.EnShowForm = EnShowForm; prms.EnTitle = EnTitle; diff --git a/TBF/Rig/DataEntry/Uni/EntryFormNoStartEnd.cs b/TBF/Rig/DataEntry/Uni/EntryFormNoStartEnd.cs index 829d74822..3617a1c2a 100644 --- a/TBF/Rig/DataEntry/Uni/EntryFormNoStartEnd.cs +++ b/TBF/Rig/DataEntry/Uni/EntryFormNoStartEnd.cs @@ -311,7 +311,7 @@ namespace TBF.Rig.DataEntry.Uni myCfg.TestStartBoxAlwaysEn, myCfg.TestIsLrOrder, myCfg.TestFormCloseKeys, DEItem.GetColumns(), isCompound, volumeUnit, myCfg.TestIsCameraPicture, startImages,endImages, ocr, ocrMessage, myRef.OcrStream, - myCfg.BgIsAutoReadingSerialNo, myCfg.BgAutoCloseGap); + myCfg.BgIsAutoReadingSerialNo, myCfg.BgAutoCloseGap, myCfg.DataEntryReadTimeoutSec); modelessDlg.Show(); } /// @@ -333,7 +333,8 @@ namespace TBF.Rig.DataEntry.Uni modelessDlg = new TestStartEndForm(waterMeters, myRef.regReaders, TBF.Data.LineSize, myCfg.TestTitle, myCfg.TestSize, myCfg.TestStartBoxAlwaysEn, myCfg.TestIsLrOrder, myCfg.TestFormCloseKeys, DEItem.GetColumns(), isCompound, volumeUnit, myCfg.TestIsCameraPicture, startImages, endImages, - ocr, ocrMessage, myRef.OcrStream, myCfg.BgIsAutoReadingSerialNo, myCfg.BgAutoCloseGap, wmStartStateStr, refVolume, errLimLo, errLimHi); + ocr, ocrMessage, myRef.OcrStream, myCfg.BgIsAutoReadingSerialNo, myCfg.BgAutoCloseGap, + myCfg.DataEntryReadTimeoutSec, wmStartStateStr, refVolume, errLimLo, errLimHi); modelessDlg.Show(); } /// @@ -355,7 +356,8 @@ namespace TBF.Rig.DataEntry.Uni modelessDlg = new TestStartEndForm(waterMeters, myRef.regReaders, TBF.Data.LineSize, myCfg.TestTitle, myCfg.TestSize, myCfg.TestStartBoxAlwaysEn, myCfg.TestIsLrOrder, myCfg.TestFormCloseKeys, DEItem.GetColumns(), isCompound, volumeUnit, myCfg.TestIsCameraPicture, startImages, endImages, - ocr, ocrMessage, myRef.OcrStream, myCfg.BgIsAutoReadingSerialNo,myCfg.BgAutoCloseGap, wmStartStateStr, refVolume, errLimLo, errLimHi); + ocr, ocrMessage, myRef.OcrStream, myCfg.BgIsAutoReadingSerialNo, myCfg.BgAutoCloseGap, + myCfg.DataEntryReadTimeoutSec, wmStartStateStr, refVolume, errLimLo, errLimHi); modelessDlg.Show(); } /// diff --git a/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs b/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs index be237a343..cf7d09c21 100644 --- a/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs +++ b/TBF/Rig/DataEntry/Uni/TestStartEndForm.cs @@ -73,6 +73,8 @@ namespace TBF.Rig.DataEntry.Uni /// - in seconds /// int iAutocloseGap; + const int DefaultDataEntryReadTimeoutSec = 5; + readonly int dataEntryReadTimeoutMs; /// Derived from arguments in the constructor readonly bool isEnd; @@ -173,7 +175,8 @@ namespace TBF.Rig.DataEntry.Uni public TestStartEndForm(IList waterMeters, IRegReader[] regReaders, int _lineSize, string title, FontSz sz, bool isStartBoxAlwaysEn, bool isLrOrder, string formCloseKeys, IList colItems, bool isCompound, Unit initialVolumeUnit, bool isCameraPicture, string[] startImages, string[] endImages, - OcrVidi ocrVidi, string ocrMessage, string streamName, bool bAutoRead, int iAutocloseGap, string[] wmStartStateStr = null, + OcrVidi ocrVidi, string ocrMessage, string streamName, bool bAutoRead, int iAutocloseGap, + int dataEntryReadTimeoutSec, string[] wmStartStateStr = null, double refVolume = 0, double errLimLo = 0, double errLimHi = 0) : this() { @@ -200,6 +203,10 @@ namespace TBF.Rig.DataEntry.Uni this.warningLimHi = 2 * errLimHi; this.bAutoRead = bAutoRead; this.iAutocloseGap = iAutocloseGap; + int effectiveTimeoutSec = dataEntryReadTimeoutSec > 0 + ? dataEntryReadTimeoutSec + : DefaultDataEntryReadTimeoutSec; + this.dataEntryReadTimeoutMs = Math.Min(effectiveTimeoutSec, Int32.MaxValue / 1000) * 1000; if (waterMeters == null || regReaders == null || (wmStartStateStr != null && wmStartStateStr.Length != (isCompound ? 2 : 1) * waterMeters.Count)) @@ -1726,11 +1733,11 @@ namespace TBF.Rig.DataEntry.Uni double volume = Double.NaN; if (isEnd) { - volume = await r.DataEntry_ReadEndVolume().ConfigureAwait(false); + volume = await r.DataEntry_ReadEndVolume(dataEntryReadTimeoutMs).ConfigureAwait(false); } else { - volume = await r.DataEntry_ReadBeginVolume().ConfigureAwait(false); + volume = await r.DataEntry_ReadBeginVolume(dataEntryReadTimeoutMs).ConfigureAwait(false); } log.DebugFormat( "DATA_ENTRY_VOLUME_READ_RESULT Name={0}, Position={1}, DebugLevel={2}, " + diff --git a/TBF/Rig/GenericDevices/IRegReaderSmart.cs b/TBF/Rig/GenericDevices/IRegReaderSmart.cs index 561546eb1..fc3b2d99b 100644 --- a/TBF/Rig/GenericDevices/IRegReaderSmart.cs +++ b/TBF/Rig/GenericDevices/IRegReaderSmart.cs @@ -6,8 +6,8 @@ namespace TBF.Rig.GenericDevices public interface IRegReaderSmart : ISmartMeterReader { Task DataEntry_ReadSerialNumber(); - Task DataEntry_ReadBeginVolume(); - Task DataEntry_ReadEndVolume(); + Task DataEntry_ReadBeginVolume(int timeoutMs = 3000); + Task DataEntry_ReadEndVolume(int timeoutMs = 3000); int Group { get; } int MuxBoardNrOrGroup14 { get; } diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyMeterReader.cs b/TBF/Rig/RegisterReaders/AllyReader/AllyMeterReader.cs index ef376e5df..48c9cbef0 100644 --- a/TBF/Rig/RegisterReaders/AllyReader/AllyMeterReader.cs +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyMeterReader.cs @@ -24,7 +24,7 @@ namespace TBF.Rig.RegisterReaders.AllyReader ISmartReader { private const int DataEntryCommandTimeoutMs = 5000; - private const int DataEntryOpticalTimeoutMs = 10000; + private const int DefaultDataEntryOpticalTimeoutMs = 3000; private const int MaxStoredSamples = 40000; private const long RawVolumeModulo = 0x1000000L; private const long RawVolumeHalfRange = RawVolumeModulo / 2; @@ -309,14 +309,14 @@ namespace TBF.Rig.RegisterReaders.AllyReader }); } - public Task DataEntry_ReadBeginVolume() + public Task DataEntry_ReadBeginVolume(int timeoutMs = DefaultDataEntryOpticalTimeoutMs) { - return ReadDataEntryVolume(true); + return ReadDataEntryVolume(true, timeoutMs); } - public Task DataEntry_ReadEndVolume() + public Task DataEntry_ReadEndVolume(int timeoutMs = DefaultDataEntryOpticalTimeoutMs) { - return ReadDataEntryVolume(false); + return ReadDataEntryVolume(false, timeoutMs); } public AllyVersionInfo ReadVersionAndType(int timeoutMs) @@ -560,7 +560,7 @@ namespace TBF.Rig.RegisterReaders.AllyReader endWMState = reader.ReadDouble(); } - private Task ReadDataEntryVolume(bool isBegin) + private Task ReadDataEntryVolume(bool isBegin, int timeoutMs) { return Task.Run(() => { @@ -572,7 +572,8 @@ namespace TBF.Rig.RegisterReaders.AllyReader try { Start(); - DateTime deadline = DateTime.UtcNow.AddMilliseconds(DataEntryOpticalTimeoutMs); + int effectiveTimeoutMs = Math.Max(1, timeoutMs); + DateTime deadline = DateTime.UtcNow.AddMilliseconds(effectiveTimeoutMs); while (DateTime.UtcNow < deadline) { RunDeviceBefore(); @@ -594,7 +595,7 @@ namespace TBF.Rig.RegisterReaders.AllyReader log.WarnFormat( "ALLY Data Entry {0} volume timeout after {1} ms on COM{2}", isBegin ? "begin" : "end", - DataEntryOpticalTimeoutMs, + effectiveTimeoutMs, allyCfg.OptoComPortNr); return Double.NaN; } diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs index dbec27810..90873ab34 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs @@ -36,6 +36,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { private static readonly ILog log = LogManager.GetLogger(typeof(GenesisSmartReader)); private static readonly ILog logStream = LogManager.GetLogger("StreamData"); + private const int DefaultDataEntryVolumeTimeoutMs = 3000; public override string ToString() { @@ -2901,26 +2902,26 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations return SerialNr; } - public Task DataEntry_ReadBeginVolume() + public Task DataEntry_ReadBeginVolume(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { log.Debug("called DataEntry_ReadBeginVolumer()"); - Task readedVolume = DataEntry_BeginVolumeAsync(); + Task readedVolume = DataEntry_BeginVolumeAsync(timeoutMs); return readedVolume; } - public Task DataEntry_ReadEndVolume() + public Task DataEntry_ReadEndVolume(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { log.Debug("called DataEntry_ReadBeginVolumer()"); - Task readedVolume = DataEntry_EndVolumeAsync(); + Task readedVolume = DataEntry_EndVolumeAsync(timeoutMs); return readedVolume; } - public async Task DataEntry_EndVolumeAsync() + public async Task DataEntry_EndVolumeAsync(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { if (optoSerialPort == null || !optoSerialPort.IsOpen) @@ -2939,13 +2940,16 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations int ch = channel0 >= 0 ? channel0 : 0; volumeLtr[ch] = Double.NaN; - int counter = 0; - while (Double.IsNaN(volumeLtr[ch]) && counter < 10) + DateTime readDeadline = DateTime.UtcNow.AddMilliseconds(Math.Max(1, timeoutMs)); + while (Double.IsNaN(volumeLtr[ch])) { - counter++; + int remainingTimeoutMs = (int)(readDeadline - DateTime.UtcNow).TotalMilliseconds; + if (remainingTimeoutMs <= 0) + break; + try { - string readOptoDataWithTimeout = ReadOptoDataWithTimeout(3000); + string readOptoDataWithTimeout = ReadOptoDataWithTimeout(remainingTimeoutMs); if (!string.IsNullOrEmpty(readOptoDataWithTimeout)) { try @@ -2983,9 +2987,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr}"); if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort(); - if (!Double.IsNaN(volumeLtr[channel0])) + if (!Double.IsNaN(volumeLtr[ch])) { - endWMState = volumeLtr[channel0]; + endWMState = volumeLtr[ch]; if (!Double.IsNaN(beginWMState) && !Double.IsNaN(endWMState)) { //Solve roll over @@ -2994,7 +2998,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations log.Debug($"Solve roll over! endWMState: {endWMState} < beginWMState: {beginWMState}"); const double VOL_RANGE_LITERS = 16777216.0 * 0.00025; // 4,194.304 l endWMState += VOL_RANGE_LITERS; - volumeLtr[channel0] = endWMState; + volumeLtr[ch] = endWMState; ReadPulses(); log.Debug( $"Solve roll over! Upgraded endWMState: {endWMState}, beginWMState: {beginWMState}"); @@ -3011,7 +3015,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations } - public async Task DataEntry_BeginVolumeAsync() + public async Task DataEntry_BeginVolumeAsync(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { if (ConfigStruct == null) { @@ -3029,13 +3033,16 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations int ch = channel0 >= 0 ? channel0 : 0; volumeLtr0[ch] = Double.NaN; - int counter = 0; - while (Double.IsNaN(volumeLtr0[ch]) && counter < 10) + DateTime readDeadline = DateTime.UtcNow.AddMilliseconds(Math.Max(1, timeoutMs)); + while (Double.IsNaN(volumeLtr0[ch])) { - counter++; + int remainingTimeoutMs = (int)(readDeadline - DateTime.UtcNow).TotalMilliseconds; + if (remainingTimeoutMs <= 0) + break; + try { - string readOptoDataWithTimeout = ReadOptoDataWithTimeout(5000); + string readOptoDataWithTimeout = ReadOptoDataWithTimeout(remainingTimeoutMs); if (!string.IsNullOrEmpty(readOptoDataWithTimeout)) { try diff --git a/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs b/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs index 105a4a7e5..c45844914 100644 --- a/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs +++ b/TBF/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs @@ -51,6 +51,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead private const int StartSampleDelaySec = 5; private const int EndSampleDelayCount = 2; + private const int DefaultDataEntryVolumeTimeoutMs = 3000; private OptoHeadTest _optoHeadTest; @@ -1671,26 +1672,26 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead return SerialNr; } - public Task DataEntry_ReadBeginVolume() + public Task DataEntry_ReadBeginVolume(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { log.Debug("called DataEntry_ReadBeginVolumer()"); - Task readedVolume = DataEntry_BeginVolumeAsync(); + Task readedVolume = DataEntry_BeginVolumeAsync(timeoutMs); return readedVolume; } - public Task DataEntry_ReadEndVolume() + public Task DataEntry_ReadEndVolume(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { log.Debug("called DataEntry_ReadBeginVolumer()"); - Task readedVolume = DataEntry_EndVolumeAsync(); + Task readedVolume = DataEntry_EndVolumeAsync(timeoutMs); return readedVolume; } - public async Task DataEntry_EndVolumeAsync() + public async Task DataEntry_EndVolumeAsync(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { if (optoSerialPort == null || !optoSerialPort.IsOpen) @@ -1708,13 +1709,16 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}"); volumeLtr = Double.NaN; - int counter = 0; - while (Double.IsNaN(volumeLtr) && counter < 2) + DateTime readDeadline = DateTime.UtcNow.AddMilliseconds(Math.Max(1, timeoutMs)); + while (Double.IsNaN(volumeLtr)) { - counter++; + int remainingTimeoutMs = (int)(readDeadline - DateTime.UtcNow).TotalMilliseconds; + if (remainingTimeoutMs <= 0) + break; + try { - string readOptoDataWithTimeout = ReadOptoDataWithTimeout(2000); + string readOptoDataWithTimeout = ReadOptoDataWithTimeout(remainingTimeoutMs); if (!string.IsNullOrEmpty(readOptoDataWithTimeout)) { try @@ -1766,7 +1770,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead } - public async Task DataEntry_BeginVolumeAsync() + public async Task DataEntry_BeginVolumeAsync(int timeoutMs = DefaultDataEntryVolumeTimeoutMs) { if (ConfigStruct == null) { @@ -1782,13 +1786,16 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead Start(); volumeLtr0 = Double.NaN; - int counter = 0; - while (Double.IsNaN(volumeLtr0) && counter < 10) + DateTime readDeadline = DateTime.UtcNow.AddMilliseconds(Math.Max(1, timeoutMs)); + while (Double.IsNaN(volumeLtr0)) { - counter++; + int remainingTimeoutMs = (int)(readDeadline - DateTime.UtcNow).TotalMilliseconds; + if (remainingTimeoutMs <= 0) + break; + try { - string readOptoDataWithTimeout = ReadOptoDataWithTimeout(5000); + string readOptoDataWithTimeout = ReadOptoDataWithTimeout(remainingTimeoutMs); if (!string.IsNullOrEmpty(readOptoDataWithTimeout)) { try