FM2014: - common write

This commit is contained in:
Thomas Wiedebusch 2026-01-13 10:08:09 +01:00
parent 735525c2c2
commit ba1569ae10
5 changed files with 180 additions and 46 deletions

View File

@ -59,7 +59,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
private const Char _eos = FM2014CmdDef.END_OF_STRING;
/// <inheritdoc/>
public Boolean ReceiveTaskIsActive
public Boolean CyclicReceiveTaskLoopIsActive
{
get;
private set;
@ -94,13 +94,42 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
private set;
}
private const Char _adrPre = FM2014CmdDef.ADDR_RECORD_CHAR;
private const Char _adrPost = FM2014CmdDef.ADDR_VALIDATION_CHAR;
private Int32 _address;
/// <inheritdoc/>
public Int32 Address
{
get => _address;
set
{
_address = value;
AddressStr = $"{_adrPre}{_adrPre}{Address}{_adrPost}{_eos}";
}
}
/// <summary>
/// Marker if broadcast connection was last communication
/// </summary>
public Boolean BroadcastCommIsActive
{
get;
set;
}
/// <summary>
/// Broadcast address without answer for all FM2014
/// </summary>
public static readonly String BrodcastAddressStr = $"{_adrPre}{_adrPre}0{_adrPost}{_eos}";
/// <inheritdoc/>
public String AddressStr
{
get;
private set;
}
/// <inheritdoc/>
public Int32? TolerancePercent
{
@ -217,7 +246,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <param name="refCtrOn">request to count REF pulses</param>
/// <param name="dutCtrOn">request to count DUT pulses</param>
/// <returns></returns>
/// <remarks date="2026-Jan-10..12" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-10..13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean StartPulseCounting(Boolean refCtrOn = true, Boolean dutCtrOn = false)
@ -227,32 +256,41 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return false;
}
ReceiveTaskIsActive = true;
var cmdName = FM2014CmdDef.CmdName.CMD_NA;
// Prepare REF-pulse counter measurement
try
{
if (refCtrOn && dutCtrOn)
cmdName = FM2014CmdDef.CmdName.CMD_REF_DUT_STR_PLS_CTR;
else if (refCtrOn)
cmdName = FM2014CmdDef.CmdName.CMD_REF_STR_PLS_CTR;
else
cmdName = FM2014CmdDef.CmdName.CMD_DUT_STR_PLS_CTR;
switch (refCtrOn)
{
case true when dutCtrOn:
cmdName = FM2014CmdDef.CmdName.CMD_REF_DUT_STR_PLS_CTR;
break;
case true:
cmdName = FM2014CmdDef.CmdName.CMD_REF_STR_PLS_CTR;
break;
default:
cmdName = FM2014CmdDef.CmdName.CMD_DUT_STR_PLS_CTR;
break;
}
var cmdCodeChar = FM2014CmdDef.GetCmdChar(cmdName);
SerialPort.Write($"{cmdCodeChar}{_eos}");
// Start the pulse counter measurement
if (!Write(cmdName))
{
return false;
}
}
catch (Exception e)
{
var response = new FM2014CmdDef.CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
}
// Measurement Loop
Task.Run(() =>
{
CyclicReceiveTaskLoopIsActive = true;
// Get the process information
var infoRef = FM2014CmdDef.GetCmdInfo(FM2014CmdDef.CmdName.CMD_REF_GET_PLS_CTR);
var cmdCodeRef = FM2014CmdDef.GetCmdChar(FM2014CmdDef.CmdName.CMD_REF_GET_PLS_CTR);
@ -262,9 +300,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
try
{
if (refCtrOn)
if (refCtrOn && Write(FM2014CmdDef.CmdName.CMD_REF_GET_PLS_CTR))
{
SerialPort.Write($"{cmdCodeRef}{_eos}");
var refPulseStr = SerialPort.ReadTo($"{_eos}");
if (int.TryParse(refPulseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var refPulses))
@ -274,9 +311,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(infoRef, specificInfoObj: response));
}
}
if (dutCtrOn)
if (dutCtrOn && Write(FM2014CmdDef.CmdName.CMD_DUT_GET_PLS_CTR))
{
SerialPort.Write($"{cmdCodeDut}{_eos}");
var dutPulseStr = SerialPort.ReadTo($"{_eos}");
if (int.TryParse(dutPulseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var dutPulses))
@ -290,12 +326,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new FM2014CmdDef.CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
}
//Thread.Sleep(500);
} while (ReceiveTaskIsActive);
} while (CyclicReceiveTaskLoopIsActive);
});
return true;
@ -321,30 +357,23 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
SerialPort.Open();
const Char adrPre = FM2014CmdDef.ADDR_RECORD_CHAR;
const Char adrPost = FM2014CmdDef.ADDR_VALIDATION_CHAR;
// Establish connection to one specific FM2014
SerialPort.Write($"{adrPre}{adrPre}{Address}{adrPost}{_eos}");
FullId = SerialPort.ReadTo($"{_eos}");
if (!string.IsNullOrEmpty(FullId))
if (InitiateIndividualComm())
{
FullId = FullId.Replace($"{adrPre}", "");
var fields = FullId.Split('.');
FwVersion = fields[1];
var cmdCode = FM2014CmdDef.GetCmdChar(FM2014CmdDef.CmdName.CMD_SERIAL);
// Read out the serial number of this FM2014
SerialPort.Write($"{cmdCode}{_eos}");
SerialNumber = SerialPort.ReadTo($"{_eos}");
if (!Write(FM2014CmdDef.CmdName.CMD_SERIAL))
{
return false;
}
SerialNumber = SerialPort.ReadTo($"{_eos}");
if (!string.IsNullOrEmpty(SerialNumber))
{
SerialNumber = SerialNumber.Replace($"{cmdCode}", "");
IsLoggedOn = true;
}
}
}
catch (Exception e)
{
@ -353,6 +382,98 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return IsLoggedOn;
}
/// <summary>
/// Initiate the broadcast communication
/// </summary>
private Boolean InitiateBroadcastComm()
{
if (SerialPort == null || !SerialPort.IsOpen)
{
return false;
}
// If broadcast communication is already active
if (BroadcastCommIsActive)
{
return true;
}
// Establish connection to all FM2014s with broadcast address
SerialPort.Write(BrodcastAddressStr);
BroadcastCommIsActive = true;
return true;
}
/// <summary>
/// Initiate communication to individual node
/// </summary>
/// <returns></returns>
private Boolean InitiateIndividualComm()
{
if (SerialPort == null || !SerialPort.IsOpen)
{
return false;
}
// Establish connection to one specific FM2014
SerialPort.Write(AddressStr);
FullId = SerialPort.ReadTo($"{_eos}");
if (!string.IsNullOrEmpty(FullId))
{
FullId = FullId.Replace($"{_adrPre}", "");
var fields = FullId.Split('.');
FwVersion = fields[1];
BroadcastCommIsActive = false;
return true;
}
return false;
}
/// <summary>
/// Write command to FM2014:
/// - Select broadcast or individual addressing based on the command table,
/// - Transmits optional data.
/// </summary>
/// <param name="cmdName"></param>
/// <param name="dataObj"></param>
/// <returns></returns>
public Boolean Write(FM2014CmdDef.CmdName cmdName, Object dataObj = null)
{
if (SerialPort == null || !SerialPort.IsOpen)
{
return false;
}
// Initiate communication using the individual or broadcast address
var retVal = true;
if (FM2014CmdDef.CmdType.BROADCAST == FM2014CmdDef.GetCmdCmdType(cmdName))
{
if (!BroadcastCommIsActive)
{
retVal = InitiateBroadcastComm();
}
}
else
{
if (BroadcastCommIsActive)
{
retVal = InitiateIndividualComm();
}
}
if (retVal)
{
// TODO THW Prepare data object
var cmdCodeChar = FM2014CmdDef.GetCmdChar(cmdName);
SerialPort.Write($"{cmdCodeChar}{_eos}");
}
return retVal;
}
/// <summary>
/// Reset the measurement but only if receive task is active.
/// </summary>
@ -362,19 +483,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </remarks>
public Boolean ResetMeasurement()
{
if (SerialPort == null || !SerialPort.IsOpen)
return false;
if (!ReceiveTaskIsActive)
if (!CyclicReceiveTaskLoopIsActive)
return true;
ReceiveTaskIsActive = false;
CyclicReceiveTaskLoopIsActive = false;
// Reset measurement
var cmdCode = FM2014CmdDef.GetCmdChar(FM2014CmdDef.CmdName.CMD_RST_MEAS);
var infoStr = FM2014CmdDef.GetCmdInfo(FM2014CmdDef.CmdName.CMD_RST_MEAS);
// Read out the serial number of this FM2014
SerialPort?.Write($"{cmdCode}{_eos}");
if (!Write(FM2014CmdDef.CmdName.CMD_RST_MEAS))
return false;
// Wait until FM2014 calibration finished
var resetDelayCtr_ms = 4000;
const Int32 loopTime_ms = 500;

View File

@ -59,7 +59,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// Receive task is active:
/// Used to mark this event and to kill ongoing task if set inactive.
/// </summary>
Boolean ReceiveTaskIsActive
Boolean CyclicReceiveTaskLoopIsActive
{
get;
}
@ -87,13 +87,21 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
/// <summary>
/// FM2014 address string
/// FM2014 address
/// </summary>
Int32 Address
{
get;
}
/// <summary>
/// FM2014 address string
/// </summary>
String AddressStr
{
get;
}
/// <summary>
/// Tolerance output of current loop in percent:
/// - hardware dependent 3% or 5%

View File

@ -44,7 +44,7 @@ namespace Xylem.Common.Logic.SoftwareAccessHelper
}
var url = $"{ServiceUrls.GetSoftwareStartUpAccessUrl()}?Program={program}&Major={major}&Minor={minor}&Build={build}";
var requestResponse = LocalWebRequest.GetRequest(url, 1200);
var requestResponse = LocalWebRequest.GetRequest(url, 8000);
var ret = bool.Parse(requestResponse.Trim('"'));
return ret;

