TM.iPerlCommunication and IperlHead synced with v.2.27, ISessionDataMngmnt replaces IHasClearSessionData and IHasTestName.
This commit is contained in:
@@ -99,9 +99,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
Count /// Number of flow states
|
||||
}
|
||||
|
||||
public enum OptoState
|
||||
public enum DataStreamState
|
||||
{
|
||||
Read,
|
||||
Flush,
|
||||
Flush = 0,
|
||||
ProcessAndSave,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,25 +11,26 @@ using Config.Entities;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.GenericDevices;
|
||||
using TBF.Rig.Output;
|
||||
using TBF.Rig.Sequences;
|
||||
|
||||
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
{
|
||||
/// <summary>
|
||||
/// This component = instance of this class is a placeholder for a combined main watermeter
|
||||
/// </summary>
|
||||
public class IperlHead : ComponentBase, IDevice, IRegReaderDatastream, IHasTestName, IHasClearSessionData, IOperation
|
||||
public class IperlHead : ComponentBase, IDevice, IRegReaderDatastream, ISessionDataMngmnt, IOperation
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
|
||||
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
||||
|
||||
#if TURA_SPECIAL
|
||||
public const int MaxOptoDataCount = 250000;
|
||||
public const int OptoDataBufferSize = 250000;
|
||||
#else
|
||||
public const int MaxOptoDataCount = 40000;
|
||||
public const int OptoDataBufferSize = 40000; /// Opto data count is not limitted by the buffer size
|
||||
#endif
|
||||
public const string OptoDataDirectory = "C:\\TBF\\ProcessData";
|
||||
public const int StartOptoDataCount = MaxOptoDataCount / 2;
|
||||
public const int EndOptoDataCount = MaxOptoDataCount - StartOptoDataCount;
|
||||
public const int StartOptoDataCount = OptoDataBufferSize / 2;
|
||||
public const int EndOptoDataCount = OptoDataBufferSize - StartOptoDataCount;
|
||||
public const int StartEndFilterSamplesCount2 = 20; /// StartEndFilterSamplesCount = 2 * StartEndFilterSamplesCount2 + 1
|
||||
public const int FeatureVectorSize = 9;
|
||||
|
||||
@@ -212,7 +213,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
/// <returns>Calculated Q2 correction factor</returns>
|
||||
public double CalculateQ2CorrectionFactor(Results.Entities.MeterTestRslt q2adjResult, double calibTarget, double nominalFlow, int currentFactor = 0)
|
||||
{
|
||||
double nominalTestFlowLph = Common.Units.ConvertTo(Common.Unit.lph, nominalFlow);
|
||||
double nominalTestFlowLph = Units.ConvertTo(Common.Unit.lph, nominalFlow);
|
||||
double volumeRefShiftedToTarget = q2adjResult.VolumeRef * (1.0 + calibTarget / 100.0);
|
||||
double q2adjErrorShiftedToTarget = Formulas.ErrorFromVolumes(q2adjResult.VolumeMeter, volumeRefShiftedToTarget);
|
||||
|
||||
@@ -273,18 +274,57 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Name set by the test, to be used as a part of the opto-data log file name </summary>
|
||||
public string TestName { set { testName = value; } }
|
||||
public int TestRepeats { set { testRepeats = value; } }
|
||||
public int RepetitionNr { set { repetitionNr = value; } }
|
||||
/// <summary>
|
||||
/// Store/update values to be used as a part of the opto-data log file name.
|
||||
/// Stop data stream processing and saving, if it is enabled.
|
||||
/// </summary>
|
||||
/// <param name="test">Currently executed test</param>
|
||||
/// <param name="repetitionNr">Currently executed repetition number</param>
|
||||
public void TestIsGoingToStartSoon(Test test, int repetitionNr)
|
||||
{
|
||||
/// Store/update values to be used as a part of the opto-data log file name
|
||||
testName = test.Name;
|
||||
testRepeats = test.Repeats;
|
||||
this.repetitionNr = repetitionNr;
|
||||
|
||||
if (IsDataStreamProcessing())
|
||||
{
|
||||
StopDataStreamProcessing();
|
||||
|
||||
/// Dummy '#### start test ####' and '#### end of test ####' marks are added
|
||||
/// to the raw data file on request of Joern Goege
|
||||
if (optoDataCount >= 100 && optoDataCount <= optoData.Length &&
|
||||
optoData[40].Flags == OptoTelegramFlags.OK &&
|
||||
optoData[optoDataCount - 40].Flags == OptoTelegramFlags.OK)
|
||||
{
|
||||
optoData[40].Flags = OptoTelegramFlags.OK_TestStart;
|
||||
optoData[optoDataCount - 40].Flags = OptoTelegramFlags.OK_TestEnd;
|
||||
}
|
||||
|
||||
DataStreamPostProcessing();
|
||||
|
||||
string pcbNr = (ConfigStruct != null) ? ConfigStruct.GetPcbNrString() : "UnknownPcbNr";
|
||||
string wmPosition = Name.Substring(5); /// WMPosition is extracted from a component name in form 'iPerl#'
|
||||
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
|
||||
string cycleStartTime = StateMachine.CycleStartTimeStamp.ToString("HH_mm_ss");
|
||||
///
|
||||
string relativeDirectory = Path.Combine(StateMachine.CycleStartTimeStamp.ToString("yy"),
|
||||
StateMachine.CycleStartTimeStamp.ToString("MM"),
|
||||
StateMachine.CycleStartTimeStamp.ToString("dd"));
|
||||
string directory = Path.Combine(OptoDataDirectory, relativeDirectory);
|
||||
string fileName = string.Format("{0}_{1}_{2}_{3}.txt", pcbNr, wmPosition, "WM", cycleStartTime);
|
||||
|
||||
if (SaveOptoDataToFile(directory, fileName))
|
||||
{
|
||||
extraDataPath = Path.Combine(relativeDirectory, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
///
|
||||
string testName;
|
||||
int testRepeats;
|
||||
int repetitionNr;
|
||||
|
||||
/// <summary> Name set by the test, to be used as a part of the opto-data log file name </summary>
|
||||
public string BenchName;
|
||||
|
||||
|
||||
///
|
||||
/// Indices to determine centers of start / end samples
|
||||
@@ -341,8 +381,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
|
||||
|
||||
OptoTelegramRaw[] optoData;
|
||||
int optoDataCount;
|
||||
string optoDataLogFileName;
|
||||
int optoDataCount; /// Real opto deta count, can be larger then optoData.Length
|
||||
///
|
||||
OptoTelegramRaw toBeFlushed;
|
||||
|
||||
@@ -363,26 +402,25 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
ClearSessionData();
|
||||
flowDirectionDetection = new FlowDirectionDetection();
|
||||
|
||||
/// Allocate memory for opto-data from iPerl
|
||||
optoData = new OptoTelegramRaw[MaxOptoDataCount];
|
||||
for (int i = 0; i < MaxOptoDataCount; i++)
|
||||
optoData = new OptoTelegramRaw[OptoDataBufferSize];
|
||||
for (int i = 0; i < OptoDataBufferSize; i++)
|
||||
{
|
||||
optoData[i] = new OptoTelegramRaw();
|
||||
}
|
||||
|
||||
toBeFlushed = new OptoTelegramRaw();
|
||||
|
||||
optoSerialPortParsingEnabled = false;
|
||||
dataStreamState = DataStreamState.Flush;
|
||||
synchronized = false;
|
||||
synchronized2 = false;
|
||||
partOfTelegram = string.Empty;
|
||||
|
||||
if (DebugLevel == DebugMode.Normal)
|
||||
{
|
||||
/// Prepare serial port
|
||||
/// Open serial port: 9600 Bd, 8 data bits, 1 stop bit, no parity
|
||||
string portName = string.Format("COM{0}", iperlHeadCfg.OptoComPortNr);
|
||||
optoSerialPort = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One);
|
||||
optoSerialPort.Handshake = Handshake.None;
|
||||
@@ -399,7 +437,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
/// <summary>
|
||||
/// Clear data related to a specific water meter
|
||||
/// </summary>
|
||||
public void ClearSessionData()
|
||||
public void StartSession()
|
||||
{
|
||||
ResultCode = 0;
|
||||
|
||||
@@ -418,12 +456,20 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
Q2ErrWOCorrection = 0;
|
||||
Q2CorrRL = 0;
|
||||
Q2CorrLR = 0;
|
||||
|
||||
/// Clear the data buffer
|
||||
optoDataCount = 0;
|
||||
|
||||
dataStreamState = DataStreamState.Flush;
|
||||
|
||||
currentFlowDir = InitFlowDir;
|
||||
if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection
|
||||
}
|
||||
|
||||
public void SaveMark(object mark)
|
||||
{
|
||||
/// TODO
|
||||
}
|
||||
|
||||
public void EndSession()
|
||||
{
|
||||
StopDataStreamProcessing();
|
||||
}
|
||||
|
||||
|
||||
@@ -462,7 +508,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
{
|
||||
try
|
||||
{
|
||||
ReadOptoData(optoSerialPortParsingEnabled ? OptoState.Read : OptoState.Flush);
|
||||
ReadOptoData(dataStreamState);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -520,137 +566,107 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
timestampSec = 0;
|
||||
timestampSec0 = 0;
|
||||
|
||||
/// Clear the feature vector
|
||||
for (int i = 0; i < FeatureVectorSize; i++) x[i] = 0;
|
||||
extraDataPath = null;
|
||||
|
||||
ReadPulses();
|
||||
/// Clear the feature vector
|
||||
for (int i = 0; i < FeatureVectorSize; i++)
|
||||
{
|
||||
x[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void TestCompleted()
|
||||
{
|
||||
/// TODO: Implement
|
||||
}
|
||||
|
||||
|
||||
int timeFromStart; /// [s] Time from test start to determine when the test start sample should be taken
|
||||
|
||||
/// <summary>Start this operation</summary>
|
||||
/// <summary>
|
||||
/// Start this operation
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
Clear();
|
||||
|
||||
/// Reset opto data, etc.
|
||||
timeFromStart = 0;
|
||||
optoDataCount = 0;
|
||||
currentTelegramIx = -1;
|
||||
startSampleAcquired = false;
|
||||
TestStartTelegramIx = 0;
|
||||
endTelegramIdx1 = 0;
|
||||
endTelegramIdx2 = 0;
|
||||
endTelegramIdx3 = 0;
|
||||
TestEndTelegramIx = 0;
|
||||
|
||||
|
||||
///
|
||||
/// Determine opto data file name: PCB_AA_BB_HH_MI_SS..txt
|
||||
///
|
||||
Results.Output.SensusTestInfo[] localTestInfos = Sequences.ProcessData.CompleteTestInfos;
|
||||
if (localTestInfos == null && StateMachine.Procedure != null)
|
||||
lock (this)
|
||||
{
|
||||
localTestInfos = Results.Output.SensusTestInfo.CreateFromProcedure(StateMachine.Procedure);
|
||||
Clear();
|
||||
ReadPulses();
|
||||
StartDataStreamProcessing();
|
||||
}
|
||||
///
|
||||
string fullTestName = Results.Utils.GetTestName(testName, testRepeats, repetitionNr);
|
||||
string qBezeichnungOpto = null; /// To be determined from TestInfo-s (if there are any)
|
||||
if (localTestInfos != null)
|
||||
{
|
||||
foreach (var ti in localTestInfos)
|
||||
{
|
||||
if ((ti.PruefungsNrOpto != 0) && (ti.TestName == fullTestName))
|
||||
{
|
||||
qBezeichnungOpto = string.IsNullOrEmpty(ti.QBezeichnungOpto) ? ti.PruefungsNrOpto.ToString("D2") : ti.QBezeichnungOpto;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (qBezeichnungOpto == null)
|
||||
{
|
||||
qBezeichnungOpto = fullTestName; /// No appropriate TestInfo found => set default opto data file name
|
||||
}
|
||||
///
|
||||
string pcbNr = (ConfigStruct != null) ? ConfigStruct.GetPcbNrString() : "UnknownPcbNr";
|
||||
string wmPosition = Name.Substring(5); /// WMPosition is extracted from a component name in form 'iPerl#'
|
||||
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
|
||||
string cycleStartTime = StateMachine.CycleStartTimeStamp.ToString("HH_mm_ss");
|
||||
///
|
||||
optoDataLogFileName = string.Format("{0}_{1}_{2}_{3}.txt", pcbNr, wmPosition, qBezeichnungOpto, cycleStartTime);
|
||||
|
||||
extraDataPath = null;
|
||||
|
||||
StartParsingOptoSerialPort();
|
||||
}
|
||||
|
||||
/// <summary>Run this operation</summary>
|
||||
/// <summary>
|
||||
/// Run this operation
|
||||
/// </summary>
|
||||
/// <returns>eventDone</returns>
|
||||
public Event Run()
|
||||
{
|
||||
timeFromStart += StateMachine.Period;
|
||||
ReadPulses();
|
||||
lock (this)
|
||||
{
|
||||
timeFromStart += StateMachine.Period;
|
||||
ReadPulses();
|
||||
|
||||
if (!startSampleAcquired && (timeFromStart >= 8) && (currentTelegramIx >= 0))
|
||||
{
|
||||
/// Take the test start sample
|
||||
startSampleAcquired = true;
|
||||
TestStartTelegramIx = currentTelegramIx;
|
||||
}
|
||||
else if (startSampleAcquired)
|
||||
{
|
||||
/// Shift data in pipelines
|
||||
TestEndTelegramIx = endTelegramIdx3;
|
||||
endTelegramIdx3 = endTelegramIdx2;
|
||||
endTelegramIdx2 = endTelegramIdx1;
|
||||
endTelegramIdx1 = currentTelegramIx;
|
||||
if (!startSampleAcquired && (timeFromStart >= 8) && (currentTelegramIx >= 0))
|
||||
{
|
||||
/// Take the test start sample
|
||||
startSampleAcquired = true;
|
||||
TestStartTelegramIx = currentTelegramIx;
|
||||
}
|
||||
else if (startSampleAcquired)
|
||||
{
|
||||
/// Shift data in pipelines
|
||||
TestEndTelegramIx = endTelegramIdx3;
|
||||
endTelegramIdx3 = endTelegramIdx2;
|
||||
endTelegramIdx2 = endTelegramIdx1;
|
||||
endTelegramIdx1 = currentTelegramIx;
|
||||
}
|
||||
}
|
||||
|
||||
return Event.ReadRegisterDone;
|
||||
}
|
||||
|
||||
/// <summary>Stop this operation</summary>
|
||||
/// <summary>
|
||||
/// Stop this operation
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
int startIx = BufferIdx(TestStartTelegramIx);
|
||||
int endIx = BufferIdx(TestEndTelegramIx);
|
||||
|
||||
log.WarnFormat("IperlHeadd.Stop() startIx={0} endIx={1} optoData.Len={2} filename={3}", startIx, endIx, optoData.Length, !string.IsNullOrEmpty(optoDataLogFileName) ? optoDataLogFileName : "<null>");
|
||||
|
||||
if ((startIx > 0) && (startIx < StartOptoDataCount) && (optoData[startIx].Flags == OptoTelegramFlags.OK))
|
||||
{
|
||||
optoData[startIx].Flags = OptoTelegramFlags.OK_TestStart;
|
||||
OptoTelegramRaw.TestStartTimestampDec = optoData[startIx].TimestampDec();
|
||||
}
|
||||
|
||||
if ((endIx > 0) && (optoData[endIx].Flags == OptoTelegramFlags.OK))
|
||||
{
|
||||
optoData[endIx].Flags = OptoTelegramFlags.OK_TestEnd;
|
||||
}
|
||||
|
||||
log.DebugFormat("Flow filtering start: {0:HH:mm:ss.fff}", DateTime.Now);
|
||||
|
||||
StopParsingOptoSerialPort();
|
||||
if (optoDataCount <= MaxOptoDataCount)
|
||||
{
|
||||
FIRFilterFlow(optoData, 0, optoDataCount - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
FIRFilterFlow(optoData, 0, StartOptoDataCount - 1);
|
||||
FIRFilterFlow(optoData, (optoDataCount - EndOptoDataCount), optoDataCount - 1);
|
||||
}
|
||||
|
||||
log.DebugFormat("Flow filtering end, feature vector calculation start: {0:HH:mm:ss.fff}", DateTime.Now);
|
||||
|
||||
Common.Utils.CalculateFeatureVector(optoData, optoDataCount, startIx, endIx, ref x);
|
||||
int startIx;
|
||||
int endIx;
|
||||
|
||||
log.DebugFormat("Feature vector calculation end, save opto-file start: {0:HH:mm:ss.fff}", DateTime.Now);
|
||||
lock (this)
|
||||
{
|
||||
StopDataStreamProcessing();
|
||||
AddTestStartEndMarksToData(out startIx, out endIx);
|
||||
}
|
||||
|
||||
extraDataPath = SaveOptoData();
|
||||
DataStreamPostProcessing();
|
||||
|
||||
log.DebugFormat("Save opto-file end: {0:HH:mm:ss.fff}", DateTime.Now);
|
||||
// TODO: Enable when calculations completed
|
||||
//
|
||||
// float[] offsetV, kOhmsR, kOhmsC, dutFlow, refFlow, flowRatio, magField, emfV;
|
||||
// x = Common.StatisticalMetrics.Calculate(optoData, optoDataCount, startIx, endIx, true,
|
||||
// out offsetV, out kOhmsR, out kOhmsC, out dutFlow,
|
||||
// out refFlow, out flowRatio, out magField, out emfV);
|
||||
//
|
||||
// log.DebugFormat("Feature vector calculation end, save opto-file start: {0:HH:mm:ss.fff}", DateTime.Now);
|
||||
|
||||
string relativeDirectory = Path.Combine(StateMachine.CycleStartTimeStamp.ToString("yy"),
|
||||
StateMachine.CycleStartTimeStamp.ToString("MM"),
|
||||
StateMachine.CycleStartTimeStamp.ToString("dd"));
|
||||
string directory = Path.Combine(OptoDataDirectory, relativeDirectory);
|
||||
string fileName = DetermineExtraDataFileName();
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
if (SaveOptoDataToFile(directory, fileName))
|
||||
{
|
||||
extraDataPath = Path.Combine(relativeDirectory, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
log.WarnFormat("IperlHeadd.Stop() startIx={0} endIx={1} optoData.Len={2} filename={3}", startIx, endIx, optoData.Length, !string.IsNullOrEmpty(fileName) ? fileName : "<null>");
|
||||
|
||||
if (TestStartTelegramIx == 0 || optoDataCount < 100)
|
||||
{
|
||||
@@ -662,73 +678,163 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save opto data to a file.
|
||||
/// </summary>
|
||||
string SaveOptoData()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(optoDataLogFileName))
|
||||
void AddTestStartEndMarksToData(out int startIx, out int endIx)
|
||||
{
|
||||
startIx = BufferIdx(TestStartTelegramIx);
|
||||
if ((startIx > 0) && (startIx < StartOptoDataCount) && (optoData[startIx].Flags == OptoTelegramFlags.OK))
|
||||
{
|
||||
string relativeDirectory = Path.Combine(StateMachine.CycleStartTimeStamp.ToString("yy"),
|
||||
StateMachine.CycleStartTimeStamp.ToString("MM"),
|
||||
StateMachine.CycleStartTimeStamp.ToString("dd"));
|
||||
string fullFileName = Path.Combine(OptoDataDirectory, relativeDirectory, optoDataLogFileName);
|
||||
|
||||
log.WarnFormat("Saving {0} opto data to {1}", Name, fullFileName);
|
||||
|
||||
try
|
||||
optoData[startIx].Flags = OptoTelegramFlags.OK_TestStart;
|
||||
OptoTelegramRaw.TestStartTimestampDec = optoData[startIx].TimestampDec();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int ix = 0; ix < StartOptoDataCount; ix++)
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(OptoDataDirectory, relativeDirectory));
|
||||
|
||||
double scalFact = ScalingFactor();
|
||||
|
||||
using (TextWriter optoLogFile = new StreamWriter(fullFileName))
|
||||
if (optoData[ix].Flags == OptoTelegramFlags.OK)
|
||||
{
|
||||
if (optoDataCount <= MaxOptoDataCount)
|
||||
{
|
||||
/// Telegrams are stored continuously, save them.
|
||||
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
||||
for (int i = 1; i < optoDataCount; i++)
|
||||
{
|
||||
optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
||||
}
|
||||
}
|
||||
else /// if (optoDataCount > MaxOptoDataCount)
|
||||
{
|
||||
/// Buffer overflow
|
||||
/// First part of the buffer is saved as is
|
||||
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
||||
for (int i = 1; i < StartOptoDataCount; i++)
|
||||
{
|
||||
optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
||||
}
|
||||
|
||||
optoLogFile.WriteLine(" ...");
|
||||
|
||||
/// Second part of the buffer is an overflowed circular buffer
|
||||
optoLogFile.WriteLine(optoData[BufferIdx(optoDataCount)].ToString(scalFact, null));
|
||||
for (int i = optoDataCount - EndOptoDataCount + 1; i < optoDataCount; i++)
|
||||
{
|
||||
optoLogFile.WriteLine(optoData[BufferIdx(i)].ToString(scalFact, optoData[BufferIdx(i - 1)]));
|
||||
}
|
||||
}
|
||||
|
||||
log.WarnFormat("{0} opto data successfully saved: {1} lines", Name, optoDataCount);
|
||||
|
||||
optoLogFile.Close();
|
||||
/// This is the first correct opto-telegram received
|
||||
OptoTelegramRaw.TestStartTimestampDec = optoData[ix].TimestampDec();
|
||||
break;
|
||||
}
|
||||
|
||||
return Path.Combine(relativeDirectory, optoDataLogFileName);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
File.Delete(fullFileName);
|
||||
log.ErrorFormat(string.Format("Error writing into file {0}", fullFileName));
|
||||
log.ErrorFormat(string.Format("Exception message: {0}", exc.Message));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
endIx = BufferIdx(TestEndTelegramIx);
|
||||
if ((endIx > 0) && (optoData[endIx].Flags == OptoTelegramFlags.OK))
|
||||
{
|
||||
optoData[endIx].Flags = OptoTelegramFlags.OK_TestEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data stream post processing:
|
||||
/// Flow from a reference flowmeter is FIR filtered
|
||||
/// </summary>
|
||||
void DataStreamPostProcessing()
|
||||
{
|
||||
if (optoDataCount <= OptoDataBufferSize)
|
||||
{
|
||||
FIRFilterFlow(optoData, 0, optoDataCount - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
FIRFilterFlow(optoData, 0, StartOptoDataCount - 1);
|
||||
FIRFilterFlow(optoData, (optoDataCount - EndOptoDataCount), optoDataCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine opto data file name: PCB_AA_BB_HH_MI_SS..txt
|
||||
/// </summary>
|
||||
/// <param name="testName">Test name</param>
|
||||
/// <param name="testRepeats">Test repeats count (>= 1)</param>
|
||||
/// <param name="repetitionNr">Repetition number (1 .. testRepeats)</param>
|
||||
/// <returns></returns>
|
||||
string DetermineExtraDataFileName()
|
||||
{
|
||||
///
|
||||
/// Select or create appropriate test infos
|
||||
///
|
||||
Results.Output.SensusTestInfo[] testInfos = Sequences.ProcessData.CompleteTestInfos;
|
||||
///
|
||||
if ((ProcessData.OracleDB != null && ProcessData.OracleDB.DesigMode == Output.DB.SensusOracle.DesigMode.Based_on_procedure) ||
|
||||
(testInfos == null && StateMachine.Procedure != null))
|
||||
{
|
||||
testInfos = Results.Output.SensusTestInfo.CreateFromProcedure(StateMachine.Procedure);
|
||||
}
|
||||
|
||||
///
|
||||
/// Select or create appropriate 'qBezeichnung'
|
||||
///
|
||||
string fullTestName = Results.Utils.GetTestName(testName, testRepeats, repetitionNr);
|
||||
string qBezeichnungOpto = null; /// To be determined from TestInfo-s (if there are any)
|
||||
foreach (var ti in testInfos)
|
||||
{
|
||||
if ((ti.PruefungsNrOpto != 0) && (ti.TestName == fullTestName))
|
||||
{
|
||||
qBezeichnungOpto = string.IsNullOrEmpty(ti.QBezeichnungOpto) ? ti.PruefungsNrOpto.ToString("D2") : ti.QBezeichnungOpto;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (qBezeichnungOpto == null)
|
||||
{
|
||||
qBezeichnungOpto = fullTestName; /// No appropriate TestInfo found => set default opto data file name
|
||||
}
|
||||
|
||||
///
|
||||
/// Get other required pieces of information
|
||||
///
|
||||
string pcbNr = (ConfigStruct != null) ? ConfigStruct.GetPcbNrString() : "UnknownPcbNr";
|
||||
string wmPosition = Name.Substring(5); /// WMPosition is extracted from a component name in form 'iPerl#'
|
||||
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
|
||||
string cycleStartTime = StateMachine.CycleStartTimeStamp.ToString("HH_mm_ss");
|
||||
|
||||
///
|
||||
/// Return the file name
|
||||
///
|
||||
return string.Format("{0}_{1}_{2}_{3}.txt", pcbNr, wmPosition, qBezeichnungOpto, cycleStartTime);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save opto data to a file.
|
||||
/// </summary>
|
||||
bool SaveOptoDataToFile(string directory, string fileName)
|
||||
{
|
||||
string fullFileName = Path.Combine(directory, fileName);
|
||||
log.WarnFormat("Saving {0} opto data to {1}", Name, fullFileName);
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
double scalFact = ScalingFactor();
|
||||
|
||||
using (TextWriter optoLogFile = new StreamWriter(fullFileName))
|
||||
{
|
||||
if (optoDataCount <= OptoDataBufferSize)
|
||||
{
|
||||
/// Telegrams are stored continuously, save them.
|
||||
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
||||
for (int i = 1; i < optoDataCount; i++)
|
||||
{
|
||||
optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
||||
}
|
||||
}
|
||||
else /// if (optoDataCount > MaxOptoDataCount)
|
||||
{
|
||||
/// Buffer overflow
|
||||
/// First part of the buffer is saved as is
|
||||
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
||||
for (int i = 1; i < StartOptoDataCount; i++)
|
||||
{
|
||||
optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
||||
}
|
||||
|
||||
optoLogFile.WriteLine(" ...");
|
||||
|
||||
/// Second part of the buffer is an overflowed circular buffer
|
||||
optoLogFile.WriteLine(optoData[BufferIdx(optoDataCount)].ToString(scalFact, null));
|
||||
for (int i = optoDataCount - EndOptoDataCount + 1; i < optoDataCount; i++)
|
||||
{
|
||||
optoLogFile.WriteLine(optoData[BufferIdx(i)].ToString(scalFact, optoData[BufferIdx(i - 1)]));
|
||||
}
|
||||
}
|
||||
|
||||
log.WarnFormat("{0} opto data successfully saved: {1} lines", Name, optoDataCount);
|
||||
|
||||
optoLogFile.Close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
File.Delete(fullFileName);
|
||||
log.ErrorFormat(string.Format("Error writing into file {0}", fullFileName));
|
||||
log.ErrorFormat(string.Format("Exception message: {0}", exc.Message));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadPulses()
|
||||
@@ -742,18 +848,46 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
}
|
||||
|
||||
|
||||
bool optoSerialPortParsingEnabled;
|
||||
DataStreamState dataStreamState;
|
||||
|
||||
/// <summary> Flush internal buffers and start parsing the opto serial port data </summary>
|
||||
void StartParsingOptoSerialPort()
|
||||
/// <summary>
|
||||
/// Reset counters / indices / time and start processing and saving datastream data
|
||||
/// </summary>
|
||||
public void StartDataStreamProcessing()
|
||||
{
|
||||
optoSerialPortParsingEnabled = true;
|
||||
/// Reset opto-data, etc.
|
||||
optoDataCount = 0;
|
||||
timeFromStart = 0;
|
||||
currentTelegramIx = -1;
|
||||
startSampleAcquired = false;
|
||||
TestStartTelegramIx = 0;
|
||||
endTelegramIdx1 = 0;
|
||||
endTelegramIdx2 = 0;
|
||||
endTelegramIdx3 = 0;
|
||||
TestEndTelegramIx = 0;
|
||||
|
||||
if (optoSerialPort != null && optoSerialPort.IsOpen) optoSerialPort.DiscardInBuffer();
|
||||
|
||||
if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection
|
||||
|
||||
/// Enable opto-data parsing and saving
|
||||
dataStreamState = DataStreamState.ProcessAndSave;
|
||||
}
|
||||
|
||||
/// <summary> Stop parsig the opto serial port data </summary>
|
||||
void StopParsingOptoSerialPort()
|
||||
/// <summary>
|
||||
/// Returns true when processing and saving datastream data is in progress
|
||||
/// </summary>
|
||||
bool IsDataStreamProcessing()
|
||||
{
|
||||
optoSerialPortParsingEnabled = false;
|
||||
return dataStreamState == DataStreamState.ProcessAndSave;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop processing and saving datastream data
|
||||
/// </summary>
|
||||
void StopDataStreamProcessing()
|
||||
{
|
||||
dataStreamState = DataStreamState.Flush;
|
||||
}
|
||||
|
||||
///
|
||||
@@ -764,110 +898,111 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
string partOfTelegram;
|
||||
|
||||
/// <summary>
|
||||
/// 9600 Bd, 8 data bits, 1 stop bit, no parity
|
||||
///
|
||||
/// Reads opto-datastream via serual port. Invoked from RunDeviceBefore()
|
||||
///
|
||||
/// Telegram description:
|
||||
///
|
||||
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
|
||||
///
|
||||
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
|
||||
/// Example:
|
||||
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
|
||||
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
|
||||
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
|
||||
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
|
||||
/// ...
|
||||
/// </summary>
|
||||
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
|
||||
void ReadOptoData(OptoState optoState)
|
||||
void ReadOptoData(DataStreamState optoState)
|
||||
{
|
||||
int nrBytes = optoSerialPort.BytesToRead;
|
||||
if (nrBytes > 0)
|
||||
{
|
||||
char[] buffer = new char[nrBytes];
|
||||
optoSerialPort.Read(buffer, 0, nrBytes);
|
||||
string received = new string(buffer);
|
||||
lock (this)
|
||||
{
|
||||
int nrBytes = optoSerialPort.BytesToRead;
|
||||
if (nrBytes > 0)
|
||||
{
|
||||
char[] buffer = new char[nrBytes];
|
||||
optoSerialPort.Read(buffer, 0, nrBytes);
|
||||
string received = new string(buffer);
|
||||
|
||||
string allRcvd = partOfTelegram + received;
|
||||
string allRcvd = partOfTelegram + received;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int pos = allRcvd.IndexOf("\r\n");
|
||||
while (true)
|
||||
{
|
||||
int pos = allRcvd.IndexOf("\r\n");
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
/// No CR+LF found, wait for more characters in the next invocation
|
||||
partOfTelegram = allRcvd;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// CR+LF found
|
||||
if (optoState == OptoState.Read)
|
||||
{
|
||||
int bufferIx = BufferIdx(optoDataCount);
|
||||
if (pos < 0)
|
||||
{
|
||||
/// No CR+LF found, wait for more characters in the next invocation
|
||||
partOfTelegram = allRcvd;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// CR+LF found
|
||||
if (optoState == DataStreamState.ProcessAndSave)
|
||||
{
|
||||
int bufferIx = BufferIdx(optoDataCount);
|
||||
|
||||
if (pos < OptoTelegramRaw.Length - 2)
|
||||
{
|
||||
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
if (synchronized)
|
||||
{
|
||||
optoData[bufferIx].Counter = optoDataCount;
|
||||
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
|
||||
}
|
||||
synchronized = true;
|
||||
}
|
||||
else if (optoData[bufferIx].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
||||
optoDataCount,
|
||||
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
||||
ref volumeRawExtLast, ref timestampExtLast))
|
||||
{
|
||||
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) && the telegram is OK
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
OptoTelegramRreceived(optoDataCount, synchronized2, volumeRawExtLast, timestampExtLast);
|
||||
synchronized2 = synchronized;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) but the telgram was not OK
|
||||
optoData[bufferIx].Counter = optoDataCount;
|
||||
optoDataCount++;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
if (pos < OptoTelegramRaw.Length - 2)
|
||||
{
|
||||
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
if (synchronized)
|
||||
{
|
||||
optoData[bufferIx].Counter = optoDataCount;
|
||||
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
|
||||
}
|
||||
synchronized = true;
|
||||
}
|
||||
else if (optoData[bufferIx].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
||||
optoDataCount,
|
||||
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
||||
ref volumeRawExtLast, ref timestampExtLast))
|
||||
{
|
||||
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) && the telegram is OK
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
OptoTelegramRreceived(optoDataCount, synchronized2, volumeRawExtLast, timestampExtLast);
|
||||
synchronized2 = synchronized;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) but the telgram was not OK
|
||||
optoData[bufferIx].Counter = optoDataCount;
|
||||
optoDataCount++;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
|
||||
optoDataCount++;
|
||||
}
|
||||
else /// optoState == OptoState.Flush
|
||||
{
|
||||
if (pos < OptoTelegramRaw.Length - 2)
|
||||
{
|
||||
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
synchronized = true;
|
||||
}
|
||||
// CR+LF found and (pos >= OptoTelegram.Length - 2)
|
||||
else if (toBeFlushed.UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
||||
0,
|
||||
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
||||
ref volumeRawExtLast, ref timestampExtLast))
|
||||
{
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
synchronized2 = synchronized;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
optoDataCount++;
|
||||
}
|
||||
else /// optoState == OptoState.Flush
|
||||
{
|
||||
if (pos < OptoTelegramRaw.Length - 2)
|
||||
{
|
||||
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
synchronized = true;
|
||||
}
|
||||
// CR+LF found and (pos >= OptoTelegram.Length - 2)
|
||||
else if (toBeFlushed.UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
||||
0,
|
||||
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
||||
ref volumeRawExtLast, ref timestampExtLast))
|
||||
{
|
||||
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
||||
synchronized2 = synchronized;
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRcvd = allRcvd.Substring(pos + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
|
||||
}
|
||||
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1113,13 +1248,13 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
/// <returns>Index to the buffer</returns>
|
||||
public static int BufferIdx(int index)
|
||||
{
|
||||
if (index < IperlHead.MaxOptoDataCount)
|
||||
if (index < IperlHead.OptoDataBufferSize)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IperlHead.StartOptoDataCount + (index - IperlHead.MaxOptoDataCount) % IperlHead.EndOptoDataCount;
|
||||
return IperlHead.StartOptoDataCount + (index - IperlHead.OptoDataBufferSize) % IperlHead.EndOptoDataCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
///
|
||||
/// Copyright (c) 2015-2017 Sensus Metering Systems
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Xml.Serialization;
|
||||
using Common;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Resources;
|
||||
|
||||
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user