tbf/TBF/BenchControl/Elde/SetValvesOp.cs

310 lines
13 KiB
C#

///
/// Copyright (c) 2013-2018 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using log4net;
using TBF.BenchControl.GenericDevices;
using Dirichlet.Numerics;
namespace TBF.BenchControl.Elde
{
public class SetValvesOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(SetValvesOp));
/// Set by the constructor
ControlBoardDev controlBoard;
class SwitchPoint
{
public int TimeSec; /// Relative time in [s] from the start of the operation
public IList<IValve> VOpen; /// List of valves to open in this point of time
public IList<IValve> VClose; /// List of valves to close in this point of time
public UInt128 MasksOpen; /// OR-ed bitmasks of valves to open in this point of time
public UInt128 MasksClose; /// OR-ed bitmasks of valves to close in this point of time
public SwitchPoint(int time, IList<IValve> vOpen, IList<IValve> vClose)
{
this.TimeSec = time;
this.VOpen = (vOpen == null) ? new List<IValve>() : vOpen;
this.VClose = (vClose == null) ? new List<IValve>() : vClose;
}
/// <summary>
/// Max. delay of all valves that were opened or closed
/// </summary>
/// <param name="changed">'ulong' with bits of valves that were actually changed set ot '1'</param>
/// <returns>Delay in [s]</returns>
public int MaxDelay(UInt128 changed)
{
int max = 0;
foreach (var vlv in VOpen)
{
/// Skip the delay evaluation and update in case the valve/pump have not changed
if (vlv is Valve.Valve && ((vlv as Valve.Valve).Mask & changed) == 0) continue;
if (vlv is Pump.Pump && ((vlv as Pump.Pump).Mask & changed) == 0) continue;
if (vlv is PumpWithFM.Pump && ((vlv as PumpWithFM.Pump).Mask & changed) == 0) continue;
if (vlv is Danfoss.VLT2800.Pump && ((vlv as Danfoss.VLT2800.Pump).Mask & changed) == 0) continue;
if ((vlv != null) && (vlv.Delay > max)) max = vlv.Delay;
}
foreach (var vlv in VClose)
{
/// Skip the delay evaluation and update in case the valve/pump have not changed
if (vlv is Valve.Valve && ((vlv as Valve.Valve).Mask & changed) == 0) continue;
if (vlv is Pump.Pump && ((vlv as Pump.Pump).Mask & changed) == 0) continue;
if (vlv is PumpWithFM.Pump && ((vlv as PumpWithFM.Pump).Mask & changed) == 0) continue;
if (vlv is Danfoss.VLT2800.Pump && ((vlv as Danfoss.VLT2800.Pump).Mask & changed) == 0) continue;
if ((vlv != null) && (vlv.Delay > max)) max = vlv.Delay;
}
return max;
}
}
IList<SwitchPoint> switchPointsRaw;
IList<SwitchPoint> switchPoints;
int nextIx;
int swStartTime;
int maxDelayTime; /// Max. delay from all valve/pump switches.
/// Relative time in [s] from the start of the operation
/// <summary>
/// Processes coupled valves and creates switch points.
/// </summary>
/// <param name="valvesOpen">A list of opening master valves</param>
/// <param name="valvesClose">A list of closing master valves</param>
/// <returns></returns>
IList<SwitchPoint> AddSwitchPoints(IList<SwitchPoint> swPoints, IList<IValve> valvesOpen, IList<IValve> valvesClose, int time)
{
int ix0 = swPoints.Count;
swPoints.Add(new SwitchPoint(time, valvesOpen, valvesClose));
foreach (var valve in StateMachine.CoupledValves)
{
if ((!valve.InvertCouple && valvesOpen.Contains(valve.CoupledTo)) ||
(valve.InvertCouple && valvesClose.Contains(valve.CoupledTo)))
{
/// The coupled valve 'valve' is opening
int lag = time + valve.LagOpening;
if (lag == time)
{
swPoints[ix0].VOpen.Add(valve);
}
else
{
swPoints.Add(new SwitchPoint(lag, ValveBase.MakeList(valve), null));
}
}
else if ((!valve.InvertCouple && valvesClose.Contains(valve.CoupledTo)) ||
(valve.InvertCouple && valvesOpen.Contains(valve.CoupledTo)))
{
/// The coupled valve 'valve' is closing
int lag = time + valve.LagClosing;
if (lag == time)
{
swPoints[ix0].VClose.Add(valve);
}
else
{
swPoints.Add(new SwitchPoint(lag, null, ValveBase.MakeList(valve)));
}
}
}
return swPoints;
}
IList<SwitchPoint> SortAndMergeSwitchPoints(IList<SwitchPoint> unsorted)
{
IList<SwitchPoint> result = new List<SwitchPoint>();
SwitchPoint lastAdded = null;
while (unsorted.Count > 0)
{
SwitchPoint first = null;
foreach (var point in unsorted)
{
if (first == null || first.TimeSec > point.TimeSec) first = point;
}
if (lastAdded == null || lastAdded.TimeSec != first.TimeSec)
{
result.Add(first);
lastAdded = first;
}
else
{
lastAdded.VOpen = ValveBase.Merge(lastAdded.VOpen, first.VOpen);
lastAdded.VClose = ValveBase.Merge(lastAdded.VClose, first.VClose);
}
unsorted.Remove(first);
}
return result;
}
void MakeMasks(IList<SwitchPoint> switchPoints, int timeShift)
{
foreach (var point in switchPoints)
{
/// Only Elde.Valve valves are used, other valves are ignored
point.MasksOpen = 0;
foreach (var valve in point.VOpen)
{
if (valve is Valve.Valve) point.MasksOpen |= (valve as Valve.Valve).Mask;
if (valve is Pump.Pump) point.MasksOpen |= (valve as Pump.Pump).Mask;
if (valve is PumpWithFM.Pump) point.MasksOpen |= (valve as PumpWithFM.Pump).Mask;
if (valve is Danfoss.VLT2800.Pump) point.MasksOpen |= (valve as Danfoss.VLT2800.Pump).Mask;
if (valve is PumpTandem.Pump) point.MasksOpen |= (valve as PumpTandem.Pump).Mask;
}
point.MasksClose = 0;
foreach (var valve in point.VClose)
{
if (valve is Valve.Valve) point.MasksClose |= (valve as Valve.Valve).Mask;
if (valve is Pump.Pump) point.MasksClose |= (valve as Pump.Pump).Mask;
if (valve is PumpWithFM.Pump) point.MasksClose |= (valve as PumpWithFM.Pump).Mask;
if (valve is Danfoss.VLT2800.Pump) point.MasksClose |= (valve as Danfoss.VLT2800.Pump).Mask;
if (valve is PumpTandem.Pump) point.MasksClose |= (valve as PumpTandem.Pump).Mask;
}
point.TimeSec += timeShift;
}
}
/// <summary>
/// Open and/or close multiple valves.
/// Events: ValvesSet
/// </summary>
/// <param name="controlBoard">Control board device</param>
/// <param name="valvesOpen">A list of opening master valves</param>
/// <param name="valvesClose">A list of closing master valves</param>
/// <remarks>Only Elde.Valve valves are used, other valves on the lists are ignored</remarks>
public SetValvesOp(ControlBoardDev controlBoard, IList<IValve> valvesOpen, IList<IValve> valvesClose)
{
if (controlBoard == null) throw new ArgumentNullException("ctrlBoard");
this.controlBoard = controlBoard;
if (valvesOpen == null) valvesOpen = new List<IValve>();
if (valvesClose == null) valvesClose = new List<IValve>();
switchPointsRaw = new List<SwitchPoint>();
AddSwitchPoints(switchPointsRaw, valvesOpen, valvesClose, 0);
switchPoints = SortAndMergeSwitchPoints(switchPointsRaw);
int timeShift = -switchPoints[0].TimeSec;
MakeMasks(switchPoints, timeShift);
}
/// <summary>
/// Open one valve and/or close one valve.
/// Events: ValvesSet
/// </summary>
/// <param name="controlBoard">Control board device</param>
/// <param name="valveOpen">Valve to be opened</param>
/// <param name="valveClose">Valve to be closed</param>
/// <remarks>Only Elde.Valve valves are used, other valves on the lists are ignored</remarks>
public SetValvesOp(ControlBoardDev controlBoard, IValve valveOpen, IValve valveClose)
: this(controlBoard, ValveBase.MakeList(valveOpen), ValveBase.MakeList(valveClose))
{
}
/// <summary>
/// OPEN or CLOSE start/stop valve and associated valves and/or pumps.
/// Events: ValvesSet
/// </summary>
/// <param name="controlBoard">Control board device</param>
/// <param name="close">true: close start valve, false: open start valve</param>
/// <param name="outPath">OutputPath specifying the start valve and associated valves/pumps</param>
/// <remarks>Only Elde.Valve valves are used, other valves on the lists are ignored</remarks>
public SetValvesOp(ControlBoardDev controlBoard, bool close, OutputPath outPath)
{
if (controlBoard == null) throw new ArgumentNullException("ctrlBoard");
this.controlBoard = controlBoard;
IList<IValve> none = new List<IValve>(); /// An empty list of valves
switchPointsRaw = new List<SwitchPoint>();
if (close)
{
if (outPath.InvertSV1) AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve1), none, 0);
else AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve1), 0);
if (outPath.StartValve2 != null)
{
if (outPath.InvertSV2) AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve2), none, outPath.LagSV2);
else AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve2), outPath.LagSV2);
}
if (outPath.StartValve3 != null)
{
if (outPath.InvertSV3) AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve3), none, outPath.LagSV3);
else AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve3), outPath.LagSV3);
}
}
else
{
if (outPath.InvertSV1) AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve1), 0);
else AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve1), none, 0);
if (outPath.StartValve2 != null)
{
if (outPath.InvertSV2) AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve2), -outPath.LagSV2);
else AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve2), none, -outPath.LagSV2);
}
if (outPath.StartValve3 != null)
{
if (outPath.InvertSV3) AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve3), -outPath.LagSV3);
else AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve3), none, -outPath.LagSV3);
}
}
switchPoints = SortAndMergeSwitchPoints(switchPointsRaw);
int timeShift = -switchPoints[0].TimeSec;
MakeMasks(switchPoints, timeShift);
}
/// <summary>Start this operation</summary>
public void Start()
{
UInt128 changed = controlBoard.SetValves(switchPoints[0].MasksOpen, switchPoints[0].MasksClose, StateMachine.ExtendedValves);
swStartTime = StateMachine.Time;
maxDelayTime = switchPoints[0].MaxDelay(changed);
nextIx = 1;
}
/// <summary>Run this operation</summary>
/// <returns>
/// Event.SetValvesDone
/// </returns>
public Event Run()
{
if (nextIx >= switchPoints.Count)
{
if (StateMachine.Time >= swStartTime + maxDelayTime) return Event.ValvesSet;
else return Event.ValvesBusy;
}
if (StateMachine.Time >= swStartTime + switchPoints[nextIx].TimeSec)
{
UInt128 changed = controlBoard.SetValves(switchPoints[nextIx].MasksOpen, switchPoints[nextIx].MasksClose, StateMachine.ExtendedValves);
int nextDelay = switchPoints[nextIx].MaxDelay(changed);
if (switchPoints[nextIx].TimeSec + nextDelay > maxDelayTime)
{
maxDelayTime = switchPoints[nextIx].TimeSec + nextDelay;
}
nextIx++;
}
return Event.ValvesBusy;
}
/// <summary>Start this operation</summary>
public void Stop()
{
}
}
}