tbf/TBF/Rig/ControlBoard/Uni/UniCB.cs
Michal Buzik 2b3e0dfa32 Remove SharedComponents + UNI shows Serial Nr
Remove `SharedComponents` references and legacy `LiveLogCache` logic:

- Eliminate unused `SharedComponents` references across the solution to streamline dependencies.
- Comment out `LiveLogCache` interactions in multiple modules, transitioning to alternative or undefined logging mechanisms.
- Add optional `regReadersOptional` parameters to `ShowCycleBeginFormOp` methods for improved flexibility.
- Introduce `ITestMethodSmart` interface to support smart reader functionality.
- Add new `LogCacheAppender` configuration to `log4netConfig.xml` for diagnostic use.
- Update project files to remove outdated references and include newly introduced files.
2026-02-23 15:55:24 +01:00

985 lines
41 KiB
C#

///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using log4net;
using Common;
using Config.Entities;
using Dirichlet.Numerics;
using SchematicDrawing;
using TBF.Boxes;
using TBF.Rig.GenericDevices;
using TBF.Rig.Sequences;
using TBF.UiBridge;
using AppDiagnostic;
namespace TBF.Rig.ControlBoard.Uni
{
public class UniCB : ComponentBase, IAdvancedControlBoard, SchematicDrawing.IDrawingItCmpntWithMeasuredVal
{
private static readonly ILog log = LogManager.GetLogger(typeof(UniCB));
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
/// TBF component configuration and useful wrappers
readonly UniCBCfg cbCfg;
string comPort { get { return string.Format("COM{0}", cbCfg.ComPortNr); } }
public DivResolution DivResolution { get; set; }
public int DivResolutionMs
{
get
{
switch (DivResolution)
{
case DivResolution._1ms: return 1;
default:
case DivResolution._2ms: return 2;
case DivResolution._4ms: return 4;
case DivResolution._8ms: return 8;
}
}
}
short config { get { return (short)((cbCfg.DrainValvesCount << 8) + ((int)DivResolution << 6) + ((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 {
if (cbCfg != null && cbCfg.ShowState)
{
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", Utils.ToBin40(DigitalInputs));
altString.AppendFormat("DO: {0}\n", Utils.ToBin40((ulong)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();
}
return string.Empty;
}
}
///
/// Serial communication
///
SerialPort serialPort; /// Seral port or null, used in DebugMode.Normal, check serialPort.IsOpen before using it
TextReader textReader; /// Used instead of serialPort in DebugMode.Simulate
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; /// Cumulative sums to verify checksums easily
int rcvdBytesCount; /// Count of valid bytes in rcvdData buffer
int age0BytesCount; /// Just received bytes count
int age1BytesCount; /// Count of bytes received 1 second ago
DateTime lastSentTime; /// Time stamp of the last sent command/request frame
DateTime lastReceivedTime; /// Time stamp of the last received valid frame
int lastRcvdStMTime; /// StateMachine.Time time stamp of the last received valid frame
bool outputsInitialized; /// Flag indicating that outputs were sent at least once
///
/// Received data
///
public GeneralData Data;
DivTransitionData divTransitionData;
ScopeAnalyzerData scopeAnalyzerData;
SwitchCountersData switchCounterData;
public UInt64 State { get { return Data.State; } } /// Control board state
public Activity Activity { get { return activity; } } /// Control board activity
Activity activity;
public void SetActivity(Activity a) { activity = a; }
/// Quido RS
TBF.Rig.Modbus.QuidoRS.QuidoRS quidoRS;
/// 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];
}
else return 0;
}
///
/// Digital outputs / Route / States of electromagnetic valves
///
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
UInt128 invInputsMask; /// Typically 0xff000000 0000ffff ffffffff ffffffff, inputs are mapped to route bits 80..119
UInt128 valvesToInvert; /// From configurations of valve and pump components. Inversion is applied when creating
/// a message to be sent by serial port
UInt128 displayedRoutePlusUI; /// A route displayed on the screen plus the modifications from UI (inversions are not applied)
UInt128 bitsChangedByUI; /// Ones in this word indicate which valves/pumpes were changed in UI by mouse clicks
UInt128 outputLatch; /// A copy of data sent to physical digital outputs (inversions are not applied)
///
/// 128-bit word representing the current state of all valves and pumps (inversions are not applied)
///
UInt128 route;
public UInt128 Route { get { return route; } }
///
/// Queue of higher level commands for the control board
///
Queue<Action> actionQueue;
bool isEmergencyStop;
/// UI actions are blocked when true
public bool IsUIBlocked { get; set; }
/// Measured 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]; } }
/// Measured values valid during and after a test
public int RefPulses { get { return Data.EtPulses[0]; } }
public int RefPulses1 { get { return Data.EtPulses[0] - Data.EtPulses2 - Data.EtPulses3; } }
public int RefPulses2 { get { return Data.EtPulses2; } }
public int RefPulses3 { get { return Data.EtPulses3; } }
public int MassRefPulses { get { return Data.EtPulsesK; } }
/// Last test results
public double TestTime { get { return Data.Ttime; } }
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() { }
/// <summary>
/// Constructor invoked when starting the test bench and creating components.
/// </summary>
public UniCB(Generic.IComponentCfg cfg)
: base(cfg)
{
cbCfg = cfg as UniCBCfg;
Data = new GeneralData();
log.Warn(this.ToString());
}
/// <summary>
/// Specify inverted valves
/// </summary>
/// <param name="valvesToInvert"></param>
public void SpecifyInvertedValves(UInt128 valvesToInvert)
{
this.valvesToInvert = valvesToInvert;
}
#region IDevice interface
/// <summary>
/// Initialize control board device hardware
/// </summary>
public override void Initialize()
{
quidoRS = TbfComponents.FindComponent(cbCfg.QuidoRS) as TBF.Rig.Modbus.QuidoRS.QuidoRS;
devices = new OutputPath(string.Empty, 0);
actionQueue = new Queue<Action>();
actionQueue.Enqueue(Action.Stop(false)); /// Stop all
outputsInitialized = false;
divTransitionData = null;
scopeAnalyzerData = null;
switchCounterData = null;
routeMask = 0x000000ff1effff00;
if (cbCfg.RelaysFInstalled) routeMask |= 0x0000ff0000000000;
if (cbCfg.RelaysGInstalled) routeMask |= 0x00ff000000000000;
if (cbCfg.RelaysHInstalled) routeMask |= 0xff00000000000000;
if (cbCfg.RelaysIInstalled) routeMask |= 0x00000000000000ff;
for (int i = 4; i < cbCfg.RegValvesCount; i++) routeMask &= ~(((UInt128)3) << (2 * i));
for (int i = 1; i < cbCfg.DivertersCount; i++) routeMask &= ~(((UInt128)1) << (24 + i));
if (quidoRS != null) routeMask |= (((UInt128)0xffff) << 64);
invInputsMask = (UInt128)0xffffffffffffffffL;
invInputsMask |= (((UInt128)0xffff) << 64);
invInputsMask |= (((UInt128)0xff) << 120);
lastRcvdStMTime = StateMachine.Time; /// To indicate the serial connection state
lastSentTime = DateTime.Now;
lastReceivedTime = DateTime.Now;
/// 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;
lock (this)
{
bitsChangedByUI |= oneBitMask;
displayedRoutePlusUI = displayedRoutePlusUI ^ oneBitMask;
}
};
if (cbCfg.DebugLevel == DebugMode.Normal)
{
buffer = new byte[BUFFER_SIZE];
rcvdData = new byte[BUFFER_SIZE];
cumulativeSums = new int[BUFFER_SIZE];
rcvdBytesCount = 0;
age0BytesCount = 0;
age1BytesCount = 0;
serialPort = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One);
serialPort.DtrEnable = true;
serialPort.WriteTimeout = 4000;
serialPort.ParityReplace = 0xFF;
serialPort.Open();
if (!string.IsNullOrEmpty(cbCfg.QuidoRS) && quidoRS == null)
{
throw new Exception(string.Format("Cannot find Quido RS component named {0}", cbCfg.QuidoRS));
}
log.FatalFormat("{0} initialized, routeMask = {1}", Name, Utils.ToBin88(routeMask));
}
else if (cbCfg.DebugLevel == DebugMode.Replay)
{
buffer = new byte[BUFFER_SIZE];
rcvdData = new byte[BUFFER_SIZE];
cumulativeSums = new int[BUFFER_SIZE];
rcvdBytesCount = 0;
age0BytesCount = 0;
age1BytesCount = 0;
serialPort = null;
try
{
textReader = new StreamReader("C:\\TBF\\Simulate\\unicbsim.txt");
}
catch (Exception)
{
throw new Exception("Missing file C:\\TBF\\Simulate\\unicbsim.txt");
}
log.FatalFormat("{0} in replay mode, routeMask = {1}", Name, Utils.ToBin88(routeMask));
}
else
{
serialPort = null;
log.FatalFormat("{0} simulated: {1}", Name, this);
}
}
/// <summary>
/// Reecive and process data from the control board serial port
/// </summary>
public void RunDeviceBefore()
{
///
/// Read data from the control board
///
bool generalDataReceived = false;
if ((cbCfg.DebugLevel == DebugMode.Normal && serialPort != null && serialPort.IsOpen) ||
cbCfg.DebugLevel == DebugMode.Replay)
{
int age2BytesCount = age1BytesCount;
age1BytesCount = age0BytesCount;
if (cbCfg.DebugLevel == DebugMode.Normal)
{
age0BytesCount = serialPort.BytesToRead;
/// Read new received bytes and append them to an intermediate buffer 'buffer'
if (age0BytesCount > 0)
{
age0BytesCount = serialPort.Read(buffer, 0, Math.Min(age0BytesCount, BUFFER_SIZE));
}
}
else if (cbCfg.DebugLevel == DebugMode.Replay)
{
/// Simulation
age0BytesCount = 0;
string hexValues = textReader.ReadLine();
if (hexValues != null)
{
string[] hexValuesSplit = hexValues.Split(' ');
foreach (var hex in hexValuesSplit)
{
buffer[age0BytesCount++] = Convert.ToByte(hex, 16);
}
}
}
else
{
age0BytesCount = 0; /// No data
}
StringBuilder dataLog = new StringBuilder();
StringBuilder caption = new StringBuilder();
string summary = string.Format("{0}-{1}+{2}={3} bytes:", rcvdBytesCount, age2BytesCount, age0BytesCount,
rcvdBytesCount - age2BytesCount + age0BytesCount);
dataLog.Append(summary);
for (int i = 0; i < summary.Length; i++) caption.Append(" ");
/// Remove data 2 seconds old
for (int i = 0; i < age1BytesCount; i++)
{
rcvdData[i] = rcvdData[age2BytesCount + i];
dataLog.Append(string.Format(" {0:X2}", rcvdData[i]));
}
rcvdBytesCount -= age2BytesCount;
/// Append new received data (0 seconds old)
for (int i = 0; i < age0BytesCount && rcvdBytesCount < BUFFER_SIZE; i++, rcvdBytesCount++)
{
rcvdData[rcvdBytesCount] = buffer[i];
dataLog.Append(string.Format(" {0:X2}", 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;
string msg = string.Empty;
if (ScopeAnalyzerData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
{
/// Scope analyzer data received
scopeAnalyzerData = ScopeAnalyzerData.GetData(rcvdData, offset);
caption.Append(ScopeAnalyzerData.Caption());
offset += ScopeAnalyzerData.MessageLen;
anyProcessed = true;
}
else if (GeneralData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
{
/// Regular data received
generalDataReceived = true;
Data.UpdateData(rcvdData, offset, DivResolutionMs);
caption.Append(GeneralData.Caption());
offset += GeneralData.MessageLen;
anyProcessed = true;
}
else if (SwitchCountersData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
{
/// Switch counter data received
switchCounterData = SwitchCountersData.GetData(rcvdData, offset);
caption.Append(SwitchCountersData.Caption());
offset += SwitchCountersData.MessageLen;
anyProcessed = true;
}
else if (DivTransitionData.FrameFitsData(rcvdData, cumulativeSums, rcvdBytesCount, offset))
{
/// Diverter transition data received
divTransitionData = DivTransitionData.GetData(rcvdData, offset, DivResolutionMs);
caption.Append(DivTransitionData.Caption());
offset += DivTransitionData.MessageLen;
anyProcessed = true;
}
if (anyProcessed)
{
lastValidOffset = offset;
lastRcvdStMTime = StateMachine.Time;
lastReceivedTime = DateTime.Now;
}
else
{
caption.Append(" ");
offset++;
}
}
while (offset < rcvdBytesCount);
log.Debug(caption.ToString());
log.Debug(dataLog.ToString());
/// Remove processed data from the receive buffer
for (int i = lastValidOffset; i < rcvdBytesCount; i++)
{
rcvdData[i - lastValidOffset] = rcvdData[i];
}
rcvdBytesCount -= lastValidOffset;
if (lastValidOffset <= age1BytesCount)
{
age1BytesCount -= lastValidOffset;
}
else
{
age1BytesCount = 0;
age0BytesCount -= (lastValidOffset - age1BytesCount);
}
}
///
/// Combine data from the current route, from digital outputs and from UI
///
lock (this)
{
if (generalDataReceived) route = ((route & (((UInt128)0xffffffffffffffff) << 64)) | Data.Vystupy) ^ valvesToInvert;
///
if (!IsUIBlocked && bitsChangedByUI != 0)
{
route = (route & ~bitsChangedByUI) | (displayedRoutePlusUI & bitsChangedByUI);
bitsChangedByUI = 0;
}
}
///
/// Map Inputs bits 0..39 to Route bits 80..119
///
route = (route & invInputsMask) | ((UInt128)Data.Vstupy) << 80;
///
/// Map UniCB indication to Route bit BitNr (default 120)
///
if (cbCfg.DebugLevel == DebugMode.Normal && (StateMachine.Time - lastRcvdStMTime) >= 5)
{
/// No connection to control board ... set indicator bit
route |= ((UInt128)1) << cbCfg.BitNr;
}
///
/// Handle emergency stop signal
///
bool emergencyStop = ((Data.Vstupy & 0x10000) == 0);
///
if (emergencyStop && !isEmergencyStop)
{
TBF.UiBridge.Bridge.Ui2Bench(TBF.UiBridge.UI2BenchCmd.Stop); /// The same as pressing 'STOP' button in BenchControlPanel
}
///
isEmergencyStop = emergencyStop;
log.DebugFormat("Route synced with RRoute and UI changes, new route = {0}", Utils.ToBin88(route));
}
/// <summary>
/// Send data to the control board serial port
/// </summary>
public void RunDeviceAfter()
{
///
/// Combine data from the current route and from UI
///
lock (this)
{
if (!IsUIBlocked && bitsChangedByUI != 0)
{
route = (route & ~bitsChangedByUI) | (displayedRoutePlusUI & bitsChangedByUI);
bitsChangedByUI = 0;
}
displayedRoutePlusUI = route;
bitsChangedByUI = 0;
}
ProcessData.UpdateMeasuredValuesAndSetpoints();
Bridge.OnStateMachineTick(this, new StateMachineTickEventArgs(route, ProcessData.MsrmntAvailableFlags, ProcessData.MeasuredValues,
ProcessData.AltStrings, ProcessData.Setpoints, ProcessData.CustomBitmaps));
/// Make sure the serial port is open
if (cbCfg.DebugLevel == DebugMode.Normal && (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 (quidoRS != null) quidoRS.SetOutputs((route >> 64) & 0xFFFF);
if (!outputsInitialized || (outputLatch & routeMask) != (route & 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 = route;
log.InfoFormat("ChangeRoute(route={0:X}) ... an action in the queue modified", route);
}
if (!oneModified)
{
actionQueue.Enqueue(Action.ChangeRoute(false, (ulong)route));
log.InfoFormat("ChangeRoute(route={0:X}) ... a new action enqueued", (ulong)route);
}
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
var combinedAction = Action.FetchNonConflictingActions(actionQueue); /// Default action is RequestDataOnly
combinedAction.RegulationMinStep = devices.RegulMinStep;
byte[] outData = OutputMessage.GetMessage(combinedAction, config, (ulong)valvesToInvert);
if (outData != null && outData.Length > 0)
{
if ((combinedAction.ActionId & ActionID.ChangeRoute) != 0)
{
outputsInitialized = true;
outputLatch = combinedAction.Route;
if (cbCfg.DebugLevel != DebugMode.Normal)
{
Data.Vystupy = (ulong)(combinedAction.Route ^ valvesToInvert);
}
}
if ((combinedAction.ActionId & ActionID.StartTest) != 0)
{
Data.RestartReadingOfWMPulses = true;
}
if (cbCfg.DebugLevel == DebugMode.Normal)
{
serialPort.Write(outData, 0, outData.Length);
lastSentTime = DateTime.Now;
}
log.Debug(" " + OutputMessage.Caption());
log.Debug(Telegram.LogTelegram(string.Format("Sent {0}: ", lastSentTime.ToString("HH:mm:ss")), outData));
}
log.DebugFormat(" Queue content:");
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void StopDevice()
{
if (serialPort != null) serialPort.Close();
serialPort = null;
}
public void StopDevice2()
{
}
#endregion
#region IControlBoard interface
///
/// Interface functions
///
public void ClearProcessValues()
{
/// Called from ProcessData.ClearProcessValues() at the beginning of each test
SetActivity(Activity.Idle);
}
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 = route;
route = (route | valvesToOpen) & (~valvesToClose);
foreach (var logFnV in logicalFnValves) logFnV.UpdateRoute(ref route);
log.DebugFormat("SetValves(x,x), new route = {0}", Utils.ToBin40((ulong)route >> 8));
UInt128 retVal = oldRoute ^ route;
log.DebugFormat("{0}.SetValves({1}, {2}) returns {3}", Name, valvesToOpen.ToString("X"), valvesToClose.ToString("X"), retVal.ToString("X"));
return retVal;
}
///
/// Operations
///
/// <param name="delay">Delay of flow setting start in [s], default = 0</param>
public IOperation SetFlowOp(double flowLimLo, double flowLimHi, DoubleBox measuredFlow, int timeout)
{
return SetFlowOp(flowLimLo, flowLimHi, measuredFlow, timeout, 0);
}
public IOperation SetFlowOp(double flowLimLo, double flowLimHi, DoubleBox measuredFlow, int timeout, int delay)
{
return Devices.RegValve.SetFlowOp(Devices.FlowMeter, flowLimLo, flowLimHi, measuredFlow, timeout, delay);
}
public IOperation StopFlowRegulationOp()
{
StopAll(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, int pulsesCount, bool withDiverter)
{
return new StandingStartStopTestOp(this, devices, pulsesCount, withDiverter);
}
/// <summary>
/// Start a flying start-stop test (opitonally with a diverter, optionally prolonged)
/// </summary>
/// <returns>Operation running a test</returns>
public IOperation FlyingStartStopTestOp(Test test, double qFrom, double qTo, int pulsesCount, bool withDiverter = false,
bool isDelayedStart = false, bool isProlonged = false, int massPulsesCount = 0,
bool readDivTransition = false, Statistics plotterStart = null, Statistics plotterEnd = null)
{
return new FlyingStartStopTestOp(this, devices, qFrom, qTo, pulsesCount, withDiverter,
isDelayedStart, isProlonged, massPulsesCount, readDivTransition, plotterStart, plotterEnd);
}
/// <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 OpenStartValveOp(null, null); }
public IOperation OpenStartValveOp(DateTimeBox openTimeStamp, FloatBox switchTime)
{
return new BuiltIn.SetValvesOp(this, true, devices, openTimeStamp, switchTime);
}
/// <summary>
/// Close start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation CloseStartValveOp() { return CloseStartValveOp(null, null); }
public IOperation CloseStartValveOp(DateTimeBox closeTimeStamp, FloatBox switchTime)
{
return new BuiltIn.SetValvesOp(this, false, devices, closeTimeStamp, switchTime);
}
#endregion
public void MeasureFlow(bool isFromUI, int flowMeterId, int regulationMinStep = 0)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.MeasureFlow(isFromUI, flowMeterId, regulationMinStep));
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, int regulationMinStep = 0)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.SetFlow(isFromUI, regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)), 200, regulationMinStep));
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 isDelayedStart, bool isStartStop, bool isProlonged,
int totalPulsesCount, int massPulsesCount = -1)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.StartTest(isFromUI, flowMId, divId, divThreshold,
isSyncMethod, isDivUsed, isDelayedStart, 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 StopFlowControl(bool isFromUI, int regVId, int regulationMinStep = 0)
{
if (IsUIBlocked && isFromUI) return;
if (regVId < TBF.Rig.Uni.RegValveAnalog.RegValve.RVAnalogIdx)
{
actionQueue.Enqueue(Action.RegVlvStop(isFromUI, regVId, regulationMinStep));
log.InfoFormat("Enqueue( RegVlvStop(rv={0}) )", regVId);
}
else if (regVId == TBF.Rig.Uni.RegValveAnalog.RegValve.RVAnalogIdx)
{
int dacVal = Data.StavDA[0];
actionQueue.Enqueue(Action.RegVlvMoveToPos(isFromUI, regVId, dacVal, regulationMinStep));
log.InfoFormat("Enqueue( RegVlvMoveToPos(rv={0}, positionLo={1}) )", regVId, dacVal);
}
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void StopAll(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, int regulationMinStep = 0)
{
if (IsUIBlocked && isFromUI || regVId >= TBF.Rig.Uni.RegValveAnalog.RegValve.RVAnalogIdx) return;
var newAction = Action.RegVlvIncrMove(isFromUI, regVId, time, regulationMinStep);
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, int adcValLo, int adcValHi = -1, int regulationMinStep = 0)
{
if (IsUIBlocked && isFromUI || regVId > TBF.Rig.Uni.RegValveAnalog.RegValve.RVAnalogIdx) return;
actionQueue.Enqueue(Action.RegVlvMoveToPos(isFromUI, regVId, adcValLo, adcValHi, regulationMinStep));
log.InfoFormat("Enqueue( RegVlvMoveToPos(rv={0}, positionLo={1}, positionHi={2}) )", regVId, adcValLo, adcValHi);
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void SwitchDiverter(bool isFromUI, int divNr1, bool toTank)
{
if (IsUIBlocked && isFromUI) return;
if (toTank)
{
actionQueue.Enqueue(Action.StartTest(isFromUI, 0, divNr1, 127, true, true, false, false, false, int.MaxValue, 0));
}
else
{
actionQueue.Enqueue(Action.Stop(isFromUI));
}
log.InfoFormat("Enqueue( SwitchDiverter(div#={0}, toTank={1}) )", divNr1, toTank);
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void DelayStartOrStop(bool isFromUI)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.DelayStartOrStop(isFromUI));
log.InfoFormat("Enqueue( DelayStartOrStop() )");
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void GetDiverterTransitionData(bool isFromUI, TBF.Rig.Uni.Diverter.Diverter diverter, bool toTank,
Statistics plotter = null)
{
if (IsUIBlocked && isFromUI) return;
DivTransitionData.Diverter = diverter;
DivTransitionData.ToTank = toTank;
DivTransitionData.Plotter = plotter;
DivTransitionData.DiverterAssociationValidUntil = StateMachine.Time + 5;
actionQueue.Enqueue(Action.GetDiverterTransitionData(isFromUI));
log.InfoFormat("Enqueue( GetDiverterTransitionData(div#={0}, toTank={1}) )", diverter.DiverterNr, toTank);
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void GetScopeAnalyzerData(bool isFromUI)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.GetScopeAnalyzerData(isFromUI));
log.InfoFormat("Enqueue( GetScopeAnalyzerData() )");
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void ResetScopeAnalyzer(bool isFromUI)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.ResetScopeAnalyzer(isFromUI));
log.InfoFormat("Enqueue( ResetScopeAnalyzer() )");
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void GetSwitchCounterData(bool isFromUI)
{
if (IsUIBlocked && isFromUI) return;
actionQueue.Enqueue(Action.GetSwitchCounterData(isFromUI));
log.InfoFormat("Enqueue( GetSwitchCounterData() )");
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
}
public void SetFiltersPidShortPulses(int[] filters, float pidCoef, int shortPulses)
{
/// TODO
}
}
}