Add simulation logic for test operations and water metrology, enhance UI initialization, and implement optohead flow rate handling.

- Introduced simulation timers in `FlyingStartStopTestOp`.
- Added `Simulate` methods in `WaterMetrologyData`, `WaterMetrologyDataC7`, and `WaterMetrologyDataC2`.
- Enhanced `SmartCommunicationForm` with textbox resizing logic and initialization code.
- Implemented flow rate and volume calculation from optohead telemetry in `SmartReader`.
- Added unit tests for Poseidon correction parsing.
This commit is contained in:
Michal Buzik 2025-12-06 12:46:12 +01:00
parent 06a401a1ff
commit 2842425673
20 changed files with 1073 additions and 301 deletions

View File

@ -2,6 +2,7 @@
/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
///
using System;
using Common;
using log4net;
using TBF.Rig.Sequences;
@ -11,6 +12,10 @@ namespace TBF.Rig.ControlBoard.Uni
{
private static readonly ILog log = LogManager.GetLogger(typeof(FlyingStartStopTestOp));
public override string ToString() { return string.Format("FlyingStartStopTestOp()"); }
private DateTime startTimeForSimulation;
private bool simulationTimerStarted = false;
/// Arguments of the constructor
readonly UniCB uniCB;
@ -123,6 +128,28 @@ namespace TBF.Rig.ControlBoard.Uni
{
log.DebugFormat("Op.Run() opState={0}", opState);
//Simulation of processing time 25 seconds
if (uniCB.DebugLevel == DebugMode.Simulate)
{
if (opState == OpState.StartingTest)
{
startTimeForSimulation = DateTime.Now;
simulationTimerStarted = false;
}
if (opState == OpState.TestInProgress)
{
if (!simulationTimerStarted)
{
startTimeForSimulation = DateTime.Now;
simulationTimerStarted = true;
}
else if (DateTime.Now - startTimeForSimulation > TimeSpan.FromSeconds(25))
{
return Event.TestCompleted;
}
}
}
switch (opState)
{
case OpState.StartingTest:

View File

@ -3,16 +3,42 @@
///
using System;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols;
namespace TBF.Rig.RegisterReaders.CommonRR.IPerl.communication
{
public class OptoReceivedEventArgs : EventArgs
{
public string Data;
public WaterMetrologyData WaterMetrologyData;
public byte[] RawData;
public OptoReceivedEventArgs(string data)
{
this.Data = data;
WaterMetrologyData = null;
RawData = null;
}
public OptoReceivedEventArgs(string data, WaterMetrologyData waterMetrologyData)
{
this.Data = data;
this.WaterMetrologyData = waterMetrologyData;
RawData = null;
}
public OptoReceivedEventArgs(byte[] data)
{
this.RawData = data;
Data = null;
WaterMetrologyData = null;
}
public OptoReceivedEventArgs(WaterMetrologyData data)
{
WaterMetrologyData = data;
Data = null;
RawData = null;
}
}
}

View File

@ -28,8 +28,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonCmdStartStop
readonly PoseidonCfg registerReaderCfg;
readonly ControlBoard.IControlBoard controlBoard;
public int ComPortNr => registerReaderCfg?.ComPortNr ?? -1;
public PoseidonCfg RegPoseidonCfg => registerReaderCfg;
private bool activeHandlerSessioEnabled = false;
private CliRunner _cliRunner;

View File

@ -2,6 +2,7 @@ using System;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Windows.Forms.VisualStyles;
using System.Xml.Linq;
using Common;
using Common.Iperl;
@ -11,6 +12,8 @@ using NHibernate;
using Sensus.iPerl.NfcHandler;
using TBF.Rig.Generic;
using TBF.Rig.GenericDevices;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
using CalibrationStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStruct;
@ -45,6 +48,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
readonly PoseidonCfg _poseidonCfg;
public PoseidonCfg RegPoseidonCfg { get { return _poseidonCfg; } }
public int RfidComPortNr { get { return _poseidonCfg.RfidComPortNr; } }
public int OptoComPortNr { get { return _poseidonCfg.OptoComPortNr; } }
public MeterType MeterType { get { return _poseidonCfg.MeterType; } }
@ -86,6 +90,21 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
float[] x;
public float[] X { get { return x; } }
// Volume of water from the opto telegram
private DateTime _firstSampleTime;
private DateTime _lastSampleTime;
private double _averageFlow;
private long _averageFlowCount;
private readonly object _avgLock = new object();
private bool _optoheadStarted = false;
private OptoHeadService _optoHeadService;
/// <summary>
/// Passed to OptoTelegramRaw.UpdateFromString(...)
@ -164,9 +183,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
///
/// Timestamp from the opto telegram
///
private Int64 lastTimestamp;
private double timestampSec;
private double timestampSec0;
int timeFromStart; /// [s] Time from test start to determine when the test start sample should be taken
@ -174,12 +191,22 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
/// Test start volume for metrology in seconds
public double TimestampSecStart
{
get { return TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, StartEndFilterSamplesCount2); }
get
{
return _lastSampleTime != DateTime.MinValue ? 1 : 0; // return one second if is initialized, 0 - is false
//return TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, StartEndFilterSamplesCount2);
}
}
/// Test end time for metrology in seconds
public double TimestampSecEnd
{
get { return TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, StartEndFilterSamplesCount2); }
get
{
if (_lastSampleTime == DateTime.MinValue) return 0;
TimeSpan delta = _lastSampleTime - _firstSampleTime;
return (delta.TotalSeconds + 1);
//return TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, StartEndFilterSamplesCount2);
}
}
///
public bool NoSamples
@ -197,12 +224,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
/// Test start volume for metrology in liters
public double VolumeLtrStart
{
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
get
{
return 0;
//return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2);
}
}
/// Test end volume for metrology in liters
public double VolumeLtrEnd
{
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
get
{
return wmVolume; // complet calculated volume (time * flowrate)
//return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2);
}
}
@ -274,9 +309,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
{
if (_poseidonCfg != null)
{
OpenOptoSerialPort($"COM{_poseidonCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One,
Handshake.None);
CloseOptoSerialPort();
// OpenOptoSerialPort($"COM{_poseidonCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One,
// Handshake.None);
// CloseOptoSerialPort();
log.FatalFormat($"{Name} initialized: {this}");
}
else
@ -401,6 +437,16 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
Q2CorrRL = 0;
Q2CorrLR = 0;
lock (_avgLock)
{
_firstSampleTime = DateTime.MinValue;
_lastSampleTime = DateTime.MinValue;
_averageFlow = 0;
_averageFlowCount = 0;
log.Debug("Initializing datastream state");
}
simulatedPcbNr = null;
dataStreamState = DataStreamState.Flush;
@ -442,6 +488,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
{
timeFromStart += StateMachine.Period;
ReadPulses();
//TODO read flow
if (_optoHeadService!= null && !_optoHeadService.IsRunning)
StartOptohead();
if (!startSampleAcquired && (timeFromStart >= 8) && (currentTelegramIx >= 0))
{
@ -462,7 +514,94 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
return Event.ReadRegisterDone;
}
/// <summary>
private void StartOptohead()
{
lock (_avgLock)
{
_firstSampleTime = DateTime.MinValue;
_lastSampleTime = DateTime.MinValue;
_averageFlow = 0;
_averageFlowCount = 0;
}
StartOptoTestInputLoop(new EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs>(OnOptoHandler));
}
private bool OpenOptoConnection(PoseidonCfg iHeadCfg)
{
try
{
if (iHeadCfg != null)
{
if (_optoHeadService != null) return false;
OptoHeadService.Con connection = new OptoHeadService.Con($"COM{iHeadCfg.OptoComPortNr}", iHeadCfg.DebugLevel);
_optoHeadService = new OptoHeadService(connection);
return _optoHeadService.CreateSerialConnection();
}
}
catch (Exception ex)
{
throw ex;
}
return false;
}
private bool StartOptoTestInputLoop(EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs> onOptoReceivedHandler)
{
try
{
if (_optoHeadService != null)
{
if (_optoHeadService.IsRunning) return false;
_optoHeadService.RunLoop(onOptoReceivedHandler);
//run loop runstate = true;
return true;
}
}
catch (Exception ex)
{
log.Error(ex.Message);
throw ex;
}
return false;
}
private void OnOptoHandler(object sender, CommonRR.IPerl.communication.OptoReceivedEventArgs e)
{
//received data from optohead
WaterMetrologyData eWaterMetrologyData = e?.WaterMetrologyData;
if (eWaterMetrologyData != null && eWaterMetrologyData.C7Data != null)
{
double flowRateLPerS = eWaterMetrologyData.C7Data?.FlowRateLPerS ?? 0;
lock (_avgLock)
{
if (_averageFlowCount == 0)
{
_firstSampleTime = eWaterMetrologyData.C7Data?.Dt ?? DateTime.Now;
}
_lastSampleTime = eWaterMetrologyData.C7Data?.Dt ?? DateTime.Now;
_averageFlowCount++;
// Running average (no overflow)
_averageFlow += (flowRateLPerS - _averageFlow) / _averageFlowCount;
TimeSpan delta = _lastSampleTime - _firstSampleTime;
if (delta.TotalMilliseconds == 0)
wmVolume = 0;
else
wmVolume = _averageFlow * (delta.TotalMilliseconds / 1000); //volume in liters
//wmVolume = Units.ConvertFrom(Unit.l, _averageFlow * (delta.TotalMilliseconds / 1000));
log.Info("Calculated Value:" + wmVolume);
}
}
}
/// <summary>
/// Stop this operation
/// </summary>
public void Stop()
@ -570,78 +709,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
}
/// <summary>
/// Q2 correction factor calculated from the last test (Q2).
/// This factor should be used only for R800 meters.
/// </summary>
/// <param name="q2TestResult">A test result from which to calculate the factor</param>
/// <param name="nominalFlow">Nominal flow in m3/h</param>
/// <param name="currentFactor">0 or the current Q2 correction factor when updating the factor</param>
/// <returns>Calculated Q2 correction factor</returns>
public double CalculateQ2CorrectionFactor(Results.Entities.MeterTestRslt currentQ2Result, int currentFactor, double nominalFlow, double errorTarget = 0)
{
double nominalTestFlowLph = Units.ConvertTo(Unit.lph, nominalFlow);
double volumeRefShiftedToTarget = currentQ2Result.VolumeRef * (1.0 + errorTarget / 100.0);
double q2adjErrorShiftedToTarget = Config.Formulas.ErrorFromVolumes(currentQ2Result.VolumeMeter, volumeRefShiftedToTarget);
double A = 16.0 / ScalingFactor(); /// Raw units per ml: DN15=16, DN20=8, DN25=4, DN32=2, DN40=1
const double B = 8.0; /// Raw units per minute, 8
const double C = B * 60.0; /// Raw units per hour, 480
double D = C / A; /// ml correction per hour
double F = D / (nominalTestFlowLph * 10.0); /// Error corrected with 8 Raw Units per minute [%]
double G = F / B; /// Error corrected with 1 Raw Unit per minute [%]
/// Do not change the factor for an invalid measurement (q2adjResult.VolumeMeter == 0)
double q2CorrectionFactor = (Math.Abs(currentQ2Result.VolumeMeter) <= float.Epsilon) ? Convert.ToDouble(currentFactor) :
Convert.ToDouble(currentFactor) - (q2adjErrorShiftedToTarget / G) * (volumeRefShiftedToTarget / currentQ2Result.VolumeMeter);
log.WarnFormat("CalculateQ2CorrectionFactor() : Pos={0}, PCB#={1}, Error={2}%, Target={3}%, Current factor={4} New factor={5}",
Name,
SerialNr,
currentQ2Result.Error.ToString("F2"),
errorTarget.ToString("F3"),
currentFactor.ToString("F1"),
q2CorrectionFactor.ToString("F1"));
return q2CorrectionFactor;
}
/// <summary>
/// 2 Hz correction factor calculated from two Q3 tests - done at 2Hz and at 8Hz.
/// This factors should be used only for DN32 and DN40 meters.
/// </summary>
/// <param name="resultAt2Hz">Test result @2Hz from which to calculate the factor</param>
/// <param name="resultAt8Hz">Test result @8Hz from which to calculate the factor</param>
/// <param name="hz2CorrectionFactor">The calculated Q2 correction factor</param>
/// <returns>true = OK, false = failed</returns>
public bool Calculate2HzCorrectionFactor(Results.Entities.MeterTestRslt resultAt2Hz,
Results.Entities.MeterTestRslt resultAt8Hz,
out double diff2Hz8Hz, out int hz2CorrectionFactor)
{
hz2CorrectionFactor = 0;
diff2Hz8Hz = 0;
if ((resultAt2Hz == null) || (resultAt8Hz == null))
{
return false; /// Test result @2Hz and/or @8Hz is missing ==> water meter failed
}
diff2Hz8Hz = resultAt2Hz.Error - resultAt8Hz.Error;
if (Math.Abs(diff2Hz8Hz) > 2.5) return false; /// Difference of errors > 2.5 % ==> water meter failed
hz2CorrectionFactor = -1 * (int)Math.Round(10 * diff2Hz8Hz);
log.WarnFormat("2Hz correction: Pos={0}, PCB#={1}, corrFactor={2}, erro@2Hz={3}%, erro@8Hz={4}%",
Name,
SerialNr,
hz2CorrectionFactor,
resultAt2Hz.Error.ToString("F2"),
resultAt8Hz.Error.ToString("F2"));
return true;
}
/// <summary>
@ -703,147 +771,24 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
void ReadOptoData(DataStreamState optoState)
{
if (optoSerialPort is null) return;
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;
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 == 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);
OptoTelegramReceived(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);
}
}
}
}
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
}
else
{
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
}
}
// lock (this)
// {
//
// }
}
public string ReadOptoData()
{
if (optoSerialPort is null) return "";
string received = ".";
lock (this)
{
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
{
char[] buffer = new char[nrBytes];
optoSerialPort.Read(buffer, 0, nrBytes);
received = new string(buffer);
}
}
// lock (this)
// {
//
// }
return received;
}
void OptoTelegramReceived(int currentIx, bool async, Int64 volumeRawExt, Int64 timestampRawExt)
{
currentTelegramIx = currentIx;
lastVolumeRaw = volumeRawExt;
lastTimestamp = timestampRawExt;
if (volumeLtr == 0 && volumeLtr0 == 0)
{
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
volumeLtr0 = volumeLtr;
}
else
{
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
}
if (timestampSec == 0 && timestampSec0 == 0)
{
timestampSec = (double)lastTimestamp / 8192.0;
timestampSec0 = timestampSec;
}
else
{
timestampSec = (double)lastTimestamp / 8192.0;
}
}
/// <summary>
@ -1002,12 +947,15 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
void ReadPulses()
{
beginWMState = volumeLtr0;
endWMState = volumeLtr;
TimeSpan delta = _lastSampleTime - _firstSampleTime;
double volume = _averageFlow * (delta.TotalMilliseconds / 1000);
//log.Debug("ReadPulses - Calculated Value:" + volume);
beginWMState = 1;
endWMState = beginWMState + volume ;
wmVolume = Math.Abs(endWMState - beginWMState);
wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
wmRefPulses = StateMachine.ControlBoardMain.RefPulses;
wmTestTime = timestampSec - timestampSec0;
wmTestTime = delta.TotalSeconds;
}
private void OpenOptoSerialPort(string comPort, int baudRate, Parity parity, int dataBits, StopBits stopBit, Handshake handshake)
@ -1018,6 +966,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
/// Open serial port: 9600 Bd, 8 data bits, 1 stop bit, no parity
try
{
CloseOptoSerialPort();
optoSerialPort = new SerialPort(comPort, baudRate, parity, dataBits, stopBit);
optoSerialPort.Handshake = handshake;
@ -1039,12 +988,15 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
private void CloseOptoSerialPort()
{
if (optoSerialPort != null)
if (_optoHeadService != null)
{
optoSerialPort.Close();
optoSerialPort = null;
_optoHeadService.CloseSerialConnection();
_optoHeadService = null;
log.FatalFormat($"{Name} OptoPort closed: {this}");
}
communication.OpticalHeadTest.SetActiveMode(_poseidonCfg);
}
@ -1056,10 +1008,14 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
{
try
{
OpenOptoSerialPort($"COM{_poseidonCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One, Handshake.None);
//set test mode via the cli
communication.OpticalHeadTest.SetTestMode(_poseidonCfg);
//open opto serial port
OpenOptoConnection(_poseidonCfg);
}
catch (Exception)
catch (Exception e)
{
log.Error($"{Name} OptoPort - error opening port: {_poseidonCfg.OptoComPortNr}, Details: {e.Message}");
}
/// Reset opto-data, etc.
optoDataCount = 0;
@ -1482,8 +1438,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader
volumeLtr = 0;
volumeLtr0 = 0;
timestampSec = 0;
timestampSec0 = 0;
extraDataPath = null;

View File

@ -2,6 +2,8 @@ using System;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using log4net;
using TBF.Rig.Output.Printers.Label;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils;
using CliRunner = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils.CliRunnerOld;
using OptoHeadStatus = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols.OptoHeadStatus;
@ -13,6 +15,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
public class NfcHeadServiceOld
{
private static readonly ILog log = LogManager.GetLogger(typeof(NfcHeadServiceOld));
private bool activeHandlerSessioEnabled = false;
private CliRunner _cliRunner;
@ -208,7 +211,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
}
else
{
throw new Exception($"Failed to parse OptoHeadStatus from output. Result: {result}");
log.Error($"Failed to parse OptoHeadStatus from output. Result: {result}");
//throw new Exception($"Failed to parse OptoHeadStatus from output. Result: {result}");
}
}

View File

@ -1,6 +1,10 @@
using System;
using System.IO.Ports;
using System.Threading.Tasks;
using Common;
using log4net;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols;
using TBF.Rig.Sequences;
using SERIAL_Driver = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils.SERIAL_Driver;
using WaterMetrologyData = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols.WaterMetrologyData;
@ -8,9 +12,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
{
public class OptoHeadService
{
static readonly ILog log = LogManager.GetLogger("PoseidonConnection");
public class Con
{
public string com = "COM5";
public int baudrate = 38400;
public int dataBits = 8;
@ -18,6 +24,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
public StopBits stopbits = StopBits.Two;
public int readTimeout = 5000;
public int writeTimeout = 1000;
private DebugMode _debugLevel;
public DebugMode DebugModeSetting { get => _debugLevel; }
public Con(string com, int baudrate, int dataBits, Parity parity, StopBits stopbits, int readTimeout,
int writeTimeout) : this(com)
@ -29,10 +37,23 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
public Con(DebugMode debugLevel,string com, int baudrate, int dataBits, Parity parity, StopBits stopbits, int readTimeout,
int writeTimeout) : this(com)
{
this._debugLevel = debugLevel;
this.baudrate = baudrate;
this.dataBits = dataBits;
this.parity = parity;
this.stopbits = stopbits;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
public Con(string com)
public Con(string com, DebugMode debugLevel = DebugMode.Normal)
{
this.com = com;
this._debugLevel = debugLevel;
}
}
@ -55,11 +76,19 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
OnOptoReceivedHandler = null;
bool isopen = false;
Con con = Connection;
if (con == null)
{
return false;
}
byte[] message = {0x00};
byte[] bytesReceived;
if (!driver.isOpen())
if (con?.DebugModeSetting == DebugMode.Simulate)
{
isopen = true;
}
else if (!driver.isOpen())
{
isopen = driver.OpenConnection(
@ -72,6 +101,8 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
con.writeTimeout);
}
_bRunStarted = false;
return isopen;
}
@ -79,24 +110,39 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
{
dissableRunLoop = true;
OnOptoReceivedHandler = null;
driver.Close();
OnOptoReceivedHandler = null;
if (Connection?.DebugModeSetting != DebugMode.Simulate)
{
driver.Close();
}
_bRunStarted = false;
}
private EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs> OnOptoReceivedHandler;
public bool IsRunning
{
get { return !dissableRunLoop
&& ((Connection?.DebugModeSetting != DebugMode.Simulate) ? driver.isOpen() : true)
&& _bRunStarted;}
}
public void RunLoop(EventHandler<CommonRR.IPerl.communication.OptoReceivedEventArgs> onOptoReceivedHandler)
{
log.Debug("RunLoop started on event!");
OnOptoReceivedHandler = onOptoReceivedHandler;
Task.Run(() => Run());
}
public void RunLoop()
{
log.Debug("RunLoop started!");
Task.Run(() => Run());
}
private bool dissableRunLoop = false;
private bool _bRunStarted = false;
/// <summary>
/// Run the service. Catch one communication to WaterMetrologyData field.
/// </summary>
@ -104,10 +150,12 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
{
while (!dissableRunLoop)
{
_bRunStarted = true;
WaterMetrologyData = ParseData(RunReading());
if (OnOptoReceivedHandler != null && WaterMetrologyData != null)
{
OnOptoReceivedHandler.Invoke(this, new CommonRR.IPerl.communication.OptoReceivedEventArgs(WaterMetrologyData.ToString()));
log.Debug($"Received OptoData: {WaterMetrologyData}");
OnOptoReceivedHandler?.Invoke(this, new CommonRR.IPerl.communication.OptoReceivedEventArgs(WaterMetrologyData?.ToString(), WaterMetrologyData));
}
}
@ -115,13 +163,20 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
byte[] RunReading()
{
if (Connection?.DebugModeSetting != DebugMode.Simulate)
{
return new byte[] {0x00};
}
if (driver.isOpen())
{
driver.SendMessage(new byte[] {0x00}, 1);
return driver.GetRawData();
byte[] rawData = driver.GetRawData();
log.Debug($"Received data size: {rawData?.Length ?? 0} bytes, raw data: {(rawData==null? "" :BitConverter.ToString(rawData))}");
return rawData;
}
else
{
log.Debug("Serial port is not open.");
dissableRunLoop = true;
}
@ -130,6 +185,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7
public WaterMetrologyData ParseData(byte[] data)
{
if (Connection?.DebugModeSetting != DebugMode.Simulate)
{
return WaterMetrologyData.SimulateC7();
}
try
{
if (data == null || data.Length == 0)

View File

@ -37,6 +37,21 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
return waterMetrologyData;
}
public static WaterMetrologyData SimulateC7()
{
WaterMetrologyData waterMetrologyData = new WaterMetrologyData();
waterMetrologyData.c7Data = WaterMetrologyDataC7.Simulate();
return waterMetrologyData;
}
public static WaterMetrologyData SimulateC2()
{
WaterMetrologyData waterMetrologyData = new WaterMetrologyData();
waterMetrologyData.c2Data = WaterMetrologyDataC2.Simulate();
return waterMetrologyData;
}
public override string ToString()
{
return $"Status: {_optoHeadStatus}, C7Data: {c7Data}, C2Data: {c2Data}";

View File

@ -21,7 +21,17 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
public bool FastHPFC { get; set; }
public bool FieldPolarity { get; set; }
public bool ImpedancePolarity { get; set; }
public double FlowRateLPerS // Flow Rate in L/s metric units
{
get
{
double flowRateGPM = FlowRate / 10000; //investigation flow meter GPM
double flowRateLPerS = flowRateGPM * 0.063090196432096 ; // conversion factor from GPM to L/s with minimal digit lost
return flowRateLPerS;
}
}
public double CalcFlowmLps
{
get { return FlowRate / 4.0; } // FlowRate is in 1/4 mL/s
@ -41,6 +51,32 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
return Parse(data, dt, dutinfo);
}
public static WaterMetrologyDataC2 Simulate()
{
var result = new WaterMetrologyDataC2();
result.DutInfo = "dutinfo";
result.Dt = DateTime.Now;
result.AdcSample = 1;
result.LastField = 2;
result.FlowRate = 12456;
result.Accumulator = 789465;
result.FlipPeriod = 1;
result.VinfStart = 0;
result.VinfEnd = 0;
result.ElectrodeDelta = 1;
result.Impedance = 1;
result.FieldDriveTime = 0x00 ;
result.IsInLowFlow = false;
result.IsInEmptyPipe = false;
result.FastHPFC = false; // Fast High Pass Filter Constant in bit 2
result.FieldPolarity = false; // Field Polarity in bit 3
result.ImpedancePolarity = false; // Impedance Polarity in bit 4
return result;
}
public static WaterMetrologyDataC2 Parse(byte[] data, DateTime dt, string dutinfo)
{
if (data.Length < 24)

View File

@ -23,6 +23,47 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
public bool IsLearningActive { get; set; }
public bool AdcShiftsUpdated { get; set; }
public static WaterMetrologyDataC7 Simulate()
{
var result = new WaterMetrologyDataC7();
result.DutInfo = "dutinfo";
result.Dt = DateTime.Now;
result.AdcSample = 1;
result.LastField = 2;
result.FlowRate = 12456;
result.Accumulator = 789465;
result.FlipPeriod = 1;
result.VinfStart = 0;
result.VinfEnd = 0;
result.ElectrodeDelta = 1;
result.Impedance = 1;
result.FieldDriveTime = 0x00 ;
result.IsInLowFlow = false;
result.IsInEmptyPipe = false;
result.FastHPFC = false; // Fast High Pass Filter Constant in bit 2
result.FieldPolarity = false; // Field Polarity in bit 3
result.ImpedancePolarity = false; // Impedance Polarity in bit 4
result.MagTamperState = true; // bits 5 and 6 represent MagTamperState
result.IsLearningActive = true; // bit 7 represents IsLearningActive
result.AdcShiftsUpdated = false; // bit 0 represents AdcShiftsUpdated
result.LastFieldmilliGauss = 0;
result.ImpedanceI = 0; // in phase
result.ImpedanceQ = 0; // out of phase
result.NoiseMetric = 0; //
result.LearningLockout = 0;
result.ReverseBuffer = 0;
result.ConditionedAdc = 0;
result.Totalalizer = 0;
return result;
}
public static WaterMetrologyDataC7 Parse(string base64Data, DateTime dt, string dutinfo)
{

View File

@ -6,6 +6,6 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
{
[Description("..")] None,
[Description("Nfc")] Nfc,
[Description("Touch Capl")]Touched,
[Description("cTouchRead")]Touched,
}
}

View File

@ -17,6 +17,9 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
internal class OpticalHeadTest
{
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
DebugMode _debugMode;
public DebugMode DebugMode { get => _debugMode; set => _debugMode = value; }
internal static string OpenSealing(ISmartReader iHead)
{
@ -26,6 +29,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
internal static string ReadRequest_SerialNo(PoseidonCfg iHeadCfg)
{
if (iHeadCfg != null && iHeadCfg.DebugLevel == DebugMode.Simulate)
{
return "1111";
}
string serialNo = null;
try
{
@ -77,6 +85,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
internal static string SetActiveMode(PoseidonCfg iHeadCfg)
{
if (iHeadCfg != null && iHeadCfg.DebugLevel == DebugMode.Simulate)
{
return "OK";
}
SerialPortData serialPortData = new SerialPortData(
$"COM{iHeadCfg.RfidComPortNr}",
iHeadCfg.CliProgramName,
@ -100,6 +113,11 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
internal static string SetTestMode(PoseidonCfg iHeadCfg)
{
if (iHeadCfg != null && iHeadCfg.DebugLevel == DebugMode.Simulate)
{
return "OK";
}
SerialPortData serialPortData = new SerialPortData(
$"COM{iHeadCfg.RfidComPortNr}",
iHeadCfg.CliProgramName,
@ -123,6 +141,10 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
public static void Deactivate()
{
if (_lastIHeadCfg != null && _lastIHeadCfg.DebugLevel == DebugMode.Simulate)
{
return;
}
StopOptoTestInputLoop();
if (_lastOptoHeadStatus != OptoHeadStatus.Unknown &&
_lastOptoHeadStatus != OptoHeadStatus.OptoHeadDisabled &&
@ -142,7 +164,7 @@ namespace TBF.Rig.RegisterReaders.PoseidonReader.communication
{
if (optoHeadService != null) return false;
OptoHeadService.Con connection = new OptoHeadService.Con($"COM{iHeadCfg.OptoComPortNr}");
OptoHeadService.Con connection = new OptoHeadService.Con($"COM{iHeadCfg.OptoComPortNr}", iHeadCfg.DebugLevel);
optoHeadService = new OptoHeadService(connection);
optoHeadService.CreateSerialConnection();
optoHeadService.RunLoop(onOptoReceivedHandler);

View File

@ -57,7 +57,11 @@ namespace TBF.Rig.Sequences
try
{
/// 1nd argument
ITestMethodCfg iPerlCfgIPerl = cfg as ITestMethodCfg;
ITestMethodCfg testMethodCfg = cfg as ITestMethodCfg;
if (testMethodCfg == null)
{
}
/// 2rd argument: as is
@ -68,8 +72,7 @@ namespace TBF.Rig.Sequences
/*myRef.modelessDlg = new TestMethods.iPerlCommunication.iPerlCommunicationForm(iPerlCfg, tests, iPerlCommParams);
myRef.modelessDlg.Show();*/
myRef.modelessDlg = new SmartCommunicationForm(
testMethod , tests, iPerlCommParams);
myRef.modelessDlg = new SmartCommunicationForm( testMethod , tests, iPerlCommParams);
myRef.modelessDlg.Show();
}
catch (Exception e)

View File

@ -1,10 +1,16 @@
using TBF.Rig.Configs.NameOnly;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
{
public interface ISmartTestMethod
{
public void MeterCommMilestone(int iItem, bool bValue);
public bool IsMeterCommMilestone(int iItem);
public ITestMethodCfg TestMethodCfg { get; }
}
}

View File

@ -135,7 +135,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
public static string SelectedTypeReader { get; set; }
private List<ICorrections> GetNewCorrectionList(ISmartTestMethod componentBase ,
TestMethodCfg cfg, IList<Test> tests, IList<ITestParams> multiTestParams)
ITestMethodCfg cfg, IList<Test> tests, IList<ITestParams> multiTestParams)
{
List<ICorrections> correctionsList = new List<ICorrections>();
@ -154,7 +154,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
{
if (correctionsList.Any(x => x is SmartReader))
continue;
correctionsList.Add(new PoseidonCorrections(this));
correctionsList.Add(new PoseidonCorrections(this,log, rfidDataLogger, componentBase, cfg, tests, multiTestParams));
continue;
}
@ -297,7 +297,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
ShuffleTextBoxes(ProcessData.WMsCount, ProcessData.LineSize);
this.ContextMenu = Correction.GetContextMenu();
//this.ContextMenu = Correction.GetContextMenu();
}
@ -325,9 +325,11 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
{
checkBoxesEditMode = false;
ITestMethodCfg cfg = (componentBase as ITestMethodCfg);
ISmartTestMethod smartTestMethod = componentBase as ISmartTestMethod;
//TODO get corrections based on defined meter
_corrections = GetNewCorrectionList(componentBase as ISmartTestMethod,
componentBase.Cfg as TestMethodCfg, tests, multiTestParams);
_corrections = GetNewCorrectionList(smartTestMethod, smartTestMethod.TestMethodCfg, tests, multiTestParams);
InitializeMeterTypeItems();
UpdateHeads();
@ -381,6 +383,27 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
private void UpdateHeads()
{
if (!(waterMeterPositions0 == null || waterMeterPositions0.Count <= 0)
&& labels != null && counters != null && messages != null && checkBoxes != null)
{
foreach (int position in waterMeterPositions0)
{
try
{
labels[position].Visible = false;
counters[position].Visible = false;
messages[position].Visible = false;
checkBoxes[position].Visible = false;
ckbIndex[position] = 0;
ckbState[position] = false;
}
catch (Exception e)
{
log.Error("UpdateHeads()", e);
}
}
}
iperlHeads?.Clear();
if (iperlHeads == null) iperlHeads = new List<ISmartReader>();
waterMeterPositions0?.Clear();
@ -415,6 +438,21 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
if (wmPos >= ProcessData.WMsCount) break;
}
}
//we have items from the list, so we can enable the rows
if (iperlHeads.Count > 0)
{
WaterMetersCount = iperlHeads.Count;
ShuffleTextBoxes(WaterMetersCount, ProcessData.LineSize);
this.ContextMenu = Correction.GetContextMenu();
Correction.PrepareForTestsActivities(WaterMetersCount);
}
else
{
this.ContextMenu = null;
}
}
}
@ -458,6 +496,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
/// this part works fine if we are on <b>test loop</b>
/// - because ProcessData.RegisterReaders is initialized in test loop
/// </summary>
/// <param name="selectedTypeReader"></param>
private static void InitializeSmartReaderLists()
{
iperlHeads = new List<ISmartReader>();
@ -501,43 +540,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
/// <param name="lineSize">Number of watermeters in one line</param>
void ShuffleTextBoxes(int wmsCount, int lineSize)
{
labels = new Label[MaxTextBoxesCount]
{
wmLabel1, wmLabel2, wmLabel3, wmLabel4, wmLabel5, wmLabel6, wmLabel7, wmLabel8, wmLabel9, wmLabel10,
wmLabel11, wmLabel12, wmLabel13, wmLabel14, wmLabel15, wmLabel16, wmLabel17, wmLabel18, wmLabel19, wmLabel20,
wmLabel21, wmLabel22, wmLabel23, wmLabel24, wmLabel25, wmLabel26, wmLabel27, wmLabel28, wmLabel29, wmLabel30,
wmLabel31, wmLabel32, wmLabel33, wmLabel34, wmLabel35, wmLabel36, wmLabel37, wmLabel38, wmLabel39, wmLabel40,
wmLabel41, wmLabel42, wmLabel43, wmLabel44, wmLabel45, wmLabel46, wmLabel47, wmLabel48,
};
counters = new PictureBox[MaxTextBoxesCount]
{
pictureBox1, pictureBox2, pictureBox3, pictureBox4, pictureBox5, pictureBox6, pictureBox7, pictureBox8, pictureBox9, pictureBox10,
pictureBox11, pictureBox12, pictureBox13, pictureBox14, pictureBox15, pictureBox16, pictureBox17, pictureBox18, pictureBox19, pictureBox20,
pictureBox21, pictureBox22, pictureBox23, pictureBox24, pictureBox25, pictureBox26, pictureBox27, pictureBox28, pictureBox29, pictureBox30,
pictureBox31, pictureBox32, pictureBox33, pictureBox34, pictureBox35, pictureBox36, pictureBox37, pictureBox38, pictureBox39, pictureBox40,
pictureBox41, pictureBox42, pictureBox43, pictureBox44, pictureBox45, pictureBox46, pictureBox47, pictureBox48,
};
messages = new TextBox[MaxTextBoxesCount]
{
wmTextBox1, wmTextBox2, wmTextBox3, wmTextBox4, wmTextBox5, wmTextBox6, wmTextBox7, wmTextBox8, wmTextBox9, wmTextBox10,
wmTextBox11, wmTextBox12, wmTextBox13, wmTextBox14, wmTextBox15, wmTextBox16, wmTextBox17, wmTextBox18, wmTextBox19, wmTextBox20,
wmTextBox21, wmTextBox22, wmTextBox23, wmTextBox24, wmTextBox25, wmTextBox26, wmTextBox27, wmTextBox28, wmTextBox29, wmTextBox30,
wmTextBox31, wmTextBox32, wmTextBox33, wmTextBox34, wmTextBox35, wmTextBox36, wmTextBox37, wmTextBox38, wmTextBox39, wmTextBox40,
wmTextBox41, wmTextBox42, wmTextBox43, wmTextBox44, wmTextBox45, wmTextBox46, wmTextBox47, wmTextBox48,
};
checkBoxes = new CheckBoxImage[MaxTextBoxesCount]
{
checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5, checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10,
checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15, checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20,
checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25, checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30,
checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35, checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40,
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48,
};
ckbIndex = new int[MaxTextBoxesCount];
ckbState = new bool[MaxTextBoxesCount];
textBoxesCount = MaxTextBoxesCount;
InitializeTextBoxArrays();
///
if (wmsCount < textBoxesCount && lineSize > 0)
{
@ -576,7 +579,56 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
ResizeDlgToFitEnabledControls();
}
void ResizeDlgToFitEnabledControls()
private void InitializeTextBoxArrays()
{
//if is initialized before we ignore initialization
if (labels != null
&& counters != null
&& messages != null
&& checkBoxes != null
&& ckbIndex != null
&& ckbState != null) return;
labels = new Label[MaxTextBoxesCount]
{
wmLabel1, wmLabel2, wmLabel3, wmLabel4, wmLabel5, wmLabel6, wmLabel7, wmLabel8, wmLabel9, wmLabel10,
wmLabel11, wmLabel12, wmLabel13, wmLabel14, wmLabel15, wmLabel16, wmLabel17, wmLabel18, wmLabel19, wmLabel20,
wmLabel21, wmLabel22, wmLabel23, wmLabel24, wmLabel25, wmLabel26, wmLabel27, wmLabel28, wmLabel29, wmLabel30,
wmLabel31, wmLabel32, wmLabel33, wmLabel34, wmLabel35, wmLabel36, wmLabel37, wmLabel38, wmLabel39, wmLabel40,
wmLabel41, wmLabel42, wmLabel43, wmLabel44, wmLabel45, wmLabel46, wmLabel47, wmLabel48,
};
counters = new PictureBox[MaxTextBoxesCount]
{
pictureBox1, pictureBox2, pictureBox3, pictureBox4, pictureBox5, pictureBox6, pictureBox7, pictureBox8, pictureBox9, pictureBox10,
pictureBox11, pictureBox12, pictureBox13, pictureBox14, pictureBox15, pictureBox16, pictureBox17, pictureBox18, pictureBox19, pictureBox20,
pictureBox21, pictureBox22, pictureBox23, pictureBox24, pictureBox25, pictureBox26, pictureBox27, pictureBox28, pictureBox29, pictureBox30,
pictureBox31, pictureBox32, pictureBox33, pictureBox34, pictureBox35, pictureBox36, pictureBox37, pictureBox38, pictureBox39, pictureBox40,
pictureBox41, pictureBox42, pictureBox43, pictureBox44, pictureBox45, pictureBox46, pictureBox47, pictureBox48,
};
messages = new TextBox[MaxTextBoxesCount]
{
wmTextBox1, wmTextBox2, wmTextBox3, wmTextBox4, wmTextBox5, wmTextBox6, wmTextBox7, wmTextBox8, wmTextBox9, wmTextBox10,
wmTextBox11, wmTextBox12, wmTextBox13, wmTextBox14, wmTextBox15, wmTextBox16, wmTextBox17, wmTextBox18, wmTextBox19, wmTextBox20,
wmTextBox21, wmTextBox22, wmTextBox23, wmTextBox24, wmTextBox25, wmTextBox26, wmTextBox27, wmTextBox28, wmTextBox29, wmTextBox30,
wmTextBox31, wmTextBox32, wmTextBox33, wmTextBox34, wmTextBox35, wmTextBox36, wmTextBox37, wmTextBox38, wmTextBox39, wmTextBox40,
wmTextBox41, wmTextBox42, wmTextBox43, wmTextBox44, wmTextBox45, wmTextBox46, wmTextBox47, wmTextBox48,
};
checkBoxes = new CheckBoxImage[MaxTextBoxesCount]
{
checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5, checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10,
checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15, checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20,
checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25, checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30,
checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35, checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40,
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48,
};
ckbIndex = new int[MaxTextBoxesCount];
ckbState = new bool[MaxTextBoxesCount];
textBoxesCount = MaxTextBoxesCount;
}
void ResizeDlgToFitEnabledControls()
{
int xMax = 0;
int yMax = 0;
@ -645,7 +697,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
}
/// Start communication process by incrementing 'currentGroup'.
currentGroup++;
//currentGroup++;
int wtId = 0;
foreach (var wt in Correction.GetAllThreads())
@ -869,6 +921,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
if (senderCombo == null) return;
SelectedTypeReader = senderCombo.SelectedItem?.ToString();
UpdateHeads();
SmartCommunicationForm_Load(this, EventArgs.Empty);
}
}
}

View File

@ -1,21 +1,28 @@
using TBF.Rig.Configs.NameOnly;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
{
public abstract class SmartComponentBase : ComponentBase, ISmartTestMethod
{
private ITestMethodCfg cfg;
public abstract void MeterCommMilestone(int iItem, bool bValue);
public abstract bool IsMeterCommMilestone(int iItem);
public ITestMethodCfg TestMethodCfg { get => cfg; }
public SmartComponentBase()
: base()
{
cfg = null;
}
public SmartComponentBase(IComponentCfg cfg)
: base(cfg)
{
this.cfg = cfg as ITestMethodCfg;
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
{
public static class EnumExtensions
{
public static bool TryParseByDescription<TEnum>(string description, out TEnum result)
where TEnum : struct, Enum
{
foreach (var field in typeof(TEnum).GetFields())
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if ((attribute != null && attribute.Description == description) ||
field.Name == description)
{
result = (TEnum)field.GetValue(null);
return true;
}
}
result = default;
return false;
}
}
}

View File

@ -1,19 +1,28 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Common;
using Config.Entities;
using log4net;
using Results.Entities;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
using TBF.Rig.RegisterReaders.PoseidonReader;
using TBF.Rig.RegisterReaders.PoseidonReader.communication;
using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
using TBF.Rig.Sequences;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
using TBF.Rig.TestMethods.SmartTest;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
using CheckBoxImage = TBF.Boxes.CheckBoxImage;
using Factory = TBF.Rig.RegisterReaders.iPerlReaderUNI.Factory;
using PoseidonReader = TBF.Rig.RegisterReaders.PoseidonCmdStartStop.PoseidonReader;
namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
{
@ -32,6 +41,16 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
private IList<Test> tests;
private IList<ITestParams> multiTestParams;
private SmartCommunicationForm _parentFrom;
static IList<Thread> workerThreads;
static bool stopWorkerThreads;
static int currentActivityStep;
static int currentGroup;
/// form -> worker thread (0 = none)
static int lastGroup;
static int completedCommCount;
public string TypeIdentificatorName()
@ -56,12 +75,122 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
public IList<ISmartReader> iperlHeads { get => ParentFrom.Heads;}
private Label activityLabel { get => ParentFrom?.ActivityLabel;}
private Label[] labels { get => ParentFrom?.Labels; }
private PictureBox[] counters{get => ParentFrom?.Counters;}
private TextBox[] messages{get => ParentFrom?.Messages;}
private CheckBoxImage[] checkBoxes{get => ParentFrom?.CheckBoxes;}
private int[] ckbIndex{get => ParentFrom?.CkbIndex;}
private bool[] ckbState{get => ParentFrom?.CkbState;}
public void Worker(object threadData)
{
throw new NotImplementedException();
int threadID = (threadData as Boxes.IntBox)?.Val ?? -1;
int activityStep = 0; /// activity step > 0 in case multiTestParams are used
for (int iMultiTestParamsItem = 0; iMultiTestParamsItem < MultiTestParams.Count; iMultiTestParamsItem++)
{
Test currentTest = Tests[iMultiTestParamsItem];
ITestParams currentTestParams = MultiTestParams[iMultiTestParamsItem];
string currentActivity = currentTestParams.Activity; /// Current activity
TBF.UiBridge.TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 10, 0, 140, 0, 0, 0 });
TBF.UiBridge.Bridge.OnTestProgress(null,
new TBF.UiBridge.TestProgressEventArgs(currentTest, Progress.JustStarted));
if (threadID == 0)
{
StartDataStreamProcessingForActiveMeters(iMultiTestParamsItem);
/// A new activity starts - information into RFID data log
rfidDataLogger.InfoFormat("");
rfidDataLogger.WarnFormat("Activity = {0}", currentActivity);
rfidDataLogger.InfoFormat("");
}
for (int group = 1; group <= lastGroup; group++)
{
/// Synchronize with QuidoRS and other threads
while (((group != currentGroup) || (activityStep != currentActivityStep)) &&
!GetStopWorkerThreads())
{
Thread.Sleep(50);
}
if (GetStopWorkerThreads()) break;
// #if TURA_SPECIAL
int threadIx = threadID; /// Just one thread for TURA_SPECIAL
// #else
// for (int threadIx = threadID; threadIx < threadID + 4; threadIx += Cfg.NrThreads)
// #endif
{
bool wmFound = false;
for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++)
{
//TODO BUMI doplnit if podomienky - last grop je teraz 1 ak existuju readre
if(iperlHeads[wmNr0] is SmartReader ihead)
// if ((ihead.Group == group) && (threadIx < muxBrdOrGroup14Nrs.Count) &&
// (ihead.MuxBoardNrOrGroup14 == muxBrdOrGroup14Nrs[threadIx]))
{
wmFound = true;
WaterMeter wm = null;
if (ProcessData.BatchRslts.Batch.WaterMeters != null)
{
foreach (var w in ProcessData.BatchRslts.Batch.WaterMeters)
{
if (w.WMPosition == wmNr0 + 1)
{
wm = w;
break;
}
}
}
//Do worker activity
CommErr error = CommErr.None;
string resultStr = string.Empty;
WorkerActivity(currentActivity, ihead, wm, currentTest,
wmNr0, ref error, ref resultStr, ckbState, threadID, currentActivityStep);
ProcessResultOfWorkerActivity(iMultiTestParamsItem, currentActivity,
currentGroup, ihead, wm, wmNr0, error, resultStr, ckbState, threadID);
break;
}
TBF.UiBridge.Bridge.OnTestProgress(null,
new TBF.UiBridge.TestProgressEventArgs(Tests[iMultiTestParamsItem],
Progress.FlowSetting));
}
if (!wmFound)
{
SmartCommunicationForm.OnCommCompleted(null,
new CommCompletedEventArgs(threadID, -1, null, null, string.Empty,
CommErr.None)); /// Send negative wmNr
}
if (GetStopWorkerThreads()) break;
}
if (GetStopWorkerThreads()) break;
} /// for (int group
TBF.UiBridge.Bridge.OnTestProgress(null,
new TBF.UiBridge.TestProgressEventArgs(Tests[iMultiTestParamsItem], Progress.Completed));
activityStep++;
if (GetStopWorkerThreads()) break;
}
}
public bool WorkerActivity(string currentActivity, ISmartReader iHead, WaterMeter wm, Test currentTest, int wmNr0,
@ -78,17 +207,17 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
public void StopWorkerThreads(bool bStopAllThreads)
{
throw new NotImplementedException();
stopWorkerThreads = bStopAllThreads;
}
public bool GetStopWorkerThreads()
{
throw new NotImplementedException();
return stopWorkerThreads;
}
public IList<Thread> GetAllThreads()
{
throw new NotImplementedException();
return workerThreads;
}
public ICorrections GetNewCorrection()
@ -100,37 +229,229 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
{
throw new NotImplementedException();
}
public ContextMenu GetContextMenu()
{
throw new NotImplementedException();
}
public void PrepareForTestsActivities( int waterMeterPositions0)
{
throw new NotImplementedException();
StartTime = DateTime.Now;
StartTimeSec = StateMachine.Time;
///
/// Prepare worker threads, 'rfidPortNrs', 'lastGroup', etc..
///
currentActivityStep = 0;
currentGroup = 0;
completedCommCount = 0;
stopWorkerThreads = false;
lastGroup = 0;
if (iperlHeads != null)
{
foreach (var iSmartReader in iperlHeads)
{
try
{
if (iSmartReader is SmartReader reader){
if (reader != null ) lastGroup = 1;
}
}
catch (Exception E)
{
log.ErrorFormat("PrepareForTestsActivities: {0}", E.Message);
}
}
}
workerThreads = new List<Thread>();
if (Cfg != null)
{
for (int i = 0; i < Cfg.NrThreads; i++)
{
Thread thread = new Thread(Worker);
thread.CurrentCulture = CultureInfo.CurrentCulture;
thread.CurrentUICulture = CultureInfo.CurrentUICulture;
workerThreads.Add(thread);
}
}
}
public void Load(Label[] labels, PictureBox[] counters, TextBox[] messages, CheckBoxImage[] checkBoxes, int[] ckbIndex,
bool[] ckbState, IList<ISmartReader> iperlHeads, int textBoxesCount, bool checkBoxesEditMode)
{
throw new NotImplementedException();
if (iperlHeads == null)
{
for (int i = 0; i < textBoxesCount; i++)
{
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true;
messages[i].Text = "---";
}
return;
}
///
/// Set checkbox states accroding to iPerlHeads[i].Disabled states
///
for (int i = 0; i < textBoxesCount; i++)
{
labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true;
ISmartReader iperlHead = iperlHeads[i];
if (!checkBoxesEditMode && (iperlHead == null || iperlHead.Disabled))
{
/// iPerl position i+1 is disabled
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = false;
counters[i].BackColor = iPerlCommunicationConstants.DisabledColor;
messages[i].Text = "Strings.Head_was_disabled_by_the_user";
}
else
{
/// iPerl position i+1 is enabled
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true;
messages[i].Text = "---";
}
}
}
public int GetHeadsCount()
{
throw new NotImplementedException();
return ParentFrom?.Heads?.Count() ?? 0;
}
public void StartDataStreamProcessingForActiveMeters(int iMultiTestParamsItem)
{
throw new NotImplementedException();
/// Check whether previous activity was 'Set test mode A0' or 'A4'
if (iMultiTestParamsItem > 0 &&
MultiTestParams[iMultiTestParamsItem - 1].Activity.ToLower()
.Contains(iPerlCommunicationConstants.SetTestModeStr.ToLower()) &&
!MultiTestParams[iMultiTestParamsItem - 1].Activity.Contains("80"))
{
/// Start processing of opto-datastreams from all iPERL-s
int count = 0;
for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++)
{
WaterMeter wm = (ProcessData.BatchRslts.Batch.WaterMeters != null &&
ProcessData.BatchRslts.Batch.WaterMeters.Count > wmNr0)
? ProcessData.BatchRslts.Batch.WaterMeters[wmNr0]
: null;
ISmartReader ihead = iperlHeads[wmNr0];
if (ihead != null && wm != null && !wm.Disabled)
{
lock (ihead)
{
ihead.StartDataStreamProcessing();
count++;
}
}
}
log.WarnFormat("End of activity '{0}', StartDataStreamProcessing() of {1} heads was called.",
MultiTestParams[iMultiTestParamsItem - 1].Activity, count);
}
}
public void DoOnCommCompleted(object sender, CommCompletedEventArgs data, IList<int> waterMeterPositions0)
{
throw new NotImplementedException();
try
{
///
/// Update the text message
///
if (data.WMNr0 >= 0) messages[data.WMNr0].Text = data.CommMessage;
///
/// Update head active/inactive switch
///
if (data.WMNr0 >= 0 && data.CommErr == CommErr.HeadDisabledByUser)
{
/// iPerl head was disabled by the user
ckbState[data.WMNr0] = false;
checkBoxes[data.WMNr0].Checked = false;
checkBoxes[data.WMNr0].Enabled = false;
if (data.Ihead != null) data.Ihead.Disabled = true;
if (data.Wm != null) data.Wm.Disabled = true;
}
else if (data.WMNr0 >= 0 && data.CommErr == CommErr.None)
{
/// One RFID communication successful => iPerl cannot be disabled by the user anymore
ckbState[data.WMNr0] = true;
checkBoxes[data.WMNr0].Checked = true;
checkBoxes[data.WMNr0].Enabled = false;
}
///
/// Update opto-communication indication
///
for (int i = 0; i < iperlHeads.Count; i++)
{
if (iperlHeads[i] == null || iperlHeads[i].Disabled)
{
counters[i].BackColor = iPerlCommunicationConstants.DisabledColor;
}
else
{
if (iperlHeads[i] is SmartReader iperlHead)
{
OptoHeadState checkFlowDirection =
((iperlHead == null) ? OptoHeadState.Disabled : iperlHead.CheckFlowDirection());
switch (checkFlowDirection)
{
case OptoHeadState.OptoAndDirOK:
counters[i].BackColor = iPerlCommunicationConstants.OptoAndDirOKColor;
break;
case OptoHeadState.DirNok:
counters[i].BackColor = iPerlCommunicationConstants.DirNokColor;
break;
default:
case OptoHeadState.OptoNok:
counters[i].BackColor = iPerlCommunicationConstants.OptoNokColor;
break;
}
}
}
}
#if !TURA_SPECIAL
///
/// Branch
///
lock (this)
{
if (++completedCommCount < 4) return;
completedCommCount = 0;
}
#endif
if (currentGroup < lastGroup)
{
/// Go to the next step / next group
currentGroup++;
}
else if (currentActivityStep + 1 < MultiTestParams.Count)
{
currentGroup = 0;
currentActivityStep++;
activityLabel.Text = MultiTestParams[currentActivityStep].Activity;
currentGroup++;
}
else
{
/// Wait until all threads are finished
workerThreads[data.ThreadId].Join(2000);
ParentFrom.NormalClose();
}
}
catch (Exception e)
{
log.ErrorFormat("DoOnCommCompleted({0}) failed: {1}", data, e.Message);
log.FatalFormat("StackTrace : {0}{1}", Environment.NewLine, e.StackTrace);
}
}
public void NormalClose(IList<int> waterMeterPositions0)
@ -171,7 +492,129 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
this.cfg = cfg;
this.multiTestParams = multiTestParams;
}
//////////////////////////////////////////////////////////////
///
private MenuItem NewMenuItem(string text, string tag)
{
MenuItem menuItem = new MenuItem { Text = text, Tag = tag };
menuItem.Click += OnClick_Optical_Heads_Settings_Menu;
return menuItem;
}
private async void OnClick_Optical_Heads_Settings_Menu(object sender, EventArgs e)
{
// Validate sender
if (!(sender is MenuItem menuItem))
{
log?.Error("OnClick_Optical_Heads_Settings_Menu: sender is not a MenuItem");
return;
}
if (activityLabel != null)
activityLabel.Text = menuItem.Text;
List<Task> tasks = new List<Task>();
foreach (var iSmartReader in ProcessData.SmartHeadsUni)
{
if (!(iSmartReader is SmartReader iHead))
{
continue;//ignore different types of heads
}
int position = iHead.Position;
if (position < 0 || position >= checkBoxes.Length || position >= messages.Length)
{
continue; // Skip this head if position is out of range
}
if (!checkBoxes[position].Checked)
{
if (position < messages.Length) messages[position].Text = "";
continue;
}
messages[position].Text = $@"COM{iHead.RfidComPortNr}";
Application.DoEvents(); // Refresh UI
tasks.Add(Task.Run(async () =>
{
string result = await ProcessTask(iHead.RegPoseidonCfg, menuItem.Tag);
ParentFrom?.Invoke((Action)(() =>
{
messages[position].Text = result;
Application.DoEvents(); // Refresh UI
}));
}));
}
await Task.WhenAll(tasks);
}
public static bool TryParseByDescription(string description, out PoseidonImplHeadTestCtrl.Operations result)
{
foreach (PoseidonImplHeadTestCtrl.Operations op
in Enum.GetValues(typeof(PoseidonImplHeadTestCtrl.Operations)))
{
// step-by-step compare
var desc = ((Enum)op).ToDescription(); // uses extension above
// exact compare, you can use OrdinalIgnoreCase if you want
if (string.Equals(desc, description, StringComparison.Ordinal))
{
result = op;
return true;
}
}
result = PoseidonImplHeadTestCtrl.Operations.Empty;
return false;
}
private async Task<string> ProcessTask(IComponentCfg head, object tag)
{
string txt = "";
PoseidonImplHeadTestCtrl.Operations operation;
if (!TryParseByDescription((string)tag, out operation))
{
operation = PoseidonImplHeadTestCtrl.Operations.Empty;
}
if (head is PoseidonCfg poseidonCfg)
{
switch (operation)
{
case PoseidonImplHeadTestCtrl.Operations.ReadSerialNo:
txt = OpticalHeadTest.ReadRequest_SerialNo(poseidonCfg);
break;
case PoseidonImplHeadTestCtrl.Operations.SetTestModeOn:
txt = OpticalHeadTest.SetTestMode(poseidonCfg);
break;
case PoseidonImplHeadTestCtrl.Operations.SetTestModeOff:
txt = OpticalHeadTest.SetActiveMode(poseidonCfg);
break;
default:
txt = "-";
break;
}
}
return txt;
}
public ContextMenu GetContextMenu()
{
ContextMenu cm = new ContextMenu();
foreach (KeyValuePair<string, PoseidonImplHeadTestCtrl.Operations> itemsOperation in PoseidonImplHeadTestCtrl.ItemsOperations)
{
cm.MenuItems.Add(NewMenuItem(itemsOperation.Key, itemsOperation.Value.ToDescription()));
}
return cm;
}
}
}

View File

@ -2067,6 +2067,7 @@
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\common\ICommonRegReader.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\common\ICorrections.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\common\ISmartReader.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\EnumExtensions.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\IPerlCorrections.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\PoseidonCorrections.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\SmartComponentBase.cs" />

View File

@ -0,0 +1,41 @@
using Common;
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.PoseidonReader.implementations;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations;
namespace TBFTests.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
{
[TestClass]
[TestSubject(typeof(PoseidonCorrections))]
public class PoseidonCorrectionsTest
{
[TestMethod]
public void TryParseByDescription_test()
{
PoseidonImplHeadTestCtrl.Operations testOp = PoseidonImplHeadTestCtrl.Operations.ReadSerialNo;
ValidateOperationParsing(testOp.ToDescription());
testOp = PoseidonImplHeadTestCtrl.Operations.SetTestModeOn;
ValidateOperationParsing(testOp.ToDescription());
testOp = PoseidonImplHeadTestCtrl.Operations.SetTestModeOff;
ValidateOperationParsing(testOp.ToDescription());
//negative test
ValidateOperationParsing("khvcdh jkbhvf", false);
}
private static void ValidateOperationParsing(string tag, bool expectedResult = true)
{
PoseidonImplHeadTestCtrl.Operations operation;
if (!PoseidonCorrections.TryParseByDescription((string)tag, out operation))
{
Assert.IsFalse(expectedResult);
}
else
{
Assert.IsTrue(operation.ToDescription() == tag);
}
}
}
}

View File

@ -107,6 +107,7 @@
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\PoseidonCorrectionsTest.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />