diff --git a/Results/Entities/WaterMeter.cs b/Results/Entities/WaterMeter.cs index a80e5d927..b9ec8e867 100644 --- a/Results/Entities/WaterMeter.cs +++ b/Results/Entities/WaterMeter.cs @@ -58,9 +58,11 @@ namespace Results.Entities #if IPERL public virtual int SerialNrEx { get; set; } /// Aux s/n for compound meters, Serial number for iPerl water meter - public virtual double OrigCalibFactor { get; set; } /// iPerl calibration factor used during the test - public virtual double CalibFactor { get; set; } /// iPerl calibration factor used during the test - public virtual double Q2ErrWOCorrection { get; set; } + public virtual double OrigCalibFactor { get; set; } /// Original iPerl calibration factor used during the test + public virtual double CalibFactor { get; set; } /// iPerl calibration factor used during the test + public virtual double OrigCalibFactorLNA { get; set; } /// Original iPerl LNA calibration factor used during the test + public virtual double CalibFactorLNA { get; set; } /// iPerl LNA calibration factor used during the test + public virtual double Q2ErrWOCorrection { get; set; } public virtual bool Q2CorrectionDone { get; set; } /// true = iPerl Q2 correction was done public virtual double Q2Correction { get; set; } /// !!! obsolete public virtual int Q2CorrRFlow { get; set; } /// iPerl Q2 correction for the R-flow written to iPerl @@ -264,6 +266,21 @@ namespace Results.Entities return null; } + public virtual bool TryGetTestRslt(string testName, out TestRslt testRslt) + { + MeterTestRslt mtr = GetMeterTestRslt(testName); + if (mtr != null) + { + testRslt = mtr.TestRslt; + return true; + } + else + { + testRslt = null; + return false; + } + } + public virtual TestData GetTestData(string testName) { foreach (var mtr in MeterTestRslts) @@ -276,6 +293,21 @@ namespace Results.Entities return null; } + public virtual bool TryGetTestData(string testName, out TestData testData) + { + foreach (var mtr in MeterTestRslts) + { + if (mtr.TestRslt.TestData.Name == testName) + { + testData = mtr.TestRslt.TestData; + return true; + } + } + + testData = null; + return false; + } + /// diff --git a/Results/Mappings/WaterMeterMap.cs b/Results/Mappings/WaterMeterMap.cs index d13b5d792..07d3fdc94 100644 --- a/Results/Mappings/WaterMeterMap.cs +++ b/Results/Mappings/WaterMeterMap.cs @@ -31,7 +31,13 @@ namespace Results.Mappings Map(x => x.SerialNrEx); Map(x => x.OrigCalibFactor); Map(x => x.CalibFactor); - Map(x => x.Q2ErrWOCorrection); +#endif +#if IPERL2 + Map(x => x.OrigCalibFactorLNA); + Map(x => x.CalibFactorLNA); +#endif +#if IPERL + Map(x => x.Q2ErrWOCorrection); Map(x => x.Q2CorrectionDone); Map(x => x.Q2Correction); Map(x => x.Q2CorrRFlow); diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs index 7fe8c5938..aa0a30462 100644 --- a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs @@ -1,5 +1,5 @@ /// -/// Copyright (c) 2015-2017 Sensus Metering Systems +/// Copyright (c) 2015-2018 Sensus Slovensko a.s. /// //#define VERIFY_ACTIVE_MODE //#define VERIFY_Q2_CORR_RESET @@ -41,6 +41,7 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication MissingTest, /// F = 15 RFPowerRecordMissing, /// 10H = 16 HeadDisabledByUser, /// 11H = 17 + WrongArguments, /// 12H = 18 } @@ -171,8 +172,12 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : ConfigStruct.FromByteArray(buffer).ToString()); else if ((messageID == MessageID.Calibration) && (offset == 0) && (lenght == CalibrationStruct.Length)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : CalibrationStruct.FromByteArray(buffer).ToString()); + else if ((messageID == MessageID.Calibration) && (offset == 0) && (lenght == CalibrationStructV4.Length)) + rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : CalibrationStructV4.FromByteArray(buffer).ToString()); else if ((messageID == MessageID.Calibration) && (offset == 2) && (lenght == 2)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},2,2,...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : string.Format("Cal={0}", buf[0] + 256 * buf[1])); + else if ((messageID == MessageID.Calibration) && (offset == 34) && (lenght == 2)) + rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},34,2,...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : string.Format("CalLNA={0}", buf[0] + 256 * buf[1])); else rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1}, {2}, {3}, ...) returned {4} {5}", name, messageID, offset, lenght, retv, (retv != 0) ? "!" : ""); @@ -231,20 +236,23 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication #endregion DLL_Interface - const string ReadConfigurationStr = "Read configuration"; /// Example: "Read configuration" or "Read configuration if enabled" - const string SetTestModeStr = "Set Test mode"; /// Example: "Set Test mode" or "Set Test mode A0" (hexadecimal number is the required 'testModeConfig' - const string SetActiveModeStr = "Set Active mode"; - const string ReadCalibrationStr = "Read calibration"; - const string WriteCalibrationFactorStr = "Write calibration factor"; - const string NormalizeCalibrationFactorStr = "Normalize calibration factor"; - const string ResetQ2CorrectionStr = "Reset Q2 correction"; - const string WriteQ2CorrectionStr = "Write Q2 correction"; - const string WriteQ2CorrectionAltStr = "Write Q2 correction Alt"; - const string WriteQ2CorrectionGreeceStr = "Write Q2 correction Greece"; - const string WriteQ2CorrectionRLStr = "Write Q2 correction R-L"; - const string WriteQ2CorrectionLRStr = "Write Q2 correction L-R"; - const string Reset2HzCorrectionStr = "Reset 2Hz correction"; - const string Write2HzCorrectionStr = "Write 2Hz correction"; + public const string ReadConfigurationStr = "Read configuration"; /// Example: "Read configuration" or "Read configuration if enabled" + public const string SetTestModeStr = "Set Test mode"; /// Example: "Set Test mode" or "Set Test mode A0" (hexadecimal number is the required 'testModeConfig' + public const string SetActiveModeStr = "Set Active mode"; + public const string ReadCalibrationStr = "Read calibration"; + public const string ReadCalibrationV4Str = "Read calibration_V4"; + public const string WriteCalibrationFactorStr = "Write calibration factor"; + public const string WriteCalibrationV4FactorsStr = "Write calibration_V4"; + public const string NormalizeCalibrationFactorStr = "Normalize calibration factor"; + public const string NormalizeCalibrationV4FactorsStr = "Normalize calibration_V4"; + public const string ResetQ2CorrectionStr = "Reset Q2 correction"; + public const string WriteQ2CorrectionStr = "Write Q2 correction"; + public const string WriteQ2CorrectionAltStr = "Write Q2 correction Alt"; + public const string WriteQ2CorrectionGreeceStr = "Write Q2 correction Greece"; + public const string WriteQ2CorrectionRLStr = "Write Q2 correction R-L"; + public const string WriteQ2CorrectionLRStr = "Write Q2 correction L-R"; + public const string Reset2HzCorrectionStr = "Reset 2Hz correction"; + public const string Write2HzCorrectionStr = "Write 2Hz correction"; readonly bool checkBoxesEditMode; @@ -732,21 +740,24 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication /// /// RFID communication activity call /// - if (ihead.Disabled || !ckbState[wmNr0]) error = CommErr.HeadDisabledByUser; - else if (activity.ToLower().Contains(ReadConfigurationStr.ToLower())) error = ReadConfiguration(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Contains(SetTestModeStr.ToLower())) error = SetTestMode(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Equals(SetActiveModeStr.ToLower())) error = SetActiveMode(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Equals(ReadCalibrationStr.ToLower())) error = ReadCalibration(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Contains(WriteCalibrationFactorStr.ToLower())) error = WriteCalibrationFactor(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Equals(NormalizeCalibrationFactorStr.ToLower())) error = NormalizeCalibrationFactor(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Equals(ResetQ2CorrectionStr.ToLower())) error = ResetQ2Correction(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Equals(WriteQ2CorrectionStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.Standard, null, false); - else if (activity.ToLower().Equals(WriteQ2CorrectionAltStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.Dewa, null, false); - else if (activity.ToLower().Contains(WriteQ2CorrectionGreeceStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.Greece, activity.Substring(WriteQ2CorrectionGreeceStr.Length).Trim(), true); - else if (activity.ToLower().Contains(WriteQ2CorrectionRLStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.RL, activity.Substring(WriteQ2CorrectionRLStr.Length).Trim(), true); - else if (activity.ToLower().Contains(WriteQ2CorrectionLRStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.LR, activity.Substring(WriteQ2CorrectionLRStr.Length).Trim(), true); - else if (activity.ToLower().Equals(Reset2HzCorrectionStr.ToLower())) error = Reset2HzCorrection(threadID, ihead, wm, ref resultStr); - else if (activity.ToLower().Equals(Write2HzCorrectionStr.ToLower())) error = Write2HzCorrection(threadID, ihead, wm, ref resultStr); + if (ihead.Disabled || !ckbState[wmNr0]) error = CommErr.HeadDisabledByUser; + else if (activity.ToLower().Contains(ReadConfigurationStr.ToLower())) error = ReadConfiguration(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Contains(SetTestModeStr.ToLower())) error = SetTestMode(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(SetActiveModeStr.ToLower())) error = SetActiveMode(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(ReadCalibrationStr.ToLower())) error = ReadCalibration(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(ReadCalibrationV4Str.ToLower())) error = ReadCalibrationV4(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Contains(WriteCalibrationFactorStr.ToLower())) error = WriteCalibrationFactor(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Contains(WriteCalibrationV4FactorsStr.ToLower())) error = WriteCalibrationV4Factors(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(NormalizeCalibrationFactorStr.ToLower())) error = NormalizeCalibrationFactor(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(NormalizeCalibrationV4FactorsStr.ToLower())) error = NormalizeCalibrationV4Factors(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(ResetQ2CorrectionStr.ToLower())) error = ResetQ2Correction(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(WriteQ2CorrectionStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.Standard, null, false); + else if (activity.ToLower().Equals(WriteQ2CorrectionAltStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.Dewa, null, false); + else if (activity.ToLower().Contains(WriteQ2CorrectionGreeceStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.Greece, activity.Substring(WriteQ2CorrectionGreeceStr.Length).Trim(), true); + else if (activity.ToLower().Contains(WriteQ2CorrectionRLStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.RL, activity.Substring(WriteQ2CorrectionRLStr.Length).Trim(), true); + else if (activity.ToLower().Contains(WriteQ2CorrectionLRStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, ref resultStr, Q2CorrType.LR, activity.Substring(WriteQ2CorrectionLRStr.Length).Trim(), true); + else if (activity.ToLower().Equals(Reset2HzCorrectionStr.ToLower())) error = Reset2HzCorrection(threadID, ihead, wm, ref resultStr); + else if (activity.ToLower().Equals(Write2HzCorrectionStr.ToLower())) error = Write2HzCorrection(threadID, ihead, wm, ref resultStr); else { error = CommErr.None; @@ -1071,16 +1082,61 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication } + /// + /// Read a complete calibration structure from the watermeter + /// + /// Water meter object + /// String passed to caller + /// true on success + static CommErr ReadCalibrationV4(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) + { + if (ihead.CommFailed) return CommErr.CommFailed; + + if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port + + CommErr error = CommErr.Read; + int readRetVal = 0; + + /// Read calibration + byte[] calib = null; + for (int j = 0; j < cfg.MaxCommRetries; j++) + { + readRetVal = ReadRequestPort(threadId, ihead, MessageID.Calibration, 0, CalibrationStructV4.Length, out calib, cfg.CommTimeout); + if (readRetVal == 0) + { + ihead.CalibrationStructV4 = CalibrationStructV4.FromByteArray(calib); +#if IPERL + if (wm != null && wm.OrigCalibFactor == 0 && wm.OrigCalibFactorLNA == 0) + { + wm.OrigCalibFactor = ihead.CalibrationStructV4.Calibration; + wm.OrigCalibFactorLNA = ihead.CalibrationStructV4.CalibrationLNA; + wm.FWVersion = ihead.CalibrationStructV4.FWVersionStr(); + } +#endif + resultStr = ihead.CalibrationStructV4.ToString(); + error = ihead.VerifyIPerlType() ? CommErr.None : CommErr.WrongIPerlType; + break; + } + } + + ClosePort(threadId, ihead); /// Close RFID port + + return error + Math.Max(0, Math.Min(readRetVal, 4)); + } + + /// /// Write the calculated calibration factor to the water meter. /// Read a part of CalibrationStruct afterwards to verify factor was written correctly. /// - /// Water meter object - /// String passed to caller - /// true on success + /// Thread ID + /// IperlHead object + /// Water meter object + /// String passed to caller + /// true on success static CommErr WriteCalibrationFactor(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { - if (ihead.CommFailed) return CommErr.CommFailed; + if (ihead.CommFailed || (ihead.CalibrationStruct == null)) return CommErr.CommFailed; /// /// Parse calibration factor (1 argument) or calibration factor limits (2 arguments) @@ -1202,6 +1258,218 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication } + /// + /// Write the calculated calibration factor to the water meter. + /// Read a part of CalibrationStruct afterwards to verify factor was written correctly. + /// + /// Thread ID + /// IperlHead object + /// Water meter object + /// String passed to caller + /// true on success + static CommErr WriteCalibrationV4Factors(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) + { + if (ihead.CommFailed || (ihead.CalibrationStructV4 == null)) return CommErr.CommFailed; + + /// + /// Parse calibration factor (1 argument) or calibration factor limits (2 arguments) + /// + UInt16 newCalibFactor = 0; + UInt16 newCalibFactorLNA = 0; + if (multiTestParams[currentActivityStep].Activity.Length > WriteCalibrationV4FactorsStr.Length) + { + UInt16 factorLimitLo = 0; + UInt16 factorLimitHi = 0; + UInt16 lnaFactorLimitLo = 0; + UInt16 lnaFactorLimitHi = 0; + Results.Entities.TestData adjustTestData = null; + Results.Entities.TestData lnaAdjustTestData = null; + Results.Entities.MeterTestRslt adjustTestRslt = null; + Results.Entities.MeterTestRslt lnaAdjustTestRslt = null; + + string calibFactrorStr = multiTestParams[currentActivityStep].Activity.Substring(WriteCalibrationV4FactorsStr.Length + 1); + string[] arguments = calibFactrorStr.Split(new char[] { ' ' }); + + if (arguments.Length == 6) + { + if (!wm.TryGetTestData(arguments[0], out adjustTestData) || + !UInt16.TryParse(arguments[1], out factorLimitLo) || factorLimitLo <= 0 && + !UInt16.TryParse(arguments[2], out factorLimitHi) || factorLimitHi <= 0 && + !wm.TryGetTestData(arguments[3], out lnaAdjustTestData) || + !UInt16.TryParse(arguments[4], out lnaFactorLimitLo) || lnaFactorLimitLo <= 0 && + !UInt16.TryParse(arguments[5], out lnaFactorLimitHi) || lnaFactorLimitHi <= 0) + { + return CommErr.WrongArguments; + } + else + { + adjustTestRslt = GetAverageTestRslt(wm, adjustTestData); + lnaAdjustTestRslt = GetAverageTestRslt(wm, lnaAdjustTestData); + + if ((adjustTestRslt == null) || (lnaAdjustTestRslt == null)) + { + return CommErr.MissingTest; + } + + newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, factorLimitLo, factorLimitHi); + newCalibFactorLNA = ihead.CalculateNewCalibFactor(lnaAdjustTestRslt, ihead.CalibFactorLNA, lnaFactorLimitLo, lnaFactorLimitHi); + } + } + else if (arguments.Length == 4) + { + if (!wm.TryGetTestData(arguments[0], out adjustTestData) || + !wm.TryGetTestData(arguments[1], out lnaAdjustTestData) || + !UInt16.TryParse(arguments[2], out lnaFactorLimitLo) || lnaFactorLimitLo <= 0 && + !UInt16.TryParse(arguments[3], out lnaFactorLimitHi) || lnaFactorLimitHi <= 0) + { + return CommErr.WrongArguments; + } + else + { + factorLimitLo = ihead.FactorLimitLo; + factorLimitHi = ihead.FactorLimitHi; + + adjustTestRslt = GetAverageTestRslt(wm, adjustTestData); + lnaAdjustTestRslt = GetAverageTestRslt(wm, lnaAdjustTestData); + + if ((adjustTestRslt == null) || (lnaAdjustTestRslt == null)) + { + return CommErr.MissingTest; + } + + newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, factorLimitLo, factorLimitHi); + newCalibFactorLNA = ihead.CalculateNewCalibFactor(lnaAdjustTestRslt, ihead.CalibFactorLNA, lnaFactorLimitLo, lnaFactorLimitHi); + } + } + else if (arguments.Length == 2) + { + if (!wm.TryGetTestData(arguments[0], out adjustTestData) || + !wm.TryGetTestData(arguments[1], out lnaAdjustTestData)) + { + return CommErr.WrongArguments; + } + else + { + factorLimitLo = ihead.FactorLimitLo; + factorLimitHi = ihead.FactorLimitHi; + lnaFactorLimitLo = ihead.FactorLimitLo; + lnaFactorLimitHi = ihead.FactorLimitHi; + + adjustTestRslt = GetAverageTestRslt(wm, adjustTestData); + lnaAdjustTestRslt = GetAverageTestRslt(wm, lnaAdjustTestData); + + if ((adjustTestRslt == null) || (lnaAdjustTestRslt == null)) + { + return CommErr.MissingTest; + } + + newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, factorLimitLo, factorLimitHi); + newCalibFactorLNA = ihead.CalculateNewCalibFactor(lnaAdjustTestRslt, ihead.CalibFactorLNA, lnaFactorLimitLo, lnaFactorLimitHi); + } + } + else + { + return CommErr.WrongArguments; + } + } + else + { + return CommErr.WrongArguments; + } + + if (newCalibFactor == 0 || newCalibFactorLNA == 0) return CommErr.CalibFactorOoRange; + + /// + /// Start communication with iPerl + /// + if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port + + CommErr error; + byte[] data = new byte[2] { (byte)(newCalibFactor & 0x00FF), (byte)((newCalibFactor >> 8) & 0x00FF) }; + byte[] dataLNA = new byte[2] { (byte)(newCalibFactorLNA & 0x00FF), (byte)((newCalibFactorLNA >> 8) & 0x00FF) }; + int writeAndVerifyRetries = 0; + do + { + /// + /// Write the new calibration factor (up to cfg.MaxCommRetries tims) + /// + error = CommErr.Write; + if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout) && + 0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 34, 2, dataLNA, cfg.CommTimeout)) + { + /// + /// Read and verify the calibration factor + /// + error = CommErr.ReadAfterWrite; + byte[] calib_2_3 = null; + byte[] calib_34_35 = null; + if (0 == ReadRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout) && + 0 == ReadRequestPort(threadId, ihead, MessageID.Calibration, 34, 2, out calib_34_35, cfg.CommTimeout)) + { + error = CommErr.Verify; + if (calib_2_3 != null && calib_2_3.Length == 2 && data[0] == calib_2_3[0] && data[1] == calib_2_3[1] && + calib_34_35 != null && calib_34_35.Length == 2 && dataLNA[0] == calib_34_35[0] && dataLNA[1] == calib_34_35[1]) + { + error = CommErr.None; + ihead.CalibrationStructV4.Update(data, 2); + ihead.CalibrationStructV4.Update(dataLNA, 34); +#if IPERL + if (wm != null) + { + wm.CalibFactor = newCalibFactor; + wm.CalibFactorLNA = newCalibFactorLNA; + } +#endif + resultStr = ihead.CalibrationStructV4.ToString(); + break; + } + } + } + + ihead.CalibrationStructV4.Update(data, 2); + ihead.CalibrationStructV4.Update(dataLNA, 34); + } + while (++writeAndVerifyRetries <= cfg.MaxCommRetries); + + ClosePort(threadId, ihead); /// Close RFID port + + return error; + } + + + /// + /// Get averaged meter test result using test name and repetitions from a given test data. + /// + /// Water meter + /// TestData (name and repetitions) + /// (1) selected MeterTestRslt or (2) average of repeated MTR-s or (3) null when at least one MTR is missing + static Results.Entities.MeterTestRslt GetAverageTestRslt(Results.Entities.WaterMeter wm, Results.Entities.TestData testData) + { + Results.Entities.MeterTestRslt avgTestRslt; + + if (testData.Repeats > 1) + { + /// Calculate a summarized test result if Repeats > 1 + avgTestRslt = new Results.Entities.MeterTestRslt(); + for (int i = 1; i <= testData.Repeats; i++) + { + Results.Entities.MeterTestRslt oneMTR = wm.GetMeterTestRslt(Utils.TestTitle(testData, i)); + if (oneMTR == null || !oneMTR.TestDone) return null; + + avgTestRslt.VolumeMeter += oneMTR.VolumeMeter; + avgTestRslt.VolumeRef += oneMTR.VolumeRef; + } + } + else + { + avgTestRslt = wm.GetMeterTestRslt(Utils.TestTitle(testData, 1)); + if (avgTestRslt == null || !avgTestRslt.TestDone) return null; + } + + return avgTestRslt; + } + + /// /// Normalize calibration factor in case ti is close to value 8000. /// @@ -1210,7 +1478,7 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication /// true on success static CommErr NormalizeCalibrationFactor(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { - if (ihead.CommFailed) return CommErr.CommFailed; + if (ihead.CommFailed || (ihead.CalibrationStruct == null)) return CommErr.CommFailed; if (ihead.FactorLimitLo <= ihead.CalibFactor && ihead.CalibFactor <= ihead.FactorLimitHi) { @@ -1223,23 +1491,23 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication CommErr error = CommErr.Write; /// Determine the new calibration factor - UInt16 newCalibFactor; + UInt16 defaultCalibFactor; switch (ihead.CalibrationStruct.MeterType) { - case MeterType.DN15: newCalibFactor = 2710; break; - case MeterType.DN20: newCalibFactor = 3746; break; - case MeterType.DN25: newCalibFactor = 3300; break; - case MeterType.DN32: newCalibFactor = 2500; break; - case MeterType.DN40: newCalibFactor = 3080; break; + case MeterType.DN15: defaultCalibFactor = 2710; break; + case MeterType.DN20: defaultCalibFactor = 3746; break; + case MeterType.DN25: defaultCalibFactor = 3300; break; + case MeterType.DN32: defaultCalibFactor = 2500; break; + case MeterType.DN40: defaultCalibFactor = 3080; break; case MeterType.DN26: case MeterType.CoaxManifold: default: - newCalibFactor = 3040; + defaultCalibFactor = 3040; break; } - byte[] data = new byte[2] { (byte)(newCalibFactor & 0x00FF), (byte)((newCalibFactor >> 8) & 0x00FF) }; + byte[] data = new byte[2] { (byte)(defaultCalibFactor & 0x00FF), (byte)((defaultCalibFactor >> 8) & 0x00FF) }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout)) @@ -1274,6 +1542,78 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication } + /// + /// Normalize calibration factor in case ti is close to value 8000. + /// + /// Water meter object + /// String passed to caller + /// true on success + static CommErr NormalizeCalibrationV4Factors(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) + { + if (ihead.CommFailed || (ihead.CalibrationStructV4 == null)) return CommErr.CommFailed; + + if (ihead.FactorLimitLo <= ihead.CalibFactor && ihead.CalibFactor <= ihead.FactorLimitHi) + { + resultStr = "Calibratin factor is OK"; + return CommErr.None; /// No need to update the calibration factor + } + + if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port + + CommErr error = CommErr.Write; + + /// Determine the new calibration factor + UInt16 defaultCalibFactor; + switch (ihead.CalibrationStructV4.MeterType) + { + case MeterType.DN15: defaultCalibFactor = 2710; break; + case MeterType.DN20: defaultCalibFactor = 3746; break; + case MeterType.DN25: defaultCalibFactor = 3300; break; + case MeterType.DN32: defaultCalibFactor = 2500; break; + case MeterType.DN40: defaultCalibFactor = 3080; break; + + case MeterType.DN26: + case MeterType.CoaxManifold: + default: + defaultCalibFactor = 3040; + break; + } + + byte[] data = new byte[2] { (byte)(defaultCalibFactor & 0x00FF), (byte)((defaultCalibFactor >> 8) & 0x00FF) }; + for (int j = 0; j < cfg.MaxCommRetries; j++) + { + if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout)) + { + error = CommErr.None; + ihead.CalibrationStructV4.Update(data, 2); + break; + } + } + + if (error == CommErr.None) + { + error = CommErr.Verify; + + /// Read calibration + byte[] calib_2_3 = null; + for (int j = 0; j < cfg.MaxCommRetries; j++) + { + if (0 == ReadRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout)) + { + error = CommErr.None; + ihead.CalibrationStructV4.Update(calib_2_3, 2); + resultStr = ihead.CalibrationStructV4.ToString(); + break; + } + } + } + + ClosePort(threadId, ihead); /// Close RFID port + + return error; + } + + const int Hz2CorrFactorsAddr = 0x1875; /// Used by Reset2HzCorrection(...) and Write2HzCorrection(...) const int Q2CorrFactorsAddr = 0x1878; /// Used by ResetQ2Correction(...) and WriteQ2Correction(...) @@ -1421,10 +1761,16 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication /// if (string.IsNullOrEmpty(q2TestName)) { - /// Use ihead.LastTestResult is no test name is specified as an argument - q2adjResult = ihead.LastTestResult; - q2adjTestData = q2adjResult.TestRslt.TestData; - if (q2adjResult == null || !q2adjResult.TestDone) return CommErr.MissingTest; + /// In case of no test name argument, ihead.LastTestResult is used (if it exists) + if (ihead.LastTestResult != null && ihead.LastTestResult.TestDone) + { + q2adjResult = ihead.LastTestResult; + q2adjTestData = q2adjResult.TestRslt.TestData; + } + else + { + return CommErr.MissingTest; + } } else { diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs index 39ac6b05d..9228b0213 100644 --- a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs @@ -44,25 +44,28 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication if (i == 0) { - retVal.Add("Read configuration"); - retVal.Add("Set Test mode A0"); - retVal.Add("Set Test mode A4"); - retVal.Add("Read calibration"); - retVal.Add("Normalize calibration factor"); - retVal.Add("Reset Q2 correction"); - retVal.Add("Write calibration factor"); - retVal.Add("Write Q2 correction"); - retVal.Add("Write Q2 correction Alt"); - retVal.Add("Write Q2 correction Greece"); - retVal.Add("Write Q2 correction R-L"); - retVal.Add("Write Q2 correction L-R"); + retVal.Add(iPerlCommunicationForm.ReadConfigurationStr); + retVal.Add(string.Format("{0} A0", iPerlCommunicationForm.SetTestModeStr)); + retVal.Add(string.Format("{0} A4", iPerlCommunicationForm.SetTestModeStr)); + retVal.Add(iPerlCommunicationForm.ReadCalibrationStr); + retVal.Add(iPerlCommunicationForm.ReadCalibrationV4Str); + retVal.Add(iPerlCommunicationForm.NormalizeCalibrationFactorStr); + retVal.Add(iPerlCommunicationForm.NormalizeCalibrationV4FactorsStr); + retVal.Add(iPerlCommunicationForm.ResetQ2CorrectionStr); + retVal.Add(iPerlCommunicationForm.WriteCalibrationFactorStr); + retVal.Add(iPerlCommunicationForm.WriteCalibrationV4FactorsStr); + retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionStr); + retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionAltStr); + retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionGreeceStr); + retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionRLStr); + retVal.Add(iPerlCommunicationForm.WriteQ2CorrectionLRStr); retVal.Add("Q2 corrected from Q2adj"); - retVal.Add("Set Active mode"); + retVal.Add(iPerlCommunicationForm.SetActiveModeStr); retVal.Add("---"); - retVal.Add("Reset 2Hz correction"); - retVal.Add("Write 2Hz correction"); - retVal.Add("Read configuration if enabled"); - retVal.Add("Set Test mode 80"); + retVal.Add(iPerlCommunicationForm.Reset2HzCorrectionStr); + retVal.Add(iPerlCommunicationForm.Write2HzCorrectionStr); + retVal.Add(string.Format("{0} if enabled", iPerlCommunicationForm.ReadConfigurationStr)); + retVal.Add(string.Format("{0} 80", iPerlCommunicationForm.SetTestModeStr)); } else { diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStruct.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStruct.cs index 8c71fce7c..9fb45bc43 100644 --- a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStruct.cs +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStruct.cs @@ -129,13 +129,13 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead /// true when successful, false when data are not appropriate public bool Update(byte[] data, int offset) { - if (offset == 2 && data.Length == 2) + if ((data.Length == 2) && (offset == 2)) { /// Data containing iPerl calibration factor - Calibration = (UInt16)(data[2 - offset] + 256 * data[3 - offset]); + Calibration = (UInt16)(data[0] + 256 * data[1]); return true; } - else if (offset == 0 && data.Length == Length) + else if ((data.Length == Length) && (offset == 0)) { /// Data containing a complete CalibrationStruct Version = data[0]; diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStructV4.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStructV4.cs new file mode 100644 index 000000000..1d441c2aa --- /dev/null +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/CalibrationStructV4.cs @@ -0,0 +1,211 @@ +/// +/// Copyright (c) 2018 Sensus Slovensko a.s. +/// +using System; + +namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead +{ + public class CalibrationStructV4 + { + public static readonly int Length = 37; + + public Byte Version; + public MeterType MeterType; + public UInt16 Calibration; + public VolumeUnits VolumeUnits; + public FlowArrow FlowArrow; + public UInt16 FWVersion; + public UInt16[] TargetField; + public UInt16 RecipMeanCurrent; + public UInt16 ThresholdVolume; + public UInt16 ThresholdTime; + public UInt16 FlowActivationThr; + public UInt16 VolumeArrowThr; + public UInt32 CalibrationTime; + public ulong SerialNumber; + public MeterSealed MeterSealed; + public UInt16 CalibrationLNA; + public byte CheckSum; + + + public CalibrationStructV4() + { + TargetField = new UInt16[3]; + } + + public byte[] ToByteArray() + { + byte[] result = new byte[Length]; + + result[0] = Version; + result[1] = (byte)MeterType; + + result[2] = (byte)(Calibration & 0x00FF); + result[3] = (byte)((Calibration >> 8) & 0x00FF); + + result[4] = (byte)VolumeUnits; + + result[5] = (byte)FlowArrow; + + result[6] = (byte)(FWVersion & 0x00FF); + result[7] = (byte)((FWVersion >> 8) & 0x00FF); + + result[8] = (byte)( TargetField[0] & 0x00FF); + result[9] = (byte)((TargetField[0] >> 8) & 0x00FF); + result[10] = (byte)( TargetField[1] & 0x00FF); + result[11] = (byte)((TargetField[1] >> 8) & 0x00FF); + result[12] = (byte)( TargetField[2] & 0x00FF); + result[13] = (byte)((TargetField[2] >> 8) & 0x00FF); + + result[14] = (byte)(RecipMeanCurrent & 0x00FF); + result[15] = (byte)((RecipMeanCurrent >> 8) & 0x00FF); + + result[16] = (byte)(ThresholdVolume & 0x00FF); + result[17] = (byte)((ThresholdVolume >> 8) & 0x00FF); + + result[18] = (byte)(ThresholdTime & 0x00FF); + result[19] = (byte)((ThresholdTime >> 8) & 0x00FF); + + result[20] = (byte)(FlowActivationThr & 0x00FF); + result[21] = (byte)((FlowActivationThr >> 8) & 0x00FF); + + result[22] = (byte)(VolumeArrowThr & 0x00FF); + result[23] = (byte)((VolumeArrowThr >> 8) & 0x00FF); + + result[24] = (byte)(CalibrationTime & 0x000000FF); + result[25] = (byte)((CalibrationTime >> 8) & 0x000000FF); + result[26] = (byte)((CalibrationTime >> 16) & 0x000000FF); + result[27] = (byte)((CalibrationTime >> 24) & 0x000000FF); + + result[28] = (byte)(SerialNumber & 0x00000000000000FF); + result[29] = (byte)((SerialNumber >> 8) & 0x00000000000000FF); + result[30] = (byte)((SerialNumber >> 16) & 0x00000000000000FF); + result[31] = (byte)((SerialNumber >> 24) & 0x00000000000000FF); + result[32] = (byte)((SerialNumber >> 32) & 0x00000000000000FF); + + result[33] = (byte)MeterSealed; + + result[34] = (byte)(CalibrationLNA & 0x00FF); + result[35] = (byte)((CalibrationLNA >> 8) & 0x00FF); + + result[36] = CheckSum; + + return result; + } + + /// + /// Create a calibration structure from a complete byte array + /// + /// A complete byte array data + /// CalibrationStructV4 or null when byte array was not complete + public static CalibrationStructV4 FromByteArray(byte[] data) + { + if (data.Length != Length) return null; + + CalibrationStructV4 result = new CalibrationStructV4(); + + result.Version = data[0]; + result.MeterType = (MeterType)data[1]; + result.Calibration = (UInt16)(data[2] + 256 * data[3]); + result.VolumeUnits = (VolumeUnits)data[4]; + result.FlowArrow = (FlowArrow)data[5]; + result.FWVersion = (UInt16)(data[6] + 256 * data[7]); + result.TargetField[0] = (UInt16)(data[8] + 256 * data[9]); + result.TargetField[1] = (UInt16)(data[10] + 256 * data[11]); + result.TargetField[2] = (UInt16)(data[12] + 256 * data[13]); + result.RecipMeanCurrent = (UInt16)(data[14] + 256 * data[15]); + result.ThresholdVolume = (UInt16)(data[16] + 256 * data[17]); + result.ThresholdTime = (UInt16)(data[18] + 256 * data[19]); + result.FlowActivationThr = (UInt16)(data[20] + 256 * data[21]); + result.VolumeArrowThr = (UInt16)(data[22] + 256 * data[23]); + result.CalibrationTime = (((UInt32)data[27] * 256 + data[26]) * 256 + data[25]) * 256 + data[24]; + result.SerialNumber = ((((UInt64)data[32] * 256 + data[31]) * 256 + data[30]) * 256 + data[29]) * 256 + data[28]; + result.MeterSealed = (MeterSealed)data[33]; + result.CalibrationLNA = (UInt16)(data[34] + 256 * data[35]); + result.CheckSum = data[36]; + + return result; + } + + /// + /// Update the calibration structure from an incomplete byte array + /// + /// Byte array data + /// Offset of byte array data in CalibrationStructV2 + /// true when successful, false when data are not appropriate + public bool Update(byte[] data, int offset) + { + if ((data.Length == 2) && (offset == 2)) + { + /// Data containing iPerl calibration factor + Calibration = (UInt16)(data[0] + 256 * data[1]); + return true; + } + else if ((data.Length == 2) && (offset == 34)) + { + /// Data containing iPerl calibration factor + CalibrationLNA = (UInt16)(data[0] + 256 * data[1]); + return true; + } + else if ((data.Length == Length) && (offset == 0)) + { + /// Data containing a complete CalibrationStruct + Version = data[0]; + MeterType = (MeterType)data[1]; + Calibration = (UInt16)(data[2] + 256 * data[3]); + VolumeUnits = (VolumeUnits)data[4]; + FlowArrow = (FlowArrow)data[5]; + FWVersion = (UInt16)(data[6] + 256 * data[7]); + TargetField[0] = (UInt16)(data[8] + 256 * data[9]); + TargetField[1] = (UInt16)(data[10] + 256 * data[11]); + TargetField[2] = (UInt16)(data[12] + 256 * data[13]); + RecipMeanCurrent = (UInt16)(data[14] + 256 * data[15]); + ThresholdVolume = (UInt16)(data[16] + 256 * data[17]); + ThresholdTime = (UInt16)(data[18] + 256 * data[19]); + FlowActivationThr = (UInt16)(data[20] + 256 * data[21]); + VolumeArrowThr = (UInt16)(data[22] + 256 * data[23]); + CalibrationTime = (((UInt32)data[27] * 256 + data[26]) * 256 + data[25]) * 256 + data[24]; + SerialNumber = ((((UInt64)data[32] * 256 + data[31]) * 256 + data[30]) * 256 + data[29]) * 256 + data[28]; + MeterSealed = (MeterSealed)data[33]; + CalibrationLNA = (UInt16)(data[34] + 256 * data[35]); + CheckSum = data[36]; + return true; + } + else + return false; + } + + public string FWVersionStr() + { + int d1 = (FWVersion >> 8) & 0x000F; + int d2 = (FWVersion >> 12) & 0x000F; + int d3 = (FWVersion >> 4) & 0x000F; + int d4 = FWVersion & 0x000F; + return string.Format("{0}.{1}{2}{3}", d1, d2, d3, d4); + } + + public override string ToString() + { + return string.Format("Calibration: V{0} Type={1} Cal={2} Units={3} FlowArrow.{4} FW={5} Hi={6} Norm={7} Low={8} RMC={9} ThrVol={10} ThrTime={11} FlActThr={12} VolArrThr={13} CalTm={14} SN={15} MeterSealed={16} CalLNA={17} Chksum={18}", + Version, + MeterType, + Calibration, + VolumeUnits, + FlowArrow, + FWVersion, + TargetField[0], + TargetField[1], + TargetField[2], + RecipMeanCurrent, + ThresholdVolume, + ThresholdTime, + FlowActivationThr, + VolumeArrowThr, + CalibrationTime, + SerialNumber, + MeterSealed, + CalibrationLNA, + CheckSum.ToString("X2")); + } + } +} diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs index 8acad489e..852476875 100644 --- a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs @@ -114,8 +114,28 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead } CalibrationStruct calibrationStruct; + /// CalibrationStruct of the water meter obtained or updated by iPerlCommunication + public CalibrationStructV4 CalibrationStructV4 + { + get { return calibrationStructV4; } + set { calibrationStructV4 = value; } + } + CalibrationStructV4 calibrationStructV4; + public ushort OrigCalibFactor; - public ushort CalibFactor { get { return (CalibrationStruct != null) ? CalibrationStruct.Calibration : (ushort)0; } } + public ushort CalibFactor + { + get + { + return (CalibrationStruct != null) ? CalibrationStruct.Calibration + : ((CalibrationStructV4 != null) ? CalibrationStructV4.Calibration + : (ushort)0); + } + } + + public ushort OrigCalibFactorLNA; + public ushort CalibFactorLNA { get { return (CalibrationStructV4 != null) ? CalibrationStructV4.CalibrationLNA : (ushort)0; } } + public double Q2ErrWOCorrection; public bool Q2CorrectionDone; @@ -127,7 +147,15 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead public bool Hz2CorrectionDone; public int Hz2Correction; - public string FWVersion { get { return (CalibrationStruct != null) ? CalibrationStruct.FWVersionStr() : string.Empty; } } + public string FWVersion + { + get + { + return (CalibrationStruct != null) ? CalibrationStruct.FWVersionStr() + : ((CalibrationStructV4 != null) ? CalibrationStructV4.FWVersionStr() + : string.Empty); + } + } /// Result of the last test used to calculate Q2 correction factors, etc @@ -395,11 +423,13 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead configStruct = null; calibrationStruct = null; + calibrationStructV4 = null; LastTestResult = null; NominalTestFlowLph = 0; OrigCalibFactor = 0; + OrigCalibFactorLNA = 0; Q2ErrWOCorrection = 0; Q2CorrectionDone = false; Q2CorrRFlow = 0; @@ -428,7 +458,7 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead /// Prepare serial port optoSerialPort = new SerialPort(string.Format("COM{0}", iperlHeadCfg.OptoComPortNr), - 9600, Parity.None, 8, StopBits.One); + 9600, Parity.None, 8, StopBits.One); optoSerialPort.Handshake = Handshake.None; optoSerialPort.Open(); diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs index f560b4f00..15aa683e9 100644 --- a/TBF/Properties/AssemblyInfo.cs +++ b/TBF/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("2.18.1001.0")] -[assembly: AssemblyFileVersion("2.18.1001.0")] +[assembly: AssemblyVersion("2.18.1004.0")] +[assembly: AssemblyFileVersion("2.18.1004.0")] diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index f37eeeb71..c1dc2c821 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -1134,6 +1134,7 @@ +