View File

@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=BAUDRATE/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Brodcast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Consts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=LAATZEN/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Meineckestr/@EntryIndexedValue">True</s:Boolean>

View File

@ -624,7 +624,7 @@ namespace Sensus.MiniPrf.Ui
/// </remarks>
private void btnManualRefCalibration_Click(Object sender, EventArgs e)
{
if (!_fm2014.ReceiveTaskIsActive)
if (!_fm2014.CyclicReceiveTaskLoopIsActive)
{
_startTime = DateTimeOffset.UtcNow;
ActionControl(true);
@ -668,7 +668,7 @@ namespace Sensus.MiniPrf.Ui
/// <summary>
/// Feedback from FM2014.
/// </summary>
/// <remarks date="2023-Jan-04..12" author="Thomas Wiedebusch">
/// <remarks date="2023-Jan-04..13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void DataReceived_Handler(Object sender, ProcessExecEventArgs e)
@ -676,7 +676,15 @@ namespace Sensus.MiniPrf.Ui
var resp = (FM2014CmdDef.CmdResponse)e.SpecificInfoObj;
if (resp.IntValue != null)
{
Invoke(new Action(() =>
{
lblActualProcess.Text = resp.AnswerStr;
if (e.ActualProcessPercent != null && !_autoProgressBar && _progressBarOn)
barSingleProgressUpdate.Value = (Int32)e.ActualProcessPercent;
}));
LogText($"{resp.AnswerStr}: {resp.IntValue:D}");
}
}
#endregion
}