348 lines
14 KiB
C#
348 lines
14 KiB
C#
///
|
|
/// Copyright (c) 2013-2023 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Common;
|
|
using log4net;
|
|
using Dirichlet.Numerics;
|
|
using TBF.Boxes;
|
|
using TBF.Rig.ControlBoard;
|
|
using TBF.Rig.GenericDevices;
|
|
using System.Windows.Forms;
|
|
|
|
namespace TBF.Rig.BuiltIn
|
|
{
|
|
public class SetValvesOp : IOperation
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(SetValvesOp));
|
|
|
|
/// Set by the constructor
|
|
IControlBoard cb;
|
|
DateTimeBox timeStamp;
|
|
FloatBox switchTime;
|
|
int startStopValveBitNr = 0;
|
|
|
|
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
|
|
int timeShift; /// Set in SetValvesOp(cb, bool open, OutputPath outPath, DateTimeBox timeStamp)
|
|
|
|
int waitOnCBDelay = 2; // additional wait for the correct ControlBoard response in [s]
|
|
|
|
/// <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 PumpTandem.Pump) point.MasksOpen |= (valve as PumpTandem.Pump).Mask;
|
|
if (valve is Danfoss.VLT2800.Pump) point.MasksOpen |= (valve as Danfoss.VLT2800.Pump).Mask;
|
|
if (valve is Modbus.PumpFM.DanfossVLT.Pump) point.MasksOpen |= (valve as Modbus.PumpFM.DanfossVLT.Pump).Mask;
|
|
if (valve is Modbus.PumpFM.Grundfoss.Pump) point.MasksOpen |= (valve as Modbus.PumpFM.Grundfoss.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 PumpTandem.Pump) point.MasksClose |= (valve as PumpTandem.Pump).Mask;
|
|
if (valve is Danfoss.VLT2800.Pump) point.MasksClose |= (valve as Danfoss.VLT2800.Pump).Mask;
|
|
if (valve is Modbus.PumpFM.DanfossVLT.Pump) point.MasksClose |= (valve as Modbus.PumpFM.DanfossVLT.Pump).Mask;
|
|
if (valve is Modbus.PumpFM.Grundfoss.Pump) point.MasksClose |= (valve as Modbus.PumpFM.Grundfoss.Pump).Mask;
|
|
}
|
|
point.TimeSec += timeShift;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Open and/or close multiple valves.
|
|
/// Events: ValvesSet
|
|
/// </summary>
|
|
/// <param name="cb">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(IControlBoard cb, IList<IValve> valvesOpen, IList<IValve> valvesClose)
|
|
{
|
|
this.cb = cb;
|
|
if (this.cb == null) throw new ArgumentNullException("ctrlBoard");
|
|
|
|
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="cb">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(IControlBoard cb, IValve valveOpen, IValve valveClose)
|
|
: this(cb, ValveBase.MakeList(valveOpen), ValveBase.MakeList(valveClose))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// OPEN or CLOSE start/stop valve and associated valves and/or pumps.
|
|
/// Events: ValvesSet
|
|
/// </summary>
|
|
/// <param name="cb">Control board device</param>
|
|
/// <param name="open">true: open start valve, false: close start valve</param>
|
|
/// <param name="outPath">OutputPath specifying the start valve and associated valves/pumps</param>
|
|
/// <param name="timeStamp">Time stamp of the real valve switch (open or close)</param>
|
|
/// <remarks>Only Elde.Valve valves are used, other valves on the lists are ignored</remarks>
|
|
public SetValvesOp(IControlBoard cb, bool open, OutputPath outPath, DateTimeBox timeStamp)
|
|
: this(cb, open, outPath, timeStamp, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// OPEN or CLOSE start/stop valve and associated valves and/or pumps.
|
|
/// Events: ValvesSet
|
|
/// </summary>
|
|
/// <param name="cb">Control board device</param>
|
|
/// <param name="open">true: open start valve, false: close start valve</param>
|
|
/// <param name="outPath">OutputPath specifying the start valve and associated valves/pumps</param>
|
|
/// <param name="timeStamp">Time stamp of the real valve switch (open or close)</param>
|
|
/// <remarks>Only Elde.Valve valves are used, other valves on the lists are ignored</remarks>
|
|
public SetValvesOp(IControlBoard cb, bool open, OutputPath outPath, DateTimeBox timeStamp, FloatBox switchTime)
|
|
{
|
|
try
|
|
{
|
|
this.cb = cb;
|
|
if (this.cb == null) throw new ArgumentNullException("ctrlBoard");
|
|
|
|
this.timeStamp = timeStamp;
|
|
this.switchTime = switchTime;
|
|
this.startStopValveBitNr = outPath.StartValve1 is Valve.Valve ? (outPath.StartValve1 as Valve.Valve).BitPosition : 0;
|
|
|
|
IList<IValve> none = new List<IValve>(); /// An empty list of valves
|
|
switchPointsRaw = new List<SwitchPoint>();
|
|
|
|
if (open)
|
|
{
|
|
if (outPath.InvertSV1)
|
|
AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve1), 0); /// close SV1
|
|
else
|
|
AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve1), none, 0); /// open SV1
|
|
|
|
if (outPath.StartValve2 != null)
|
|
{
|
|
if (outPath.InvertSV2)
|
|
AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve2), -outPath.LagSV2); /// close SV2 after lag
|
|
else
|
|
AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve2), none, -outPath.LagSV2); /// open SV2 after lag
|
|
}
|
|
|
|
if (outPath.StartValve3 != null)
|
|
{
|
|
if (outPath.InvertSV3)
|
|
AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve3), -outPath.LagSV3); /// close SV3 after lag
|
|
else
|
|
AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve3), none, -outPath.LagSV3); /// open SV3 after lag
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (outPath.InvertSV1)
|
|
AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve1), none, 0); /// open SV1
|
|
else
|
|
AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve1), 0); /// close SV1
|
|
|
|
if (outPath.StartValve2 != null)
|
|
{
|
|
if (outPath.InvertSV2)
|
|
AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve2), none, outPath.LagSV2); /// open SV2 after lag
|
|
else
|
|
AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve2), outPath.LagSV2); /// close SV2 after lag
|
|
}
|
|
|
|
if (outPath.StartValve3 != null)
|
|
{
|
|
if (outPath.InvertSV3)
|
|
AddSwitchPoints(switchPointsRaw, ValveBase.MakeList(outPath.StartValve3), none, outPath.LagSV3); /// open SV3 after lag
|
|
else
|
|
AddSwitchPoints(switchPointsRaw, none, ValveBase.MakeList(outPath.StartValve3), outPath.LagSV3); /// close SV3 after lag
|
|
}
|
|
}
|
|
|
|
switchPoints = SortAndMergeSwitchPoints(switchPointsRaw);
|
|
timeShift = -switchPoints[0].TimeSec;
|
|
MakeMasks(switchPoints, timeShift);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(
|
|
$"Chyba počas zastavenia regulácie prietoku, v ramci SetValvesOp():\n{ex.Message}\n\nStack trace:\n{ex.StackTrace}",
|
|
"Chyba",
|
|
MessageBoxButtons.OK,
|
|
MessageBoxIcon.Error
|
|
);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/// <summary>Start this operation</summary>
|
|
public void Start()
|
|
{
|
|
if (cb.DebugLevel == DebugMode.Simulate)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
UInt128 changed = cb.SetValves(switchPoints[0].MasksOpen, switchPoints[0].MasksClose, StateMachine.LogicalFnValves);
|
|
|
|
if (timeStamp != null && switchPoints[0].TimeSec == timeShift)
|
|
{
|
|
timeStamp.Val = DateTime.Now; /// Create a time stamp if (swPnt[ix].TimeSec == timeShift)
|
|
}
|
|
|
|
swStartTime = StateMachine.Time;
|
|
maxDelayTime = switchPoints[0].MaxDelay(changed);
|
|
nextIx = 1;
|
|
}
|
|
|
|
/// <summary>Run this operation</summary>
|
|
/// <returns>
|
|
/// Event.SetValvesDone
|
|
/// </returns>
|
|
public Event Run()
|
|
{
|
|
if (cb.DebugLevel == DebugMode.Simulate)
|
|
{
|
|
return Event.ValvesSet;
|
|
}
|
|
|
|
if (nextIx >= switchPoints.Count)
|
|
{
|
|
return (StateMachine.Time >= swStartTime + maxDelayTime + waitOnCBDelay) ? Event.ValvesSet : Event.ValvesBusy;
|
|
}
|
|
|
|
if (StateMachine.Time >= swStartTime + switchPoints[nextIx].TimeSec)
|
|
{
|
|
UInt128 changed = cb.SetValves(switchPoints[nextIx].MasksOpen, switchPoints[nextIx].MasksClose, StateMachine.LogicalFnValves);
|
|
|
|
if (timeStamp != null && switchPoints[nextIx].TimeSec == timeShift)
|
|
{
|
|
timeStamp.Val = DateTime.Now; /// Create a time stamp if (swPnt[ix].TimeSec == timeShift)
|
|
}
|
|
|
|
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()
|
|
{
|
|
if (cb.DebugLevel == DebugMode.Simulate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (switchTime != null && startStopValveBitNr >= 16 && startStopValveBitNr <= 23 && cb is ControlBoard.Uni.UniCB)
|
|
{
|
|
switchTime.Val = (cb as ControlBoard.Uni.UniCB).Data.ValveSwitchTime[startStopValveBitNr - 16];
|
|
}
|
|
}
|
|
}
|
|
}
|