laatzen/MiniPrf/Ui/FrmMainMiniPrf.cs
2025-12-23 13:23:41 +01:00

637 lines
23 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'
*
*=======================================================================================\n
* @copyright
*
*_______________________________________________________________________________________\n
* Copyright (c) 2025 SENSUS GmbH.\n
* All Rights Reserved.\n
* \n
* Confidential property of\n
* SENSUS GmbH,\n
* Meineckestr. 10, 30880 LAATZEN, GERMANY\n
* \n
*=======================================================================================\n
* @authors
*
*_______________________________________________________________________________________\n
* Thomas Wiedebusch\n
*
*
*=======================================================================================\n
* @version
*
*_______________________________________________________________________________________\n
* V1.00 13-Nov-2025..22-Dec-2025 by Thomas Wiedebusch\n
* - Initial.
*
*
*=======================================================================================\n
*/
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Config;
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core;
using System;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using Sensus.MiniPrf.Ui.Properties;
namespace Sensus.MiniPrf.Ui
{
/// <summary>
/// Main form for 'MiniPrf'
/// </summary>
public partial class FrmMainMiniPrf : Form
{
#region Properties
private FM2014 _fm2014;
private Boolean _progressBarOn;
private DateTimeOffset _startTime;
private Boolean _resetTimeMeasurement;
private readonly Boolean _updateGui = true;
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 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 _toleranceIdx;
#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;
}
/// <summary>
/// Clear data table and set genesis to not connected
/// </summary>
private void Init()
{
ViewProcessControl(false);
// 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?.SerialPort != null && !cbxFM2014ComPort.Items.Contains(_fm2014Config.SerialPort))
{
cbxFM2014ComPort.Items.Add(_fm2014Config.SerialPort);
}
else if (_fm2014Config?.SerialPort == null && !cbxFM2014ComPort.Items.Contains("?"))
{
cbxFM2014ComPort.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 => !cbxFM2014ComPort.Items.Contains(comPort)))
{
cbxFM2014ComPort.Items.Add(comPort);
}
//_comPortIdx = 0;
//cbxFM2014ComPort.Text = cbxFM2014ComPort.Items[0].ToString();
cbxFM2014ComPort.Text = cbxFM2014ComPort.Items[_comPortIdx].ToString();
var itemContent = _fm2014Config?.Address ?? 1;
for (var idx = 0; idx < cbxFM2014Address.MaxDropDownItems; idx++)
{
if (!cbxFM2014Address.Items[idx].ToString().Equals(itemContent.ToString())) continue;
_addressIdx = idx;
cbxFM2014Address.SelectedItem = cbxFM2014Address.Items[idx];
break;
}
itemContent = _fm2014Config?.TolerancePercent ?? 3;
for (var idx = 0; idx < cbxFM2014TolerancePercent.MaxDropDownItems; idx++)
{
if (!cbxFM2014TolerancePercent.Items[idx].ToString().Equals(itemContent.ToString())) continue;
_toleranceIdx = idx;
cbxFM2014TolerancePercent.SelectedItem = cbxFM2014TolerancePercent.Items[idx];
break;
}
// FM2014 group box
lblConnectFM2014.Text = Resources.StrLblFM2014NotConnected;
lblConnectFM2014.ForeColor = Color.Red;
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;
tbxRefPulsePerVolume.Text = "";
lblDutPulsePerVolume.Text = Resources.StrLblDutPulsesPerCm;
tbxDutPulsePerVolume.Text = "";
lblScaleRefToDut.Text = Resources.StrLblScaleRefToDut;
tbxScaleRefToDut.Text = "";
lblAttenuation.Text = Resources.StrLblAttenuation;
btnSaveRegulationSetup.Text = Resources.StrBtnSaveRegulationSetup;
// Manual REF Calibration group box
gbxManualRefCalibration.Text = Resources.StrGbxManualRefCalibration;
lblMeasuredRefPulses.Text = Resources.StrLblMeasuredRefPulses;
tbxMeasuredRefPulses.Text = "";
lblWeightScaleVolume.Text = Resources.StrLblMeasuredWeightScaleVolume;
tbxWeightScaleMass.Text = "";
lblPulseVolumeCmRelation.Text = Resources.StrLblRefPulsesPerCm;
tbxPulseVolumeCmRelation.Text = "";
btnManualRefCalibration.Text = Resources.StrBtnStartCalibration;
// DUT to REF Regulation group box
gbxDutToRefRegulation.Text = Resources.StrGbxDutToRefRegulation;
lblActualFlowRateCmPerHour.Text = Resources.StrLblActualMeasuredFlowRate;
tbxActualFlowRateCmPerHour.Text = "";
lblActualMeasuredTolerance.Text = Resources.StrLblActualMeasuredToleranceDutToRef;
tbxActualMeasuredToleranceDutToRef.Text = "";
chkUseDampedTolerance.Text = Resources.StrChkUseDampedTolerance;
btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
// Process status
lblActualProcess.Text = Resources.StrLblProcessStatus;
lblTimeText.Text = Resources.StrLblTime;
lblWaitingForMeterResponse.Text = Resources.StrLblWaitingForMeterResponse;
lblWaitingForMeterResponse.Visible = false;
SetFM2014AccessLocked();
btnFM2014Connect.Focus();
}
/// <summary>
/// Store the settings adjusted by the UI
/// </summary>
/// <remarks date="2025-Nov-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void StoreFM2014Settings()
{
_fm2014Config.SerialPort = cbxFM2014ComPort.Text;
if (int.TryParse(cbxFM2014Address.SelectedItem.ToString(), out var address))
_fm2014Config.Address = address;
if (int.TryParse(cbxFM2014TolerancePercent.SelectedItem.ToString(), out var tolerancePercent)
&& (tolerancePercent == 3 || tolerancePercent == 5))
_fm2014Config.TolerancePercent = tolerancePercent;
_fm2014Config.Update();
}
private void FrmMainMiniPrf_Load(Object sender, EventArgs e)
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
lblFwUpdateInfo.Text = $@"Version: {version.Major}.{version.Minor}.{version.Build}";
_resetTimeMeasurement = true;
Init();
var xPosition = Location.X + Size.Width;
var yPosition = Location.Y;
if (_frmHistory != null)
{
_frmHistory.SetDesktopLocation(xPosition, yPosition);
_frmHistory.Show();
}
LogText($"MiniPrf: {_version.Major}.{_version.Minor}.{_version.Build}");
LogText(StrSeparator);
}
private void FrmMainMiniPrf_FormClosed(Object sender, FormClosedEventArgs e)
{
_frmHistory?.Close();
_fm2014?.Logout();
_fm2014?.Dispose();
Dispose();
}
#endregion FormControls
#region TimerControls
private void tmrProgressUpdate_Tick(Object sender, EventArgs e)
{
if (_autoProgressBar || _progressBarOn)
{
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>
/// Disable all buttons except the connect button
/// </summary>
private void DisableToolButtons()
{
btnSaveRegulationSetup.Enabled = false;
btnManualRefCalibration.Enabled = false;
btnDutToRefRegulation.Enabled = false;
}
/// <summary>
/// Enable all buttons except the connect button
/// </summary>
private void EnableToolButtons()
{
btnSaveRegulationSetup.Enabled = true;
btnManualRefCalibration.Enabled = true;
btnDutToRefRegulation.Enabled = true;
}
/// <summary>
/// Lock all buttons, enable the connect button
/// </summary>
private void SetFM2014AccessLocked()
{
btnFM2014Connect.Enabled = true;
DisableToolButtons();
}
/// <summary>
/// Enable all buttons, FM2014 has to be connected
/// </summary>
private void SetFM2014AccessEnabled()
{
btnFM2014Connect.Enabled = true;
EnableToolButtons();
}
/// <summary>
/// Restore setting of buttons and timeout after operation with device
/// </summary>
private void CheckUpdateEnabled()
{
if (_fm2014 == null || !_fm2014.IsLoggedOn)
{
SetFM2014AccessLocked();
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
{
Init();
if (string.IsNullOrEmpty(cbxFM2014ComPort?.SelectedItem?.ToString()) ||
cbxFM2014ComPort.SelectedItem.ToString().Equals("?"))
{
var text = Resources.StrErrorMsgComPortNotAssigned;
MessageBox.Show(text, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
LogText(text);
return;
}
StoreFM2014Settings();
ActionControl(true);
_fm2014?.Dispose();
//dispose old meter
_fm2014 = null;
//assign new meter
_fm2014 = new FM2014();
if (int.TryParse(cbxFM2014Address.SelectedItem.ToString(), out var address))
{
_fm2014.Address = address;
}
else
{
ActionControl(false);
LogErrorText(Resources.StrErrorMsgFM2014AddressInvalid);
}
if (int.TryParse(cbxFM2014TolerancePercent.SelectedItem.ToString(), out var tolerancePercent)
&& (tolerancePercent == 3 || tolerancePercent == 5))
{
_fm2014.TolerancePercent = tolerancePercent;
}
else
{
ActionControl(false);
LogErrorText(Resources.StrErrorMsgFM2014ToleranceInvalid);
}
lblActualProcess.Text = Resources.StrMsgConnecting;
_fm2014.Connect(cbxFM2014ComPort.SelectedItem.ToString());
Task.Factory.StartNew(() =>
{
_fm2014.Login();
if (!_fm2014.IsLoggedOn)
{
LogErrorText(Resources.StrErrorMsgFM2014AccessDenied);
_fm2014.Logout();
return;
}
if (_fm2014.IsLoggedOn)
{
Invoke(new Action(() =>
{
lblConnectFM2014.Text = Resources.StrLblFM2014Connected;
lblConnectFM2014.ForeColor = Color.Green;
tbxFM2014FullId.Text = _fm2014.FullId;
tbxFM2014FullId.Visible = true;
tbxFM2014ApplicationFwVersion.Text = _fm2014.FwVersion;
tbxFM2014SerialNumber.Text = _fm2014.SerialNumber;
// Logging to history window
LogText("FM2014 ID:\t\t" + _fm2014.FullId);
LogText("FM2014 Firmware Version:\t" + _fm2014.FwVersion);
LogText($"FM2014 {Resources.StrLblFM2014SerialNumber}\t" + _fm2014.SerialNumber);
LogText($"FW2014 {Resources.StrLblFM2014Address}\t\t" + _fm2014.Address);
var dt = DateTime.UtcNow;
var msg = $"{dt:yyyy-MM-dd HH:mm:ss} UTC";
LogText($"{Resources.StrMsgPcDateTime}\t{msg}");
CheckUpdateEnabled();
}));
}
}).ContinueWith(delegate
{
ActionControl(false);
});
}
catch (Exception ex)
{
ActionControl(false);
MessageBox.Show(ex.Message, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
LogErrorText(ex.Message);
_fm2014?.Dispose();
}
}
#endregion BoardControls
#region ProcessControls
private void ActionControl(Boolean isActive)
{
if (isActive)
{
Invoke(new Action(() =>
{
ViewProcessControl(true);
tmrProgressUpdate.Enabled = true;
_progressBarOn = true;
}));
}
else
{
Invoke(new Action(() =>
{
ViewProcessControl(false);
_progressBarOn = false;
CheckUpdateEnabled();
}));
}
}
private void ViewProgressPcb(Boolean isActive)
{
if (_updateGui)
{
if (isActive)
{
Invoke(new Action(() =>
{
_autoProgressBar = true;
ViewProcessControl(true);
}));
}
else
{
Invoke(new Action(() =>
{
_autoProgressBar = false;
ViewProcessControl(false);
}));
}
}
}
/// <summary>
/// View all process bars and labels
/// </summary>
private void ViewProcessControl(Boolean view)
{
if (view)
{
lblActualProcess.Visible = true;
barSingleProgressUpdate.Visible = true;
tmrProgressUpdate.Enabled = true;
SetFM2014AccessLocked();
}
else
{
lblActualProcess.Visible = false;
barSingleProgressUpdate.Visible = false;
tmrProgressUpdate.Enabled = false;
lblWaitingForMeterResponse.Visible = false;
lblActualProcess.Text = "";
SetFM2014AccessEnabled();
}
//common actions and settings
lblActualProcess.Update();
barSingleProgressUpdate.Value = 0;
barSingleProgressUpdate.Update();
Update();
}
#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="2023-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 Tools
/// <summary>
/// Set the actual process and log the text.
/// </summary>
/// <remarks date="2023-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
private void btnConnect_Click(Object sender, EventArgs e)
{
if (_resetTimeMeasurement)
{
_startTime = DateTimeOffset.UtcNow;
_resetTimeMeasurement = false;
}
Connect();
}
private void cbxFM2014ComPort_SelectedValueChanged(Object sender, EventArgs e)
{
if (_toleranceIdx != cbxFM2014TolerancePercent.SelectedIndex ||
_addressIdx != cbxFM2014Address.SelectedIndex ||
_comPortIdx != cbxFM2014ComPort.SelectedIndex)
{
_toleranceIdx = cbxFM2014TolerancePercent.SelectedIndex;
_addressIdx = cbxFM2014Address.SelectedIndex;
_comPortIdx = cbxFM2014ComPort.SelectedIndex;
StoreFM2014Settings();
}
}
#endregion Buttons and Controls
}
}