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}, 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 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,
ValveMoveToPosition,
SettingPosition,
ValveMoveToFlow,
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
/// 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 regulValve, IFlowMeter flowMeter,
float requiredFlowLo, float requiredFlowHi, float pidCoef, 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.Idx1;
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, IFlowMeter flowMeter,
float requiredFlowLo, float requiredFlowHi, float pidCoef, FloatBox measuredFlow, int timeout)
: this(controlBoard, regValve, flowMeter, requiredFlowLo, requiredFlowHi, pidCoef, measuredFlow, timeout, false)
{
}
///
/// Set required water flow - No timeout.
/// Events: FlowSet
///
public SetFlowOp(ControlBoardDev controlBoard, IRegulValve regValve, IFlowMeter flowMeter,
float requiredFlowLo, float requiredFlowHi, float pidCoef, FloatBox measuredFlow)
: this(controlBoard, regValve, 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.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;
// 'regulValve.RegulValveCfg.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 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.ValveMoveToPosition)
{
controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetPosition,
new float[2] { targetPositionLo, targetPositionHi },
regulValve.StableTime);
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 > 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)
{
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);
}
}
}