737 lines
30 KiB
C#
737 lines
30 KiB
C#
///
|
|
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using log4net;
|
|
using Common;
|
|
using Config.Entities;
|
|
using Dirichlet.Numerics;
|
|
using TBF.Rig.GenericDevices;
|
|
using TBF.Rig.Sequences;
|
|
using TBF.UiBridge;
|
|
|
|
namespace TBF.Rig.ControlBoard.Uni
|
|
{
|
|
public class UniCB : ComponentBase, IControlBoard, SchematicDrawing.IDrawingItCmpntWithMeasuredVal
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(UniCB));
|
|
public override string ToString() { return string.Format("CBoard({0})", cbCfg != null ? cbCfg.ToString(-1) : string.Empty); }
|
|
|
|
/// TBF component configuration and useful wrappers
|
|
readonly UniCBCfg cbCfg;
|
|
string comPort { get { return string.Format("COM{0}", cbCfg.ComPortNr); } }
|
|
short config { get { return (short)((cbCfg.DrainValvesCount << 8) + ((cbCfg.DivertersCount - 1) << 3) + cbCfg.RegValvesCount - 4); } }
|
|
|
|
/// IDrawingItCmpnt interface implementation
|
|
public SchematicDrawing.IDrawingItem DrawingItem { get { return cbCfg as SchematicDrawing.IDrawingItem; } }
|
|
|
|
public bool MsrmntAvailable { get { return true; } }
|
|
public double MeasuredVal { get { return 0; } }
|
|
|
|
StringBuilder altString = new StringBuilder();
|
|
public string AltString
|
|
{
|
|
get {
|
|
altString.Clear();
|
|
altString.AppendFormat("State: {0}.{1}.{2}.{3}\n", (State >> 48) & 0xFF, (State >> 40) & 0xFF, (State >> 32) & 0xFF, Convert.ToUInt32(State & 0xFFFFFFFF).ToString("X6"));
|
|
altString.AppendFormat("DI: {0}\n", ToBin40(DigitalInputs));
|
|
altString.AppendFormat("DO: {0}\n", ToBin40(((UInt64)Route) >> 8));
|
|
altString.AppendFormat("AI0: {0:D3} AI1: {1:D3} AI2: {2:D3} AI3: {3:D3} AI4: {4:D3} AI5: {5:D3}\n",
|
|
AnalogInput(0), AnalogInput(1), AnalogInput(2), AnalogInput(3), AnalogInput(4), AnalogInput(5));
|
|
altString.AppendFormat("AI8: {0:D3} AI9: {1:D3} AI10: {2:D3} AI11: {3:D3}\n",
|
|
AnalogInput(8), AnalogInput(9), AnalogInput(10), AnalogInput(11));
|
|
altString.AppendFormat("Frequency: {0:F1} Hz Pulses: {1}", RefFrequency, RefPulses);
|
|
return altString.ToString();
|
|
}
|
|
}
|
|
|
|
string ToBin40(UInt64 val)
|
|
{
|
|
return string.Format("{0}-{1}-{2}-{3}-{4}", ToBin8((val >> 32) & 0xFF), ToBin8((val >> 24) & 0xFF),
|
|
ToBin8((val >> 16) & 0xFF), ToBin8((val >> 8) & 0xFF), ToBin8(val & 0xFF));
|
|
}
|
|
|
|
string ToBin8(UInt64 val)
|
|
{
|
|
string str = Convert.ToString((byte)val, 2);
|
|
switch (str.Length)
|
|
{
|
|
case 1: return "0000000" + str;
|
|
case 2: return "000000" + str;
|
|
case 3: return "00000" + str;
|
|
case 4: return "0000" + str;
|
|
case 5: return "000" + str;
|
|
case 6: return "00" + str;
|
|
case 7: return "0" + str;
|
|
default: return str;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Serial communication
|
|
///
|
|
SerialPort serialPort; /// Seral port or null: Check serialPort.IsOpen before using the serial port
|
|
const int BUFFER_SIZE = 4000; /// Size of input buffers
|
|
byte[] buffer; /// Buffer for data received in one attempt in RunDeviceBefore()
|
|
byte[] rcvdData; /// Buffer for all recevied but nut processed data. May contain data from several read attempts
|
|
int[] cumulativeSums;
|
|
int rcvdBytesCount; /// Count of valid bytes in rcvdData buffer
|
|
|
|
///
|
|
/// Route / digital outputs / states of electromagnetic valves
|
|
///
|
|
readonly UInt128 routeMask; /// Set in the constructor, bits of outputs that can be controlled by Action.ChangeRoute are set.
|
|
/// Regulation valve control outputs and diverters cannot be controlled by Action.ChangeRoute
|
|
/// Depends on the control board configuration
|
|
|
|
ulong valvesToInvert; /// From configurations of valve and pump components. Inversion is applied when creating
|
|
/// Action.ChangeRoute an dafter obtaining RRoute from the control board
|
|
|
|
/// State of digital outputs obtained from the control board
|
|
public UInt128 RRoute { get { return (UInt128)Data.Vystupy ^ valvesToInvert; } }
|
|
|
|
UInt128 displayedRoute; /// A route displayed on the screen (inversions are not applied)
|
|
UInt128 bitsChangedByUI; /// Ones in this word indicate which valves/pumpes were changed in UI by mouse clicks
|
|
UInt128 displayedRoutePlusUI; /// A route displayed on the screen plus the modifications from UI (inversions are not applied)
|
|
UInt128 outputLatch; /// A copy of route sent to physical outputs
|
|
UInt128 benchModelRoute;
|
|
bool outputsInitialized;
|
|
int lastStateMachineTime = 0;
|
|
|
|
/// <summary>
|
|
/// Route: 128-bit word representing the current state of all valves and pumps
|
|
/// </summary>
|
|
public UInt128 Route { get { return benchModelRoute; } }
|
|
|
|
|
|
/// UI actions are blocked when true
|
|
public bool IsUIBlocked { get; set; }
|
|
|
|
/// Message queue
|
|
Queue<Action> actionQueue;
|
|
|
|
/// Received data
|
|
public GeneralData Data;
|
|
DivTransitionData divTransitionData;
|
|
ScopeAnalyzerData scopeAnalyzerData;
|
|
SwitchCountersData switchCounterData;
|
|
|
|
public UInt64 State { get { return Data.State; } } /// Control board state
|
|
|
|
/// Current flow meter ID
|
|
public int FlowmeterId { get { return Convert.ToInt32(Data.State & (ulong)StatusP.FlowmtrNrMask); } }
|
|
|
|
public UInt64 DigitalInputs { get { return Data.Vstupy; } } /// Digital inputs
|
|
|
|
public Int16 AnalogInput(int channel)
|
|
{
|
|
if (Data.AnalogInput != null && channel >= 0 && channel < Data.AnalogInput.Length)
|
|
{
|
|
return Data.AnalogInput[channel];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// Values valid during a test
|
|
public double RefFrequency { get { return Data.ReferenceFreq[0]; } }
|
|
public double RefFrequency1 { get { return Data.ReferenceFreq[1]; } }
|
|
public double RefFrequency2 { get { return Data.ReferenceFreq[2]; } }
|
|
public double RefFrequency3 { get { return Data.ReferenceFreq[3]; } }
|
|
|
|
/// Last test results
|
|
public double TestTime { get { return Data.Ttime; } }
|
|
public int RefPulses { get { return Data.EtPulses[0]; } }
|
|
public float ValveOpenCloseTime { set; get; } /// Start valve open or close time in sec
|
|
|
|
/// <summary>
|
|
/// Select devices used for control and measurement
|
|
/// </summary>
|
|
/// <param name="outPath"></param>
|
|
public OutputPath Devices
|
|
{
|
|
set { devices = value; }
|
|
get { return devices; }
|
|
}
|
|
|
|
OutputPath devices;
|
|
IFlowMeter flowMeter { get { return devices.FlowMeter; } }
|
|
IRegValve regValve { get { return devices.RegValve; } }
|
|
IDiverter diverter { get { return devices.Diverter; } }
|
|
|
|
|
|
/// <summary>
|
|
/// Constructor for user interfaces (change settings only).
|
|
/// Initialize(), RunDeviceXY() and StopDevice() methods of the created object will not be called.
|
|
/// </summary>
|
|
public UniCB()
|
|
{
|
|
devices = new OutputPath(string.Empty, 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor invoked when starting the test bench and creating components.
|
|
/// </summary>
|
|
public UniCB(Generic.IComponentCfg cfg)
|
|
: base(cfg)
|
|
{
|
|
cbCfg = cfg as UniCBCfg;
|
|
|
|
devices = new OutputPath(string.Empty, 0);
|
|
actionQueue = new Queue<Action>();
|
|
actionQueue.Enqueue(Action.Stop(false)); /// Stop all
|
|
outputsInitialized = false;
|
|
|
|
Data = new GeneralData();
|
|
divTransitionData = null;
|
|
scopeAnalyzerData = null;
|
|
switchCounterData = null;
|
|
|
|
routeMask = 0xFF00FFFFFF;
|
|
for (int i = 0; i < cbCfg.RegValvesCount; i++) routeMask &= ~((UInt128)3 << 2*i);
|
|
if (cbCfg.RelaysFInstalled) routeMask |= 0xFF0000000000;
|
|
if (cbCfg.RelaysGInstalled) routeMask |= 0xFF000000000000;
|
|
if (cbCfg.RelaysHInstalled) routeMask |= 0xFF00000000000000;
|
|
|
|
log.Warn(this.ToString());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specify inverted valves
|
|
/// </summary>
|
|
/// <param name="valvesToInvert"></param>
|
|
public void SpecifyInvertedValves(UInt128 valvesToInvert)
|
|
{
|
|
this.valvesToInvert = (ulong)valvesToInvert;
|
|
}
|
|
|
|
#region IDevice interface
|
|
|
|
/// <summary>
|
|
/// Initialize control board device hardware
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
if (cbCfg.DebugLevel != DebugMode.Normal)
|
|
{
|
|
log.FatalFormat("{0} - Device simulated", Name);
|
|
return;
|
|
}
|
|
|
|
buffer = new byte[BUFFER_SIZE];
|
|
rcvdData = new byte[BUFFER_SIZE];
|
|
cumulativeSums = new int[BUFFER_SIZE];
|
|
rcvdBytesCount = 0;
|
|
|
|
serialPort = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One);
|
|
serialPort.DtrEnable = true;
|
|
serialPort.WriteTimeout = 4000;
|
|
serialPort.ParityReplace = 0xFF;
|
|
serialPort.Open();
|
|
|
|
/// Handler handling mouse clicks on the schematc drawinng - valve or pump state changes
|
|
TBF.UiBridge.Bridge.RouteChangeHandler += delegate(object sndr, TBF.UiBridge.RouteChangeArgs args)
|
|
{
|
|
UInt128 oneBitMask = ((UInt128)1) << args.BitNr;
|
|
bitsChangedByUI |= oneBitMask;
|
|
displayedRoutePlusUI = displayedRoutePlusUI ^ oneBitMask;
|
|
//if ((displayedRoutePlusUI & oneBitMask) == 0)
|
|
//{
|
|
// displayedRoutePlusUI |= oneBitMask;
|
|
//}
|
|
//else
|
|
//{
|
|
// displayedRoutePlusUI &= (~oneBitMask);
|
|
//}
|
|
};
|
|
|
|
log.FatalFormat("{0} - Device successfully initialized", Name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reecive and process data from the control board serial port
|
|
/// </summary>
|
|
public void RunDeviceBefore()
|
|
{
|
|
if (cbCfg.DebugLevel != DebugMode.Normal || serialPort == null || !serialPort.IsOpen) return;
|
|
|
|
/// Read new received bytes and append them to a buffer
|
|
int bytesCount = serialPort.BytesToRead;
|
|
if (bytesCount > 0)
|
|
{
|
|
bytesCount = serialPort.Read(buffer, 0, Math.Min(bytesCount, BUFFER_SIZE));
|
|
}
|
|
for (int i = 0; i < bytesCount && rcvdBytesCount < BUFFER_SIZE; i++, rcvdBytesCount++)
|
|
{
|
|
rcvdData[rcvdBytesCount] = buffer[i];
|
|
}
|
|
|
|
/// Calculate cumulative sums (to verify checksums easily)
|
|
int cumulativeSum = 0;
|
|
for (int i = 0; i < rcvdBytesCount; i++)
|
|
{
|
|
cumulativeSum += rcvdData[i];
|
|
cumulativeSums[i] = cumulativeSum;
|
|
}
|
|
|
|
/// Try to detect various kinds of data in rcvdData buffer in a loop, start at offset 0
|
|
int offset = 0;
|
|
int lastValidOffset = 0; /// Offset of the first byte after the last successfully parsed frame
|
|
do
|
|
{
|
|
bool anyProcessed = false;
|
|
if (ScopeAnalyzerData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
|
|
{
|
|
/// Scope analyzer data received
|
|
scopeAnalyzerData = ScopeAnalyzerData.GetData(rcvdData, offset);
|
|
string s = Telegram.LogTelegram("Scope analyzer data ", rcvdData, offset, ScopeAnalyzerData.MessageLen);
|
|
Debug.WriteLine(s);
|
|
log.Debug(s);
|
|
offset += ScopeAnalyzerData.MessageLen;
|
|
lastValidOffset = offset;
|
|
anyProcessed = true;
|
|
}
|
|
else if (GeneralData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
|
|
{
|
|
/// Regular data received
|
|
Data.UpdateData(rcvdData, offset);
|
|
string s = Telegram.LogTelegram("General data ", rcvdData, offset, GeneralData.MessageLen);
|
|
Debug.WriteLine(s);
|
|
log.Debug(s);
|
|
offset += GeneralData.MessageLen;
|
|
lastValidOffset = offset;
|
|
anyProcessed = true;
|
|
}
|
|
else if (SwitchCountersData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
|
|
{
|
|
/// Switch counter data received
|
|
switchCounterData = SwitchCountersData.GetData(rcvdData, offset);
|
|
string s = Telegram.LogTelegram("Switch counter data ", rcvdData, offset, SwitchCountersData.MessageLen);
|
|
Debug.WriteLine(s);
|
|
log.Debug(s);
|
|
offset += SwitchCountersData.MessageLen;
|
|
lastValidOffset = offset;
|
|
anyProcessed = true;
|
|
}
|
|
else if (DivTransitionData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
|
|
{
|
|
/// Diverter transition data received
|
|
divTransitionData = DivTransitionData.GetData(rcvdData, offset);
|
|
string s = Telegram.LogTelegram("Diverter transition data ", rcvdData, offset, DivTransitionData.MessageLen);
|
|
Debug.WriteLine(s);
|
|
log.Debug(s);
|
|
offset += DivTransitionData.MessageLen;
|
|
lastValidOffset = offset;
|
|
anyProcessed = true;
|
|
}
|
|
|
|
if (!anyProcessed) offset++;
|
|
}
|
|
while (offset < rcvdBytesCount);
|
|
|
|
/// Remove processed data from the receive buffer
|
|
for (int i = lastValidOffset; i < rcvdBytesCount; i++)
|
|
{
|
|
rcvdData[i - lastValidOffset] = rcvdData[i];
|
|
}
|
|
rcvdBytesCount -= lastValidOffset;
|
|
|
|
benchModelRoute = RRoute;
|
|
///
|
|
if (!IsUIBlocked && bitsChangedByUI != 0)
|
|
{
|
|
benchModelRoute = (benchModelRoute & ~bitsChangedByUI) | (displayedRoutePlusUI & bitsChangedByUI);
|
|
bitsChangedByUI = 0;
|
|
}
|
|
|
|
log.DebugFormat("Route synced with RRoute and UI changes, new route = {0}", Utils.RouteToStr(benchModelRoute));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send data to the control board serial port
|
|
/// </summary>
|
|
public void RunDeviceAfter()
|
|
{
|
|
if (cbCfg.DebugLevel != DebugMode.Normal) return;
|
|
|
|
|
|
/// Synchronize benchModelRoute with RRoute
|
|
if (!IsUIBlocked && bitsChangedByUI != 0)
|
|
{
|
|
benchModelRoute = (benchModelRoute & ~bitsChangedByUI) | (displayedRoutePlusUI & bitsChangedByUI);
|
|
bitsChangedByUI = 0;
|
|
}
|
|
displayedRoute = Route;
|
|
displayedRoutePlusUI = Route;
|
|
bitsChangedByUI = 0;
|
|
ProcessData.UpdateMeasuredValuesAndSetpoints();
|
|
Bridge.OnStateMachineTick(this, new StateMachineTickEventArgs(displayedRoute, ProcessData.MsrmntAvailableFlags, ProcessData.MeasuredValues,
|
|
ProcessData.AltStrings, ProcessData.Setpoints));
|
|
|
|
/// Make sure the serial port is open
|
|
if (serialPort == null || !serialPort.IsOpen)
|
|
{
|
|
/// Serial port is closed or does not exist => re-open the serial port
|
|
try
|
|
{
|
|
if (serialPort == null)
|
|
{
|
|
serialPort = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One);
|
|
}
|
|
serialPort.DtrEnable = true;
|
|
serialPort.WriteTimeout = 4000;
|
|
serialPort.ParityReplace = 0xFF;
|
|
serialPort.Open();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
/// Re-opening serial port failed
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!outputsInitialized || (outputLatch & routeMask) != (benchModelRoute & routeMask)) /// || (StateMachine.Time - lastStateMachineTime) >= 15)
|
|
{
|
|
var actionsToModify = actionQueue.Where(x => (x.ActionId == ActionID.ChangeRoute));
|
|
bool oneModified = false;
|
|
foreach (var a in actionsToModify)
|
|
{
|
|
if (oneModified)
|
|
{
|
|
log.ErrorFormat("ChangeRoute !!! TWO actions of the same type in the queue");
|
|
break; /// Do not modify more actions, this should never happen
|
|
}
|
|
|
|
oneModified = true;
|
|
a.Route = (ulong)benchModelRoute;
|
|
log.InfoFormat("ChangeRoute(route={0:X}) ... an action in the queue modified", benchModelRoute);
|
|
}
|
|
|
|
if (!oneModified)
|
|
{
|
|
actionQueue.Enqueue(Action.ChangeRoute(false, (ulong)benchModelRoute));
|
|
log.InfoFormat("ChangeRoute(route={0:X}) ... a new action enqueued", (ulong)benchModelRoute);
|
|
}
|
|
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
|
|
var combinedAction = Action.FetchNonConflictingActions(actionQueue); /// Default action is RequestDataOnly
|
|
byte[] outData = OutputMessage.GetMessage(combinedAction, config, valvesToInvert);
|
|
if (outData != null && outData.Length > 0)
|
|
{
|
|
if ((combinedAction.ActionId & ActionID.ChangeRoute) != 0)
|
|
{
|
|
outputsInitialized = true;
|
|
outputLatch = combinedAction.Route;
|
|
lastStateMachineTime = StateMachine.Time;
|
|
}
|
|
|
|
serialPort.Write(outData, 0, outData.Length);
|
|
|
|
string s = Telegram.LogTelegram("Sent: ", outData);
|
|
Debug.WriteLine(s);
|
|
log.Debug(s);
|
|
}
|
|
}
|
|
|
|
public void StopDevice()
|
|
{
|
|
if (serialPort != null) serialPort.Close();
|
|
serialPort = null;
|
|
}
|
|
|
|
public void StopDevice2()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IControlBoard interface
|
|
|
|
///
|
|
/// Interface functions
|
|
///
|
|
public void ClearProcessValues() { }
|
|
|
|
public double TestTimeWM(int wmNr1)
|
|
{
|
|
if (wmNr1 >= 1 && wmNr1 < Data.ImpulseTime.Length)
|
|
{
|
|
return Data.ImpulseTime[wmNr1];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public int RefPulsesWM(int wmNr1)
|
|
{
|
|
if (wmNr1 >= 1 && wmNr1 < Data.EtPulses.Length)
|
|
{
|
|
return Data.EtPulses[wmNr1];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public int PulsesWM(int wmNr1)
|
|
{
|
|
if (wmNr1 >= 1 && wmNr1 < Data.WMeterPuls.Length)
|
|
{
|
|
return Data.WMeterPuls[wmNr1];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from Start() and Run() functions of SetValvesOp, i.e. from State.RunOperations().
|
|
/// </summary>
|
|
/// <param name="valvesToOpen">Bits of valves to be open are set</param>
|
|
/// <param name="valvesToClose">Bits of valves to be closed are set</param>
|
|
/// <param name="logicalFnValves">A list of logical function valves (to be calculated and applied afterwards)</param>
|
|
/// <returns>Bits of changed valves are set in the return value</returns>
|
|
public UInt128 SetValves(UInt128 valvesToOpen, UInt128 valvesToClose, IList<BuiltIn.ValveEx.Valve> logicalFnValves)
|
|
{
|
|
UInt128 oldRoute = benchModelRoute;
|
|
benchModelRoute = (benchModelRoute | valvesToOpen) & (~valvesToClose);
|
|
foreach (var logFnV in logicalFnValves) logFnV.UpdateRoute(ref benchModelRoute);
|
|
log.DebugFormat("SetValves(x,x), new route = {0}", Utils.RouteToStr(benchModelRoute));
|
|
UInt128 retVal = oldRoute ^ benchModelRoute;
|
|
|
|
log.DebugFormat("{0}.SetValves({1}, {2}) returns {3}", Name, valvesToOpen.ToString("X"), valvesToClose.ToString("X"), retVal.ToString("X"));
|
|
return retVal;
|
|
}
|
|
|
|
///
|
|
/// Operations
|
|
///
|
|
public IOperation SetFlowOp(double flowLimLo, double flowLimHi, TBF.Boxes.DoubleBox measuredFlow, int timeout)
|
|
{
|
|
return Devices.RegValve.SetFlowOp(Devices.FlowMeter, flowLimLo, flowLimHi, measuredFlow, timeout);
|
|
}
|
|
|
|
public IOperation StopFlowRegulationOp()
|
|
{
|
|
Stop(false);
|
|
//scheduledActionQueue.Enqueue(Action.Stop());
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a standing start-stop test
|
|
/// </summary>
|
|
/// <returns>Operation running a test</returns>
|
|
public IOperation StandingStartStopTestOp(Test test, double qFrom, double qTo, int pulsesCount)
|
|
{
|
|
return new StandingStartStopTestOp(this, devices, qFrom, qTo, pulsesCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a flying start-stop test
|
|
/// </summary>
|
|
/// <returns>Operation running a test</returns>
|
|
public IOperation FlyingStartStopTestOp(Test test, double qFrom, double qTo, int pulsesCount)
|
|
{
|
|
return new FlyingStartStopTestOp(this, devices, qFrom, qTo, pulsesCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a flying start-stop test with diverter
|
|
/// </summary>
|
|
/// <returns>Operation running a test</returns>
|
|
public IOperation FlyingStartStopTestWithDivOp(Test test, double qFrom, double qTo, int pulsesCount,
|
|
int massPulsesCount = 0, bool readDivTransition = true)
|
|
{
|
|
return new FlyingStartStopTestWithDivOp(this, devices, qFrom, qTo, pulsesCount, massPulsesCount, readDivTransition);
|
|
}
|
|
|
|
public IOperation ReadDiverterTransitionOp(bool isStart, Sequences.Statistics plotter, int batchNr, string testName, int repetition)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns operation with events: Event.MeasurementCompleted or Event.None
|
|
/// </summary>
|
|
/// <returns>Operation</returns>
|
|
public IOperation QueryMeasurementEndOp()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public IOperation StopPreviousOp()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IValveControl interface
|
|
|
|
/// <summary>
|
|
/// Open/close one valve.
|
|
/// Events: ValvesSet, ValvesBusy, Error
|
|
/// </summary>
|
|
/// <param name="valveToOpen">Valve to be opened</param>
|
|
/// <param name="valveToClose">Valve to be closed</param>
|
|
/// <returns>Newly created operation instance reference casted to IOperaton</returns>
|
|
public IOperation SetValvesOp(IValve valveOpen, IValve valveClose)
|
|
{
|
|
return new BuiltIn.SetValvesOp(this, valveOpen, valveClose);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open/close multiple valves.
|
|
/// Events: ValvesSet, ValvesBusy, Error
|
|
/// </summary>
|
|
/// <param name="valvesToOpen">A list of valves to be opened</param>
|
|
/// <param name="valvesToClose">A list of valves to be closed</param>
|
|
/// <returns>Newly created operation instance reference casted to IOperaton</returns>
|
|
public IOperation SetValvesOp(IList<IValve> valvesOpen, IList<IValve> valvesClose)
|
|
{
|
|
return new BuiltIn.SetValvesOp(this, valvesOpen, valvesClose);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open start/stop valve.
|
|
/// Events: ValvesSet, ValvesBusy, Error
|
|
/// </summary>
|
|
/// <returns>Created operation</returns>
|
|
public IOperation OpenStartValveOp()
|
|
{
|
|
return new BuiltIn.SetValvesOp(this, true, devices);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close start/stop valve.
|
|
/// Events: ValvesSet, ValvesBusy, Error
|
|
/// </summary>
|
|
/// <returns>Created operation</returns>
|
|
public IOperation CloseStartValveOp()
|
|
{
|
|
return new BuiltIn.SetValvesOp(this, false, devices);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void MeasureFlow(bool isFromUI, int flowMeterId)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
actionQueue.Enqueue(Action.MeasureFlow(isFromUI, flowMeterId));
|
|
log.InfoFormat("Enqueue( MeasureFlow(fm={0}) )", flowMeterId);
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
/// <summary>
|
|
/// To be used with a regulation valve controlled by incremental pulses.
|
|
/// Stability time is fixed: 200 ms
|
|
/// </summary>
|
|
public void SetFlow(bool isFromUI, int regVId, double freqLo, double freqHi)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
actionQueue.Enqueue(Action.SetFlow(isFromUI, regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)), 200));
|
|
log.InfoFormat("Enqueue( SetFlow(rv={0}, fLo={1}, fHi={2}, pid={3}) )", regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)));
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
public void StartTest(bool isFromUI, int flowMId, int divId, int divThreshold,
|
|
bool isSyncMethod, bool isDivUsed, bool isStartStop, bool isProlonged,
|
|
int totalPulsesCount, int massPulsesCount = -1)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
actionQueue.Enqueue(Action.StartTest(isFromUI, flowMId, divId, divThreshold,
|
|
isSyncMethod, isDivUsed, isStartStop, isProlonged,
|
|
totalPulsesCount, massPulsesCount));
|
|
log.InfoFormat("Enqueue( StartTest(fm={0}, div={1}, Sync={2}, withDiv={3}, s/s={4}, prolonged={5} pulsesCount={6} massPulsesCount={7}) )",
|
|
flowMId, divId, isSyncMethod, isDivUsed, isStartStop, isProlonged, totalPulsesCount, massPulsesCount);
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
public void Stop(bool isFromUI)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
actionQueue.Enqueue(Action.Stop(isFromUI));
|
|
log.InfoFormat("Enqueue( Stop() )");
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Perform incremental movement of a regulation valve
|
|
/// </summary>
|
|
/// <param name="rvId">Reg. valve ID</param>
|
|
/// <param name="time">Time in seconds, positive value opens the reg. valve</param>
|
|
public void RegVlvIncrMove(bool isFromUI, int regVId, double time)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
var newAction = Action.RegVlvIncrMove(isFromUI, regVId, time);
|
|
|
|
var actionsToModify = actionQueue.Where(x => (x.ActionId == ActionID.RegVlvIncrMove && x.RegVId == newAction.RegVId));
|
|
bool oneModified = false;
|
|
foreach (var a in actionsToModify)
|
|
{
|
|
if (oneModified)
|
|
{
|
|
log.ErrorFormat("RegVlvIncrMove !!! TWO actions of the same type in the queue");
|
|
break; /// Do not modify more actions, this should never happen
|
|
}
|
|
|
|
a.RVMovePar1 += newAction.RVMovePar1;
|
|
a.RVMovePar2 += newAction.RVMovePar2;
|
|
oneModified = true;
|
|
log.InfoFormat("RegVlvIncrMove(UI={0}, RV={1}, time={2}) ... an action in the queue modified", isFromUI, regVId, time);
|
|
}
|
|
|
|
if (!oneModified)
|
|
{
|
|
actionQueue.Enqueue(newAction);
|
|
log.InfoFormat("RegVlvIncrMove(UI={0}, RV={1}, time={2}) ... a new action enqueued", isFromUI, regVId, time);
|
|
}
|
|
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Move a regulation valve to specfied position
|
|
/// </summary>
|
|
/// <param name="rvId">Reg. valve ID</param>
|
|
/// <param name="position">Reg. valve position (0 .. 1.0)</param>
|
|
public void RegVlvMoveToPos(bool isFromUI, int regVId, double positionLo, double positionHi = -1)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
actionQueue.Enqueue(Action.RegVlvMoveToPos(isFromUI, regVId, positionLo, positionHi));
|
|
log.InfoFormat("Enqueue( RegVlvMoveToPos(rv={0}, positionLo={1}, positionHi={2}) )", regVId, positionLo, positionHi);
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
public void SwitchDiverter(bool isFromUI, int divNr1, bool toTank)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
actionQueue.Enqueue(Action.SwitchDiverter(isFromUI, divNr1, toTank));
|
|
log.InfoFormat("Enqueue( SwitchDiverter(div={0}, toTank={1}) )", divNr1, toTank);
|
|
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
|
}
|
|
|
|
public void ReadDiverterTransition(bool isFromUI, int divNr1)
|
|
{
|
|
if (IsUIBlocked && isFromUI) return;
|
|
|
|
/// TODO
|
|
}
|
|
|
|
public void SetFiltersPidShortPulses(int[] filters, float pidCoef, int shortPulses)
|
|
{
|
|
/// TODO
|
|
}
|
|
}
|
|
}
|