404 lines
15 KiB
C#
404 lines
15 KiB
C#
///
|
|
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO.Ports;
|
|
using log4net;
|
|
using Common;
|
|
using Config.Entities;
|
|
using Dirichlet.Numerics;
|
|
using TBF.BenchControl.GenericDevices;
|
|
|
|
namespace TBF.BenchControl.ControlBoard.Uni
|
|
{
|
|
public class CBoard : ComponentBase, IControlBoard
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(CBoard));
|
|
public override string ToString() { return string.Format("CBoard({0})", cBoardCfg != null ? cBoardCfg.ToString(1) : string.Empty); }
|
|
|
|
/// TBF component configuration
|
|
readonly CBoardCfg cBoardCfg;
|
|
int comPortNr { get { return cBoardCfg.ComPortNr; } }
|
|
int virtValvesRngLo { get { return cBoardCfg.VirtValvesRangeLo; } }
|
|
int virtValvesRngHi { get { return cBoardCfg.VirtValvesRangeHi; } }
|
|
|
|
/// Propagated from configurations of other components
|
|
UInt128 valvesToInvert;
|
|
|
|
/// 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
|
|
|
|
/// Message queue
|
|
Queue<IOutputMessage> msgQueue;
|
|
|
|
/// Received data
|
|
GeneralData generalData;
|
|
DivTransitionData divTransitionData;
|
|
ScopeAnalyzerData scopeAnalyzerData;
|
|
SwitchCountersData switchCounterData;
|
|
|
|
|
|
UInt128 benchModelRoute;
|
|
public UInt128 Route { set; get; } /// State of valves, test bench route
|
|
///
|
|
public UInt64 DigitalInputs { set; get; } /// Inputs
|
|
|
|
/// Values valid during a test
|
|
public double RefFrequency { set; get; }
|
|
public double RefFlow { set; get; }
|
|
|
|
/// Last test results
|
|
public double TestTime { set; get; }
|
|
public int RefPulses { set; get; }
|
|
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; get; }
|
|
|
|
|
|
/// <summary>
|
|
/// Constructor for user interfaces (change settings only).
|
|
/// Initialize(), RunDeviceXY() and StopDevice() methods of the created object will not be called.
|
|
/// </summary>
|
|
public CBoard()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor invoked when starting the test bench and creating components.
|
|
/// </summary>
|
|
public CBoard(Generic.IComponentCfg cfg)
|
|
: base(cfg)
|
|
{
|
|
#if DEBUG
|
|
Debug.WriteLine("");
|
|
Debug.WriteLine("Uni CB enums");
|
|
Debug.WriteLine("");
|
|
foreach (var e in Enum.GetValues(typeof(Poz)))
|
|
{
|
|
Debug.WriteLine(string.Format("{0} ... {1}", e, ((int)e)));
|
|
}
|
|
#endif
|
|
cBoardCfg = cfg as CBoardCfg;
|
|
|
|
generalData = new GeneralData();
|
|
divTransitionData = null;
|
|
scopeAnalyzerData = null;
|
|
switchCounterData = null;
|
|
|
|
msgQueue = new Queue<IOutputMessage>();
|
|
|
|
serialPort = null;
|
|
buffer = new byte[BUFFER_SIZE];
|
|
rcvdData = new byte[BUFFER_SIZE];
|
|
cumulativeSums = new int[BUFFER_SIZE];
|
|
rcvdBytesCount = 0;
|
|
|
|
log.Warn(this.ToString());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specify inverted valves
|
|
/// </summary>
|
|
/// <param name="valvesToInvert"></param>
|
|
public void SpecifyInvertedValves(UInt128 valvesToInvert)
|
|
{
|
|
this.valvesToInvert = valvesToInvert;
|
|
this.benchModelRoute = valvesToInvert;
|
|
}
|
|
|
|
#region IDevice interface
|
|
|
|
/// <summary>
|
|
/// Initialize control board device hardware
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
if (cBoardCfg.DebugLevel != DebugMode.Normal)
|
|
{
|
|
log.FatalFormat("{0} - Device simulated", Name);
|
|
return;
|
|
}
|
|
|
|
serialPort = new SerialPort(string.Format("COM{0}", comPortNr), 9600, Parity.None, 8, StopBits.One);
|
|
serialPort.DtrEnable = true;
|
|
serialPort.WriteTimeout = 4000;
|
|
serialPort.ParityReplace = 0xFF;
|
|
serialPort.Open();
|
|
|
|
log.FatalFormat("{0} - Device successfully initialized", Name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reecive and process data from the control board serial port
|
|
/// </summary>
|
|
public void RunDeviceBefore()
|
|
{
|
|
if (cBoardCfg.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
|
|
generalData.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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send data to the control board serial port
|
|
/// </summary>
|
|
public void RunDeviceAfter()
|
|
{
|
|
if (cBoardCfg.DebugLevel != DebugMode.Normal) return;
|
|
|
|
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(string.Format("COM{0}", comPortNr), 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 (msgQueue.Count > 0)
|
|
{
|
|
/// There is a message in the message queue => write it to the serial port
|
|
var message = msgQueue.Dequeue();
|
|
byte[] outData = message.GetMessage();
|
|
serialPort.Write(outData, 0, outData.Length);
|
|
|
|
string s1 = message.ToString();
|
|
string s2 = Telegram.LogTelegram("Message ", outData);
|
|
Debug.WriteLine(s1);
|
|
Debug.WriteLine(s2);
|
|
log.Debug(s1);
|
|
log.Debug(s2);
|
|
}
|
|
}
|
|
|
|
public void StopDevice()
|
|
{
|
|
if (serialPort != null) serialPort.Close();
|
|
serialPort = null;
|
|
}
|
|
|
|
public void StopDevice2()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void SendCommand(Command cmd, int etalonNr, int regValveNr, int divNr, ulong totalPulsesCount, ulong massPuleseCount, UInt128 route)
|
|
{
|
|
msgQueue.Enqueue(new CommandMsg(cmd, etalonNr, regValveNr, divNr, totalPulsesCount, massPuleseCount, route));
|
|
}
|
|
|
|
public void ValveMove(Command cmd, int etalonNr, int regValveNr, int divNr, ulong totalPulsesCount, ulong massPuleseCount, UInt128 route)
|
|
{
|
|
msgQueue.Enqueue(new CommandMsg(cmd, etalonNr, regValveNr, divNr, totalPulsesCount, massPuleseCount, route));
|
|
}
|
|
|
|
#region IControlBoard interface
|
|
|
|
///
|
|
/// Interface functions
|
|
///
|
|
public void UpdateUI(string sParam, int iParam, double value) { }
|
|
public void ClearProcessValues() { }
|
|
public double TestTimeWM(int wmNr1) { return 0; }
|
|
public int RefPulsesWM(int wmNr1) { return 0; }
|
|
public int PulsesWM(int wmNr1) { return 0; }
|
|
public UInt128 SetValves(UInt128 valvesToOpen, UInt128 valvesToClose, IList<BuiltIn.ValveEx.Valve> extendedValves) { return 0; }
|
|
|
|
///
|
|
/// Operations
|
|
///
|
|
public IOperation SetFlowOp(double flowLimLo, double flowLimHi, TBF.Boxes.DoubleBox measuredFlow, int timeout)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public IOperation StopFlowRegulationOp()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a test, Events: MeasurementStarted
|
|
/// </summary>
|
|
/// <param name="iParam">Int32 parameter 1</param>
|
|
/// <param name="dParam1">Double parameter 1 (alternative lower flow limit)</param>
|
|
/// <param name="dParam2">Double parameter 2 (alternative upper flow limit)</param>
|
|
/// <returns>Operation</returns>
|
|
public IOperation StartTestOp(Test test, int iParam = 0, double dParam1 = 0, double dParam2 = 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
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 valveToOpen, IValve valveToClose)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <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> valvesToOpen, IList<IValve> valvesToClose)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open start/stop valve.
|
|
/// Events: ValvesSet, ValvesBusy, Error
|
|
/// </summary>
|
|
/// <returns>Created operation</returns>
|
|
public IOperation OpenStartValveOp()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close start/stop valve.
|
|
/// Events: ValvesSet, ValvesBusy, Error
|
|
/// </summary>
|
|
/// <returns>Created operation</returns>
|
|
public IOperation CloseStartValveOp()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|