/// /// Copyright (c) 2013-2015 Sensus Metering Systems /// using System; using System.Collections.Generic; using log4net; using TBF.BenchControl.GenericDevices; using TBF.Boxes; namespace TBF.BenchControl.Elde.RegulValveTandem { 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}, PID={3})", regulValve.Name, reqFlowLo, reqFlowHi, pidCoef); } /// Set by the constructor readonly ControlBoardDev controlBoard; readonly Elde.RegulValve.RegulValve regulValve; readonly int regulValveNr; readonly Elde.RegulValve.RegulValve fixedRV; readonly int fixedRVNr; readonly float fixedPctLo; readonly float fixedPctHi; readonly int flowMeterNr; readonly float reqFlowLo; readonly float reqFlowHi; readonly float reqFlowAve; readonly float pidCoef; readonly int timeout; readonly bool leaveMeasurementRunning; readonly float nominalFlow; /// Process values enum OpState { Idle = 0, IssueSetPositionCommand, SettingPosition, IssueSetPositionCommand_FixedRV, SettingPosition_FixedRV, IssueSetFlowCommand, SettingFlow, FlowReached, } OpState opState; float targetPositionLo; float targetPositionHi; int expireTime; FloatBox measuredFlow; int flowWithinBoundsTime; /// /// Set required water flow. Conditionally leave the measurement running. /// Events: FlowSet, FlowTimeOut /// /// Control board device /// Regulation valve component /// Regulation valve set to a fixed position /// Lower limit of the fixed position of the fixed reg.v. /// Upper limit of the fixed position of the fixed reg.v. /// Flowmeter component /// Lower limit of the flow to be achieved in [m3/h] /// Upper limit of the flow to be achieved in [m3/h] /// PID coefficient (float) /// 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 regV, IRegulValve fxdRV, float fxdPctLo, float fxdPctHi, IFlowMeter flowMeter, float requiredFlowLo, float requiredFlowHi, float pidCoef, FloatBox measuredFlow, int timeout, bool leaveMeasurementRunning) { if (controlBoard == null) throw new ArgumentNullException("ctrlBoard"); this.controlBoard = controlBoard; regulValve = regV as Elde.RegulValve.RegulValve; if (regulValve == null) throw new ArgumentNullException("regValve is null or not Elde"); regulValveNr = regulValve.Idx1; fixedRV = fxdRV as Elde.RegulValve.RegulValve; if (fixedRV == null) throw new ArgumentNullException("regValve is null or not Elde"); fixedRVNr = fixedRV.Idx1; this.fixedPctLo = fxdPctLo; this.fixedPctHi = fxdPctHi; if (flowMeter is Elde.FlowMeter.FlowMeter) { this.flowMeterNr = (flowMeter as Elde.FlowMeter.FlowMeter).Idx1; } else if (flowMeter is Elde.FlowMeterTwins.FlowMeter) { this.flowMeterNr = 0; } else { throw new ArgumentNullException("flowMeter is null or not Elde"); } nominalFlow = flowMeter.NominalFlow; this.reqFlowLo = requiredFlowLo; this.reqFlowHi = requiredFlowHi; this.reqFlowAve = (reqFlowLo + reqFlowHi) / 2.0f; this.pidCoef = pidCoef; 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, IRegulValve fixedRV, float fixedPctLo, float fixedPctHi, IFlowMeter flowMeter, float requiredFlowLo, float requiredFlowHi, float pidCoef, FloatBox measuredFlow, int timeout) : this(controlBoard, regValve, fixedRV, fixedPctLo, fixedPctHi, flowMeter, requiredFlowLo, requiredFlowHi, pidCoef, measuredFlow, timeout, false) { } /// /// Set required water flow - No timeout. /// Events: FlowSet /// public SetFlowOp(ControlBoardDev controlBoard, IRegulValve regValve, IRegulValve fixedRV, float fixedPctLo, float fixedPctHi, IFlowMeter flowMeter, float requiredFlowLo, float requiredFlowHi, float pidCoef, FloatBox measuredFlow) : this(controlBoard, regValve, fixedRV, fixedPctLo, fixedPctHi, flowMeter, requiredFlowLo, pidCoef, 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() { flowWithinBoundsTime = 0; 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.IssueSetPositionCommand; } else { log.InfoFormat("Start(): rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}", regulValveNr, flowMeterNr, reqFlowLo, reqFlowHi); opState = OpState.IssueSetPositionCommand_FixedRV; } expireTime = StateMachine.Time + timeout; if (expireTime < 0) expireTime = int.MaxValue; TestMethods tm = 0; StopDevs sd = 0; // '_regulValve.RegulValveTandemCfg.PidCoef' replaced by a casted operation parameter 'pidCoef' controlBoard.SendCommand(Command.Start, flowMeterNr, controlBoard.Route, int.MaxValue, tm, 0.0f, (int)pidCoef, 0, sd); } /// Run this operation /// /// Event.None, Event.FlowReached, Event.RegulValveTimeOut, Event.OpArgumentError /// public Event Run() { float rvPosition = controlBoard.RValvePosition(regulValveNr); float fixedRVPosition = controlBoard.RValvePosition(fixedRVNr); float flowMtrFreq = controlBoard.ReferenceFreq; float flow = flowMtrFreq * 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.IssueSetPositionCommand) { /// /// Done once, issues an appropriate ValveMove(...) command /// controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetPosition, new float[2] { targetPositionLo, targetPositionHi }, regulValve.StableTime); opState = OpState.SettingPosition; return Event.None; } else if (opState == OpState.SettingPosition) { /// /// Repeated untill position is reached /// if ((targetPositionLo <= rvPosition) && (rvPosition <= targetPositionHi)) { opState = OpState.IssueSetPositionCommand_FixedRV; } return Event.None; } else if (opState == OpState.IssueSetPositionCommand_FixedRV) { /// /// Done once, issues an appropriate ValveMove(...) command /// controlBoard.ValveMove(fixedRVNr, RegulValveMode.TargetPosition, new float[2] { fixedPctLo, fixedPctHi }, regulValve.StableTime); opState = OpState.SettingPosition_FixedRV; return Event.None; } else if (opState == OpState.SettingPosition_FixedRV) { /// /// Repeated untill position is reached /// if ((fixedPctLo <= fixedRVPosition) && (fixedRVPosition <= fixedPctHi)) { opState = OpState.IssueSetFlowCommand; } return Event.None; } else if (opState == OpState.IssueSetFlowCommand) { /// /// Done once, issues an appropriate ValveMove(...) command /// if (reqFlowLo >= reqFlowHi || reqFlowLo > 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 / nominalFlow; float freqHi = 2000.0f * reqFlowHi / nominalFlow; controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetFrequency, new float[2] { freqLo, freqHi }, regulValve.StableTime); opState = OpState.SettingFlow; return Event.None; } else if (opState == OpState.SettingFlow) { /// /// Repeated untill the required flow is reached /// if ((reqFlowLo <= flow) && (flow <= reqFlowHi)) { if (flowWithinBoundsTime++ >= regulValve.FlowStableSec) { 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 { return Event.None; } } else if (StateMachine.Time > expireTime) { return Event.RegulValveTimeOut; } else { flowWithinBoundsTime = 0; 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); } } }