/// /// Copyright (c) 2021 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using log4net; using Dirichlet.Numerics; namespace TBF.Rig.ControlBoard.Uni { public enum ActionID { /// Basic RequestDataOnly = 0, ChangeRoute = 1, /// Change digital outputs (route) MeasureFlow = 2, /// Activate a flow meter to measure the flow SetFlow = 4, /// Set flow StartTest = 8, /// Start a test Stop = 0x10, /// Stop a test or a flow regulation RegVlvIncrMove = 0x20, RegVlvMoveToPos = 0x40, RegVlvStop = 0x80, SwitchDiverter = 0x100, /// Switch diverter to tank/sink DelayStartOrStop = 0x200, GetDiverterTransitionData = 0x400, GetScopeAnalyzerData = 0x800, ResetScopeAnalyzer = 0x1000, GetSwitchCounterData = 0x2000, } public class Action { private static readonly ILog log = LogManager.GetLogger(typeof(Action)); public ActionID ActionId { get; set; } public bool IsFromUI { get; set; } /// false = action is comming from the state machine, true = action is comming from the UI public ulong Route { get; set; } /// ChangeRoute argument public int FlowMId { get; set; } /// MeasureFlow, SetFlow and StartTest argument public int RegVId { get; set; } /// SetFlow and StartTest argument public int DivId { get; set; } /// StartTest and SwitchDiverter argument public Command StartStop { get; set; } /// MeasureFlow, SetFlow, StartTest, StopTest and SwitchDiverter public bool IsSyncMethod { get; set; } /// StartTest argument public bool IsDivUsed { get; set; } /// StartTest argument public bool IsStartStop { get; set; } /// StartTest argument public bool IsProlonged { get; set; } /// StartTest argument public bool ToTank { get; set; } /// SwitchDiverter argument public int TotalPulsesCount { get; set; } /// StartTest argument public int MassPulsesCount { get; set; } /// StartTest argument public RegValveMode RegVMode { get; set; } /// SetFlow, RegVlvIncrMove, RegVlvMoveToPos, RegVlvStop public int RVMovePar1 { get; set; } /// SetFlow, RegVlvIncrMove, RegVlvMoveToPos public int RVMovePar2 { get; set; } /// SetFlow, RegVlvIncrMove, RegVlvMoveToPos public int PID { get; set; } /// SetFlow public int TolerRV { get; set; } /// SetFlow public int StabTime { get; set; } /// SetFlow public int DivThreshold { get; set; } /// StartTest argument public Action() { /// Values -1 indicate the related arguments are not used yet FlowMId = -1; RegVId = -1; DivId = -1; TotalPulsesCount = -1; StartStop = Command.None; RegVMode = RegValveMode.None; /// These values are used by one action type only. No conflicts possible Route = 0; IsSyncMethod = false; IsDivUsed = false; IsStartStop = false; IsProlonged = false; MassPulsesCount = 0; RVMovePar1 = 0; RVMovePar2 = 0; } public bool IsStart() { return StartStop == Command.Start; } public bool IsStop() { return StartStop == Command.Stop; } ///---------------------------------------------------------------------------------------------- public static Action RequestDataOnly() { return new Action { ActionId = ActionID.RequestDataOnly, IsFromUI = false, }; } ///---------------------------------------------------------------------------------------------- public static Action ChangeRoute(bool isFromUI, ulong route) { return new Action { ActionId = ActionID.ChangeRoute, IsFromUI = isFromUI, /// Route = route, }; } void SetChangeRouteArgs(Action action) { Route = action.Route; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedChangeRouteArgs(Action newAction) { return true; } ///---------------------------------------------------------------------------------------------- public static Action MeasureFlow(bool isFromUI, int flowMeterId) { return new Action { ActionId = ActionID.MeasureFlow, IsFromUI = isFromUI, /// FlowMId = flowMeterId, IsSyncMethod = false, IsDivUsed = false, IsStartStop = false, IsProlonged = false, TotalPulsesCount = 0x7FFFFF, MassPulsesCount = 0x7FFFFF, StartStop = Command.Start, }; } void SetMeasureFlowArgs(Action action) { FlowMId = action.FlowMId; IsSyncMethod = false; IsDivUsed = false; IsStartStop = false; IsProlonged = false; TotalPulsesCount = action.TotalPulsesCount; MassPulsesCount = action.MassPulsesCount; StartStop = Command.Start; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedMeasureFlowArgs(Action newAction) { return (FlowMId == -1 || FlowMId == newAction.FlowMId) && (TotalPulsesCount == -1 || TotalPulsesCount == newAction.TotalPulsesCount) && (StartStop == Command.None || StartStop == Command.Start); } ///---------------------------------------------------------------------------------------------- public static Action SetFlow(bool isFromUI, int regValveId, double freqLo, double freqHi, int pid, int stabTime_ms) { int rvMovePar1 = Convert.ToInt32(Math.Round(13.1072 * freqLo)); int rvMovePar2 = Convert.ToInt32(Math.Round(13.1072 * freqHi)); int stabTime = Convert.ToInt32(Math.Round(stabTime_ms / 200.0)); return new Action { ActionId = ActionID.SetFlow, IsFromUI = isFromUI, /// RegVId = regValveId, RegVMode = RegValveMode.TargetFrequency, RVMovePar1 = rvMovePar1, RVMovePar2 = rvMovePar2, PID = pid, TolerRV = 0, StabTime = stabTime, }; } void SetSetFlowArgs(Action action) { RegVId = action.RegVId; RegVMode = action.RegVMode; RVMovePar1 = action.RVMovePar1; RVMovePar2 = action.RVMovePar2; PID = action.PID; TolerRV = action.TolerRV; StabTime = action.StabTime; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedSetFlowArgs(Action newAction) { return (RegVId == -1 || RegVId == newAction.RegVId) && (RegVMode == RegValveMode.None || RegVMode == newAction.RegVMode); } ///---------------------------------------------------------------------------------------------- public static Action StartTest(bool isFromUI, int flowMId, int divId, int divThreshold, bool isSyncMethod, bool isDivUsed, bool isStartStop, bool isProlonged, int totalPulsesCount, int massPulsesCount = 0) { return new Action { ActionId = ActionID.StartTest, IsFromUI = isFromUI, /// FlowMId = flowMId, DivId = divId, DivThreshold = divThreshold, IsSyncMethod = isSyncMethod, IsDivUsed = isDivUsed, IsStartStop = isStartStop, IsProlonged = isProlonged, TotalPulsesCount = totalPulsesCount, MassPulsesCount = (massPulsesCount <= 0) ? totalPulsesCount : massPulsesCount, StartStop = Command.Start, }; } void SetStartTestArgs(Action action) { FlowMId = action.FlowMId; DivId = action.DivId; DivThreshold = action.DivThreshold; IsSyncMethod = action.IsSyncMethod; IsDivUsed = action.IsDivUsed; IsStartStop = action.IsStartStop; IsProlonged = action.IsProlonged; TotalPulsesCount = action.TotalPulsesCount; MassPulsesCount = action.MassPulsesCount; StartStop = Command.Start; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedStartTestArgs(Action newAction) { return (FlowMId == -1 || FlowMId == newAction.FlowMId) && (DivId == -1 || DivId == newAction.DivId) && (TotalPulsesCount == -1 || TotalPulsesCount == newAction.TotalPulsesCount) && (StartStop == Command.None || StartStop == Command.Start); } ///---------------------------------------------------------------------------------------------- public static Action Stop(bool isFromUI) { return new Action() { ActionId = ActionID.Stop, IsFromUI = isFromUI, /// StartStop = Command.Stop, }; } void SetStopArgs(Action action) { StartStop = Command.Stop; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedStopArgs(Action action) { return (StartStop == Command.None || StartStop == Command.Stop); } ///---------------------------------------------------------------------------------------------- public static Action RegVlvIncrMove(bool isFromUI, int regValveId, double timeSec) { int rvMovePar = Math.Abs(Convert.ToInt32(Math.Round(timeSec / 0.050))); /// Step is 50 ms return new Action { ActionId = ActionID.RegVlvIncrMove, IsFromUI = isFromUI, /// RegVId = regValveId, RegVMode = (timeSec >= 0) ? RegValveMode.PulseWidth : (RegValveMode.PulseWidth | RegValveMode.NegPulseWidth), RVMovePar1 = rvMovePar, RVMovePar2 = rvMovePar, }; } void SetRegVlvIncrMoveArgs(Action action) { RegVId = action.RegVId; RegVMode = action.RegVMode; RVMovePar1 = action.RVMovePar1; RVMovePar2 = action.RVMovePar2; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedRegVlvIncrMoveArgs(Action newAction) { return (RegVId == -1 || RegVId == newAction.RegVId) && (RegVMode == RegValveMode.None || RegVMode == newAction.RegVMode); } ///---------------------------------------------------------------------------------------------- public static Action RegVlvMoveToPos(bool isFromUI, int regValveId, double positionLo, double positionHi = -1) { int rvMovePar1, rvMovePar2; /// TODO /// if (positionHi < 0) { /// Center position specified, calculate a range rvMovePar1 = 0; // TODO rvMovePar2 = 0; // TODO } else { /// Lower and upper range of position specified rvMovePar1 = 0; // TODO rvMovePar2 = 0; // TODO } return new Action { ActionId = ActionID.RegVlvMoveToPos, IsFromUI = isFromUI, /// RegVId = regValveId, RegVMode = RegValveMode.TargetPosition, RVMovePar1 = rvMovePar1, RVMovePar2 = rvMovePar2, }; } void SetRegVlvMoveToPosArgs(Action action) { RegVId = action.RegVId; RegVMode = action.RegVMode; RVMovePar1 = action.RVMovePar1; RVMovePar2 = action.RVMovePar2; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedRegVlvMoveToPosArgs(Action newAction) { return (RegVId == -1 || RegVId == newAction.RegVId) && (RegVMode == RegValveMode.None || RegVMode == newAction.RegVMode); } ///---------------------------------------------------------------------------------------------- public static Action RegVlvStop(bool isFromUI, int regValveId) { return new Action { ActionId = ActionID.RegVlvStop, IsFromUI = isFromUI, /// RegVId = regValveId, RegVMode = RegValveMode.Stop, RVMovePar1 = 0, RVMovePar2 = 0, }; } void SetRegVlvStopArgs(Action action) { RegVId = action.RegVId; RegVMode = action.RegVMode; RVMovePar1 = action.RVMovePar1; RVMovePar2 = action.RVMovePar2; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedRegVlvStopArgs(Action newAction) { return (RegVId == -1 || RegVId == newAction.RegVId) && (RegVMode == RegValveMode.None || RegVMode == newAction.RegVMode); } ///---------------------------------------------------------------------------------------------- public static Action SwitchDiverter(bool isFromUI, int divId, bool toTank) { return new Action { ActionId = ActionID.SwitchDiverter, IsFromUI = isFromUI, /// DivId = divId, ToTank = toTank, StartStop = Command.Start, }; } void SetSwitchDiverterArgs(Action action) { DivId = action.DivId; ToTank = action.ToTank; StartStop = Command.Start; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckSharedSwitchDiverterArgs(Action action) { return (DivId == -1 || DivId == action.DivId) || (StartStop == Command.None || StartStop == Command.Start); } ///---------------------------------------------------------------------------------------------- public static Action DelayStartOrStop(bool isFromUI) { return new Action { ActionId = ActionID.DelayStartOrStop, IsFromUI = isFromUI, /// StartStop = Command.DelayStartOrStop, }; } void SetDelayStartOrStopArgs(Action action) { StartStop = Command.DelayStartOrStop; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckDelayStartOrStopArgs(Action action) { return (StartStop == Command.None || StartStop == Command.DelayStartOrStop); } ///---------------------------------------------------------------------------------------------- public static Action GetDiverterTransitionData(bool isFromUI) { return new Action { ActionId = ActionID.GetDiverterTransitionData, IsFromUI = isFromUI, /// StartStop = Command.GetDiverterTransitionData, }; } void SetGetDiverterTransitionDataArgs(Action action) { StartStop = Command.GetDiverterTransitionData; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckGetDiverterTransitionDataArgs(Action action) { return (StartStop == Command.None || StartStop == Command.GetDiverterTransitionData); } ///---------------------------------------------------------------------------------------------- public static Action GetScopeAnalyzerData(bool isFromUI) { return new Action { ActionId = ActionID.GetScopeAnalyzerData, IsFromUI = isFromUI, /// StartStop = Command.GetScopeAnalyzerData, }; } void SetGetScopeAnalyzerDataArgs(Action action) { StartStop = Command.GetScopeAnalyzerData; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckGetScopeAnalyzerDataArgs(Action action) { return (StartStop == Command.None || StartStop == Command.GetScopeAnalyzerData); } ///---------------------------------------------------------------------------------------------- public static Action ResetScopeAnalyzer(bool isFromUI) { return new Action { ActionId = ActionID.ResetScopeAnalyzer, IsFromUI = isFromUI, /// StartStop = Command.ResetScopeAnalyzer, }; } void SetResetScopeAnalyzerArgs(Action action) { StartStop = Command.GetScopeAnalyzerData; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckResetScopeAnalyzerArgs(Action action) { return (StartStop == Command.None || StartStop == Command.ResetScopeAnalyzer); } ///---------------------------------------------------------------------------------------------- public static Action GetSwitchCounterData(bool isFromUI) { return new Action { ActionId = ActionID.GetSwitchCounterData, IsFromUI = isFromUI, /// StartStop = Command.GetSwitchCounterData, }; } void SetGetSwitchCounterDataArgs(Action action) { StartStop = Command.GetSwitchCounterData; } /// Check whether new action arguments are compatible with alredy collected arguments (true = yes) bool CheckGetSwitchCounterDataArgs(Action action) { return (StartStop == Command.None || StartStop == Command.GetSwitchCounterData); } ///---------------------------------------------------------------------------------------------- public void SetArgs(Action action, ActionID actionId) { switch (actionId) { case ActionID.ChangeRoute: SetChangeRouteArgs(action); return; case ActionID.MeasureFlow: SetMeasureFlowArgs(action); return; case ActionID.SetFlow: SetSetFlowArgs(action); return; case ActionID.StartTest: SetStartTestArgs(action); return; case ActionID.Stop: SetStopArgs(action); return; case ActionID.RegVlvIncrMove: SetRegVlvIncrMoveArgs(action); return; case ActionID.RegVlvMoveToPos: SetRegVlvMoveToPosArgs(action); return; case ActionID.RegVlvStop: SetRegVlvStopArgs(action); return; case ActionID.SwitchDiverter: SetSwitchDiverterArgs(action); return; case ActionID.DelayStartOrStop: SetDelayStartOrStopArgs(action); return; case ActionID.GetDiverterTransitionData: SetGetDiverterTransitionDataArgs(action); return; case ActionID.GetScopeAnalyzerData: SetGetScopeAnalyzerDataArgs(action); return; case ActionID.ResetScopeAnalyzer: SetResetScopeAnalyzerArgs(action); return; case ActionID.GetSwitchCounterData: SetGetSwitchCounterDataArgs(action); return; default: return; } } void SetArgs(Action action) { ActionId |= action.ActionId; SetArgs(action, action.ActionId); } /// /// Check whether new action arguments are compatible with alredy collected arguments /// /// Action to be executed together with already collected actions /// true when compatible bool CheckSharedArgs(Action action) { switch (action.ActionId) { case ActionID.RequestDataOnly: return true; case ActionID.ChangeRoute: return CheckSharedChangeRouteArgs(action); case ActionID.MeasureFlow: return CheckSharedMeasureFlowArgs(action); case ActionID.SetFlow: return CheckSharedSetFlowArgs(action); case ActionID.StartTest: return CheckSharedStartTestArgs(action); case ActionID.Stop: return CheckSharedStopArgs(action); case ActionID.RegVlvIncrMove: return CheckSharedRegVlvIncrMoveArgs(action); case ActionID.RegVlvMoveToPos: return CheckSharedRegVlvMoveToPosArgs(action); case ActionID.RegVlvStop: return CheckSharedRegVlvStopArgs(action); case ActionID.SwitchDiverter: return CheckSharedSwitchDiverterArgs(action); case ActionID.DelayStartOrStop: return CheckDelayStartOrStopArgs(action); case ActionID.GetDiverterTransitionData: return CheckGetDiverterTransitionDataArgs(action); case ActionID.GetScopeAnalyzerData: return CheckGetScopeAnalyzerDataArgs(action); case ActionID.ResetScopeAnalyzer: return CheckResetScopeAnalyzerArgs(action); case ActionID.GetSwitchCounterData: return CheckGetSwitchCounterDataArgs(action); default: return false; } } ///---------------------------------------------------------------------------------------------- public static Action FetchNonConflictingActions(Queue queue, bool blockUI = false) { Action combinedAction = Action.RequestDataOnly(); while (queue.Count > 0) { var peekA = queue.Peek(); if ((combinedAction.ActionId & peekA.ActionId) == 0 && combinedAction.CheckSharedArgs(peekA) == true) { /// ActionId would not be repeated and arguments are compatible => apply the new action combinedAction.SetArgs(peekA); log.Debug(peekA.ToString()); queue.Dequeue(); } else { /// Leave actions in the queue and quit the loop as the next scheduled action is incompatible break; } } log.InfoFormat("Combined action: {0} Rt={1} Et={2} RV={3} Div#={4} Cmd={5} Sync={6} wDiv={7} S/S={8} Prolng={9} ToTank={10} Puls={11} MPls={12} RVMode={13} P1={14} P2={15}", ((int)combinedAction.ActionId).ToString("X3"), combinedAction.Route.ToString("X12"), combinedAction.FlowMId, combinedAction.RegVId, combinedAction.DivId, combinedAction.StartStop, combinedAction.IsSyncMethod, combinedAction.IsDivUsed, combinedAction.IsStartStop, combinedAction.IsProlonged, combinedAction.ToTank, combinedAction.TotalPulsesCount, combinedAction.MassPulsesCount, combinedAction.RegVMode, combinedAction.RVMovePar1, combinedAction.RVMovePar2, combinedAction.PID, combinedAction.TolerRV, combinedAction.StabTime, combinedAction.DivThreshold); return combinedAction; } public override string ToString() { string args; switch (ActionId) { case ActionID.ChangeRoute: args = string.Format("Rt={0}", Route.ToString("X12")); break; case ActionID.MeasureFlow: args = string.Format("Et={0}", FlowMId); break; case ActionID.SetFlow: args = string.Format("RV={0} P1={1} P2={2}", RegVId, RVMovePar1, RVMovePar2); break; case ActionID.StartTest: args = string.Format("Et={0} Puls={1} MPls={2} Sync={3} Div={4} S/S={5} Prolonged={6}", FlowMId, TotalPulsesCount, MassPulsesCount, IsSyncMethod, IsDivUsed, IsStartStop, IsProlonged); break; case ActionID.RegVlvIncrMove: args = string.Format("RV={0} Time={1}ms", RegVId, (RegVMode & RegValveMode.NegPulseWidth) != 0 ? -50 * RVMovePar1 : 50 * RVMovePar1); break; case ActionID.RegVlvMoveToPos: args = string.Format("RV={0} Pos={1}%", RegVId, 0); /// TODO break; default: args = string.Empty; break; } return string.Format(" {0} {1}", ActionId, args); } } }