310 lines
13 KiB
C#
310 lines
13 KiB
C#
///
|
|
/// Copyright (c) 2013-2018 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using log4net;
|
|
using TBF.Rig.GenericDevices;
|
|
using Dirichlet.Numerics;
|
|
|
|
namespace TBF.Rig.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()
|
|
{
|
|
}
|
|
}
|
|
}
|