using System; using System.Collections.Generic; using log4net; using TBF.BenchControl.GenericDevices; using TBF.Boxes; namespace TBF.BenchControl.Elde.RegulValve { public class SetFlowOp : IOperation { private static readonly ILog log = LogManager.GetLogger(typeof(SetFlowOp)); public override string ToString() { return string.Format("SetFlowOp({0},{1},{2})", regulValve.Name, reqFlowLo, reqFlowHi); } /// Set by the constructor readonly ControlBoardDev controlBoard; readonly Elde.FlowMeter.FlowMeter flowMeter; readonly Elde.RegulValve.RegulValve regulValve; readonly int regulValveNr; readonly int flowMeterNr; readonly float reqFlowLo; readonly float reqFlowHi; readonly float reqFlowAve; readonly int timeout; readonly bool leaveMeasurementRunning; /// Process values enum OpState { Idle = 0, ValveMoveToPosition, SettingPosition, ValveMoveToFlow, SettingFlow, FlowReached, } OpState opState; float targetPositionLo; float targetPositionHi; int expireTime; FloatBox measuredFlow; /// /// 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] /// Timeout for the flow setting 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(ControlBoardDev controlBoard, IRegulValve regulValve, IFlowMeter flowMeter, float requiredFlowLo, float requiredFlowHi, FloatBox measuredFlow, int timeout, bool leaveMeasurementRunning) { if (controlBoard == null) throw new ArgumentNullException("ctrlBoard"); this.controlBoard = controlBoard; this.regulValve = regulValve as Elde.RegulValve.RegulValve; if (regulValve == null) throw new ArgumentNullException("regValve is null or not Elde"); this.regulValveNr = this.regulValve.Position; this.flowMeter = flowMeter as Elde.FlowMeter.FlowMeter; if (flowMeter == null) throw new ArgumentNullException("flowMeter in null or not Elde"); this.flowMeterNr = this.flowMeter.Position; this.reqFlowLo = requiredFlowLo; this.reqFlowHi = requiredFlowHi; this.reqFlowAve = (reqFlowLo + reqFlowHi) / 2.0f; this.measuredFlow = measuredFlow; this.timeout = timeout; this.leaveMeasurementRunning = leaveMeasurementRunning; log.Debug(this.ToString()); } /// /// Set required water flow - Do not leave the measurement running. /// Events: FlowSet /// public SetFlowOp(ControlBoardDev controlBoard, IRegulValve regValve, IFlowMeter flowMeter, float requiredFlowLo, float requiredFlowHi, FloatBox measuredFlow, int timeout) : this(controlBoard, regValve, flowMeter, requiredFlowLo, requiredFlowHi, measuredFlow, timeout, false) { } /// /// Set required water flow - No timeout. /// Events: FlowSet /// public SetFlowOp(ControlBoardDev controlBoard, IRegulValve regValve, IFlowMeter flowMeter, float requiredFlowLo, float requiredFlowHi, FloatBox measuredFlow) : this(controlBoard, regValve, flowMeter, requiredFlowLo, requiredFlowHi, measuredFlow, int.MaxValue, 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(float avgReqFlow, out float positionLo, out float positionHi) { float targetPosition; if (regulValve.Dict.TryGetValue(avgReqFlow, out targetPosition)) { positionLo = Math.Max(targetPosition * 0.95f, 0.0f); positionHi = Math.Min(targetPosition * 1.05f, 100.0f); return true; } else { positionLo = 0.0f; positionHi = 0.0f; return false; } } void StoreTargetPosition(float avgReqFlow, float actPosition) { if (!regulValve.Dict.ContainsKey(reqFlowAve)) { regulValve.Dict.Add(new KeyValuePair(avgReqFlow, actPosition)); } return; } /// Start this operation public void Start() { if (regulValve.RegulValveCfg.StoredPositionReuse && FetchTargetPosition(reqFlowAve, out targetPositionLo, out targetPositionHi)) { log.InfoFormat("Start(): rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3} FETCHED: posLo={4} posHi={5}", regulValveNr, flowMeterNr, reqFlowLo, reqFlowHi, targetPositionLo, targetPositionHi); opState = OpState.ValveMoveToPosition; } else { log.InfoFormat("Start(): rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}", regulValveNr, flowMeterNr, reqFlowLo, reqFlowHi); opState = OpState.ValveMoveToFlow; } expireTime = StateMachine.Time + timeout; if (expireTime < 0) expireTime = int.MaxValue; TestMethods tm = 0; StopDevs sd = 0; controlBoard.SendCommand(Command.Start, flowMeterNr, controlBoard.Route, int.MaxValue, tm, 0.0f, regulValve.RegulValveCfg.PidCoef, 0, sd); } /// Run this operation /// /// Event.None, Event.FlowReached, Event.RegulValveTimeOut, Event.OpArgumentError /// public Event Run() { float rvPosition = controlBoard.RValvePosition(regulValveNr); float flowMtrFreq = controlBoard.ReferenceFreq; float flow = flowMtrFreq * flowMeter.NominalFlow / 2000.0f; if (measuredFlow != null) measuredFlow.Val = flow; log.InfoFormat("Run(): rv#={0} pos={1}% freq={2} flow={3} opState={4}", regulValveNr, rvPosition.ToString("F1"), flowMtrFreq, flow, opState); if (opState == OpState.ValveMoveToPosition) { controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetPosition, new float[2] { targetPositionLo, targetPositionHi }); opState = OpState.SettingPosition; return Event.None; } else if (opState == OpState.SettingPosition) { if ((targetPositionLo <= rvPosition) && (rvPosition <= targetPositionHi)) { opState = OpState.ValveMoveToFlow; } return Event.None; } else if (opState == OpState.ValveMoveToFlow) { if (reqFlowLo >= reqFlowHi || reqFlowLo > flowMeter.NominalFlow || reqFlowHi <= 0) { return Event.OpArgumentError; } log.InfoFormat("Run(): rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}", regulValveNr, flowMeterNr, reqFlowLo, reqFlowHi); float freqLo = 2000.0f * reqFlowLo / flowMeter.NominalFlow; float freqHi = 2000.0f * reqFlowHi / flowMeter.NominalFlow; controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetFrequency, new float[2] { freqLo, freqHi }); opState = OpState.SettingFlow; return Event.None; } else if (opState == OpState.SettingFlow) { if ((reqFlowLo <= flow) && (flow <= reqFlowHi)) { if (regulValve.RegulValveCfg.StoredPositionReuse) { float storedPosition; if (regulValve.Dict.TryGetValue(reqFlowAve, out storedPosition)) { /// update the stored valve position StoreTargetPosition(reqFlowAve, (rvPosition + storedPosition) / 2.0f); log.InfoFormat("Run(): rv#={0} reqFlow={1} pos={2}% stored={3} <--- Updating a stored position", regulValveNr, reqFlowAve, rvPosition.ToString("F1"), storedPosition); } else { /// store the valve position StoreTargetPosition(reqFlowAve, rvPosition); log.InfoFormat("Run(): rv#={0} reqFlow={1} pos={2}% <--- Storing a new position", regulValveNr, reqFlowAve, rvPosition.ToString("F1")); } } opState = OpState.FlowReached; return Event.FlowReached; } else if (StateMachine.Time > expireTime) { return Event.RegulValveTimeOut; } else { return Event.None; } } else /// opState == OpState.FlowReached { return Event.FlowReached; } } /// Start this operation public void Stop() { opState = OpState.Idle; if (leaveMeasurementRunning) return; /// Stop measurement TestMethods tm = 0; StopDevs sd = StopDevs.RegValveRegulation; controlBoard.SendCommand(Command.Stop, flowMeterNr, controlBoard.Route, 50000, tm, 0.0f, 20, 0, sd); } } }