Files
laatzen/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/MagFluxMeter.cs
T

1191 lines
42 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using NLog;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Ports.SerialPorts;
using Xylem.Common.Hardware.Interfaces.Protocols.ProtocolCore;
using Xylem.Common.Hardware.Interfaces.Protocols.ProtocolCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol;
using Xylem.Common.Hardware.WaterMeter.MagFlux.DataPackages.DataTypes;
using Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.StreamingProtocol;
using Xylem.Common.Hardware.WaterMeter.MagFlux.DataPackages.EventArguments;
using Xylem.Common.Hardware.WaterMeter.MagFlux.DataPackages.MeasurementRecords;
using Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxConfig;
using Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts;
using Xylem.Common.Utils.Logging;
using XYLEM.Device;
namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
{
/// <inheritdoc cref="IMeter" />
/// <summary>
/// MagFlux meter is the core class for the MJK MagFlux water meter.
/// One meter always is always linked to two port, one for communication and one for LED streaming.
/// </summary>
public class MagFluxMeter : IMeter, IMeterEvents
{
#region Variables and Properties
/// <summary>
/// Mark the streaming catcher as active
/// </summary>
private Boolean _streamingCatcherIsActive;
/// <summary>
/// Enable raw record logging, if set every incoming package will be logged
/// </summary>
private Boolean _enableRawDataLogging;
/// <summary>
/// to reduce the IrdA communication in test bench, preparation will be done once (when SkipPreparationForTestBench is true)
/// </summary>
public Boolean SkipPreparationForTestBench
{
get; set;
}
/// <summary>
/// List of ongoing measurements, base- and calibration-measurements
/// </summary>
private List<IMeasurement> _listOfIMeasurement = new List<IMeasurement>();
/// <summary>
/// Quality watch mode can be used to decode intermediate records and check
/// for actual quality of the measurements.
/// </summary>
public Boolean QualityWatchMode;
/// <summary>
/// Represent FW version
/// </summary>
public String FwVersion
{
get;
private set;
} = "?";
/// <summary>
/// Represent meter size in mm
/// </summary>
public Int32 MeterSizeMm
{
get;
private set;
}
/// <summary>
/// Represent FW version
/// </summary>
public String FwBuildDate
{
get;
private set;
} = "?";
/// <summary>
/// Represent FW GIT hash
/// </summary>
public String FwGitHash
{
get;
private set;
} = "?";
/// <summary>
/// Represent pcb serial number
/// </summary>
public String PcbId
{
get; private set;
}
/// <summary>
/// Represent unique id
/// </summary>
public String UniqueId
{
get; private set;
}
private String _currentActionText = "";
/// <summary>
/// Hold the current process name e.g. FlowTest, Preadjustment for logging
/// </summary>
public String CurrentActionText
{
get => _currentActionText;
set
{
_currentActionText = value;
SetLogger();
}
}
///// <summary>
///// Add on ctor a password and it will be used for login in if no other password is set
///// </summary>
//private String _password;
/// <summary>
/// request port assignment/info
/// </summary>
private IPort RequestPort
{
get;
set;
}
/// <summary>
/// request protocol assignment/info
/// </summary>
private RequestProtocol RequestProtocol;
/// <summary>
/// streaming port assignment/info
/// </summary>
private IPort StreamingPort
{
get;
set;
}
/// <summary>
/// streaming protocol assignment/info
/// </summary>
private StreamingProtocol StreamingProtocol;
/// <inheritdoc />
public String SerialNumber
{
get; set;
}
/// <summary>
/// do not use it to set ProcessStatus
/// if you want to change ProcessStatus of this genesis use <see cref="ProcessStatus" />
/// <see cref="_myProcessStatus" /> is just store for some routines
/// </summary>
private ProcessState _myProcessStatus;
/// <inheritdoc />
/// if state is change
/// <see cref="OnProcessStatusChanged" />
/// will be invoked.
/// on some states other events will be invoke as well
public ProcessState ProcessStatus
{
get => _myProcessStatus;
private set
{
if (_myProcessStatus == value)
{
return;
}
_myProcessStatus = value;
Invoker(OnProcessStatusChanged);
switch (value)
{
case ProcessState.InitIsActive:
Invoker(OnInitCompleted);
break;
case ProcessState.MeasurementIsActive:
Invoker(OnMeasurementInitCompleted);
break;
case ProcessState.MeasurementIsDone:
Invoker(OnMeasurementCompleted);
break;
case ProcessState.CalibrationIsActive:
Invoker(OnCalibInitCompleted);
break;
case ProcessState.CalibrationIsDone:
Invoker(OnCalibCompleted);
break;
case ProcessState.QRefDone:
case ProcessState.WaitForQRef:
case ProcessState.WaitForCalibration:
case ProcessState.WaitForMeasurement:
case ProcessState.IsNotInit:
//no extra event needed
break;
default:
throw new OverflowException("MagFlux measurement unknown process status");
}
}
}
public Boolean IsLoggedOn
{
private set;
get;
}
/// <inheritdoc />
public ErrorState ErrorStatus
{
private set;
get;
} = ErrorState.None;
/// <inheritdoc />
public Int32 Slot
{
get; set;
}
/// <inheritdoc />
public Int32 IntermediateUpdateTimeS
{
get;
set;
} = CommunicationConfig.MeasurementUpdateTimeS;
/// <summary>
/// Process configuration
/// </summary>
public ProcessConfig Configuration = new ProcessConfig();
/// <summary>
/// Transmit protocol access for underlie objects to change response timeout
/// </summary>
private ITransmitProtocol TransmitProtocol;
//initially do not signal event
private readonly AutoResetEvent _onSyncKeepSessionThread = new AutoResetEvent(false);
private Thread _keepSessionThread;
private readonly CancellationTokenSource _keepSessionToken = new CancellationTokenSource();
private ILogger _logger;
#endregion
#region Events
event EventHandler IMeter.OnDisposeCompleted
{
add
{
// throw new NotImplementedException();
}
remove
{
// throw new NotImplementedException();
}
}
//public event EventHandler<StringEventArgs> HasInformationForCaller;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnCalibCompleted;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnErrorStatusChanged;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnProcessStatusChanged;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnCalibInitCompleted;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnInitCompleted;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnMeasurementInitCompleted;
/// <inheritdoc cref="IMeter" />
public event EventHandler OnMeasurementCompleted;
/// <summary>
/// Response received after request for record
/// </summary>
public event EventHandler<RequestResponseDataEventArgs> OnRequestRecordReceived;
/// <summary>
/// Streaming record is decoded
/// </summary>
public event EventHandler<MagFluxFlowDataEventArgs> OnStreamingRecordDecoded;
///// <summary>
///// call this if session is expired and you need an re-authorization
///// </summary>
//public EventHandler<AuthorizationRequiredEventArgs> AuthorizationRequired;
/// <summary>
/// A helper to invoke events only if someone is listening, otherwise nothing will happen
/// </summary>
/// <param name="handler">Event handler to call</param>
/// <param name="sender">Sender (can be null)</param>
/// <param name="e">Event arguments (can be null)</param>
private void Invoker(EventHandler handler, Object sender = null, EventArgs e = null)
{
if (handler != null)
{
if (sender == null)
{
sender = this;
}
if (e == null)
{
e = new EventArgs();
}
handler.Invoke(sender, e);
}
// ReSharper disable once RedundantIfElseBlock
else
{
//nobody is listening
}
}
/// <summary>
/// Track all incoming led record packages (Calibration and Flow)
/// </summary>
/// <param name="sender"></param>
/// <param name="e">
/// I should be <see cref="MagFluxRecord" />
/// otherwise this method does nothing
/// </param>
private void StreamingProtocol_ApplyDecodedRecord(Object sender, BaseDataEventArgs e)
{
var result = e.GetEventData();
if (result is IMeasurementRecord)
{
if (result is MagFluxRecord)
{
if (!((MagFluxRecord)result).IsValid)
{
return;
}
}
AddData(((IMeasurementRecord)result));
}
}
/// <summary>
/// tracking request record processing
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RequestProtocol_ApplyDecodedRecord(Object sender, BaseDataEventArgs e)
{
if (e is RequestResponseDataEventArgs)
{
OnRequestRecordReceived?.Invoke(sender, (RequestResponseDataEventArgs)e);
}
}
/// <summary>
/// Kick off the streaming catcher.
/// </summary>
public void StartStreamingCatcher()
{
if (_streamingCatcherIsActive)
{
return;
}
if (!(StreamingPort is LedSerialPort))
{
throw new ApplicationException("No valid streaming port");
}
SyncStreamingBuffer(SyncMarkRecord.DecodeEveryPackage);
StreamingProtocol.OnRecordIsDecoded += StreamingCatcher_OnRawRecordReceived;
LogRawData(true);
_streamingCatcherIsActive = true;
}
/// <summary>
/// Cyclic execution of catcher on incoming events.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StreamingCatcher_OnRawRecordReceived(Object sender, BaseDataEventArgs e)
{
OnStreamingRecordDecoded?.Invoke(this, (MagFluxFlowDataEventArgs)e);
}
/// <summary>
/// Stop the streaming catcher.
/// </summary>
/// <returns></returns>
public void StopStreamingCatcher()
{
if (StreamingPort == null || !(StreamingPort is LedSerialPort))
{
throw new ApplicationException("No valid streaming port");
}
LogRawData(false);
StreamingProtocol.OnRecordIsDecoded -= StreamingCatcher_OnRawRecordReceived;
SyncStreamingBuffer(SyncMarkRecord.SkipDecoding);
_streamingCatcherIsActive = false;
}
#endregion
#region class handling
/// <summary>
/// Ctor - default
/// </summary>
/// <remarks date="2023-Apr-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public MagFluxMeter()
{
}
/// <inheritdoc />
public void Dispose()
{
_logger.Trace($"Slot:{Slot} - Start to dispose");
StreamingProtocol.OnRecordIsDecoded -= StreamingProtocol_ApplyDecodedRecord;
RequestProtocol.OnRecordIsDecoded -= RequestProtocol_ApplyDecodedRecord;
LogRawData(false);
LogManager.Flush();
//isPart of Logout
//StopKeepSession();
//Cancel receive tokens
_keepSessionToken?.Cancel();
try
{
Logout();
}
catch (Exception)
{
// ignored
}
//Run thread again to notice CancellationToken has changed
_onSyncKeepSessionThread?.Set();
//this timeout counter is being used for dispose only
var timeoutCounter = 100;
if (_keepSessionThread != null)
{
while (_keepSessionThread.ThreadState != ThreadState.Stopped && timeoutCounter > 0)
{
Thread.Sleep(1);
timeoutCounter -= 1;
}
if (_keepSessionThread.ThreadState != ThreadState.Stopped)
{
//if thread is still running try to abort
// try to avoid abort (takes age to run and is not safe)
_keepSessionThread.Abort();
}
}
_keepSessionThread = null;
RequestPort?.Dispose();
StreamingPort?.Dispose();
RequestProtocol?.Dispose();
StreamingProtocol?.Dispose();
_logger.Trace($"Slot:{Slot} - Dispose is finished");
}
/// <summary>
/// Ctor
/// </summary>
/// <param name="slot">Slot number for test-bench </param>
/// <param name="requestPort"><see cref="PortConfig" /> for request Port</param>
/// <param name="streamingPort"><see cref="PortConfig" /> for streaming Port</param>
/// <param name="ignoreCorruptedData">don´t send CRC error or telegrams with error flag to caller</param>
/// <param name="password">
/// If you have a password for highest access level you need, if you don't want to access the meter
/// (just read led record) than you can leave it null
/// </param>
/// <remarks date="2023-Apr-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public MagFluxMeter(Int32 slot, PortConfig? requestPort, PortConfig? streamingPort,
Boolean ignoreCorruptedData = true, String password = null)
{
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{slot}", "Slot", "Meter", "MeterBase", "MeterBase");
SetupMagFluxMeter(slot, requestPort, streamingPort, ignoreCorruptedData, password);
}
#endregion
#region MagFlux handling
/// <summary>
/// just internal setup. called on ctor or from vb6 setup function
/// </summary>
/// <param name="slot">Slot Number for test-bench </param>
/// <param name="requestPort"><see cref="PortConfig" /> for RFID/UART/IrDA Port</param>
/// <param name="streamingPort"><see cref="PortConfig" /> for LED Port</param>
/// <param name="ignoreCorruptedData">don´t send CRC error or telegrams with error flag to caller</param>
/// <param name="password">
/// If you have a password for highest access level you need, if you don't want to access the meter
/// (just read led record) than you can leave it nullS
/// </param>
private void SetupMagFluxMeter(Int32 slot, PortConfig? requestPort, PortConfig? streamingPort,
Boolean ignoreCorruptedData = true, String password = "")
{
Slot = slot;
//_password = password;
if (requestPort != null)
{
if (requestPort.Value.Type == "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.UartSerialPort")
{
var requestIdent = $"Slot:{Slot}, Port:{requestPort.Value.PortName}, Protocol:Request, Type:UART -";
RequestProtocol = new RequestProtocol(requestIdent);
TransmitProtocol = new UartTransmitProtocol(requestPort.Value.PortName);
//RequestProtocol.SetTransmitProtocol(TransmitProtocol);
// request port is needed to assign the port name from file, else it is not used
// as the IMagFluxRequestProtocol implements the port handling and communication
RequestPort = new UartSerialPort(requestIdent, requestPort.Value.GetSerialPort(),
TransmitProtocol.GetTransmitPortSettings());
}
}
if (streamingPort != null)
{
var ledTransmit = new LedTransmitProtocol(streamingPort.Value.PortName);
var streamingIdent = $"Slot:{Slot}, Port:{streamingPort.Value.PortName}, Protocol:LED";
StreamingPort = new LedSerialPort(streamingIdent, streamingPort.Value.GetSerialPort(),
ledTransmit.GetTransmitPortSettings());
StreamingProtocol = new StreamingProtocol(streamingIdent, ignoreCorruptedData);
}
Configuration = new ProcessConfig(ProgramConfig.ConfigFileName);
SetLogger();
_logger.Debug($"Slot:{Slot} - Setup done");
}
/// <inheritdoc />
/// <summary>
/// Start Event listening, has to be called after <see cref="SetupMagFluxMeter" />
/// </summary>
public void ConnectMeter()
{
if (RequestPort != null && RequestPort.GetPortName() != BaseSerialPort.PortNotAssigned)
{
if (RequestProtocol.Connect(RequestPort.GetPortName()))
{
_logger.Trace($"Slot:{Slot} - RequestPort and RequestProtocol linked");
RequestProtocol.OnRecordIsDecoded += RequestProtocol_ApplyDecodedRecord;
RequestProtocol.CloseConnection();
}
else
{
_logger.Trace($"Slot:{Slot} - RequestPort Connection failed");
}
}
if (StreamingPort != null && StreamingPort.GetPortName() != BaseSerialPort.PortNotAssigned)
{
LinkPortToProtocol(StreamingPort, StreamingProtocol);
_logger.Trace($"Slot:{Slot} - StreamingPort and StreamingProtocol linked");
StreamingProtocol.OnRecordIsDecoded += StreamingProtocol_ApplyDecodedRecord;
}
_logger.Debug($"Slot:{Slot} - Connected and communication started");
}
/// <summary>
/// Add a port to working queue
/// </summary>
/// <param name="port">On <see cref="IPort" /> should one once added in runtime</param>
/// <param name="protocol"></param>
private static void LinkPortToProtocol(IPort port, IProtocol protocol)
{
//for streaming mode only
if (port == null)
{
return;
}
port.OnRawRecordReceived += delegate (Object o, BasePortDataEventArgs rawMsg)
{
protocol.FillDecodingBuffer(rawMsg);
};
try
{
if (!port.IsOpen())
{
port.Open();
}
}
catch (Exception ex)
{
throw new ApplicationException($"Cannot Open Comport. Check TaskManager for other Genesis relevant programs and close them. Also check if the comport is valid! { Environment.NewLine }{ ex.Message}");
}
protocol.OnRecordReadyToSend += delegate (Object o, BasePortDataEventArgs dataToSend)
{
var data = (List<Byte>)dataToSend.GetData();
port.PortWrite(data.ToArray());
};
}
/// <summary>
/// Synchronization of the record for the streaming interface
/// </summary>
/// <param name="markRecord"></param>
/// <exception cref="ApplicationException"></exception>
private void SyncStreamingBuffer(SyncMarkRecord markRecord)
{
if (StreamingPort == null)
{
throw new ApplicationException("Synchronization not possible due to undefined streaming port");
}
StreamingPort.SynchronizeReceiveBuffer(markRecord);
}
private void RequestProtocol_AuthorizationGrant(Object sender, EventArgs e)
{
_logger.Debug($"Slot:{Slot} - Authorization succeeded");
}
public void SetupFromConfigFile(Int32 slot, Boolean ignoreCorruptedData = true)
{
SetupFromConfigFile(slot, ignoreCorruptedData, true, true);
}
/// <summary>
/// Copy list of flow results to <see cref="MagFlux.DataPackages.DataTypes.Calibration"/> and kick off the
/// writing of calibrations to MagFlux.
/// </summary>
/// <param name="results">List of flow results</param>
public void WriteCalibrationTestBench(List<WaterMeter.DataPackages.MeasurementRecords.FlowDeviations> results)
{
if (RequestProtocol == null) return;
var calibrationsCmPerH = new List<Calibration>();
var calibrationCmPerH = new Calibration();
foreach (var result in results)
{
calibrationCmPerH.refFlowRate = (Single)result.MeasuredRefFlowQmPerH;
calibrationCmPerH.dutFlowRate = (Single)result.MeasuredDutFlowQmPerH;
calibrationsCmPerH.Add(calibrationCmPerH);
}
RequestProtocol.SetCalibrationPoints(calibrationsCmPerH);
}
/// <inheritdoc/>
void IMeter.SetupFromConfigFile(Int32 slot, Boolean ignoreCorruptedData, Boolean useRequest, Boolean useStreaming)
{
SetupFromConfigFile(slot, ignoreCorruptedData);
}
/// <summary>
/// Setup water meter from configuration file:
/// -slot,
/// -streaming protocol,
/// -streaming port
/// -request port
/// </summary>
/// <param name="slot"></param>
/// <param name="ignoreCorruptedData"></param>
/// <param name="useRequest"></param>
/// <param name="useStreaming"></param>
/// <exception cref="ApplicationException"></exception>
private void SetupFromConfigFile(Int32 slot, Boolean ignoreCorruptedData, Boolean useRequest, Boolean useStreaming)
{
Slot = slot;
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(MagFlux), ProgramConfig.SerialConfigFileName);
if (!File.Exists(configFile))
{
configFile = Path.Combine(ProgramConfig.SerialConfigFileName);
if (!File.Exists(configFile))
{
throw new ApplicationException($"Configuration file {configFile} not found ");
}
}
var tr = new StreamReader(configFile);
var slotConfigList = JsonConvert.DeserializeObject<SlotConfig[]>(tr.ReadToEnd());
var slotConfig = slotConfigList.FirstOrDefault(t => t.Slot == slot);
if (slotConfig == null)
{
throw new ApplicationException($"Slot:{slot} - File " +
$"{ProgramConfig.SerialConfigFileName} does not contain valid configuration");
}
if (!useRequest)
{
SetupMagFluxMeter(slot, null, slotConfig.Streaming, ignoreCorruptedData);
}
else if (!useStreaming)
{
SetupMagFluxMeter(slot, slotConfig.Request, null, ignoreCorruptedData);
}
else
{
SetupMagFluxMeter(slot, slotConfig.Request, slotConfig.Streaming, ignoreCorruptedData);
}
SetLogger();
_logger.Debug($"Slot:{slot} - Configuration loaded");
}
/// <inheritdoc />
public Boolean Logout()
{
RequestProtocol.CloseConnection();
IsLoggedOn = false;
return true;
}
/// <inheritdoc />
/// <remarks date="2023-Mai-30" author="T.Wiedebusch">
/// - Unique Id introduced,
/// - Sensor Serial number mapped as PcbId.
/// </remarks>
public Boolean Login()
{
if (!RequestProtocol.Connect(RequestPort.GetPortName()))
return IsLoggedOn;
IsLoggedOn = true;
if (RequestProtocol.GetFirmwareVersion(out var fwVersion))
{
FwVersion = fwVersion;
}
if (RequestProtocol.GetFwBuildDate(out var fwBuildDate))
{
FwBuildDate = fwBuildDate;
}
if (RequestProtocol.GetMeterSizeMm(out var meterSizeMm))
{
MeterSizeMm = meterSizeMm;
}
if (RequestProtocol.GetFwGitHash(out var gitHash))
{
FwGitHash = gitHash;
}
if (RequestProtocol.GetUniqueId(out var uniqueId))
{
UniqueId = uniqueId;
}
// The PcbId will be named as SensorSerialNo by MJK
if (RequestProtocol.GetPcbId(out var pcbId))
{
PcbId = pcbId;
}
StreamingProtocol?.SetDecodingMask(pcbId, uniqueId);
return IsLoggedOn;
}
/// <inheritdoc />
public Boolean Login(String password, Boolean runImmediately = true, Boolean skipReadMeterFwAndAssignRegisters = false)
{
return true;
}
/// <inheritdoc />
public void InitMeasurement()
{
_logger.Info($"Slot:{Slot} - Init measurement");
PreMeasurement();
}
private void PreMeasurement()
{
if (!SkipPreparationForTestBench)
{
SkipPreparationForTestBench = true;
}
else
{
WriteLog("Wait on measurement start");
}
ProcessStatus = ProcessState.WaitForMeasurement;
}
/// <inheritdoc />
public void StartMeasurement()
{
_listOfIMeasurement = new List<IMeasurement>
{
new BaseMeasurement(Slot, typeof(MagFluxRecord), IntermediateUpdateTimeS)
};
foreach (var measurement in _listOfIMeasurement)
{
measurement.OnActionStateHasChanged += Measurement_ActionStateHasChanged;
}
_logger.Info($"Slot:{Slot} - Start measurement");
ProcessStatus = ProcessState.MeasurementIsActive;
_listOfIMeasurement.ForEach(m => m.MeasurementState = MeasurementStates.IsWaitingForStartData);
SyncStreamingBuffer(SyncMarkRecord.SyncStart);
}
/// <inheritdoc />
public void StopMeasurement()
{
_logger.Info($"Slot:{Slot} - Stop measurement");
ProcessStatus = ProcessState.MeasurementIsDone;
_listOfIMeasurement.ForEach(m => m.MeasurementState = MeasurementStates.IsWaitingForEndData);
SyncStreamingBuffer(SyncMarkRecord.SyncEnd);
}
private void Measurement_ActionStateHasChanged(Object sender, EventArgs e)
{
SyncStreamingBuffer(SyncMarkRecord.DecodeIntermediate);
}
/// <inheritdoc />
public MeasurementResults GetMainMeasurementResult(Double? refVolume, Double? refTimeS,
Boolean intermediateMeasurement = false)
{
if (!intermediateMeasurement && GetMeasurementState(0) != MeasurementStates.IsCompleted)
{
throw new ApplicationException("Measurement is not completed");
}
return GetAllMeasurementResults(refVolume, refTimeS, intermediateMeasurement, 0).First();
}
/// <inheritdoc />
public List<MeasurementResults> GetAllMeasurementResults(Double? refVolume, Double? refTimeS, Boolean intermediateMeasurement = false,
Int32? channel = null)
{
if (!intermediateMeasurement && GetMeasurementState(channel) != MeasurementStates.IsCompleted)
{
throw new ApplicationException("Measurement is not completed");
}
var mainMeasurement = _listOfIMeasurement.FirstOrDefault(m => m.GetType() == typeof(MagFluxRecord));
if (mainMeasurement == null)
{
throw new ApplicationException("Can not find an ongoing measurement");
}
var results = new List<MeasurementResults>();
var mainResult = intermediateMeasurement
? mainMeasurement.GetIntermediateMeasurementResult(refVolume, refTimeS)
: mainMeasurement.GetFinalMeasurementResult(refVolume, refTimeS);
results.Add(mainResult);
_logger.Info($"Slot:{Slot}, Channel:{mainMeasurement.GetChannel()} - MeasuredTime({mainResult.DutTimeS}s), " +
$"MeasuredVolume({mainResult.DutVolumeCm}Qm)");
foreach (var otherMeasurement in _listOfIMeasurement.Where(m => m.GetType() != typeof(MagFluxRecord)))
{
try
{
var otherResults = intermediateMeasurement
? otherMeasurement.GetIntermediateMeasurementResult(refVolume, refTimeS)
: otherMeasurement.GetFinalMeasurementResult(refVolume, refTimeS);
_logger.Info($"Slot:{Slot}, Channel:{otherMeasurement.GetChannel()} - ChannelTime({otherResults.DutTimeS}s), " +
$"ChannelVolume({otherResults.DutVolumeCm}Qm)");
results.Add(otherResults);
}
catch (Exception ex)
{
_logger.Warn($"Slot:{Slot}, No Results for channel:{otherMeasurement.GetChannel()} Ex: {ex.Message}");
if (channel == null)
{
throw new ApplicationException($"Can not read result from channel:{otherMeasurement.GetChannel() } Ex: {ex.Message}");
}
}
}
return results;
}
/// <inheritdoc />
public MeasurementStates GetMeasurementState(Int32? onlyChannelNr = null)
{
_logger.Trace($"Slot:{Slot} - Get measurement state");
if (!_listOfIMeasurement.Any())
{
return MeasurementStates.NotStarted;
}
if (onlyChannelNr.HasValue)
{
var measurement = _listOfIMeasurement.First(f => f.GetChannel() == onlyChannelNr.Value);
return measurement.GetMeasurementState();
}
return _listOfIMeasurement.Min(m => m.MeasurementState);
}
/// <inheritdoc />
public Boolean RequestIntermediateMeasurementState(Int32? onlyChannelNr = null)
{
_logger.Trace($"Slot:{Slot} - Get measurement state");
if (!_listOfIMeasurement.Any())
{
return false;
}
Boolean? ret = null;
foreach (var item in _listOfIMeasurement)
{
if (!onlyChannelNr.HasValue || item.GetChannel() == onlyChannelNr.Value)
{
var measurement = _listOfIMeasurement.First(f => onlyChannelNr != null && f.GetChannel() == onlyChannelNr.Value);
if ((measurement.MeasurementState == MeasurementStates.IsRunning
|| measurement.MeasurementState == MeasurementStates.IsWaitingForIntermediateData))
{
measurement.RequestIntermediateRecord();
if (!ret.HasValue)
{
ret = true;
}
}
else
{
ret = false;
}
}
}
return ret.HasValue ? ret.Value : false;
}
/// <inheritdoc/>>
public String GetPcbId(Int32 expectedLength = 12)
{
if (!string.IsNullOrEmpty(PcbId) && !string.Equals("?", PcbId))
{
return PcbId;
}
var pcbId = PcbId;
RequestProtocol?.GetPcbId(out pcbId);
PcbId = pcbId;
return pcbId;
}
/// <summary>
/// Write calibration values to MagFlux
/// </summary>
public void WriteCalibration(List<Calibration> calibrationsCmPerH)
{
_logger.Info($"Slot:{Slot} - Write calibration values to MagFlux");
RequestProtocol?.SetCalibrationPoints(calibrationsCmPerH);
}
/// <summary>
/// Read calibration values to MagFlux
/// </summary>
public void ReadCalibration(out List<Calibration> calibrationsCmPerH)
{
calibrationsCmPerH = null;
_logger.Info($"Slot:{Slot} - Read calibration values from MagFlux");
RequestProtocol?.GetCalibrationPoints(out calibrationsCmPerH);
}
/// <inheritdoc />
public void InitCalibration()
{
_logger.Info($"Slot:{Slot} - Init calibration and set calibration values of all channels to default");
if (RequestProtocol == null)
return;
if (RequestProtocol.PrepareDeviceForCalibration())
PreMeasurement();
}
/// <inheritdoc />
public void StartCalibration()
{
StartMeasurement();
}
/// <inheritdoc />
public void StopCalibration()
{
StopMeasurement();
RequestProtocol?.AbortDeviceForCalibration();
}
/// <inheritdoc />
public MeasurementStates GetCalibrationState()
{
return GetMeasurementState();
}
/// <inheritdoc />
public void BuildAndCheckCalibFactorsAllChannels(Double refVolumeCm, Double? refTimeS = null,
Double reqDeviationPercent = 0, Double maxCalibFactorTolerancePercent = 0)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void SetCalibFactorsAllChannels(Boolean justLog = false)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void SetLcdText(Boolean release, Byte[] textInHex)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void SetProcessState(DisplayCodes newState, Boolean webRequest = true)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public DisplayCodes GetLastProcessState()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void WriteLog(String text)
{
_logger.Info($"Slot:{Slot} - {text}");
//if (_enableRawDataLogging)
//{
// if (_loggerRawData == null)
// {
// _loggerRawData = NLogHelper.CreateOrGetMultiLogger(
// StreamingPort.GetPortName(), "", "LedRawData", "TargetLedRawDataBase", "LedRawDataBase");
// }
// _loggerRawData.Info($"{text}");
//}
}
/// <inheritdoc />
public void LogRawData(Boolean on)
{
_enableRawDataLogging = on;
if (StreamingPort is LedSerialPort port)
{
port.RecordStreamingRawData = _enableRawDataLogging;
}
}
/// <inheritdoc />
public Boolean CheckStateAfterMeasurement()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void DisposeMeter()
{
Dispose();
}
private void AddData(IMeasurementRecord newRecord)
{
foreach (var measurement in _listOfIMeasurement)
{
if (measurement.GetType() != newRecord.GetType())
{
continue;
}
if (measurement.GetChannel() == newRecord.GetChannel())
{
measurement.AddData(newRecord);
}
}
// when every measurement has state IsRunning or every measurement has state IsCompleted, the streaming buffer set to skip decoding
if (_listOfIMeasurement.All(m => m.MeasurementState == MeasurementStates.IsRunning)
|| _listOfIMeasurement.All(m => m.MeasurementState == MeasurementStates.IsCompleted))
{
SyncStreamingBuffer(QualityWatchMode
? SyncMarkRecord.DecodeIntermediateQuality
: SyncMarkRecord.SkipDecoding);
}
}
#endregion
#region logging
private void SetLogger()
{
if (_logger != null)
{
LogManager.Flush();
}
// the id is a combination of UniqueId stored in PcbId and SensorSN (SensorId)
var id = !string.IsNullOrEmpty(PcbId) ? $"PCBID{PcbId}" : "?";
id += !string.IsNullOrEmpty(SerialNumber) ? $"_SN{SerialNumber}" : "?";
var fileName = $"MagFluxRawData_Slot_{Slot}";
if (string.IsNullOrEmpty(CurrentActionText))
{
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{Slot}", $"", "Meter", "MeterBase", "MeterBase");
if (RequestProtocol != null)
{
var pathName = NLogHelper.GetPath(_logger);
var path = Path.GetDirectoryName(pathName);
RequestProtocol.SetLogLocation(path, fileName);
}
}
else
{
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{Slot}_{id}_{CurrentActionText}", "Slot", "Meter", "MeterBase", "MeterBase");
if (RequestProtocol != null)
{
var pathName = NLogHelper.GetPath(_logger);
var path = Path.GetDirectoryName(pathName);
RequestProtocol.SetLogLocation(path, fileName);
}
//TODO check with roland
if (StreamingPort is LedSerialPort)
{
var streamingIdent = $"LedRawData_Slot_{Slot}_{id}_{CurrentActionText}";
((LedSerialPort)StreamingPort).Ident = streamingIdent;
((LedSerialPort)StreamingPort).RefreshLogger();
if (!string.IsNullOrEmpty(SerialNumber) && CurrentActionText.Contains("PG"))
{
//var pathMain = NLogHelper.GetPath(_logger);
//var pathRaw = NLogHelper.GetPath(((LedSerialPort)StreamingPort).GetRawLogger());
//var basePath = Path.GetDirectoryName(pathMain);
//var testRunString = CurrentActionText.Replace("PG", "");
//var lastChat = pathMain.LastIndexOf('\\');
//pathMain = pathMain.Substring(lastChat + 1);
//pathRaw = Path.GetFileName(pathRaw);
//var tmpUrl = $"http://10.49.40.25/MeterProcessState/api/TestBench/SetMeterLogPathInfo?SerialNumber={SerialNumber}&TestRunId={testRunString}&BasePath={basePath}&MainLogName={pathMain}&LEDName={pathRaw}";
//_logger.Debug($"Request Url: { tmpUrl}");
////ToDO: Update to service URl
//LocalWebRequest.GetRequest(tmpUrl);
}
}
}
}
#endregion
}
}