1398 lines
56 KiB
C#
1398 lines
56 KiB
C#
/*********************************************************************************************************************/
|
|
/*!@file FrmMainMiniPrf.cs
|
|
* @brief Main form for 'MiniPrf' - Single FM2014 Manual Flow Test Station:
|
|
* - Used to regulate one mechanical flow meter - 'DUT - Device under test',
|
|
* - Can be used to calibrate the 'REF - Reference meter'
|
|
*
|
|
* @author Thomas Wiedebusch
|
|
* @date 2025-Nov-13
|
|
*
|
|
* @details <i>Implements the serial communication to the FM2014 .</i>
|
|
*
|
|
* @copyright © SENSUS GmbH 2025..2026. All rights reserved.
|
|
**********************************************************************************************************************/
|
|
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Config;
|
|
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core;
|
|
using Sensus.MiniPrf.Ui.Properties;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts;
|
|
using Xylem.Common.CommonCore.Consts;
|
|
using Xylem.Common.Utils.ProcessExec.EventArguments;
|
|
|
|
namespace Sensus.MiniPrf.Ui
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
/// Main form for 'MiniPrf'
|
|
/// </summary>
|
|
public partial class FrmMainMiniPrf : Form
|
|
{
|
|
#region Properties
|
|
|
|
private static readonly String _assemblyExecRootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
|
|
// cancellation token
|
|
private CancellationTokenSource _cancellationTokenSource;
|
|
private CancellationToken _cancellationToken;
|
|
|
|
/// <summary>
|
|
/// The single FM2014 for this program
|
|
/// </summary>
|
|
private FM2014 Fm2014 { get; set; }
|
|
|
|
/// <summary>
|
|
/// The start time is going to be used for time measurements of processes. It has to be set to
|
|
/// the actual time if the measurement should be (re)started.
|
|
/// </summary>
|
|
private DateTimeOffset _startTime;
|
|
|
|
/// <summary>
|
|
/// The auto progress bar enabled is for infinite processes or if the process doesn't feed
|
|
/// the progress bar with information (e.g. Connect()).
|
|
/// </summary>
|
|
private Boolean _autoProgressBar;
|
|
|
|
// external update remarks form
|
|
private readonly FrmHistory _frmHistory = new FrmHistory();
|
|
|
|
private readonly Version _version;
|
|
|
|
// status information
|
|
private static readonly Color ColorDefault = Color.Black;
|
|
private static readonly Color ColorSuccess = Color.Green;
|
|
|
|
private static readonly Color ColorProcessFailed = Color.Red;
|
|
//private static readonly Color ColorOngoingProcess = Color.Blue;
|
|
//private static readonly Color ColorUnknownStatus = Color.Gray;
|
|
private static Color ColorStandardInputField;
|
|
private static Color ColorStandardDisplayField;
|
|
|
|
//private const String SuccessSign = @"✔";
|
|
//private const String FailedSign = @"✘";
|
|
|
|
private const String StrSeparator = "--------------------------------------------------" +
|
|
"--------------------------------------------------" +
|
|
"--------------------------------------------------";
|
|
|
|
//private readonly ILogger _logger = NLogHelper.CreateOrGetLogger("FM2014");
|
|
private readonly FM2014Config _fm2014Config = new FM2014Config();
|
|
|
|
// internal reminders of changed items to avoid write access on startup if items are preloaded
|
|
private Int32 _comPortIdx;
|
|
private Int32 _addressIdx;
|
|
private Int32 _currentOutputDisplayRangeIdx;
|
|
|
|
// Reminder of settings in standalone measurement to avoid repeated storage af already stored values
|
|
private UInt32 _initialRefPulsesPerCm;
|
|
private UInt16 _initialDutPulsesPerCm;
|
|
private Byte _initialCurrentOutputAttenuation;
|
|
|
|
/// <summary>
|
|
/// The regulation setup will be stored directly to the FM2014 being able to use it at startup
|
|
/// </summary>
|
|
private Boolean _fm2014RegulationSetupHasChanged;
|
|
|
|
/// <summary>
|
|
/// If the program configuration has changed, this shall be stored to the configuration file
|
|
/// </summary>
|
|
private Boolean _programConfigSetupHasChanged;
|
|
|
|
#endregion Properties
|
|
|
|
#region FormControls
|
|
/// <summary>
|
|
/// Ctor
|
|
/// </summary>
|
|
/// <remarks date="2025-Nov-13" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public FrmMainMiniPrf()
|
|
{
|
|
InitializeComponent();
|
|
//var cultureInfo = new CultureInfo("en-GB");
|
|
//Thread.CurrentThread.CurrentUICulture = cultureInfo;
|
|
//Thread.CurrentThread.CurrentCulture = cultureInfo;
|
|
_version = Assembly.GetExecutingAssembly().GetName().Version;
|
|
|
|
ColorStandardDisplayField = lblFM2014SerialPort.BackColor;
|
|
ColorStandardInputField = cbxProgramComPort.BackColor;
|
|
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
_cancellationToken = _cancellationTokenSource.Token;
|
|
|
|
helpToolStripMenuItem.Text = Resources.StrMenuHelp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear data table and set genesis to not connected
|
|
/// </summary>
|
|
/// <remarks date="2025-Nov-13" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void Init()
|
|
{
|
|
ActionControl(false);
|
|
_autoProgressBar = false;
|
|
_fm2014RegulationSetupHasChanged = false;
|
|
_programConfigSetupHasChanged = false;
|
|
|
|
gbxDutToRefRegulation.Visible = true;
|
|
gbxRegulationSetup.Visible = true;
|
|
|
|
if (_fm2014Config != null)
|
|
{
|
|
// Load the configuration from the local file stored in AppData\FM2014
|
|
_fm2014Config.ReadFM2014Config();
|
|
|
|
// Display actual com-port, if not assigned use the '?'. This will be placed on Items[0]
|
|
if (_fm2014Config.SharedSerialPort != null && !cbxProgramComPort.Items.Contains(_fm2014Config.SharedSerialPort))
|
|
{
|
|
cbxProgramComPort.Items.Add(_fm2014Config.SharedSerialPort);
|
|
}
|
|
else if (_fm2014Config.SharedSerialPort == null && !cbxProgramComPort.Items.Contains("?"))
|
|
{
|
|
cbxProgramComPort.Items.Add("?");
|
|
}
|
|
|
|
// Collect and add all com-ports from device manager to items
|
|
var comPorts = SerialPort.GetPortNames().ToList();
|
|
foreach (var comPort in comPorts.Where(comPort => !cbxProgramComPort.Items.Contains(comPort)))
|
|
{
|
|
cbxProgramComPort.Items.Add(comPort);
|
|
}
|
|
cbxProgramComPort.Text = cbxProgramComPort.Items[_comPortIdx].ToString();
|
|
|
|
// Select the last address
|
|
var itemContent = _fm2014Config.Address ?? 1;
|
|
for (var idx = 0; idx < cbxFM2014Address.Items.Count; idx++)
|
|
{
|
|
if (!cbxFM2014Address.Items[idx].ToString().Equals(itemContent.ToString())) continue;
|
|
|
|
_addressIdx = idx;
|
|
cbxFM2014Address.SelectedItem = cbxFM2014Address.Items[idx];
|
|
break;
|
|
}
|
|
|
|
// Select the current output range
|
|
itemContent = _fm2014Config.CommonCurrentOutputDisplayRangeNominal_percent;
|
|
for (var idx = 0; idx < cbxToleranceDisplayRange.Items.Count; idx++)
|
|
{
|
|
if (!cbxToleranceDisplayRange.Items[idx].ToString().Equals(itemContent.ToString())) continue;
|
|
|
|
_currentOutputDisplayRangeIdx = idx;
|
|
cbxToleranceDisplayRange.SelectedItem = cbxToleranceDisplayRange.Items[idx];
|
|
break;
|
|
}
|
|
|
|
// Restore baudrate
|
|
FM2014.Baudrate = _fm2014Config.Baudrate;
|
|
}
|
|
|
|
// FM2014 group box
|
|
lblConnectFM2014.Text = Resources.StrLblFM2014NotConnected;
|
|
lblConnectFM2014.ForeColor = ColorProcessFailed;
|
|
tbxFM2014FullId.Text = "";
|
|
tbxFM2014ApplicationFwVersion.Text = "";
|
|
lblFM2014SerialNumber.Text = Resources.StrLblFM2014SerialNumber;
|
|
tbxFM2014SerialNumber.Text = "";
|
|
lblFM2014SerialPort.Text = Resources.StrLblFM2014SerialPort;
|
|
lblFM2014Address.Text = Resources.StrLblFM2014Address;
|
|
lblFM2014Tolerance.Text = Resources.StrLblFM2014Tolerance;
|
|
btnFM2014Connect.Text = Resources.StrBtnConnect;
|
|
|
|
// Regulation Setup group box
|
|
gbxRegulationSetup.Text = Resources.StrGbxRegulationSetup;
|
|
lblRefPulsePerVolume.Text = Resources.StrLblRefPulsesPerCm;
|
|
tbxRefPulsesPerCm.Text = "";
|
|
lblDutPulsePerVolume.Text = Resources.StrLblDutPulsesPerCm;
|
|
tbxDutPulsesPerCm.Text = "";
|
|
lblScaleRefToDut.Text = Resources.StrLblScaleRefToDut;
|
|
tbxScaleRefToDut.Text = "";
|
|
tbxScaleRefToDut.BackColor = ColorStandardDisplayField;
|
|
lblToleranceDisplayAttenuation.Text = Resources.StrLblAttenuation;
|
|
btnSaveSetupToFm2014.Text = Resources.StrBtnSaveRegulationSetup;
|
|
|
|
// Manual REF Calibration group box
|
|
gbxManualRefCalibration.Text = Resources.StrGbxManualRefCalibration;
|
|
lblMeasuredRefPulses.Text = Resources.StrLblMeasuredRefPulses;
|
|
tbxRefPulsesMeasured.Text = "";
|
|
cbxMeasuredDutPulses.Text = Resources.StrLblMeasuredDutPulses;
|
|
tbxDutPulsesMeasured.Text = "";
|
|
lblVolumeMeasuredLiters.Text = Resources.StrLblMeasuredWeightScaleVolume;
|
|
tbxVolumeMeasuredLiters.Text = "";
|
|
tbxVolumeMeasuredLiters.BackColor = ColorStandardInputField;
|
|
lblPulseVolumeCmRelation.Text = Resources.StrLblRefPulsesPerCm;
|
|
tbxRefCalibrationResultPulsePerCm.Text = "";
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField;
|
|
btnManualRefCalibration.Text = Resources.StrBtnStartCalibration;
|
|
|
|
// DUT to REF Regulation group box
|
|
gbxDutToRefRegulation.Text = Resources.StrGbxDutToRefRegulation;
|
|
lblActualFlowRateCmPerHour.Text = Resources.StrLblActualMeasuredFlowRate;
|
|
tbxActualFlowRateCmPerHour.Text = "";
|
|
tbxActualFlowRateCmPerHour.BackColor = ColorStandardDisplayField;
|
|
lblActualMeasuredTolerance.Text = Resources.StrLblActualMeasuredToleranceDutToRef;
|
|
tbxActualMeasuredToleranceDutToRef.Text = "";
|
|
chkUseDampedTolerance.Text = Resources.StrChkUseDampedTolerance;
|
|
btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
|
|
tbxRefFrequencyHz.Text = "";
|
|
tbxRefFrequencyHz.BackColor = ColorStandardDisplayField;
|
|
|
|
// Process status
|
|
lblActualProcess.Text = Resources.StrLblProcessStatus;
|
|
lblTimeText.Text = Resources.StrLblTime;
|
|
lblWaitingForMeterResponse.Text = Resources.StrLblWaitingForMeterResponse;
|
|
lblWaitingForMeterResponse.Visible = false;
|
|
|
|
CheckUpdateEnabled();
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Store the settings adjusted by the UI
|
|
/// </summary>
|
|
/// <remarks date="2025-Nov-13" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2026-Jan-15..Feb-16" author="Thomas Wiedebusch">
|
|
/// - Update FM2014 properties.
|
|
/// </remarks>
|
|
private void StoreProgramConfiguration()
|
|
{
|
|
|
|
if (_fm2014Config == null)
|
|
return;
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
_programConfigSetupHasChanged = false;
|
|
|
|
_fm2014Config.SharedSerialPort = cbxProgramComPort.Text;
|
|
if (int.TryParse(cbxFM2014Address.SelectedItem.ToString(), out var address))
|
|
{
|
|
_fm2014Config.Address = address;
|
|
}
|
|
if (int.TryParse(cbxToleranceDisplayRange.SelectedItem.ToString(), out var tolerancePercent)
|
|
&& (tolerancePercent == FM2014.CURRENT_OUTPUT_DISPLAY_RANGE_STANDARD_NOMINAL_percent ||
|
|
tolerancePercent == FM2014.CURRENT_OUTPUT_DISPLAY_RANGE_EXTENDED_NOMINAL_percent))
|
|
{
|
|
_fm2014Config.CommonCurrentOutputDisplayRangeNominal_percent = tolerancePercent;
|
|
}
|
|
_fm2014Config.Update();
|
|
}));
|
|
}
|
|
|
|
private void FrmMainMiniPrf_Load(Object sender, EventArgs e)
|
|
{
|
|
Init();
|
|
|
|
var xPosition = Location.X + Size.Width;
|
|
var yPosition = Location.Y;
|
|
if (_frmHistory != null)
|
|
{
|
|
_frmHistory.SetDesktopLocation(xPosition, yPosition);
|
|
_frmHistory.Show();
|
|
}
|
|
|
|
var version = $"Version: {_version.Major}.{_version.Minor}.{_version.Build}";
|
|
Text = $@"MiniPrf - {version}";
|
|
LogText($"MiniPrf: {version}");
|
|
LogText(StrSeparator);
|
|
btnFM2014Connect.Focus();
|
|
}
|
|
|
|
private void FrmMainMiniPrf_FormClosed(Object sender, FormClosedEventArgs e)
|
|
{
|
|
_cancellationTokenSource?.Cancel();
|
|
_frmHistory?.Close();
|
|
FM2014.Dispose();
|
|
Dispose();
|
|
}
|
|
#endregion FormControls
|
|
|
|
#region TimerControls
|
|
private void tmrProgressUpdate_Tick(Object sender, EventArgs e)
|
|
{
|
|
if (_autoProgressBar || barSingleProgressUpdate.Visible)
|
|
{
|
|
barSingleProgressUpdate.Value =
|
|
barSingleProgressUpdate.Value + 4 > 100 ? 0 : barSingleProgressUpdate.Value + 4;
|
|
}
|
|
|
|
SetTimeDisplay();
|
|
Update();
|
|
}
|
|
|
|
private void SetTimeDisplay()
|
|
{
|
|
var time = DateTimeOffset.UtcNow;
|
|
var timeSpan = time - _startTime;
|
|
lblUpdateTimeValue.Text = $@"{(UInt32)timeSpan.TotalMinutes}:{timeSpan.Seconds:00}";
|
|
}
|
|
#endregion TimerControls
|
|
|
|
#region ActivationControls
|
|
/// <summary>
|
|
/// Lock all buttons
|
|
/// </summary>
|
|
private void SetFM2014AccessLocked()
|
|
{
|
|
// Disable all buttons
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
btnManualRefCalibration.Enabled = false;
|
|
btnDutToRefRegulation.Enabled = false;
|
|
btnFM2014Connect.Enabled = false;
|
|
|
|
// Disable manual input
|
|
tbxVolumeMeasuredLiters.ReadOnly = true;
|
|
tbxRefPulsesPerCm.ReadOnly = true;
|
|
tbxDutPulsesPerCm.ReadOnly = true;
|
|
|
|
// Disable setting selections
|
|
cbxToleranceDisplayAttenuation.Enabled = false;
|
|
cbxFM2014Address.Enabled = false;
|
|
cbxProgramComPort.Enabled = false;
|
|
cbxToleranceDisplayRange.Enabled = false;
|
|
cbxMeasuredDutPulses.Enabled = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable all buttons, FM2014 has to be connected
|
|
/// </summary>
|
|
private void SetFM2014AccessEnabled()
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
// Enable save setup depending on changed properties
|
|
btnSaveSetupToFm2014.Enabled = _fm2014RegulationSetupHasChanged;
|
|
|
|
// Enable buttons and display correct information
|
|
btnManualRefCalibration.Enabled = true;
|
|
btnDutToRefRegulation.Enabled = true;
|
|
btnFM2014Connect.Enabled = true;
|
|
btnManualRefCalibration.Text = Resources.StrBtnStartCalibration;
|
|
btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
|
|
|
|
// Enable manual input
|
|
tbxVolumeMeasuredLiters.ReadOnly = false;
|
|
tbxRefPulsesPerCm.ReadOnly = false;
|
|
tbxDutPulsesPerCm.ReadOnly = false;
|
|
|
|
// Enable setting selections
|
|
cbxToleranceDisplayAttenuation.Enabled = true;
|
|
cbxFM2014Address.Enabled = true;
|
|
cbxProgramComPort.Enabled = true;
|
|
cbxToleranceDisplayRange.Enabled = true;
|
|
cbxMeasuredDutPulses.Enabled = true;
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restore setting of buttons and timeout after operation with device
|
|
/// </summary>
|
|
private void CheckUpdateEnabled()
|
|
{
|
|
if (Fm2014 == null || !Fm2014.IsLoggedOn)
|
|
{
|
|
SetFM2014AccessLocked();
|
|
btnFM2014Connect.Enabled = true;
|
|
cbxFM2014Address.Enabled = true;
|
|
cbxProgramComPort.Enabled = true;
|
|
return;
|
|
}
|
|
SetFM2014AccessEnabled();
|
|
}
|
|
#endregion ActivationControls
|
|
|
|
#region BoardControls
|
|
/// <summary>
|
|
/// Establish connection
|
|
/// </summary>
|
|
/// <remarks date="2025-Aug-07" author="Thomas Wiedebusch">
|
|
/// - Compare the max supported FW versions of the configuration.json.
|
|
/// </remarks>
|
|
/// <remarks date="2025-Oct-02" author="Thomas Wiedebusch">
|
|
/// - Catch error message on unknown data type and kill meter.
|
|
/// </remarks>
|
|
private void Connect()
|
|
{
|
|
try
|
|
{
|
|
if (_programConfigSetupHasChanged)
|
|
StoreProgramConfiguration();
|
|
|
|
Init();
|
|
|
|
if (string.IsNullOrEmpty(cbxProgramComPort?.SelectedItem?.ToString()) ||
|
|
cbxProgramComPort.SelectedItem.ToString().Equals("?"))
|
|
{
|
|
var text = Resources.StrErrorMsgComPortNotAssigned;
|
|
MessageBox.Show(text, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
LogText(text);
|
|
return;
|
|
}
|
|
|
|
ActionControl(true);
|
|
|
|
if (Fm2014 != null)
|
|
{
|
|
Fm2014.OnRawRecordReceived -= DataReceived_Handler;
|
|
FM2014.Dispose();
|
|
//dispose old meter
|
|
Fm2014 = null;
|
|
}
|
|
|
|
//assign new meter
|
|
Fm2014 = new FM2014();
|
|
|
|
Fm2014.OnRawRecordReceived += DataReceived_Handler;
|
|
ResetCancellationToken();
|
|
FM2014.SharedCancellationToken = _cancellationToken;
|
|
if (int.TryParse(cbxFM2014Address.SelectedItem.ToString(), out var address))
|
|
{
|
|
Fm2014.Address = address;
|
|
}
|
|
else
|
|
{
|
|
ActionControl(false);
|
|
LogErrorText(Resources.StrErrorMsgFM2014AddressInvalid);
|
|
}
|
|
if (int.TryParse(cbxToleranceDisplayRange.SelectedItem.ToString(), out var tolerancePercent)
|
|
&& (tolerancePercent == FM2014.CURRENT_OUTPUT_DISPLAY_RANGE_STANDARD_NOMINAL_percent ||
|
|
tolerancePercent == FM2014.CURRENT_OUTPUT_DISPLAY_RANGE_EXTENDED_NOMINAL_percent))
|
|
{
|
|
Fm2014.CurrentOutputDisplayRangeNominal_percent = tolerancePercent;
|
|
}
|
|
else
|
|
{
|
|
ActionControl(false);
|
|
LogErrorText(Resources.StrErrorMsgFM2014ToleranceInvalid);
|
|
}
|
|
|
|
lblActualProcess.Text = Resources.StrMsgConnecting;
|
|
Fm2014.Connect(cbxProgramComPort.SelectedItem.ToString(), true);
|
|
|
|
Task.Factory.StartNew(() =>
|
|
{
|
|
Fm2014.Login();
|
|
|
|
if (!Fm2014.IsLoggedOn)
|
|
{
|
|
LogErrorText(Resources.StrErrorMsgFM2014AccessDenied);
|
|
Fm2014.Logout();
|
|
ActionControl(false);
|
|
|
|
return;
|
|
}
|
|
|
|
if (Fm2014.IsLoggedOn)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
lblConnectFM2014.Text = Resources.StrLblFM2014Connected;
|
|
lblConnectFM2014.ForeColor = Color.Green;
|
|
tbxFM2014FullId.Text = Fm2014.ConnectResponse;
|
|
tbxFM2014FullId.Visible = true;
|
|
tbxFM2014ApplicationFwVersion.Text = Fm2014.FwVersion;
|
|
tbxFM2014SerialNumber.Text = Fm2014.SerialNumber;
|
|
|
|
// Logging to history window
|
|
LogText("FM2014 ID: " + Fm2014.ConnectResponse);
|
|
LogText("FM2014 Firmware Version: " + Fm2014.FwVersion);
|
|
LogText($"FM2014 {Resources.StrLblFM2014SerialNumber} " + Fm2014.SerialNumber);
|
|
LogText($"FW2014 {Resources.StrLblFM2014Address} " + Fm2014.Address);
|
|
var dt = DateTime.UtcNow;
|
|
var msg = $"{dt:yyyy-MM-dd HH:mm:ss} UTC";
|
|
LogText($"{Resources.StrMsgPcDateTime} {msg}");
|
|
LogText(StrSeparator);
|
|
|
|
CheckUpdateEnabled();
|
|
}));
|
|
}
|
|
}, _cancellationToken).ContinueWith(delegate
|
|
{
|
|
ActionControl(false);
|
|
}, _cancellationToken);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ActionControl(false);
|
|
MessageBox.Show(ex.Message, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
LogErrorText(ex.Message);
|
|
FM2014.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion BoardControls
|
|
|
|
#region ProcessControls
|
|
|
|
/// <summary>
|
|
/// Common method to (de-)activate controls and timer.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-14..20" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void ActionControl(Boolean isActive)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (isActive)
|
|
{
|
|
lblActualProcess.Visible = true;
|
|
barSingleProgressUpdate.Visible = true;
|
|
SetFM2014AccessLocked();
|
|
tmrProgressUpdate.Enabled = true;
|
|
}
|
|
else
|
|
{
|
|
lblActualProcess.Visible = false;
|
|
barSingleProgressUpdate.Visible = false;
|
|
tmrProgressUpdate.Enabled = false;
|
|
lblWaitingForMeterResponse.Visible = false;
|
|
lblActualProcess.Text = "";
|
|
CheckUpdateEnabled();
|
|
}
|
|
//common actions and settings
|
|
lblActualProcess.Update();
|
|
barSingleProgressUpdate.Value = 0;
|
|
barSingleProgressUpdate.Update();
|
|
Update();
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common method to prepare for changed setup.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-14..20" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void DutToRefRegulationSetupHasChanged()
|
|
{
|
|
_fm2014RegulationSetupHasChanged = true;
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
btnSaveSetupToFm2014.Enabled = true;
|
|
btnDutToRefRegulation.Enabled = true;
|
|
|
|
tbxRefFrequencyHz.BackColor = ColorStandardDisplayField;
|
|
tbxActualFlowRateCmPerHour.BackColor = ColorStandardDisplayField;
|
|
tbxActualMeasuredToleranceDutToRef.BackColor = ColorStandardDisplayField;
|
|
|
|
tbxRefPulsesPerCm.BackColor = ColorStandardInputField;
|
|
tbxDutPulsesPerCm.BackColor = ColorStandardInputField;
|
|
tbxScaleRefToDut.BackColor = ColorStandardDisplayField;
|
|
|
|
tbxVolumeMeasuredLiters.BackColor = ColorStandardInputField;
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField;
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method checks if any setting applied to the FM2014 memory has been changed.
|
|
/// Initially, those values will be read and preset during the connection procedure.
|
|
/// </summary>
|
|
/// <remarks date="2026-Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void CheckForFM2014SetupChanged()
|
|
{
|
|
if (Fm2014 == null)
|
|
return;
|
|
|
|
// Avoid stress to the FM2014 EEPROM as the identical values mustn't be stored again
|
|
if (_initialCurrentOutputAttenuation == Fm2014.CurrentOutputAttenuation &&
|
|
_initialRefPulsesPerCm == Fm2014.RefPulses_per_cm &&
|
|
_initialDutPulsesPerCm == Fm2014.DutPulses_per_cm)
|
|
{
|
|
_fm2014RegulationSetupHasChanged = false;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
return;
|
|
}
|
|
|
|
DutToRefRegulationSetupHasChanged();
|
|
}
|
|
/// <summary>
|
|
/// Common routine for REF to DUT scale check and update
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2026-Jan-14..21" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean UpdateRefToDutScale()
|
|
{
|
|
var retVal = true;
|
|
Invoke(new Action(() =>
|
|
{
|
|
tbxScaleRefToDut.Text = $@"{Fm2014.RefToDutScale_norm:F4}";
|
|
|
|
// Display error if scale doesn't fit
|
|
var tempRefToDutScale_norm = (Double)Fm2014.RefPulses_per_cm / Fm2014.DutPulses_per_cm;
|
|
if (tempRefToDutScale_norm < FM2014.REF_TO_DUT_SCALE_MIN ||
|
|
tempRefToDutScale_norm > FM2014.REF_TO_DUT_SCALE_MAX ||
|
|
Fm2014.RefToDutScale_norm < FM2014.REF_TO_DUT_SCALE_MIN ||
|
|
Fm2014.RefToDutScale_norm > FM2014.REF_TO_DUT_SCALE_MAX)
|
|
{
|
|
tbxScaleRefToDut.BackColor = ColorProcessFailed;
|
|
tbxScaleRefToDut.Text = Resources.StrError;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
btnDutToRefRegulation.Enabled = false;
|
|
retVal = false;
|
|
}
|
|
}));
|
|
return retVal;
|
|
}
|
|
/// <summary>
|
|
/// Common routine to reset the cancellation token on restart or new meter
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-26" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void ResetCancellationToken()
|
|
{
|
|
// This state needs an external state change like user input to go ahead
|
|
// Remove cancellation token if idle reached for clean start
|
|
if (_cancellationToken.IsCancellationRequested)
|
|
{
|
|
// Reset the cancellation request
|
|
_cancellationTokenSource?.Dispose();
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
_cancellationToken = _cancellationTokenSource.Token;
|
|
}
|
|
}
|
|
#endregion ProcessControls
|
|
|
|
#region InfoWindow
|
|
/// <summary>
|
|
/// Clear history window.
|
|
/// </summary>
|
|
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void ClearHistoryWindow()
|
|
{
|
|
if (_frmHistory != null && _frmHistory.rtbHistory.InvokeRequired)
|
|
{
|
|
_frmHistory.rtbHistory.Invoke(new Action(() => { _frmHistory.rtbHistory.Clear(); }));
|
|
}
|
|
else
|
|
{
|
|
_frmHistory?.rtbHistory.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Color added.
|
|
/// </remarks>
|
|
/// <remarks date="2026-Aug-15" author="Thomas Wiedebusch">
|
|
/// - File output added.
|
|
/// </remarks>
|
|
private void LogText(String txtHistory, String filename = null)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorDefault);
|
|
if (!string.IsNullOrEmpty(filename))
|
|
{
|
|
File.AppendAllLines(filename, new[] { txtHistory });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void LogErrorText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorProcessFailed);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void LogSuccessText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorSuccess);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void InfoWindowColoredText(String txtHistory, Color color)
|
|
{
|
|
if (_frmHistory?.rtbHistory == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
_frmHistory.rtbHistory.SuspendLayout();
|
|
_frmHistory.rtbHistory.SelectionStart = _frmHistory.rtbHistory.Text.Length;
|
|
_frmHistory.rtbHistory.SelectionLength = 0;
|
|
_frmHistory.rtbHistory.SelectionColor = color;
|
|
_frmHistory.rtbHistory.AppendText($"{txtHistory}{Environment.NewLine}");
|
|
_frmHistory.rtbHistory.SelectionColor = _frmHistory.rtbHistory.ForeColor;
|
|
_frmHistory.rtbHistory.ScrollToCaret();
|
|
_frmHistory.rtbHistory.ResumeLayout();
|
|
}));
|
|
}
|
|
|
|
#endregion HistoryWindow
|
|
#region Event handler
|
|
|
|
/// <summary>
|
|
/// Feedback from FM2014being parsed to GUI
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-04..Feb-16" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void DataReceived_Handler(Object sender, ProcessExecEventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
// Process info
|
|
if (e.ActualProcessMessage != null)
|
|
lblActualProcess.Text = e.ActualProcessMessage;
|
|
lblActualProcess.Update();
|
|
SetTimeDisplay();
|
|
lblUpdateTimeValue.Update();
|
|
if (e.ActualProcessPercent != null && !_autoProgressBar && barSingleProgressUpdate.Visible)
|
|
barSingleProgressUpdate.Value = (Int32)e.ActualProcessPercent;
|
|
|
|
// Data dispatcher
|
|
var resp = (CmdResponse)e.SpecificInfoObj;
|
|
|
|
// Used as marker for error, null means measurement is in range or status or health is good
|
|
var statusColor = ColorStandardInputField;
|
|
|
|
// Color the output for the user display for quick check capability
|
|
if (e.StatusReturn == StatusReturn.MeasurementOutOfRange)
|
|
statusColor = ColorProcessFailed;
|
|
else if (e.StatusReturn == StatusReturn.MeasurementInRange)
|
|
statusColor = ColorSuccess;
|
|
|
|
if (e.StatusReturn == StatusReturn.Failed)
|
|
{
|
|
LogErrorText(resp.AnswerStr);
|
|
//ErrorHandler(resp.CmdName);
|
|
}
|
|
else if (resp.IntValue == null && resp.DoubleValue == null)
|
|
{
|
|
LogText(resp.AnswerStr);
|
|
}
|
|
else if (resp.IntValue != null)
|
|
{
|
|
LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.Unit}");
|
|
switch (resp.CmdName)
|
|
{
|
|
case CmdName.CmdGetRefPulsesCounted:
|
|
case CmdName.CmdGetRefPulsesCountedBackup:
|
|
tbxRefPulsesMeasured.Text = $@"{resp.IntValue:D}";
|
|
break;
|
|
case CmdName.CmdGetDutPulsesCounted:
|
|
case CmdName.CmdGetDutPulsesCountedBackup:
|
|
tbxDutPulsesMeasured.Text = $@"{resp.IntValue:D}";
|
|
break;
|
|
case CmdName.CmdSetRefPulsesPerCm:
|
|
tbxRefPulsesPerCm.Text = $@"{resp.IntValue:D}";
|
|
_initialRefPulsesPerCm = (UInt32)resp.IntValue;
|
|
break;
|
|
case CmdName.CmdSetDutPulsesPerCm:
|
|
tbxDutPulsesPerCm.Text = $@"{resp.IntValue:D}";
|
|
_initialDutPulsesPerCm = (UInt16)resp.IntValue;
|
|
break;
|
|
case CmdName.CmdSetCurrentOutputAttenuation:
|
|
var idx = cbxToleranceDisplayAttenuation.FindString($@"{resp.IntValue}");
|
|
cbxToleranceDisplayAttenuation.SelectedIndex = idx;
|
|
_initialCurrentOutputAttenuation = (Byte)resp.IntValue;
|
|
break;
|
|
case CmdName.CmdGetRefFrequency:
|
|
tbxRefFrequencyHz.Text = $@"{resp.IntValue:D}";
|
|
tbxRefFrequencyHz.BackColor = statusColor;
|
|
tbxActualFlowRateCmPerHour.BackColor = statusColor;
|
|
break;
|
|
case CmdName.CmdResetMeasurement:
|
|
// Immediately lock all buttons and input fields until reset is finished
|
|
SetFM2014AccessLocked();
|
|
// Check countdown of reset method
|
|
if (resp.IntValue == 0)
|
|
ActionControl(false);
|
|
break;
|
|
|
|
}
|
|
}
|
|
else if (resp.DoubleValue != null)
|
|
{
|
|
LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.Unit}");
|
|
switch (resp.CmdName)
|
|
{
|
|
case CmdName.CmdGetDutToRefToleranceUndamped:
|
|
case CmdName.CmdGetDutToRefToleranceDamped:
|
|
tbxActualMeasuredToleranceDutToRef.Text = $@"{resp.DoubleValue:F2}";
|
|
tbxActualMeasuredToleranceDutToRef.BackColor = statusColor;
|
|
break;
|
|
case CmdName.CmdRefToDutScale:
|
|
tbxScaleRefToDut.Text = $@"{resp.DoubleValue:F4}";
|
|
break;
|
|
case CmdName.CmdCalculatedFlowRefFrequ:
|
|
tbxActualFlowRateCmPerHour.Text = $@"{resp.DoubleValue:F3}";
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Catch the latest information
|
|
Update();
|
|
}));
|
|
}
|
|
#endregion Event Handler
|
|
|
|
|
|
#region Tools
|
|
/// <summary>
|
|
/// Set the actual process and log the text.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-04" author="Thomas Wiedebusch">
|
|
/// - Color added.
|
|
/// </remarks>
|
|
private void SetActualProcessAndLog(String message, String logFileName = null)
|
|
{
|
|
Invoke(new Action(() => { lblActualProcess.Text = message; }));
|
|
LogText(message, logFileName);
|
|
}
|
|
#endregion
|
|
|
|
#region Buttons and Controls
|
|
/// <summary>
|
|
/// Establish connection to FM2014 with individual address and read out FM2014 info.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-04" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void btnConnect_Click(Object sender, EventArgs e)
|
|
{
|
|
_startTime = DateTimeOffset.UtcNow;
|
|
Connect();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start and stop the DUT to REF regulation measurement.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void btnDutToRefRegulation_Click(Object sender, EventArgs e)
|
|
{
|
|
_startTime = DateTimeOffset.UtcNow;
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (FM2014.SharedCyclicMeasSequ == CyclicMeasSequ.idle)
|
|
{
|
|
if (FM2014.RegisteredFm2014s == null || FM2014.RegisteredFm2014s.Count == 0)
|
|
return;
|
|
|
|
if (FM2014.RegulationMeasurement(Fm2014.RefPulses_per_cm, Fm2014.DutPulses_per_cm,
|
|
Fm2014.DoublePulseDeadtime_ms, Fm2014.CurrentOutputAttenuation))
|
|
{
|
|
_autoProgressBar = true;
|
|
ActionControl(true);
|
|
btnDutToRefRegulation.Text = Resources.StrBtnStopRegulation;
|
|
btnDutToRefRegulation.Enabled = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Deactivate the button temporary to avoid repeated execution as the reset takes a certain time
|
|
_autoProgressBar = false;
|
|
btnDutToRefRegulation.Enabled = false;
|
|
FM2014.ResetHardwareAllDevices();
|
|
btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start and stop the manual REF calibration measurement.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-04..12" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void btnManualRefCalibration_Click(Object sender, EventArgs e)
|
|
{
|
|
_startTime = DateTimeOffset.UtcNow;
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (FM2014.SharedCyclicMeasSequ == CyclicMeasSequ.idle)
|
|
{
|
|
tbxRefPulsesMeasured.Text = "";
|
|
tbxDutPulsesMeasured.Text = "";
|
|
tbxVolumeMeasuredLiters.Text = "";
|
|
tbxRefCalibrationResultPulsePerCm.Text = "";
|
|
tbxActualFlowRateCmPerHour.Text = "";
|
|
tbxActualMeasuredToleranceDutToRef.Text = "";
|
|
tbxRefFrequencyHz.Text = "";
|
|
|
|
if (FM2014.PulseCounterMeasurement(true, cbxMeasuredDutPulses.Checked))
|
|
{
|
|
_autoProgressBar = true;
|
|
ActionControl(true);
|
|
btnManualRefCalibration.Text = Resources.StrBtnStopCalibration;
|
|
btnManualRefCalibration.Enabled = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Deactivate the button temporary to avoid repeated execution as the reset takes a certain time
|
|
_autoProgressBar = false;
|
|
btnManualRefCalibration.Enabled = false;
|
|
FM2014.ResetHardwareAllDevices();
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Selected COM port, tolerance display settings or address item has changed.
|
|
/// These values can be setup before any connection to the FM2014 has been established. Therefore, those
|
|
/// settings have to be remembered within this object.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-04" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void cbxProgramSettings_Changed(Object sender, EventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (_currentOutputDisplayRangeIdx != cbxToleranceDisplayRange.SelectedIndex ||
|
|
_addressIdx != cbxFM2014Address.SelectedIndex ||
|
|
_comPortIdx != cbxProgramComPort.SelectedIndex)
|
|
{
|
|
_currentOutputDisplayRangeIdx = cbxToleranceDisplayRange.SelectedIndex;
|
|
_addressIdx = cbxFM2014Address.SelectedIndex;
|
|
_comPortIdx = cbxProgramComPort.SelectedIndex;
|
|
StoreProgramConfiguration();
|
|
Init();
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Safe standalone measurement setup.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-18..Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void btnSaveSetupToFm2014_Click(Object sender, EventArgs e)
|
|
{
|
|
if (Fm2014 != null && FM2014.StoreAllConfigurations())
|
|
{
|
|
_fm2014RegulationSetupHasChanged = false;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
_initialRefPulsesPerCm = Fm2014.RefPulses_per_cm;
|
|
_initialDutPulsesPerCm = Fm2014.DutPulses_per_cm;
|
|
_initialCurrentOutputAttenuation = Fm2014.CurrentOutputAttenuation;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Switch between damped or undamped tolerance display. This can be changed during the 'Regulation Measurement'
|
|
/// as it will just collect a different dataset and not change anything on the measurement setup.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void chkUseDampedTolerance_CheckedChanged(Object sender, EventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (Fm2014 != null)
|
|
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="2026-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>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-14..24" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void tbxRefPulsesPerCm_KeyDown(Object sender, KeyEventArgs e)
|
|
{
|
|
// Catch the enter key, recalculate all settings
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
tbxRefPulsesPerCm_Leave(this, null);
|
|
SelectNextControl(tbxDutPulsesPerCm, true, true, true, true);
|
|
}
|
|
else if (!MaskEditIntegerInput(e.KeyCode))
|
|
{
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
else
|
|
{
|
|
// Take the new input and calculate the results after the input has been taken after this event!
|
|
Task.Factory.StartNew(() =>
|
|
{
|
|
Thread.Sleep(1);
|
|
}, _cancellationToken).ContinueWith(delegate
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
tbxRefPulsesPerCm_Leave(this, null);
|
|
}));
|
|
}, _cancellationToken);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// After manual input of the REF pulses per cubic meter the scale REF to DUT has to be recalculated
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-14..Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void tbxRefPulsesPerCm_Leave(Object sender, EventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (Fm2014 == null)
|
|
return;
|
|
|
|
if (string.IsNullOrEmpty(tbxRefPulsesPerCm.Text))
|
|
{
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
tbxRefPulsesPerCm.BackColor = ColorProcessFailed;
|
|
btnDutToRefRegulation.Enabled = false;
|
|
return;
|
|
}
|
|
|
|
// Parse and check limits
|
|
if (int.TryParse(tbxRefPulsesPerCm.Text, out var pulses_per_cm) &&
|
|
FM2014.PULSES_PER_CM_REGULATION_SETUP_MIN <= pulses_per_cm &&
|
|
FM2014.REF_PULSES_PER_CM_REGULATION_INPUT_MAX >= pulses_per_cm)
|
|
{
|
|
// During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated
|
|
Fm2014.RefPulses_per_cm = (UInt32)pulses_per_cm;
|
|
|
|
if (!UpdateRefToDutScale())
|
|
{
|
|
tbxRefPulsesPerCm.BackColor = ColorProcessFailed;
|
|
return;
|
|
}
|
|
|
|
tbxRefPulsesPerCm.BackColor = ColorStandardInputField;
|
|
CheckForFM2014SetupChanged();
|
|
}
|
|
else
|
|
{
|
|
tbxRefPulsesPerCm.BackColor = ColorProcessFailed;
|
|
_fm2014RegulationSetupHasChanged = false;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Redirect keystroke 'Enter' to leaving the cell event
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-14..24" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void tbxDutPulsesPerCm_KeyDown(Object sender, KeyEventArgs e)
|
|
{
|
|
// Catch the enter key, recalculate all settings
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
tbxDutPulsesPerCm_Leave(this, null);
|
|
SelectNextControl(ActiveControl, true, true, true, true);
|
|
}
|
|
else if (!MaskEditIntegerInput(e.KeyCode))
|
|
{
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
else
|
|
{
|
|
// Take the new input and calculate the results after the input has been taken after this event!
|
|
Task.Factory.StartNew(() =>
|
|
{
|
|
Thread.Sleep(1);
|
|
}, _cancellationToken).ContinueWith(delegate
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
tbxDutPulsesPerCm_Leave(this, null);
|
|
}));
|
|
}, _cancellationToken);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// After manual input of the DUT pulses per cubic meter the scale REF to DUT has to be recalculated
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-14..Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void tbxDutPulsesPerCm_Leave(Object sender, EventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (Fm2014 == null)
|
|
return;
|
|
|
|
if (string.IsNullOrEmpty(tbxDutPulsesPerCm.Text))
|
|
{
|
|
tbxDutPulsesPerCm.BackColor = ColorProcessFailed;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
btnDutToRefRegulation.Enabled = false;
|
|
return;
|
|
}
|
|
|
|
// Parse and check limits
|
|
if (int.TryParse(tbxDutPulsesPerCm.Text, out var pulses_per_cm) &&
|
|
FM2014.PULSES_PER_CM_REGULATION_SETUP_MIN <= pulses_per_cm &&
|
|
FM2014.PULSES_PER_CM_REGULATION_SETUP_MAX >= pulses_per_cm)
|
|
{
|
|
// During setup of the 'Dut_pulse_per_cm' the 'RefToDutScaleStr' will be generated
|
|
Fm2014.DutPulses_per_cm = (UInt16)pulses_per_cm;
|
|
|
|
if (!UpdateRefToDutScale())
|
|
{
|
|
tbxDutPulsesPerCm.BackColor = ColorProcessFailed;
|
|
return;
|
|
}
|
|
|
|
tbxDutPulsesPerCm.BackColor = ColorStandardInputField;
|
|
CheckForFM2014SetupChanged();
|
|
}
|
|
else
|
|
{
|
|
tbxDutPulsesPerCm.BackColor = ColorProcessFailed;
|
|
_fm2014RegulationSetupHasChanged = false;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Redirect keystroke 'Enter' to leaving the cell event
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-15..24" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void tbxManualInputVolumeLiters_KeyStoke(Object sender, KeyEventArgs e)
|
|
{
|
|
// Catch the enter key, recalculate all settings
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
tbxManualInputVolumeLiters_Leave(this, null);
|
|
SelectNextControl(ActiveControl, true, true, true, true);
|
|
}
|
|
else if (!MaskEditIntegerInput(e.KeyCode))
|
|
{
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
else
|
|
{
|
|
// Take the new input and calculate the results after the input has been taken after this event!
|
|
Task.Factory.StartNew(() =>
|
|
{
|
|
Thread.Sleep(1);
|
|
}, _cancellationToken).ContinueWith(delegate
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
tbxManualInputVolumeLiters_Leave(this, null);
|
|
}));
|
|
}, _cancellationToken);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// After manual input of measured weight scale or reservoir in liters this will calculate the
|
|
/// REF pulses per cubic meter and copy from REF pulses per cubic meter in 'Manual REF Calibration'
|
|
/// to 'Regulation Setup'.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2026-Jan-15..Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void tbxManualInputVolumeLiters_Leave(Object sender, EventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
if (Fm2014 == null)
|
|
return;
|
|
|
|
if (!string.IsNullOrEmpty(tbxRefPulsesMeasured.Text) &&
|
|
string.IsNullOrEmpty(tbxVolumeMeasuredLiters.Text))
|
|
{
|
|
tbxVolumeMeasuredLiters.BackColor = ColorProcessFailed;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
btnDutToRefRegulation.Enabled = false;
|
|
return;
|
|
}
|
|
|
|
// Calculate REF pulses per cubic meter
|
|
if (int.TryParse(tbxRefPulsesMeasured.Text, out var pulses) && pulses > 0 &&
|
|
int.TryParse(tbxVolumeMeasuredLiters.Text, out var liters) && liters > 0)
|
|
{
|
|
// 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.RefPulses_per_cm = pulses_per_cm;
|
|
|
|
// If this succeeded, then update the new manual calibrated value
|
|
if (pulses_per_cm == Fm2014.RefPulses_per_cm)
|
|
{
|
|
tbxRefCalibrationResultPulsePerCm.Text = $@"{Fm2014.RefPulses_per_cm:D}";
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField;
|
|
tbxRefPulsesPerCm.Text = $@"{Fm2014.RefPulses_per_cm:D}";
|
|
tbxVolumeMeasuredLiters.BackColor = ColorStandardInputField;
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField;
|
|
|
|
if (!UpdateRefToDutScale())
|
|
{
|
|
tbxVolumeMeasuredLiters.BackColor = ColorProcessFailed;
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorProcessFailed;
|
|
return;
|
|
}
|
|
tbxVolumeMeasuredLiters.BackColor = ColorStandardInputField;
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardInputField;
|
|
CheckForFM2014SetupChanged();
|
|
}
|
|
else
|
|
{
|
|
_fm2014RegulationSetupHasChanged = false;
|
|
btnSaveSetupToFm2014.Enabled = false;
|
|
tbxVolumeMeasuredLiters.BackColor = ColorProcessFailed;
|
|
tbxRefCalibrationResultPulsePerCm.BackColor = ColorProcessFailed;
|
|
tbxRefCalibrationResultPulsePerCm.Text = Resources.StrError;
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Switch between DEBUG and regular operation
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void picFM2014_Click(Object sender, EventArgs e)
|
|
{
|
|
Invoke(new Action(() =>
|
|
{
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// FM2014 current attenuation item has changed:
|
|
/// - This will dispatch the new value to all FM2014s.
|
|
/// </summary>
|
|
/// <remarks date="2026-Jan-18..Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void cbxToleranceDisplayAttenuation_SelectedIndexChanged(Object sender, EventArgs e)
|
|
{
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
//Backup the actual setting to detect changes
|
|
var backupAttenuation = Fm2014.CurrentOutputAttenuation;
|
|
|
|
if (byte.TryParse(cbxToleranceDisplayAttenuation.SelectedItem.ToString(), out var attenuation) &&
|
|
attenuation >= FM2014.CURRENT_OUTPUT_ATTENUATION_MIN &&
|
|
attenuation <= FM2014.CURRENT_OUTPUT_ATTENUATION_MAX &&
|
|
backupAttenuation != attenuation)
|
|
{
|
|
Fm2014.CurrentOutputAttenuation = attenuation;
|
|
CheckForFM2014SetupChanged();
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Select next control on enter key pressed
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void cbxToleranceDisplayAttenuation_KeyDown(Object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
SelectNextControl(ActiveControl, true, true, true, true);
|
|
}
|
|
}
|
|
private void helpToolStripMenuItem_Click(Object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
var filename = "";
|
|
if (Thread.CurrentThread.CurrentCulture.Name.Contains("de"))
|
|
{
|
|
filename = Path.Combine(_assemblyExecRootPath, "Docu", @"MiniPrf Kurzanleitung.pdf");
|
|
}
|
|
else if (Thread.CurrentThread.CurrentCulture.Name.Contains("en"))
|
|
{
|
|
filename = Path.Combine(_assemblyExecRootPath, "Docu", @"MiniPrf Quick Reference.pdf");
|
|
}
|
|
if (!string.IsNullOrEmpty(filename))
|
|
{
|
|
Process.Start(filename);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Console.WriteLine(exception.Message);
|
|
}
|
|
|
|
}
|
|
|
|
#endregion Buttons and Controls
|
|
}
|
|
}
|