diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs index 0cdaaacee..2b87e7171 100644 --- a/TBF/Properties/AssemblyInfo.cs +++ b/TBF/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("3.3.1935.0")] -[assembly: AssemblyFileVersion("3.3.1935.0")] +[assembly: AssemblyVersion("3.3.1940.0")] +[assembly: AssemblyFileVersion("3.3.1940.0")] diff --git a/TBF/Rig/TbfComponents.cs b/TBF/Rig/TbfComponents.cs index e57aa3f80..573b93092 100644 --- a/TBF/Rig/TbfComponents.cs +++ b/TBF/Rig/TbfComponents.cs @@ -187,6 +187,7 @@ namespace TBF.Rig Factories.Add(new TestMethods.StandingStartMassCollection.Single.Factory()); Factories.Add(new TestMethods.StandingStartMassCollection.Compound.Factory()); Factories.Add(new TestMethods.StandingStartMassCollection.HeatMeters.Factory()); + Factories.Add(new TestMethods.StandingStartMassCollWODiv.Factory()); Factories.Add(new BuiltIn.Valve.ValveFactory()); Factories.Add(new BuiltIn.ValveEx.ValveFactory()); Factories.Add(new Various.Clamping.Factory()); diff --git a/TBF/Rig/TestMethods/StandingStartMassCollWODiv/Component.cs b/TBF/Rig/TestMethods/StandingStartMassCollWODiv/Component.cs new file mode 100644 index 000000000..c20d71a3f --- /dev/null +++ b/TBF/Rig/TestMethods/StandingStartMassCollWODiv/Component.cs @@ -0,0 +1,39 @@ +/// +/// Copyright (c) 2022 Sensus Slovensko a.s. +/// +using System; +using System.Collections.Generic; +using log4net; +using Common; +using Config.Entities; + +namespace TBF.Rig.TestMethods.StandingStartMassCollWODiv +{ + public class Component : ComponentBase, GenericDevices.ITestMethod + { + private static readonly ILog log = LogManager.GetLogger(typeof(Component)); + public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } + + public bool CanTest(MetersKind meters) { return meters == MetersKind.Single; } + public bool DoTransitions() { return true; } + public bool CheckDeviceCaps(Test test, OutputPath devices, out string message) + { + return StandingStartMassCollWODirSeq.CheckDeviceCaps(test, devices, out message); + } + + public Component() { } + /// + public Component(Generic.IComponentCfg cfg) + : base(cfg) + { + log.Warn(this.ToString()); + } + /// + public override void Initialize() { } + + public IList Execute(Test test, int repetNr, bool isLastRepetition) + { + return (new StandingStartMassCollWODirSeq()).Execute(test, repetNr, isLastRepetition, DebugLevel); + } + } +} diff --git a/TBF/Rig/TestMethods/StandingStartMassCollWODiv/Factory.cs b/TBF/Rig/TestMethods/StandingStartMassCollWODiv/Factory.cs new file mode 100644 index 000000000..38b11f3a4 --- /dev/null +++ b/TBF/Rig/TestMethods/StandingStartMassCollWODiv/Factory.cs @@ -0,0 +1,26 @@ +/// +/// Copyright (c) 2022 Sensus Slovensko a.s. +/// +using System.Collections.Generic; +using TBF.Rig.Generic; +using TBF.Rig.Configs.NameOnly; + +namespace TBF.Rig.TestMethods.StandingStartMassCollWODiv +{ + public class Factory : IComponentFactory + { + public string ClassName { get { return this.GetType().Namespace.Substring(8); } } + public override string ToString() { return ClassName; } + + public IComponent DummyComponent() { return new Component(); } + + public IComponent GetComponent(IComponentCfg cfg, IList components) { return new Component(cfg); } + + public IComponentCfg DefaultConfig() { return new TestMethodCfg(this.GetType().Namespace.Substring(20), this); } + + public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component) + { + return ComponentCfgBase.CreateFromDbEntity(TestMethodCfg.Serializer, component, this); + } + } +} diff --git a/TBF/Rig/TestMethods/StandingStartMassCollWODiv/StandingStartMassCollWODirSeq.cs b/TBF/Rig/TestMethods/StandingStartMassCollWODiv/StandingStartMassCollWODirSeq.cs new file mode 100644 index 000000000..dcc87c6b3 --- /dev/null +++ b/TBF/Rig/TestMethods/StandingStartMassCollWODiv/StandingStartMassCollWODirSeq.cs @@ -0,0 +1,924 @@ +/// +/// Copyright (c) 2022 Sensus Slovensko a.s. +/// +using System; +using System.Collections.Generic; +using System.IO; +using log4net; +using Common; +using Config.Entities; +using TBF.Boxes; +using TBF.Resources; +using TBF.Rig.GenericDevices; +using TBF.UiBridge; + +namespace TBF.Rig.TestMethods.StandingStartMassCollWODiv +{ + public class StandingStartMassCollWODirSeq : Sequences.SequenceBase + { + private static readonly ILog log = LogManager.GetLogger(typeof(StandingStartMassCollWODirSeq)); + + /// + /// Check capabilities of devces in the output path required for this test method + /// + /// Devices + /// true when capabilities of devices are OK + public static bool CheckDeviceCaps(Config.Entities.Test test, OutputPath devices, out string message) + { + if (!(StateMachine.ControlBoard is ControlBoard.Papouch.PapouchCB) && + !(StateMachine.ControlBoard is ControlBoard.Uni.UniCB)) + { + /// Control board does not support this method + message = string.Format("{0}: {1}", test.Name, Strings.Test_bench_does_not_suport_selected_test_method); + return false; + } + + if (!(devices.Scale is IScale)) + { + /// Scale is missing + message = string.Format("{0}: {1}", test.Name, Strings.Missing_a_scale); + return false; + } + + if (test.Volume > devices.Scale.Capacity * Constants.TankFullFactor) + { + /// Scale capacity is not sufficient + message = string.Format("{0}: {1}", test.Name, Strings.Test_volume_exceeds_the_scale_capacity); + return false; + } + + if (!(devices.FlowMeter is IFlowMeter)) + { + /// Flow meter is missing + message = string.Format("{0}: {1}", test.Name, Strings.Missing_a_flow_meter); + return false; + } + + if (!(devices.RegValve is GenericDevices.IRegValve)) + { + /// Regulation valve is missing + message = string.Format("{0}: {1}", test.Name, Strings.Missing_a_regulation_valve); + return false; + } + + message = string.Empty; + return true; + } + + /// + /// Fixed start mass collection method sequence + /// + /// Test entity + /// + /// Event.Done . . . . . . . OK + /// Event.MakeSecondPass . . OK, 2nd pass (=evaluation) required + /// Event.UiCmdStop . . . . Stopped by the user using the on-screen button STOP + /// Event.OpArgumentError . Target flow is out of range + /// Event.Error . . . . . . Unspecified error + /// + public IList Execute(Config.Entities.Test test, int repetitionNr, bool isLastRepetition, + Common.DebugMode debugLevel) + { + ControlBoard.IControlBoard cBrd = StateMachine.ControlBoard; + IScale scale = cBrd.Devices.Scale as IScale; + + if ((outPath.FlowMeter is IFlowMeterSingle) && + (outPath.FlowMeter as IFlowMeterSingle).GetRange(test.TempLimLo, test.TempLimHi) == -1) + { + Bridge.OnError(this, Strings.Flow_meter_temperature_range_does_not_fit_this_test_conditions); + return new List { Event.ConfigurationError }; /// or Event.UiCmdStop ??? + } + + IList e; /// Events from currently running operations + int waterMetersCount = Math.Min(BenchInfo.WaterMetersCount, sensPath.RegisterReaders.Length); + DateTimeBox timeStampStart = new DateTimeBox(); + DateTimeBox timeStampEnd = new DateTimeBox(); + int tMass1 = 0; + int tMass2 = 0; + checkUiOp = new Operations.CheckUIOp(true); /// Runs in more then one state + processDataLoggingOp = new TBF.Rig.Operations.ProcessDataLoggingOp(processDataLogger, this, false); + + if (cBrd is ControlBoard.Uni.UniCB) + { + int[] filters = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + if (sensPath != null && sensPath.RegisterReaders != null) + { + foreach (var rr in sensPath.RegisterReaders) + { + IRegReaderPulses rrPls = rr as IRegReaderPulses; + if (rrPls != null && rrPls.Position >= 1 && rrPls.Position <= 8) + { + filters[rrPls.Position - 1] = rrPls.Filter; + } + } + } + (cBrd as ControlBoard.Uni.UniCB).SetFiltersPidShortPulses(filters, outPath.PidCoef, test.ShortPulses); + } + + IList readTempPressOps = new List(); + if (benchPath.TempMtrUp != null) readTempPressOps.Add(benchPath.TempMtrUp.ReadTempOp(ref TempUp)); + if (benchPath.TempMtrDown != null) readTempPressOps.Add(benchPath.TempMtrDown.ReadTempOp(ref TempDown)); + if (outPath.TempMtrDiv != null) readTempPressOps.Add(outPath.TempMtrDiv.ReadTempOp(ref TempDiv)); + if (benchPath.PressMtrUp != null) readTempPressOps.Add(benchPath.PressMtrUp.ReadPressureOp(ref PressUp)); + if (benchPath.PressMtrDown != null) readTempPressOps.Add(benchPath.PressMtrDown.ReadPressureOp(ref PressDown)); + if (benchPath.PressMtrDelta != null) readTempPressOps.Add(benchPath.PressMtrDelta.ReadPressureOp(ref PressDelta)); + if (heatMetersPath != null) readTempPressOps.Add(heatMetersPath.TMeterRefWarm1.ReadTempOp(ref TempRefHi1)); + if (heatMetersPath != null) readTempPressOps.Add(heatMetersPath.TMeterRefWarm2.ReadTempOp(ref TempRefHi2)); + if (heatMetersPath != null) readTempPressOps.Add(heatMetersPath.TMeterRefCold1.ReadTempOp(ref TempRefLo1)); + if (heatMetersPath != null) readTempPressOps.Add(heatMetersPath.TMeterRefCold2.ReadTempOp(ref TempRefLo2)); + + Event retVal = Event.Done; + + float switchTimeStart = 0.001f; /// in seconds, original vale is 1 ms + float switchTimeEnd = 0.001f; /// in seconds, original vale is 1 ms + + int totalPulses = Convert.ToInt32(test.Volume / outPath.FlowMeter.LtrPerPulse); + + ///============================================================================================ + + /// Read pressure and temperature once before calling Bridge.OnTestSelected(...) + State.Create(string.Format("{0}({1}) : Measuring process data", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (e.Contains(Event.Busy)); + + /// Start the test, initialize test results + Bridge.OnTestSelected(this, new TestSelectedEventArgs(test, repetitionNr, inPath, benchPath, outPath, sensPath, heatMetersPath)); + string testName = Results.Utils.GetTestName(test.Name, test.Repeats, repetitionNr); + + + if (test.DoDraining || (scale.Mass + test.Volume >= scale.Capacity * Constants.TankFullFactor)) + { + /// + /// There is not enough room in the tank OR unconditional draining ... drain the water tank + /// + IList ops = new List(readTempPressOps); + switch (DrainTheTank(scale, ops)) + { + case Event.Error: { retVal = Event.Error; goto stopTest; } + case Event.UiCmdStop: { retVal = Event.UiCmdStop; goto stopTest; } + } + } + + //------------------------------------------------ + Bridge.OnActivity(this, Strings.Starting_the_pump); + //------------------------------------------------ + + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + int flowSetTime0 = StateMachine.Time; + + IList pumpAndDrainValve = new List(); + pumpAndDrainValve.Add(inPath.Pump); + pumpAndDrainValve.Add(scale.DrainValve); + + if (inPath.Pump is GenericDevices.IPumpFM) (inPath.Pump as GenericDevices.IPumpFM).TurnOn(test.PumpPower); + /// + State.Create(string.Format("{0}({1}) : Starting the pump", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + //.AddOperation(new Operations.SetAllRegulValvesOp(inPath.RegulValves, inPath.RegulValvesPct, 60)) + .AddOperation(inPath.Pump != null ? cBrd.SetValvesOp(pumpAndDrainValve, null) : null) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + } + while (e.Contains(Event.ValvesBusy) /* || !e.Contains(Event.AllPositionsReached)*/); + + if (test.TimePump2StartV > 0) + { + State.Create(string.Format("{0}({1}) : Waiting after the pump started", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(new Operations.TimerOp(test.TimePump2StartV)) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (!e.Contains(Event.TimerExpired)); + } + + + if (benchPath.StopBFValve != null) + { + State.Create(string.Format("{0}({1}) : Opening the stop backflow valve", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(cBrd.SetValvesOp(benchPath.StopBFValve, null)) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + } + while (!e.Contains(Event.ValvesSet)); + } + + + if (test.TimeBeforeFlow > 0) + { + State.Create(string.Format("{0}({1}) : Waiting before flow setting process starts", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(new Operations.TimerOp(test.TimeBeforeFlow)) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (!e.Contains(Event.TimerExpired)); + } + + //------------------------------------------------ + Bridge.OnActivity(this, Strings.Setting_the_flow); + //------------------------------------------------ + if (inPath.Pump is GenericDevices.IPumpFM) + { + (inPath.Pump as GenericDevices.IPumpFM).TurnOn(test.PumpPower); + } + /// + State.Create(string.Format("{0}({1}) : Pump on", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(cBrd.SetValvesOp(pumpAndDrainValve, null)) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (e.Contains(Event.ValvesBusy)); + /// + State.Create(string.Format("{0}({1}) : Start valve open", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(cBrd.OpenStartValveOp()) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (e.Contains(Event.ValvesBusy)); + /// + State.Create(string.Format("{0}({1}) : Setting the flow", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(cBrd.SetFlowOp(test.Qfrom, test.Qto, RefFlow, FlowSettingTimeoutSec)) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.FlowSetting)); + + if (e.Contains(Event.OpArgumentError)) { retVal = Event.OpArgumentError; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + if (e.Contains(Event.RegulValveTimeOut)) + { + Bridge.OnError(this, Strings.Flow_adjustment_failed); + retVal = Event.RecoverableError; + goto stopTest; + } + if (e.Contains(Event.Next)) + { + break; /// Simulate flow is within range + } + } + while (!e.Contains(Event.FlowReached)); + + /// + /// Flow is withing required range at this point + /// + int flowSetTime = StateMachine.Time - flowSetTime0; + if (!string.IsNullOrEmpty(test.TempControl) && benchPath.TempMtrUp != null && benchPath.TempMtrDown != null) + { + //--------------------------------------------------- + Bridge.OnActivity(this, Strings.Setting_temperature); + //--------------------------------------------------- + + State.Create(string.Format("{0}({1}) : Wait until the water temperature is within limits", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + if (e.Contains(Event.Next)) + { + break; /// Simulate temperature is within range + } + + if (TempUp.Val >= test.TempLimLo && TempUp.Val <= test.TempLimHi && + TempDown.Val >= test.TempLimLo && TempDown.Val <= test.TempLimHi) + { + break; /// Temperature set withing required range + } + } + while (true); + } + + /// + /// Temperature is withing required range at this point + /// + + //----------------------------------------------------------------- + Bridge.OnActivity(this, Strings.Stopping_flow_for_the_fixed_start); + //----------------------------------------------------------------- + State.Create(string.Format("{0}({1}) : Stop flow regulation", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .EnterState(); + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + + IOperation testInProgress = cBrd.StandingStartStopTestOp(test, 2 * totalPulses, false); + IOperation controlFlowOp = cBrd.SetFlowOp(test.Qfrom, test.Qto, RefFlow, int.MaxValue, test.TimeBeforeFlow); + + State.Create(string.Format("{0}({1}) : Start the standing start/stop test", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .EnterState(); + for (int i = 0; i < 3; i++) + { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + + State.Create(string.Format("{0}({1}) : Close the start/stop valve before measuring the start mass", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .AddOperation(cBrd.CloseStartValveOp()) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (!e.Contains(Event.ValvesSet)); + + + int time = StateMachine.Time; + double currentFlow = RefFlow.Val; + + LogProcessDataTestInfo(processDataLogger, test.Procedure.Name, test.Name); + + if (heatMetersPath == null) LogProcessDataHeader(processDataLogger, "Start mass"); + else LogProcessDataHeaderHeatMeters(processDataLogger, "Start mass"); + + /// + /// Enter water meter begin states and read the start mass at the same time + /// + GenericDevices.IHasWMStatesForm dataEntryCmpnt = TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IHasWMStatesForm; + if (dataEntryCmpnt != null) + { + string fileName = string.Format("{0}-start.bmp", test.Name); + + if (dataEntryCmpnt is GenericDevices.IDataEntryForCamera) + { + string[] startImages = new string[sensPath.RegisterReaders.Length]; + + State state2 = State.Create(string.Format("{0}({1}) : Grab images", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .AddOperation(processDataLoggingOp); + + for (int i = 0; i < sensPath.RegisterReaders.Length; i++) + { + GenericDevices.IRegReaderStillCamera roi = sensPath.RegisterReaders[i] as GenericDevices.IRegReaderStillCamera; + if (roi != null) + { + string directory = Path.Combine(Program.TempImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + Directory.CreateDirectory(directory); + startImages[i] = Path.Combine(directory, fileName); + state2.AddOperation(roi.GrabImageOp(startImages[i])); + } + else + { + /// Camera simulation + string directory = Path.Combine(Program.TempImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + Directory.CreateDirectory(directory); + startImages[i] = Path.Combine(directory, fileName); + } + } + state2.EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.Test)); + + if (e.Contains(Event.Error)) { retVal = Event.Error; break; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; break; } + } + while (e.Contains(Event.CameraBusy)); + + (dataEntryCmpnt as GenericDevices.IDataEntryForCamera).StartImages = startImages; + } + + Bridge.OnActivity(this, Strings.Enter_water_meter_data); + State.Create(string.Format("{0}({1}) : Enter start states of water meters", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .AddOperation((dataEntryCmpnt as GenericDevices.IHasWMStatesForm).ShowTestStartFormOp(sensPath.RegisterReaders)) + .AddOperation(processDataLoggingOp) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (!e.Contains(Event.ModelessFormClosed)); + + for (int i = 0; i < waterMetersCount; i++) + { + GenericDevices.IRegReader rr = sensPath.RegisterReaders[i]; + + if (rr is Rig.RegisterReaders.StandingStartStop.RegisterReader) + (rr as Rig.RegisterReaders.StandingStartStop.RegisterReader).BeginWMState = dataEntryCmpnt.WMStartState(i); + + if (rr is Rig.Network.Camera.RoiForFixedStart.Roi) + (rr as Rig.Network.Camera.RoiForFixedStart.Roi).BeginWMState = dataEntryCmpnt.WMStartState(i); + } + + if (dataEntryCmpnt is GenericDevices.IDataEntryForCamera && (dataEntryCmpnt as GenericDevices.IDataEntryForCamera).SaveImages) + { + /// SaveImages==true ... copy images to the directory that will be archived at the end of the cycle + for (int i = 0; i < sensPath.RegisterReaders.Length; i++) + { + GenericDevices.IRegReaderStillCamera roi = sensPath.RegisterReaders[i] as GenericDevices.IRegReaderStillCamera; + if (roi != null) + { + string srcDir = Path.Combine(Program.TempImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + string destDir = Path.Combine(Program.ImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + try + { + if (File.Exists(Path.Combine(srcDir, fileName))) + { + Directory.CreateDirectory(destDir); + File.Copy(Path.Combine(srcDir, fileName), Path.Combine(destDir, fileName), true); + } + } + catch (Exception exc) + { + log.ErrorFormat("Failed to copy image {0} to {1}: {2}", fileName, destDir, exc.Message); + } + } + } + } + } + + /// + /// Make sure the drain valve is closed + /// + State.Create(string.Format("{0}({1}) : Make sure the drain valve is closed", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(cBrd.SetValvesOp(null, scale.DrainValve)) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.JustStarted)); + + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (!e.Contains(Event.ValvesSet)); + + //---------------------------------------------------- + Bridge.OnActivity(this, Strings.Measuring_the_weight); + //---------------------------------------------------- + + State.Create(string.Format("{0}({1}) : Measure the start mass", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .AddOperation(scale.ReadStableMassOp(ref StartMass, test.TimeFlow2Mass, test.MassMethod, test.MassRepeats, test.MassSpread)) + .AddOperation(processDataLoggingOp) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + if (e.Contains(Event.ScaleTimeout)) + { + Bridge.OnError(this, Strings.Mass_measurement_timeout); + retVal = Event.RecoverableError; + goto stopTest; + } + } + while (!e.Contains(Event.ScaleDone) && !e.Contains(Event.Next)); + /// + tMass1 = StateMachine.Time; + + + if (heatMetersPath == null) + LogProcessDataHeader(processDataLogger, "Measurement"); + else + LogProcessDataHeaderHeatMeters(processDataLogger, "Measurement"); + + //------------------------------------------------ + Bridge.OnActivity(this, Strings.Test_in_progress); + //------------------------------------------------ + + /// Measurement loop preparation + TestStartTime = DateTime.Now; + StartTime = (double)StateMachine.Time; + int estimtdEndTime = StateMachine.Time + (int)test.TestTime; + int remainingTime; + StartNewStatistics(outPath.FlowMeter, BatchRslts.Batch.BatchNr, test, repetitionNr, Math.Max((int)(test.TestTime / 10), 5)); + + int initialPulsesCount = RefPulses; + + State.Create(string.Format("{0}({1}) : Start the test, open the start/stop valve", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(cBrd.OpenStartValveOp(timeStampStart)) + .AddOperation(testInProgress) + .AddOperation(controlFlowOp) + .AddOperation(processDataLoggingOp) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.OpArgumentError)) { retVal = Event.OpArgumentError; goto stopTest; } + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + + /// Show remaining time + if ((remainingTime = Math.Max(estimtdEndTime - StateMachine.Time, 0)) > 60) + Bridge.OnActivity(this, string.Format("{0} ... {1} {2} {3} {4}", Strings.Test_in_progress, remainingTime / 60, "min", remainingTime % 60, Strings.sec)); + else + Bridge.OnActivity(this, string.Format("{0} ... {1} s", Strings.Test_in_progress, remainingTime)); + + /// Update statistics + RefFrequency.Val = cBrd.RefFrequency; + RefFlow.Val = outPath.FlowMeter.ReadFlow(); + UpdateAllStatistics(); + + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.Test)); + } + while (cBrd.RefPulses < initialPulsesCount + totalPulses && !e.Contains(Event.TestCompleted) && !e.Contains(Event.Next)); + + /// Measurement loop end + switchTimeStart = 0.001f * cBrd.ValveOpenCloseTime; + log.WarnFormat("Start valve switch time on test start = {0} ms", cBrd.ValveOpenCloseTime); + StopRecordingStatistics(); + EndTime = (double)StateMachine.Time; + TestEndTime = DateTime.Now; + + State.Create(string.Format("{0}({1}) : Closing the start/stop valve at the end of the standing start/stop test", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .AddOperation(cBrd.CloseStartValveOp(timeStampEnd)) + .AddOperation(processDataLoggingOp) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + } + while (e.Contains(Event.ValvesBusy)); + + switchTimeEnd = 0.001f * cBrd.ValveOpenCloseTime; + log.WarnFormat("Start valve switch time on test end = {0} ms", cBrd.ValveOpenCloseTime); + + if (heatMetersPath == null) + LogProcessDataHeader(processDataLogger, "End mass"); + else + LogProcessDataHeaderHeatMeters(processDataLogger, "End mass"); + + //---------------------------------------------------- + Bridge.OnActivity(this, Strings.Measuring_the_weight); + //---------------------------------------------------- + State.Create(string.Format("{0}({1}) : Measuring the end mass", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .AddOperation(testInProgress) + .AddOperation(inPath.Pump != null ? cBrd.SetValvesOp(null, inPath.Pump) : null) + .AddOperation(scale.ReadStableMassOp(ref EndMass, test.TimeStop2Mass, test.MassMethod, test.MassRepeats, test.MassSpread)) + .AddOperation(new Operations.TimerOp(StableMassMsrmntTimeoutSec)) + .AddOperation(processDataLoggingOp) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + if (e.Contains(Event.ScaleTimeout)) + { + Bridge.OnError(this, Strings.Mass_measurement_timeout); + retVal = Event.RecoverableError; + goto stopTest; + } + } + while (!e.Contains(Event.ScaleDone) && !e.Contains(Event.Next)); + /// + tMass2 = StateMachine.Time; + + if (test.DoDrainingAfter) + { + State.Create(string.Format("{0}({1}) : Open the drain valve", test.Method, test.Name)) + .AddOperation(checkUiOp) + .AddOperation(cBrd.SetValvesOp(scale.DrainValve, null)) + .EnterState(); + do + { + e = StateMachine.WaitRunDevsRunOps(); + if (e.Contains(Event.Error)) { retVal = Event.Error; goto stopTest; } + } + while (e.Contains(Event.ValvesBusy)); + } + + + double massStart = MeasurementCorrection.CorrectedValue(StartMass.Val, scale.Corrections); + double massEnd = MeasurementCorrection.CorrectedValue(EndMass.Val, scale.Corrections); + double densityOut = Formulas.WaterDensityFromTempPress((TempUpStat.Average + TempDownStat.Average) / 2, + (PressUpStat.Average + PressDownStat.Average) / 2); + double buoyancy = Formulas.Buoyancy(); + double massOfEvaporatedWater = (double)(tMass2 - tMass1) * outPath.Scale.EvaporationRate(TempDivStat.Average); + double volumeCTV = 1000.0 * buoyancy * (massEnd - massStart + massOfEvaporatedWater) / densityOut; + if (debugLevel == Common.DebugMode.Simulate) volumeCTV = test.Volume; + + double refEnergy = Energy.Sum * volumeCTV / VolumeForEnergy.Sum; /// [J]=[J]*[l]/[l] + + /// + /// Enter watermeter end states here + /// + if (dataEntryCmpnt != null) + { + string fileName = string.Format("{0}-end.bmp", test.Name); + + if (dataEntryCmpnt is GenericDevices.IDataEntryForCamera) + { + string[] endImages = new string[sensPath.RegisterReaders.Length]; + + State state2 = State.Create(string.Format("{0}({1}) : Grabbing images", test.Method, test.Name)) + .AddOperation(checkUiOp); + for (int i = 0; i < sensPath.RegisterReaders.Length; i++) + { + GenericDevices.IRegReaderStillCamera roi = sensPath.RegisterReaders[i] as GenericDevices.IRegReaderStillCamera; + if (roi != null) + { + string directory = Path.Combine(Program.TempImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + Directory.CreateDirectory(directory); + endImages[i] = Path.Combine(directory, fileName); + state2.AddOperation(roi.GrabImageOp(endImages[i])); + } + else + { + /// Camera simulation + string directory = Path.Combine(Program.TempImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + Directory.CreateDirectory(directory); + endImages[i] = Path.Combine(directory, fileName); + } + } + state2.EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.Test)); + + if (e.Contains(Event.Error)) { retVal = Event.Error; break; } + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; break; } + } + while (e.Contains(Event.CameraBusy)); + + (dataEntryCmpnt as GenericDevices.IDataEntryForCamera).EndImages = endImages; + } + + Bridge.OnActivity(this, Strings.Enter_water_meter_data); + State state = State.Create(string.Format("{0}({1}) : Entering the end-state", test.Method, test.Name)); + if (false && dataEntryCmpnt is GenericDevices.IHasHeatMtrStatesForm) + { + state.AddOperation((dataEntryCmpnt as GenericDevices.IHasHeatMtrStatesForm). + ShowTestEndFormOp(sensPath.RegisterReaders, + volumeCTV, test.ErrLimLo + test.Uncertainty, test.ErrLimHi - test.Uncertainty, + refEnergy, test.ErrLimLo + test.Uncertainty, test.ErrLimHi - test.Uncertainty)); + } + else + { + state.AddOperation(dataEntryCmpnt.ShowTestEndFormOp(sensPath.RegisterReaders, + volumeCTV, test.ErrLimLo + test.Uncertainty, test.ErrLimHi - test.Uncertainty)); + } + state.AddOperation(checkUiOp) + .AddOperations(readTempPressOps) + .EnterState(); + do { + e = StateMachine.WaitRunDevsRunOps(); + if (TestAndLogUiCmdStop(test, e)) { retVal = Event.UiCmdStop; goto stopTest; } + } + while (!e.Contains(Event.ModelessFormClosed)); + + for (int i = 0; i < waterMetersCount; i++) + { + GenericDevices.IRegReader rr = sensPath.RegisterReaders[i]; + + if (rr is Rig.RegisterReaders.StandingStartStop.RegisterReader) + (rr as Rig.RegisterReaders.StandingStartStop.RegisterReader).EndWMState = dataEntryCmpnt.WMEndState(i); + + if (rr is Rig.Network.Camera.RoiForFixedStart.Roi) + (rr as Rig.Network.Camera.RoiForFixedStart.Roi).EndWMState = dataEntryCmpnt.WMEndState(i); + } + + if (dataEntryCmpnt is GenericDevices.IDataEntryForCamera && (dataEntryCmpnt as GenericDevices.IDataEntryForCamera).SaveImages) + { + /// SaveImages==true ... copy images to the directory that will be archived at the end of the cycle + for (int i = 0; i < sensPath.RegisterReaders.Length; i++) + { + GenericDevices.IRegReaderStillCamera roi = sensPath.RegisterReaders[i] as GenericDevices.IRegReaderStillCamera; + if (roi != null) + { + string srcDir = Path.Combine(Program.TempImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + string destDir = Path.Combine(Program.ImagesDir, BatchRslts.Batch.BatchNr.ToString(), (i + 1).ToString()); + try + { + if (File.Exists(Path.Combine(srcDir, fileName))) + { + Directory.CreateDirectory(destDir); + File.Copy(Path.Combine(srcDir, fileName), Path.Combine(destDir, fileName), true); + } + } + catch (Exception exc) + { + log.ErrorFormat("Failed to copy image {0} to {1}: {2}", fileName, destDir, exc.Message); + } + } + } + } + } + + //------------------------------------------------ + Bridge.OnActivity(this, Strings.Test_completed); + //------------------------------------------------ + + /// Make sure the CycleBeginForm is closed so that water meter data (s/n) can be copied into results + if (UIFlowControl.Stop == WaitBeginFormClosed()) goto stopTest; + + /// + /// Populate TestResult data entity with data + /// + Results.Entities.TestRslt tstRslt = BatchRslts.GetTestRslt(testName, test.Part); + + bool stopCycle = false; + if (tstRslt != null) + { + Results.Utils.GetCounterStates(tstRslt, Program.LocalSettings.Counters); + + /// Raw data + UpdateTempPressDensAmb(tstRslt); + tstRslt.MethodClass = TbfComponents.FindComponent(test.Method).ClassName; + tstRslt.StartTime = TestStartTime; + tstRslt.EndTime = TestEndTime; + tstRslt.FlowSetTime = flowSetTime; + tstRslt.TestTime = Math.Max(1.0, DateTimeBox.DurationSec(timeStampStart, timeStampEnd)); /// Min. 1s to prevent division by zero + tstRslt.PulsesMaster = Convert.ToDouble(cBrd.RefPulses); /// Pulses of the master flow meter (test total) + tstRslt.MassStartRaw = StartMass.Val; + tstRslt.MassEndRaw = EndMass.Val; + tstRslt.TimeBtwnMassMsrmnts = tMass2 - tMass1; + + /// Corrected values + tstRslt.MassStart = massStart; + tstRslt.MassEnd = massEnd; + tstRslt.MassOfEvapWater = massOfEvaporatedWater; + + /// Main results + tstRslt.VolumeCTV = volumeCTV; /// [l] 1000.0f is because density is in [kg/m3] + tstRslt.Flow = 3.6 * volumeCTV / tstRslt.TestTime; /// [m3/h] + + if (cBrd is ControlBoard.Uni.UniCB) + { + /// Test bench with Uni control board and q reference flow meter + tstRslt.ConstMasterRaw = outPath.FlowMeter.LtrPerPulse; + tstRslt.ConstMasterCorr = outPath.FlowMeter.LtrPerPulseCorrected(tstRslt.Flow, tstRslt.TempDownMean); + tstRslt.VolumeMaster = tstRslt.ConstMasterRaw * tstRslt.PulsesMaster; /// [l] volume from the master flow meter + tstRslt.ConstMaster = (tstRslt.VolumeMaster != 0) ? (tstRslt.ConstMasterRaw * tstRslt.VolumeCTV / tstRslt.VolumeMaster) : tstRslt.ConstMasterCorr; + } + else + { + /// Test bench with Papouch control board without reference flow meter + tstRslt.VolumeMaster = volumeCTV; /// [l] volume from the master flow meter + tstRslt.ConstMasterRaw = 1; /// Uncorrected master flowmeter coefficient + tstRslt.ConstMasterCorr = 1; /// Corrected master pulses per liter + tstRslt.ConstMaster = 1; + } + tstRslt.ErrorMaster = Formulas.ErrorFromVolumes(tstRslt.VolumeMaster, tstRslt.VolumeCTV); + + tstRslt.FlowMean = (float)RefFlowStat.Average; + tstRslt.FlowStart = (float)RefFlowStat.First; + tstRslt.FlowEnd = (float)RefFlowStat.Last; + tstRslt.FlowMin = (float)RefFlowStat.Min; + tstRslt.FlowMax = (float)RefFlowStat.Max; + + tstRslt.DiverterStart = switchTimeStart; + tstRslt.DiverterEnd = switchTimeEnd; + + long infoFlags = 0; + tstRslt.ErrorFlags = (ErrorFlagsComp != null) ? ErrorFlagsComp.GetErrorFlags(tstRslt, false, true, switchTimeStart, switchTimeEnd, out infoFlags, out stopCycle) : 0; + tstRslt.InfoFlags = infoFlags; + + /// + /// Single meters + /// + for (int i = 0; i < BatchRslts.WMPositionsCount; i++) + { + if (!test.IsPartCompatible(Utils.PartNr(i + 1, BatchRslts.Batch.Compound))) continue; + + Results.Entities.MeterTestRslt meterRslt = BatchRslts.GetMeterTestRslt(testName, i, Common.CompoundMeterId.Single); + GenericDevices.IRegReader regReader = sensPath.RegisterReaders[i]; + + if (meterRslt != null && regReader != null) + { + meterRslt.RegReaderType = (int)regReader.RegisterReaderType; + meterRslt.PulsesPerLiter = regReader.PulsesPerLtr; + meterRslt.VolumeStart = regReader.BeginWMState; + meterRslt.VolumeEnd = regReader.EndWMState; + meterRslt.VolumeMeter = Math.Abs(meterRslt.VolumeEnd - meterRslt.VolumeStart); + meterRslt.VolumeRef = tstRslt.VolumeCTV; /// liter + meterRslt.PulsesMeter = meterRslt.VolumeMeter; + meterRslt.PulsesMaster = tstRslt.PulsesMaster; + meterRslt.TestTime = tstRslt.TestTime; + meterRslt.Error = Formulas.ErrorFromVolumes(meterRslt.VolumeMeter, meterRslt.VolumeRef); + meterRslt.Passed = (meterRslt.Error >= test.GetErrLimLo(tstRslt.VolumeCTV, tstRslt.TestTime) + test.Uncertainty) + && (meterRslt.Error <= test.GetErrLimHi(tstRslt.VolumeCTV, tstRslt.TestTime) - test.Uncertainty) + && (tstRslt.ErrorFlags == 0); + meterRslt.TestDone = true; + tstRslt.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, + scale != null ? scale.Name : string.Empty, + outPath.RegValve != null ? outPath.RegValve.Name : string.Empty, + outPath.Diverter != null ? outPath.Diverter.Name : string.Empty)); + + /// Update water meter error flags + if (ErrorFlagsComp != null) + { + if (BatchRslts.Batch != null && BatchRslts.Batch.WaterMeters != null) + { + for (int i = 0; i < BatchRslts.WMPositionsCount; i++) + { + if (BatchRslts.Batch.WaterMeters[i] != null && !BatchRslts.Batch.WaterMeters[i].Disabled) + { + long wmInfoFlags; + BatchRslts.Batch.WaterMeters[i].ErrorFlags = ErrorFlagsComp.GetWMtrErrorFlags(BatchRslts.Batch.WaterMeters[i].MeterTestRslts, out wmInfoFlags); + BatchRslts.Batch.WaterMeters[i].InfoFlags = wmInfoFlags; + } + } + } + } + + /// Update results + Bridge.OnTestCompleted(this, new TestCompletedEventArgs(testName, tstRslt)); + } + + Bridge.OnTestProgress(this, new TestProgressEventArgs(test, repetitionNr, Progress.TransitionAfter)); + + /// Append the results to the CSV-file + allResults.Info(TestResult2CsvLine(testName, test.Part)); + + if (stopCycle) retVal = Event.ErrorFlagsStop; + + stopTest: + + StopRecordingStatistics(); + + /// + /// Quit this sequence + /// + if (isLastRepetition || retVal == Event.UiCmdStop + || retVal == Event.OpArgumentError + || retVal == Event.RecoverableError + || retVal == Event.ErrorFlagsStop + || retVal == Event.Error + || retVal == Event.ConfigurationError) + { + cBrd.StopAll(false); + } + + return new List { retVal }; + } + } +} diff --git a/TBF/Rig/TestMethods/StandingStartMassCollection/StandingStartMassCollectionSeq.cs b/TBF/Rig/TestMethods/StandingStartMassCollection/StandingStartMassCollectionSeq.cs index f6fca5d66..81c940f92 100644 --- a/TBF/Rig/TestMethods/StandingStartMassCollection/StandingStartMassCollectionSeq.cs +++ b/TBF/Rig/TestMethods/StandingStartMassCollection/StandingStartMassCollectionSeq.cs @@ -47,6 +47,13 @@ namespace TBF.Rig.TestMethods.StandingStartMassCollection return false; } + if (!(devices.Diverter is IDiverter)) + { + /// Diverter is missing + message = string.Format("{0}: {1}", test.Name, Strings.Missing_a_diverter); + return false; + } + if (!(devices.FlowMeter is IFlowMeter)) { /// Flow meter is missing diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index de1bcb417..cc1671dc8 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -1622,6 +1622,9 @@ + + +