/// /// Copyright (c) 2013-2015 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Linq; using log4net; using TBF.BenchControl.GenericDevices; using TBF.BenchControl.Operations; using TBF.Boxes; using TBF.Resources; using TBF.UiBridge; namespace TBF.BenchControl.Sequences { /// /// Sequence is a group of states that can be dynamically added to /// and removed from the state machine /// public class SequenceBase { 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 BenchId.Component BenchId; public static IList FlowMeters; /// list of reference flowmeters public static IList RegulValves; /// list of regulation valves public static IList PumpsWithFM; /// list of FM controlled pumps public static IList WaterMeters; /// list of water meters public static IList Cameras; /// list of cameras ///------------------------------------------------------------ /// Procedure related (static) variables. /// They are re-initialized when LoadProcedure() is called ///------------------------------------------------------------ protected static IList results; public static int ReferenceFlowmetersCount; public static float[] LtrPerRefPulse; /// Reference flowmeter coefficients public static float Qrise; public static float Qfall; /// /// Clear all test results. /// protected static void ResetResults() { results = new List(); Qrise = 0; Qfall = 0; } /// /// Add result to the list of results. /// Overwrite (=delete) any previous result with the same name. /// /// New test result protected static void AddOrOverwriteResult(Entities.TestResult newTestResult) { Entities.TestResult toDelete = results.FirstOrDefault(x => x.Name.Equals(newTestResult.Name) && (x.Part == newTestResult.Part) && (x.TestId == newTestResult.TestId)); if (toDelete != null) results.Remove(toDelete); results.Add(newTestResult); } /// /// Check whether the results are complete, whether there is a result for each test. /// /// All tests /// true = The results are complete protected static bool ResultsAreComplete(IList 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) protected static bool cycleBeginFormOpened; static SequenceBase() { results = new List(); WMVolumes = new FloatBox[Program.WMsCount]; WMErrors = new FloatBox[Program.WMsCount]; for (int i = 0; i < Program.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" }; } cycleBeginFormOpened = false; } ///------------------------------------------------------------ /// 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 Entities.TransitionSequence transitionStart; protected static Entities.TransitionSequence transitionStop; #region Temperature_Pressure_Humidity protected static FloatBox tempIn = new FloatBox() { Name = "Temperature In", Format = "F2" }; protected static FloatBox tempOut = new FloatBox() { Name = "Temperature Out", Format = "F2" }; protected static FloatBox tempDiv = new FloatBox() { Name = "Temperature Div", Format = "F2" }; protected static FloatBox pressIn = new FloatBox() { Name = "Pressure In", Format = "F3", Factor = 0.01f }; protected static FloatBox pressOut = new FloatBox() { Name = "Pressure Out", Format = "F3", Factor = 0.01f }; protected static FloatBox airTemperature = new FloatBox() { Name = "Ambient Temperature", Format = "F1" }; protected static FloatBox airPressure = new FloatBox() { Name = "Ambient Pressure", Format = "F1" }; protected static FloatBox airHumidity = new FloatBox() { Name = "Ambient Humidity", Format = "F1" }; /// 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 += pressIn.Val; pressOutSum += pressOut.Val; ambientTempSum += airTemperature.Val; ambientPressSum += airPressure.Val; ambientHumiSum += airHumidity.Val; /// averagedDataCount++; } /// protected void UpdateTestRsltWithAveragedData(Entities.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 readRegisters1; protected IOperation readRegisters2; protected IOperation queryEnd1; protected IOperation queryEnd2; protected IOperation checkUiOp; protected IOperation processDataLoggingOp; protected bool ticTac = false; protected int[] WMPulses = new int[Program.WMsCount]; protected int[] WMRefPulses = new int[Program.WMsCount]; protected static FloatBox[] WMVolumes; protected static FloatBox[] WMErrors; protected static IntBox refCount = new IntBox() { Name = "RefCount" }; protected static FloatBox refFreq = new FloatBox() { Name = "RefFreq", Format = "F2" }; protected static FloatBox refFlow = new FloatBox() { Name = "RefFlow", Format = "F2" }; protected static FloatBox mass = new FloatBox() { Name = "Mass", Format = "F3" }; protected static FloatBox startMass = new FloatBox() { Name = "Start Mass", Format = "F3" }; protected static FloatBox endMass = new FloatBox() { Name = "End Mass", Format = "F3" }; /// /// To clear process values at the beginning of each test /// 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 < Program.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, pressIn, pressOut, mass, "VolMM", airTemperature, airHumidity, airPressure, outPath.RegulValve.Position.ToString("F1")); } /// /// Empties the tank: opens the emptying valve and measures the weight. /// /// Valve to empty the tank /// Balance underneath the tank /// Event.Done or Event.Error protected Event EmptyTheTank(IValve EmptyTankValve, IBalance Balance) { //------------------------------------------------ Bridge.OnActivity(this, TBF.Resources.Strings.Emptying_tank); //------------------------------------------------ IList 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; } /// /// Passed as an argument to Transition(sequence, context) /// public enum TransitionContext { PurgeBegin, PurgeEnd, TestStart, TestEnd, } /// /// Executes steps of a transition sequence /// /// TransitionSequence entity /// Calling context (see above) /// /// 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) /// protected Event Transition(Entities.TransitionSequence transitionSequence, TransitionContext context) { IList 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.TestStart: message = Strings.Test_start_sequence_i_n; break; case TransitionContext.TestEnd: message = Strings.Test_stop_sequence_i_n; break; default: message = "Transition"; break; } if (transitionSequence == null) { /// /// No transition sequence defined --> Default action /// if (context == TransitionContext.TestStart) { State.Create("SequenceBase : Transition : TestStart - Default action") .AddOperation(checkUiOp).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.TestEnd) { State.Create("SequenceBase : Transition : TestEnd - Default action") .AddOperation(checkUiOp).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 transitionSteps = StateMachine.WtSession .CreateQuery("FROM TransitionStep WHERE TransitionSequence = :tsId ORDER BY ItemNr") .SetParameter("tsId", transitionSequence.Id) .List(); int stepsCount = transitionSteps.Count; foreach (var step in transitionSteps) { //------------------------------------------------ Bridge.OnActivity(this, string.Format(message, transitionSequence.Name, step.ItemNr + 1, stepsCount)); Bridge.OnMessage(this, step.Message); //------------------------------------------------ /// FM controlled pumps are canged imediately without using any state operations 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 rvPosOps = new List(); for (int i = 0; i < allRegvPositions.Length; i++) { if (allRegvPositions[i] >= 0) /// Negative value means no position change { rvPosOps.Add(RegulValves[i] .SetRegulValvePositionOp(Math.Max(0, allRegvPositions[i] - 0.05f), Math.Min(100.0f, allRegvPositions[i] + 0.05f), 60)); } } /// 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++) { State stepStrt = State.Create(string.Format("SequenceBase.Transition() : Step {0}", step.ItemNr + 1)) .AddOperation(checkUiOp) .AddOperation(StateMachine.ControlBoard.SetValvesOp(Utils.ValvesOpen(step), Utils.ValvesClose(step))); for (int j = 0; j <= i; j++) stepStrt.AddOperation(rvPosOps[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)) { State stepDelay = State.Create(string.Format("SequenceBase.Transition() : Step {0}", step.ItemNr + 1)) .AddOperation(checkUiOp) .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]); stepDelay.EnterState(); do { e = StateMachine.WaitRunDevsRunOps(); if (e.Contains(Event.Error) || e.Contains(Event.RegulValveTimeOut)) { errorFlag = true; break; } if (e.Contains(Event.UiCmdStop)) { stopFlag = true; break; } } while (e.Contains(Event.TimerBusy) || e.Contains(Event.ValvesBusy)); } for (int first = 1; first <= lastStartedRV; first++) { State stepStop = State.Create(string.Format("SequenceBase.Transition() : Step {0}", step.ItemNr + 1)) .AddOperation(checkUiOp) .AddOperation(StateMachine.ControlBoard.SetValvesOp(Utils.ValvesOpen(step), Utils.ValvesClose(step))); for (int j = first; j <= lastStartedRV; j++) stepStop.AddOperation(rvPosOps[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.TestEnd) { /// /// 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((inPath.Pump is GenericDevices.IPumpFM) ? (inPath.Pump as GenericDevices.IPumpFM).TurnOffOp() : null) .AddOperation(StateMachine.ControlBoard.SetValvesOp(null, inPath.Pump)) .AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp()) .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; } /// /// Opens a modeless dialog for entering data at the beginning of a procedure (serial numbers) /// /// false = OK, true = stop pressed protected bool OpenCycleBeginForm() { IList 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; } /// /// 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. /// /// false = OK, true = stop pressed protected bool WaitBeginFormClosed() { GenericDevices.IDataEntry dataEntryCmpnt = TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IDataEntry; bool forcedClose = false; /// When STOP button pressed if (dataEntryCmpnt is IHasCycleBeginForm) { IList 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)) { forcedClose = 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)) { forcedClose = true; } } return forcedClose; } /// /// 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. /// 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 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)); } } /// /// Main loop where measurements are collected. /// /// false = a flow setting or a switching flow detection, true = measurement /// Event.MeasurementCompleted, Event.UiCmdStop, Event.Error or Event.Done protected Event ReadRegistersTempPressAmbient(IList measureOperations, bool realTest) { IList e; ticTac = !ticTac; State.Create("Read water meters") .AddOperation(checkUiOp) .AddOperations(measureOperations) .AddOperation(ticTac ? readRegisters1 : readRegisters2) .AddOperation(benchPath.TempIn.ReadTempOp(ref tempIn)) .AddOperation(benchPath.TempOut.ReadTempOp(ref tempOut)) .AddOperation(outPath.TempDiv.ReadTempOp(ref tempDiv)) .AddOperation(benchPath.PressIn.ReadPressureOp(ref pressIn)) .AddOperation(benchPath.PressOut.ReadPressureOp(ref pressOut)) .AddOperation(realTest ? outPath.Balance.ReadMassOp(ref mass) : null) .AddOperation(StateMachine.ControlBoard.UpdateTankWeightOp()) .AddOperation((StateMachine.Ambient != null) ? StateMachine.Ambient.ReadAmbientOp(airTemperature, airPressure, airHumidity) : null) .AddOperation(realTest ? (ticTac ? queryEnd1 : queryEnd2) : 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; } /// /// Prepares 'TestProgressEventArgs' object that update screens during the test /// /// Current 'Test' entity /// Current 'TestResult' entity /// Control board component reference /// Current test time in [s] /// Current progress 0 .. 1.0f /// Data for the UI protected TestProgressEventArgs GetTestProgressData(Entities.Test test, Entities.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 = pressIn; data.Pout = pressOut; data.AmbientTemp = airTemperature; data.AmbientPressure = airPressure; data.AmbientHumidity = airHumidity; 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(Program.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 = data.Volume.Val; data.TestResult.Meters[i].VolumeErrorPct = Formulas.ErrorFromVolumes(data.TestResult.Meters[i].VolumeMeter, data.Volume.Val); } } } else { data.Volume = new FloatBox() { Name = "Volume", Format = "F1", Val = 0 }; } return data; } protected string TestResult2CsvLine(Entities.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); sb.Append(";"); sb.Append(tstRslt.ErrLimHi); sb.Append(";"); sb.Append("0"); sb.Append(";"); sb.Append("60"); sb.Append(";"); sb.Append("0"); sb.Append(";"); sb.Append("0"); sb.Append(";"); sb.Append("2"); 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(" "); /// Exp T: teraz vynechat sb.Append(";"); sb.Append(" "); /// Exp P: teraz vynechat 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++) { Entities.MeterTestResult mtrRslt = tstRslt.Meters[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) && (mtrRslt.VolumeErrorPct <= tstRslt.ErrLimHi); 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(" "); /// WM Vinit - pri datastreamovych hodnotach (alebo kamera) sb.Append(";"); sb.Append(" "); /// WM Time init ??? sb.Append(";"); sb.Append(" "); /// WM Vend ??? sb.Append(";"); sb.Append(" "); /// WM Time end ??? } for (int i = 0; i < tstRslt.CombinedMeters.Count; i++) { Entities.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) && (mtrRslt.VolumeErrorPct <= tstRslt.ErrLimHi); 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(" "); /// WM Vinit - pri datastreamovych hodnotach (alebo kamera) sb.Append(";"); sb.Append(" "); /// WM Time init ??? sb.Append(";"); sb.Append(" "); /// WM Vend ??? sb.Append(";"); sb.Append(" "); /// WM Time end ??? } sb.Append(";"); return sb.ToString(); } } }