MagFluxToolbox: - start of test implementation

This commit is contained in:
Thomas Wiedebusch
2023-04-27 06:56:18 +02:00
parent d9c4839a28
commit f4cf815bbc
14 changed files with 611 additions and 724 deletions
@@ -31,6 +31,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\NLog.4.6.7\lib\net45\NLog.dll</HintPath>
</Reference>
@@ -113,6 +116,10 @@
<Project>{26afcf1e-1287-48b8-a506-32b1e8a0dd0e}</Project>
<Name>WaterMeterRegisters</Name>
</ProjectReference>
<ProjectReference Include="..\DataPackages\DataPackages.csproj">
<Project>{BA17D20E-0CEB-4B27-9772-2392BD750ED5}</Project>
<Name>DataPackages</Name>
</ProjectReference>
<ProjectReference Include="..\MagFluxConfig\MagFluxConfig.csproj">
<Project>{70398b99-b134-4bbd-968b-7399b70c3ce2}</Project>
<Name>MagFluxConfig</Name>
@@ -2,13 +2,21 @@
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.Genesis.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.MagFluxConfig.Const;
using Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
@@ -72,6 +80,17 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
#region variables and properties
/// <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 pcb serial number
/// </summary>
@@ -134,10 +153,62 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
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;
get
{
return _myProcessStatus;
}
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");
}
}
}
/// <inheritdoc />
@@ -295,7 +366,7 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
/// 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>
public void SetupMagFluxMeter(Int32 slot, PortConfig? requestPort, PortConfig? streamingPort,
private void SetupMagFluxMeter(Int32 slot, PortConfig? requestPort, PortConfig? streamingPort,
Boolean ignoreCorruptedData = true, String password = "")
{
Slot = slot;
@@ -304,12 +375,10 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
if (requestPort != null)
{
String requestIdent;
if (requestPort.Value.Type == "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.UartSerialPort")
{
requestIdent = $"Slot:{Slot}, Port:{requestPort.Value.PortName}, Protocol:Request, Type:UART -";
var requestIdent = $"Slot:{Slot}, Port:{requestPort.Value.PortName}, Protocol:Request, Type:UART -";
//CRC calculation does include the CRC itself filled with 0x0000 in advance
RequestProtocol = new RequestProtocol(requestIdent);
TransmitProtocol = new UartTransmitProtocol(requestPort.Value.PortName);
RequestProtocol.SetTransmitProtocol(TransmitProtocol);
@@ -335,86 +404,162 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
#endregion
#region logging
private void SetLogger()
{
if (_logger != null)
{
LogManager.Flush();
}
if (string.IsNullOrEmpty(CurrentActionText))
{
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{Slot}", $"", "Meter", "MeterBase", "MeterBase");
}
else
{
var id = !string.IsNullOrEmpty(SerialNumber) ? $"SN{SerialNumber}" : $"PCB{PcbId}";
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{Slot}_{id}_{CurrentActionText}", $"Slot", "Meter", "MeterBase", "MeterBase");
if (StreamingPort != null && 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
/// <inheritdoc />
/// <summary>
/// Start Event listening, has to be called after <see cref="SetupMagFluxMeter" />
/// </summary>
public void ConnectMeter()
{
throw new NotImplementedException();
if (RequestPort != null && RequestPort.GetPortName() != BaseSerialPort.PortNotAssigned)
{
LinkPortToProtocol(RequestPort, RequestProtocol);
_logger.Trace($"Slot:{Slot} - RequestPort and RequestProtocol linked");
RequestProtocol.OnRecordIsDecoded += RequestProtocol_ApplyDecodedRecord;
}
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>
public 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>
public 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");
}
/// <inheritdoc />
public void SetupFromConfigFile(Int32 slot, Boolean ignoreCorruptedData = true)
{
throw new NotImplementedException();
SetupFromConfigFile(slot, ignoreCorruptedData, true, true);
}
/// <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>
public void SetupFromConfigFile(Int32 slot, Boolean ignoreCorruptedData, Boolean useRequest, Boolean useStreaming)
{
Slot = slot;
SetLogger();
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");
}
_logger.Debug($"Slot:{slot} - Configuration loaded");
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);
}
}
/// <inheritdoc />
public Boolean Logout()
{
throw new NotImplementedException();
return true;
}
/// <inheritdoc />
public Boolean Login()
{
throw new NotImplementedException();
return true;
}
/// <inheritdoc />
public Boolean Login(String password, Boolean runImmediately = true, Boolean skipReadMeterFwAndAssignRegisters = false)
{
throw new NotImplementedException();
return true;
}
/// <inheritdoc />
@@ -426,13 +571,35 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
/// <inheritdoc />
public void StartMeasurement()
{
throw new NotImplementedException();
_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()
{
throw new NotImplementedException();
_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 />
@@ -537,7 +704,186 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore
/// <inheritdoc />
public void DisposeMeter()
{
throw new NotImplementedException();
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);
}
}
#region Events
//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>
/// 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="FlowTestRecord" /> or <see cref="CalibDataEventArgs" />
/// 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);
}
}
#endregion
#region logging
private void SetLogger()
{
if (_logger != null)
{
LogManager.Flush();
}
if (string.IsNullOrEmpty(CurrentActionText))
{
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{Slot}", $"", "Meter", "MeterBase", "MeterBase");
}
else
{
var id = !string.IsNullOrEmpty(SerialNumber) ? $"SN{SerialNumber}" : $"PCB{PcbId}";
_logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{Slot}_{id}_{CurrentActionText}", $"Slot", "Meter", "MeterBase", "MeterBase");
if (StreamingPort != null && 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
}
}
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
<package id="NLog" version="4.6.7" targetFramework="net48" />
</packages>