tbf/TestBenchFramework/BenchControl/Sequences/SequenceBase.cs

1158 lines
48 KiB
C#

///
/// Copyright (c) 2013-2015 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.Linq;
using log4net;
using Config.Entities;
using TBF.BenchControl.GenericDevices;
using TBF.BenchControl.Operations;
using TBF.Boxes;
using TBF.Resources;
using TBF.UiBridge;
namespace TBF.BenchControl.Sequences
{
/// <summary>
/// Sequence is a group of states that can be dynamically added to
/// and removed from the state machine
/// </summary>
public class SequenceBase : ProcessData
{
private static readonly ILog log = LogManager.GetLogger(typeof(SequenceBase));
protected static readonly ILog processDataLogger = LogManager.GetLogger("ProcessData");
protected static readonly ILog allResults = LogManager.GetLogger("AllResults");
protected static readonly ILog summaryResults = LogManager.GetLogger("SummaryResults");
///------------------------------------------------------------
/// Global static variables set only once.
///------------------------------------------------------------
public static IList<IFlowMeter> FlowMeters; /// list of reference flowmeters
public static IList<IRegulValve> RegulValves; /// list of regulation valves
public static IList<IPumpFM> PumpsWithFM; /// list of FM controlled pumps
public static IList<IWaterMeter> WaterMeters; /// list of water meters
public static IList<ICamera> Cameras; /// list of cameras
///------------------------------------------------------------
/// Procedure related (static) variables.
/// They are re-initialized when LoadProcedure() is called
///------------------------------------------------------------
protected static IList<TestResult> results;
public static int ReferenceFlowmetersCount;
public static float[] LtrPerRefPulse; /// Reference flowmeter coefficients
public static float Qrise;
public static float Qfall;
/// <summary>
/// Clear all test results.
/// </summary>
protected static void ResetResults()
{
results = new List<TestResult>();
Qrise = 0;
Qfall = 0;
}
/// <summary>
/// Add result to the list of results.
/// Overwrite (=delete) any previous result with the same name.
/// </summary>
/// <param name="newTestResult">New test result</param>
public static void AddOrOverwriteResult(TestResult newTestResult)
{
if (newTestResult == null) return;
TestResult toDelete =
results.FirstOrDefault<TestResult>(x => x.Name.Equals(newTestResult.Name) &&
(x.Part == newTestResult.Part) &&
(x.TestId == newTestResult.TestId));
if (toDelete != null) results.Remove(toDelete);
results.Add(newTestResult);
}
/// <summary>
/// Check whether the results are complete, whether there is a result for each test.
/// </summary>
/// <param name="tests">All tests</param>
/// <returns>true = The results are complete</returns>
protected static bool ResultsAreComplete(IList<Test> tests)
{
foreach (var test in tests)
{
if (!test.Method.Contains("RoiDetection")) /// TODO: Use 'DoNotEvaluate' etc.
{
for (int r = 1; r <= test.Repeats; r++)
{
bool resultExists = false;
foreach (var tr in results)
{
if ((tr.DoNotEvaluate == false) && (tr.RepetitionNr == r) && (tr.TestName == test.Name))
{
resultExists = true;
break;
}
}
if (!resultExists) return false;
}
}
}
return true;
}
protected static float TimeEstimateTotal; /// Time estimate of the selected cycle or test
protected static float TimeEstimateBeginRpts; /// Time estimate at the beginning of all repetitions of the current tests
protected static float TimeEstimateOneTest; /// Time estimate of the current test (one repetition)
static SequenceBase()
{
results = new List<TestResult>();
WMVolumes = new FloatBox[Config.Data.WMsCount];
WMErrors = new FloatBox[Config.Data.WMsCount];
for (int i = 0; i < Config.Data.WMsCount; i++)
{
WMVolumes[i] = new FloatBox() { Name = string.Format("Volume{0}", i), Format = "F2" };
WMErrors[i] = new FloatBox() { Name = string.Format("Error{0}", i), Format = "F2" };
}
}
///------------------------------------------------------------
/// Test related (instance) variables.
/// Created when test sequence is open.
/// They persist during all repetitions of the same test
///------------------------------------------------------------
protected static BenchControl.FeedingPath inPath;
protected static BenchControl.BenchPath benchPath;
protected static BenchControl.OutputPath outPath;
protected static BenchControl.MetersPath sensPath;
protected static TransitionSequence transitionBefore;
protected static TransitionSequence transitionAfter;
#region Temperature_Pressure_Humidity
protected float tempInSum;
protected float tempOutSum;
protected float tempDivSum;
protected float pressInSum;
protected float pressOutSum;
protected float ambientTempSum;
protected float ambientPressSum;
protected float ambientHumiSum;
///
protected int averagedDataCount;
///
protected void ResetAveragedData()
{
tempInSum = 0;
tempOutSum = 0;
tempDivSum = 0;
pressInSum = 0;
pressOutSum = 0;
ambientTempSum = 0;
ambientPressSum = 0;
ambientHumiSum = 0;
///
averagedDataCount = 0;
}
///
protected void AccumulateAveragedData()
{
tempInSum += TempIn.Val;
tempOutSum += TempOut.Val;
tempDivSum += TempDiv.Val;
pressInSum += PressureUp.Val;
pressOutSum += PressureDown.Val;
ambientTempSum += AmbientTemp.Val;
ambientPressSum += AmbientPressure.Val;
ambientHumiSum += AmbientHumidity.Val;
///
averagedDataCount++;
}
///
protected void UpdateTestRsltWithAveragedData(TestResult tstRslt)
{
if (averagedDataCount != 0)
{
float denominator = (float)averagedDataCount;
tstRslt.AmbientTempAve = ambientTempSum / denominator;
tstRslt.AmbientPressAve = ambientPressSum / denominator;
tstRslt.AmbientHumiAve = ambientHumiSum / denominator;
tstRslt.PressInAvrg = pressInSum / denominator;
tstRslt.PressOutAvrg = pressOutSum / denominator;
tstRslt.TempInAvrg = tempInSum / denominator;
tstRslt.TempOutAvrg = tempOutSum / denominator;
tstRslt.TempDivAvrg = tempDivSum / denominator;
}
}
#endregion
protected float ltrPerRefPulse;
protected IOperation readRegistersOp;
protected IOperation queryEnd1;
protected IOperation queryEnd2;
protected IOperation checkUiOp;
protected IOperation processDataLoggingOp;
protected int[] WMPulses = new int[Config.Data.WMsCount];
protected int[] WMRefPulses = new int[Config.Data.WMsCount];
protected double[] WMTimes = new double[Config.Data.WMsCount];
protected static FloatBox[] WMVolumes;
protected static FloatBox[] WMErrors;
protected static FloatBox startMass = new FloatBox() { Name = "Start Mass", Format = "F3" };
protected static FloatBox endMass = new FloatBox() { Name = "End Mass", Format = "F3" };
/// <summary>
/// To clear process values at the beginning of each test
/// </summary>
protected void ClearProcessValues()
{
//if (WMPulses != null) { for (int i = 0; i < WMPulses.Length; i++) WMPulses[i] = 0; }
//if (WMRefPulses != null) { for (int i = 0; i < WMRefPulses.Length; i++) WMPulses[i] = 0; }
for (int i = 0; i < Config.Data.WMsCount; i++)
{
WMPulses[i] = 0;
WMPulses[i] = 0;
WMVolumes[i].Clear();
WMErrors[i].Clear();
}
RefCount.Clear();
RefFreq.Clear();
RefFlow.Clear();
Mass.Clear();
startMass.Clear();
endMass.Clear();
}
///
/// Process data logging
///
public void LogProcessHeader(ILog logger)
{
LogProcessHeader(logger, null);
}
public void LogProcessHeader(ILog logger, string sectionName)
{
logger.Info(Environment.NewLine);
if (sectionName != null) logger.Info(sectionName);
logger.Info("Time Flow TstTime Ref.cnt Ref.vol Tin Tout Tdiv Pin Pout Mass VolMM Tamb Hamb Pamb Rv");
logger.Info(Environment.NewLine);
}
public void LogProcessData(ILog logger)
{
logger.InfoFormat("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15}",
DateTime.Now.ToLongTimeString(),
Utils.FloatToStr(RefFlow.Val, 4),
StateMachine.ControlBoard.TTime.ToString("F3"),
StateMachine.ControlBoard.EtPulses(0),
Formulas.VolumeFromPulses(StateMachine.ControlBoard.EtPulses(0), 1.0f / ltrPerRefPulse).ToString("F3"),
TempIn,
TempOut,
TempDiv,
PressureUp,
PressureDown,
Mass,
"VolMM",
AmbientTemp,
AmbientHumidity,
AmbientPressure,
outPath.RegulValve.Position.ToString("F1"));
}
/// <summary>
/// Empties the tank: opens the emptying valve and measures the weight.
/// </summary>
/// <param name="EmptyTankValve">Valve to empty the tank</param>
/// <param name="Balance">Balance underneath the tank</param>
/// <returns>Event.Done or Event.Error</returns>
protected Event EmptyTheTank(IValve EmptyTankValve, IBalance Balance)
{
//------------------------------------------------
Bridge.OnActivity(this, TBF.Resources.Strings.Emptying_tank);
//------------------------------------------------
IList<Event> e;
Bridge.Bench2UI(ButtonsEtc.StopBtnEn);
State.Create("SequenceBase : Opening the emptying valve")
.AddOperation(checkUiOp)
.AddOperation(StateMachine.ControlBoard.SetValvesOp(EmptyTankValve, null))
.EnterState();
do { e = StateMachine.WaitRunDevsRunOps(); }
while (!e.Contains(Event.ValvesSet));
do
{
//--------------------------------
State.Create("SequenceBase : Emptying the tank")
.AddOperation(checkUiOp)
.AddOperation(Balance.ReadMassOp(ref Mass))
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error)) return Event.Error;
if (e.Contains(Event.UiCmdStop)) goto quit_emptying;
if (e.Contains(Event.BalanceOverload)) { }; /// Tank should be emptying now
}
while (!e.Contains(Event.BalanceDone));
}
while (!Balance.IsEmpty(Mass.Val));
quit_emptying:
//--------------------------------
State.Create("SequenceBase : Closing the emptying valve")
.AddOperation(checkUiOp)
.AddOperation(Balance.ReadMassOp(ref Mass))
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation(StateMachine.ControlBoard.SetValvesOp(null, EmptyTankValve))
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error)) return Event.Error;
}
while (!e.Contains(Event.ValvesSet) || !e.Contains(Event.BalanceDone));
State.Create("SequenceBase : Updating the weight")
.AddOperation(checkUiOp)
.AddOperation(Balance.ReadMassOp(ref Mass))
.AddOperation(new Operations.TimerOp(5))
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error)) return Event.Error;
}
while (!e.Contains(Event.BalanceDone) || !e.Contains(Event.TimerExpired));
return Event.Done;
}
/// <summary>
/// Passed as an argument to Transition(sequence, context)
/// </summary>
public enum TransitionContext
{
PurgeBegin,
PurgeEnd,
BeforeTest,
AfterTest,
}
/// <summary>
/// Executes steps of a transition sequence
/// </summary>
/// <param name="transitionSequence">TransitionSequence entity</param>
/// <param name="context">Calling context (see above)</param>
/// <returns>
/// Event.Done Transition sequence completed OK
/// Event.UiCmdStop Transition sequence interrupted by the STOP on-screen button
/// Event.Error Error (e.g. RegulValveTimeOut returned by Run() of SetRegulValvePositionOp)
/// </returns>
protected Event Transition(TransitionSequence transitionSequence, TransitionContext context)
{
IList<Event> e;
string message;
switch (context)
{
case TransitionContext.PurgeBegin: message = Strings.Purging_i_n; break;
case TransitionContext.PurgeEnd: message = Strings.Emptying_i_n; break;
case TransitionContext.BeforeTest: message = Strings.Test_start_sequence_i_n; break;
case TransitionContext.AfterTest: message = Strings.Test_stop_sequence_i_n; break;
default: message = "Transition"; break;
}
if (transitionSequence == null)
{
///
/// No transition sequence defined --> Default action
///
if (context == TransitionContext.BeforeTest)
{
State.Create("SequenceBase : Transition : TestStart - Default action")
.AddOperation(checkUiOp)
.AddOperation(new MettlerToledo.KeepReadingMassesOp())
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation(StateMachine.ControlBoard
.SetValvesOp(GenericDevices.ValveBase.Merge(inPath.ValvesOpen, benchPath.ValvesOpen, outPath.ValvesOpen),
GenericDevices.ValveBase.Merge(inPath.ValvesClose, benchPath.ValvesClose, outPath.ValvesClose)))
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.UiCmdStop)) return Event.UiCmdStop;
}
while (e.Contains(Event.ValvesBusy));
}
else if (context == TransitionContext.AfterTest)
{
State.Create("SequenceBase : Transition : TestEnd - Default action")
.AddOperation(checkUiOp)
.AddOperation(new MettlerToledo.KeepReadingMassesOp())
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation(StateMachine.ControlBoard.SetValvesOp(StateMachine.DefaultValvesOpen,
StateMachine.DefaultValvesClose))
.EnterState();
do {
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.UiCmdStop)) return Event.UiCmdStop;
}
while (!e.Contains(Event.ValvesSet));
}
}
else
{
///
/// Execute the transition sequence
///
IList<TransitionStep> transitionSteps = StateMachine.WtSession
.CreateQuery("FROM TransitionStep WHERE TransitionSequence = :tsId ORDER BY ItemNr")
.SetParameter("tsId", transitionSequence.Id)
.List<TransitionStep>();
int stepsCount = transitionSteps.Count;
foreach (var step in transitionSteps)
{
//------------------------------------------------
string activity = string.Format(message, transitionSequence.Name, step.ItemNr + 1, stepsCount);
Bridge.OnActivity(this, activity);
Bridge.OnMessage(this, step.Message);
log.Info(activity + " " +step.Message);
//------------------------------------------------
/// FM controlled pumps are canged imediately without using any state operations
log.DebugFormat("step.PumpWithFMPcts = {0}", step.PumpWithFMPcts);
float[] allFMPumpPcts = Utils.GetPumpWithFMPcts(step);
for (int i = 0; i < allFMPumpPcts.Length; i++)
{
float pwr = allFMPumpPcts[i];
if (pwr > 0) /// Negative value means no power change
{
PumpsWithFM[i].TurnOn(pwr);
}
else if (pwr == 0)
{
PumpsWithFM[i].TurnOff();
}
}
/// Get new regulation valve positions,
float[] allRegvPositions = Utils.GetRegulValvesPositions(step);
/// Prepare necessary SetRegValvePositionOp operations for RV-s with changed positions
IList<IOperation> rvPosOps = new List<IOperation>();
IList<string> rvPosStr = new List<string>();
for (int i = 0; i < allRegvPositions.Length; i++)
{
if (allRegvPositions[i] >= 0) /// Negative value means no position change
{
float lo = Math.Max(0, allRegvPositions[i] - 0.05f);
float hi = Math.Min(100.0f, allRegvPositions[i] + 0.05f);
rvPosOps.Add(RegulValves[i].SetRegulValvePositionOp(lo, hi, 60));
rvPosStr.Add(string.Format("RV{0}.SetRegulValvePositionOp({1}, {2}, 60s)", i, lo, hi));
}
}
/// Max. one SetRegulValvePositionOp can be started or stopped in one sub-step.
/// Therefore SetRegulValvePositionOp operations are added and removed to subsequent states one by one.
int delay = Math.Max(2, step.Duration - rvPosOps.Count + 2);
bool stopFlag = false;
bool errorFlag = false;
///
int lastStartedRV = -1;
for (int i = 0; i < rvPosOps.Count; i++)
{
log.DebugFormat("SequenceBase.Transition() : Step {0} start, opening={1}, closing={2}", step.ItemNr + 1, step.ValvesOpen, step.ValvesClose);
State stepStrt = State
.Create(string.Format("SequenceBase.Transition() : Step {0} start, opening={1}, closing={2}", step.ItemNr + 1, step.ValvesOpen, step.ValvesClose))
.AddOperation(checkUiOp)
.AddOperation(new MettlerToledo.KeepReadingMassesOp())
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation(StateMachine.ControlBoard.SetValvesOp(Utils.ValvesOpen(step), Utils.ValvesClose(step)));
for (int j = 0; j <= i; j++)
{
stepStrt.AddOperation(rvPosOps[j]);
log.Debug(rvPosStr[j]);
}
lastStartedRV = i;
stepStrt.EnterState();
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error) || e.Contains(Event.RegulValveTimeOut)) { errorFlag = true; break; }
if (e.Contains(Event.UiCmdStop)) { stopFlag = true; break; }
}
/// Max. valaue of lastStartedRV after exitting the loop is (rvPosOps.Count - 1)
if (!(stopFlag || errorFlag))
{
log.DebugFormat("SequenceBase.Transition() : Step {0} delay {1}s, opening={2}, closing={3}", step.ItemNr + 1, delay, step.ValvesOpen, step.ValvesClose);
State stepDelay = State
.Create(string.Format("SequenceBase.Transition() : Step {0} delay {1}s, opening={2}, closing={3}", step.ItemNr + 1, delay, step.ValvesOpen, step.ValvesClose))
.AddOperation(checkUiOp)
.AddOperation(new MettlerToledo.KeepReadingMassesOp())
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation(StateMachine.ControlBoard.SetValvesOp(Utils.ValvesOpen(step), Utils.ValvesClose(step)))
.AddOperation(new TimerOp(delay));
for (int j = 0; j <= lastStartedRV; j++)
{
stepDelay.AddOperation(rvPosOps[j]);
log.Debug(rvPosStr[j]);
}
stepDelay.EnterState();
bool endContitionFulfilled = false;
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error) || e.Contains(Event.RegulValveTimeOut)) { errorFlag = true; break; }
if (e.Contains(Event.UiCmdStop)) { stopFlag = true; break; }
switch (step.EndCondition)
{
case StepCondition.Scale1Empty:
endContitionFulfilled = (StateMachine.Balance1 == null) || StateMachine.Balance1.IsEmpty();
break;
case StepCondition.Scale2Empty:
endContitionFulfilled = (StateMachine.Balance2 == null) || StateMachine.Balance2.IsEmpty();
break;
case StepCondition.Scale3Empty:
endContitionFulfilled = (StateMachine.Balance3 == null) || StateMachine.Balance3.IsEmpty();
break;
case StepCondition.AllScalesEmpty:
endContitionFulfilled = ((StateMachine.Balance1 == null) || StateMachine.Balance1.IsEmpty()) &&
((StateMachine.Balance2 == null) || StateMachine.Balance2.IsEmpty()) &&
((StateMachine.Balance3 == null) || StateMachine.Balance3.IsEmpty());
break;
}
}
while (e.Contains(Event.ValvesBusy) || (!endContitionFulfilled && e.Contains(Event.TimerBusy)));
}
for (int first = 1; first <= lastStartedRV; first++)
{
log.DebugFormat("SequenceBase.Transition() : Step {0} stop, opening={1}, closing={2}", step.ItemNr + 1, step.ValvesOpen, step.ValvesClose);
State stepStop = State.Create(string.Format("SequenceBase.Transition() : Step {0} stop, opening={1}, closing={2}", step.ItemNr + 1, step.ValvesOpen, step.ValvesClose))
.AddOperation(checkUiOp)
.AddOperation(new MettlerToledo.KeepReadingMassesOp())
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation(StateMachine.ControlBoard.SetValvesOp(Utils.ValvesOpen(step), Utils.ValvesClose(step)));
for (int j = first; j <= lastStartedRV; j++)
{
stepStop.AddOperation(rvPosOps[j]);
log.Debug(rvPosStr[j]);
}
stepStop.EnterState();
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error) || e.Contains(Event.RegulValveTimeOut)) { errorFlag = true; }
if (e.Contains(Event.UiCmdStop)) { stopFlag = true; }
}
if (errorFlag) return Event.Error;
if (stopFlag) return Event.UiCmdStop;
}
}
if (context == TransitionContext.AfterTest)
{
///
/// Stop the pump at the end of test
///
foreach (var fmPump in PumpsWithFM) fmPump.TurnOff();
State.Create("SequenceBase : Test(s) completed -> Stopping the pump")
.AddOperation(checkUiOp)
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation((inPath.Pump is GenericDevices.IPumpFM) ? (inPath.Pump as GenericDevices.IPumpFM).TurnOffOp() : null)
.AddOperation(StateMachine.ControlBoard.SetValvesOp(null, inPath.Pump))
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error)) return Event.Error;
}
while (!e.Contains(Event.ValvesSet) || ((inPath.Pump is GenericDevices.IPumpFM) && !e.Contains(Event.TurnPumpOnOffDone)));
}
return Event.Done;
}
/// <summary>
/// Opens a modeless dialog for entering data at the beginning of a procedure (serial numbers)
/// </summary>
/// <returns>false = OK, true = stop pressed</returns>
protected bool OpenCycleBeginForm()
{
IList<Event> e;
GenericDevices.IDataEntry dataEntryCmpnt =
TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IDataEntry;
if (dataEntryCmpnt is IHasCycleBeginForm)
{
Bridge.OnActivity(this, "Enter the water meter data");
State.Create("MainSeq : Enter begin data")
.AddPermanentOperation((dataEntryCmpnt as IHasCycleBeginForm).ShowCycleBeginFormOp(WaterMeters))
.AddOperation(checkUiOp)
.EnterState();
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.UiCmdStop)) return true;
}
return false;
}
/// <summary>
/// Waits until a modeless dialog for entering data at the beginnig of a procedure is closed.
/// This function is typically called at the end of the first test of the procedure.
/// </summary>
/// <returns>false = OK, true = stop pressed</returns>
protected UIFlowControl WaitBeginFormClosed()
{
GenericDevices.IDataEntry dataEntryCmpnt =
TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IDataEntry;
bool stopPressed = false; /// true when STOP button pressed
if (dataEntryCmpnt is IHasCycleBeginForm)
{
IList<Event> e;
///
/// Wait until modeless form is closed by the user if it is stil open
///
if ( State.LastEvents.Contains(Event.ModelessFormIsOpen))
{
State.Create("MainSeq : Wait until the entry form is closed")
.AddOperation(checkUiOp)
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.UiCmdStop))
{
stopPressed = true;
break;
}
}
while (!e.Contains(Event.ModelessFormClosed));
}
///
/// A state without any dataEntryCmpnt operation so that Stop() when entering
/// this state and Start() when entering the following state are executed.
///
State.Create("MainSeq : Stopping modeless form")
.AddOperation(checkUiOp)
.RemovePermanentOperation(dataEntryCmpnt as IOperation)
.EnterState();
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.UiCmdStop)) { stopPressed = true; }
}
return stopPressed ? UIFlowControl.Stop : UIFlowControl.Continue;
}
/// <summary>
/// Forces closing of a modeless dialog for entering data at the beginnig of a procedure.
/// This function is typically called before starting a new cycle
/// in case previous cycle was aborted.
/// </summary>
protected void CloseBeginForm()
{
if (StateMachine.Procedure == null || StateMachine.Procedure.DataEntry == null) return;
GenericDevices.IDataEntry dataEntryCmpnt =
TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IDataEntry;
if ((dataEntryCmpnt is IHasCycleBeginForm) &&
(State.LastEvents.Contains(Event.ModelessFormIsOpen) || State.LastEvents.Contains(Event.ModelessFormClosed)))
{
IList<Event> e;
/// A state without any dataEntryCmpnt operation so that Stop() when entering
/// this state and Start() when entering the following state are executed.
State.Create("MainSeq : Stopping modeless form")
.RemovePermanentOperation(dataEntryCmpnt as IOperation)
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
}
while (e.Contains(Event.ModelessFormIsOpen));
}
}
/// <summary>
/// Main loop where measurements are collected.
/// </summary>
/// <param name="realTest">false = a flow setting or a switching flow detection, true = measurement</param>
/// <returns>Event.MeasurementCompleted, Event.UiCmdStop, Event.Error or Event.Done</returns>
protected Event ReadRegistersTempPressAmbient(IList<IOperation> measureOperations, bool realTest)
{
IList<Event> e;
State.Create("Read water meters")
.AddOperation(checkUiOp)
.AddOperations(measureOperations)
.AddOperation(readRegistersOp)
.AddOperation(benchPath.TempIn.ReadTempOp(ref TempIn))
.AddOperation(benchPath.TempOut.ReadTempOp(ref TempOut))
.AddOperation(outPath.TempDiv.ReadTempOp(ref TempDiv))
.AddOperation(benchPath.PressIn.ReadPressureOp(ref PressureUp))
.AddOperation(benchPath.PressOut.ReadPressureOp(ref PressureDown))
.AddOperation(realTest ? outPath.Balance.ReadMassOp(ref Mass) : null)
.AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp())
.AddOperation((StateMachine.Ambient != null)
? StateMachine.Ambient.ReadAmbientOp(AmbientTemp, AmbientPressure, AmbientHumidity)
: null)
//.AddOperation(realTest ? (ticTac ? queryEnd1 : queryEnd2) : null)
.AddOperation(realTest ? queryEnd1 : null)
.AddOperation(realTest ? processDataLoggingOp : null)
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
if (e.Contains(Event.Error)) return Event.Error;
if (e.Contains(Event.UiCmdStop)) return Event.UiCmdStop;
if (e.Contains(Event.MeasurementCompleted)) return Event.MeasurementCompleted;
}
while ( (realTest && !e.Contains(Event.BalanceDone)) ||
!e.Contains(Event.ReadAllRegistersDone));
return Event.Done;
}
/// <summary>
/// Prepares 'TestProgressEventArgs' object that update screens during the test
/// </summary>
/// <param name="test">Current 'Test' entity</param>
/// <param name="tstRslt">Current 'TestResult' entity</param>
/// <param name="cBrd">Control board component reference</param>
/// <param name="time">Current test time in [s]</param>
/// <param name="progress">Current progress 0 .. 1.0f</param>
/// <returns>Data for the UI</returns>
protected TestProgressEventArgs GetTestProgressData(Test test, TestResult tstRslt, bool testRunning, Elde.ControlBoardDev cBrd, float time, float progress)
{
TestProgressEventArgs data = new TestProgressEventArgs();
data.TestResult = tstRslt;
RefFreq.Val = cBrd.ReferenceFreq;
data.FlowMtrFreq = RefFreq;
data.Flow = RefFlow;
data.Time = time;
data.StartMass = startMass;
data.Mass = Mass;
data.Progress = progress;
float estCurrentTime = TimeEstimateBeginRpts + ((float)(tstRslt.RepetitionNr - 1) + progress) * TimeEstimateOneTest;
data.OveralProgress = estCurrentTime / TimeEstimateTotal;
data.Tin = TempIn;
data.Tout = TempOut;
data.Tdiv = TempDiv;
data.Pin = PressureUp;
data.Pout = PressureDown;
data.AmbientTemp = AmbientTemp;
data.AmbientPressure = AmbientPressure;
data.AmbientHumidity = AmbientHumidity;
if (testRunning)
{
/// Only when test is running
data.RefPulses = cBrd.EtPulses(0);
float refPulsesPerLtr = 1.0f / outPath.FlowMeter.LtrPerPulseCorrected(RefFlow.Val); /// [pulse/ltr]
data.Volume = new FloatBox() { Name = "Volume", Format = "F1", Val = Formulas.VolumeFromPulses(data.RefPulses, refPulsesPerLtr) };
int count = Math.Min(Config.Data.WMsCount, data.TestResult.Meters.Count);
for (int i = 0; i < count; i++)
{
if (sensPath.RegisterReaders[i] != null)
{
data.TestResult.Meters[i].PulsesMeter = WMPulses[i];
data.TestResult.Meters[i].PulsesMaster = WMRefPulses[i];
data.TestResult.Meters[i].VolumeMeter = Formulas.VolumeFromPulses(WMPulses[i], sensPath.RegisterReaders[i].PulsesPerLtr);
data.TestResult.Meters[i].VolumeRef = Formulas.VolumeFromPulses(WMRefPulses[i], refPulsesPerLtr);
data.TestResult.Meters[i].VolumeErrorPct = Formulas.ErrorFromVolumes(data.TestResult.Meters[i].VolumeMeter, data.TestResult.Meters[i].VolumeRef);
}
}
}
else
{
data.Volume = new FloatBox() { Name = "Volume", Format = "F1", Val = 0 };
}
return data;
}
protected string TestResult2CsvLine(TestResult tstRslt)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(tstRslt.TimeStart);
sb.Append(";"); sb.Append(tstRslt.BatchNr);
sb.Append(";"); sb.Append(tstRslt.TestName);
sb.Append(";"); sb.Append(tstRslt.RepetitionNr);
sb.Append(";"); sb.Append("1");
sb.Append(";"); sb.Append(tstRslt.Method);
sb.Append(";"); sb.Append(tstRslt.Volume);
sb.Append(";"); sb.Append(tstRslt.Qfrom);
sb.Append(";"); sb.Append(tstRslt.Qto);
sb.Append(";"); sb.Append(tstRslt.ErrLimLo + tstRslt.Uncertainty);
sb.Append(";"); sb.Append(tstRslt.ErrLimHi - tstRslt.Uncertainty);
sb.Append(";"); sb.Append("0");
sb.Append(";"); sb.Append("60");
sb.Append(";"); sb.Append("0");
sb.Append(";"); sb.Append("0");
sb.Append(";"); sb.Append(outPath.FlowMeter.Idx1);
sb.Append(";"); sb.Append(" ");
sb.Append(";"); if (outPath.Balance != null) sb.Append(outPath.Balance.Name);
sb.Append(";"); sb.Append(tstRslt.AmbientTempAve);
sb.Append(";"); sb.Append(tstRslt.AmbientPressAve);
sb.Append(";"); sb.Append(tstRslt.AmbientHumiAve);
sb.Append(";"); sb.Append(tstRslt.PressInAvrg);
sb.Append(";"); sb.Append(tstRslt.PressOutAvrg);
sb.Append(";"); sb.Append(tstRslt.PressInStart);
sb.Append(";"); sb.Append(tstRslt.PressOutStart);
sb.Append(";"); sb.Append(tstRslt.PressInEnd);
sb.Append(";"); sb.Append(tstRslt.PressOutEnd);
sb.Append(";"); sb.Append(tstRslt.TempInAvrg);
sb.Append(";"); sb.Append(tstRslt.TempOutAvrg);
sb.Append(";"); sb.Append(tstRslt.TempDivAvrg);
sb.Append(";"); sb.Append(tstRslt.TempInStart);
sb.Append(";"); sb.Append(tstRslt.TempOutStart);
sb.Append(";"); sb.Append(tstRslt.TempDivStart);
sb.Append(";"); sb.Append(tstRslt.TempInEnd);
sb.Append(";"); sb.Append(tstRslt.TempOutEnd);
sb.Append(";"); sb.Append(tstRslt.TempDivEnd);
sb.Append(";"); sb.Append(tstRslt.MassStartRaw);
sb.Append(";"); sb.Append(tstRslt.MassStart);
sb.Append(";"); sb.Append(tstRslt.MassEndRaw);
sb.Append(";"); sb.Append(tstRslt.MassEnd);
sb.Append(";"); sb.Append(tstRslt.MassDiff);
sb.Append(";"); sb.Append(tstRslt.DensityDiv);
sb.Append(";"); sb.Append(tstRslt.DensityIn);
sb.Append(";"); sb.Append(tstRslt.DensityOut);
sb.Append(";"); sb.Append(" "); /// d_air: Hustota vzduchu: Sheet1 - K9
sb.Append(";"); sb.Append(" "); /// Buoyancy: Sheet1 - X9
sb.Append(";"); sb.Append(Program.LocalSettings.RealDensity);
sb.Append(";"); sb.Append(Program.LocalSettings.AtTemperature);
sb.Append(";"); sb.Append(" "); /// pipe expansion: teraz vynechat
sb.Append(";"); sb.Append(tstRslt.FlowMass); /// Qm [kg/h]
sb.Append(";"); sb.Append(tstRslt.FlowVolume); /// Qv [l/h]
sb.Append(";"); sb.Append(tstRslt.VolumeCTV); /// Vet . . . komercne prava hodnota - podla vahy
sb.Append(";"); sb.Append(tstRslt.VolumeMaster); /// Velm . . . . objem podla etalonu
sb.Append(";"); sb.Append(" "); /// Vmass . . . objem podla druheho etalonu / prietokomeru pred tratou (teraz vynechavame)
sb.Append(";"); sb.Append(tstRslt.Time); /// t
sb.Append(";"); sb.Append(tstRslt.ErrorMaster); /// Eelm . . . chyba etalonu voci komercne pravej hodnote
sb.Append(";"); sb.Append(" "); /// Emass . . . chyba druheho etalonu voci komercne pravej hodnote (teraz vynechavame)
sb.Append(";"); if (outPath.FlowMeter != null && outPath.FlowMeter.LtrPerPulse != 0)
{
sb.Append(1.0f / outPath.FlowMeter.LtrPerPulse);/// Const.MID . konstanta eatlonu
}
sb.Append(";"); sb.Append(" "); /// Const.MA . . konstanta druheho etalonu
sb.Append(";"); sb.Append(tstRslt.TimeDivStart0); /// Time Div Start celkovy cas v [ms]
sb.Append(";"); sb.Append(tstRslt.TimeDivStart1); /// Time Div Start1
sb.Append(";"); sb.Append(tstRslt.TimeDivStart2); /// Time Div Start2
sb.Append(";"); sb.Append(tstRslt.TimeDivStart3); /// Time Div Start3
sb.Append(";"); sb.Append(tstRslt.TimeDivStart4); /// Time Div Start4
sb.Append(";"); sb.Append(tstRslt.TimeDivStart5); /// Time Div Start5
sb.Append(";"); sb.Append(tstRslt.TimeDivEnd0); /// Time Div End celkovy cas v [ms]
sb.Append(";"); sb.Append(tstRslt.TimeDivEnd1); /// Time Div End1
sb.Append(";"); sb.Append(tstRslt.TimeDivEnd2); /// Time Div End2
sb.Append(";"); sb.Append(tstRslt.TimeDivEnd3); /// Time Div End3
sb.Append(";"); sb.Append(tstRslt.TimeDivEnd4); /// Time Div End4
sb.Append(";"); sb.Append(tstRslt.TimeDivEnd5); /// Time Div End5
sb.Append(";"); sb.Append(tstRslt.PulsesMaster); /// Celkovy pocet et. pulzov skusky
sb.Append(";"); sb.Append(" "); /// - '' - pre druhy
for (int i = 0; i < tstRslt.Meters.Count; i++)
{
MeterTestResult mtrRslt = tstRslt.Meters[i];
sb.Append(";"); sb.Append(mtrRslt.SerialNr); /// WM Ser.No.
sb.Append(";"); sb.Append(mtrRslt.VolumeStart); /// WM Vstart - pociatocny stav pri pevnom starte alebo zachyteny pri data streame
sb.Append(";"); sb.Append(mtrRslt.VolumeEnd); /// WM Vend - konecny stav pri pevnom starte alebo zachyteny pri data streame
sb.Append(";"); sb.Append(mtrRslt.VolumeMeter); /// WM Vmer - objem namerany vodomerom
sb.Append(";"); sb.Append(mtrRslt.VolumeRef); /// WM Vref - objem namerany stanicou
sb.Append(";"); sb.Append(mtrRslt.VolumeErrorPct); /// WM Emt - chyba vodomerom nameraneho objemu
sb.Append(";"); sb.Append(mtrRslt.CalibFactor); /// iPerl calibration factor used during the test / ...
sb.Append(";"); sb.Append(mtrRslt.PulsesMeter); /// WM Np met - pocet impulzov zo skusaneho meradla
sb.Append(";"); sb.Append(mtrRslt.PulsesMaster); /// WM Np elm - pocet impulzov etalonu pocas merania pre prislusny vodomer
sb.Append(";"); sb.Append(mtrRslt.Time); /// WM Tmet - cas merania (obmedzany pri synchro skuske)
bool ok = (mtrRslt.VolumeErrorPct >= tstRslt.ErrLimLo + tstRslt.Uncertainty)
&& (mtrRslt.VolumeErrorPct <= tstRslt.ErrLimHi - tstRslt.Uncertainty);
sb.Append(";"); sb.Append(ok ? "OK" : "NOK"); /// WM Vysledok (t.j. ci je v hraniciach chyb) - OK/NOK
sb.Append(";"); sb.Append(mtrRslt.Q2Correction.ToString("F1")); /// iPerl Q2 correction factor used during the test / AN value - hodnota z analogoveho prevodnika
sb.Append(";"); sb.Append(mtrRslt.VolumeStart); /// WM Volume_start - pri datastreamovych hodnotach (alebo kamera)
sb.Append(";"); sb.Append(mtrRslt.TimeStart); /// WM Time_start - ' ' -
sb.Append(";"); sb.Append(mtrRslt.VolumeEnd); /// WM Volume_end - ' ' -
sb.Append(";"); sb.Append(mtrRslt.TimeEnd); /// WM Time_end - ' ' -
}
for (int i = 0; i < tstRslt.CombinedMeters.Count; i++)
{
MeterTestResult mtrRslt = tstRslt.CombinedMeters[i];
sb.Append(";"); sb.Append(mtrRslt.SerialNr); /// WM Ser.No.
sb.Append(";"); sb.Append(mtrRslt.VolumeStart); /// WM Vinit - pri pevnom starte pociatocny stav natukany alebo cez inteligentny system
sb.Append(";"); sb.Append(mtrRslt.VolumeEnd); /// WM Vfin - pri pevnom starte konecny stav natukany alebo cez inteligentny system
sb.Append(";"); sb.Append(mtrRslt.VolumeMeter); /// WM Vmer - objem namerany vodomerom
sb.Append(";"); sb.Append(mtrRslt.VolumeRef); /// WM Vet - objem namerany stanicou
sb.Append(";"); sb.Append(mtrRslt.VolumeErrorPct); /// WM Emt - chyba vodomerom nameraneho objemu
sb.Append(";"); sb.Append(" "); /// WM U - neistota (zatial nechat prazdne)
sb.Append(";"); sb.Append(mtrRslt.PulsesMeter); /// WM Np met - pocet impulzov zo skusaneho meradla
sb.Append(";"); sb.Append(mtrRslt.PulsesMaster); /// WM Np elm - pocet impulzov etalonu pocas merania pre prislusny vodomer
sb.Append(";"); sb.Append(mtrRslt.Time); /// WM Tmet - cas merania (obmedzany pri synchro skuske)
bool ok = (mtrRslt.VolumeErrorPct >= tstRslt.ErrLimLo + tstRslt.Uncertainty)
&& (mtrRslt.VolumeErrorPct <= tstRslt.ErrLimHi - tstRslt.Uncertainty);
sb.Append(";"); sb.Append(ok ? "OK" : "NOK"); /// WM Vysledok (t.j. ci je v hraniciach chyb) - OK/NOK
sb.Append(";"); sb.Append(" "); /// WM AN value - hodnota z analogoveho prevodnika (teraz nic)
sb.Append(";"); sb.Append(mtrRslt.VolumeStart); /// WM Volume_start - pri datastreamovych hodnotach (alebo kamera)
sb.Append(";"); sb.Append(mtrRslt.TimeStart); /// WM Time_start - ' ' -
sb.Append(";"); sb.Append(mtrRslt.VolumeEnd); /// WM Volume_end - ' ' -
sb.Append(";"); sb.Append(mtrRslt.TimeEnd); /// WM Time_end - ' ' -
}
sb.Append(";");
return sb.ToString();
}
/// <summary>
/// Create a simulated test result (single meter).
/// </summary>
/// <param name="test">Test to be simulated</param>
/// <returns>Test result</returns>
protected TestResult MakeSimulated(Test test, int repetitionNr, float errorPctBase)
{
TestResult tstRslt = new TestResult(test, repetitionNr, MetersKind.Single);
tstRslt.TimeStart = DateTime.Now;
tstRslt.TimeEnd = DateTime.Now;
tstRslt.AmbientTempAve = 20.0f;
tstRslt.AmbientPressAve = 1000.0f;
tstRslt.AmbientHumiAve = 50.0f;
tstRslt.PressInStart = 1000.0f;
tstRslt.PressOutStart = 1000.0f;
tstRslt.TempInStart = 20.0f;
tstRslt.TempOutStart = 20.0f;
tstRslt.TempDivStart = 20.0f;
tstRslt.PressInEnd = 1000.0f;
tstRslt.PressOutEnd = 1000.0f;
tstRslt.TempInEnd = 20.0f;
tstRslt.TempOutEnd = 20.0f;
tstRslt.TempDivEnd = 20.0f;
tstRslt.PressInAvrg = 1000.0f;
tstRslt.PressOutAvrg = 1000.0f;
tstRslt.TempInAvrg = 20.0f;
tstRslt.TempOutAvrg = 20.0f;
tstRslt.TempDivAvrg = 20.0f;
tstRslt.BatchNr = Program.LocalSettings.BatchNr;
tstRslt.MassStartRaw = 0;
tstRslt.MassStart = 0;
tstRslt.MassEndRaw = test.Volume * Program.LocalSettings.RealDensity / 1000.0f;
tstRslt.MassEnd = tstRslt.MassEndRaw;
tstRslt.MassDiff = tstRslt.MassEnd;
tstRslt.DensityIn = Program.LocalSettings.RealDensity;
tstRslt.DensityOut = Program.LocalSettings.RealDensity;
tstRslt.DensityDiv = Program.LocalSettings.RealDensity;
tstRslt.Time = test.TstTime;
tstRslt.FlowMass = tstRslt.MassDiff / test.TstTime;
tstRslt.FlowVolume = test.Volume / test.TstTime;
tstRslt.VolumeCTV = test.Volume;
tstRslt.VolumeMaster = test.Volume;
tstRslt.PulsesMaster = (ltrPerRefPulse > 1E-6) ? (test.Volume / ltrPerRefPulse) : 1;
tstRslt.ConstMaster = ltrPerRefPulse;
tstRslt.ErrorMaster = 0;
for (int i = 0; i < Config.Data.WMsCount; i++)
{
float errorPct = errorPctBase + 0.05f * i;
/// Reference to iPerl water meter or null:
WaterMeters.iPerl.WaterMeter iPerl = ((sensPath.RegisterReaders != null) && (i < sensPath.RegisterReaders.Length))
? (sensPath.RegisterReaders[i] as WaterMeters.iPerl.WaterMeter)
: null;
if ((sensPath.RegisterReaders[i] != null) && (WaterMeters.Count > i) && !WaterMeters[i].Disabled)
{
tstRslt.Meters[i].SerialNr = (WaterMeters.Count > i) ? WaterMeters[i].SerialNr : string.Empty;
tstRslt.Meters[i].PulsesPerLiter = sensPath.RegisterReaders[i].PulsesPerLtr;
tstRslt.Meters[i].VolumeRef = test.Volume;
tstRslt.Meters[i].Time = test.TstTime;
tstRslt.Meters[i].TimeStart = 0;
tstRslt.Meters[i].TimeEnd = test.TstTime;
tstRslt.Meters[i].PulsesMaster = tstRslt.PulsesMaster;
tstRslt.Meters[i].VolumeStart = 0;
tstRslt.Meters[i].VolumeErrorPct = errorPct;
tstRslt.Meters[i].Passed = (errorPct >= test.ErrLimLo + test.Uncertainty)
&& (errorPct <= test.ErrLimHi - test.Uncertainty);
tstRslt.Meters[i].VolumeMeter = test.Volume * (1.0f + 0.01f * errorPct);
tstRslt.Meters[i].PulsesMeter = sensPath.RegisterReaders[i].PulsesPerLtr * tstRslt.Meters[i].VolumeMeter;
tstRslt.Meters[i].Disabled = false;
if (iPerl == null)
{
tstRslt.Meters[i].VolumeEnd = 0; /// Normal meter
}
else
{
/// iPerl
tstRslt.Meters[i].VolumeEnd = tstRslt.Meters[i].VolumeStart + tstRslt.Meters[i].VolumeMeter;
}
}
}
return tstRslt;
}
/// <summary>
/// Create a simulated test result (compound meter).
/// </summary>
/// <param name="test">Test to be simulated</param>
/// <returns>Test result</returns>
protected TestResult MakeSimulatedCompound(Test test, CompoundTestType testType, int repetitionNr, float errorPct)
{
TestResult tstRslt = new TestResult(test, repetitionNr, MetersKind.Combined);
tstRslt.TimeStart = DateTime.Now;
tstRslt.TimeEnd = DateTime.Now;
tstRslt.AmbientTempAve = 20.0f;
tstRslt.AmbientPressAve = 1000.0f;
tstRslt.AmbientHumiAve = 50.0f;
tstRslt.PressInStart = 1000.0f;
tstRslt.PressOutStart = 1000.0f;
tstRslt.TempInStart = 20.0f;
tstRslt.TempOutStart = 20.0f;
tstRslt.TempDivStart = 20.0f;
tstRslt.PressInEnd = 1000.0f;
tstRslt.PressOutEnd = 1000.0f;
tstRslt.TempInEnd = 20.0f;
tstRslt.TempOutEnd = 20.0f;
tstRslt.TempDivEnd = 20.0f;
tstRslt.PressInAvrg = 1000.0f;
tstRslt.PressOutAvrg = 1000.0f;
tstRslt.TempInAvrg = 20.0f;
tstRslt.TempOutAvrg = 20.0f;
tstRslt.TempDivAvrg = 20.0f;
tstRslt.BatchNr = Program.LocalSettings.BatchNr;
tstRslt.MassStartRaw = 0;
tstRslt.MassStart = 0;
tstRslt.MassEndRaw = test.Volume * Program.LocalSettings.RealDensity / 1000.0f;
tstRslt.MassEnd = tstRslt.MassEndRaw;
tstRslt.MassDiff = tstRslt.MassEnd;
tstRslt.DensityIn = Program.LocalSettings.RealDensity;
tstRslt.DensityOut = Program.LocalSettings.RealDensity;
tstRslt.DensityDiv = Program.LocalSettings.RealDensity;
tstRslt.Time = test.TstTime;
tstRslt.FlowMass = tstRslt.MassDiff / test.TstTime;
tstRslt.FlowVolume = test.Volume / test.TstTime;
tstRslt.VolumeCTV = test.Volume;
tstRslt.VolumeMaster = test.Volume;
tstRslt.PulsesMaster = (ltrPerRefPulse > 1E-6) ? (test.Volume / ltrPerRefPulse) : 1;
tstRslt.ConstMaster = ltrPerRefPulse;
tstRslt.ErrorMaster = 0;
for (int iCmbnd = 0; iCmbnd < Config.Data.CompoundWMsCount; iCmbnd++)
{
tstRslt.CombinedMeters[iCmbnd].VolumeRef = tstRslt.VolumeCTV; /// [l] must be calculated before main & aux. meter error
for (int i = 2 * iCmbnd; i < 2 * iCmbnd + 2; i++)
{
if (sensPath.RegisterReaders[i] == null || sensPath.RegisterReaders[i].PulsesPerLtr <= float.Epsilon)
{
tstRslt.Meters[i].PulsesPerLiter = 1.0f;
tstRslt.Meters[i].VolumeMeter = 0;
}
else
{
tstRslt.Meters[i].PulsesPerLiter = sensPath.RegisterReaders[i].PulsesPerLtr;
tstRslt.Meters[i].VolumeMeter = Convert.ToSingle(WMPulses[i]) / sensPath.RegisterReaders[i].PulsesPerLtr;
}
tstRslt.Meters[i].SerialNr = (WaterMeters.Count > i) ? WaterMeters[i].SerialNr : string.Empty;
tstRslt.Meters[i].VolumeStart = 0;
tstRslt.Meters[i].VolumeEnd = 0; /// Normal meter
tstRslt.Meters[i].VolumeRef = test.Volume;
if (testType == CompoundTestType.Regular && IsMainMtr(i))
{
tstRslt.Meters[i].VolumeMeter = test.Volume * (1.0f + 0.01f * errorPct);
}
else if (testType == CompoundTestType.DetectionRise && !IsMainMtr(i))
{
tstRslt.Meters[i].VolumeMeter = test.Volume * (1.0f + 0.01f * errorPct);
}
else if (testType == CompoundTestType.DetectionFall && IsMainMtr(i))
{
tstRslt.Meters[i].VolumeMeter = test.Volume * (1.0f + 0.01f * errorPct);
}
else
{
tstRslt.Meters[i].VolumeMeter = 0;
}
tstRslt.Meters[i].Time = test.TstTime;
tstRslt.Meters[i].TimeStart = 0;
tstRslt.Meters[i].TimeEnd = test.TstTime;
tstRslt.Meters[i].PulsesMaster = tstRslt.PulsesMaster;
tstRslt.Meters[i].VolumeErrorPct = errorPct;
tstRslt.Meters[i].Passed = (errorPct >= test.ErrLimLo + test.Uncertainty)
&& (errorPct <= test.ErrLimHi - test.Uncertainty);
tstRslt.Meters[i].PulsesMeter = sensPath.RegisterReaders[i].PulsesPerLtr * tstRslt.Meters[i].VolumeMeter;
}
tstRslt.CombinedMeters[iCmbnd].VolumeMeter = tstRslt.Meters[2 * iCmbnd].VolumeMeter + tstRslt.Meters[2 * iCmbnd + 1].VolumeMeter;
tstRslt.CombinedMeters[iCmbnd].PulsesMaster = tstRslt.PulsesMaster;
tstRslt.CombinedMeters[iCmbnd].Time = test.TstTime;
tstRslt.CombinedMeters[iCmbnd].VolumeErrorPct = Formulas.ErrorFromVolumes(tstRslt.CombinedMeters[iCmbnd].VolumeMeter, tstRslt.CombinedMeters[iCmbnd].VolumeRef);
tstRslt.CombinedMeters[iCmbnd].Passed = (tstRslt.CombinedMeters[iCmbnd].VolumeErrorPct >= tstRslt.ErrLimLo + tstRslt.Uncertainty)
&& (tstRslt.CombinedMeters[iCmbnd].VolumeErrorPct <= tstRslt.ErrLimHi - tstRslt.Uncertainty);
}
return tstRslt;
}
/// <summary>
/// Returns information whether a meter is a main meter or an aux. meter (compound meters).
/// </summary>
/// <param name="i">Index to TestResult.Meters[] array</param>
/// <returns>true = main meter, false = aux. meter</returns>
bool IsMainMtr(int i)
{
return (i % 2) == 0;
}
}
}