FM2014, MiniPrf: - rounding issues solved

This commit is contained in:
Thomas Wiedebusch
2026-01-25 12:19:27 +01:00
parent d4af0bdbfa
commit 6f381395b3
2 changed files with 163 additions and 151 deletions
@@ -215,7 +215,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
_ref_pulse_per_cm = value;
if (_dut_pulse_per_cm != 0 && _ref_pulse_per_cm != 0)
RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 2);
RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 5);
}
}
@@ -235,7 +235,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
_dut_pulse_per_cm = value;
if (_dut_pulse_per_cm != 0 && _ref_pulse_per_cm != 0)
RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 2);
RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 5);
}
}
@@ -248,6 +248,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
public const Double RefToDutScaleMax = 999.9;
/// <summary>
/// The mantissa for the scale value has to be in the range from 1000 to 9999.
/// </summary>
private const UInt16 MantissaScaleMin = 1000;
/// <summary>
/// As the scale needed to be sent to the FM2014 doesn't follow the standardized nomenclature
/// for floating point values it has to be converted to the FM2014 requirement.
@@ -259,6 +264,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// "1000K0" = 0.1
/// "1000K1" = 1.0
/// "1000K2" = 10.0
/// "9999K2" = 99.99
/// "1000K3" = 100.0
/// Max value is "9999K3" = 999.9
/// </summary>
@@ -266,7 +272,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
private Double _refToDutScale_norm = 10.0f;
/// <inheritdoc/>
/// <remarks date="2026-Jan-14..20" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-14..25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Double RefToDutScale_norm
@@ -278,15 +284,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return;
_refToDutScale_norm = value;
var exponent = 4;
var exponent = 4;
var number = _refToDutScale_norm;
while (number < RefToDutScaleMax + 0.1f)
UInt16 mantissa;
do
{
number *= 10.0f;
number *= 10.0;
mantissa = (UInt16)(number + RefToDutScaleMin);
exponent--;
}
var mantissa = (UInt16)number;
} while (mantissa < MantissaScaleMin);
// The pre-generated string will be used to send it to the FM2014 directly
_refToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}";
@@ -808,7 +814,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Jan-18" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2023-Jan-23" author="Thomas Wiedebusch">
/// <remarks date="2023-Jan-25" author="Thomas Wiedebusch">
/// - 'Ref_pulse_per_cm' will be calculated by 'Ref_pulses_per_cm = Dut_pulses_per_cm * RefToDutScale_norm'
/// to avoid an overflow as the REF pulses per volume can store max 9999 pulses. As the Dut_pulse_per_cm
/// is for the smallest meter 1000 Impulses/m³
@@ -911,7 +917,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
cmdName = CmdName.CMD_REF_LPP_SCALE;
info = GetCmdInfo(cmdName);
// Calculate REF pulses per cubic meter
Ref_pulse_per_cm = (UInt32)Math.Round(Dut_pulse_per_cm * RefToDutScale_norm, 2);
Ref_pulse_per_cm = (UInt32)Math.Round(Dut_pulse_per_cm * RefToDutScale_norm, 1);
response = new CmdResponse(cmdName, info, (Int32)Ref_pulse_per_cm);
OnRawRecordReceived?.Invoke(this,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
+146 -140
View File
@@ -589,7 +589,7 @@ namespace Sensus.MiniPrf.Ui
tempRefToDutScale_norm > FM2014.RefToDutScaleMax ||
Fm2014.RefToDutScale_norm < FM2014.RefToDutScaleMin ||
Fm2014.RefToDutScale_norm > FM2014.RefToDutScaleMax)
{
{
tbxScaleRefToDut.BackColor = ColorProcessFailed;
tbxScaleRefToDut.Text = Resources.StrError;
btnSaveRegulationSetup.Enabled = false;
@@ -852,6 +852,24 @@ namespace Sensus.MiniPrf.Ui
Fm2014.UseDampedTolerance = chkUseDampedTolerance.Checked;
}
/// <summary>
/// Common check for key input to edit an integer field:
/// - Allows Left, Right, Back, Delete and 0-9 keys.
/// </summary>
/// <param name="keyCode"></param>
/// <returns>true an allowed key-code</returns>
/// <remarks date="2023-Jan-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private static Boolean MaskEditIntegerInput(Keys keyCode)
{
return keyCode == Keys.Back ||
keyCode == Keys.Left ||
keyCode == Keys.Right ||
keyCode == Keys.Delete ||
Regex.IsMatch($"{keyCode}", @"[0-9]");
}
/// <summary>
/// Redirect keystroke 'Enter' to leaving the cell event
/// </summary>
@@ -868,11 +886,7 @@ namespace Sensus.MiniPrf.Ui
tbxRefPulsesPerCm_Leave(this, null);
SelectNextControl(ActiveControl, true, true, true, true);
}
else if (e.KeyCode != Keys.Back &&
e.KeyCode != Keys.Left &&
e.KeyCode != Keys.Right &&
!Regex.IsMatch($"{e.KeyCode}", @"[0-9]"))
else if (!MaskEditIntegerInput(e.KeyCode))
{
e.SuppressKeyPress = true;
}
@@ -884,7 +898,10 @@ namespace Sensus.MiniPrf.Ui
Thread.Sleep(1);
}).ContinueWith(delegate
{
tbxRefPulsesPerCm_Leave(this, null);
Invoke(new Action(() =>
{
tbxRefPulsesPerCm_Leave(this, null);
}));
});
}
}
@@ -899,63 +916,61 @@ namespace Sensus.MiniPrf.Ui
/// </remarks>
private void tbxRefPulsesPerCm_Leave(Object sender, EventArgs e)
{
Invoke(new Action(() =>
if (Fm2014 == null)
return;
if (string.IsNullOrEmpty(tbxRefPulsePerCm.Text))
{
if (Fm2014 == null)
tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
}
// Setup FM2014
if (int.TryParse(tbxRefPulsePerCm.Text, out var pulses_per_cm))
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Ref_pulse_per_cm;
Fm2014.Ref_pulse_per_cm = (UInt32)pulses_per_cm;
// Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be
// limited during the setup of the FM2014 property!
tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
// During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated
if (!UpdateRefToDutScale())
{
tbxRefPulsePerCm.BackColor = ColorProcessFailed;
return;
if (string.IsNullOrEmpty(tbxRefPulsePerCm.Text))
{
tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
}
// Setup FM2014
if (int.TryParse(tbxRefPulsePerCm.Text, out var pulses_per_cm))
// Check if values have changed and need to be updated in the standalone setup
if (backupPulses_per_cm != Fm2014.Ref_pulse_per_cm)
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Ref_pulse_per_cm;
Fm2014.Ref_pulse_per_cm = (UInt32)pulses_per_cm;
// Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be
// limited during the setup of the FM2014 property!
tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
// During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated
if (!UpdateRefToDutScale())
{
tbxRefPulsePerCm.BackColor = ColorProcessFailed;
return;
}
// Check if values have changed and need to be updated in the standalone setup
if (backupPulses_per_cm != Fm2014.Ref_pulse_per_cm)
{
SetupHasChanged();
}
// SPECIAL BACKUP AND RESTORE FOR REF CALIBRATION
// If the REF pulses per cubic meter have been changed manually, the 'Manual REF Calibration'
// fields have to be cleared if those are different. A backup to restore it may be useful.
if (!tbxRefPulsePerCm.Text.Equals(tbxRefCalibrationResultPulsePerCm.Text) &&
!string.IsNullOrEmpty(tbxRefCalibrationResultPulsePerCm.Text))
{
_backupMeasuredRefPulsesStr = tbxMeasuredRefPulses.Text;
tbxMeasuredRefPulses.Text = "";
_backupRefCalibrationResultPulsePerCmStr = tbxRefCalibrationResultPulsePerCm.Text;
tbxRefCalibrationResultPulsePerCm.Text = "";
_backupWeightScaleVolumeLitersStr = tbxManualInputVolumeLiters.Text;
tbxManualInputVolumeLiters.Text = "";
}
// Restore values if REF pulses per cubic meters is back to the calibrated one
else if (tbxRefPulsePerCm.Text.Equals(_backupRefCalibrationResultPulsePerCmStr))
{
tbxMeasuredRefPulses.Text = _backupMeasuredRefPulsesStr;
tbxRefCalibrationResultPulsePerCm.Text = _backupRefCalibrationResultPulsePerCmStr;
tbxManualInputVolumeLiters.Text = _backupWeightScaleVolumeLitersStr;
}
SetupHasChanged();
}
}));
// SPECIAL BACKUP AND RESTORE FOR REF CALIBRATION
// If the REF pulses per cubic meter have been changed manually, the 'Manual REF Calibration'
// fields have to be cleared if those are different. A backup to restore it may be useful.
if (!tbxRefPulsePerCm.Text.Equals(tbxRefCalibrationResultPulsePerCm.Text) &&
!string.IsNullOrEmpty(tbxRefCalibrationResultPulsePerCm.Text))
{
_backupMeasuredRefPulsesStr = tbxMeasuredRefPulses.Text;
tbxMeasuredRefPulses.Text = "";
_backupRefCalibrationResultPulsePerCmStr = tbxRefCalibrationResultPulsePerCm.Text;
tbxRefCalibrationResultPulsePerCm.Text = "";
_backupWeightScaleVolumeLitersStr = tbxManualInputVolumeLiters.Text;
tbxManualInputVolumeLiters.Text = "";
}
// Restore values if REF pulses per cubic meters is back to the calibrated one
else if (tbxRefPulsePerCm.Text.Equals(_backupRefCalibrationResultPulsePerCmStr))
{
tbxMeasuredRefPulses.Text = _backupMeasuredRefPulsesStr;
tbxRefCalibrationResultPulsePerCm.Text = _backupRefCalibrationResultPulsePerCmStr;
tbxManualInputVolumeLiters.Text = _backupWeightScaleVolumeLitersStr;
}
}
}
/// <summary>
/// Redirect keystroke 'Enter' to leaving the cell event
/// </summary>
@@ -972,11 +987,7 @@ namespace Sensus.MiniPrf.Ui
tbxDutPulsesPerCm_Leave(this, null);
SelectNextControl(ActiveControl, true, true, true, true);
}
else if (e.KeyCode != Keys.Back &&
e.KeyCode != Keys.Left &&
e.KeyCode != Keys.Right &&
!Regex.IsMatch($"{e.KeyCode}", @"[0-9]"))
else if (!MaskEditIntegerInput(e.KeyCode))
{
e.SuppressKeyPress = true;
}
@@ -988,7 +999,10 @@ namespace Sensus.MiniPrf.Ui
Thread.Sleep(1);
}).ContinueWith(delegate
{
tbxDutPulsesPerCm_Leave(this, null);
Invoke(new Action(() =>
{
tbxDutPulsesPerCm_Leave(this, null);
}));
});
}
}
@@ -1003,39 +1017,36 @@ namespace Sensus.MiniPrf.Ui
/// </remarks>
private void tbxDutPulsesPerCm_Leave(Object sender, EventArgs e)
{
Invoke(new Action(() =>
if (Fm2014 == null)
return;
if (string.IsNullOrEmpty(tbxDutPulsePerCm.Text))
{
if (Fm2014 == null)
tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}";
}
// Setup FM2014
if (int.TryParse(tbxDutPulsePerCm.Text, out var pulses_per_cm))
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Dut_pulse_per_cm;
// Try to set the new calculated REF pulses limited by the property setter
Fm2014.Dut_pulse_per_cm = (UInt32)pulses_per_cm;
// Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be
// limited during the setup of the FM2014 property!
tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}";
// During setup of the 'Dut_pulse_per_cm' the 'RefToDutScaleStr' will be generated
if (!UpdateRefToDutScale())
{
tbxDutPulsePerCm.BackColor = ColorProcessFailed;
return;
if (string.IsNullOrEmpty(tbxDutPulsePerCm.Text))
{
tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}";
}
// Setup FM2014
if (int.TryParse(tbxDutPulsePerCm.Text, out var pulses_per_cm))
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Dut_pulse_per_cm;
// Try to set the new calculated REF pulses limited by the property setter
Fm2014.Dut_pulse_per_cm = (UInt32)pulses_per_cm;
// Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be
// limited during the setup of the FM2014 property!
tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}";
// During setup of the 'Dut_pulse_per_cm' the 'RefToDutScaleStr' will be generated
if (!UpdateRefToDutScale())
{
tbxDutPulsePerCm.BackColor = ColorProcessFailed;
return;
}
// Check if values have changed and need to be updated in the standalone setup
if (backupPulses_per_cm != Fm2014.Dut_pulse_per_cm)
{
SetupHasChanged();
}
// Check if values have changed and need to be updated in the standalone setup
if (backupPulses_per_cm != Fm2014.Dut_pulse_per_cm)
{
SetupHasChanged();
}
}));
}
}
/// <summary>
@@ -1054,11 +1065,7 @@ namespace Sensus.MiniPrf.Ui
tbxManualInputVolumeLiters_Leave(this, null);
SelectNextControl(ActiveControl, true, true, true, true);
}
else if (e.KeyCode != Keys.Back &&
e.KeyCode != Keys.Left &&
e.KeyCode != Keys.Right &&
!Regex.IsMatch($"{e.KeyCode}", @"[0-9]"))
else if (!MaskEditIntegerInput(e.KeyCode))
{
e.SuppressKeyPress = true;
}
@@ -1070,7 +1077,10 @@ namespace Sensus.MiniPrf.Ui
Thread.Sleep(1);
}).ContinueWith(delegate
{
tbxManualInputVolumeLiters_Leave(this, null);
Invoke(new Action(() =>
{
tbxManualInputVolumeLiters_Leave(this, null);
}));
});
}
}
@@ -1087,57 +1097,53 @@ namespace Sensus.MiniPrf.Ui
/// </remarks>
private void tbxManualInputVolumeLiters_Leave(Object sender, EventArgs e)
{
Invoke(new Action(() =>
if (Fm2014 == null)
return;
if (string.IsNullOrEmpty(tbxRefPulsePerCm.Text))
{
if (Fm2014 == null)
return;
tbxManualInputVolumeLiters.Text = @"0";
}
// Calculate REF pulses per cubic meter
if (int.TryParse(tbxMeasuredRefPulses.Text, out var pulses) && pulses > 0 &&
int.TryParse(tbxManualInputVolumeLiters.Text, out var liters) && liters > 0)
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Ref_pulse_per_cm;
if (string.IsNullOrEmpty(tbxRefPulsePerCm.Text))
// Calculate the new pulse ratio based on the manual calibration
var pulses_per_cm = (UInt32)(pulses / (liters / 1000.0) + 0.5);
// Try to set the new calculated REF pulses limited by the property setter
Fm2014.Ref_pulse_per_cm = pulses_per_cm;
// If this succeeded, then update the new manual calibrated value
if (pulses_per_cm == Fm2014.Ref_pulse_per_cm)
{
tbxManualInputVolumeLiters.Text = @"0";
}
// Calculate REF pulses per cubic meter
if (int.TryParse(tbxMeasuredRefPulses.Text, out var pulses) && pulses > 0 &&
int.TryParse(tbxManualInputVolumeLiters.Text, out var liters) && liters > 0)
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Ref_pulse_per_cm;
tbxRefCalibrationResultPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
tbxManualInputVolumeLiters.BackColor = ColorStandardInputField;
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField;
// Calculate the new pulse ratio based on the manual calibration
var pulses_per_cm = (UInt32)(pulses / (liters / 1000.0f));
// Try to set the new calculated REF pulses limited by the property setter
Fm2014.Ref_pulse_per_cm = pulses_per_cm;
// If this succeeded, then update the new manual calibrated value
if (pulses_per_cm == Fm2014.Ref_pulse_per_cm)
{
tbxRefCalibrationResultPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}";
tbxManualInputVolumeLiters.BackColor = ColorStandardInputField;
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField;
if (!UpdateRefToDutScale())
{
tbxManualInputVolumeLiters.BackColor = ColorProcessFailed;
return;
}
// Check if values have changed and need to be updated in the standalone setup
if (backupPulses_per_cm != Fm2014.Ref_pulse_per_cm)
{
SetupHasChanged();
}
}
else
if (!UpdateRefToDutScale())
{
tbxManualInputVolumeLiters.BackColor = ColorProcessFailed;
tbxRefCalibrationResultPulsePerCm.BackColor = ColorProcessFailed;
tbxRefCalibrationResultPulsePerCm.Text = Resources.StrError;
return;
}
// Check if values have changed and need to be updated in the standalone setup
if (backupPulses_per_cm != Fm2014.Ref_pulse_per_cm)
{
SetupHasChanged();
}
}
}));
else
{
tbxManualInputVolumeLiters.BackColor = ColorProcessFailed;
tbxRefCalibrationResultPulsePerCm.BackColor = ColorProcessFailed;
tbxRefCalibrationResultPulsePerCm.Text = Resources.StrError;
}
}
}
/// <summary>