From c3874f85c0938c86a262264db630a50cfd23470e Mon Sep 17 00:00:00 2001 From: Stoyan Zlatev Date: Tue, 23 May 2023 14:02:52 +0200 Subject: [PATCH] Pressure measurement process done. --- .../CommonCore.Configuration/ServiceUrls.cs | 48 +++- .../CordonelPreadjustmentUi.csproj | 9 +- .../PreAdjustmentControl.cs | 5 + .../Processes/Actions/PreparationProcess.cs | 2 - .../Processes/Actions/PressureTestProcess.cs | 256 ++++++++++++++++++ .../Processes/BaseProcess.cs | 36 +-- Common/CordonelPreadjustmentUi/app.config | 5 + .../CordonelPressureSensorTestController.cs | 11 +- .../MeterProcessState/Models/Cordonel.cs | 15 +- ...ToXlsHelper.csproj.CoreCompileInputs.cache | 2 +- 10 files changed, 356 insertions(+), 33 deletions(-) create mode 100644 Common/CordonelPreadjustmentUi/Processes/Actions/PressureTestProcess.cs diff --git a/Common/CommonCore.Configuration/ServiceUrls.cs b/Common/CommonCore.Configuration/ServiceUrls.cs index 469eb12c..2d5bb901 100644 --- a/Common/CommonCore.Configuration/ServiceUrls.cs +++ b/Common/CommonCore.Configuration/ServiceUrls.cs @@ -187,8 +187,54 @@ namespace Xylem.Common.CommonCore.Configuration // "http://localhost:56011/api/SoftwareAccess/GetVersionIsValid"; + private const String PressureSensorTestURL_GetTestSpec + = "http://sla12iis01.emea.sensus.net/MeterProcessState/api/CordonelPressureSensorTest/GetTestSpec"; - + private const String PressureSensorTestURL_PostTestResults + = "http://sla12iis01.emea.sensus.net/MeterProcessState/api/CordonelPressureSensorTest/PostTestResults"; + + /// + /// usage: string.Format(CordonelPressureSensorTestURL_GetTestResults, pcbId); + /// + private const String PressureSensorTestURL_GetTestResults + = "http://sla12iis01.emea.sensus.net/MeterProcessState/api/CordonelPressureSensorTest/GetTestResults/{0}"; + + + /// + /// GET: test specification for water meter with pressure sensor. + /// + public static string GetPressureSensorSpecificationURL +#if DEBUG + => "http://localhost:56011/api/CordonelPressureSensorTest/GetTestSpec"; +#else + => ConfigurationManager.AppSettings[nameof(GetPressureSensorSpecificationURL)] + ?? PressureSensorTestURL_GetTestSpec; +#endif + + /// + /// POST: test result for water meter with pressure sensor. + /// + public static string PostPressureSensorResultURL +#if DEBUG + => "http://localhost:56011/api/CordonelPressureSensorTest/PostTestResults"; +#else + => ConfigurationManager.AppSettings[nameof(PostPressureSensorResultURL)] + ?? PressureSensorTestURL_PostTestResults; +#endif + + /// + /// GET: a list of test results for the water meter with specified pcbId. + /// + public static string PressureSensorResultsURL(this T pcbId) + { +#if DEBUG + var url = "http://localhost:56011/api/CordonelPressureSensorTest/GetTestResults/{0}"; +#else + var url = ConfigurationManager.AppSettings[nameof(PressureSensorResultsURL)] ?? PressureSensorTestURL_GetTestResults; +#endif + + return string.Format(url, pcbId); + } /// /// List all Cordonel FW packages. diff --git a/Common/CordonelPreadjustmentUi/CordonelPreadjustmentUi.csproj b/Common/CordonelPreadjustmentUi/CordonelPreadjustmentUi.csproj index 0dca42ea..b3996ca0 100644 --- a/Common/CordonelPreadjustmentUi/CordonelPreadjustmentUi.csproj +++ b/Common/CordonelPreadjustmentUi/CordonelPreadjustmentUi.csproj @@ -5,7 +5,7 @@ Debug AnyCPU {D0C8D887-ED52-40AB-A069-90BCE0E801E2} - Library + WinExe Properties Xylem.Common.Ui.CordonelPreadjustmentUi Xylem.Common.Ui.CordonelPreadjustmentUi @@ -103,6 +103,9 @@ + + true + ..\packages\Newtonsoft.Json.12.0.3\lib\net40\Newtonsoft.Json.dll @@ -116,6 +119,9 @@ + + ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Net.Http.dll + @@ -153,6 +159,7 @@ PreAdjustmentControl.cs + diff --git a/Common/CordonelPreadjustmentUi/PreAdjustmentControl.cs b/Common/CordonelPreadjustmentUi/PreAdjustmentControl.cs index c190ab64..7805f8ed 100644 --- a/Common/CordonelPreadjustmentUi/PreAdjustmentControl.cs +++ b/Common/CordonelPreadjustmentUi/PreAdjustmentControl.cs @@ -393,6 +393,7 @@ namespace CordonelPreadjustmentUi { this.l_ZeroFlowCal_TempCal.ForeColor = color; } + public enum StatusPanelItems { None = 0, @@ -1681,9 +1682,13 @@ namespace CordonelPreadjustmentUi listOfProgrammParts.Add(new FlushProcess("First Flush", StatusPanelItems.Detect, PredefinedMessages.WaitUntilFlushFinished(pp.Setting.Culture), string.Empty, 2 * 60)); + listOfProgrammParts.Add(new PressureTestProcess("PreussureTest", StatusPanelItems.Prepare, PredefinedMessages.WaitUntilPreparationFinished(pp.Setting.Culture), PredefinedMessages.PreparationFailed(pp.Setting.Culture), 1 * 60)); listOfProgrammParts.Add(new PreparationProcess("Preparation", StatusPanelItems.Prepare, PredefinedMessages.WaitUntilPreparationFinished(pp.Setting.Culture), PredefinedMessages.PreparationFailed(pp.Setting.Culture), 1 * 60)); + + + if (!pp.Setting.TempOnly) { listOfProgrammParts.Add(new AmplitudeTestProcess("Amplitude Test", StatusPanelItems.Amplitude, PredefinedMessages.WaitUntilAmplitudeTestFinished(pp.Setting.Culture), PredefinedMessages.AmplitudeFailed(pp.Setting.Culture), 4 * 60)); diff --git a/Common/CordonelPreadjustmentUi/Processes/Actions/PreparationProcess.cs b/Common/CordonelPreadjustmentUi/Processes/Actions/PreparationProcess.cs index a153d05a..53f6e3ba 100644 --- a/Common/CordonelPreadjustmentUi/Processes/Actions/PreparationProcess.cs +++ b/Common/CordonelPreadjustmentUi/Processes/Actions/PreparationProcess.cs @@ -16,8 +16,6 @@ namespace CordonelPreadjustmentUi.Processes.Actions { public class PreparationProcess : BaseProcess { - - public PreparationProcess(string processName, StatusPanelItems panelState, string performMessage, string failedMessage, int? expectedTimeS) : base(processName, panelState, performMessage, failedMessage, expectedTimeS) { } diff --git a/Common/CordonelPreadjustmentUi/Processes/Actions/PressureTestProcess.cs b/Common/CordonelPreadjustmentUi/Processes/Actions/PressureTestProcess.cs new file mode 100644 index 00000000..c9bf190d --- /dev/null +++ b/Common/CordonelPreadjustmentUi/Processes/Actions/PressureTestProcess.cs @@ -0,0 +1,256 @@ +namespace CordonelPreadjustmentUi.Processes.Actions +{ + using Newtonsoft.Json; + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + + using Xylem.Common.CommonCore.Configuration; + using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; + using Xylem.Common.Logic.SoftwareAccessHelper; + + using static CordonelPreadjustmentUi.PreAdjustmentControl; + + /// + /// Process for cordonel water meters with pressure sensor. + /// + /// + /// Just started implementing pressure test for water meters with pressure sensor. + /// + public class PressureTestProcess : BaseProcess + { + /// + /// Read / Write is pressure present for the water meter. + /// + /// + /// Just started implementing pressure test for water meters with pressure sensor. + /// + private const string PRESSURE_PRESENT = "METROLOGYASST_PressurePresent"; + /// + /// Read / Write pressure measurements for the water meter. + /// + /// + /// Just started implementing pressure test for water meters with pressure sensor. + /// + private const string PRESSURE_MEASURE = "METROLOGYASST_PressureMeasure"; + + /// + /// Constructor for passing parameters to the base constructor of the derived class. + /// + /// The name used to show into the GUI. + /// TODO: Get the right description. Probably the state for the box into the GUI. ??? + /// TODO: Get the right description. Probably the success message to show into the GUI. ??? + /// TODO: Get the right description. Probably the error message to show into the GUI. ??? + /// TODO: Get the right description. Probably the expected time for process the pressure tests. ??? + /// + /// Just started implementing pressure test for water meters with pressure sensor. + /// + public PressureTestProcess(string processName, StatusPanelItems panelState, string performMessage, string failedMessage, int? expectedTimeS) + : base(processName, panelState, performMessage, failedMessage, expectedTimeS) + { + } + + /// + /// Determinates and initializes a list of pressure measure tests processes on the logedin cordonels with pressure sensor. + /// Programm workflow for Cordonels with pressure sensor. + /// When any of the water meters has presure sensor - load the test specification. + /// Acording to the test specification process all meters, also the referenz on workplaces 11 and 12. + /// + /// Measurement as follow. + /// Measure starts with = 1 + /// Then registry is readeded and comaired to the referenz meters acording to the specification. + /// + /// + /// Returns a for processing the pressure tests on the goggedin cordonels. + /// + /// Just started implementing pressure test for water meters with pressure sensor. + /// + public override List StartWork() + { + /// List of the processes to start. + var listOfPresureTestTasks = new List(); + var pressureTestSpecification = default(PressureTestSpecification); + var pressureTestSpecificationLoaded = false; + /// Just to not lose from focus the process progrss object. + var processProgress = base.CurrentProcessProgress; + var minPressureReferenzValue = default(int?); + var maxPressureReferenzValue = default(int?); + + /// Looping trough all s to determinate whitch are with pressure sensor. + foreach (var meterStateControl in base.MeterStateCtls) + { + /// If the meter is enabled and pressure sensor is present a new pressure mesure task should be initialized and added to the processes list. + if (meterStateControl.IsEnabled) + { + /// Getting the meter object from the state control. + var meter = meterStateControl.Meter; + /// Reads the buffer for determinating if the pressure sensor is present. + var isPressurePresentBuffer = meter.ReadRegister(PRESSURE_PRESENT); + /// Converted value for determinating if the pressure sensor is present. + var isPressurePresent = RegisterConverter.ConvertTo(isPressurePresentBuffer); + + if (!isPressurePresent) + { + processProgress.DebugMessage($"Watermeter has no pressure sensor!", meterStateControl.Slot, "DC", meter.PcbId); + + continue; + } + + /// Loads the pressure test specification from the defined web service if not yet loaded. + if (pressureTestSpecification is null && !pressureTestSpecificationLoaded) + { + pressureTestSpecificationLoaded = true; + pressureTestSpecification = this.LoadPressureTestSpecification(); + + /// if loading fails + if (pressureTestSpecification is null) + { + processProgress.DebugMessage($"Test specification could not be loaded properly!", meterStateControl.Slot, "DC", meter.PcbId); + + // TODO: Handle possible exceptions by making http request. + continue; + } + } + + /// Initialize min and max referenz values ones for all meters + if (minPressureReferenzValue is null) + { + /// setting the min and max values on their oposit values. + /// checking the equality later will set up a value by 1 or more referenz meters. + minPressureReferenzValue = int.MaxValue; + maxPressureReferenzValue = int.MinValue; + + if (processProgress.Setting.GetTempUseTempFlansh() || TempMeterStateCtls.Any(a => a.IsEnabled)) + { + /// Determinates the min and max values aus temperatur meters list. + foreach (var referenzMeterState in base.TempMeterStateCtls) + { + if (referenzMeterState.IsEnabled) + { + var referenzMeter = referenzMeterState.Meter; + /// Reset meter pressure + referenzMeter.WriteRegister(PRESSURE_MEASURE, 1); + + if (!referenzMeter.IsLoggedOn) + { + referenzMeter.Login(); + } + + var referenzPressureBuffer = referenzMeter.ReadRegister(PRESSURE_MEASURE); + var referenzPressure = RegisterConverter.ConvertTo(referenzPressureBuffer); + + if (referenzPressure < minPressureReferenzValue) + { + minPressureReferenzValue = referenzPressure; + } + + if (referenzPressure > maxPressureReferenzValue) + { + maxPressureReferenzValue = referenzPressure; + } + } + } + } + + /// Continue if min pressure value is still its oposit for the type. There was no referenz zehler found. + if (minPressureReferenzValue == int.MaxValue) + { + processProgress.DebugMessage($"No referenz watermeter found!", meterStateControl.Slot, "DC", meter.PcbId); + + continue; + } + + listOfPresureTestTasks.Add(new Task(() => + { + var measuredTestValues = new int[pressureTestSpecification.NumberOfMeasurements]; + var measurementDelyInS = pressureTestSpecification.TimeMeasurementsInS * 1000; + var numberOfMeasurements = pressureTestSpecification.NumberOfMeasurements; + var testDate = DateTime.Now; + + for (int measurement = 0; measurement < numberOfMeasurements; measurement++) + { + /// Reset meter pressure + meter.WriteRegister(PRESSURE_MEASURE, 1); + + /// Read the new meter pressure + var pressureMeasureBuffer = meter.ReadRegister(PRESSURE_MEASURE); + var pressureInPascals = RegisterConverter.ConvertTo(pressureMeasureBuffer); + + measuredTestValues[measurement] = pressureInPascals; + processProgress.DebugMessage($"Measure {measurement + 1}: {pressureInPascals}Pa", meterStateControl.Slot, "DC", meter.PcbId); + + /// Dely only for first n - 1 measurements + if (measurement < numberOfMeasurements - 1) + { + Thread.Sleep(measurementDelyInS); + } + } + + var avgPressure = (int)measuredTestValues.Average(); + var testPassed = minPressureReferenzValue <= avgPressure && avgPressure <= maxPressureReferenzValue; + var avgPressureInBars = avgPressure / 100_000F; + + processProgress.DebugMessage($"Average pressure: {avgPressure}Pa", meterStateControl.Slot, "DC", meter.PcbId); + processProgress.DebugMessage($"Average pressure: {avgPressureInBars}Bar", meterStateControl.Slot, "DC", meter.PcbId); + + if (!this.PostPressureTestResult(meter.PcbId, avgPressureInBars, testPassed, testDate)) + { + processProgress.DebugMessage($"Test result was not posted!", meterStateControl.Slot, "DC", meter.PcbId); + + return false; + } + + return testPassed; + })); + } + } + } + + return listOfPresureTestTasks; + } + + private PressureTestSpecification LoadPressureTestSpecification() + { + try + { + var responseContent = LocalWebRequest.GetRequest(ServiceUrls.GetPressureSensorSpecificationURL); + + return JsonConvert.DeserializeObject(responseContent); + } + catch (Exception e) + { + return null; + } + } + + private bool PostPressureTestResult(string pcbIdText, float avgPressure, bool passed, DateTime testDate) + { + try + { + return LocalWebRequest.PostRequestAsync(ServiceUrls.PostPressureSensorResultURL, json: new + { + PcbId = int.TryParse(pcbIdText, out int pcbId) ? pcbId : default(int?), + AvgPressureInBar = avgPressure, + TestPassed = passed, + TestDate = testDate, + }); + } + catch (Exception e) + { + return false; + } + } + } + + internal class PressureTestSpecification + { + public int NumberOfMeasurements { get; set; } + + public int TimeMeasurementsInS { get; set; } + + public float MaxDeviationInBar { get; set; } + } +} diff --git a/Common/CordonelPreadjustmentUi/Processes/BaseProcess.cs b/Common/CordonelPreadjustmentUi/Processes/BaseProcess.cs index d2c6d2a4..24cbcb81 100644 --- a/Common/CordonelPreadjustmentUi/Processes/BaseProcess.cs +++ b/Common/CordonelPreadjustmentUi/Processes/BaseProcess.cs @@ -20,6 +20,7 @@ namespace CordonelPreadjustmentUi.Processes public readonly string PerformMessage; public readonly int ExpectedTimeS; public DateTimeOffset StartTime; + public BaseProcess(string processName, StatusPanelItems panelState, string performMessage, string failedMessage, int? expectedTimeS = null) { ProcessName = processName; @@ -30,6 +31,7 @@ namespace CordonelPreadjustmentUi.Processes ExpectedTimeS = expectedTimeS.HasValue ? expectedTimeS.Value : 0; } + internal List AllMeterStateCtrls() { var r = new List(); @@ -75,29 +77,29 @@ namespace CordonelPreadjustmentUi.Processes var completTasks = new Task(() => + { + try + { + while (!listOfProcessTasks.All(t => t.IsCompleted)) { - try + if (pp.CancellationSource != null && pp.CancellationSource.IsCancellationRequested) { - while (!listOfProcessTasks.All(t => t.IsCompleted)) - { - if (pp.CancellationSource != null && pp.CancellationSource.IsCancellationRequested) - { - return; - } - Thread.Sleep(5); - } - - GetSequenceResult($"{ProcessName} sequence"); - pp.IsBusy = false; + return; } - catch (Exception ex) - { + Thread.Sleep(5); + } - throw new ApplicationException("Error on waiting for completition", ex); - } + GetSequenceResult($"{ProcessName} sequence"); + pp.IsBusy = false; + } + catch (Exception ex) + { + + throw new ApplicationException("Error on waiting for completition", ex); + } - }); + }); completTasks.Start(); diff --git a/Common/CordonelPreadjustmentUi/app.config b/Common/CordonelPreadjustmentUi/app.config index e936cc13..67c9d49a 100644 --- a/Common/CordonelPreadjustmentUi/app.config +++ b/Common/CordonelPreadjustmentUi/app.config @@ -1,5 +1,10 @@  + + + + + diff --git a/Common/Service/MeterProcessState/Controllers/CordonelPressureSensorTestController.cs b/Common/Service/MeterProcessState/Controllers/CordonelPressureSensorTestController.cs index 1e9ee000..a7fd2351 100644 --- a/Common/Service/MeterProcessState/Controllers/CordonelPressureSensorTestController.cs +++ b/Common/Service/MeterProcessState/Controllers/CordonelPressureSensorTestController.cs @@ -14,6 +14,9 @@ using Xylem.Common.Service.MeterProcessState.Extensions; using Xylem.Common.Service.MeterProcessState.Models; + /// + /// Controller for tests on cordonel pressure sensor. + /// [RoutePrefix("api/CordonelPressureSensorTest")] public class CordonelPressureSensorTestController : ApiController { @@ -106,11 +109,7 @@ return httpActionResult; } - - /// - /// Nach dem Test wird für jede PcbID diese Funktion aufgerufen und die daten in die Datenbank übermittelt - /// - /// TODO: return value + [HttpGet] [Route(nameof(GetTestResults) + "/{pcbId}")] public async Task GetTestResults(int pcbId) @@ -152,7 +151,7 @@ { testResults.Add(new CordonelTestResult { - Id = await sqlDataReader.GetLongAsync(0), + Id = await sqlDataReader.GetIntAsync(0), PcbId = await sqlDataReader.GetIntAsync(1), AvgPressureInBar = await sqlDataReader.GetFloatAsync(2), TestPassed = await sqlDataReader.GetBoolAsync(3), diff --git a/Common/Service/MeterProcessState/Models/Cordonel.cs b/Common/Service/MeterProcessState/Models/Cordonel.cs index a1995366..0d27946a 100644 --- a/Common/Service/MeterProcessState/Models/Cordonel.cs +++ b/Common/Service/MeterProcessState/Models/Cordonel.cs @@ -1,6 +1,7 @@ namespace Xylem.Common.Service.MeterProcessState.Models { using System; + using System.ComponentModel.DataAnnotations; public class CordonelTestSpec { @@ -13,14 +14,18 @@ public class CordonelTestResult { - public long Id { get; set; } + public int? Id { get; set; } - public int PcbId { get; set; } + [Required] + public int? PcbId { get; set; } - public DateTime TestDate { get; set; } + [Required] + public DateTime? TestDate { get; set; } - public float AvgPressureInBar { get; set; } + [Required] + public float? AvgPressureInBar { get; set; } - public bool TestPassed { get; set; } + [Required] + public bool? TestPassed { get; set; } } } \ No newline at end of file diff --git a/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache b/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache index f98f081f..1edd49e3 100644 --- a/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache +++ b/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -57a46bdf07e306281dc4ae72a3747ed460385fe7 +ac0776a643239630cd5909e36516a0bfa05cb113