tbf/TBF/Rig/Uni/RegValve/SetFlowOp.cs

489 lines
19 KiB
C#

using Config.Entities;
using log4net;
using SharedComponents;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using TBF.Boxes;
using TBF.Rig.ControlBoard.Uni;
using TBF.Rig.GenericDevices;
namespace TBF.Rig.Uni.RegValve
{
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,
ValveMoveToPosition2,
ValveMoveToPosition3,
SettingPosition,
Wait4FlowMsrmntAndSendRVMove,
SettingFlow,
FlowReached,
SendCommandAgain,
}
OpState opState;
double currentReqFlowLo;
double currentReqFlowHi;
double targetPositionLo;
double targetPositionHi;
int setFlowTime;
int expireTime;
DoubleBox msrdFlow;
int isFlowOkDuration;
/// <summary>
/// Set required water flow. Conditionally leave the measurement running.
/// Events: FlowSet, FlowTimeOut
/// </summary>
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);
shrinkedTgtFlowHi = (RqrdFlowRangeRatio * rqrdFlowHiBeforeCorr)
+ ((1 - RqrdFlowRangeRatio) * targetFlowAve);
this.msrdFlow = msrdFlow;
this.timeout = timeout;
this.delay = delay;
this.leaveFlowControlRunning = leaveFlowControlRunning;
log.Debug(this.ToString());
LiveLogDiag.Log1(
"{0} >> CTOR() >> qFrom={1} qTo={2} rqrdFlowLoBeforeCorr={3} rqrdFlowHiBeforeCorr={4} targetFlowAve={5} shrinkedTgtFlowLo={6} shrinkedTgtFlowHi={7} nominalFlow={8} nominalFreq={9} maximalFlow={10} delay={11} timeout={12} leaveFlowControlRunning={13}",
regV.Name, qFrom, qTo, rqrdFlowLoBeforeCorr, rqrdFlowHiBeforeCorr, targetFlowAve,
shrinkedTgtFlowLo, shrinkedTgtFlowHi, flowMeter.NominalFlow, flowMeter.NominalFreq,
maximalFlow, delay, timeout, leaveFlowControlRunning);
}
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)
{
}
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)
{
}
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)
{
}
/// <summary>
/// Fetch target position limits from the dictionary or return false
/// </summary>
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);
LiveLogDiag.Log1(
"{0} >> FetchTargetPosition() >> avgReqFlow={1} FOUND targetPosition={2} positionLo={3} positionHi={4}",
regV.Name, avgReqFlow, targetPosition, positionLo, positionHi);
return true;
}
else
{
positionLo = 0;
positionHi = 0;
LiveLogDiag.Log1(
"{0} >> FetchTargetPosition() >> avgReqFlow={1} NOT FOUND",
regV.Name, avgReqFlow);
return false;
}
}
void StoreTargetPosition(double avgReqFlow, double actPosition)
{
if (!regV.Dict.ContainsKey(targetFlowAve))
{
regV.Dict.Add(new KeyValuePair<double, double>(avgReqFlow, actPosition));
LiveLogDiag.Log1(
"{0} >> StoreTargetPosition() >> STORED avgReqFlow={1} actPosition={2}",
regV.Name, avgReqFlow, actPosition);
}
else
{
LiveLogDiag.Log1(
"{0} >> StoreTargetPosition() >> SKIPPED because targetFlowAve={1} already exists in Dict",
regV.Name, targetFlowAve);
}
return;
}
/// <summary>Start this operation</summary>
public void Start()
{
setFlowTime = StateMachine.Time + delay;
expireTime = StateMachine.Time + timeout;
if (expireTime < 0) expireTime = int.MaxValue;
isFlowOkDuration = 0;
currentReqFlowLo = 0;
currentReqFlowHi = 0;
log.InfoFormat(
"SetFlowOp:Start() rv#={0} flowMtr#={1} TARGET_SHRINKED: flowLo={2} flowHi={3}",
regV.Idx1, flowMeter.Idx1, shrinkedTgtFlowLo, shrinkedTgtFlowHi);
LiveLogDiag.Log1(
"{0} >> Start() >> rv#={1} flowMtr#={2} setFlowTime={3} expireTime={4} delay={5} timeout={6} shrinkedTgtFlowLo={7} shrinkedTgtFlowHi={8} targetFlowAve={9} nominalFlow={10} nominalFreq={11}",
regV.Name, regV.Idx1, flowMeter.Idx1, setFlowTime, expireTime, delay, timeout,
shrinkedTgtFlowLo, shrinkedTgtFlowHi, targetFlowAve, flowMeter.NominalFlow, flowMeter.NominalFreq);
/// Start the flow measurement
LiveLogDiag.Log1(
"{0} >> Start() >> calling uniCB.MeasureFlow(false, flowMeter.Idx1={1})",
regV.Name, flowMeter.Idx1);
uniCB.MeasureFlow(false, flowMeter.Idx1);
opState = OpState.Wait4FlowMsrmntAndSendRVMove;
LiveLogDiag.Log1(
"{0} >> Start() >> opState={1}",
regV.Name, opState);
}
/// <summary>
/// Run this operation
/// </summary>
public Event Run()
{
log.DebugFormat("Op.Run() opState={0}", opState);
LiveLogDiag.Log1(
"{0} >> Run() >> time={1} opState={2} currentReqFlowLo={3} currentReqFlowHi={4} isFlowOkDuration={5}",
regV.Name, StateMachine.Time, opState, currentReqFlowLo, currentReqFlowHi, isFlowOkDuration);
if (StateMachine.Time > expireTime)
{
LiveLogDiag.Log1(
"{0} >> Run() >> TIMEOUT: StateMachine.Time={1} > expireTime={2}",
regV.Name, StateMachine.Time, expireTime);
return Event.RegulValveTimeOut;
}
if (opState == OpState.Wait4FlowMsrmntAndSendRVMove)
{
LiveLogDiag.Log1(
"{0} >> Run() >> Wait4FlowMsrmntAndSendRVMove: MsrmntAvailable={1}",
regV.Name, flowMeter.MsrmntAvailable);
if (!flowMeter.MsrmntAvailable)
return Event.Starting;
double flow1 = flowMeter.ReadFlow();
LiveLogDiag.Log1(
"{0} >> Run() >> Wait4FlowMsrmntAndSendRVMove: initial measured flow1={1}",
regV.Name, flow1);
/// Set the target flow/frequency range
currentReqFlowLo = shrinkedTgtFlowLo;
currentReqFlowHi = shrinkedTgtFlowHi;
LiveLogDiag.Log1(
"{0} >> Run() >> target range assigned: currentReqFlowLo={1} currentReqFlowHi={2} maximalFlow={3}",
regV.Name, currentReqFlowLo, currentReqFlowHi, maximalFlow);
//if (flow1 >= targetFlowAve) currentReqFlowHi = targetFlowAve;
//if (flow1 <= targetFlowAve) currentReqFlowLo = targetFlowAve;
if ((currentReqFlowLo >= currentReqFlowHi) || (currentReqFlowLo > maximalFlow) || (currentReqFlowHi <= 0))
{
LiveLogDiag.Log1(
"{0} >> Run() >> OpArgumentError: currentReqFlowLo={1} currentReqFlowHi={2} maximalFlow={3}",
regV.Name, currentReqFlowLo, currentReqFlowHi, maximalFlow);
return Event.OpArgumentError;
}
if (StateMachine.Time > setFlowTime)
{
double freqLo = flowMeter.NominalFreq * currentReqFlowLo / flowMeter.NominalFlow;
double freqHi = flowMeter.NominalFreq * currentReqFlowHi / flowMeter.NominalFlow;
LiveLogDiag.Log1(
"{0} >> Run() >> calling uniCB.SetFlow(false, rv#={1}, freqLo={2}, freqHi={3}) from nominalFreq={4}, nominalFlow={5}, flowLo={6}, flowHi={7}",
regV.Name, regV.Idx1, freqLo, freqHi, flowMeter.NominalFreq, flowMeter.NominalFlow, currentReqFlowLo, currentReqFlowHi);
uniCB.SetFlow(false, regV.Idx1, freqLo, freqHi);
log.InfoFormat("Run(): rv#={0} TARGET: flowLo={1} flowHi={2}", regV.Idx1, currentReqFlowLo, currentReqFlowHi);
opState = OpState.SettingFlow;
LiveLogDiag.Log1(
"{0} >> Run() >> opState changed to {1}",
regV.Name, opState);
}
else
{
LiveLogDiag.Log1(
"{0} >> Run() >> waiting for delay: StateMachine.Time={1}, setFlowTime={2}",
regV.Name, StateMachine.Time, setFlowTime);
}
return Event.Starting;
}
if (opState == OpState.ValveMoveToPosition1)
{
LiveLogDiag.Log1("{0} >> Run() >> ValveMoveToPosition1 -> ValveMoveToPosition2", regV.Name);
opState = OpState.ValveMoveToPosition2;
return Event.Starting;
}
else if (opState == OpState.ValveMoveToPosition2)
{
LiveLogDiag.Log1(
"{0} >> Run() >> ValveMoveToPosition2: MoveToPosition(targetPositionLo={1}, targetPositionHi={2})",
regV.Name, targetPositionLo, targetPositionHi);
regV.MoveToPosition(false, targetPositionLo, targetPositionHi);
opState = OpState.SettingPosition;
LiveLogDiag.Log1("{0} >> Run() >> opState changed to {1}", regV.Name, opState);
return Event.Starting;
}
else if (opState == OpState.ValveMoveToPosition3)
{
LiveLogDiag.Log1("{0} >> Run() >> ValveMoveToPosition3 -> SettingPosition", regV.Name);
opState = OpState.SettingPosition;
return Event.Starting;
}
else if (opState == OpState.SettingPosition)
{
double rvPosition = regV.Position;
LiveLogDiag.Log1(
"{0} >> Run() >> SettingPosition: rvPosition={1}, targetPositionLo={2}, targetPositionHi={3}",
regV.Name, rvPosition, targetPositionLo, targetPositionHi);
if ((targetPositionLo <= rvPosition) && (rvPosition <= targetPositionHi))
{
opState = OpState.Wait4FlowMsrmntAndSendRVMove;
LiveLogDiag.Log1(
"{0} >> Run() >> position reached, opState changed to {1}",
regV.Name, opState);
}
return Event.Starting;
}
else if (opState == OpState.SettingFlow)
{
double frequency = flowMeter.ReadFrequency();
double flow = flowMeter.ReadFlow();
LiveLogDiag.Log1(
"{0} >> Run() >> SettingFlow: frequency={1} flow={2} reqLo={3} reqHi={4}",
regV.Name, frequency, flow, currentReqFlowLo, currentReqFlowHi);
if (msrdFlow != null && flow != 0)
{
msrdFlow.Val = flow;
LiveLogDiag.Log1(
"{0} >> Run() >> SettingFlow: msrdFlow.Val updated to {1}",
regV.Name, msrdFlow.Val);
}
if (currentReqFlowLo <= flow && flow <= currentReqFlowHi)
{
isFlowOkDuration++;
LiveLogDiag.Log1(
"{0} >> Run() >> SettingFlow: flow IN RANGE, isFlowOkDuration={1}/{2}",
regV.Name, isFlowOkDuration, regV.FlowStableSec);
if (isFlowOkDuration >= regV.FlowStableSec)
{
if (regV.StoredPositionReuse)
{
double rvPosition = regV.Position;
double storedPosition;
LiveLogDiag.Log1(
"{0} >> Run() >> StoredPositionReuse ON: rvPosition={1} targetFlowAve={2}",
regV.Name, rvPosition, targetFlowAve);
if (regV.Dict.TryGetValue(targetFlowAve, out storedPosition))
{
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);
LiveLogDiag.Log1(
"{0} >> Run() >> updating stored position: old={1} newAvg={2}",
regV.Name, storedPosition, (rvPosition + storedPosition) / 2.0);
}
else
{
StoreTargetPosition(targetFlowAve, rvPosition);
log.InfoFormat(
"Run(): rv#={0} reqFlow={1} pos={2}% <--- Storing a new position",
regV.Idx1, targetFlowAve, rvPosition.ToString("F1"));
LiveLogDiag.Log1(
"{0} >> Run() >> storing new position: {1}",
regV.Name, rvPosition);
}
}
log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2}, REACHED)", flow, currentReqFlowLo, currentReqFlowHi);
opState = OpState.FlowReached;
LiveLogDiag.Log1(
"{0} >> Run() >> FLOW REACHED, opState changed to {1}",
regV.Name, opState);
return Event.FlowReached;
}
else
{
log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2}, FLOW_OK_TIMER={3}s)", flow, currentReqFlowLo, currentReqFlowHi, isFlowOkDuration);
LiveLogDiag.Log1(
"{0} >> Run() >> flow in range but not stable long enough yet",
regV.Name);
return Event.Busy;
}
}
else
{
isFlowOkDuration = 0;
log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2})", flow, currentReqFlowLo, currentReqFlowHi);
LiveLogDiag.Log1(
"{0} >> Run() >> SettingFlow: flow OUT OF RANGE, reset isFlowOkDuration to 0",
regV.Name);
return Event.Busy;
}
}
else
{
double finalFlow = flowMeter.ReadFlow();
log.InfoFormat("flow = {0} m3/h (lo={1}, hi={2}, REACHED)", finalFlow, currentReqFlowLo, currentReqFlowHi);
LiveLogDiag.Log1(
"{0} >> Run() >> FlowReached state: finalFlow={1} reqLo={2} reqHi={3}",
regV.Name, finalFlow, currentReqFlowLo, currentReqFlowHi);
return Event.FlowReached;
}
}
/// <summary>Stop this operation</summary>
public void Stop()
{
LiveLogDiag.Log1(
"{0} >> Stop() >> leaveFlowControlRunning={1} opState(before)={2}",
regV.Name, leaveFlowControlRunning, opState);
if (!leaveFlowControlRunning)
{
LiveLogDiag.Log1(
"{0} >> Stop() >> calling uniCB.StopFlowControl(false, regV.Idx1={1})",
regV.Name, regV.Idx1);
uniCB.StopFlowControl(false, regV.Idx1);
}
opState = OpState.Idle;
LiveLogDiag.Log1(
"{0} >> Stop() >> opState(after)={1}",
regV.Name, opState);
}
/// <summary>
/// Logging
/// For activation use compilation condition: LIVELOGDIAG_FlowMeter_cs
/// </summary>
static class LiveLogDiag
{
[Conditional("LIVELOGDIAG_RegValve_cs")]
public static void Log1(string format, params object[] args)
{
LiveLogCache.Instance.AddLog("RegValve.cs LOG>> " + string.Format(format, args));
}
}
}
}