///
/// 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
/// Advanced
GetDiverterTransitionData,
GetScopeAnalyzerData,
ResetScopeAnalyzer,
GetSwitchCounterData,
}
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 = 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);
}
///----------------------------------------------------------------------------------------------
void SetArgs(Action action)
{
ActionId |= action.ActionId;
switch (action.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;
default: return;
}
}
///
/// 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);
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);
}
}
}