/// /// Copyright (c) 2013-2020 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using log4net; using Dirichlet.Numerics; using TBF.Boxes; using TBF.Rig.ControlBoard; using TBF.Rig.GenericDevices; 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; IList switchPointsRaw; IList 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) /// /// Processes coupled valves and creates switch points. /// /// A list of opening master valves /// A list of closing master valves /// IList AddSwitchPoints(IList swPoints, IList valvesOpen, IList 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 SortAndMergeSwitchPoints(IList unsorted) { IList result = new List(); 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 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; } 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; } point.TimeSec += timeShift; } } /// /// Open and/or close multiple valves. /// Events: ValvesSet /// /// Control board device /// A list of opening master valves /// A list of closing master valves /// Only Elde.Valve valves are used, other valves on the lists are ignored public SetValvesOp(IControlBoard cb, IList valvesOpen, IList valvesClose) { this.cb = cb; if (this.cb == null) throw new ArgumentNullException("ctrlBoard"); if (valvesOpen == null) valvesOpen = new List(); if (valvesClose == null) valvesClose = new List(); switchPointsRaw = new List(); AddSwitchPoints(switchPointsRaw, valvesOpen, valvesClose, 0); switchPoints = SortAndMergeSwitchPoints(switchPointsRaw); int timeShift = -switchPoints[0].TimeSec; MakeMasks(switchPoints, timeShift); } /// /// Open one valve and/or close one valve. /// Events: ValvesSet /// /// Control board device /// Valve to be opened /// Valve to be closed /// Only Elde.Valve valves are used, other valves on the lists are ignored public SetValvesOp(IControlBoard cb, IValve valveOpen, IValve valveClose) : this(cb, ValveBase.MakeList(valveOpen), ValveBase.MakeList(valveClose)) { } /// /// OPEN or CLOSE start/stop valve and associated valves and/or pumps. /// Events: ValvesSet /// /// Control board device /// true: open start valve, false: close start valve /// OutputPath specifying the start valve and associated valves/pumps /// Time stamp of the real valve switch (open or close) /// Only Elde.Valve valves are used, other valves on the lists are ignored public SetValvesOp(IControlBoard cb, bool open, OutputPath outPath, DateTimeBox timeStamp) { this.cb = cb; if (this.cb == null) throw new ArgumentNullException("ctrlBoard"); this.timeStamp = timeStamp; IList none = new List(); /// An empty list of valves switchPointsRaw = new List(); 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); } /// Start this operation public void Start() { 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; } /// Run this operation /// /// Event.SetValvesDone /// public Event Run() { if (nextIx >= switchPoints.Count) { return (StateMachine.Time >= swStartTime + maxDelayTime) ? 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; } /// Start this operation public void Stop() { } } }