using System; using System.Collections.Generic; using log4net; using TBF.BenchControl.GenericDevices; namespace TBF.BenchControl.Elde.Valve { 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 VOpen; /// List of valves to open in this point of time public IList VClose; /// List of valves to close in this point of time public ulong MasksOpen; /// OR-ed bitmasks of valves to open in this point of time public ulong MasksClose; /// OR-ed bitmasks of valves to close in this point of time public SwitchPoint(int time, IList vOpen, IList vClose) { this.TimeSec = time; this.VOpen = (vOpen == null) ? new List() : vOpen; this.VClose = (vClose == null) ? new List() : vClose; } /// Max. delay in [s] of all valves to be opened or closed public int MaxDelay { get { int max = 0; foreach (var vlv in VOpen) if ((vlv != null) && (vlv.Delay > max)) max = vlv.Delay; foreach (var vlv in VClose) if ((vlv != null) && (vlv.Delay > max)) max = vlv.Delay; return max; } } } 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 /// /// Processes coupled valves and creates switch points. /// /// A list of opening master valves /// A list of closing master valves /// IList CollectSwitchPoints(IList valvesOpen, IList valvesClose) { IList result = new List(); result.Add(new SwitchPoint(0, 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 time = valve.LagOpening; if (time == 0) { result[0].VOpen.Add(valve); } else { result.Add(new SwitchPoint(time, 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 time = valve.LagClosing; if (time == 0) { result[0].VClose.Add(valve); } else { result.Add(new SwitchPoint(time, null, ValveBase.MakeList(valve))); } } } return result; } 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) point.MasksOpen |= (valve as 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; } point.MasksClose = 0; foreach (var valve in point.VClose) { if (valve is Valve) point.MasksClose |= (valve as 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; } 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(ControlBoardDev controlBoard, IList valvesOpen, IList valvesClose) { if (controlBoard == null) throw new ArgumentNullException("ctrlBoard"); this.controlBoard = controlBoard; if (valvesOpen == null) valvesOpen = new List(); if (valvesClose == null) valvesOpen = new List(); switchPointsRaw = CollectSwitchPoints(valvesOpen, valvesClose); 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(ControlBoardDev controlBoard, IValve valveOpen, IValve valveClose) : this(controlBoard, ValveBase.MakeList(valveOpen), ValveBase.MakeList(valveClose)) { } /// Start this operation public void Start() { controlBoard.SetValves(switchPoints[0].MasksOpen, switchPoints[0].MasksClose); swStartTime = StateMachine.Time; maxDelayTime = switchPoints[0].MaxDelay; nextIx = 1; } /// Run this operation /// /// Event.SetValvesDone /// public Event Run() { if (nextIx >= switchPoints.Count) { if (StateMachine.Time >= swStartTime + maxDelayTime) return Event.ValvesSet; else return Event.None; } if (StateMachine.Time >= swStartTime + switchPoints[nextIx].TimeSec) { if (switchPoints[nextIx].TimeSec + switchPoints[nextIx].MaxDelay > maxDelayTime) { maxDelayTime = switchPoints[nextIx].TimeSec + switchPoints[nextIx].MaxDelay; } controlBoard.SetValves(switchPoints[nextIx].MasksOpen, switchPoints[nextIx].MasksClose); nextIx++; } return Event.None; } /// Start this operation public void Stop() { } } }