/// /// Copyright (c) 2013-2015 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Linq; using log4net; using Config; using Config.Entities; 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 : 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 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 ///------------------------------------------------------------ public static int ReferenceFlowmetersCount; public static float[] CalibratedLtrPerRefPulse; /// Reference flowmeter coefficients public static double Qrise; public static double Qfall; ///------------------------------------------------------------ /// 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 BenchControl.HeatMetersPath heatMetersPath; protected static TransitionSequence transitionBefore; protected static TransitionSequence transitionAfter; protected IOperation readRegistersOp; protected IOperation queryEnd1; protected IOperation queryEnd2; protected IOperation checkUiOp; protected IOperation processDataLoggingOp; /// /// 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.DoubleToStr(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")); } /// /// 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) { bool stopped = false; //------------------------------------------------ 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 (TestAndLogUiCmdStop(e)) { stopped = true; break; } if (e.Contains(Event.BalanceOverload)) { }; /// Tank should be emptying now } while (!e.Contains(Event.BalanceDone)); if (stopped) break; } 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)); if (stopped) return Event.UiCmdStop; else return Event.Done; } /// /// Passed as an argument to Transition(sequence, context) /// public enum TransitionContext { PurgeBegin, BeforeTest, AfterTest, PurgeEnd, Stop, } /// /// 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(TransitionSequence transitionSequence, TransitionContext context) { bool stopFlag = false; bool errorFlag = false; IList e; string message; /// switch (context) { case TransitionContext.PurgeBegin: message = Strings.Purging_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; case TransitionContext.PurgeEnd: message = Strings.Emptying_i_n; break; case TransitionContext.Stop: message = Strings.Test_stop_sequence_i_n; break; default: message = "Transition"; break; } if (transitionSequence == null) { log.WarnFormat("Transition(null, context={0})", context); /// /// No transition sequence defined --> Default action /// if (context == TransitionContext.AfterTest) { if (inPath.Pump is GenericDevices.IPumpFM) (inPath.Pump as GenericDevices.IPumpFM).TurnOff(); 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 (TestAndLogUiCmdStop(e)) 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; log.WarnFormat("Transition(sequence={0} ({1} steps), context={2})", transitionSequence.Name, stepsCount, context); 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 rvPosOps = new List(); IList rvPosStr = new List(); for (int i = 0; i < allRegvPositions.Length; i++) { if (allRegvPositions[i] >= 0) /// Negative value means no position change { if (RegulValves[i].IsCoax) { rvPosOps.Add(RegulValves[i].SetRegulValvePositionOp(allRegvPositions[i], allRegvPositions[i], 60)); rvPosStr.Add(string.Format("RV{0}.SetRegulValvePositionOp({1}, {1}, 60s)", i, allRegvPositions[i])); } else { 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); /// 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 (TestAndLogUiCmdStop(e)) { 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 (TestAndLogUiCmdStop(e)) { 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 (TestAndLogUiCmdStop(e)) { stopFlag = true; } } if (errorFlag || stopFlag) break; } Bridge.OnMessage(this, string.Empty); /// Clear the last step message } /// /// Do this after executing the transition sequence /// if (context == TransitionContext.Stop || errorFlag || stopFlag) { /// /// On error or when STOP pressed /// foreach (var fmPump in PumpsWithFM) fmPump.TurnOff(); if (inPath != null) { /// Stop the pump /// State.Create("SequenceBase.Transition() : Test stopped -> 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))); } } else if (context == TransitionContext.BeforeTest) { if (inPath != null && benchPath != null && outPath != null) { 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.Error)) return Event.Error; if (TestAndLogUiCmdStop(e)) return Event.UiCmdStop; } while (e.Contains(Event.ValvesBusy)); } } if (errorFlag) return Event.Error; else if (stopFlag) return Event.UiCmdStop; else 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, Strings.Enter_water_meter_data); State.Create("MainSeq : Enter begin data") .AddPermanentOperation((dataEntryCmpnt as IHasCycleBeginForm).ShowCycleBeginFormOp()) .AddOperation(checkUiOp) .EnterState(); e = StateMachine.WaitRunDevsRunOps(); if (TestAndLogUiCmdStop(e)) 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 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 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 (TestAndLogUiCmdStop(e)) { 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 (TestAndLogUiCmdStop(e)) { stopPressed = true; } } return stopPressed ? UIFlowControl.Stop : UIFlowControl.Continue; } /// /// 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; 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(heatMetersPath == null ? null : heatMetersPath.TMeterRefWarm1.ReadTempOp(ref TempRefWarm1)) .AddOperation(heatMetersPath == null ? null : heatMetersPath.TMeterRefWarm2.ReadTempOp(ref TempRefWarm2)) .AddOperation(heatMetersPath == null ? null : heatMetersPath.TMeterRefCold1.ReadTempOp(ref TempRefCold1)) .AddOperation(heatMetersPath == null ? null : heatMetersPath.TMeterRefCold2.ReadTempOp(ref TempRefCold2)) .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 (TestAndLogUiCmdStop(e)) return Event.UiCmdStop; if (e.Contains(Event.MeasurementCompleted)) return Event.MeasurementCompleted; } while ( (realTest && !e.Contains(Event.BalanceDone)) || !e.Contains(Event.ReadAllRegistersDone)); return Event.Done; } protected string TestResult2CsvLine(string testName, int part) { Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(testName, part); if (tstRslt == null) return string.Empty; return TestResult2CsvLine(tstRslt); } protected string TestResult2CsvLine(Results.Entities.TestRslt tstRslt) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append(tstRslt.StartTime); sb.Append(";"); sb.Append(tstRslt.Batch.BatchNr); sb.Append(";"); sb.Append(tstRslt.Name()); sb.Append(";"); sb.Append(tstRslt.RepetitionNr); sb.Append(";"); sb.Append("1"); sb.Append(";"); sb.Append(tstRslt.Method()); sb.Append(";"); sb.Append(tstRslt.TargetVolume()); 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(Units.ConvertTo(Unit.mbar, tstRslt.AmbientPressAve).ToString("F0")); /// [mbar] sb.Append(";"); sb.Append(tstRslt.AmbientHumiAve); /// [%] sb.Append(";"); sb.Append(Units.ConvertTo(Unit.kPa, tstRslt.PressUpAvrg).ToString("F0")); /// [kPa] sb.Append(";"); sb.Append(Units.ConvertTo(Unit.kPa, tstRslt.PressDownAvrg).ToString("F0")); /// [kPa] sb.Append(";"); sb.Append(Units.ConvertTo(Unit.kPa, tstRslt.PressUpStart).ToString("F0")); /// [kPa] sb.Append(";"); sb.Append(Units.ConvertTo(Unit.kPa, tstRslt.PressDownStart).ToString("F0")); /// [kPa] sb.Append(";"); sb.Append(Units.ConvertTo(Unit.kPa, tstRslt.PressUpEnd).ToString("F0")); /// [kPa] sb.Append(";"); sb.Append(Units.ConvertTo(Unit.kPa, tstRslt.PressDownEnd).ToString("F0")); /// [kPa] 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.MassEnd - tstRslt.MassStart); 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.TestTime); /// 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("0"); /// Time Div Start celkovy cas v [ms] sb.Append(";"); sb.Append("0"); /// Time Div Start1 sb.Append(";"); sb.Append("0"); /// Time Div Start2 sb.Append(";"); sb.Append("0"); /// Time Div Start3 sb.Append(";"); sb.Append("0"); /// Time Div Start4 sb.Append(";"); sb.Append("0"); /// Time Div Start5 sb.Append(";"); sb.Append("0"); /// Time Div End celkovy cas v [ms] sb.Append(";"); sb.Append("0"); /// Time Div End1 sb.Append(";"); sb.Append("0"); /// Time Div End2 sb.Append(";"); sb.Append("0"); /// Time Div End3 sb.Append(";"); sb.Append("0"); /// Time Div End4 sb.Append(";"); sb.Append("0"); /// 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 < ProcessData.BatchRslts.WMPositionsCount; i++) { if (ProcessData.BatchRslts.WaterMeters[i] != null) { if (!ProcessData.BatchRslts.WaterMeters[i].Compound()) { Results.Entities.MeterTestRslt mtrRslt = ProcessData.BatchRslts.GetMeterTestRslt(tstRslt.Name(), i, CompoundMeterId.Single); if (mtrRslt != null) { sb.Append(";"); sb.Append(ProcessData.BatchRslts.WaterMeters[i].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.Error); /// WM Emt - chyba vodomerom nameraneho objemu #if IPERLST || IPERLST_SPECIAL sb.Append(";"); sb.Append(ProcessData.BatchRslts.WaterMeters[i].CalibFactor); /// iPerl calibration factor used during the test / ... #else sb.Append(";"); sb.Append(" "); /// nechat prazdne #endif 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.TestTime); /// WM Tmet - cas merania (obmedzany pri synchro skuske) sb.Append(";"); sb.Append(mtrRslt.Passed ? "OK" : "NOK"); /// WM Vysledok (t.j. ci je v hraniciach chyb) - OK/NOK #if IPERLST || IPERLST_SPECIAL sb.Append(";"); sb.Append(mtrRslt.WaterMeter.Q2Correction.ToString("F1")); /// iPerl Q2 correction factor used during the test / AN value - hodnota z analogoveho prevodnika #else sb.Append(";"); sb.Append(" "); /// nechat prazdne #endif sb.Append(";"); sb.Append(mtrRslt.VolumeStart); /// WM Volume_start - pri datastreamovych hodnotach (alebo kamera) sb.Append(";"); sb.Append(mtrRslt.TimestampStart); /// WM Time_start - ' ' - sb.Append(";"); sb.Append(mtrRslt.VolumeEnd); /// WM Volume_end - ' ' - sb.Append(";"); sb.Append(mtrRslt.TimestampEnd); /// WM Time_end - ' ' - } } else { for (byte b = (byte)CompoundMeterId.CompoundMain; b <= (byte)CompoundMeterId.Compound; b++) { Results.Entities.MeterTestRslt mtrRslt = ProcessData.BatchRslts.GetMeterTestRslt(tstRslt.Name(), i, (CompoundMeterId)b); if (mtrRslt != null) { sb.Append(";"); switch ((CompoundMeterId)b) { case CompoundMeterId.CompoundMain: sb.Append(ProcessData.BatchRslts.WaterMeters[i].SerialNr); break; case CompoundMeterId.CompoundAux: sb.Append(ProcessData.BatchRslts.WaterMeters[i].SerialNrAux); break; case CompoundMeterId.Compound: sb.Append(ProcessData.BatchRslts.WaterMeters[i].SerialNr); break; } 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.Error); /// 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.TestTime); /// WM Tmet - cas merania (obmedzany pri synchro skuske) sb.Append(";"); sb.Append(mtrRslt.Passed ? "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.TimestampStart); /// WM Time_start - ' ' - sb.Append(";"); sb.Append(mtrRslt.VolumeEnd); /// WM Volume_end - ' ' - sb.Append(";"); sb.Append(mtrRslt.TimestampEnd); /// WM Time_end - ' ' - } } } } } sb.Append(";"); return sb.ToString(); } /// /// Create a simulated test result (single meter). /// /// Test to be simulated /// Test result protected void MakeSimulated(string testName, int repeats, int repetitionNr, int part, float errorPctBase) { string fullTestName = Results.Utils.GetTestName(testName, repeats, repetitionNr); Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(fullTestName, part); tstRslt.StartTime = DateTime.Now; tstRslt.EndTime = DateTime.Now; tstRslt.FlowSetTime = 10; tstRslt.TestTime = tstRslt.TargetTime(); /// TODO: Verify whether 'ltrPerRefPulse' is up to date tstRslt.PulsesMaster = (LtrPerRefPulse > 1E-6) ? (tstRslt.TargetVolume() / LtrPerRefPulse) : 1; tstRslt.ConstMaster = LtrPerRefPulse; tstRslt.MassStartRaw = 0; tstRslt.MassStart = 0; tstRslt.MassEndRaw = tstRslt.TargetVolume() * Program.LocalSettings.RealDensity / 1000.0f; tstRslt.MassEnd = tstRslt.MassEndRaw; tstRslt.DensityIn = Program.LocalSettings.RealDensity; tstRslt.DensityOut = Program.LocalSettings.RealDensity; tstRslt.DensityDiv = Program.LocalSettings.RealDensity; tstRslt.Buoyancy = 0; /// TODO tstRslt.FlowMass = 3600.0 * tstRslt.MassEnd / tstRslt.TargetTime(); tstRslt.FlowVolume = 3.6 * tstRslt.TargetVolume() / tstRslt.TargetTime(); tstRslt.VolumeCTV = tstRslt.TargetVolume(); tstRslt.VolumeMaster = tstRslt.TargetVolume(); tstRslt.ErrorMaster = 0; tstRslt.AmbientTempAve = 20.0f; tstRslt.AmbientPressAve = 1.0f; tstRslt.AmbientHumiAve = 50.0f; tstRslt.PressUpStart = 1.0f; tstRslt.PressDownStart = 1.0f; tstRslt.TempInStart = 20.0f; tstRslt.TempOutStart = 20.0f; tstRslt.TempDivStart = 20.0f; tstRslt.PressUpEnd = 1.0f; tstRslt.PressDownEnd = 1.0f; tstRslt.TempInEnd = 20.0f; tstRslt.TempOutEnd = 20.0f; tstRslt.TempDivEnd = 20.0f; tstRslt.PressUpAvrg = 1.0f; tstRslt.PressDownAvrg = 1.0f; tstRslt.TempInAvrg = 20.0f; tstRslt.TempOutAvrg = 20.0f; tstRslt.TempDivAvrg = 20.0f; tstRslt.PressUpMin = 1.0f; tstRslt.PressDownMin = 1.0f; tstRslt.TempInMin = 20.0f; tstRslt.TempOutMin = 20.0f; tstRslt.TempDivMin = 20.0f; tstRslt.PressUpMax = 1.0f; tstRslt.PressDownMax = 1.0f; tstRslt.TempInMax = 20.0f; tstRslt.TempOutMax = 20.0f; tstRslt.TempDivMax = 20.0f; tstRslt.FlowMin = 0; /// TODO tstRslt.FlowMax = 0; /// TODO for (int i = 0; i < BatchRslts.WMPositionsCount; i++) { float errorPct = errorPctBase + 0.05f * i; Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(fullTestName, i, CompoundMeterId.Single); IRegisterReader regReader = sensPath.RegisterReaders[i]; if (meterRslt != null && regReader != null) { meterRslt.VolumeMeter = tstRslt.TargetVolume() * (1.0 + 0.01 * errorPct); meterRslt.PulsesMeter = regReader.PulsesPerLtr * meterRslt.VolumeMeter; meterRslt.PulsesMaster = tstRslt.PulsesMaster; meterRslt.PulsesPerLiter = regReader.PulsesPerLtr; meterRslt.VolumeStart = 0; meterRslt.VolumeEnd = meterRslt.VolumeMeter; meterRslt.VolumeRef = tstRslt.TargetVolume(); meterRslt.TimestampStart = 0; meterRslt.TimestampEnd = tstRslt.TargetTime(); meterRslt.TestTime = tstRslt.TargetTime(); meterRslt.Error = errorPct; meterRslt.Passed = (errorPct >= tstRslt.ErrLimLo() + tstRslt.Uncertainty()) && (errorPct <= tstRslt.ErrLimHi() - tstRslt.Uncertainty()); meterRslt.TestDone = true; } } tstRslt.Components = Results.Entities.Components .UpdateList(BatchRslts.ComponentsList, new Results.Entities.Components((BenchInfo != null) ? BenchInfo.TestBenchId : 1, (BenchInfo != null) ? BenchInfo.TestBenchName : "testbench", inPath.Pump != null ? inPath.Pump.Name : string.Empty, outPath.FlowMeter != null ? outPath.FlowMeter.Name : string.Empty, outPath.Balance != null ? outPath.Balance.Name : string.Empty, outPath.RegulValve != null ? outPath.RegulValve.Name : string.Empty, outPath.Diverter != null ? outPath.Diverter.Name : string.Empty)); } /// /// Create a simulated test result (compound meter). /// /// Test to be simulated /// Test result protected void MakeSimulatedCompound(string testName, int repeats, int repetitionNr, int part, float errorPct, float mainPart) { string fullTestName = Results.Utils.GetTestName(testName, repeats, repetitionNr); Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(fullTestName, part); tstRslt.StartTime = DateTime.Now; tstRslt.EndTime = DateTime.Now; tstRslt.FlowSetTime = 10; tstRslt.TestTime = tstRslt.TargetTime(); /// TODO: Verify whether 'ltrPerRefPulse' is up to date tstRslt.PulsesMaster = (LtrPerRefPulse > 1E-6) ? (tstRslt.TargetVolume() / LtrPerRefPulse) : 1; tstRslt.ConstMaster = LtrPerRefPulse; tstRslt.MassStartRaw = 0; tstRslt.MassStart = 0; tstRslt.MassEndRaw = tstRslt.TargetVolume() * Program.LocalSettings.RealDensity / 1000.0f; tstRslt.MassEnd = tstRslt.MassEndRaw; tstRslt.DensityIn = Program.LocalSettings.RealDensity; tstRslt.DensityOut = Program.LocalSettings.RealDensity; tstRslt.DensityDiv = Program.LocalSettings.RealDensity; tstRslt.Buoyancy = 0; /// TODO tstRslt.FlowMass = tstRslt.MassEnd / tstRslt.TargetTime(); tstRslt.FlowVolume = tstRslt.TargetVolume() / tstRslt.TargetTime(); tstRslt.VolumeCTV = tstRslt.TargetVolume(); tstRslt.VolumeMaster = tstRslt.TargetVolume(); tstRslt.ErrorMaster = 0; tstRslt.AmbientTempAve = 20.0f; tstRslt.AmbientPressAve = 1.0f; tstRslt.AmbientHumiAve = 50.0f; tstRslt.PressUpStart = 1.0f; tstRslt.PressDownStart = 1.0f; tstRslt.TempInStart = 20.0f; tstRslt.TempOutStart = 20.0f; tstRslt.TempDivStart = 20.0f; tstRslt.PressUpEnd = 1.0f; tstRslt.PressDownEnd = 1.0f; tstRslt.TempInEnd = 20.0f; tstRslt.TempOutEnd = 20.0f; tstRslt.TempDivEnd = 20.0f; tstRslt.PressUpAvrg = 1.0f; tstRslt.PressDownAvrg = 1.0f; tstRslt.TempInAvrg = 20.0f; tstRslt.TempOutAvrg = 20.0f; tstRslt.TempDivAvrg = 20.0f; tstRslt.PressUpMin = 1.0f; tstRslt.PressDownMin = 1.0f; tstRslt.TempInMin = 20.0f; tstRslt.TempOutMin = 20.0f; tstRslt.TempDivMin = 20.0f; tstRslt.PressUpMax = 1.0f; tstRslt.PressDownMax = 1.0f; tstRslt.TempInMax = 20.0f; tstRslt.TempOutMax = 20.0f; tstRslt.TempDivMax = 20.0f; tstRslt.FlowMin = 0; /// TODO tstRslt.FlowMax = 0; /// TODO for (int i = 0; i < BatchRslts.WMPositionsCount; i++) { Results.Entities.MeterTestRslt compoundRslt = ProcessData.BatchRslts.GetMeterTestRslt(fullTestName, i, CompoundMeterId.Compound); Results.Entities.MeterTestRslt mainRslt = ProcessData.BatchRslts.GetMeterTestRslt(fullTestName, i, CompoundMeterId.CompoundMain); Results.Entities.MeterTestRslt auxRslt = ProcessData.BatchRslts.GetMeterTestRslt(fullTestName, i, CompoundMeterId.CompoundAux); double compoundVolume = tstRslt.TargetVolume() * (1.0 + 0.01 * errorPct); double mainVolume = compoundVolume * mainPart; double auxVolume = compoundVolume * (1.0 - mainPart); for (int isAux = 0; isAux <= 1; isAux++) /// 0=main, 1=aux { GenericDevices.IRegisterReader regReader = sensPath.RegisterReaders[2 * i + isAux]; Results.Entities.MeterTestRslt meterRslt = (isAux == 0) ? mainRslt : auxRslt; if (meterRslt != null && regReader != null) { meterRslt.VolumeMeter = (isAux == 0) ? mainVolume : auxVolume; meterRslt.PulsesMeter = regReader.PulsesPerLtr * meterRslt.VolumeMeter; meterRslt.PulsesMaster = tstRslt.PulsesMaster; meterRslt.PulsesPerLiter = regReader.PulsesPerLtr; meterRslt.VolumeStart = 0; meterRslt.VolumeEnd = meterRslt.VolumeMeter; meterRslt.VolumeRef = tstRslt.TargetVolume(); meterRslt.TimestampStart = 0; meterRslt.TimestampEnd = tstRslt.TargetTime(); meterRslt.TestTime = tstRslt.TargetTime(); meterRslt.Error = Formulas.ErrorFromVolumes(meterRslt.VolumeMeter, tstRslt.VolumeCTV); meterRslt.Passed = (errorPct >= tstRslt.ErrLimLo() + tstRslt.Uncertainty()) && (errorPct <= tstRslt.ErrLimHi() - tstRslt.Uncertainty()); meterRslt.TestDone = true; } } compoundRslt.VolumeRef = tstRslt.VolumeCTV; /// [l] must be calculated before main & aux. meter error compoundRslt.VolumeMeter = mainRslt.VolumeMeter + auxRslt.VolumeMeter; compoundRslt.PulsesMaster = tstRslt.PulsesMaster; compoundRslt.TestTime = tstRslt.TargetTime(); compoundRslt.Error = Formulas.ErrorFromVolumes(compoundRslt.VolumeMeter, tstRslt.VolumeCTV); compoundRslt.Passed = (compoundRslt.Error >= tstRslt.ErrLimLo() + tstRslt.Uncertainty()) && (compoundRslt.Error <= tstRslt.ErrLimHi() - tstRslt.Uncertainty()); compoundRslt.TestDone = true; } tstRslt.Components = Results.Entities.Components .UpdateList(BatchRslts.ComponentsList, new Results.Entities.Components((BenchInfo != null) ? BenchInfo.TestBenchId : 1, (BenchInfo != null) ? BenchInfo.TestBenchName : "testbench", inPath.Pump != null ? inPath.Pump.Name : string.Empty, outPath.FlowMeter != null ? outPath.FlowMeter.Name : string.Empty, outPath.Balance != null ? outPath.Balance.Name : string.Empty, outPath.RegulValve != null ? outPath.RegulValve.Name : string.Empty, outPath.Diverter != null ? outPath.Diverter.Name : string.Empty)); } protected bool TestAndLogUiCmdStop(IList e) { return TestAndLogUiCmdStop(null, e); } /// /// Returns true and makes a log when 'e' contains Event.UiCmdStop /// /// /// /// protected bool TestAndLogUiCmdStop(Test test, IList e) { if (!e.Contains(Event.UiCmdStop)) return false; log.FatalFormat("STOP pressed: Procedure={0}, Test={1}, State={2}", (StateMachine.Procedure == null) ? "?" : StateMachine.Procedure.Name, (test == null) ? "?" : test.Name, State.CurrentState.Name); if (test != null) { log.FatalFormat("Process values:"); log.FatalFormat(" Method: {0}", test.Method); log.FatalFormat(" Test start time: {0}", TestStartTime.ToShortTimeString()); log.FatalFormat(" Feeding path: {0}", (inPath != null) ? inPath.ToString() : "none"); log.FatalFormat(" Bench path: {0}", (benchPath != null) ? benchPath.ToString() : "none"); log.FatalFormat(" Output path: {0}", (outPath != null) ? outPath.ToString() : "none"); if (inPath.Pump is IPump) log.FatalFormat(" Pump power: {0}%", (inPath.Pump as IPump).Power); else if (inPath.Pump is IValve) log.FatalFormat(" Feeding valve: {0}", (inPath.Pump as IValve).State ? "open" : "close"); if (outPath.RegulValve is IRegulValve) log.FatalFormat(" Regulation valve position: {0}%", outPath.RegulValve.Position); log.FatalFormat(" Flow: {0}", RefFlow); log.FatalFormat(" Mass: {0}", Mass); log.FatalFormat(" Start mass: {0}", StartMass); log.FatalFormat(" Liter/ref.pulse: {0}", LtrPerRefPulse); log.FatalFormat(" Reference pulses: {0}", RefPulses); } return true; } } }