diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/ChangeRegValvePositionOp.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/ChangeRegValvePositionOp.cs
new file mode 100644
index 000000000..0a398fcde
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/ChangeRegValvePositionOp.cs
@@ -0,0 +1,70 @@
+///
+/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using log4net;
+using TBF.Rig.ControlBoard.Uni;
+using TBF.Rig.GenericDevices;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public class ChangeRegValvePositionOp : IOperation
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(ChangeRegValvePositionOp));
+ public override string ToString()
+ {
+ return string.Format("ChangeRegValvePositionOp({0},{1}s)", regV.Name, timePulseSec.ToString("F2"));
+ }
+
+ /// Set by the constructor
+ readonly UniCB uniCB;
+ readonly RegValve regV;
+ readonly int regulValveNr;
+ readonly double timePulseSec;
+
+ ///
+ /// Set required water flow.
+ /// Events: FlowSet, FlowTimeOut
+ ///
+ /// Control board device
+ /// Regulation valve component
+ /// Lower limit of the position to be achieved
+ /// Upper limit of the position to be achieved
+ /// Timeout in sec. for setting the flow
+ /// Only Elde.Valve flow are used, other flow on the lists are ignored
+ public ChangeRegValvePositionOp(UniCB uniCB, RegValve regV, double timePulseSec)
+ {
+ this.uniCB = uniCB;
+ if (this.uniCB == null) throw new ArgumentNullException("ctrlBoard");
+
+ this.regV = regV as RegValve;
+ if (this.regV == null) throw new ArgumentNullException("regValve is null or not Uni");
+ regulValveNr = this.regV.Idx1;
+
+ this.timePulseSec = timePulseSec;
+
+ log.Debug(this.ToString());
+ }
+
+ /// Start this operation
+ public void Start()
+ {
+ uniCB.RegVlvIncrMove(false, regulValveNr, timePulseSec, this.regV.RegulationMinStep);
+ }
+
+ /// Run this operation
+ ///
+ /// Event.SetFlowDone
+ ///
+ public Event Run()
+ {
+ return Event.PositionReached;
+ }
+
+ /// Stop this operation
+ public void Stop()
+ {
+ }
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/Factory.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/Factory.cs
new file mode 100644
index 000000000..52d05f2f8
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/Factory.cs
@@ -0,0 +1,25 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System.Collections.Generic;
+using TBF.Rig.Generic;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public class Factory : IComponentFactory
+ {
+ public string ClassName { get { return GetType().Namespace.Substring(12); } }
+ public override string ToString() { return ClassName; }
+
+ public IComponent DummyComponent() { return new RegValve(); }
+
+ public IComponent GetComponent(IComponentCfg cfg, IList components) { return new RegValve(cfg, components); }
+
+ public IComponentCfg DefaultConfig() { return new RegValveCfg("VR", this); }
+
+ public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
+ {
+ return ComponentCfgBase.CreateFromDbEntity(RegValveCfg.Serializer, component, this);
+ }
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/Handlers.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/Handlers.cs
new file mode 100644
index 000000000..1aa203913
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/Handlers.cs
@@ -0,0 +1,36 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using log4net;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ static class Handlers
+ {
+ static readonly ILog log = LogManager.GetLogger(typeof(Handlers));
+
+ static Handlers()
+ {
+ }
+
+ ///
+ /// Called from the state machine when a procedure is selected and UI needs to be updated.
+ ///
+ public static void OnAdcChanged(object sender, CmdResponseArgs data)
+ {
+ if (AdcChangedHandler == null)
+ return;
+
+ try
+ {
+ AdcChangedHandler(sender, data);
+ }
+ catch (Exception e)
+ {
+ log.Error("AdcChangedHandler(...) failed", e);
+ }
+ }
+ public static event EventHandler AdcChangedHandler;
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValve.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValve.cs
new file mode 100644
index 000000000..d43935b5e
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValve.cs
@@ -0,0 +1,243 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using log4net;
+using SchematicDrawing;
+using TBF.Rig.ControlBoard.Uni;
+using TBF.Boxes;
+using SharedComponents;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public class RegValve : ComponentBase, Generic.IDevice, GenericDevices.IRegValve, IDrawingItCmpntWithSetpoint
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(RegValve));
+ public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
+
+ public readonly RegValveCfg regValveCfg;
+ public IDrawingItem DrawingItem { get { return regValveCfg as IDrawingItem; } }
+
+ IDictionary dict;
+ public IDictionary Dict { get { return dict; } }
+
+ public readonly UniCB UniCB;
+
+ public ValveCategory Category { get { return regValveCfg.Category; } }
+ public int Idx1 { get { return regValveCfg.Idx1; } } /// 1 .. 8
+ public int DacValueClosed { get { return regValveCfg.AdcValueClosed; } } /// 0 .. 1023
+ public int DacValueOpen { get { return regValveCfg.AdcValueOpen; } } /// 0 .. 1023
+ public int StableTime { get { return regValveCfg.StableTime; } } /// 0=200ms, step=50ms, max. 1500ms (max.26)
+ public int FlowStableSec { get { return regValveCfg.FlowStableSec; } } /// 0 .. 60 sec
+ public bool StoredPositionReuse { get { return regValveCfg.StoredPositionReuse; } }
+ public int RegulationMinStep { get { return regValveCfg.RegulationMinStep; } } /// low regulation saturation time ...0=0, 1=50, 2=100, 3=150, 4=200, 5=250, 6=300 [ms]
+ ///
+ public bool IsCoax { get { return false; } }
+
+ public RegValveState RegValveState
+ {
+ get
+ {
+ switch (UniCB.Data.RegVStatus[Idx1 - 1])
+ {
+ default:
+ case 0: return ControlBoard.Uni.RegValveState.Idle;
+ case 2: return ControlBoard.Uni.RegValveState.PwOrFreqRegul;
+ case 3: return ControlBoard.Uni.RegValveState.DacValueRegul;
+ }
+ }
+ }
+
+ /// Position 0.0 .. 100.0 in %
+ public double Position
+ {
+ get
+ {
+ int denominator = DacValueOpen - DacValueClosed;
+ if (denominator == 0) denominator = 1;
+ return 100.0 * Convert.ToDouble(Math.Max(0, Math.Min(denominator, (int)UniCB.AnalogInput(adcChannel) - DacValueClosed)))
+ / Convert.ToDouble(denominator);
+ }
+ }
+ ///
+ public double SetpointVal
+ {
+ get { return Position; }
+ set { TargetRegValvePosition = value; }
+ }
+ ///
+ public void IncreaseSetpoint()
+ {
+ IncrementalMovement(true, 0.5);
+ }
+ ///
+ public void DecreaseSetpoint()
+ {
+ IncrementalMovement(true, -0.5);
+ }
+
+ int adcChannel;
+ public double TargetRegValvePosition;
+
+
+ ///
+ /// Incremental movement of the regulation valve.
+ ///
+ /// Time in seconds, positive value opens the reg. valve
+ public void IncrementalMovement(bool isFromUI, double time)
+ {
+ UniCB.RegVlvIncrMove(isFromUI, Idx1, time, RegulationMinStep);
+ }
+
+ ///
+ /// Move a regulation valve to specfied position
+ ///
+ /// Reg. valve ID
+ /// Reg. valve position (0 .. 1.0)
+ public void MoveToPosition(bool isFromUI, double posPctLo, double posPctHi = -1)
+ {
+ int adcDiff = Math.Abs(regValveCfg.AdcValueOpen - regValveCfg.AdcValueClosed);
+ int adcLower = Math.Min(regValveCfg.AdcValueOpen, regValveCfg.AdcValueClosed);
+
+ int targetAdcValLo = Math.Max(0, Convert.ToInt32(Math.Round((posPctLo * adcDiff / 100.0) + adcLower)));
+ int targetAdcValHi = (posPctHi < 0) ? -1 : Math.Min(1023, Convert.ToInt32(Math.Round((posPctHi * adcDiff / 100.0) + adcLower)));
+
+ UniCB.RegVlvMoveToPos(isFromUI, Idx1, targetAdcValLo, targetAdcValHi, RegulationMinStep);
+ }
+
+
+ #region Configuration Change Handling
+
+ public static void OnCfgChange(object sender, CfgChangeArgs args)
+ {
+ if (CfgChangeHandler == null) return;
+ try { CfgChangeHandler(sender, args); }
+ catch (Exception e) { log.Error("CfgChangeHandler(...) failed", e); }
+ }
+
+ public static event EventHandler CfgChangeHandler;
+
+ public override void StartChangeHandler()
+ {
+ CfgChangeHandler += delegate(object sender, CfgChangeArgs args)
+ {
+ RegValveCfg tmpcfg = args.Cfg as RegValveCfg;
+ if (tmpcfg != null && tmpcfg.Name.Equals(Name))
+ {
+ if (args.Command == CfgChangeCmd.CfgChange)
+ {
+ regValveCfg.StableTimeMs = tmpcfg.StableTimeMs;
+ regValveCfg.FlowStableSec = tmpcfg.FlowStableSec;
+ regValveCfg.RegulationMinStep = tmpcfg.RegulationMinStep;
+ }
+ else if (args.Command == CfgChangeCmd.RVOpenStep)
+ {
+ IncrementalMovement(true, 0.5); /// true = command is comming from the UI
+ }
+ else if (args.Command == CfgChangeCmd.RVCloseStep)
+ {
+ IncrementalMovement(true, -0.5); /// true = command is comming from the UI
+ }
+ else if (args.Command == CfgChangeCmd.GetAdc1 || args.Command == CfgChangeCmd.GetAdc2)
+ {
+ short adcValue = UniCB.AnalogInput(adcChannel);
+ RegValveCfgCtrl.OnCmdResponse(this, new CmdResponseArgs(args.Command, Idx1, adcValue));
+ }
+ }
+ };
+ }
+
+ #endregion Configuration Change Handling
+
+
+ public RegValve() { }
+
+ public RegValve(Generic.IComponentCfg cfg, IList components)
+ : base(cfg)
+ {
+ regValveCfg = cfg as RegValveCfg;
+
+ this.UniCB = TbfComponents.FindComponent(cfg.ParentName, components) as UniCB;
+ if (this.UniCB == null) throw new Exception(string.Format("Cannot find {0} (a parent of {1})", cfg.ParentName, Name));
+ }
+
+ public override void Initialize()
+ {
+ adcChannel = regValveCfg.Idx1 - 1;
+ this.dict = new Dictionary();
+
+ TBF.UiBridge.Bridge.SetpointChangeHandler += delegate(object sndr, TBF.UiBridge.SetpointChangeArgs args)
+ {
+ if (args.Name == Name)
+ {
+ if (args.Increase) IncreaseSetpoint(); else DecreaseSetpoint();
+ }
+ };
+
+ log.FatalFormat("{0} initialized: {1}", Name, this);
+ }
+
+ ///
+ /// IDevice interface implementation
+ ///
+ public void RunDeviceBefore()
+ {
+ Int16 adcValue = UniCB.AnalogInput(adcChannel);
+ Handlers.OnAdcChanged(this, new CmdResponseArgs(CfgChangeCmd.GetAdc, Idx1, adcValue));
+ }
+ public void RunDeviceAfter() { }
+ public void StopDevice() { }
+ public void StopDevice2() { }
+
+
+ ///
+ /// Operation to set the water flow within tolerances
+ /// Events: Event.None, Event.FlowReached, Event.FlowTimeOut
+ ///
+ /// Flowmeter component
+ /// Lower limit of the required flow [m3/h]
+ /// Higher limit of the required flow [m3/h]
+ /// PID coefficient (float)
+ /// Measured flow [m3/h]
+ /// Timeout in [s]
+ /// SetFlowOp instance
+ public IOperation SetFlowOp(GenericDevices.IFlowMeter flowMeter, double requiredFlowLo, double requiredFlowHi, DoubleBox measuredFlow, int timeout, int delay)
+ {
+ return new SetFlowOp(UniCB, this, flowMeter, requiredFlowLo, requiredFlowHi, measuredFlow, timeout, delay);
+ }
+
+ ///
+ /// Operation to set the water flow within tolerances. Leave the measurement running.
+ /// Events: Event.None, Event.FlowReached, Event.FlowTimeOut
+ ///
+ /// Flowmeter component
+ /// Lower limit of the required flow [m3/h]
+ /// Higher limit of the required flow [m3/h]
+ /// Measured flow [m3/h]
+ /// Timeout in [s]
+ /// SetFlowOp instance
+ public IOperation SetFlowAndMeasureOp(GenericDevices.IFlowMeter flowMeter, double requiredFlowLo, double requiredFlowHi,
+ DoubleBox measuredFlow, int timeout, float filterConstant)
+ {
+ return new SetFlowOp(UniCB, this, flowMeter, requiredFlowLo, requiredFlowHi, measuredFlow, timeout, 0, true);
+ }
+
+ ///
+ /// Operation to set the regulation valve to a required position
+ /// Events: Event.None
+ ///
+ /// Lower limit of the required valve position in [%]
+ /// Higher limit of the required valve position in [%]
+ /// SetPositionOp instance
+ public IOperation SetRegValvePositionOp(double pctLo, double pctHi, int timeoutMs)
+ {
+ return new SetRegValvePositionOp(UniCB, this, pctLo, pctHi, timeoutMs);
+ }
+
+ public IOperation ChangeRegValvePositionOp(double timePulseSec)
+ {
+ return new ChangeRegValvePositionOp(UniCB, this, timePulseSec);
+ }
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValveCfg.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValveCfg.cs
new file mode 100644
index 000000000..9b24c1b3f
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValveCfg.cs
@@ -0,0 +1,108 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml.Serialization;
+using Config.Entities;
+using SchematicDrawing;
+using TBF.Rig.Generic;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public class RegValveCfg : ComponentCfgBase, IChildComponentCfg, IDrawingItemWithSetpoint
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(RegValveCfg) })[0];
+ public override XmlSerializer GetSerializer() { return Serializer; }
+
+ public IComponentCfgCtrl GetControl(IList cmpntEntities)
+ {
+ return new RegValveCfgCtrl();
+ }
+
+ ///
+ /// Serialized parameters
+ ///
+ public int Idx1; /// 1..8
+ public ValveCategory Category; /// Feeding, Output or All
+ public int AdcValueClosed; /// 0..1023
+ public int AdcValueOpen; /// 0..1023
+ public int StableTimeMs; /// 200..1500 ms, step is 50 ms
+ public int FlowStableSec; /// 0..60 sec
+ public bool StoredPositionReuse; /// true = store and re-use previous regulation valve positions
+ public int RegulationMinStep { get; set; } /// low regulation saturation time ...0=0, 1=50, 2=100, 3=150, 4=200, 5=250, 6=300 [ms]
+ public string SetpFormat { get; set; }
+ public Common.Unit SetpUnit { get; set; }
+
+ /// Schematic drawing info
+ public Shape Shape { get; set; }
+ public int X { get; set; }
+ public int Y { get; set; }
+ public Sz Sz { get; set; }
+ public Orient Orient { get; set; }
+ public bool Flip { get; set; }
+ public int LblX { get; set; }
+ public int LblY { get; set; }
+ public Orient LblOrient { get; set; }
+ public int SetpX { get; set; }
+ public int SetpY { get; set; }
+ public Orient SetpOrient { get; set; }
+
+ [XmlIgnore]
+ public IList GNodes { get; set; }
+
+ ///
+ /// Stabilization time when setting the flow: 0=200ms, step 50ms, max. 1.5 sec.
+ ///
+ public int StableTime { get { return (StableTimeMs - 200) / 50; } }
+
+ /// Private parameterless constructor invoked by all other (public) constructors
+ RegValveCfg()
+ {
+ GNodes = new List();
+ }
+
+ public RegValveCfg(string name, IComponentFactory factory)
+ : this()
+ {
+ Shape = Shape.RegV;
+ Sz = Sz.M;
+ SetpFormat = "{0:F0} %";
+ SetpUnit = Common.Unit.Pct;
+
+ Name = name;
+ Factory = factory;
+ ParentName = "UniCB";
+ Category = ValveCategory.Output;
+ Idx1 = 1;
+ AdcValueClosed = 100;
+ AdcValueOpen = 500;
+ StableTimeMs = 500;
+ FlowStableSec = 5;
+ StoredPositionReuse = false;
+ RegulationMinStep = 0;
+
+ LblX = 38;
+ SetpX = 38;
+ SetpY = 22;
+ }
+
+
+
+ public string ToString(int i)
+ {
+ return string.Format("Name={0} ({1}), Idx1={2}, Cat.={3}, ADC-Closed={4}, ADC-Open={5}, StabTm={6}ms, FlowStable={7}s, PosReuse={8}",
+ Name,
+ (string.IsNullOrEmpty(ParentName) ? "-" : ParentName),
+ Idx1,
+ Category,
+ AdcValueClosed,
+ AdcValueOpen,
+ StableTimeMs,
+ FlowStableSec,
+ StoredPositionReuse,
+ RegulationMinStep);
+ }
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValveCfgCtrl.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValveCfgCtrl.cs
new file mode 100644
index 000000000..98e37457a
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/RegValveCfgCtrl.cs
@@ -0,0 +1,394 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using log4net;
+using Common;
+using Config.Entities;
+using TBF.Rig.Generic;
+using TBF.UI.Bench.Components;
+using static System.Windows.Forms.VisualStyles.VisualStyleElement;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public partial class RegValveCfgCtrl : UserControl, IComponentCfgCtrl
+ {
+ static readonly ILog log = LogManager.GetLogger(typeof(RegValveCfgCtrl));
+
+ ComponentParametersDlg parent;
+
+ public bool ShowMore { get { return true; } }
+
+ RegValveCfg config;
+ public IComponentCfg Config
+ {
+ get { return config as IComponentCfg; }
+ set
+ {
+ config = value as RegValveCfg;
+ Redraw();
+ }
+ }
+
+ public RegValveCfgCtrl()
+ {
+ InitializeComponent();
+ }
+
+ private void RegulationValveCfgCtrl_Load(object sender, EventArgs e)
+ {
+ parent = ParentForm as ComponentParametersDlg;
+ if (parent == null) return;
+
+ if (parent.CmpntEntities != null)
+ {
+ foreach (var c in parent.CmpntEntities.Where(x => x.ClassName == "ControlBoard.Uni"))
+ {
+ parentNameComboBox.Items.Add(c.Name);
+ }
+ }
+
+ categoryComboBox.Items.Add(ValveCategory.Feeding.ToString());
+ categoryComboBox.Items.Add(ValveCategory.Output.ToString());
+
+ if (config != null)
+ {
+ adc1 = config.AdcValueClosed;
+ pct1 = 0;
+ adc2 = config.AdcValueOpen;
+ pct2 = 100;
+ }
+
+ for (Common.Unit units = 0; units < global::Common.Unit.Count; units++)
+ {
+ if (global::Common.Units.IsQuantity(units, Quantity.Error))
+ {
+ unitsComboBox.Items.Add(units.ToString());
+ }
+ }
+
+ regulMinStepsComboBox.Items.Add("0");
+ regulMinStepsComboBox.Items.Add("50");
+ regulMinStepsComboBox.Items.Add("100");
+ regulMinStepsComboBox.Items.Add("150");
+ regulMinStepsComboBox.Items.Add("200");
+ regulMinStepsComboBox.Items.Add("250");
+ regulMinStepsComboBox.Items.Add("300");
+
+ Redraw();
+ StartResponseHandler();
+ }
+
+ public void Closing()
+ {
+ StopResponseHandler();
+ }
+
+ void Redraw()
+ {
+ if (config == null) return; /// Control was not loaded, settings were not changed
+
+ classNameLabel.Text = config.Factory.ClassName;
+ nameTextBox.Text = config.Name;
+ parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName;
+ categoryComboBox.Text = config.Category.ToString();
+ positionTextBox.Text = config.Idx1.ToString();
+ adcClosedTextBox.Text = config.AdcValueClosed.ToString();
+ adcOpenTextBox.Text = config.AdcValueOpen.ToString();
+ stableTimeTextBox.Text = config.StableTimeMs.ToString();
+ flowStableSecTextBox.Text = config.FlowStableSec.ToString();
+ storedPosReuseCheckBox.Checked = config.StoredPositionReuse;
+ formatTextBox.Text = config.SetpFormat;
+ unitsComboBox.Text = config.SetpUnit.ToString();
+ regulMinStepsComboBox.Text = (config.RegulationMinStep * 50).ToString();
+
+ adcTextBox1.Text = adc1.ToString();
+ adcTextBox2.Text = adc2.ToString();
+ pctTextBox1.Text = pct1.ToString();
+ pctTextBox2.Text = pct2.ToString();
+ }
+
+ public void Unlock()
+ {
+ nameTextBox.Enabled = true;
+ parentNameComboBox.Enabled = true;
+ categoryComboBox.Enabled = true;
+ positionTextBox.Enabled = true;
+ adcClosedTextBox.Enabled = true;
+ adcOpenTextBox.Enabled = true;
+ stableTimeTextBox.Enabled = true;
+ flowStableSecTextBox.Enabled = true;
+ storedPosReuseCheckBox.Enabled = true;
+ formatTextBox.Enabled = true;
+ unitsComboBox.Enabled = true;
+ regulMinStepsComboBox.Enabled = true;
+ }
+
+ public CfgUpdateFlags VerifyCfg(ref string message)
+ {
+ CfgUpdateFlags flags = CfgUpdateFlags.None;
+
+ int dummy;
+ if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text))
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "Invalid 'Parent Name'";
+ }
+
+ if (!categoryComboBox.Text.Equals(ValveCategory.Feeding.ToString()) &&
+ !categoryComboBox.Text.Equals(ValveCategory.Output.ToString()))
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "Invalid 'Category'";
+ }
+
+ if (!int.TryParse(positionTextBox.Text, out dummy) || dummy < 1 || dummy > 8)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'Position' should be between 1 and 8";
+ }
+
+ if (!int.TryParse(adcClosedTextBox.Text, out dummy) || dummy < 0 || dummy > 1023)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'ADC when Closed' should be between 0 and 1023";
+ }
+
+ if (!int.TryParse(adcOpenTextBox.Text, out dummy) || dummy < 0 || dummy > 1023)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'ADC when Open' should be between 0 and 1023";
+ }
+
+ if (!int.TryParse(stableTimeTextBox.Text, out dummy) || dummy < 200 || dummy > 1500 || (dummy % 50) != 0)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'Stable time' should be between 200 and 1500 ms in 50 ms steps";
+ }
+
+ if (!int.TryParse(flowStableSecTextBox.Text, out dummy) || dummy < 0 || dummy > 60)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'Flow stable' should be between 0 and 60 sec";
+ }
+
+ if (!unitsComboBox.Items.Contains(unitsComboBox.Text))
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "Invalid 'Units'";
+ }
+
+ return flags;
+ }
+
+ public CfgUpdateFlags UpdateCfg()
+ {
+ CfgUpdateFlags flags = CfgUpdateFlags.None;
+
+ if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
+ ///
+ if (!config.Name.Equals(nameTextBox.Text))
+ {
+ config.Name = nameTextBox.Text;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ string parentname = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text;
+ if (!config.ParentName.Equals(parentname))
+ {
+ config.ParentName = parentname;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ ValveCategory newCategory;
+ if (categoryComboBox.Text.Equals(ValveCategory.Feeding.ToString())) newCategory = ValveCategory.Feeding;
+ else if (categoryComboBox.Text.Equals(ValveCategory.Output.ToString())) newCategory = ValveCategory.Output;
+ else newCategory = ValveCategory.None;
+ if (newCategory != config.Category)
+ {
+ config.Category = newCategory;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ int tmp = int.Parse(positionTextBox.Text);
+ if (config.Idx1 != tmp)
+ {
+ config.Idx1 = tmp;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ tmp = int.Parse(adcClosedTextBox.Text);
+ if (config.AdcValueClosed != tmp)
+ {
+ config.AdcValueClosed = tmp;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ tmp = int.Parse(adcOpenTextBox.Text);
+ if (config.AdcValueOpen != tmp)
+ {
+ config.AdcValueOpen = tmp;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ tmp = int.Parse(stableTimeTextBox.Text);
+ if (config.StableTimeMs != tmp)
+ {
+ config.StableTimeMs = tmp;
+ flags |= CfgUpdateFlags.AnyChange;
+ }
+
+ tmp = int.Parse(flowStableSecTextBox.Text);
+ if (config.FlowStableSec != tmp)
+ {
+ config.FlowStableSec = tmp;
+ flags |= CfgUpdateFlags.AnyChange;
+ }
+
+ tmp = regulMinStepsComboBox.SelectedIndex;
+ if (config.RegulationMinStep != tmp)
+ {
+ config.RegulationMinStep = tmp;
+ flags |= CfgUpdateFlags.AnyChange;
+ }
+
+ bool btmp = storedPosReuseCheckBox.Checked;
+ if (config.StoredPositionReuse != btmp)
+ {
+ config.StoredPositionReuse = btmp;
+ flags |= CfgUpdateFlags.RestartRqrd;
+ }
+
+ if ((flags & CfgUpdateFlags.AnyChange) != 0)
+ {
+ RegValve.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.CfgChange, config));
+ }
+
+ config.SetpFormat = formatTextBox.Text;
+ config.SetpUnit = global::Common.Unit.None;
+ for (Common.Unit units = 0; units < global::Common.Unit.Count; units++)
+ {
+ if (unitsComboBox.Text == units.ToString())
+ {
+ config.SetpUnit = units;
+ break;
+ }
+ }
+
+ return flags;
+ }
+
+ int adc1;
+ int pct1;
+
+ int adc2;
+ int pct2;
+
+ private void openButton_Click(object sender, EventArgs e)
+ {
+ RegValve.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.RVOpenStep, config));
+ }
+
+ private void closeButton_Click(object sender, EventArgs e)
+ {
+ RegValve.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.RVCloseStep, config));
+ }
+
+ //
+ private void getButton1_Click(object sender, EventArgs e)
+ {
+ RegValve.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.GetAdc1, config));
+ }
+
+ private void getButton2_Click(object sender, EventArgs e)
+ {
+ RegValve.OnCfgChange(this, new CfgChangeArgs(CfgChangeCmd.GetAdc2, config));
+ }
+
+ private void setButton_Click(object sender, EventArgs e)
+ {
+ int tmpPct1;
+ int tmpPct2;
+ if ((int.TryParse(pctTextBox1.Text, out tmpPct1) && (tmpPct1 >= 0) && (tmpPct1 <= 100)) &&
+ (int.TryParse(pctTextBox2.Text, out tmpPct2) && (tmpPct2 >= 0) && (tmpPct2 <= 100)))
+ {
+ pct1 = tmpPct1;
+ pct2 = tmpPct2;
+ UpdateValveOpenClose();
+ }
+ }
+
+ void UpdateValveOpenClose()
+ {
+ if ((adc1 == adc2) || (pct1 == pct2)) return;
+
+ double factor = (float)(adc2 - adc1) / (float)(pct2 - pct1);
+ int adcClosed = (int)((float)(0 - pct1) * factor + (float)adc1 + 0.5f);
+ int adcOpen = (int)((float)(100 - pct1) * factor + (float)adc1 + 0.5f);
+
+ if ((adcClosed >= 0) && (adcOpen >= 0))
+ {
+ adcClosedTextBox.Text = adcClosed.ToString();
+ adcOpenTextBox.Text = adcOpen.ToString();
+ }
+ }
+
+ private void regulMinStepsComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (regulMinStepsComboBox.SelectedItem != null)
+ {
+ //config.RegulationMinStep = regulMinStepsComboBox.SelectedIndex;
+ }
+ }
+ #region Configuration Change Handling
+
+ public static void OnCmdResponse(object sender, CmdResponseArgs args)
+ {
+ if (CmdResponseHandler == null) return;
+ try { CmdResponseHandler(sender, args); }
+ catch (Exception e) { log.Error("CmdResponseHandler(...) failed", e); }
+ }
+
+ public static event EventHandler CmdResponseHandler;
+
+ public void StartResponseHandler()
+ {
+ Handlers.AdcChangedHandler += delegate(object sndr, CmdResponseArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnAdcChanged), sndr, args); }
+ else OnAdcChanged(sndr, args);
+ };
+
+ CmdResponseHandler += delegate(object sender, CmdResponseArgs args)
+ {
+ if (args.Command == CfgChangeCmd.GetAdc1)
+ {
+ adc1 = args.Response;
+ adcTextBox1.Text = adc1.ToString();
+ }
+ else if (args.Command == CfgChangeCmd.GetAdc2)
+ {
+ adc2 = args.Response;
+ adcTextBox2.Text = adc2.ToString();
+ }
+ };
+ }
+
+ public void StopResponseHandler()
+ {
+ CmdResponseHandler = null;
+ }
+
+ void OnAdcChanged(object sender, CmdResponseArgs data)
+ {
+ if (data.Command == CfgChangeCmd.GetAdc && data.Id == config.Idx1)
+ {
+ adcTextBox.Text = data.Response.ToString();
+ }
+ }
+
+ #endregion Configuration Change Handling
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/SetFlowOp.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/SetFlowOp.cs
new file mode 100644
index 000000000..8eb540601
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/SetFlowOp.cs
@@ -0,0 +1,340 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using Common;
+using log4net;
+using Config.Entities;
+using TBF.Rig.ControlBoard.Uni;
+using TBF.Rig.GenericDevices;
+using TBF.Boxes;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public class SetFlowOp : IOperation
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(SetFlowOp));
+ public override string ToString()
+ {
+ return string.Format("SetFlowOp({0}, Qfrom={1}, Qto={2})", regV.Name, shrinkedTgtFlowLo, shrinkedTgtFlowHi);
+ }
+
+ public const double RqrdFlowRangeRatio = 0.5;
+
+ ///
+ /// Set by the constructor
+ ///
+ readonly UniCB uniCB;
+ readonly RegValve regV;
+ readonly IFlowMeter flowMeter;
+ readonly double shrinkedTgtFlowLo;
+ readonly double shrinkedTgtFlowHi;
+ readonly double targetFlowAve;
+ readonly int timeout;
+ readonly int delay;
+ readonly bool leaveFlowControlRunning;
+
+ readonly double maximalFlow;
+
+ ///
+ /// Internal state of this operation
+ ///
+ enum OpState
+ {
+ Idle = 0,
+ ValveMoveToPosition1, /// Wait 1 takt
+ ValveMoveToPosition2, /// This is actual move to position
+ ValveMoveToPosition3, /// Wait 1 takt
+ SettingPosition,
+ Wait4FlowMsrmntAndSendRVMove, /// Wait until the flow measurement starts, then issue reg. valve move command
+ SettingFlow, /// Measure the flow and verify whether it is within limits sufficiently long time
+ FlowReached,
+ SendCommandAgain,
+ }
+ OpState opState;
+
+ double currentReqFlowLo;
+ double currentReqFlowHi;
+ double targetPositionLo;
+ double targetPositionHi;
+
+ int setFlowTime;
+ int expireTime;
+ DoubleBox msrdFlow;
+ int isFlowOkDuration;
+
+
+ ///
+ /// Set required water flow. Conditionally leave the measurement running.
+ /// Events: FlowSet, FlowTimeOut
+ ///
+ /// Control board device
+ /// Regulation valve component
+ /// Flowmeter component
+ /// Lower limit of the flow to be achieved in [m3/h]
+ /// Upper limit of the flow to be achieved in [m3/h]
+ /// DoubleBox for measured flow
+ /// Timeout for the flow setting in [s]
+ /// Flow setting starts after this delay in [s]
+ /// true = Leave the measurement running after op. stop
+ /// Only Elde.Valve flow are used, other flow on the lists are ignored
+ public SetFlowOp(UniCB uniCB, IRegValve regValve, IFlowMeter flowMeter, double qFrom, double qTo, DoubleBox msrdFlow,
+ int timeout, int delay, bool leaveFlowControlRunning)
+ {
+ this.uniCB = uniCB;
+ if (this.uniCB == null) throw new ArgumentNullException("Control board is null or not Uni");
+
+ this.regV = regValve as RegValve;
+ if (this.regV == null) throw new ArgumentNullException(string.Format("{0} is not Uni.RegValve", regValve.Name));
+
+ this.flowMeter = flowMeter;
+ if (this.flowMeter == null) throw new ArgumentNullException("flowMeter");
+
+ maximalFlow = flowMeter.NominalFlow * 1.25;
+
+ double rqrdFlowLoBeforeCorr = qFrom - MeasurementCorrection.GetCorrection(qFrom, flowMeter.Corrections);
+ double rqrdFlowHiBeforeCorr = qTo - MeasurementCorrection.GetCorrection(qTo, flowMeter.Corrections);
+ targetFlowAve = (rqrdFlowLoBeforeCorr + rqrdFlowHiBeforeCorr) / 2;
+ shrinkedTgtFlowLo = (RqrdFlowRangeRatio * rqrdFlowLoBeforeCorr)
+ + ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the lower limit 15% of the range up
+ shrinkedTgtFlowHi = (RqrdFlowRangeRatio * rqrdFlowHiBeforeCorr)
+ + ((1 - RqrdFlowRangeRatio) * targetFlowAve); /// Move the upper limit 15% of the range down
+
+ this.msrdFlow = msrdFlow;
+ this.timeout = timeout;
+ this.delay = delay;
+ this.leaveFlowControlRunning = leaveFlowControlRunning;
+
+ log.Debug(this.ToString());
+ }
+
+ ///
+ /// Set required water flow - Do not leave the measurement running.
+ /// Events: FlowSet
+ ///
+ public SetFlowOp(UniCB uniCB, IRegValve regValve, IFlowMeter flowMeter, double qFrom, double qTo, DoubleBox flowbox, int timeout, int delay)
+ : this(uniCB, regValve, flowMeter, qFrom, qTo, flowbox, timeout, delay, false)
+ {
+ }
+
+ ///
+ /// Set required water flow - Do not leave the measurement running.
+ /// Events: FlowSet
+ ///
+ public SetFlowOp(UniCB uniCB, IRegValve regValve, IFlowMeter flowMeter, double qFrom, double qTo, DoubleBox flowbox, int timeout)
+ : this(uniCB, regValve, flowMeter, qFrom, qTo, flowbox, timeout, 0, false)
+ {
+ }
+
+ ///
+ /// Set required water flow - No timeout.
+ /// Events: FlowSet
+ ///
+ public SetFlowOp(UniCB uniCB, IRegValve regValve, IFlowMeter flowMeter, double qFrom, double qTo, DoubleBox flowbox)
+ : this(uniCB, regValve, flowMeter, qFrom, qTo, flowbox, int.MaxValue, 0, false)
+ {
+ }
+
+ ///
+ /// Fetch target position limits from the dictionary or return false
+ ///
+ /// Average of the flow targer range (input)
+ /// Target valve position low limit (output)
+ /// Target valve position high limit (output)
+ /// true when positions for the target flow are stored in the memory, otherwise return false
+ bool FetchTargetPosition(double avgReqFlow, out double positionLo, out double positionHi)
+ {
+ double targetPosition;
+ if (regV.Dict.TryGetValue(avgReqFlow, out targetPosition))
+ {
+ positionLo = Math.Max(targetPosition * 0.95, 0.0);
+ positionHi = Math.Min(targetPosition * 1.05, 100.0);
+ return true;
+ }
+ else
+ {
+ positionLo = 0;
+ positionHi = 0;
+ return false;
+ }
+ }
+
+ void StoreTargetPosition(double avgReqFlow, double actPosition)
+ {
+ if (!regV.Dict.ContainsKey(targetFlowAve))
+ {
+ regV.Dict.Add(new KeyValuePair(avgReqFlow, actPosition));
+ }
+ return;
+ }
+
+ /// Start this operation
+ public void Start()
+ {
+ log.InfoFormat("SetFlowOp:Start() rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}",
+ regV.Idx1, flowMeter.Idx1, currentReqFlowLo, currentReqFlowHi);
+
+ /// Store the current time, etc.
+ setFlowTime = StateMachine.Time + delay;
+ expireTime = StateMachine.Time + timeout;
+ if (expireTime < 0) expireTime = int.MaxValue;
+
+ isFlowOkDuration = 0;
+
+ /// Start the flow measurement
+ uniCB.MeasureFlow(false, flowMeter.Idx1);
+
+ opState = OpState.Wait4FlowMsrmntAndSendRVMove;
+ }
+
+ ///
+ /// Run this operation
+ ///
+ ///
+ /// Event.OpArgumentError
+ /// Event.Starting
+ /// Event.Busy
+ /// Event.FlowReached
+ /// Event.RegulValveTimeOut
+ ///
+ public Event Run()
+ {
+ log.DebugFormat("Op.Run() opState={0}", opState);
+
+ if (StateMachine.Time > expireTime) return Event.RegulValveTimeOut;
+
+ if (opState == OpState.Wait4FlowMsrmntAndSendRVMove)
+ {
+ if (flowMeter.DebugLevel == DebugMode.Simulate) return Event.FlowReached;
+
+ if (!flowMeter.MsrmntAvailable) return Event.Starting;
+
+ double flow1 = flowMeter.ReadFlow();
+
+ /// Set the targer flow/frequency range
+ currentReqFlowLo = shrinkedTgtFlowLo; /// Default lower limit
+ currentReqFlowHi = shrinkedTgtFlowHi; /// Default upper limit
+ //if (flow1 >= targetFlowAve) currentReqFlowHi = targetFlowAve; /// Decrease upper limit
+ //if (flow1 <= targetFlowAve) currentReqFlowLo = targetFlowAve; /// Increase lower limit
+
+ if ((currentReqFlowLo >= currentReqFlowHi) || (currentReqFlowLo > maximalFlow) || (currentReqFlowHi <= 0))
+ {
+ return Event.OpArgumentError;
+ }
+
+ if (StateMachine.Time > setFlowTime)
+ {
+ uniCB.SetFlow(false, regV.Idx1, flowMeter.NominalFreq * currentReqFlowLo / flowMeter.NominalFlow,
+ flowMeter.NominalFreq * currentReqFlowHi / flowMeter.NominalFlow);
+
+ log.InfoFormat("Run(): rv#={0} TARGET: flowLo={1} flowHi={2}", regV.Idx1, currentReqFlowLo, currentReqFlowHi);
+
+ opState = OpState.SettingFlow;
+ }
+
+ return Event.Starting;
+ }
+
+ if (opState == OpState.ValveMoveToPosition1)
+ {
+ opState = OpState.ValveMoveToPosition2;
+ return Event.Starting;
+ }
+ else if (opState == OpState.ValveMoveToPosition2) /// Move to position
+ {
+ /// Issues the appropriate ValveMove(...) command
+ regV.MoveToPosition(false, targetPositionLo, targetPositionHi);
+
+ opState = OpState.SettingPosition;
+ return Event.Starting;
+ }
+ else if (opState == OpState.ValveMoveToPosition3)
+ {
+ opState = OpState.SettingPosition;
+ return Event.Starting;
+ }
+ else if (opState == OpState.SettingPosition)
+ {
+ /// Repeated untill position is reached
+ double rvPosition = regV.Position;
+ if ((targetPositionLo <= rvPosition) && (rvPosition <= targetPositionHi))
+ {
+ opState = OpState.Wait4FlowMsrmntAndSendRVMove;
+ }
+ return Event.Starting;
+ }
+ else if (opState == OpState.SettingFlow)
+ {
+ /// Regulation valve is setting flow to the required value
+ ///
+ double frequency = flowMeter.ReadFrequency();
+ double flow = flowMeter.ReadFlow();
+ if (msrdFlow != null && flow != 0) msrdFlow.Val = flow;
+
+ if (currentReqFlowLo <= flow && flow <= currentReqFlowHi)
+ {
+ isFlowOkDuration++; /// Increment the flow within range duration
+
+ if (isFlowOkDuration >= regV.FlowStableSec)
+ {
+ /// Flow is within range for sufficiently long time
+ if (regV.StoredPositionReuse)
+ {
+ double rvPosition = regV.Position; /// Read the current position
+ double storedPosition;
+ if (regV.Dict.TryGetValue(targetFlowAve, out storedPosition))
+ {
+ /// update the stored valve position
+ StoreTargetPosition(targetFlowAve, (rvPosition + storedPosition) / 2.0);
+ log.InfoFormat("Run(): rv#={0} reqFlow={1} pos={2}% stored={3} <--- Updating a stored position",
+ regV.Idx1, targetFlowAve, rvPosition.ToString("F1"), storedPosition);
+ }
+ else
+ {
+ /// store the valve position
+ StoreTargetPosition(targetFlowAve, rvPosition);
+ log.InfoFormat("Run(): rv#={0} reqFlow={1} pos={2}% <--- Storing a new position",
+ regV.Idx1, targetFlowAve, rvPosition.ToString("F1"));
+ }
+ }
+
+ log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2}, REACHED)", flow, currentReqFlowLo, currentReqFlowHi);
+
+ opState = OpState.FlowReached;
+ return Event.FlowReached;
+ }
+ else
+ {
+ log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2}, FLOW_OK_TIMER={3}s)", flow, currentReqFlowLo, currentReqFlowHi, isFlowOkDuration);
+ return Event.Busy;
+ }
+ }
+ else
+ {
+ /// Flow is out of range
+ isFlowOkDuration = 0;
+
+ log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2})", flow, currentReqFlowLo, currentReqFlowHi);
+ return Event.Busy;
+ }
+ }
+ else /// opState == OpState.FlowReached
+ {
+ log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2}, REACHED)", flowMeter.ReadFlow(), currentReqFlowLo, currentReqFlowHi);
+ return Event.FlowReached;
+ }
+ }
+
+ /// Stop this operation
+ public void Stop()
+ {
+ if (!leaveFlowControlRunning)
+ {
+ uniCB.StopFlowControl(false, regV.Idx1); /// Stop the flow measurement
+ }
+
+ opState = OpState.Idle;
+ }
+ }
+}
diff --git a/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/SetRegValvePositionOp.cs b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/SetRegValvePositionOp.cs
new file mode 100644
index 000000000..071a9c163
--- /dev/null
+++ b/TBF/Rig/Uni/RegValveLowRegulTimeSaturation/SetRegValvePositionOp.cs
@@ -0,0 +1,155 @@
+///
+/// Copyright (c) 2021-2022 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using log4net;
+using TBF.Rig.ControlBoard.Uni;
+using TBF.Rig.GenericDevices;
+
+namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation
+{
+ public class SetRegValvePositionOp : IOperation
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(SetRegValvePositionOp));
+ public override string ToString()
+ {
+ return string.Format("SetRegValvePositionOp({0},{1},{2})", regV.Name, posLoPct, posHiPct);
+ }
+
+ ///
+ /// Internal states of this operation
+ ///
+ enum OpState
+ {
+ Idle = 0,
+ MoveToPosition, /// Send commend to move to position
+ CheckState, /// Check RV state by reading statos word
+ MovingToPosition, /// Command passed OK, RV is moving to a position
+ }
+ OpState opState;
+
+ /// Set by the constructor
+ readonly UniCB uniCB;
+ readonly RegValve regV;
+ readonly int regValveNr;
+ readonly double posLoPct;
+ readonly double posHiPct;
+ readonly int timeout;
+
+ /// Process values
+ int expireTime;
+ bool positionReached;
+
+ ///
+ /// Set required water flow.
+ /// Events: FlowSet, FlowTimeOut
+ ///
+ /// Control board device
+ /// Regulation valve component
+ /// Lower limit of the position to be achieved
+ /// Upper limit of the position to be achieved
+ /// Timeout in sec. for setting the flow
+ /// Only Elde.Valve flow are used, other flow on the lists are ignored
+ public SetRegValvePositionOp(UniCB uniCB, RegValve regV, double posLoPct, double posHiPct, int timeout)
+ {
+ this.uniCB = uniCB;
+ if (this.uniCB == null) throw new ArgumentNullException("ctrlBoard");
+
+ this.regV = regV as RegValve;
+ if (this.regV == null) throw new ArgumentNullException("regValve is null or not Uni");
+ this.regValveNr = this.regV.Idx1;
+
+ this.posLoPct = posLoPct;
+ this.posHiPct = posHiPct;
+ this.timeout = timeout;
+
+ log.Debug(this.ToString());
+ }
+
+ ///
+ /// Set required water flow - no timeout.
+ /// Events: FlowSet
+ ///
+ public SetRegValvePositionOp(UniCB cBoard, RegValve regValve, float posLoPct, float posHiPct)
+ : this(cBoard, regValve, posLoPct, posHiPct, int.MaxValue)
+ {
+ }
+
+ /// Start this operation
+ public void Start()
+ {
+ positionReached = false;
+ expireTime = StateMachine.Time + timeout;
+ log.InfoFormat("Start(): {0} reqPosLo={1}%, reqPosHi={2}%", regV.Name, posLoPct.ToString("F1"), posHiPct.ToString("F1"));
+ opState = OpState.MoveToPosition;
+ }
+
+ ///
+ /// Run this operation
+ ///
+ ///
+ /// Event.None . . . . . . . busy adjusting position
+ /// Event.PositionReached . . position reached
+ /// Event.OpArgumentError . . invalid required position
+ ///
+ public Event Run()
+ {
+ if (opState == OpState.MoveToPosition)
+ {
+ if (posLoPct >= posHiPct || posLoPct > 100.0 || posHiPct < 0)
+ {
+ return Event.OpArgumentError;
+ }
+
+ regV.MoveToPosition(false, posLoPct, posHiPct);
+ opState = OpState.CheckState;
+ log.WarnFormat("Run(): RV#={0}, state={1}, MoveToPosition(false, {2:D1}, {3:D1}) issued", regValveNr, opState, posLoPct, posHiPct);
+ return Event.None;
+ }
+
+ if (positionReached) return Event.PositionReached;
+
+ double positionPct = regV.Position;
+ log.WarnFormat("Run(): RV#={0}, state={1}, pos={2}%", regValveNr, opState, positionPct.ToString("F1"));
+
+ if (opState == OpState.CheckState)
+ {
+ if (posLoPct <= positionPct && positionPct <= posHiPct)
+ {
+ positionReached = true;
+ return Event.PositionReached;
+ }
+ else if (regV.RegValveState != RegValveState.DacValueRegul)
+ {
+ opState = OpState.MoveToPosition; /// Resend move command again in the next run
+ return Event.None;
+ }
+ else
+ {
+ opState = OpState.MovingToPosition;
+ return Event.None;
+ }
+ }
+ else if (StateMachine.Time > expireTime)
+ {
+ return Event.RegulValveTimeOut;
+ }
+ else if (opState == OpState.MovingToPosition)
+ {
+ if (posLoPct <= positionPct && positionPct <= posHiPct)
+ {
+ positionReached = true;
+ return Event.PositionReached;
+ }
+ return Event.None;
+ }
+ return Event.None;
+ }
+
+ /// Stop this operation
+ public void Stop()
+ {
+ }
+ }
+}