498 lines
21 KiB
C#
498 lines
21 KiB
C#
///
|
|
/// 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 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;
|
|
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,
|
|
TotalPulsesCount = 0x7FFFFF,
|
|
MassPulsesCount = 0x7FFFFF,
|
|
StartStop = Command.Start,
|
|
};
|
|
}
|
|
void SetMeasureFlowArgs(Action action)
|
|
{
|
|
FlowMId = action.FlowMId;
|
|
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, int totalPulsesCount, int massPulsesCount = -1)
|
|
{
|
|
return new Action
|
|
{
|
|
ActionId = ActionID.StartTest,
|
|
IsFromUI = isFromUI,
|
|
///
|
|
FlowMId = flowMId,
|
|
DivId = divId,
|
|
DivThreshold = divThreshold,
|
|
IsSyncMethod = isSyncMethod,
|
|
IsDivUsed = isDivUsed,
|
|
IsStartStop = isStartStop,
|
|
TotalPulsesCount = totalPulsesCount,
|
|
MassPulsesCount = (massPulsesCount == -1) ? 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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check whether new action arguments are compatible with alredy collected arguments
|
|
/// </summary>
|
|
/// <param name="action">Action to be executed together with already collected actions</param>
|
|
/// <returns>true when compatible</returns>
|
|
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<Action> 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} ToTank={9} Puls={10} MPls={11} RVMode={12} P1={13} P2={14}",
|
|
((int)combinedAction.ActionId).ToString("X3"),
|
|
combinedAction.Route.ToString("X12"),
|
|
combinedAction.FlowMId,
|
|
combinedAction.RegVId,
|
|
combinedAction.DivId,
|
|
combinedAction.StartStop,
|
|
combinedAction.IsSyncMethod,
|
|
combinedAction.IsDivUsed,
|
|
combinedAction.IsStartStop,
|
|
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} SS={5}", FlowMId, TotalPulsesCount, MassPulsesCount, IsSyncMethod, IsDivUsed, IsStartStop);
|
|
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);
|
|
}
|
|
}
|
|
}
|