Pressure measurement process done.

This commit is contained in:
Stoyan Zlatev
2023-05-23 14:02:52 +02:00
parent b8afbcdc14
commit c3874f85c0
10 changed files with 356 additions and 33 deletions
@@ -5,7 +5,7 @@
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D0C8D887-ED52-40AB-A069-90BCE0E801E2}</ProjectGuid>
<OutputType>Library</OutputType>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Ui.CordonelPreadjustmentUi</RootNamespace>
<AssemblyName>Xylem.Common.Ui.CordonelPreadjustmentUi</AssemblyName>
@@ -103,6 +103,9 @@
<PropertyGroup />
<PropertyGroup />
<PropertyGroup />
<PropertyGroup>
<NoWin32Manifest>true</NoWin32Manifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
@@ -116,6 +119,9 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http">
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
@@ -153,6 +159,7 @@
<DependentUpon>PreAdjustmentControl.cs</DependentUpon>
</Compile>
<Compile Include="PreAdjustmentSettingsContainer.cs" />
<Compile Include="Processes\Actions\PressureTestProcess.cs" />
<Compile Include="Processes\Actions\FlushProcess.cs" />
<Compile Include="Processes\Actions\PrepareTestProcess.cs" />
<Compile Include="Processes\Actions\AmplitudeTestProcess.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));
@@ -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)
{
}
@@ -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;
/// <summary>
/// Process for cordonel water meters with pressure sensor.
/// </summary>
/// <remarks date="2023-05-23" author="Stoyan Zlatev">
/// Just started implementing pressure test for water meters with pressure sensor.
/// </remarks>
public class PressureTestProcess : BaseProcess
{
/// <summary>
/// Read / Write is pressure present for the water meter.
/// </summary>
/// <remarks date="2023-05-23" author="Stoyan Zlatev">
/// Just started implementing pressure test for water meters with pressure sensor.
/// </remarks>
private const string PRESSURE_PRESENT = "METROLOGYASST_PressurePresent";
/// <summary>
/// Read / Write pressure measurements for the water meter.
/// </summary>
/// <remarks date="2023-05-23" author="Stoyan Zlatev">
/// Just started implementing pressure test for water meters with pressure sensor.
/// </remarks>
private const string PRESSURE_MEASURE = "METROLOGYASST_PressureMeasure";
/// <summary>
/// Constructor for passing parameters to the base constructor of the derived class.
/// </summary>
/// <param name="processName">The name used to show into the GUI.</param>
/// <param name="panelState">TODO: Get the right description. Probably the state for the box into the GUI. ???</param>
/// <param name="performMessage">TODO: Get the right description. Probably the success message to show into the GUI. ???</param>
/// <param name="failedMessage">TODO: Get the right description. Probably the error message to show into the GUI. ???</param>
/// <param name="expectedTimeS">TODO: Get the right description. Probably the expected time for process the pressure tests. ???</param>
/// <remarks date="2023-05-23" author="Stoyan Zlatev">
/// Just started implementing pressure test for water meters with pressure sensor.
/// </remarks>
public PressureTestProcess(string processName, StatusPanelItems panelState, string performMessage, string failedMessage, int? expectedTimeS)
: base(processName, panelState, performMessage, failedMessage, expectedTimeS)
{
}
/// <summary>
/// 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.
/// <list type="number">
/// <listheader>Measurement as follow.</listheader>
/// <item>Measure starts with <see cref="PRESSURE_MEASURE"/> = 1</item>
/// <item>Then registry is readeded and comaired to the referenz meters acording to the specification.</item>
/// </list>
/// </summary>
/// <returns>Returns a <see cref="List{Task}"/> for processing the pressure tests on the goggedin cordonels.</returns>
/// <remarks date="2023-05-23" author="Stoyan Zlatev">
/// Just started implementing pressure test for water meters with pressure sensor.
/// </remarks>
public override List<Task> StartWork()
{
/// List of the processes to start.
var listOfPresureTestTasks = new List<Task>();
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 <see cref="MeterStateControl"/>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<bool>(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<int>(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<bool>(() =>
{
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<int>(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<PressureTestSpecification>(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; }
}
}
@@ -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<MeterStateControl> AllMeterStateCtrls()
{
var r = new List<MeterStateControl>();
@@ -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();
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="GetPressureSensorSpecificationURL" value="http://sla12iis01.emea.sensus.net/MeterProcessState/api/CordonelPressureSensorTest/GetTestSpec"/>
<add key="PostPressureSensorResultURL" value="http://sla12iis01.emea.sensus.net/MeterProcessState/api/CordonelPressureSensorTest/PostTestResults"/>
<add key="PressureSensorResultsURL" value="http://sla12iis01.emea.sensus.net/MeterProcessState/api/CordonelPressureSensorTest/GetTestResults/{0}"/>
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>