laatzen/ServiceFwUpdate/Ui/ServiceFwUpdateBuilder/FrmFwUpdateBuilder.cs

5931 lines
270 KiB
C#

using Logic.ProductionToProductMapper.Files.Fw;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.CommonCore.ThreadWatcher;
using Xylem.Common.Cryptology.Security;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.ProductionOrderCore.FW;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Utils.DateTimeServer;
using Xylem.Common.Utils.Logging;
using Xylem.Common.Utils.UiInvoker;
using Xylem.Common.Utils.UiLanguageControl;
using Xylem.Common.Utils.FileIo;
using Xylem.ServiceFwUpdate.Common.FwUpdateConfig.Consts;
using Xylem.ServiceFwUpdate.Common.FwUpdateDb;
using Xylem.ServiceFwUpdate.Common.FwUpdateSafe;
using Xylem.ServiceFwUpdate.Ui.FwUpdateBuilder.Const;
using Xylem.ServiceFwUpdate.Ui.FwUpdateBuilder.Properties;
using Xylem.Common.Utils.Crc16Ccitt;
namespace Xylem.ServiceFwUpdate.Ui.FwUpdateBuilder
{
/// <summary>
/// FW update builder
/// </summary>
public partial class FrmFwUpdateBuilder : Form
{
#region ------------------------------------------ Variables --------------------------------------------------
private static readonly Color ColorBackGround = Color.White;
private static readonly Color ColorDefault = Color.Black;
private static readonly Color ColorSuccess = Color.Green;
private static readonly Color ColorProcessWarning = Color.DarkOrange;
private static readonly Color ColorProcessFailed = Color.Red;
//private static readonly Color ColorOngoingProcess = Color.Blue;
//private static readonly Color ColorUnknownStatus = Color.Gray;
private static readonly Color ColorBkMissing = Color.LightCoral;
private static readonly Color ColorBkValid = Color.LightGreen;
//private const String SuccessSign = @"✔";
//private const String FailedSign = @"✘";
private readonly CancellationTokenSource _processToken = new CancellationTokenSource();
private readonly Thread _fwUpdateBuilderThread;
// actual process state
private ProcessState _processState;
// locker to avoid repeated state execution
private ProcessState _lastProcessState;
// reminder for state change to execute e.g. error messaging
private ProcessState _invokerProcessState;
/// <summary>
/// remind manually changed culture setting
/// </summary>
private CultureInfo _cultureInfo;
private readonly Version _version;
private readonly String _versionString;
private readonly AssemblyName _assemblyName;
private Boolean _licenseUnchecked;
private DateTimeOffset _licenseValidDateTimeOffset;
private const Int32 DbLicenseRefreshTimeoutMs = 60000;
private Int32 _dbLicenseRefreshDelayCtrMs;
private readonly ILogger _logger;
private const String StrSeparator = "-----------------------------------------------------" +
"-----------------------------------------------------";
/// <summary>
/// Registered user information read from registration file.
/// </summary>
private readonly UserInformation _regUser = new UserInformation();
//data grid styles
private readonly DataGridViewCellStyle _styleValid = new DataGridViewCellStyle();
private readonly DataGridViewCellStyle _styleInvalid = new DataGridViewCellStyle();
private readonly DataGridViewCellStyle _styleUnknown = new DataGridViewCellStyle();
private readonly DataGridViewCellStyle _styleInstallationApproved = new DataGridViewCellStyle();
private readonly DataGridViewCellStyle _styleInstallationDenied = new DataGridViewCellStyle();
private readonly DataGridViewCellStyle _styleInstallationUserApproval = new DataGridViewCellStyle();
private readonly DataGridViewCellStyle _styleInstallationRemoved = new DataGridViewCellStyle();
private DataTable _dataTableFwPackages;
private DataTable _dataTableSummary;
private DataTable _dataTableCordonels;
private DataTable _dataTableUpdateOperators;
private DataTable _dataTableCustomers;
private DataTable _dataTableReportFiles;
private DataTable _dataTableUpdateSafes;
///<summary>
/// Display of report history.
/// </summary>
private readonly FrmHistory _frmFwUpdateReport = new FrmHistory();
///<summary>
/// Collection of software needed for the FW-Update safe!
/// </summary>
private SoftwareContainer _fwUpdateSwContainer;
///<summary>
/// All report files from the DB!
/// This information will only be used by the FW Update Builder!
/// </summary>
private readonly List<FwUpdateReportDb> _fwUpdateReports = new List<FwUpdateReportDb>();
///<summary>
/// One selected report file on grid view by the user from the DB!
/// This information will only be used by the FW Update Builder!
/// </summary>
private readonly FwUpdateReportDb _selectedReport = new FwUpdateReportDb();
///<summary>
/// All FW update safes from the DB!
/// This information will only be used by the FW Update Builder!
/// </summary>
private readonly List<FwUpdateSafeDb> _fwUpdateSafesInfos = new List<FwUpdateSafeDb>();
///<summary>
/// All customer specific production order numbers to search mask from the DB!
/// This information will only be used by the FW Update Builder!
/// </summary>
private readonly List<CordonelCustomerOrderDb> _cordonelCustomerOrdersSearch =
new List<CordonelCustomerOrderDb>();
///<summary>
/// All information acquired from the DB! This information will only be used by the FW Update Builder
/// as search!
/// </summary>
private readonly List<CordonelProductionInfosDb> _cordonelProductionSearchInfos =
new List<CordonelProductionInfosDb>();
///<summary>
/// All information acquired from the DB! This information will only be used by the FW Update Builder
/// as pre-selection!
/// </summary>
private readonly List<CordonelProductionInfosDb> _cordonelProductionPreSelectInfos =
new List<CordonelProductionInfosDb>();
///<summary>
/// The Cordonel device info is all needed information for the FW-Update SW to login, install the password file
/// and select the required release version to collect it out of the FW-Update packages!
/// This is the list of already selected Cordonels for the update process! This information will be installed
/// in the FW-Update Safe and therefore passed to the FW-Update SW.
/// </summary>
private readonly List<CordonelDeviceInfo> _cordonelUpdateList = new List<CordonelDeviceInfo>();
///<summary>
/// The FW-Update package is a collection of all needed applications and settings to install into the Cordonel!
/// This information will be passed to the FW-Update SW if any Cordonel requires this package.
/// </summary>
private CordonelFirmware _fwUpdatePackage = new CordonelFirmware();
///<summary>
/// All Cordonel FW package information needed to select the right package for installation! This is going to be
/// filled with information from the read packages from DB.
/// This information will only be used by the FW Update Builder!
/// </summary>
//private readonly List<CordonelFwPackageInfo> _cordonelFwPackageInfoSearch = new List<CordonelFwPackageInfo>();
private readonly List<String> _metrologySearchItems = new List<String>();
private readonly List<String> _coreSearchItems = new List<String>();
private readonly List<String> _regionSearchItems = new List<String>();
private readonly List<String> _radioSearchItems = new List<String>();
private readonly List<String> _sizeSearchItems = new List<String>();
///<summary>
/// The FW-Update update capability is internally used to check core revision, metrology, region, frequency a.s.o.
/// This information will only be used by the FW Update Builder!
/// </summary>
private readonly List<FwUpdateCapability> _fwUpdateCapability = new List<FwUpdateCapability>();
private readonly FwUpdateDb _fwUpdateDbAccess = new FwUpdateDb();
// private readonly UserInformation _userInfo;
private Byte[] _primaryKey;
private readonly DateTimePicker _datePicker = new DateTimePicker();
private readonly ComboBox _cbxFwReleaseSelection = new ComboBox();
private Boolean _enableFwSafeUpload;
private String _fwUpdateSafeName;
private FwUpdateSafeDb _fwUpdateSafeDb;
private Int32 _updateOperatorId;
/// <summary>
/// Reminder to avoid repeated reading of DB contents and reset of selection if item has already been assigned
/// Values have to be preset to avoid a null at first check!!!!!!
/// </summary>
private String _customerNumberBeforeLastCordonelDbSearch = "";
/// <summary>
/// Feedback from any task that an update of all tables is required
/// </summary>
private Boolean _fwUpdateOperatorsPropertyChanged;
private Boolean _customersPropertyChanged;
private Boolean _cordonelsPropertyChanged;
private Boolean _fwPackagePropertyChanged;
private Boolean _fwReportsPropertyChanged;
private Boolean _fwUpdateSafesPropertyChanged;
private Boolean _preBuildSummaryPropertyChanged;
/// <summary>
/// DB connection retry timer if DB is not connected
/// </summary>
private const Int32 DbConnectionRetryDelayMs = 6000;
/// <summary>
/// DB connection timeout on active DB connectivity test
/// </summary>
private const Int32 DbConnectionTimeoutMs = 5000;
private Int32 _dbAccessDelayCtrMs;
/// <summary>
/// Project name for the DLL to load:
/// This is the base for the source folder, the destination folder, the namespace and the form
/// </summary>
private const String FwUpdateProjectName = "ServiceFwUpdateSw";
/// <summary>
/// Name for firmware update software including the namespace
/// </summary>
private const String FwUpdateSwFullName = "Xylem.ServiceFwUpdate.Ui." + FwUpdateProjectName;
/// <summary>
/// Path for FW-Update safes
/// </summary>
private static readonly String FwUpdateSafePath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile), FwUpdateConfig.DefaultFwUpdateSafePath);
/// <summary>
/// Path to application configuration ../[user]/AppData/Roaming/Genesis/
/// </summary>
private static readonly String ApplicationConfigPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData), ProgramConfig.GenesisBaseFolder);
/// <summary>
/// Path and file user NLog configuration
/// </summary>
private static readonly String NLogConfigurationDestPathFile = Path.Combine(ApplicationConfigPath,
ProgramConfig.NlogConfig);
/// <summary>
/// Source folder of software package if taken from local [user]/downloads/ServiceFwUpdateSw
/// </summary>
private static readonly String FwUpdateSwSourcePath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile), FwUpdateConfig.DefaultFwUpdateSwSourcePath);
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
/// <summary>
/// State machine for FW update builder:
/// Will be executed in an endless loop from the "Service FW-Update Thread" until exit. State will be executed
/// once and then locked for repeated execution. A state change is the reason for reentry.
/// NOTE:
/// Change the state immediately before calling any routine to avoid repeated execution of routine!
/// </summary>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Jun-08" author="Roland Drabesch">
/// - Sleep on equal process state to force suspend of actual thread.
/// </remarks>
/// <remarks date="2021-Oct-28" author="Thomas Wiedebusch">
/// - Added report files and FW-Update safes load from DB.
/// </remarks>
private void FwUpdateBuilderStateMachine()
{
while (!_processToken.IsCancellationRequested)
{
if (_lastProcessState == _processState)
{
Thread.Sleep(1);
// do nothing until state changed
}
else
{
try
{
_lastProcessState = _processState;
switch (_processState)
{
case ProcessState.Init:
_processState = ProcessState.Idle;
break;
case ProcessState.Idle:
// Process state change catcher to leave state as is
break;
case ProcessState.Stop:
// stop clears all status's and ongoing processes
StopProcesses();
break;
case ProcessState.Error:
// stop clears all status's and ongoing processes
ErrorProcesses();
break;
case ProcessState.ConnectDb:
EstablishDbConnectionTask();
break;
case ProcessState.GetCustomersFromDb:
LoadDbCordonelCustomersTask();
break;
case ProcessState.GetReportsFromDb:
LoadDbReportsTask();
break;
case ProcessState.GetAndDisplaySingleReportFromDb:
LoadDbReportAndDisplayTask();
break;
case ProcessState.GetFwUpdateSafesInfosFromDb:
LoadDbFwUpdateSafesInfosTask();
break;
case ProcessState.GetFwUpdateOperatorsFromDb:
LoadDbFwUpdateOperatorsTask();
break;
case ProcessState.ValidateSoftware:
CheckLicenseTask();
break;
case ProcessState.BuildFwUpdateSafe:
BuildFwUpdateSafeTask();
break;
case ProcessState.UploadFwUpdateSafeToDb:
UploadFwUpdateSafeTask();
break;
case ProcessState.GetCordonelProductionOrdersFromDb:
LoadDbCordonelProductionOrdersTask();
break;
case ProcessState.GetCordonelSerialNumbersFromDb:
LoadDbCordonelSerialNumbersTask();
break;
case ProcessState.GetFwPackagesInfosFromDb:
LoadDbCordonelFwPackagesInfosTask();
break;
case ProcessState.DbAccessLocked:
// Process state change catcher to leave state as is
break;
case ProcessState.UserRegistration:
// Process state change catcher to leave state as is
break;
case ProcessState.UpdateDataTables:
// Process state change catcher to leave state as is
break;
default:
_processState = ProcessState.Idle;
break;
}
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception e)
{
_processState = ProcessState.Error;
if (_fwUpdateBuilderThread.ThreadState == ThreadState.Aborted
|| _fwUpdateBuilderThread.ThreadState == ThreadState.AbortRequested)
{
MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
throw;
}
}
//finally
//{
//}
}
}// state locked against repeated execution
}
#endregion --------------------------------------- State Machine ----------------------------------------------
#region ------------------------------------------ Timer Controls ---------------------------------------------
/// <summary>
/// The timer for progress bar.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Changed logic
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Checks exported
/// </remarks>
/// <remarks date="2021-Oct-28" author="Thomas Wiedebusch">
/// - Added checks for FW-update safes and report files.
/// </remarks>
private void TmrProgressUpdate_Tick(Object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dbAccessDelayCtrMs += TmrDbAccess.Interval;
_dbLicenseRefreshDelayCtrMs += TmrDbAccess.Interval;
switch (_processState)
{
case ProcessState.DbAccessLocked:
CheckDbConnectivity();
break;
case ProcessState.Idle:
CheckDbConnectivity();
CheckDataCollectionStatus();
CheckUpdateOperatorAccountChange();
CheckCustomerChange();
CheckCordonelChange();
CheckFwPackageChange();
CheckFwUpdateSafesChange();
CheckReportsChange();
break;
case ProcessState.BuildFwUpdateSafe:
SetStatusProgressBar(ProgressBarStatus.Value + 1 > 100 ? 0 : ProgressBarStatus.Value + 1);
break;
case ProcessState.ConnectDb:
case ProcessState.GetFwUpdateOperatorsFromDb:
case ProcessState.GetCordonelProductionOrdersFromDb:
case ProcessState.GetCustomersFromDb:
case ProcessState.GetFwPackagesInfosFromDb:
case ProcessState.GetCordonelSerialNumbersFromDb:
case ProcessState.GetFwUpdateSafesInfosFromDb:
case ProcessState.GetReportsFromDb:
case ProcessState.GetAndDisplaySingleReportFromDb:
case ProcessState.UploadFwUpdateSafeToDb:
CheckDbConnectionTimeout();
SetStatusProgressBar(ProgressBarStatus.Value + 1 > 100 ? 0 : ProgressBarStatus.Value + 1);
break;
}
}
#endregion --------------------------------------- Timer Controls ---------------------------------------------
#region ------------------------------------------ User Interaction -------------------------------------------
/// <summary>
/// Select all content for quick change
/// </summary>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void tbxCordonelProductionOrdersSearch_Enter(Object sender, EventArgs e)
{
tbxCordonelProductionOrdersSearch.Select(0, tbxCordonelProductionOrdersSearch.Text.Length);
}
/// <summary>
/// Select all content for quick change
/// </summary>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void tbxCordonelProductionOrdersSearch_MouseClick(Object sender, MouseEventArgs e)
{
tbxCordonelProductionOrdersSearch.Select(0, tbxCordonelProductionOrdersSearch.Text.Length);
}
/// <summary>
/// Validate input
/// </summary>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void tbxOrderNumber_TextChanged(Object sender, EventArgs e)
{
tbxOrderNumber.Text = Regex.Match(tbxOrderNumber.Text, "[0-9]+").Groups[0].Value;
}
/// <summary>
/// Validate input
/// </summary>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void tbxOrderPosition_TextChanged(Object sender, EventArgs e)
{
tbxOrderPosition.Text = Regex.Match(tbxOrderPosition.Text, "[0-9]+").Groups[0].Value;
}
/// <summary>
/// Display Info
/// </summary>
/// <remarks date="2021-Apr-09" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void infoToolStripMenuItem_Click(Object sender, EventArgs e)
{
_processState = ProcessState.UserRegistration;
var frmRegister = new FrmRegister(_regUser);
DisableAllControlsInvoked();
frmRegister.Show();
frmRegister.Closed += FormRegister_Closed;
Hide();
}
/// <summary>
/// Exit of Registration Form.
/// </summary>
/// <remarks date="2021-Feb-04" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void FormRegister_Closed(Object sender, EventArgs e)
{
Show();
Update();
CheckUserRegistration();
}
/// <summary>
/// Exit the program
/// </summary>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void exitToolStripMenuItem_Click(Object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Kick off customer search on key [Enter] pressed
/// </summary>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void tbxCustomerSearchMask_KeyDown(Object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
btnSearchCustomer_Click(this, null);
}
}
/// <summary>
/// Kick off Cordonel search on key [Enter] pressed
/// </summary>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void tbxCordonelProductionOrdersSearch_KeyDown(Object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// Check if the searched serial number is in the download list if ALL have been loaded
if (!string.IsNullOrEmpty(tbxCordonelProductionOrdersSearch.Text) &&
_cordonelProductionSearchInfos.Count != 0 &&
_cordonelProductionSearchInfos.Any(x =>
x.CustomerSerialNumber.Contains(tbxCordonelProductionOrdersSearch.Text)) &&
cbxCordonelProductionOrdersSearch.Text == Constants.StrWildcard)
{
_cordonelsPropertyChanged = true;
return;
}
btnCordonelSearch_Click(this, null);
}
}
/// <summary>
/// Kick off Cordonel search referenced by order number
/// </summary>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Removed DB access delay.
/// </remarks>
private void btnCordonelSearch_Click(Object sender, EventArgs e)
{
// reading if customer is selected
if (string.IsNullOrEmpty(lblCustomerNumber.Text) ||
string.IsNullOrEmpty(cbxCordonelProductionOrdersSearch.Text) ||
_fwUpdateDbAccess?.DbCordonelCustomerOrders == null)
return;
try
{
// leave all original orders list intact to keep this information for the next search
// and clear the search list which will contain the user selected order numbers
_cordonelCustomerOrdersSearch.Clear();
// as the production info is going to fill the grid table view it has to be cleared on every new search
_cordonelProductionSearchInfos.Clear();
// remove unselected devices, avoid adding of already included devices
if (cbxCordonelProductionOrdersSearch?.SelectedItem == null ||
cbxCordonelProductionOrdersSearch.SelectedItem.ToString() == Constants.StrWildcard)
{
_cordonelCustomerOrdersSearch.AddRange(_fwUpdateDbAccess.DbCordonelCustomerOrders);
}
else
{
// format is orderNumber-position e.g. 12345678-10
var orderText = cbxCordonelProductionOrdersSearch.SelectedItem.ToString();
orderText = Regex.Replace(orderText, @"[^0-9\-]", "");
var orderTexts = orderText.Split('-');
if (Int64.TryParse(orderTexts[0], out var orderNumber) &&
Int64.TryParse(orderTexts[1], out var orderPosition))
{
foreach (var order in _fwUpdateDbAccess.DbCordonelCustomerOrders.Where(order =>
order.CustomerOrderNumber == orderNumber &&
order.CustomerOrderPos == orderPosition))
{
_cordonelCustomerOrdersSearch.Add(order);
break;
}
}
}
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingCordonels);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetCordonelSerialNumbersFromDb;
}
catch (Exception)
{
//nothing to do
}
}
/// <summary>
/// Kick off customer search.
/// </summary>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - Clear last search result.
/// </remarks>
private void btnSearchCustomer_Click(Object sender, EventArgs e)
{
// clear last search result
_dataTableCustomers?.Rows.Clear();
_dataTableCustomers?.Columns.Clear();
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingCustomers);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetCustomersFromDb;
}
/// <summary>
/// Select DEBUG Cordonels.
/// </summary>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void cbxAddDebugCordonels_Click(Object sender, EventArgs e)
{
//DebugDeviceAndFwPackageSelection();
}
/// <summary>
/// Kick off FW-Update package search.
/// </summary>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - Single FW-Update package.
/// </remarks>
private void btnFwPackageSearch_Click(Object sender, EventArgs e)
{
// Load fw packages only if not selected
if (_fwUpdatePackage?.FwPackageInfo != null)
return;
DisableAllControlsInvoked();
_dataTableFwPackages?.Clear();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingFwPackages);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetFwPackagesInfosFromDb;
}
/// <summary>
/// Mask already loaded FW-Update packages.
/// </summary>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void allFwPackageSearch_Event(Object sender, EventArgs e)
{
_fwPackagePropertyChanged = true;
}
/// <summary>
/// Tab page selection event (replaced of tabPage_Enter event of a selected tab page as this
/// fires on all data sets copied to it).
/// </summary>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - FW update package selection.
/// </remarks>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Enable retry if DB connection lost.
/// </remarks>
/// <remarks date="2021-Apr-30" author="Thomas Wiedebusch">
/// - FW packages not loaded if already done on page change.
/// </remarks>
/// <remarks date="2022-Apr-13" author="Thomas Wiedebusch">
/// - Set status progress bar active for pre-build summary as this takes a lot of time.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - Single FW-Update package.
/// </remarks>
private void tabControlSelection_Selected(Object sender, TabControlEventArgs e)
{
_processState = ProcessState.Idle;
if (e.TabPage == tabPageFwUpdateSafes)
{
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingFwUpdateSafes);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetFwUpdateSafesInfosFromDb;
}
if (e.TabPage == tabPageReports)
{
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingReports);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetReportsFromDb;
}
if (e.TabPage == tabPageCordonelSelection)
{
// avoid reading of DB contents and reset of selection if customer remains identical
if (!_fwUpdateDbAccess.DbIsConnected)
_customerNumberBeforeLastCordonelDbSearch = "";
if (string.IsNullOrEmpty(lblCustomerNumber.Text) ||
_customerNumberBeforeLastCordonelDbSearch == lblCustomerNumber.Text)
return;
_customerNumberBeforeLastCordonelDbSearch = lblCustomerNumber.Text;
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingOrderNumbers);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetCordonelProductionOrdersFromDb;
}
if (e.TabPage == tabPageCustomerSelection)
{
}
if (e.TabPage == tabPagePreBuildSummary)
{
gridViewPreBuildSummary.Visible = false;
_preBuildSummaryPropertyChanged = true;
}
if (e.TabPage == tabPageFwPackageSelection)
{
// Load fw packages only if not selected indicated in count
if (_fwUpdatePackage?.FwPackageInfo != null ||
(_fwUpdateDbAccess?.DbCordonelFwPackagesInfo != null &&
_fwUpdateDbAccess.DbCordonelFwPackagesInfo.Count > 0))
return;
DisableAllControlsInvoked();
_dataTableFwPackages?.Clear();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingFwPackages);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetFwPackagesInfosFromDb;
}
if (e.TabPage == tabPageUpdateOperator)
{
// Avoid reading of DB contents and reset of selection if an update operator has already been assigned.
// If not, an update of the update operators may be sufficient, as the builder operator may call the
// update operator and require a new registration, so the update may immediately show the new registered
// or refreshed update operator without program exit and restart
if (!string.IsNullOrEmpty(lblUpdateOperator.Text))
return;
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingUpdateOperators);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetFwUpdateOperatorsFromDb;
}
}
/// <summary>
/// Upload FW-Update Safe to DB.
/// </summary>
/// <remarks date="2021-Apr-27" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void btnUploadSafeToDb_Click(Object sender, EventArgs e)
{
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrFwUpdateSafeUploading);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.UploadFwUpdateSafeToDb;
}
/// <summary>
/// License the actual FW-Update Builder which is this product!
/// </summary>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Jun-30" author="Thomas Wiedebusch">
/// - Add 10 years for valid date.
/// </remarks>
private void btnLicenseFwUpdateBuilder_Click(Object sender, EventArgs e)
{
GetFwUpdateSwLicenseFromDb(_assemblyName.Name, out var fwUpdateBuilderLicenseRead);
BuildSwLicense(_assemblyName.Name, out var fwUpdateBuilderLicense);
fwUpdateBuilderLicense.ValidTo = DateTime.Now.AddYears(10);
SetFwUpdateSwLicenseToDb(fwUpdateBuilderLicense);
GetFwUpdateSwLicenseFromDb(_assemblyName.Name, out fwUpdateBuilderLicenseRead);
if (fwUpdateBuilderLicenseRead.Major == fwUpdateBuilderLicense.Major &&
fwUpdateBuilderLicenseRead.Minor == fwUpdateBuilderLicense.Minor &&
fwUpdateBuilderLicenseRead.Build == fwUpdateBuilderLicense.Build)
{
var msg = "FW-Update Builder Version: " +
$"{_version.Major}.{_version.Minor}.{_version.Build} {Resources.StrRegistrationSuccess}";
MessageBoxShow(msg, Resources.StrSuccess);
}
else
{
var msg = $"FW-Update Builder {Resources.StrRegisteredFailed}";
MessageBoxShow(msg, Resources.StrError);
}
}
/// <summary>
/// License the actual FW-Update SW.
/// </summary>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void btnLicenseFwUpdateSw_Click(Object sender, EventArgs e)
{
GetFwUpdateSwLicenseFromDb(FwUpdateSwFullName, out var fwUpdateSwLicenseRead);
BuildSwLicense(FwUpdateSwFullName, out var fwUpdateSwLicense);
SetFwUpdateSwLicenseToDb(fwUpdateSwLicense);
GetFwUpdateSwLicenseFromDb(FwUpdateSwFullName, out fwUpdateSwLicenseRead);
String msg;
if (fwUpdateSwLicenseRead != null)
{
if (fwUpdateSwLicenseRead.Major == fwUpdateSwLicense.Major &&
fwUpdateSwLicenseRead.Minor == fwUpdateSwLicense.Minor &&
fwUpdateSwLicenseRead.Build == fwUpdateSwLicense.Build)
{
msg = "FW-Update Software Version: " +
$"{_version.Major}.{_version.Minor}.{_version.Build} {Resources.StrRegistrationSuccess}";
MessageBoxShow(msg, Resources.StrSuccess);
}
return;
}
msg = $"FW-Update Software {Resources.StrRegisteredFailed}";
MessageBoxShow(msg, Resources.StrError);
}
/// <summary>
/// Build the update safe.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Feb-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-23" author="Thomas Wiedebusch">
/// - Clear content of FW package to force reload and output of loaded package on rebuild of safe.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - Single FW-Update package.
/// </remarks>
private void btnBuildFwUpdateSafe_Click(Object sender, EventArgs e)
{
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
// clear FW package content to force reload from data base and therefore report generation,
// this is essential for rebuilding of a safe
_fwUpdatePackage?.BinaryApplicationFiles?.Clear();
rtbReport.Clear();
rtbReport.ForeColor = ColorDefault;
lblStatusDbConnect.Text = Resources.StrFwUpdateSafeBuilding;
_processState = ProcessState.BuildFwUpdateSafe;
}
/// <summary>
/// Close the date picker for the FW-Update validation date.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void datePickerFwUpdateValidationDate_CloseUp(Object sender, EventArgs e)
{
// change text two times to include the culture info
var dateTimeOffset = DateTimeServer.GetDateTimeOffsetFromDateString(_datePicker.Text);
lblFwUpdateDutyDate.Text = DateTimeServer.GetDateStringFromDateTimeOffset(dateTimeOffset, _cultureInfo);
HideDatePicker();
}
/// <summary>
/// Close the date picker for the user validation date.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void datePickerUserValidationDate_CloseUp(Object sender, EventArgs e)
{
//_userInfo.ValidDate = DateTimeServer.GetDateTimeOffsetFromDateString(_datePicker.Text);
_fwUpdateOperatorsPropertyChanged = true;
HideDatePicker();
}
/// <summary>
/// Activate date time picker.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void lblFwUpdateDutyDate_Click(Object sender, EventArgs e)
{
if (_datePicker.Visible)
{
HideDatePicker();
}
// hide the label, only with this the date picker can be seen !!!!
lblFwUpdateDutyDate.Visible = false;
var cell = tblPanelSafeInfo.GetCellPosition(lblFwUpdateDutyDate);
// Adding DateTimePicker control into cell x, y
tblPanelSafeInfo.Controls.Add(_datePicker, cell.Column, cell.Row);
// The final selection of the date
_datePicker.CloseUp += datePickerFwUpdateValidationDate_CloseUp;
// Now make it visible
_datePicker.Visible = true;
}
#endregion --------------------------------------- User Interaction -------------------------------------------
#region ------------------------------------------ Form Load Unload -------------------------------------------
/// <summary>
/// Ctor
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Invisible progress bar at start.
/// </remarks>
/// <remarks date="2021-Mar-25" author="Thomas Wiedebusch">
/// - Directory for FW-Update Safes created.
/// </remarks>
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
/// - Grid color for update capability checks.
/// </remarks>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Pre-filled FW-Update package search lists.
/// </remarks>
/// <remarks date="2021-Apr-30" author="Thomas Wiedebusch">
/// - MeterFwUpdateRuler.json class changed to nullable DateTime.
/// </remarks>
/// <remarks date="2021-Jun-08" author="Thomas Wiedebusch">
/// - Library added to NLogConfig source path, as this is the release source, copy files only if not
/// identical.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - Removed FUpdateRuler, all data are going to be taken from DB.
/// </remarks>
/// <remarks date="2024-May-08" author="Thomas Wiedebusch">
/// - Culture info fixed to english.
/// </remarks>
public FrmFwUpdateBuilder()
{
_cultureInfo = Thread.CurrentThread.CurrentCulture;
var fwUpdateBuilderExePath = AppDomain.CurrentDomain.BaseDirectory;
//var fwUpdateBuilderLibraryPath = Path.Combine(fwUpdateBuilderExePath, FwUpdateConfig.LibrarySubFolderName);
var fwUpdateBuilderConfigPath = Path.Combine(fwUpdateBuilderExePath, FwUpdateConfig.ConfigSubFolderName);
var nLogConfigSourcePathName = Path.Combine(fwUpdateBuilderConfigPath, ProgramConfig.NlogConfig);
// Build the configuration directory and copy at least the NLog config to it
try
{
if (!Directory.Exists(ApplicationConfigPath))
{
Directory.CreateDirectory(ApplicationConfigPath);
}
if (!Directory.Exists(FwUpdateSafePath))
{
Directory.CreateDirectory(FwUpdateSafePath);
}
// Check for the file in the application exe path and the application configuration path
if (File.Exists(nLogConfigSourcePathName) && !File.Exists(NLogConfigurationDestPathFile))
SystemControl.CopyFile(nLogConfigSourcePathName, NLogConfigurationDestPathFile);
}
catch (Exception)
{
// nothing to do
}
_logger = NLogHelper.CreateOrGetLogger("ServiceFwUpdateBuilder");
_assemblyName = Assembly.GetExecutingAssembly().GetName();
_version = _assemblyName.Version;
_versionString = $"{_assemblyName.Name} Version: {_version.Major}.{_version.Minor}.{_version.Build}.{_version.Revision}";
_logger.Info(StrSeparator);
_logger.Info(_versionString);
_logger.Info(StrSeparator);
InitializeComponent();
if (_cultureInfo.IetfLanguageTag.Contains("de-"))
radioBtnGermanLanguage.Checked = true;
else
radioBtnEnglishLanguage.Checked = true;
RadioBtnEnglishLanguage_Click(this, null);
lblFwUpdateInfo.Text = $@"Version: {_version.Major}.{_version.Minor}.{_version.Build}";
lblStatusDbConnect.ForeColor = ColorDefault;
DateTimeServer.SetValidationDate(FwUpdateConfig.ValidDays);
var dateTime = DateTimeServer.SetValidationDate(FwUpdateConfig.ValidDays).DateTime;
_datePicker.Format = DateTimePickerFormat.Short;
_datePicker.Value = dateTime;
lblFwUpdateDutyDate.Text = _datePicker.Value.ToShortDateString();
_styleValid.BackColor = ColorBackGround;
_styleValid.ForeColor = ColorSuccess;
_styleInvalid.BackColor = ColorBackGround;
_styleInvalid.ForeColor = ColorProcessFailed;
_styleUnknown.BackColor = ColorBackGround;
_styleUnknown.ForeColor = ColorDefault;
_styleInstallationApproved.BackColor = Color.LightGreen;
_styleInstallationApproved.ForeColor = Color.Black;
_styleInstallationDenied.BackColor = Color.LightCoral;
_styleInstallationDenied.ForeColor = Color.Black;
_styleInstallationUserApproval.BackColor = Color.Orange;
_styleInstallationUserApproval.ForeColor = Color.Black;
_styleInstallationRemoved.BackColor = Color.LightGray;
_styleInstallationRemoved.ForeColor = Color.Gray;
// start immediately the connection check
_dbAccessDelayCtrMs = DbConnectionRetryDelayMs;
// validate software license as soon as possible
_dbLicenseRefreshDelayCtrMs = DbLicenseRefreshTimeoutMs;
lblUpdateOperator.Text = "";
btnBuildFwUpdateSafe.Enabled = false;
btnUploadSafeToDb.Enabled = false;
if (System.Diagnostics.Debugger.IsAttached)
{
btnLicenseFwUpdateSw.Visible = true;
btnLicenseFwUpdateBuilder.Visible = true;
//cbxAddDebugCordonels.Visible = true;
}
cbxAddDebugCordonels.Checked = false;
btnLicenseFwUpdateSw.Enabled = false;
btnLicenseFwUpdateBuilder.Enabled = false;
tbxCustomerName.Text = "";
lblCustomerNumber.Text = "";
tbxOrderNumber.Text = @"0001";
tbxOrderPosition.Text = @"10";
var tabControlImages = new ImageList { ColorDepth = ColorDepth.Depth24Bit };
tabControlImages.Images.Add(Resources.ImageMissingInput);
tabControlImages.Images.Add(Resources.ImageValidInput);
tabControlSelection.ImageList = tabControlImages;
// set locked repeat initially different from process state to unlock first entry to state machine
_lastProcessState = ProcessState.Unspecified;
_processState = ProcessState.Init;
_invokerProcessState = _processState;
// disable all actions until license has been verified
DisableControlsExceptLanguageInvoked();
_licenseUnchecked = true;
//assign a thread name to observe this
if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
Thread.CurrentThread.Name = "Form Service FW-Update Builder thread";
//assign thread to loop and start thread
_fwUpdateBuilderThread = new Thread(FwUpdateBuilderStateMachine);
if (!string.IsNullOrEmpty(_fwUpdateBuilderThread.Name))
_fwUpdateBuilderThread.Name = "State Machine Service FW-Update Builder thread";
ThreadWatcher.Instance.Start(_fwUpdateBuilderThread);
}
/// <summary>
/// Exit
/// </summary>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void FrmFwUpdateBuilder_FormClosing(Object sender, FormClosingEventArgs e)
{
_frmFwUpdateReport?.Close();
_datePicker?.Dispose();
_cbxFwReleaseSelection?.Dispose();
_processToken?.Cancel();
}
#endregion --------------------------------------- Form Load Unload -------------------------------------------
#region ------------------------------------------ Build FW-Update Safe ---------------------------------------
/// <summary>
/// Build the firmware update safe and stores it to a file and to the DB.
/// </summary>
/// <remarks date="2021-Jan-27" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
/// - Logging added.
/// </remarks>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - FW-Update SW container added.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Order number and customer text added,
/// - Disable all controls during FW-UpdateSafe build.
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - Additional logging.
/// </remarks>
/// <remarks date="2021-Mar-30" author="Thomas Wiedebusch">
/// - Output short file name.
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - Action on missing FwUpdateSw.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Replaced _userInfo by lblUpdateOperator.
/// </remarks>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Taking FW-Update SW license from DB.
/// </remarks>
/// <remarks date="2021-Apr-27" author="Thomas Wiedebusch">
/// - Added FwUpdateSafeDb preparation
/// </remarks>
/// <remarks date="2021-Oct-27" author="Thomas Wiedebusch">
/// - Added BuildFwUpdateSwContainerFromDb, no longer taken from locally file system!
/// </remarks>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Skip loading of FW update SW if already done in preceding run.
/// </remarks>
/// <remarks date="2022-Jul-02" author="Thomas Wiedebusch">
/// - Removed special characters (e.g. characters from other language like "Umlaute") from file name,
/// as DB problems may occur on FwUpdateSafe load, limit customer name length to 45 characters to
/// keep the file name small.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - New FW-Update safe:
/// - Simply one FW-update package per safe,
/// - Register recovery implemented,
/// - FW information taken from fields instead of the name.
/// </remarks>
/// <remarks date="2023-Oct-09" author="Thomas Wiedebusch">
/// - Check for valid programming parameters.
/// </remarks>
/// <remarks date="2023-Oct-13" author="Thomas Wiedebusch">
/// - Report builder version and safe built date.
/// </remarks>
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
/// - Additional safe information like built date, builder version e.g. for report generation.
/// </remarks>
private Boolean BuildFwUpdateSafe()
{
if (string.IsNullOrEmpty(lblUpdateOperator.Text))
return false;
DisableAllControlsInvoked();
Invoke(new Action(() =>
{
tabControlSelection.SelectTab(tabPageSafeBuildReport);
tabControlSelection.Refresh();
}));
try
{
var fwUpdateSafe = new FwUpdateSafe
{
Software = new SoftwareContainer(),
Updates = new UpdateContainer(),
SafeInfo = new FwUpdateSafeInfo()
};
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
LogText(StrSeparator);
LogText(_versionString);
fwUpdateSafe.SafeInfo.FwUpdateBuilderInfo = _versionString;
LogText(Resources.StrBuilderOperator + @" " + _regUser.FullName);
fwUpdateSafe.SafeInfo.FwUpdateBuilderOperatorId = _regUser.Id.ToString();
LogText(StrSeparator);
LogText(Resources.StrSafeBuiltDate + @" " + $@"{DateTime.UtcNow:dddd, dd-MMM-yyyy HH:mm:ss} UTC");
fwUpdateSafe.SafeInfo.SafeBuiltDateTime = DateTime.UtcNow;
LogText(Resources.StrCustomerName + @" " + tbxCustomerName.Text);
fwUpdateSafe.SafeInfo.CustomerName = tbxCustomerName.Text;
LogText(Resources.StrCustomerNumber + @" " + lblCustomerNumber.Text);
fwUpdateSafe.SafeInfo.CustomerNumber = lblCustomerNumber.Text;
LogText($@"{Resources.StrOrderNumber} {tbxOrderNumber.Text}-{tbxOrderPosition.Text}");
fwUpdateSafe.SafeInfo.FwUpdateOrderPosition = $"{tbxOrderNumber.Text}-{tbxOrderPosition.Text}";
LogText(Resources.StrUpdateOperator + @" " + lblUpdateOperator.Text);
fwUpdateSafe.SafeInfo.FwUpdateFieldOperatorId = _updateOperatorId.ToString();
// first replace the underlines as these are used for the file name and later to separate for customer and order number
var fileName = tbxCustomerName.Text.Replace(FwUpdateConfig.FwUpdateFileFieldSeparator.ToString(), "");
// remove special multilingual characters as they cause problems in DB, leave numbers, the "-", the "&", the "."
// and the white space in.
fileName = Regex.Replace(fileName, @"[^a-zA-Z0-9\-\s\&\.]", "");
// limit customer name length to 45
if (fileName.Length > 45)
{
fileName = fileName.Substring(0, 45);
}
// The safe shall look like "Thames Water_01234567890-10.safe"
_fwUpdateSafeName = $@"{fileName}{FwUpdateConfig.FwUpdateFileFieldSeparator}" +
$@"{tbxOrderNumber.Text}-{tbxOrderPosition.Text}" +
FwUpdateConfig.FwUpdateFileExtension;
fwUpdateSafe.SafeInfo.FwUpdateSafeName = _fwUpdateSafeName;
// Should never occur as [Build FW-Update Safe] is locked if no device is selected
if (_cordonelUpdateList.Count == 0)
{
LogErrorText(Resources.StrCordonelsMissing);
MessageBoxShow(Resources.StrCordonelsMissing, Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
GetFwUpdateSwLicenseFromDb(FwUpdateSwFullName, out fwUpdateSafe.License);
if (fwUpdateSafe.License == null)
{
LogErrorText(Resources.StrSwLicenseMissing);
MessageBoxShow(Resources.StrSwLicenseMissing, Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
// overwrite license validation date as operator has selected this
fwUpdateSafe.License.ValidTo =
DateTimeServer.GetDateTimeFromDateString(lblFwUpdateDutyDate.Text + " 23:59:59", _cultureInfo);
LogText(Resources.StrValidDate + @" " + $@"{fwUpdateSafe.License.ValidTo:dddd, dd-MMM-yyyy HH:mm:ss} UTC");
fwUpdateSafe.SafeInfo.FwUpdateValidDate = fwUpdateSafe.License.ValidTo;
LogText(StrSeparator);
LogSuccessText($@"{Resources.StrSwLicenseLoadedSuccessfully} {FwUpdateProjectName}" +
$" {fwUpdateSafe.License.Major}.{fwUpdateSafe.License.Minor}.{fwUpdateSafe.License.Build}");
// Skip loading of FW update SW if already done in preceding run
if (_fwUpdateSwContainer?.RegisterDefinitionFile == null ||
_fwUpdateSwContainer.SoftwareSetupFile == null ||
_fwUpdateSwContainer.MeterFilesEraseRestore == null ||
string.IsNullOrEmpty(_fwUpdateSwContainer.RegisterDefinitionFile.FileName) ||
string.IsNullOrEmpty(_fwUpdateSwContainer.SoftwareSetupFile.FileName) ||
string.IsNullOrEmpty(_fwUpdateSwContainer.MeterFilesEraseRestore.FileName) ||
_fwUpdateSwContainer.SoftwareDynLinkLibs == null ||
_fwUpdateSwContainer.SoftwareDynLinkLibs.Count == 0)
{
// local files: if (!BuildFwUpdateSwContainer(fwUpdateSafe.License) ||
if (!BuildFwUpdateSwContainerFromDb(fwUpdateSafe.License) ||
_fwUpdateSwContainer?.RegisterDefinitionFile == null ||
_fwUpdateSwContainer.SoftwareSetupFile == null ||
_fwUpdateSwContainer.MeterFilesEraseRestore == null ||
string.IsNullOrEmpty(_fwUpdateSwContainer.RegisterDefinitionFile.FileName) ||
string.IsNullOrEmpty(_fwUpdateSwContainer.SoftwareSetupFile.FileName) ||
string.IsNullOrEmpty(_fwUpdateSwContainer.MeterFilesEraseRestore.FileName) ||
_fwUpdateSwContainer.SoftwareDynLinkLibs == null ||
_fwUpdateSwContainer.SoftwareDynLinkLibs.Count == 0)
{
LogErrorText(Resources.StrSwContainerCollectionFailed);
MessageBoxShow(Resources.StrSwContainerCollectionFailed, Resources.StrError,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
}
fwUpdateSafe.Software = _fwUpdateSwContainer;
LogSuccessText(Resources.StrSwContainerCollectionSuccess);
CompleteCordonelDeviceInfos();
if (_cordonelUpdateList.Count == 0)
{
LogErrorText(Resources.StrCordonelsMissing);
MessageBoxShow(Resources.StrCordonelsMissing, Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
// deny generation of FW update safe as some information couldn't be loaded
if (_fwUpdateCapability.All(x => x.ReleaseContainerLoaded == false) ||
_fwUpdateCapability.Any(x => x.RecoveryNotNeededOrLoaded == false) ||
_fwUpdateCapability.Any(x => x.PowerCorrectionNotNeededOrLoaded == false) ||
_fwUpdateCapability.Any(x => x.PasswordContainerLoaded == false))
{
_processState = ProcessState.Error;
return false;
}
fwUpdateSafe.Updates = new UpdateContainer
{
CordonelFwPackage = new CordonelFirmware(),
CordonelDeviceInfos = new List<CordonelDeviceInfo>()
};
fwUpdateSafe.Updates.CordonelFwPackage = _fwUpdatePackage;
fwUpdateSafe.Updates.CordonelDeviceInfos = _cordonelUpdateList;
////TODO THW remove this DEBUG to map from current selection to TEST BOARD
//_cordonelUpdateList[0].CustomerSerialNumber = "8 SEN20 1975 6272";
//_cordonelUpdateList[0].PcbId = "182100041";
//GetPwdFromDb(_cordonelUpdateList[0]);
//fwUpdateSafe.License.Build = 8;
//var dateTime = fwUpdateSafe.License.ValidTo.AddDays(-1);
//fwUpdateSafe.License.ValidTo = dateTime;
//var addDays = fwUpdateSafe.SafeInfo.FwUpdateValidDate.AddDays(-1);
//fwUpdateSafe.SafeInfo.FwUpdateValidDate = addDays;
////TODO THW remove this DEBUG to map from current selection to TEST BOARD
var serializedData = JsonConvert.SerializeObject(fwUpdateSafe);
var asciiStream = Encoding.ASCII.GetBytes(serializedData);
// the build primary key
BuildPrimaryKey(lblUpdateOperator.Text);
if (_primaryKey == null)
{
LogErrorText(Resources.StrUserInformationMissing);
MessageBoxShow(Resources.StrUserInformationMissing, Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
var encryptedStream = FwUpdateCrypt.Encrypt(asciiStream, _primaryKey);
// prepare FwUpdateSafeDb
if (string.IsNullOrEmpty(_regUser.FullName))
_regUser.FullName = "";
_fwUpdateSafeDb = new FwUpdateSafeDb
{
BuilderOperatorName = _regUser.FullName,
Name = _fwUpdateSafeName,
UserId = _updateOperatorId,
Content = encryptedStream,
ValidDate = new DateTimeOffset(fwUpdateSafe.License.ValidTo)
};
//assign FW update safe
var fwUpdateSafeFile = Path.Combine(FwUpdateSafePath, _fwUpdateSafeName);
File.WriteAllBytes(fwUpdateSafeFile, encryptedStream);
return true;
}
catch (Exception e)
{
LogErrorText(Resources.StrFwUpdateSafeBuiltFailed);
MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
}
/// <summary>
/// Collect cordonel device info, the user has to input the required pcbIds and release versions in advance!
/// </summary>
/// <remarks date="2021-Jan-27" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Feb-02" author="Thomas Wiedebusch">
/// - Load from DB.
/// </remarks>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Kick off DB connection with dummy request.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Password container directly assigned.
/// </remarks>
/// <remarks date="2023-Oct-09" author="Thomas Wiedebusch">
/// - Recovery registers.
/// </remarks>
/// <remarks date="2024-May-07" author="Thomas Wiedebusch">
/// - Power correction.
/// </remarks>
private void CompleteCordonelDeviceInfos()
{
// get the real passwords
foreach (var cordonelDeviceInfo in _cordonelUpdateList)
{
var fwUpdateCapability = new FwUpdateCapability
{
PcbId = cordonelDeviceInfo.PcbId,
PasswordContainerLoaded = GetPwdFromDb(cordonelDeviceInfo)
};
// stop collection of data if the password container cannot be loaded
if (!fwUpdateCapability.PasswordContainerLoaded)
{
continue;
}
// load a single release if not already done
fwUpdateCapability.ReleaseContainerLoaded = GetFwPackageFromDb(cordonelDeviceInfo.RequiredFwRelease);
// load a recovery file
fwUpdateCapability.RecoveryNotNeededOrLoaded = GetRecoveryRegistersFromDb(cordonelDeviceInfo);
// load power correction infos;
// Installed FW version has to be known in advance and
// FW package has to be loaded in advance to check for installed versus required FW version as this is the base
// for the decision of needed power correction.
if (_fwUpdatePackage.FwPackageInfo.Version != null && cordonelDeviceInfo.InstalledFwVersion != null
&& ((_fwUpdatePackage.FwPackageInfo.Region ==
"EMEA" && _fwUpdatePackage.FwPackageInfo.Version >= PowCorrConst.EmeaFwThresholdForPowCorr
&& cordonelDeviceInfo.InstalledFwVersion < PowCorrConst.EmeaFwThresholdForPowCorr)
|| (_fwUpdatePackage.FwPackageInfo.Region ==
"NA" && _fwUpdatePackage.FwPackageInfo.Version >= PowCorrConst.NaFNaFwThresholdForPowCorr
&& cordonelDeviceInfo.InstalledFwVersion < PowCorrConst.NaFNaFwThresholdForPowCorr)))
{
fwUpdateCapability.PowerCorrectionNotNeededOrLoaded = GetPowerCorrectionsFromDb(cordonelDeviceInfo);
}
else
{
cordonelDeviceInfo.PowerCorrectionValues = null;
fwUpdateCapability.PowerCorrectionNotNeededOrLoaded = true;
}
// avoid multiple assignments with deviating information before adding the new generated object
foreach (var x in _fwUpdateCapability.Where(x => x.PcbId == cordonelDeviceInfo.PcbId))
{
_fwUpdateCapability.Remove(x);
break;
}
_fwUpdateCapability.Add(fwUpdateCapability);
}
}
#endregion --------------------------------------- Build FW-Update Safe ---------------------------------------
#region ------------------------------------------ Checks -----------------------------------------------------
/// <summary>
/// Check the user registration from DB.
/// </summary>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2024-Apr-11" author="Thomas Wiedebusch">
/// - Added builder operator id.
/// </remarks>
private void CheckUserRegistration()
{
if (_fwUpdateDbAccess == null || _regUser == null)
return;
_regUser.Domain = CryptInformation.GetSysDomain();
_regUser.LogInName = CryptInformation.GetSysUserLoginName();
_fwUpdateDbAccess.GetAllBuilderOperatorsFromDb();
// If the connection to the DB cannot be established
if (!_fwUpdateDbAccess.GetAllBuilderOperatorsFromDb() ||
_fwUpdateDbAccess.DbAllBuilderOperators == null ||
_fwUpdateDbAccess.DbAllBuilderOperators.Count == 0)
return;
// search for this system user specified by Domain and LogInName
foreach (var user in _fwUpdateDbAccess.DbAllBuilderOperators.Where(user => user.Domain == _regUser.Domain &&
user.LogInName == _regUser.LogInName))
{
_regUser.AccountActive = user.AccountActive;
_regUser.FullName = user.FullName;
_regUser.Id = user.Id;
break;
}
if (_regUser.AccountActive)
{
EnableInputsInvoked();
_processState = ProcessState.Idle;
return;
}
DisableControlsExceptLanguageInvoked();
_processState = ProcessState.DbAccessLocked;
}
/// <summary>
/// Pre-select FW package if not already set.
/// Uses all information of fw-package-info from DB and cordonel-info from DB.
/// Presets the Approval or Removal based on the update capability check.
/// The _fwUpdatePackage information has to be preset in advance.
/// </summary>
/// <remarks date="2022-Apr-12" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2022-Dec-01" author="Thomas Wiedebusch">
/// - Single FW-Update package,
/// - Used fw update package id instead of name.
/// </remarks>
/// <remarks date="2023-Feb-19" author="Thomas Wiedebusch">
/// - Reason for check error added.
/// </remarks>
/// <remarks date="2023-Feb-20" author="Thomas Wiedebusch">
/// - Extended update capability check.
/// </remarks>
/// <remarks date="2023-Feb-24" author="Thomas Wiedebusch">
/// - Meter size check corrected.
/// </remarks>
/// <remarks date="2023-Mar-17" author="Thomas Wiedebusch">
/// - FW-packages with multiple meter sizes supported in update capability check,
/// - FW-packages checked for multiple radio frequencies.
/// </remarks>
private String PreSelectFwPackages(CordonelProductionInfosDb cordonelDbInfo)
{
if (cordonelDbInfo == null)
return Constants.StrUnknown;
// preset to not approved to leave the approval open for user in the "pre-build summary" tab
cordonelDbInfo.IsRemoved = true;
cordonelDbInfo.IsApproved = false;
cordonelDbInfo.RequiredReleaseNameVersion = null;
cordonelDbInfo.ReasonForUpdateDenial = "";
// the _fwUpdatePackage has to be filled with all needed information being able to compare this with
// the information about the Cordonel which should be updated
if (_fwUpdatePackage?.FwPackageInfo == null || _fwUpdateDbAccess?.DbCordonelFwPackagesInfo == null)
{
return Constants.StrUnknown;
}
// catch all meter sizes of a different entry but the identical FW-package references by name
var fwMeterSizes = new List<String>();
// catch multiple radio frequencies
Int32? fwRadioFrequencyMhz = null;
Int32? fwCoreRevisionMax = null;
Int32? fwCoreRevisionMin = null;
Int32? fwMetrologyVersion = null;
var fwRegion = "";
// search in the list of all FW-packages infos for the user selected Id
foreach (var fw in _fwUpdateDbAccess.DbCordonelFwPackagesInfo.Where(
fw => _fwUpdatePackage.FwPackageInfo.Id == fw.Id))
{
// From here the FW-package is detected by its Id
// assign the unique version for all devices, even if these are having a lack of information
cordonelDbInfo.RequiredReleaseNameVersion = fw.Name;
fwRadioFrequencyMhz = fw.RadioFrequencyMhz;
fwCoreRevisionMax = fw.CoreRevisionMax;
fwCoreRevisionMin = fw.CoreRevisionMin;
fwMetrologyVersion = fw.MetrologyVersion;
fwRegion = fw.Region;
fwMeterSizes.Add(fw.MeterSize);
// As the first package has been found, the loop can be exited.
break;
}
// Collect all meter sizes and from identical FW-package(s)
foreach (var fw in _fwUpdateDbAccess.DbCordonelFwPackagesInfo.Where(
fw => fw.Name == cordonelDbInfo.RequiredReleaseNameVersion))
{
// on a package with multiple frequency support set the frequency to null to skip radio check
// in the FW-Update SW
if (fw.RadioFrequencyMhz != fwRadioFrequencyMhz)
{
fwRadioFrequencyMhz = null;
}
// check multiple meter sizes in the FW-Update SW
if (fwMeterSizes.All(size => size != fw.MeterSize))
{
fwMeterSizes.Add(fw.MeterSize);
}
}
try
{
var checkPassed = true;
// if the installed version is unknown or not set, leave the approval to the user
if (string.IsNullOrEmpty(cordonelDbInfo.CoreRevision) || cordonelDbInfo.CoreRevision == Constants.StrUnknown)
{
cordonelDbInfo.ReasonForUpdateDenial = Resources.StrUpdateCapabilityCheckCore;
checkPassed = false;
}
else
{
// extract the Cordonel core version and validate it
var coreVersion = cordonelDbInfo.CoreRevision;
coreVersion = coreVersion.Replace(".", "");
Int32.TryParse(coreVersion, out var requiredCoreVersion);
if (requiredCoreVersion > fwCoreRevisionMax || requiredCoreVersion < fwCoreRevisionMin)
{
cordonelDbInfo.ReasonForUpdateDenial = "|";
cordonelDbInfo.ReasonForUpdateDenial = Resources.StrUpdateCapabilityCheckCore;
checkPassed = false;
}
}
// check the completeness of information in DB of the Cordonel region
if (string.IsNullOrEmpty(cordonelDbInfo.Region) || cordonelDbInfo.Region != fwRegion)
{
cordonelDbInfo.ReasonForUpdateDenial += "|";
cordonelDbInfo.ReasonForUpdateDenial += Resources.StrUpdateCapabilityCheckRegion;
checkPassed = false;
}
// check radio but skip check for NA region or if FW-package supports all frequencies indicated
// by null for the FW-package info frequency field
if (cordonelDbInfo.Region != "NA" && fwRadioFrequencyMhz != null)
{
// check the radio frequencies
if (string.IsNullOrEmpty(cordonelDbInfo.RadioFrequency)
|| cordonelDbInfo.RadioFrequency == Constants.StrUnknown
|| string.IsNullOrEmpty(fwRadioFrequencyMhz.ToString())
|| cordonelDbInfo.RadioFrequency != fwRadioFrequencyMhz.ToString())
{
cordonelDbInfo.ReasonForUpdateDenial += "|";
cordonelDbInfo.ReasonForUpdateDenial += Resources.StrUpdateCapabilityCheckRadio;
checkPassed = false;
}
}
// if meter size does not fit and not for all meter sizes
if (cordonelDbInfo.Diameter == Constants.StrUnknown
|| (fwMeterSizes.All(meterSize => meterSize != cordonelDbInfo.Diameter)
&& fwMeterSizes.Any(meterSize => meterSize != Constants.StrWildcard)))
{
cordonelDbInfo.ReasonForUpdateDenial += "|";
cordonelDbInfo.ReasonForUpdateDenial += Resources.StrUpdateCapabilityCheckMeterSize;
checkPassed = false;
}
// if the installed version is unknown or not set, leave the approval to the user
if (string.IsNullOrEmpty(cordonelDbInfo.Metrology) || cordonelDbInfo.Metrology == Constants.StrUnknown)
{
cordonelDbInfo.ReasonForUpdateDenial += "|";
cordonelDbInfo.ReasonForUpdateDenial += Resources.StrUpdateCapabilityCheckMetrokogy;
checkPassed = false;
}
else
{
// extract the metrology version and validate it
var metrologyVersion = cordonelDbInfo.Metrology;
metrologyVersion = metrologyVersion.Replace(".", "");
Int32.TryParse(metrologyVersion, out var requiredMetrologyVersion);
if (requiredMetrologyVersion != fwMetrologyVersion)
{
cordonelDbInfo.ReasonForUpdateDenial += "|";
cordonelDbInfo.ReasonForUpdateDenial += Resources.StrUpdateCapabilityCheckMetrokogy;
checkPassed = false;
}
}
if (checkPassed)
{
cordonelDbInfo.IsRemoved = false;
cordonelDbInfo.IsApproved = true;
}
}
catch (Exception)
{
return Constants.StrUnknown;
}
return cordonelDbInfo.RequiredReleaseNameVersion;
}
/// <summary>
/// Check DB connection timeout during connectivity check
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-09" author="Thomas Wiedebusch">
/// - Avoid interruption if DB is connected.
/// </remarks>
private void CheckDbConnectionTimeout()
{
// stop connectivity test after response timeout
if (_fwUpdateDbAccess == null || _fwUpdateDbAccess.DbIsConnected ||
_dbAccessDelayCtrMs <= DbConnectionTimeoutMs)
return;
EnableInputsInvoked();
_dbAccessDelayCtrMs = 0;
SetStatusProgressBar(0, false);
_processState = ProcessState.Idle;
}
/// <summary>
/// Check DB license refresh.
/// </summary>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void RefreshLicense()
{
if (DateTimeOffset.Compare(_licenseValidDateTimeOffset, DateTimeOffset.Now) < 0)
_licenseUnchecked = true;
}
/// <summary>
/// Check DB connectivity.
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - Progress bar off if connection lost.
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - On initialized DB connection fire the tabControl event to update user view,
/// - dbAccessLocked introduced.
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Disabled: On initialized DB connection fire the tabControl event to update user view.
/// </remarks>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Check for license.
/// </remarks>
private void CheckDbConnectivity()
{
if (_fwUpdateDbAccess.DbIsConnected)
{
// DB is free to use
if (_dbLicenseRefreshDelayCtrMs > DbLicenseRefreshTimeoutMs)
{
_dbLicenseRefreshDelayCtrMs = 0;
RefreshLicense();
if (_licenseUnchecked)
_processState = ProcessState.ValidateSoftware;
}
// If the progress bar is visible, the DB connection status has just changed from NOT connected
if (!ProgressBarStatus.Visible && lblStatusDbConnect.Text != Resources.StrNotConnectedToDb)
return;
// Stop connection test
SetStatusProgressBar(0, false);
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrConnectedToDb);
// enable controls only if software license has been checked
EnableInputsInvoked();
}
else
{
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorProcessFailed, Resources.StrNotConnectedToDb);
if (ProgressBarStatus.Visible)
SetStatusProgressBar(0, false);
// kick off new connectivity check if retry delay exceeded and DB is NOT connected
if (_dbAccessDelayCtrMs <= DbConnectionRetryDelayMs)
return;
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrConnectingToDb);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.ConnectDb;
}
}
/// <summary>
/// Check all required data needed to input for FW-Update safe and enables/disables the [FW Update] button.
/// This colors the labels and tabs to inform the user about the lack of data.
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - [Build FW-Update Safe] check extended
/// </remarks>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - checks extended,
/// - avoid repeated setup of item if already done.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Added builder license check.
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - FW-Update package status added.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Check order numbers.
/// </remarks>
/// <remarks date="2021-Apr-09" author="Thomas Wiedebusch">
/// - Check fw packages.
/// </remarks>
/// <remarks date="2021-Apr-10" author="Thomas Wiedebusch">
/// - Lock customer search button if customer is selected to avoid lose of information on new search.
/// </remarks>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Today is a valid day.
/// </remarks>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Metrology search items added.
/// </remarks>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Pre-Build summary check.
/// </remarks>
/// <remarks date="2021-Apr-17" author="Thomas Wiedebusch">
/// - Cordonel selection check moved to production pre select.
/// </remarks>
/// <remarks date="2021-Apr-18" author="Thomas Wiedebusch">
/// - Cordonel selection check and Cordonel approval check differentiate.
/// </remarks>
/// <remarks date="2021-Apr-29" author="Thomas Wiedebusch">
/// - Use selected Cordonels to fill the FW package search infos.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - Single FW-Update package.
/// </remarks>
/// <remarks date="2023-Feb-21" author="Thomas Wiedebusch">
/// - Preselect the search mask with last search items set by Cordonel selection to simplify the initial
/// FW-package selection.
/// </remarks>
private void CheckDataCollectionStatus()
{
// Customer Orders ----------------------------------------------------------------------------------------
// Check the order numbers (add one for the "*" )
if (_fwUpdateDbAccess?.DbCordonelCustomerOrders != null)
{
if (cbxCordonelProductionOrdersSearch.Items.Count !=
_fwUpdateDbAccess.DbCordonelCustomerOrders.Count + 1)
{
cbxCordonelProductionOrdersSearch.Items.Clear();
cbxCordonelProductionOrdersSearch.Text = Constants.StrWildcard;
cbxCordonelProductionOrdersSearch.Items.Add(Constants.StrWildcard);
foreach (var order in _fwUpdateDbAccess.DbCordonelCustomerOrders)
{
cbxCordonelProductionOrdersSearch.Items.Add(
$@"{order.CustomerOrderNumber}-{order.CustomerOrderPos}");
}
}
}
// Update Operator ----------------------------------------------------------------------------------------
// Set the background color for update operator and use it later for comparison to enable button
var backGroundColor = string.IsNullOrEmpty(lblUpdateOperator.Text) ? ColorBkMissing : ColorBkValid;
if (backGroundColor != lblUpdateOperator.BackColor)
{
lblUpdateOperator.BackColor = backGroundColor;
if (backGroundColor == ColorBkMissing)
{
tabPageUpdateOperator.ImageIndex = 0;
}
else
tabPageUpdateOperator.ImageIndex = 1;
}
// Validation Time for FW update --------------------------------------------------------------------------
// Time for update validation has been properly set time including today (offset needed as the time
// will count up continuously)
backGroundColor = DateTime.Compare(DateTimeServer.GetDateTimeFromDateString(lblFwUpdateDutyDate.Text +
" 23:59:59", _cultureInfo), DateTime.Now) > 0 ? ColorBkValid : ColorBkMissing;
if (backGroundColor != lblFwUpdateDutyDate.BackColor)
{
lblFwUpdateDutyDate.BackColor = backGroundColor;
}
// Customer Name ------------------------------------------------------------------------------------------
backGroundColor = string.IsNullOrEmpty(tbxCustomerName.Text) ? ColorBkMissing : ColorBkValid;
if (backGroundColor != tbxCustomerName.BackColor)
{
tbxCustomerName.BackColor = backGroundColor;
lblCustomerNumber.BackColor = backGroundColor;
if (backGroundColor == ColorBkMissing)
{
tabPageCustomerSelection.ImageIndex = 0;
lblInfoSelectCustomer.Visible = true;
btnSearchCustomer.Enabled = true;
}
else
{
tabPageCustomerSelection.ImageIndex = 1;
lblInfoSelectCustomer.Visible = false;
btnSearchCustomer.Enabled = false;
}
}
// Customer Cordonels Search for this production number ---------------------------------------------------
if (tbxCordonelsCustomerCount.Text != _cordonelProductionSearchInfos.Count.ToString())
tbxCordonelsCustomerCount.Text = _cordonelProductionSearchInfos.Count.ToString();
backGroundColor = _cordonelProductionSearchInfos.Count == 0 ? ColorBkMissing : ColorBkValid;
if (backGroundColor != tbxCordonelsCustomerCount.BackColor)
{
tbxCordonelsCustomerCount.BackColor = backGroundColor;
}
// Selected Cordonels -------------------------------------------------------------------------------------
if (lblCordonelsSelectedCount.Text != _cordonelProductionPreSelectInfos.Count.ToString())
lblCordonelsSelectedCount.Text = _cordonelProductionPreSelectInfos.Count.ToString();
backGroundColor = _cordonelProductionPreSelectInfos.Count == 0 ? ColorBkMissing : ColorBkValid;
if (backGroundColor != lblCordonelsSelectedCount.BackColor)
{
lblCordonelsSelectedCount.BackColor = backGroundColor;
}
if (_cordonelProductionPreSelectInfos.Count == 0)
{
if (tabPageCordonelSelection.ImageIndex != 0)
tabPageCordonelSelection.ImageIndex = 0;
}
else
{
if (tabPageCordonelSelection.ImageIndex != 1)
tabPageCordonelSelection.ImageIndex = 1;
}
// Selected FW-Packages -----------------------------------------------------------------------------------
// Add fw packages to fw package selection combo box for Cordonel FW to install selection
if (_fwUpdateDbAccess?.DbCordonelFwPackage != null && _fwUpdatePackage?.FwPackageInfo != null &&
_cbxFwReleaseSelection != null)
{
if (_cbxFwReleaseSelection.Items.Count < 1)
{
_cbxFwReleaseSelection.Items.Clear();
_cbxFwReleaseSelection.Items.Add(_fwUpdatePackage.FwPackageInfo.Name);
}
}
// Check if FW packages are selected
if (_fwUpdatePackage?.FwPackageInfo != null && lblSelectedFwPackages.Text != @"1")
lblSelectedFwPackages.Text = @"1";
else if (_fwUpdatePackage?.FwPackageInfo == null)
lblSelectedFwPackages.Text = @"0";
backGroundColor = _fwUpdatePackage?.FwPackageInfo == null ? ColorBkMissing : ColorBkValid;
if (backGroundColor != lblSelectedFwPackages.BackColor)
{
lblSelectedFwPackages.BackColor = backGroundColor;
if (backGroundColor == ColorBkMissing)
{
if (!btnFwPackageSearch.Enabled)
btnFwPackageSearch.Enabled = true;
tabPageFwPackageSelection.ImageIndex = 0;
}
else
{
if (btnFwPackageSearch.Enabled)
btnFwPackageSearch.Enabled = false;
tabPageFwPackageSelection.ImageIndex = 1;
}
}
// check if FW packages are loaded from DB and parsed to the search info to extract radio info
if (_radioSearchItems.Count + 1 != cbxFwPackageRadioMask.Items.Count)
{
cbxFwPackageRadioMask.Items.Clear();
cbxFwPackageRadioMask.Items.Add(Constants.StrWildcard);
foreach (var item in _radioSearchItems)
{
if (!string.IsNullOrEmpty(item) && item != Constants.StrUnknown)
cbxFwPackageRadioMask.Items.Add(item);
}
var index = cbxFwPackageRadioMask.Items.Count - 1;
cbxFwPackageRadioMask.Text = cbxFwPackageRadioMask.Items[index].ToString();
_fwPackagePropertyChanged = true;
}
// check if FW packages are loaded from DB and parsed to the search info to extract region info
if (_regionSearchItems.Count + 1 != cbxFwPackageRegionMask.Items.Count)
{
cbxFwPackageRegionMask.Items.Clear();
cbxFwPackageRegionMask.Items.Add(Constants.StrWildcard);
foreach (var item in _regionSearchItems)
{
if (!string.IsNullOrEmpty(item) && item != Constants.StrUnknown)
cbxFwPackageRegionMask.Items.Add(item);
}
var index = cbxFwPackageRegionMask.Items.Count - 1;
cbxFwPackageRegionMask.Text = cbxFwPackageRegionMask.Items[index].ToString();
_fwPackagePropertyChanged = true;
}
// check if FW packages are loaded from DB and parsed to the search info to extract size info
if (_sizeSearchItems.Count + 1 != cbxFwPackageSizeMask.Items.Count)
{
cbxFwPackageSizeMask.Items.Clear();
cbxFwPackageSizeMask.Items.Add(Constants.StrWildcard);
foreach (var item in _sizeSearchItems)
{
if (!string.IsNullOrEmpty(item) && item != Constants.StrUnknown)
cbxFwPackageSizeMask.Items.Add(item);
}
var index = cbxFwPackageSizeMask.Items.Count - 1;
cbxFwPackageSizeMask.Text = cbxFwPackageSizeMask.Items[index].ToString();
_fwPackagePropertyChanged = true;
}
// check if FW packages are loaded from DB and parsed to the search info to extract metrology info
if (_metrologySearchItems.Count + 1 != cbxFwPackageMetrologyMask.Items.Count)
{
cbxFwPackageMetrologyMask.Items.Clear();
cbxFwPackageMetrologyMask.Items.Add(Constants.StrWildcard);
foreach (var item in _metrologySearchItems)
{
if (!string.IsNullOrEmpty(item) && item != Constants.StrUnknown)
cbxFwPackageMetrologyMask.Items.Add(item);
}
var index = cbxFwPackageMetrologyMask.Items.Count - 1;
cbxFwPackageMetrologyMask.Text = cbxFwPackageMetrologyMask.Items[index].ToString();
_fwPackagePropertyChanged = true;
}
// check if FW packages are loaded from DB and parsed to the search info to extract core info
if (_coreSearchItems.Count + 1 != cbxFwPackageCoreMak.Items.Count)
{
cbxFwPackageCoreMak.Items.Clear();
cbxFwPackageCoreMak.Items.Add(Constants.StrWildcard);
foreach (var item in _coreSearchItems)
{
if (!string.IsNullOrEmpty(item) && item != Constants.StrUnknown)
cbxFwPackageCoreMak.Items.Add(item);
}
var index = cbxFwPackageCoreMak.Items.Count - 1;
cbxFwPackageCoreMak.Text = cbxFwPackageCoreMak.Items[index].ToString();
_fwPackagePropertyChanged = true;
}
// FW-Update Order Number ---------------------------------------------------------------------------------
backGroundColor = string.IsNullOrEmpty(tbxOrderNumber.Text) ? ColorBkMissing : ColorBkValid;
if (backGroundColor != tbxOrderNumber.BackColor)
{
tbxOrderNumber.BackColor = backGroundColor;
tbxOrderPosition.BackColor = backGroundColor;
}
// FW-Update Safe Upload Capability -----------------------------------------------------------------------
if (btnUploadSafeToDb.Enabled != _enableFwSafeUpload)
{
btnUploadSafeToDb.Enabled = _enableFwSafeUpload;
}
// Pre Build Summary Check --------------------------------------------------------------------------------
// search for missing approval in production list or missing FW update package
var updateLocked = (_cordonelProductionPreSelectInfos.Count == 0 ||
_cordonelProductionPreSelectInfos.Any(prodCord =>
!prodCord.IsApproved && prodCord.IsSelected)) ||
_cordonelUpdateList.Any(updCord => string.IsNullOrEmpty(updCord.RequiredFwRelease) ||
updCord.RequiredFwRelease == Constants.StrUnknown);
if (updateLocked)
{
if (tabPagePreBuildSummary.ImageIndex != 0)
tabPagePreBuildSummary.ImageIndex = 0;
}
else
{
if (tabPagePreBuildSummary.ImageIndex != 1)
tabPagePreBuildSummary.ImageIndex = 1;
}
// Approved Cordonels -------------------------------------------------------------------------------------
if (lblCordonelsApprovedCount.Text != _cordonelUpdateList.Count.ToString())
lblCordonelsApprovedCount.Text = _cordonelUpdateList.Count.ToString();
backGroundColor = _cordonelUpdateList.Count == 0 ? ColorBkMissing : ColorBkValid;
if (backGroundColor != lblCordonelsApprovedCount.BackColor)
{
lblCordonelsApprovedCount.BackColor = backGroundColor;
}
// Control Activation/ Deactivation -----------------------------------------------------------------------
if (_fwUpdateDbAccess != null && !_fwUpdateDbAccess.DbIsConnected)
{
if (btnBuildFwUpdateSafe.Enabled)
btnBuildFwUpdateSafe.Enabled = false;
if (btnLicenseFwUpdateSw.Enabled)
btnLicenseFwUpdateSw.Enabled = false;
if (btnLicenseFwUpdateBuilder.Enabled)
btnLicenseFwUpdateBuilder.Enabled = false;
if (btnCordonelSearch.Enabled)
btnCordonelSearch.Enabled = false;
if (btnSearchCustomer.Enabled)
btnSearchCustomer.Enabled = false;
}
else
{
// license can be activated soon the DB is connected
if (!btnLicenseFwUpdateSw.Enabled)
btnLicenseFwUpdateSw.Enabled = true;
if (!btnLicenseFwUpdateBuilder.Enabled)
btnLicenseFwUpdateBuilder.Enabled = true;
if (!string.IsNullOrEmpty(lblCustomerNumber.Text) && !btnCordonelSearch.Enabled)
btnCordonelSearch.Enabled = true;
if (string.IsNullOrEmpty(lblCustomerNumber.Text) && btnCordonelSearch.Enabled)
btnCordonelSearch.Enabled = false;
// enable FW-Update Safe generation button
if (lblUpdateOperator.BackColor != ColorBkValid ||
lblFwUpdateDutyDate.BackColor != ColorBkValid ||
tbxCustomerName.BackColor != ColorBkValid ||
tbxOrderNumber.BackColor != ColorBkValid ||
_fwUpdatePackage == null ||
_cordonelUpdateList.Count == 0 ||
updateLocked)
{
if (btnBuildFwUpdateSafe.Enabled)
btnBuildFwUpdateSafe.Enabled = false;
return;
}
if (!btnBuildFwUpdateSafe.Enabled)
btnBuildFwUpdateSafe.Enabled = true;
}
}
/// <summary>
/// Check for change in user account.
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Locked if operators are not loaded.
/// </remarks>
private void CheckUpdateOperatorAccountChange()
{
if (!_fwUpdateOperatorsPropertyChanged || _fwUpdateDbAccess?.DbFullQualifiedUpdateOperators == null ||
_fwUpdateDbAccess.DbFullQualifiedUpdateOperators.Count == 0)
return;
_fwUpdateOperatorsPropertyChanged = false;
// update output information
FillDataGridWithUpdateOperatorInfos();
}
/// <summary>
/// Check for change of customer.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Locked if customers are not loaded.
/// </remarks>
private void CheckCustomerChange()
{
if (!_customersPropertyChanged || _fwUpdateDbAccess?.DbSearchedCordonelCustomers == null ||
_fwUpdateDbAccess.DbSearchedCordonelCustomers.Count == 0)
return;
_customersPropertyChanged = false;
// update output information
FillDataGridWithCustomerInfos();
}
/// <summary>
/// Check for change of Cordonels.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Locked if Cordonels are not loaded.
/// </remarks>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Added pre build summary.
/// </remarks>
private void CheckCordonelChange()
{
if (_cordonelsPropertyChanged)
{
_cordonelsPropertyChanged = false;
// update output information
FillDataGridWithCordonelInfos();
}
if (!_preBuildSummaryPropertyChanged)
return;
_preBuildSummaryPropertyChanged = false;
// update output information
FillDataGridWithPreBuildSummaryInfos();
}
/// <summary>
/// Check for change of FW-Update packages.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Locked if FW-Packages are not loaded.
/// </remarks>
/// <remarks date="2021-Dec-01" author="Thomas Wiedebusch">
/// - Take FW package info.
/// </remarks>
private void CheckFwPackageChange()
{
if (!_fwPackagePropertyChanged || _fwUpdateDbAccess?.DbCordonelFwPackagesInfo == null ||
_fwUpdateDbAccess.DbCordonelFwPackagesInfo.Count == 0)
return;
_fwPackagePropertyChanged = false;
// update output information
FillDataGridWithFwPackageInfos();
}
/// <summary>
/// Check for change of report files.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void CheckReportsChange()
{
if (!_fwReportsPropertyChanged)
return;
_fwReportsPropertyChanged = false;
// update output information
FillDataGridWithReportsInfos();
}
/// <summary>
/// Check for change of Fw update safes.
/// </summary>
/// <remarks date="2021-Oct-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void CheckFwUpdateSafesChange()
{
if (!_fwUpdateSafesPropertyChanged)
return;
_fwUpdateSafesPropertyChanged = false;
// update output information
FillDataGridWithFwUpdateSafesInfos();
}
#endregion --------------------------------------- Checks -----------------------------------------------------
#region ------------------------------------------ Tools ------------------------------------------------------
///// <summary>
///// Build the meter file erase restore information and export to MeterFilesConfigFilePathName.
///// </summary>
///// <remarks date="2021-Feb-17" author="Thomas Wiedebusch">
///// - Initial.
///// </remarks>
//public void BuildMeterFilesEraseRestoreInfo()
//{
// // loading the lists of meter files which shall be erased before the FW-Update and restored after.
// var meterFilesEraseRestore = new MeterFilesEraseRestore
// {
// Erase = new List<String>
// {
// "1\\tstfile",
// "1\\fdrdata",
// "1\\logdata",
// "1\\blklist",
// "1\\mettable",
// "1\\pulsedbg",
// "1\\upg*",
// "1\\img*"
// }
// };
// var meterFileRestore = new MeterFileRestore
// {
// Name = "1\\tstfile",
// ByteSize = 262144,
// Pattern = null
// };
// meterFilesEraseRestore.Restore = new List<MeterFileRestore>
// {
// meterFileRestore
// };
// var text = JsonConvert.SerializeObject(meterFilesEraseRestore);
// var asciiStream = Encoding.UTF8.GetBytes(text);
// File.WriteAllBytes(FwUpdateConfig.MeterFilesEraseRestoreConfigFileName, asciiStream);
//}
/// <summary>
/// Error processes.
/// </summary>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void ErrorProcesses()
{
// take the invoker of the error state to generate the message and / or popup window
switch (_invokerProcessState)
{
default:
_processState = ProcessState.Idle;
break;
}
}
/// <summary>
/// Stop all ongoing processes.
/// </summary>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void StopProcesses()
{
// take the invoker of the error state to generate the message and / or popup window
switch (_invokerProcessState)
{
default:
_processState = ProcessState.Idle;
break;
}
}
/// <summary>
/// Common message window.
/// </summary>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - Forcing message box being modal and on top.
/// </remarks>
/// <remarks date="2023-Nov-14" author="Thomas Wiedebusch">
/// - Return user selection.
/// </remarks>
private static DialogResult MessageBoxShow(String text, String caption,
MessageBoxButtons buttons = MessageBoxButtons.OK,
MessageBoxIcon icon = MessageBoxIcon.Asterisk)
{
return MessageBox.Show(text, caption, buttons, icon, MessageBoxDefaultButton.Button1,
MessageBoxOptions.ServiceNotification);
}
/// <summary>
/// Output exclusively to user update remarks text window.
/// </summary>
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
/// - Color added.
/// </remarks>
private void LogText(String txtHistory)
{
InfoWindowColoredText(txtHistory, ColorDefault);
_logger.Info(txtHistory);
}
/// <summary>
/// Output exclusively to user update remarks text window.
/// </summary>
private void LogErrorText(String txtHistory)
{
InfoWindowColoredText(txtHistory, ColorProcessFailed);
_logger.Info(txtHistory);
}
/// <summary>
/// Output exclusively to user update remarks text window.
/// </summary>
private void LogWarningText(String txtHistory)
{
InfoWindowColoredText(txtHistory, ColorProcessWarning);
_logger.Info(txtHistory);
}
/// <summary>
/// Output exclusively to user update remarks text window.
/// </summary>
private void LogSuccessText(String txtHistory)
{
InfoWindowColoredText(txtHistory, ColorSuccess);
_logger.Info(txtHistory);
}
/// <summary>
/// Output exclusively to user update remarks text window.
/// </summary>
private void InfoWindowColoredText(String txtHistory, Color color)
{
Invoke(new Action(() =>
{
rtbReport.SuspendLayout();
rtbReport.SelectionStart = rtbReport.Text.Length;
rtbReport.SelectionLength = 0;
rtbReport.SelectionColor = color;
rtbReport.AppendText($"{txtHistory}{Environment.NewLine}");
rtbReport.SelectionColor = rtbReport.ForeColor;
rtbReport.ScrollToCaret();
rtbReport.ResumeLayout();
}));
}
/// <summary>
/// Process bar - NOT invoked, just call from Form main thread.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Avoid repeated execution on invisible progress bar.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Thread safe implementation to avoid crash.
/// </remarks>
private void SetStatusProgressBar(Int32 value = 0, Boolean visible = true)
{
if (ProgressBarStatus == null || (!ProgressBarStatus.Visible && !visible))
return;
try
{
if (ProgressBarStatus == null)
return;
if (value > 100)
value = 100;
if (value < 0)
value = 0;
ProgressBarStatus.Visible = visible;
ProgressBarStatus.Value = value;
}
catch (Exception)
{
// ignored
}
}
/// <summary>
/// Common routine to hide the data picker and uninstall all events.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Check user input.
/// </remarks>
private void HideDatePicker()
{
_datePicker.Visible = false;
_datePicker.CloseUp -= datePickerFwUpdateValidationDate_CloseUp;
_datePicker.CloseUp -= datePickerUserValidationDate_CloseUp;
// display the label
lblFwUpdateDutyDate.Visible = true;
CheckDataCollectionStatus();
}
/// <summary>
/// Common routine to disable all controls.
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - Added new buttons.
/// </remarks>
private void DisableAllControlsInvoked()
{
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
UiInvoker.ControlEnableInvoker(btnBuildFwUpdateSafe, false);
UiInvoker.ControlEnableInvoker(btnUploadSafeToDb, false);
UiInvoker.ControlEnableInvoker(btnLicenseFwUpdateSw, false);
UiInvoker.ControlEnableInvoker(btnLicenseFwUpdateBuilder, false);
UiInvoker.ControlEnableInvoker(lblFwUpdateDutyDate, false);
UiInvoker.ControlEnableInvoker(tbxOrderNumber, false);
UiInvoker.ControlEnableInvoker(tbxOrderPosition, false);
UiInvoker.ControlEnableInvoker(tabControlSelection, false);
}
/// <summary>
/// Common routine to disable all controls except the language setting.
/// This should be used, if user is not licensed.
/// </summary>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void DisableControlsExceptLanguageInvoked()
{
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
UiInvoker.ControlEnableInvoker(btnBuildFwUpdateSafe, false);
UiInvoker.ControlEnableInvoker(btnUploadSafeToDb, false);
UiInvoker.ControlEnableInvoker(btnLicenseFwUpdateSw, false);
UiInvoker.ControlEnableInvoker(btnLicenseFwUpdateBuilder, false);
UiInvoker.ControlEnableInvoker(lblFwUpdateDutyDate, false);
UiInvoker.ControlEnableInvoker(tbxOrderNumber, false);
UiInvoker.ControlEnableInvoker(tbxOrderPosition, false);
UiInvoker.ControlEnableInvoker(tabControlSelection, false);
}
/// <summary>
/// Common routine to enable inputs.
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Avoid activation on unchecked license.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void EnableInputsInvoked()
{
if (_licenseUnchecked || _regUser == null || !_regUser.AccountActive)
return;
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
UiInvoker.ControlEnableInvoker(lblFwUpdateDutyDate, true);
UiInvoker.ControlEnableInvoker(tbxOrderNumber, true);
UiInvoker.ControlEnableInvoker(tbxOrderPosition, true);
UiInvoker.ControlEnableInvoker(tabControlSelection, true);
}
/// <summary>
/// Remove search items on FW-package pre-selection and deselect FW-package.
/// </summary>
/// <remarks date="2023-Mar-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void DeselectFwPackageAndSearchMask()
{
// clear all search items for FW-Package as all cordonels are removed
_radioSearchItems.Clear();
_regionSearchItems.Clear();
_metrologySearchItems.Clear();
_coreSearchItems.Clear();
_sizeSearchItems.Clear();
// Clear FW-package selection
if (_fwUpdateDbAccess?.DbCordonelFwPackagesInfo != null)
{
foreach (var fw in _fwUpdateDbAccess.DbCordonelFwPackagesInfo)
fw.IsSelected = false;
_fwUpdatePackage = null;
}
}
#endregion --------------------------------------- Tools ------------------------------------------------------
#region ------------------------------------------ Threads and Tasks ------------------------------------------
/// <summary>
/// Upload FW-Update Safe to DB Task.
/// </summary>
/// <remarks date="2021-Apr-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void UploadFwUpdateSafeTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_enableFwSafeUpload = !UploadFwUpdateSafeToDb();
}).ContinueWith(delegate
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
if (!_enableFwSafeUpload)
{
var msg = $"{Resources.StrFwUpdateSafeUploadSuccessfull} \"{_fwUpdateSafeName}\"";
LogText(StrSeparator);
LogSuccessText(msg);
LogText(StrSeparator);
MessageBoxShow($"{Resources.StrFwUpdateSafeUploadSuccessfull}\n\n \"{_fwUpdateSafeName}\"",
Resources.StrSuccess);
_processState = ProcessState.Idle;
}
else
{
var msg = $"{Resources.StrFwUpdateSafeUploadFailed}";
LogText(StrSeparator);
LogErrorText(msg);
LogText(StrSeparator);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
_processState = ProcessState.Error;
}
EnableInputsInvoked();
});
}
/// <summary>
/// Start FW-Update Safe Builder Task.
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
/// <remarks date="2023-Nov-14" author="Thomas Wiedebusch">
/// - Modified message box to upload FW update Safe to DB.
/// </remarks>
/// <remarks date="2023-Nov-24" author="Thomas Wiedebusch">
/// - After message box state change.
/// </remarks>
private void BuildFwUpdateSafeTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_enableFwSafeUpload = BuildFwUpdateSafe();
}).ContinueWith(delegate
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
if (_enableFwSafeUpload)
{
var msg = $"{Resources.StrFwUpdateSafeBuiltSuccessfully} \"{_fwUpdateSafeName}\"";
LogText(StrSeparator);
LogSuccessText(msg);
LogText(StrSeparator);
var messageBoxButtonReturn = MessageBoxShow(
$"{Resources.StrFwUpdateSafeBuiltSuccessfully}\n\n \"{_fwUpdateSafeName}\"\n\n" +
$"{Resources.StrRequestUploadFwUpdateSafeToDb}",
Resources.StrActionRequest, MessageBoxButtons.YesNo);
// The btnUploadSafeToDb_Click is going to change the state to UploadFwUpdateSafeToDb
if (messageBoxButtonReturn == DialogResult.Yes)
btnUploadSafeToDb_Click(this, null);
else
_processState = ProcessState.Idle;
}
else
{
var msg = $"{Resources.StrFwUpdateSafeBuiltFailed}";
LogText(StrSeparator);
LogErrorText(msg);
LogText(StrSeparator);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
_processState = ProcessState.Error;
}
EnableInputsInvoked();
});
}
/// <summary>
/// Establish the DB connection.
/// </summary>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - dbAccessLocked introduced.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
private void EstablishDbConnectionTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire all FW-Update packages.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - dbAccessLocked introduced.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
private void LoadDbFwUpdateOperatorsTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetAllUpdateOperatorsFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_fwUpdateOperatorsPropertyChanged = true;
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Check the software license.
/// </summary>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void CheckLicenseTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetLicenseFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
if (_regUser.AccountActive)
{
_licenseValidDateTimeOffset = DateTimeServer.GetDateTimeTodayUntilMidnight();
_processState = ProcessState.Idle;
}
else
_processState = ProcessState.DbAccessLocked;
});
}
/// <summary>
/// Acquire all FW packages infos from DB.
/// </summary>
/// <remarks date="2022-Nov-30" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void LoadDbCordonelFwPackagesInfosTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
// load the packages information
GetCordonelFwPackagesInfosFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_fwPackagePropertyChanged = true;
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire one report file from DB and display it.
/// </summary>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void LoadDbReportAndDisplayTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetAndDisplaySingleReportFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire all report files from DB.
/// </summary>
/// <remarks date="2021-Oct-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void LoadDbReportsTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetReportsFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_fwReportsPropertyChanged = true;
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire all FW-Update safes from DB.
/// </summary>
/// <remarks date="2021-Oct-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void LoadDbFwUpdateSafesInfosTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetFwUpdateSafesInfosFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_fwUpdateSafesPropertyChanged = true;
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire all Customers specified by search mask from DB.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - dbAccessLocked introduced.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
private void LoadDbCordonelCustomersTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetAllCordonelCustomersFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_customersPropertyChanged = true;
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire all Cordonel serial numbers of a specific production number.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - dbAccessLocked introduced.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
private void LoadDbCordonelSerialNumbersTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetAllCordonelSerialNumbersOfOrderFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_cordonelsPropertyChanged = true;
_processState = ProcessState.Idle;
});
}
/// <summary>
/// Acquire all customer specific production orders from DB.
/// </summary>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Error process state introduced.
/// </remarks>
private void LoadDbCordonelProductionOrdersTask()
{
_invokerProcessState = _processState;
Task.Factory.StartNew(() =>
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
try
{
// check DB connection in advance to overcome the timing issues for DB service startup
_fwUpdateDbAccess.CheckDbConnection();
GetAllCordonelOrdersOfCustomerFromDb();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}).ContinueWith(delegate
{
_cordonelsPropertyChanged = true;
_processState = ProcessState.Idle;
});
}
#endregion --------------------------------------- Threads and Tasks ------------------------------------------
#region ------------------------------------------ Data Grid FW Packages --------------------------------------
/// <summary>
/// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for FW-Update
/// packages selection. Multiple packages can be selected!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-10" author="Thomas Wiedebusch">
/// - Search column index starting with 0.
/// </remarks>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - User search from Cordonel FW-Packages as this contains lot more selected information than the clustered
/// file.
/// </remarks>
/// <remarks date="2022-Apr-12" author="Thomas Wiedebusch">
/// - Simply one unique FW-package allowed to select,
/// - Reset status of pre-selected Cordonels.
/// </remarks>
/// <remarks date="2022-Dec-01" author="Thomas Wiedebusch">
/// - FW package information from DB used.
/// </remarks>
private void gridViewFwPackagesSelection_CellClick(Object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex < 0 || e.RowIndex < 0)
{
return;
}
// the e.RowIndex is referenced to the (sorted) data grid view
var fwPackage = new CordonelFirmware
{
FwPackageInfo = new CordonelFwPackageInfo()
};
// use the fw update package id for selection
for (var columnIndex = 0; columnIndex < gridViewFwPackageSelection.ColumnCount; columnIndex++)
{
if (gridViewFwPackageSelection.Columns[columnIndex].Name == Resources.StrTableFileId)
fwPackage.FwPackageInfo.Id =
(Int32)gridViewFwPackageSelection.Rows[e.RowIndex].Cells[columnIndex].Value;
}
// select FW package
if (gridViewFwPackageSelection.Columns[e.ColumnIndex].Name == Resources.StrTableSelect)
{
// clear FW-packages
_fwUpdatePackage = null;
// reset status of Cordonels being able to auto-assign new FW-package
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
cordonel.IsRemoved = false;
cordonel.IsApproved = false;
cordonel.RequiredReleaseNameVersion = null;
}
// reset status of Cordonels being able to auto-assign new FW-package
foreach (var cordonel in _cordonelUpdateList)
{
cordonel.RequiredFwRelease = null;
}
// Search the FW package Id in data table, select if found
if (_fwUpdateDbAccess?.DbCordonelFwPackagesInfo == null)
return;
foreach (var fw in _fwUpdateDbAccess.DbCordonelFwPackagesInfo)
{
if (fw.Id == fwPackage.FwPackageInfo.Id)
{
// toggle selection and set information on main screen
if (fw.IsSelected)
{
// remove FW package from list
fw.IsSelected = false;
}
else
{
// assign FW package
_fwUpdatePackage = fwPackage;
// add new FW-package to all Cordonels in update list
foreach (var cordonel in _cordonelUpdateList)
{
cordonel.RequiredFwRelease = fw.Name;
}
fw.IsSelected = true;
}
}
else
{
fw.IsSelected = false;
}
//search FW package in data table for update
for (var idx = 0; idx < _dataTableFwPackages.Rows.Count; idx++)
{
var dataRow = _dataTableFwPackages.Rows[idx];
if (fw.Id == (Int32)dataRow[Resources.StrTableFileId])
{
dataRow[Resources.StrTableSelect] = fw.IsSelected;
}
}
} // Cordonel search list files
}
_fwPackagePropertyChanged = false;
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Build data grid for FW-Update packages
/// </summary>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - User search from Cordonel FW-Packages as this contains lot more selected information than the clustered
/// file.
/// </remarks>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Search extended to metrology.
/// </remarks>
/// <remarks date="2022-Dec-01" author="Thomas Wiedebusch">
/// - FW package information from DB used.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
/// <remarks date="2025-Oct-01" author="Thomas Wiedebusch">
/// - Avoid unreleased versions.
/// </remarks>
private void FillDataGridWithFwPackageInfos()
{
grpLanguageSelection.Enabled = false;
_dataTableFwPackages?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableFwPackages = new DataTable();
_dataTableFwPackages.Columns.Add(Resources.StrTableSelect, typeof(Boolean));
_dataTableFwPackages.Columns.Add(Resources.StrTableCordonelFwToInstall, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableCordonelFwIsReleased, typeof(Boolean));
_dataTableFwPackages.Columns.Add(Resources.StrTableRegion, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableRadioFrequency, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableCordonelMetrology, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableFwPackageCoreMin, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableFwPackageCoreMax, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableCordonelDiameter, typeof(String));
_dataTableFwPackages.Columns.Add(Resources.StrTableReleaseDate, typeof(DateTime));
//add the fw update package id to simplify selection
_dataTableFwPackages.Columns.Add(Resources.StrTableFileId, typeof(Int32));
try
{
if (_fwUpdateDbAccess?.DbCordonelFwPackagesInfo == null)
return;
foreach (var fw in _fwUpdateDbAccess.DbCordonelFwPackagesInfo)
{
// pre-selection of displayed FW packages
var requiredCoreVersion = 0;
if (cbxFwPackageCoreMak.Text != Constants.StrWildcard)
{
var version = cbxFwPackageCoreMak.Text;
version = version.Replace(".", "");
Int32.TryParse(version, out requiredCoreVersion);
}
var requiredMetrologyVersion = 0;
if (cbxFwPackageMetrologyMask.Text != Constants.StrWildcard)
{
var version = cbxFwPackageMetrologyMask.Text;
version = version.Replace(".", "");
Int32.TryParse(version, out requiredMetrologyVersion);
}
if ((fw.Region != cbxFwPackageRegionMask.Text && cbxFwPackageRegionMask.Text != Constants.StrWildcard) ||
(fw.RadioFrequencyMhz.ToString() != cbxFwPackageRadioMask.Text && cbxFwPackageRadioMask.Text != Constants.StrWildcard) ||
(fw.MeterSize != cbxFwPackageSizeMask.Text && cbxFwPackageSizeMask.Text != Constants.StrWildcard) ||
(((fw.CoreRevisionMin != null && fw.CoreRevisionMin > requiredCoreVersion) ||
(fw.CoreRevisionMax != null && requiredCoreVersion > fw.CoreRevisionMax)) &&
cbxFwPackageCoreMak.Text != Constants.StrWildcard) ||
(fw.MetrologyVersion != requiredMetrologyVersion &&
cbxFwPackageMetrologyMask.Text != Constants.StrWildcard) ||
// Avoid unreleased versions
!fw.IsReleased)
continue;
var row = _dataTableFwPackages.NewRow();
row[Resources.StrTableSelect] = fw.IsSelected;
row[Resources.StrTableCordonelFwToInstall] = fw.Name;
row[Resources.StrTableCordonelFwIsReleased] = fw.IsReleased;
row[Resources.StrTableRegion] = fw.Region ?? Constants.StrWildcard;
row[Resources.StrTableRadioFrequency] = fw.RadioFrequencyMhz == null ? Constants.StrWildcard : $@"{fw.RadioFrequencyMhz}";
row[Resources.StrTableCordonelMetrology] = GenesisMeter.BuildFwVersionStringFromDec(fw.MetrologyVersion);
row[Resources.StrTableFwPackageCoreMin] = GenesisMeter.BuildFwVersionStringFromDec(fw.CoreRevisionMin);
row[Resources.StrTableFwPackageCoreMax] = GenesisMeter.BuildFwVersionStringFromDec(fw.CoreRevisionMax);
row[Resources.StrTableCordonelDiameter] = fw.MeterSize ?? Constants.StrWildcard;
if (fw.ReleaseDate != null)
{
row[Resources.StrTableReleaseDate] = fw.ReleaseDate;
}
row[Resources.StrTableFileId] = fw.Id;
_dataTableFwPackages.Rows.Add(row);
}
//output data to data grid view
gridViewFwPackageSelection.DataSource = _dataTableFwPackages.DefaultView;
foreach (DataGridViewColumn column in gridViewFwPackageSelection.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
if (gridViewFwPackageSelection.Columns[Resources.StrTableCordonelFwVersion] != null)
gridViewFwPackageSelection.Sort(gridViewFwPackageSelection.Columns[Resources.StrTableCordonelFwVersion],
ListSortDirection.Descending);
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
#endregion --------------------------------------- Data Grid FW Packages ---------------------------------------
#region ------------------------------------------ Data Grid Update Safes -------------------------------------
/// <summary>
/// Build data grid for FW-Update update safes
/// </summary>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2022-Jun-01" author="Thomas Wiedebusch">
/// - Removed coloring of safes depending bon valid date as it slows down the program dramatically.
/// </remarks>
/// <remarks date="2022-Jun-30" author="Thomas Wiedebusch">
/// - CHanged date time field to string with yyyy-MM-dd to make it sortable.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void FillDataGridWithFwUpdateSafesInfos()
{
grpLanguageSelection.Enabled = false;
_dataTableUpdateSafes?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableUpdateSafes = new DataTable();
// _dataTableUpdateSafes.Columns.Add(Resources.StrTableUserValidDate, typeof(DateTime));
_dataTableUpdateSafes.Columns.Add(Resources.StrTableUserValidDate, typeof(String));
_dataTableUpdateSafes.Columns.Add(Resources.StrTableCordonelPcbId, typeof(String));
_dataTableUpdateSafes.Columns.Add(Resources.StrTableCordonelUpdateOrder, typeof(Int64));
_dataTableUpdateSafes.Columns.Add(Resources.StrTableCordonelProductionPos, typeof(Int64));
_dataTableUpdateSafes.Columns.Add(Resources.StrTableCustomerName, typeof(String));
_dataTableUpdateSafes.Columns.Add(Resources.StrTableUserFullName, typeof(String));
try
{
foreach (var safeInfo in _fwUpdateSafesInfos)
{
// create one line per PcbId
foreach (var pcbId in safeInfo.PcbIds)
{
try
{
var row = _dataTableUpdateSafes.NewRow();
//row[Resources.StrTableUserValidDate] = safeInfo.ValidDate.DateTime;
row[Resources.StrTableUserValidDate] =
DateTimeServer.GetSortableDateStringFromDateTime(safeInfo.ValidDate.DateTime,
_cultureInfo);
row[Resources.StrTableCordonelPcbId] = pcbId;
// extract customer name , order and position from file name
var info = safeInfo.Name.Split(FwUpdateConfig.FwUpdateFileFieldSeparator);
var replace = info[1].Replace(FwUpdateConfig.FwUpdateFileExtension, "");
var orderPos = replace.Split(FwUpdateConfig.FwUpdateFileOrderPosSeparator);
row[Resources.StrTableCordonelUpdateOrder] = orderPos[0];
row[Resources.StrTableCordonelProductionPos] = orderPos[1];
row[Resources.StrTableCustomerName] = info[0];
var userName = Constants.StrUnknown;
foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators.Where(user =>
user.Id == safeInfo.UserId))
{
userName = user.FullName;
break;
}
row[Resources.StrTableUserFullName] = userName;
_dataTableUpdateSafes.Rows.Add(row);
}
catch (Exception)
{
// ignored
}
}
}
gridViewFwUpdateSafeSelection.DataSource = _dataTableUpdateSafes;
foreach (DataGridViewColumn column in gridViewFwUpdateSafeSelection.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
//TODO THW FwUpdateSafesInfosStyleSet();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
#endregion --------------------------------------- Data Grid Update Safes --------------------------------------
#region ------------------------------------------ Data Grid Report Files -------------------------------------
/// <summary>
/// Overwrite cell click, because edit of cells is denied (read only == true).
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2022-Apr-22" author="Thomas Wiedebusch">
/// - Use file ID for reference of report as multiple reports might be generated at the same day.
/// </remarks>
private void gridViewFwUpdateReportSelection_CellClick(Object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex < 0 || e.RowIndex < 0 || _dataTableReportFiles == null)
{
return;
}
for (var columnIndex = 0; columnIndex < gridViewFwUpdateReportSelection.ColumnCount; columnIndex++)
{
if (gridViewFwUpdateReportSelection.Columns[columnIndex].Name == Resources.StrTableFileId)
_selectedReport.FileId =
Convert.ToInt32(gridViewFwUpdateReportSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
if (gridViewFwUpdateReportSelection.Columns[columnIndex].Name == Resources.StrTableUpdateOperatorId)
_selectedReport.UserId =
Convert.ToInt32(gridViewFwUpdateReportSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
if (gridViewFwUpdateReportSelection.Columns[columnIndex].Name == Resources.StrTableDate)
_selectedReport.Date = Convert.ToDateTime(
gridViewFwUpdateReportSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
if (gridViewFwUpdateReportSelection.Columns[columnIndex].Name == Resources.StrTableCordonelPcbId)
_selectedReport.PcbId =
Convert.ToInt64(gridViewFwUpdateReportSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
if (gridViewFwUpdateReportSelection.Columns[columnIndex].Name ==
Resources.StrTableCordonelUpdateOrder)
_selectedReport.OrderNr =
Convert.ToInt64(gridViewFwUpdateReportSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
if (gridViewFwUpdateReportSelection.Columns[columnIndex].Name ==
Resources.StrTableCordonelProductionPos)
_selectedReport.OrderPos =
Convert.ToInt64(gridViewFwUpdateReportSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
}
DisableAllControlsInvoked();
UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingReports);
SetStatusProgressBar();
_dbAccessDelayCtrMs = 0;
_processState = ProcessState.GetAndDisplaySingleReportFromDb;
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Build data grid for FW-Update report files
/// </summary>
/// <remarks date="2021-Oct-30" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2022-Apr-22" author="Thomas Wiedebusch">
/// - Use file ID for reference of report as multiple reports might be generated at the same day.
/// </remarks>
/// <remarks date="2022-Jun-30" author="Thomas Wiedebusch">
/// - CHanged date time field to string with yyyy-MM-dd to make it sortable.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void FillDataGridWithReportsInfos()
{
grpLanguageSelection.Enabled = false;
_dataTableReportFiles?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableReportFiles = new DataTable();
//_dataTableReportFiles.Columns.Add(Resources.StrTableDate, typeof(DateTime));
_dataTableReportFiles.Columns.Add(Resources.StrTableDate, typeof(String));
_dataTableReportFiles.Columns.Add(Resources.StrTableCordonelPcbId, typeof(String));
_dataTableReportFiles.Columns.Add(Resources.StrTableCordonelUpdateOrder, typeof(Int64));
_dataTableReportFiles.Columns.Add(Resources.StrTableCordonelProductionPos, typeof(Int64));
_dataTableReportFiles.Columns.Add(Resources.StrTableUserFullName, typeof(String));
_dataTableReportFiles.Columns.Add(Resources.StrTableUpdateOperatorId, typeof(Int32));
_dataTableReportFiles.Columns.Add(Resources.StrTableFileId, typeof(Int32));
try
{
foreach (var report in _fwUpdateReports)
{
var row = _dataTableReportFiles.NewRow();
//row[Resources.StrTableDate] = report.Date.Date;
row[Resources.StrTableDate] =
DateTimeServer.GetSortableDateStringFromDateTime(report.Date.Date, _cultureInfo);
row[Resources.StrTableCordonelPcbId] = report.PcbId;
row[Resources.StrTableCordonelUpdateOrder] = report.OrderNr;
row[Resources.StrTableCordonelProductionPos] = report.OrderPos;
var userName = Constants.StrUnknown;
foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators.Where(user => user.Id == report.UserId))
{
userName = user.FullName;
break;
}
row[Resources.StrTableUserFullName] = userName;
row[Resources.StrTableUpdateOperatorId] = report.UserId;
row[Resources.StrTableFileId] = report.FileId;
_dataTableReportFiles.Rows.Add(row);
}
gridViewFwUpdateReportSelection.DataSource = _dataTableReportFiles;
foreach (DataGridViewColumn column in gridViewFwUpdateReportSelection.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
#endregion --------------------------------------- Data Grid Report Files --------------------------------------
#region ------------------------------------------ Data Grid Pre Build Summary --------------------------------
/// <summary>
/// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for
/// Pre build summary selection. Multiple Cordonels can be selected!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-17" author="Thomas Wiedebusch">
/// - Production pre select infos from production search infos.
/// </remarks>
/// <remarks date="2022-Apr-12" author="Thomas Wiedebusch">
/// - Removed FW-package combo box as FW-package is one unique one and can only be exchanged in the FW-package
/// selection tab.
/// </remarks>
/// <remarks date="2022-Apr-13" author="Thomas Wiedebusch">
/// - Removed IsApproved or IsRemoved if selection de-selected.
/// </remarks>
/// <remarks date="2023-Oct-08" author="Thomas Wiedebusch">
/// - Selector for register restore.
/// </remarks>
/// <remarks date="2024-May-08" author="Thomas Wiedebusch">
/// - Add all known infos of Cordonel on selection.
/// </remarks>
private void gridViewPreBuildSummary_CellClick(Object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex < 0 || e.RowIndex < 0 || _dataTableSummary == null)
{
return;
}
// the e.RowIndex is referenced to the (sorted) data grid view
var cordonelDeviceInfo = new CordonelDeviceInfo();
for (var columnIndex = 0; columnIndex < gridViewPreBuildSummary.ColumnCount;
columnIndex++)
{
if (gridViewPreBuildSummary.Columns[columnIndex].Name == Resources.StrTableCordonelPcbId)
cordonelDeviceInfo.PcbId =
gridViewPreBuildSummary.Rows[e.RowIndex].Cells[columnIndex].Value.ToString();
// add here the required release
if (gridViewPreBuildSummary.Columns[columnIndex].Name == Resources.StrTableCordonelFwToInstall)
cordonelDeviceInfo.RequiredFwRelease =
gridViewPreBuildSummary.Rows[e.RowIndex].Cells[columnIndex].Value.ToString();
}
// Select Cordonel
if (gridViewPreBuildSummary.Columns[e.ColumnIndex].Name == Resources.StrTableApprove)
{
// Search the cordonel in the production pre selected list
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
if (cordonel.PcbId == cordonelDeviceInfo.PcbId)
{
// toggle selection and set information on main screen
cordonel.IsApproved = !cordonel.IsApproved;
cordonel.IsRemoved = !cordonel.IsApproved;
//search cordonel in data table for update of "select" checkbox
for (var idx = 0; idx < _dataTableSummary.Rows.Count; idx++)
{
var dataRow = _dataTableSummary.Rows[idx];
if (cordonel.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId])
continue;
dataRow[Resources.StrTableApprove] = cordonel.IsApproved;
dataRow[Resources.StrTableRemove] = cordonel.IsRemoved;
break;
}
break;
}// Cordonel found
}// Search the cordonel in the production list
}// Approve
// Remove Cordonel from safe
if (gridViewPreBuildSummary.Columns[e.ColumnIndex].Name == Resources.StrTableRemove)
{
// Search the cordonel in the production list pre selected
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
if (cordonel.PcbId == cordonelDeviceInfo.PcbId)
{
// toggle selection and set information on main screen
cordonel.IsRemoved = !cordonel.IsRemoved;
cordonel.IsApproved = !cordonel.IsRemoved;
//search cordonel in data table for update of "select" checkbox
for (var idx = 0; idx < _dataTableSummary.Rows.Count; idx++)
{
var dataRow = _dataTableSummary.Rows[idx];
if (cordonel.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId])
continue;
dataRow[Resources.StrTableApprove] = cordonel.IsApproved;
dataRow[Resources.StrTableRemove] = cordonel.IsRemoved;
break;
}
break;
}// Cordonel found
}// Search the cordonel in the production list
}// Remove
// Select Cordonel register restore
if (gridViewPreBuildSummary.Columns[e.ColumnIndex].Name == Resources.StrTableRegisterRestoreSelect)
{
// Search the cordonel in the production list pre selected
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
if (cordonel.PcbId == cordonelDeviceInfo.PcbId)
{
// toggle selection and set information on main screen
cordonel.RegisterRecoveryRequired = !cordonel.RegisterRecoveryRequired;
//search cordonel in data table for update of "select" checkbox
for (var idx = 0; idx < _dataTableSummary.Rows.Count; idx++)
{
var dataRow = _dataTableSummary.Rows[idx];
if (cordonel.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId])
continue;
dataRow[Resources.StrTableRegisterRestoreSelect] = cordonel.RegisterRecoveryRequired;
break;
}
//_cordonelsPropertyChanged = true;
break;
}// Cordonel found
}// Search the cordonel in the production list
}// Restore registers
// Select Cordonel
if (gridViewPreBuildSummary.Columns[e.ColumnIndex].Name == Resources.StrTableSelect)
{
// Search the cordonel in the production pre selected list
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
if (cordonel.PcbId == cordonelDeviceInfo.PcbId)
{
// toggle selection and set information on main screen
if (cordonel.IsSelected)
{
// remove cordonel from list
cordonel.IsSelected = false;
cordonel.IsRemoved = false;
cordonel.IsApproved = false;
cordonel.RegisterRecoveryRequired = false;
}
else
{
cordonel.IsSelected = true;
}
break;
}// Cordonel found
}// Search the cordonel in the production list
}// Select
// Summarize selections amd update contents
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
if (cordonel.PcbId == cordonelDeviceInfo.PcbId)
{
// toggle selection and set information on main screen
if (cordonel.IsSelected)
{
cordonelDeviceInfo.RegisterRecoveryRequired = cordonel.RegisterRecoveryRequired;
cordonelDeviceInfo.CustomerSerialNumber = cordonel.CustomerSerialNumber;
cordonelDeviceInfo.InstalledFwVersion = cordonel.InstalledFwVersion;
// remove cordonel from list
foreach (var x in _cordonelUpdateList.Where(x => x.PcbId == cordonel.PcbId))
{
_cordonelUpdateList.Remove(x);
break;
}
// add cordonel to list
_cordonelUpdateList.Add(cordonelDeviceInfo);
}
else
{
// remove cordonel from list
foreach (var x in _cordonelUpdateList.Where(x => x.PcbId == cordonel.PcbId))
{
_cordonelUpdateList.Remove(x);
break;
}
}
//search cordonel in data table for update of "select" checkbox
for (var idx = 0; idx < _dataTableSummary.Rows.Count; idx++)
{
var dataRow = _dataTableSummary.Rows[idx];
if (cordonel.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId])
continue;
dataRow[Resources.StrTableSelect] = cordonel.IsSelected;
dataRow[Resources.StrTableApprove] = cordonel.IsApproved;
dataRow[Resources.StrTableRemove] = cordonel.IsRemoved;
dataRow[Resources.StrTableRegisterRestoreSelect] = cordonel.RegisterRecoveryRequired;
break;
}
break;
}// Cordonel found
}// Search the cordonel in the production list
_cordonelsPropertyChanged = true;
PreBuildSummaryInformationStyleSet();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Build data grid for pre build summary
/// </summary>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-17" author="Thomas Wiedebusch">
/// - Production pre select infos from production search infos.
/// </remarks>
/// <remarks date="2021-Apr-29" author="Thomas Wiedebusch">
/// - Fill search masks of FW packages for core revision, diameter, radio, region and metrology based
/// on selected Cordonels
/// </remarks>
/// <remarks date="2022-Apr-12" author="Thomas Wiedebusch">
/// - Pre-select FW package,
/// - Unique FW-package support cannot be exchanged here anymore.
/// </remarks>
/// <remarks date="2022-Apr-13" author="Thomas Wiedebusch">
/// - Moved masks from PreBuildSummery tab to Cordonel selection tab.
/// </remarks>
/// <remarks date="2023-Oct-08" author="Thomas Wiedebusch">
/// - Selector for register restore.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void FillDataGridWithPreBuildSummaryInfos()
{
grpLanguageSelection.Enabled = false;
_processState = ProcessState.UpdateDataTables;
_dataTableSummary?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableSummary = new DataTable();
_dataTableSummary.Columns.Add(Resources.StrTableSelect, typeof(Boolean));
_dataTableSummary.Columns.Add(Resources.StrTableApprove, typeof(Boolean));
_dataTableSummary.Columns.Add(Resources.StrTableRemove, typeof(Boolean));
_dataTableSummary.Columns.Add(Resources.StrTableRegisterRestoreSelect, typeof(Boolean));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelCustomerSerialNumber, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelPcbId, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelFwToInstall, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelMetrology, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelMetrologyIsUpdateable, typeof(Boolean));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelCore, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableRegion, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableRadioFrequency, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableCordonelDiameter, typeof(String));
_dataTableSummary.Columns.Add(Resources.StrTableReason, typeof(String));
gridViewPreBuildSummary.DataSource = _dataTableSummary;
gridViewPreBuildSummary.Visible = true;
try
{
// remove all Cordonels from update, this lis will be re-generated
_cordonelUpdateList.Clear();
var dataGridTableRow = 0;
foreach (var cordonel in _cordonelProductionPreSelectInfos)
{
var row = _dataTableSummary.NewRow();
// keep FW-package if already assigned, if NOT, check if the unique package matches the requirements
// this step has to be executed before filling the SELECT information, because it changes the content
// to preset the SELECT based on the initial FW-package to Cordonel requirement comparison.
if (!cordonel.IsRemoved && !cordonel.IsApproved)
row[Resources.StrTableCordonelFwToInstall] = PreSelectFwPackages(cordonel);
else
row[Resources.StrTableCordonelFwToInstall] = cordonel.RequiredReleaseNameVersion ?? Constants.StrUnknown;
row[Resources.StrTableSelect] = cordonel.IsSelected;
row[Resources.StrTableApprove] = cordonel.IsApproved;
row[Resources.StrTableRemove] = cordonel.IsRemoved;
row[Resources.StrTableRegisterRestoreSelect] = cordonel.RegisterRecoveryRequired;
row[Resources.StrTableCordonelCustomerSerialNumber] = cordonel.CustomerSerialNumber ?? Constants.StrUnknown;
row[Resources.StrTableCordonelPcbId] = cordonel.PcbId ?? Constants.StrUnknown;
row[Resources.StrTableCordonelMetrology] = cordonel.Metrology ?? Constants.StrUnknown;
row[Resources.StrTableCordonelMetrologyIsUpdateable] = cordonel.MetrologyIsUpdateable;
row[Resources.StrTableCordonelCore] = cordonel.CoreRevision ?? Constants.StrUnknown;
row[Resources.StrTableRegion] = cordonel.Region ?? Constants.StrUnknown;
row[Resources.StrTableRadioFrequency] = cordonel.RadioFrequency ?? Constants.StrUnknown;
row[Resources.StrTableCordonelDiameter] = cordonel.Diameter ?? Constants.StrUnknown;
row[Resources.StrTableReason] = cordonel.ReasonForUpdateDenial ?? "";
_dataTableSummary.Rows.Add(row);
// color the results of the compare
PreBuildSummaryInformationStyleSet(gridViewPreBuildSummary.Rows[dataGridTableRow]);
dataGridTableRow++;
// add approved cordonels to upgrade list, remove the not approved ones
if (cordonel.IsSelected && _cordonelUpdateList.All(x => x.PcbId != cordonel.PcbId))
{
var cordonelDeviceInfo = new CordonelDeviceInfo
{
PcbId = cordonel.PcbId,
CustomerSerialNumber = cordonel.CustomerSerialNumber,
RegisterRecoveryRequired = cordonel.RegisterRecoveryRequired,
RequiredFwRelease = cordonel.RequiredReleaseNameVersion,
InstalledFwVersion = cordonel.InstalledFwVersion
};
_cordonelUpdateList.Add(cordonelDeviceInfo);
}
tabControlSelection.Update();
}
foreach (DataGridViewColumn column in gridViewPreBuildSummary.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
_processState = ProcessState.Idle;
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
/// <summary>
/// Color the rows depending on the state
/// </summary>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void PreBuildSummaryInformationStyleSet(DataGridViewRow dataGridRow)
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
// check if selected
if ((Boolean)dataGridRow.Cells[Resources.StrTableSelect].Value)
{
// approved by software or user
var requiredRelease = (String)dataGridRow.Cells[Resources.StrTableCordonelFwToInstall].Value;
if ((Boolean)dataGridRow.Cells[Resources.StrTableApprove].Value &&
!string.IsNullOrEmpty(requiredRelease) && requiredRelease != Constants.StrUnknown)
{
dataGridRow.DefaultCellStyle = _styleInstallationApproved;
}
// not approved and not rejected - user has to decide
else if (!(Boolean)dataGridRow.Cells[Resources.StrTableRemove].Value)
{
dataGridRow.DefaultCellStyle = _styleInstallationUserApproval;
}
// not approved but rejected
else
{
dataGridRow.DefaultCellStyle = _styleInstallationDenied;
}
}
else
{
dataGridRow.DefaultCellStyle = _styleInstallationRemoved;
}
}
/// <summary>
/// Color the rows depending on the state
/// </summary>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void PreBuildSummaryInformationStyleSet()
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
foreach (DataGridViewRow dataGridRow in gridViewPreBuildSummary.Rows)
{
// check if selected
if ((Boolean)dataGridRow.Cells[Resources.StrTableSelect].Value)
{
// approved by software or user
var requiredRelease = (String)dataGridRow.Cells[Resources.StrTableCordonelFwToInstall].Value;
if ((Boolean)dataGridRow.Cells[Resources.StrTableApprove].Value &&
!string.IsNullOrEmpty(requiredRelease) && requiredRelease != Constants.StrUnknown)
{
dataGridRow.DefaultCellStyle = _styleInstallationApproved;
}
// not approved and not rejected - user has to decide
else if (!(Boolean)dataGridRow.Cells[Resources.StrTableRemove].Value)
{
dataGridRow.DefaultCellStyle = _styleInstallationUserApproval;
}
// not approved but rejected
else
{
dataGridRow.DefaultCellStyle = _styleInstallationDenied;
}
}
else
{
dataGridRow.DefaultCellStyle = _styleInstallationRemoved;
}
}
}
/// <summary>
/// After sort event to update styles
/// </summary>
/// <remarks date="2021-Apr-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void gridViewPreBuildSummary_Sorted(Object sender, EventArgs e)
{
PreBuildSummaryInformationStyleSet();
}
#endregion --------------------------------------- Data Grid Pre Build Summary --------------------------------
#region ------------------------------------------ Data Grid Cordonels ----------------------------------------
/// <summary>
/// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for
/// Cordonel selection. Multiple Cordonels can be selected!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Idx search index started with 0 instead of 1.
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - PCB Id may not always be unambiguously (Sensus GmbH Hannover PCB Id 19080071),
/// - Improved search speed.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - Check data table assignment.
/// </remarks>
/// <remarks date="2021-Apr-10" author="Thomas Wiedebusch">
/// - Added FW release picker,
/// - Search column index starting with 0.
/// </remarks>
/// <remarks date="2021-Apr-17" author="Thomas Wiedebusch">
/// - Production pre select infos from production search infos.
/// </remarks>
/// <remarks date="2022-Apr-12" author="Thomas Wiedebusch">
/// - Removed FW-Package selection from Cordonel selection tab.
/// </remarks>
/// <remarks date="2022-Apr-13" author="Thomas Wiedebusch">
/// - Moved masks from PreBuildSummery tab to Cordonel selection tab,
/// - Removed trigger to update PreBuildSummery.
/// </remarks>
/// <remarks date="2023-Feb-21" author="Thomas Wiedebusch">
/// - Search items for FW-Package selection forced to simply have one core version, one meter size,
/// one region and one metrology version as only identical Cordonels should be placed to one safe
/// according to the one FW-Package policy.
/// </remarks>
/// <remarks date="2023-Mar-03" author="Thomas Wiedebusch">
/// - Preselect order number and position to last production number to simplify update order generation,
/// this will cause the update order-pos = production order-pos..
/// </remarks>
/// <remarks date="2023-Mar-17" author="Thomas Wiedebusch">
/// - Clear all search items for FW-package selection and deselect the FW-package if cordonel count is 0.
/// </remarks>
/// <remarks date="2023-Oct-08" author="Thomas Wiedebusch">
/// - Selector for register restore.
/// </remarks>
/// <remarks date="2025-Dec-08" author="Thomas Wiedebusch">
/// - Avoid multiple identical search items.
/// </remarks>
private void gridViewCordonelsSelection_CellClick(Object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex < 0 || e.RowIndex < 0 || _dataTableCordonels == null)
{
return;
}
// the e.RowIndex is referenced to the (sorted) data grid view
var cordSelect = new CordonelProductionInfosDb();
for (var columnIndex = 0; columnIndex < gridViewCordonelSelection.ColumnCount;
columnIndex++)
{
if (gridViewCordonelSelection.Columns[columnIndex].Name == Resources.StrTableCordonelPcbId)
cordSelect.PcbId =
gridViewCordonelSelection.Rows[e.RowIndex].Cells[columnIndex].Value.ToString();
}
// select factory reset
if (gridViewCordonelSelection.Columns[e.ColumnIndex].Name == Resources.StrTableRegisterRestoreSelect)
{
// Search the cordonel in the production search list
foreach (var cordSearch in _cordonelProductionSearchInfos)
{
if (cordSearch.PcbId == cordSelect.PcbId)
{
// toggle register restore but only for selected devices
if (cordSearch.IsSelected)
{
cordSearch.RegisterRecoveryRequired = !cordSearch.RegisterRecoveryRequired;
}
//search cordonel in data table for update of "select" and "RegisterRestore" checkbox
for (var idx = 0; idx < _dataTableCordonels.Rows.Count; idx++)
{
var dataRow = _dataTableCordonels.Rows[idx];
if (cordSearch.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId])
continue;
dataRow[Resources.StrTableRegisterRestoreSelect] = cordSearch.RegisterRecoveryRequired;
break;
}
break;
}
}
}
// select Cordonel
if (gridViewCordonelSelection.Columns[e.ColumnIndex].Name == Resources.StrTableSelect)
{
// Search the cordonel in the production search list
foreach (var cordSearch in _cordonelProductionSearchInfos)
{
if (cordSearch.PcbId == cordSelect.PcbId)
{
// toggle selection and set information on main screen
if (cordSearch.IsSelected)
{
// remove cordonel and register restore from list
cordSearch.IsSelected = false;
cordSearch.RegisterRecoveryRequired = false;
foreach (var x in _cordonelProductionPreSelectInfos.Where(x =>
x.PcbId == cordSelect.PcbId))
{
_cordonelProductionPreSelectInfos.Remove(x);
break;
}
}
else
{
// add cordonel to list
cordSearch.IsSelected = true;
cordSearch.IsApproved = false;
cordSearch.IsRemoved = false;
// remove it first being able to update all information
foreach (var x in _cordonelProductionPreSelectInfos.Where(x =>
x.PcbId == cordSelect.PcbId))
{
_cordonelProductionPreSelectInfos.Remove(x);
break;
}
_cordonelProductionPreSelectInfos.Add(cordSearch);
// fill text box for update order
if (cordSearch.CustomerOrderNumber > 0)
{
tbxOrderNumber.Text = cordSearch.CustomerOrderNumber.ToString();
}
// fill text box for update position
if (cordSearch.CustomerOrderPos > 0)
{
tbxOrderPosition.Text = cordSearch.CustomerOrderPos.ToString();
}
// fill search masks for FW package selection
if (!string.IsNullOrEmpty(cordSearch.Diameter) &&
cordSearch.Diameter != Constants.StrUnknown &&
_sizeSearchItems.All(X => X != cordSearch.Diameter))
{
_sizeSearchItems.Add(cordSearch.Diameter);
}
// fill search masks for FW package selection
if (!string.IsNullOrEmpty(cordSearch.RadioFrequency) &&
cordSearch.RadioFrequency != Constants.StrUnknown &&
_radioSearchItems.All(x => x != cordSearch.RadioFrequency))
{
_radioSearchItems.Add(cordSearch.RadioFrequency);
}
// fill search masks for FW package selection
if (!string.IsNullOrEmpty(cordSearch.Region) &&
cordSearch.Region != Constants.StrUnknown &&
_regionSearchItems.All(x => x != cordSearch.Region))
{
_regionSearchItems.Add(cordSearch.Region);
}
// fill search masks for FW package selection
if (!string.IsNullOrEmpty(cordSearch.CoreRevision) &&
cordSearch.CoreRevision != Constants.StrUnknown &&
_coreSearchItems.All(x => x != cordSearch.CoreRevision))
{
_coreSearchItems.Add(cordSearch.CoreRevision);
}
// fill search masks for FW package selection
if (!string.IsNullOrEmpty(cordSearch.Metrology) &&
cordSearch.Metrology != Constants.StrUnknown &&
_metrologySearchItems.All(x => x != cordSearch.Metrology))
{
_metrologySearchItems.Add(cordSearch.Metrology);
}
}
//search cordonel in data table for update of "select" and "RegisterRestore" checkbox
for (var idx = 0; idx < _dataTableCordonels.Rows.Count; idx++)
{
var dataRow = _dataTableCordonels.Rows[idx];
if (cordSearch.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId])
continue;
dataRow[Resources.StrTableSelect] = cordSearch.IsSelected;
dataRow[Resources.StrTableRegisterRestoreSelect] = cordSearch.RegisterRecoveryRequired;
break;
}
break;
}// Cordonel found
}// Search the cordonel in the production search list
}
if (_cordonelProductionPreSelectInfos == null || _cordonelProductionPreSelectInfos.Count == 0)
{
DeselectFwPackageAndSearchMask();
}
_cordonelsPropertyChanged = false;
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Overwrite header cell click to to toggle all Cordonel selections
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2025-Dec-09" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void gridViewCordonelSelection_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
try
{
var cordSearchCtr = 0;
// Toggle all factory resets for every Cordonel
if (gridViewCordonelSelection.Columns[e.ColumnIndex].Name == Resources.StrTableRegisterRestoreSelect ||
gridViewCordonelSelection.Columns[e.ColumnIndex].Name == Resources.StrTableSelect)
{
// Search the cordonel in the production search list
foreach (var cordSearch in _cordonelProductionSearchInfos)
{
var cellEventArgs = new DataGridViewCellEventArgs(e.ColumnIndex, cordSearchCtr);
gridViewCordonelsSelection_CellClick(this, cellEventArgs);
cordSearchCtr++;
Update();
}
}
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Build data grid for cordonels
/// </summary>
/// <remarks date="2021-Feb-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Order position added.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - New data tables, important for language change.
/// </remarks>
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
/// - Added region and radio frequency.
/// </remarks>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Added customer serial number search.
/// </remarks>
/// <remarks date="2022-Apr-12" author="Thomas Wiedebusch">
/// - Removed FW-Package selection from Cordonel selection tab.
/// </remarks>
/// <remarks date="2023-Oct-08" author="Thomas Wiedebusch">
/// - Selector for register restore.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Avoid sorting of 'selection' and 'factory reset' columns.
/// </remarks>
private void FillDataGridWithCordonelInfos()
{
grpLanguageSelection.Enabled = false;
_dataTableCordonels?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableCordonels = new DataTable();
_dataTableCordonels.Columns.Add(Resources.StrTableSelect, typeof(Boolean));
_dataTableCordonels.Columns.Add(Resources.StrTableRegisterRestoreSelect, typeof(Boolean));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelCustomerSerialNumber, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelPcbId, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelInstalledFw, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelMetrology, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelMetrologyIsUpdateable, typeof(Boolean));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelCore, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableRegion, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableRadioFrequency, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelDiameter, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelProductionOrder, typeof(Int64));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelProductionPos, typeof(Int64));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelSize, typeof(String));
_dataTableCordonels.Columns.Add(Resources.StrTableCordonelCatalogueNumber, typeof(String));
try
{
foreach (var cordSearch in _cordonelProductionSearchInfos)
{
// pre-selection of displayed production order
if (!cordSearch.CustomerSerialNumber.Contains(tbxCordonelProductionOrdersSearch.Text)
&& tbxCordonelProductionOrdersSearch.Text != Constants.StrWildcard)
continue;
// search if this Cordonel is already in production pre select list as this is the marker
foreach (var cordPre in _cordonelProductionPreSelectInfos.Where(x => x.PcbId == cordSearch.PcbId))
{
cordSearch.IsSelected = cordPre.IsSelected;
cordSearch.IsApproved = cordPre.IsApproved;
cordSearch.IsRemoved = cordPre.IsRemoved;
cordSearch.RegisterRecoveryRequired = cordPre.RegisterRecoveryRequired;
break;
}
var row = _dataTableCordonels.NewRow();
row[Resources.StrTableSelect] = cordSearch.IsSelected;
row[Resources.StrTableRegisterRestoreSelect] = cordSearch.RegisterRecoveryRequired;
row[Resources.StrTableCordonelCustomerSerialNumber] = cordSearch.CustomerSerialNumber ??
Constants.StrUnknown;
row[Resources.StrTableCordonelPcbId] = cordSearch.PcbId ?? Constants.StrUnknown;
row[Resources.StrTableCordonelInstalledFw] = cordSearch.InstalledReleaseNameVersion ?? Constants.StrUnknown;
row[Resources.StrTableCordonelMetrology] = cordSearch.Metrology ?? Constants.StrUnknown;
row[Resources.StrTableCordonelMetrologyIsUpdateable] = cordSearch.MetrologyIsUpdateable;
row[Resources.StrTableCordonelCore] = cordSearch.CoreRevision ?? Constants.StrUnknown;
row[Resources.StrTableRegion] = cordSearch.Region ?? Constants.StrUnknown;
row[Resources.StrTableRadioFrequency] = cordSearch.RadioFrequency ?? Constants.StrUnknown;
row[Resources.StrTableCordonelDiameter] = cordSearch.Diameter ?? Constants.StrUnknown;
row[Resources.StrTableCordonelProductionOrder] = cordSearch.CustomerOrderNumber;
row[Resources.StrTableCordonelProductionPos] = cordSearch.CustomerOrderPos;
row[Resources.StrTableCordonelSize] = cordSearch.Length ?? Constants.StrUnknown;
row[Resources.StrTableCordonelCatalogueNumber] = cordSearch.CatalogNumber ?? Constants.StrUnknown;
_dataTableCordonels.Rows.Add(row);
}
gridViewCordonelSelection.DataSource = _dataTableCordonels;
foreach (DataGridViewColumn column in gridViewCordonelSelection.Columns)
{
if (column.Name != Resources.StrTableRegisterRestoreSelect &&
column.Name != Resources.StrTableSelect)
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
#endregion --------------------------------------- Data Grid Cordonels ----------------------------------------
#region ------------------------------------------ Data Grid Controls Update Operators ------------------------
/// <summary>
/// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for update user
/// selection. Exclusively one user can be selected.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Feb-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-10" author="Thomas Wiedebusch">
/// - Check for e.RowIndex below 0.
/// </remarks>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Removed validation date for user as it should not be set by the FwUpdateBuilder operator,
/// - Avoid selection of outdated users.
/// </remarks>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Embedded in try catch block,
/// - used data grid as cell reference to allow data grid sorting without refresh of data source!
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Idx search index started with 0 instead of 1.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - Check data table assignment.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Removed _userInfo.
/// </remarks>
/// <remarks date="2021-Apr-10" author="Thomas Wiedebusch">
/// - Search column index starting with 0.
/// </remarks>
/// <remarks date="2021-Apr-27" author="Thomas Wiedebusch">
/// - Added _updateOperatorId needed to assign the fwUpdateSafeDb.
/// </remarks>
private void gridViewFwUpdateOperators_CellClick(Object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex < 0 || e.RowIndex < 0 || _dataTableUpdateOperators == null)
{
return;
}
// the e.RowIndex is referenced to the (sorted) data grid view
var updateOperatorName = "";
var validDate = "";
for (var columnIndex = 0; columnIndex < gridViewUpdateOperators.ColumnCount; columnIndex++)
{
if (gridViewUpdateOperators.Columns[columnIndex].Name == Resources.StrTableUserFullName)
updateOperatorName = gridViewUpdateOperators.Rows[e.RowIndex].Cells[columnIndex].Value.ToString();
if (gridViewUpdateOperators.Columns[columnIndex].Name == Resources.StrTableUserValidDate)
validDate = gridViewUpdateOperators.Rows[e.RowIndex].Cells[columnIndex].Value.ToString();
}
// select update operator
if (gridViewUpdateOperators.Columns[e.ColumnIndex].Name == Resources.StrTableSelect)
{
// Search the customer name in data table, select if found, set all others to false
foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators)
{
if (user.FullName == updateOperatorName)
{
// avoid selection of outdated users
if (DateTimeOffset.Compare(DateTimeServer.GetDateTimeOffsetFromDateString(validDate),
DateTimeOffset.Now) < 1 || user.IsSelected || !user.AccountActive)
{
lblUpdateOperator.Text = "";
_updateOperatorId = 0;
user.IsSelected = false;
}
else
{
lblUpdateOperator.Text = user.FullName;
_updateOperatorId = user.Id;
user.IsSelected = true;
}
}
else
user.IsSelected = false;
//search user in data table for update
for (var idx = 0; idx < _dataTableUpdateOperators.Rows.Count; idx++)
{
var dataRow = _dataTableUpdateOperators.Rows[idx];
if (user.FullName == (String)dataRow[Resources.StrTableUserFullName])
{
dataRow[Resources.StrTableSelect] = user.IsSelected;
}
}
}
}
_fwUpdateOperatorsPropertyChanged = false;
// select new validation date
//if (gridViewUsers.Columns[e.ColumnIndex].Name == Resources.StrTableUserValidDate)
//{
// // date time picker
// if (_userInfo != null)
// {
// if (_datePicker.Visible)
// {
// HideDatePicker();
// }
// // Adding DateTimePicker control into DataGridView
// gridViewUsers.Controls.Add(_datePicker);
// // Rectangular area that represents the display area for a cell
// var location = gridViewUsers.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
// // Setting Location and size to fit within the cell
// _datePicker.Height = location.Height;
// _datePicker.Width = location.Width;
// _datePicker.Location = new Point(location.X, location.Y);
// // The final selection of the date
// _datePicker.CloseUp += datePickerUserValidationDate_CloseUp;
// // Now make it visible
// _datePicker.Visible = true;
// }
//}
//// select activation of account
//if (gridViewUsers.Columns[e.ColumnIndex].Name == Resources.StrTableUserActive)
//{
// // toggle selection
// dataRow[Resources.StrTableUserActive] = (Boolean)dataRow[Resources.StrTableUserActive] != true;
// if (_userInfo != null) _userInfo.AccountActive = (Boolean)dataRow[Resources.StrTableUserActive];
// _userAccountPropertyChanged = true;
//}
// color the results of the compare
UpdateOperatorsInformationStyleSet();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Build data grid for all fully qualified users
/// </summary>
/// <remarks date="2021-Feb-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Disable language change during update.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - New data tables, important for language change.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Added PC name.
/// </remarks>
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
/// - Registration and valid date to date time.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void FillDataGridWithUpdateOperatorInfos()
{
grpLanguageSelection.Enabled = false;
_dataTableUpdateOperators?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableUpdateOperators = new DataTable();
_dataTableUpdateOperators.Columns.Add(Resources.StrTableSelect, typeof(Boolean));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserFullName, typeof(String));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserDomain, typeof(String));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserLogInName, typeof(String));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserPcName, typeof(String));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserRegDate, typeof(DateTime));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserValidDate, typeof(DateTime));
_dataTableUpdateOperators.Columns.Add(Resources.StrTableUserActive, typeof(Boolean));
try
{
foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators)
{
var row = _dataTableUpdateOperators.NewRow();
//application information read from configuration
row[Resources.StrTableSelect] = user.IsSelected;
row[Resources.StrTableUserFullName] = user.FullName;
row[Resources.StrTableUserDomain] = user.Domain;
row[Resources.StrTableUserLogInName] = user.LogInName;
row[Resources.StrTableUserPcName] = CryptInformation.GetPcNameFromHwId(user.HardwareId);
row[Resources.StrTableUserRegDate] = user.RegisterDate.Date;
row[Resources.StrTableUserValidDate] = user.ValidDate.Date;
row[Resources.StrTableUserActive] = user.AccountActive;
_dataTableUpdateOperators.Rows.Add(row);
}
//output data to data grid view
gridViewUpdateOperators.DataSource = _dataTableUpdateOperators.DefaultView;
foreach (DataGridViewColumn column in gridViewUpdateOperators.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
// color the results of the compare
UpdateOperatorsInformationStyleSet();
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
/// <summary>
/// Color the rows depending on the state
/// </summary>
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void UpdateOperatorsInformationStyleSet()
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
foreach (DataGridViewRow dataGridRow in gridViewUpdateOperators.Rows)
{
if ((Boolean)dataGridRow.Cells[Resources.StrTableUserActive].Value &&
DateTimeServer.GetDateTimeOffsetFromDateString(
dataGridRow.Cells[Resources.StrTableUserValidDate].Value.ToString()) >= DateTimeOffset.Now)
{
dataGridRow.DefaultCellStyle = _styleValid;
}
else
{
dataGridRow.DefaultCellStyle = _styleInvalid;
}
}
}
/// <summary>
/// After sort event to update styles
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void gridViewUpdateOperators_Sorted(Object sender, EventArgs e)
{
UpdateOperatorsInformationStyleSet();
}
#endregion --------------------------------------- Data Grid Controls Update Operators ------------------------
#region ------------------------------------------ Data Grid Controls Customer --------------------------------
/// <summary>
/// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for customer
/// selection. Exclusively one customer can be selected.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Idx search index started with 0 instead of 1.
/// - Clear list of cordonels on new customer.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - clear production order numbers.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - Check data table assignment.
/// </remarks>
/// <remarks date="2021-Apr-10" author="Thomas Wiedebusch">
/// - Search column index starting with 0.
/// </remarks>
/// <remarks date="2021-Apr-17" author="Thomas Wiedebusch">
/// - Production pre select infos from production search infos.
/// </remarks>
/// <remarks date="2023-Mar-17" author="Thomas Wiedebusch">
/// - Clear all search items for FW-package selection and deselect the FW-package as on customer change
/// everything needs to be renewed.
/// </remarks>
private void gridViewCustomerSelection_CellClick(Object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex < 0 || e.RowIndex < 0 || _dataTableCustomers == null)
{
return;
}
// remove FW-package on customer change
DeselectFwPackageAndSearchMask();
// the e.RowIndex is referenced to the (sorted) data grid view
var customerNumber = 0L;
for (var columnIndex = 0; columnIndex < gridViewCustomerSelection.ColumnCount; columnIndex++)
{
if (gridViewCustomerSelection.Columns[columnIndex].Name != Resources.StrTableCustomerNumber)
continue;
customerNumber = Convert.ToInt64(gridViewCustomerSelection.Rows[e.RowIndex].Cells[columnIndex].Value);
break;
}
// select customer
if (gridViewCustomerSelection.Columns[e.ColumnIndex].Name == Resources.StrTableSelect)
{
// clear all previously selected Cordonels as a new customer is selected or deselected
_dataTableCordonels?.Rows.Clear();
_dataTableCordonels?.Columns.Clear();
_fwUpdateCapability.Clear();
_cordonelUpdateList.Clear();
_cordonelProductionSearchInfos.Clear();
_cordonelProductionPreSelectInfos.Clear();
_customerNumberBeforeLastCordonelDbSearch = "";
// Search the customer name in data table, select if found, set all others to false
foreach (var customer in _fwUpdateDbAccess.DbSearchedCordonelCustomers)
{
if (customer.CustomerNumber == customerNumber)
{
// toggle selection and set information on main screen
if (customer.IsSelected)
{
tbxCustomerName.Text = "";
lblCustomerNumber.Text = "";
customer.IsSelected = false;
}
else
{
tbxCustomerName.Text = customer.CustomerName;
tbxCustomerName.Focus();
tbxCustomerName.SelectionStart = 0;
tbxCustomerName.SelectionLength = 0;
lblCustomerNumber.Text = customer.CustomerNumber.ToString();
customer.IsSelected = true;
}
}
else
customer.IsSelected = false;
//search user in data table for update
for (var idx = 0; idx < _dataTableCustomers.Rows.Count; idx++)
{
var dataRow = _dataTableCustomers.Rows[idx];
if (customer.CustomerNumber == (Int64)dataRow[Resources.StrTableCustomerNumber])
{
dataRow[Resources.StrTableSelect] = customer.IsSelected;
}
}
}
}
_customersPropertyChanged = false;
}
catch (Exception)
{
_processState = ProcessState.Error;
}
}
/// <summary>
/// Build data grid for selected customers.
/// </summary>
/// <remarks date="2021-Feb-24" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - New data tables, important for language change.
/// </remarks>
/// <remarks date="2021-Jun-08" author="Thomas Wiedebusch">
/// - Initially sorted by name.
/// </remarks>
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
/// - Disabled language selection, culture info fixed to english.
/// </remarks>
private void FillDataGridWithCustomerInfos()
{
grpLanguageSelection.Enabled = false;
_dataTableCustomers?.Dispose();
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
_dataTableCustomers = new DataTable();
_dataTableCustomers.Columns.Add(Resources.StrTableSelect, typeof(Boolean));
_dataTableCustomers.Columns.Add(Resources.StrTableCustomerName, typeof(String));
_dataTableCustomers.Columns.Add(Resources.StrTableCustomerNumber, typeof(Int64));
_dataTableCustomers.Columns.Add(Resources.StrTableCustomerLocation, typeof(String));
try
{
foreach (var customer in _fwUpdateDbAccess.DbSearchedCordonelCustomers)
{
var row = _dataTableCustomers.NewRow();
//application information read from configuration
row[Resources.StrTableSelect] = customer.IsSelected;
row[Resources.StrTableCustomerName] = customer.CustomerName;
row[Resources.StrTableCustomerNumber] = customer.CustomerNumber;
row[Resources.StrTableCustomerLocation] = customer.CustomerLocation;
_dataTableCustomers.Rows.Add(row);
}
//output data to data grid view
gridViewCustomerSelection.DataSource = _dataTableCustomers.DefaultView;
foreach (DataGridViewColumn column in gridViewCustomerSelection.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
var initialSortColumn = gridViewCustomerSelection.Columns[Resources.StrTableCustomerName];
if (initialSortColumn != null)
gridViewCustomerSelection.Sort(initialSortColumn, ListSortDirection.Ascending);
}
catch (Exception)
{
_processState = ProcessState.Error;
}
finally
{
grpLanguageSelection.Enabled = false;
}
}
#endregion --------------------------------------- Data Grid Controls Customer --------------------------------
#region ------------------------------------------ Load DB Contents -------------------------------------------
/// <summary>
/// Get the Cordonel customer orders from DB.
/// </summary>
/// <returns>true if successful</returns>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-09" author="Thomas Wiedebusch">
/// - Getting orders directly from DB.
/// </remarks>
private void GetAllCordonelOrdersOfCustomerFromDb()
{
// get the customer ID
if (_fwUpdateDbAccess == null || string.IsNullOrEmpty(lblCustomerNumber.Text))
return;
if (Int64.TryParse(lblCustomerNumber.Text, out var customerId))
{
_fwUpdateDbAccess.GetAllCordonelCustomerOrdersFromDb(customerId);
}
_processState = ProcessState.Idle;
}
/// <summary>
/// Get the Cordonel device information from DB.
/// </summary>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - App versions added.
/// </remarks>
/// <remarks date="2021-Apr-09" author="Thomas Wiedebusch">
/// - Size and length applied to DN50, US2, 2" and 220 mm.
/// </remarks>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Added radio frequency.
/// </remarks>
/// <remarks date="2021-Apr-17" author="Thomas Wiedebusch">
/// - Clear search info on new search.
/// </remarks>
/// <remarks date="2024-Apr-30" author="Thomas Wiedebusch">
/// - order.Length == null taken into account.
/// </remarks>
/// <remarks date="2024-Aug-05" author="Thomas Wiedebusch">
/// - Display correct "USx" meter size from newly added order.CordonelMeterSizeId,
/// </remarks>
private void GetAllCordonelSerialNumbersOfOrderFromDb()
{
// get the customer ID
if (_fwUpdateDbAccess == null || string.IsNullOrEmpty(lblCustomerNumber.Text))
return;
// remove last search infos
_cordonelProductionSearchInfos.Clear();
if (_fwUpdateDbAccess.DbCordonelCustomerOrders.Count > 0)
{
// Take the pre-processed CustomerOrderSearch-list deviated from CustomerOrder-list
foreach (var order in _cordonelCustomerOrdersSearch)
{
if (_fwUpdateDbAccess.GetAllCordonelSerialNumbersFromDb(order.CustomerOrderNumber,
order.CustomerOrderPos) && _fwUpdateDbAccess.DbCordonelSerialNumbers.Count > 0)
{
foreach (var cordonelSerialNumber in _fwUpdateDbAccess.DbCordonelSerialNumbers)
{
// copy all contents to the cordonel device information
var cordonel = new CordonelProductionInfosDb
{
CatalogNumber = order.CatalogNumber,
CustomerOrderNumber = order.CustomerOrderNumber,
CustomerOrderPos = order.CustomerOrderPos,
Diameter = MeterSizeConverter.ConvertMeterSizeEnumToSizeName((MeterSize?)order.CordonelMeterSizeId),
Length = order.Length == null
? "?"
: (order.Length < 150 ? $"{order.Length}\"" : $"{order.Length} mm"),
PcbId = cordonelSerialNumber.PcbId.ToString(),
CustomerSerialNumber = cordonelSerialNumber.CustomerSerialNumber,
SensusSerialNumber = cordonelSerialNumber.SerialNumber.ToString()
};
// read for each Cordonel the AppListVersion
if (_fwUpdateDbAccess.DownloadCordonelAppVersionsFromDb(cordonel.PcbId, out var cordonelAppVersions))
{
foreach (var appVersion in cordonelAppVersions)
{
if (appVersion.Id == -1)
cordonel.CoreRevision = appVersion.Version;
if (appVersion.Id == -2)
cordonel.RadioFrequency = appVersion.Version;
if (appVersion.Id == 0x0F) //GENESISFLOW
{
cordonel.Metrology = appVersion.Version;
cordonel.MetrologyIsUpdateable = appVersion.IsUpdateable;
}
if (appVersion.Id == 0x18) //FLEXNETVERSION
{
cordonel.InstalledReleaseNameVersion =
GenesisMeter.BuildFlexnetFwVersion(out var version, appVersion.Version);
cordonel.InstalledFwVersion = (Int32?)version;
}
}
}
cordonel.Region = string.IsNullOrEmpty(cordonel.RadioFrequency) || cordonel.RadioFrequency == "0" ? "NA" : "EMEA";
_cordonelProductionSearchInfos.Add(cordonel);
}
}
}
}// orders from customer could be acquired
_processState = ProcessState.Idle;
}
/// <summary>
/// Get the Cordonel customers from DB.
/// </summary>
/// <remarks date="2021-Mar-31" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Ape-07" author="Thomas Wiedebusch">
/// - Search pattern added.
/// </remarks>
private void GetAllCordonelCustomersFromDb()
{
var searchPattern = tbxCustomerSearchMask.Text.Replace(" ", "");
_fwUpdateDbAccess?.GetAllCordonelCustomersFromDb(searchPattern);
_processState = ProcessState.Idle;
}
/// <summary>
/// Get Cordonel FW packages information from DB end extract all needed information to select a package to the
/// CordonelFwPackageInfo search list.
/// </summary>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-29" author="Thomas Wiedebusch">
/// - Meter FW update ruler.
/// </remarks>
/// <remarks date="2021-Oct-15" author="Thomas Wiedebusch">
/// - Configuration files loading from DB: - MeterFwUpdateRuler, configuration.json and MeterEraseRestore.
/// </remarks>
/// <remarks date="2022-Apr-27" author="Thomas Wiedebusch">
/// - Starting with FW 1.2.* all frequencies will be supported, removed frequency check,
/// - Supported China as new region.
/// </remarks>
/// <remarks date="2022-Dec-01" author="Thomas Wiedebusch">
/// - Removed FUpdateRuler, all data are going to be taken from DB,
/// - file name no longer used to identify capability of release, all information from DB.
/// </remarks>
private void GetCordonelFwPackagesInfosFromDb()
{
if (_fwUpdateDbAccess == null)
return;
_fwUpdateDbAccess.GetAllCordonelFwPackagesInfoFromDb();
}
/// <summary>
/// Get the license information from DB. In DEBUG mode the license of the software will be skipped,
/// but user license is of importance.
/// </summary>
/// <remarks date="2021-Apr-14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void GetLicenseFromDb()
{
// FW-Update Builder validation with DB access
var access = false;
var msg = $"{Resources.StrStartMessageSwLicenseExpired}\n\n{_versionString}";
try
{
// returns true in DEBUG configuration
access = Xylem.Common.Logic.SoftwareAccessHelper.Access.HasAccess(_assemblyName);
}
catch (Exception)
{
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
}
if (!access)
{
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
Close();
}
_licenseUnchecked = false;
CheckUserRegistration();
}
/// <summary>
/// Upload the generated FW Update safe to DB.
/// </summary>
/// <returns>true if successful</returns>
/// <remarks date="2021-Apr-27" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private Boolean UploadFwUpdateSafeToDb()
{
var pcbIds = _cordonelUpdateList.Select(pcb => pcb.PcbId).ToList();
return _fwUpdateDbAccess != null && _fwUpdateDbAccess.UploadFwUpdateSafeToDb(_fwUpdateSafeDb, pcbIds);
}
/// <summary>
/// Get the content of one selected FW update report from DB.
/// </summary>
/// <param name="reportDb">report filled with content</param>
/// <param name="pcbId"></param>
/// <param name="userId"></param>
/// <param name="fileId"></param>
/// <param name="orderNo"></param>
/// <param name="position"></param>
/// <param name="dateTime"></param>
/// <returns>true if text is set with valid content</returns>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2022-Apr-22" author="Thomas Wiedebusch">
/// - File ID added to get the correct file even if multiple files have been reported at the same day (retries).
/// </remarks>
private Boolean GetOneSelectedReportFromDb(out FwUpdateReportDb reportDb, Int64 pcbId, Int32 userId, Int32 fileId,
Int64 orderNo, Int64 position, DateTimeOffset dateTime)
{
reportDb = null;
// The DB access will load a list of all reports assigned to the input.
if (_fwUpdateDbAccess == null ||
!_fwUpdateDbAccess.DownloadFwUpdateReportsFromDb(out var fwUpdateReports, pcbId, userId, orderNo))
{
return false;
}
//The order number has to be extracted and the date has to match!
foreach (var report in fwUpdateReports.Where(report => report.Date == dateTime &&
report.OrderPos == position &&
report.FileId == fileId))
{
reportDb = report;
break;
}
return reportDb != null;
}
/// <summary>
/// Get one selected FW update report from DB and display it.
/// </summary>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void GetAndDisplaySingleReportFromDb()
{
//open rich text view with report details
if (_frmFwUpdateReport == null)
return;
Invoke(new Action(() =>
{
_frmFwUpdateReport.rtbHistory?.Clear();
_frmFwUpdateReport.Show();
}));
if (!GetOneSelectedReportFromDb(out var reportDb, _selectedReport.PcbId, _selectedReport.UserId,
_selectedReport.FileId, _selectedReport.OrderNr, _selectedReport.OrderPos,
_selectedReport.Date))
return;
//open rich text view with report details
Invoke(new Action(() =>
{
if (_frmFwUpdateReport.rtbHistory != null)
{
_frmFwUpdateReport.ApplyLabels(reportDb, _cultureInfo);
_frmFwUpdateReport.rtbHistory.AppendText(reportDb.Content);
var fwIsUpToDate = false;
foreach (var searchString in FwUpdateConfig.FirmwareUpToDateSearchStrings)
{
if (reportDb.Content.Contains(searchString))
fwIsUpToDate = true;
}
_frmFwUpdateReport.rtbHistory.ForeColor = fwIsUpToDate ? ColorSuccess : ColorProcessFailed;
_frmFwUpdateReport.rtbHistory.ScrollToCaret();
}
}));
}
/// <summary>
/// Get all FW update report files from DB. Loading only the preview (readContent = false as default) to speed up
/// the access, as the content will only be needed on inspection of the report.
/// </summary>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void GetReportsFromDb()
{
if (_fwUpdateDbAccess == null)
return;
_fwUpdateReports.Clear();
// if the users are not acquired, get these first
if (_fwUpdateDbAccess.DbFullQualifiedUpdateOperators == null ||
_fwUpdateDbAccess.DbFullQualifiedUpdateOperators.Count == 0)
{
GetAllUpdateOperatorsFromDb();
}
if (_fwUpdateDbAccess.DbFullQualifiedUpdateOperators == null)
return;
foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators)
{
var fwUpdateReportsOfUser = new List<FwUpdateReportDb>();
_fwUpdateDbAccess?.DownloadFwUpdateReportsFromDb(out fwUpdateReportsOfUser, userId: user.Id);
_fwUpdateReports.AddRange(fwUpdateReportsOfUser);
}
}
/// <summary>
/// Get all active FW update safes from DB. Outdated safes cannot be accessed.
/// </summary>
/// <remarks date="2021-Oct-29" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void GetFwUpdateSafesInfosFromDb()
{
if (_fwUpdateDbAccess == null)
return;
_fwUpdateSafesInfos.Clear();
// if the users are not acquired, get these first
if (_fwUpdateDbAccess.DbFullQualifiedUpdateOperators == null ||
_fwUpdateDbAccess.DbFullQualifiedUpdateOperators.Count == 0)
{
GetAllUpdateOperatorsFromDb();
}
if (_fwUpdateDbAccess.DbFullQualifiedUpdateOperators == null)
return;
foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators)
{
_fwUpdateDbAccess.ListAllFwUpdateSafesOfUserFromDb(user.Id, out var fwUpdateSafesOfUser);
foreach (var safe in fwUpdateSafesOfUser)
{
var pcbIdsOfSafe = _fwUpdateDbAccess.GetFwUpdateSafePcbIdsFromDb(safe.ContainerId);
var fwUpdateSafe = safe;
fwUpdateSafe.PcbIds = pcbIdsOfSafe;
_fwUpdateSafesInfos.Add(fwUpdateSafe);
}
}
}
/// <summary>
/// Get the full qualified user information from DB to generate the primary key for encryption.
/// </summary>
/// <remarks date="2021-Feb-04" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Feb-11" author="Thomas Wiedebusch">
/// - DB access.
/// </remarks>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Call of get all users from DB.
/// </remarks>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Removed DB status.
/// </remarks>
private void GetAllUpdateOperatorsFromDb()
{
_fwUpdateDbAccess?.GetAllUpdateOperatorsFromDb();
}
/// <summary>
/// Get the user information from DB to generate the primary key for encryption.
/// For this operation only fully qualified users can be used as the primary key needs as input the
/// HW Id, the domain, the LogInName and the users password hash.
/// The user registration has to be done in advance in the FW-Update Loader!
/// </summary>
/// <remarks date="2021-Feb-04" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Feb-11" author="Thomas Wiedebusch">
/// - Get fully qualified user information from DB searched by the users FullName.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Replaced userInfo by name of update operator.
/// </remarks>
private void BuildPrimaryKey(String updateOperator)
{
if (_fwUpdateDbAccess == null ||
!_fwUpdateDbAccess.GetFullQualifiedUserByFullName(updateOperator, out var userInfo))
{
return;
}
_primaryKey = FwUpdateCrypt.BuildPrimaryKey(userInfo.HardwareId, userInfo.Domain,
userInfo.LogInName, userInfo.PasswordHash);
}
/// <summary>
/// Skip if recovery registers are not needed, else collect all recovery registers from DB.
/// - To validate the register range, the configuration.json and the list of applications
/// intended to be installed are needed. Therefore, the <see cref="GetFwPackageFromDb"/>
/// and <see cref="BuildFwUpdateSwContainerFromDb"/> have to be executed to collect the
/// information.
/// - Uses a mockGenesis to extract valid registers based on application versions from
/// the configuration.json.
/// </summary>
/// <param name="cordonel"></param>
/// <returns>true if successfully loaded or not needed</returns>
/// <remarks date="2023-Oct-09" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2024-Apr-04" author="Thomas Wiedebusch">
/// - Added register values with installed or intended to be installed FW to be able to check
/// limits of registers based on the installed applications and the configuration.json.
/// </remarks>
/// <remarks date="2024-Apr-08" author="Thomas Wiedebusch">
/// - Split downloading of recovery registers and validate range of registers to two separated
/// functions.
/// </remarks>
private Boolean GetRecoveryRegistersFromDb(CordonelDeviceInfo cordonel)
{
if (!cordonel.RegisterRecoveryRequired)
return true;
var retries = 2;
var meterApps = new List<MeterApplications>();
cordonel.RecoveryRegisters?.Clear();
cordonel.RecoveryRegisters = null;
cordonel.RecoveryRegisters = new List<RecoveryRegisterItem>();
var statusReturn = StatusReturn.Okay;
String msg;
do
{
try
{
// get the programming parameters from DB
if (_fwUpdateDbAccess != null && _fwUpdateDbAccess.DownloadCordonelRecoveryRegistersFromDb(
cordonel.PcbId, cordonel.RecoveryRegisters, fieldUpdate: true))
break;
}
catch (Exception e)
{
LogErrorText(e.Message);
}
} while (retries-- > 0);
// DB download failed
if (retries <= 0)
{
msg = Resources.StrCordonelRecoveryRegisterLoadFailed + " " + cordonel.PcbId + " - " +
cordonel.CustomerSerialNumber;
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// start to build a mock genesis based on the required application versions
var mockGenesis = new GenesisMeter();
var configRegisters = new GenesisConfigurationReader();
var configurationJson = "";
// get the configuration.json out of the SW package which has to be downloaded in advance
if (_fwUpdateSwContainer?.RegisterDefinitionFile?.FileContent?.Length > 0)
{
configurationJson = Encoding.Default.GetString(
_fwUpdateSwContainer.RegisterDefinitionFile.FileContent);
}
// get the application list out of the FW package which has to be downloaded in advance
if (_fwUpdatePackage?.BinaryApplicationFiles?.Count > 0)
{
foreach (var fileApp in _fwUpdatePackage.BinaryApplicationFiles)
{
meterApps.Add(new MeterApplications
{
AppId = MeterFwUpdate.GetFileAppAppId(fileApp.FileContent),
Version = MeterFwUpdate.GetFileAppVersion(fileApp.FileContent, out var strVersion),
StrVersion = strVersion,
Crc = MeterFwUpdate.GetFileAppCrc(fileApp.FileContent),
IsInstalled = true
});
}
}
// Build a list of all accessible registers if the interface and meter applications are defined on
// an input of applications and configurationJson as those are from a mock GenesisMeter. If the meterApps
// and the configurationJson are null, the _currentGenesis is a real device and all valid registers based
// on the application versions are already defined!
if (!string.IsNullOrEmpty(configurationJson) && meterApps.Count > 0)
{
// read the raw configuration.json to build a register list of ALL registers defined there
configRegisters.BuildRegisterList(configurationJson);
// inject registers to mock Genesis
mockGenesis.SetConfigRegisterDefinitions(configRegisters.ConfigRegistersDefinitions);
// take the created dictionary to extract the valid registers for the applications
var configDefRegisters = mockGenesis.GetConfigRegistersDefinitions().MeterRegisterDic.ToList();
// set the (intended to be) installed applications
mockGenesis.MeterAppListVersion.AddRange(meterApps);
// extract the valid meter registers for the target application versions
mockGenesis.BuildValidMeterRegisters(configDefRegisters);
}
// get the pre-defined dictionary
var meterRegisters = mockGenesis.GetConfigRegistersDefinitions();
foreach (var recReg in cordonel.RecoveryRegisters)
{
// if this call does not throw an exception, the register is present
var regDef = meterRegisters.GetRegisterDefinitionByName(recReg.RegisterIdent);
RegisterRestorer.ValidateRegisterRange(recReg, regDef, out var errorMessage);
if (string.IsNullOrEmpty(errorMessage))
continue;
statusReturn = StatusReturn.Failed;
LogErrorText(errorMessage);
}
if (statusReturn == StatusReturn.Okay)
{
LogSuccessText(Resources.StrCordonelRecoveryRegistersAdded + " " + cordonel.PcbId + " - " +
cordonel.CustomerSerialNumber);
return true;
}
msg = Resources.StrCordonelRecoveryRegisterLoadFailed + " " + cordonel.PcbId + " - " +
cordonel.CustomerSerialNumber;
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
/// <summary>
/// Collect all information needed to execute the power correction from DB.
/// These are the EOL production logging values.
/// </summary>
/// <param name="cordonel"></param>
/// <returns>true if EOL production settings loaded</returns>
/// <remarks date="2024-May-07" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private Boolean GetPowerCorrectionsFromDb(CordonelDeviceInfo cordonel)
{
try
{
var reties = 2;
do
{
// get the programming parameters from DB
if (_fwUpdateDbAccess == null ||
!_fwUpdateDbAccess.DownloadDbDataAtEol(cordonel.PcbId, ref cordonel.PowerCorrectionValues))
continue;
LogSuccessText(Resources.StrCordonelDbEolDataLoadSucceded + " " + cordonel.PcbId + " - " +
cordonel.CustomerSerialNumber);
return true;
} while (reties-- > 0);
var msg = Resources.StrCordonelDbEolDataLoadFailed + " " + cordonel.PcbId + " - " +
cordonel.CustomerSerialNumber;
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
LogErrorText(ex.Message);
MessageBoxShow(ex.Message, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return false;
}
/// <summary>
/// Collect order number, radio address, skeleton key, password hashes and passwords from DB.
/// </summary>
/// <param name="cordonel"></param>
/// <returns>true if password container loaded</returns>
/// <remarks date="2021-Feb-01" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Feb-02" author="Thomas Wiedebusch">
/// - Returns bool.
/// </remarks>
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
/// - Check content of password container.
/// </remarks>
/// <remarks date="2021-Feb-04" author="Thomas Wiedebusch">
/// - Using software access helper class.
/// </remarks>
/// <remarks date="2021-Feb-15" author="Thomas Wiedebusch">
/// - Exported base function to FwUpdateDb.
/// </remarks>
/// <remarks date="2021-Mar-25" author="Thomas Wiedebusch">
/// - Retries on missing DB connection or reading of file failed.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Password container directly assigned.
/// </remarks>
/// <remarks date="2023-Oct-17" author="Thomas Wiedebusch">
/// - Password check error messages captured.
/// </remarks>
private Boolean GetPwdFromDb(CordonelDeviceInfo cordonel)
{
try
{
var reties = 2;
do
{
if (_fwUpdateDbAccess == null ||
!_fwUpdateDbAccess.DownloadCordonelPwdFromDb(cordonel.PcbId, out cordonel.PwdContainer))
continue;
LogSuccessText(Resources.StrCordonelPwdAdded + " " + cordonel.PcbId + " - " + cordonel.CustomerSerialNumber);
return true;
} while (reties-- > 0);
var msg = Resources.StrCordonelPwdMissing + " " + cordonel.PcbId + " - " + cordonel.CustomerSerialNumber;
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
LogErrorText(ex.Message);
MessageBoxShow(ex.Message, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return false;
}
/// <summary>
/// Get Cordonel FW update configuration files from DB.
/// The configuration files contain the latest configuration.json
/// and the latest MeterEraseRestore.json (needed for the FwUpdateSw).
/// </summary>
/// <remarks date="2021-Oct-27" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2023-Oct-06" author="Thomas Wiedebusch">
/// - Removed FUpdateRuler, all data are going to be taken from DB.
/// </remarks>
private void GetFwUpdateConfigFilesFromDb()
{
_fwUpdateDbAccess?.DownloadFwUpdateConfigurationFilesFromDb();
}
/// <summary>
/// Get Cordonel SW and configuration files from DB.
/// The software is a container of dll files, language packages and logging setups.
/// The configuration files contain the latest MeterFwUpdateRuler.json (needed for the FwUpdateBuilder),
/// the latest configuration.json and the latest MeterEraseRestore.json (needed for the FwUpdateSw).
/// Creates License, FileCrc, FileLength and SubDirectories.
/// </summary>
/// <param name="fwUpdateSwLicense">license information of valid software</param>
/// <returns>true if successful</returns>
/// <remarks date="2021-Oct-27" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private Boolean BuildFwUpdateSwContainerFromDb(SoftwareLicense fwUpdateSwLicense)
{
if (_fwUpdateDbAccess?.DbFwUpdateConfigFiles == null ||
_fwUpdateDbAccess.DbFwUpdateConfigFiles.Count == 0)
GetFwUpdateConfigFilesFromDb();
if (_fwUpdateDbAccess?.DbFwUpdateConfigFiles == null ||
!_fwUpdateDbAccess.DownloadFwUpdateSwContainerFromDb() ||
_fwUpdateDbAccess.DbFwUpdateSwPackage.Count == 0 ||
_fwUpdateDbAccess.DbFwUpdateConfigFiles.Count == 0)
return false;
_fwUpdateSwContainer = new SoftwareContainer
{
Major = fwUpdateSwLicense.Major,
Minor = fwUpdateSwLicense.Minor,
Build = fwUpdateSwLicense.Build,
Program = fwUpdateSwLicense.Program,
SoftwareDynLinkLibs = new List<FilePart>(),
RegisterDefinitionFile = new FilePart(),
SoftwareSetupFile = new FilePart(),
MeterFilesEraseRestore = new FilePart()
};
foreach (var f in _fwUpdateDbAccess.DbFwUpdateConfigFiles)
{
// load the register definition
if (f.FileName == ProgramConfig.RegisterDefinitionFileName)
{
f.FileContentLength = f.FileContent.Length;
f.FileContentCrc16CcittMsb = Crc16Ccitt.CalculateMsb1021(f.FileContent);
_fwUpdateSwContainer.RegisterDefinitionFile = f;
}
// load the meter files to erase and restore
if (f.FileName == FwUpdateConfig.MeterFilesEraseRestoreConfigFileName)
{
f.FileContentLength = f.FileContent.Length;
f.FileContentCrc16CcittMsb = Crc16Ccitt.CalculateMsb1021(f.FileContent);
_fwUpdateSwContainer.MeterFilesEraseRestore = f;
}
}
foreach (var f in _fwUpdateDbAccess.DbFwUpdateSwPackage)
{
// load the logging configuration
if (f.FileName == ProgramConfig.NlogConfig)
{
f.FileContentLength = f.FileContent.Length;
f.FileContentCrc16CcittMsb = Crc16Ccitt.CalculateMsb1021(f.FileContent);
_fwUpdateSwContainer.SoftwareSetupFile = f;
}
// load the DLLs
if (f.FileName.EndsWith(".dll"))
{
// extract sub-directory from file name in DB for sub-directory creation in FwUpdateLoader
// supports only depth of one level
if (f.FileName.Contains("\\"))
{
var fileName = f.FileName.Split('\\');
var subDirectory = fileName[0];
f.FileName = fileName[fileName.Length - 1];
f.SubDirectory = subDirectory;
}
f.FileContentLength = f.FileContent.Length;
f.FileContentCrc16CcittMsb = Crc16Ccitt.CalculateMsb1021(f.FileContent);
_fwUpdateSwContainer.SoftwareDynLinkLibs.Add(f);
}
}
return true;
}
/// <summary>
/// Build the FW-Update SW container
/// </summary>
/// <returns>true if successful</returns>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Init.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - User license builder.
/// </remarks>
/// <remarks date="2021-Apr-02" author="Thomas Wiedebusch">
/// - Changed return signature.
/// </remarks>
/// <remarks date="2021-Apr-29" author="Thomas Wiedebusch">
/// - Take license from DB.
/// </remarks>
// ReSharper disable once UnusedMember.Local
private Boolean BuildFwUpdateSwContainer(SoftwareLicense fwUpdateSwLicense)
{
_fwUpdateSwContainer = new SoftwareContainer
{
Major = fwUpdateSwLicense.Major,
Minor = fwUpdateSwLicense.Minor,
Build = fwUpdateSwLicense.Build,
Program = fwUpdateSwLicense.Program,
SoftwareDynLinkLibs = new List<FilePart>(),
RegisterDefinitionFile = new FilePart(),
SoftwareSetupFile = new FilePart(),
MeterFilesEraseRestore = new FilePart()
};
try
{
// for debug purposes the locally stored files from download folder are taken
// copy all elements of software release in the main directory
var fileList = new List<String>();
if (SystemControl.GetFilesOfDirectoryAndSubDirectory(FwUpdateSwSourcePath, fileList))
return false;
foreach (var f in fileList)
{
// extract the file name
var fileName = Path.GetFileName(f);
var subDirectory =
Path.GetFullPath(f).Replace(FwUpdateSwSourcePath, "").Replace("\\", "").Replace(fileName, "");
// load the register definition
if (fileName == ProgramConfig.RegisterDefinitionFileName)
{
_fwUpdateSwContainer.RegisterDefinitionFile.FileName = fileName;
var file = new List<Byte>(File.ReadAllBytes(f));
_fwUpdateSwContainer.RegisterDefinitionFile.FileContent = new Byte[file.Count];
_fwUpdateSwContainer.RegisterDefinitionFile.FileContent = file.ToArray();
_fwUpdateSwContainer.RegisterDefinitionFile.FileContentLength = file.Count;
_fwUpdateSwContainer.RegisterDefinitionFile.SubDirectory = subDirectory;
_fwUpdateSwContainer.RegisterDefinitionFile.FileContentCrc16CcittMsb =
Crc16Ccitt.CalculateMsb1021(_fwUpdateSwContainer.RegisterDefinitionFile.FileContent);
}
// load the logging configuration
if (fileName == ProgramConfig.NlogConfig)
{
_fwUpdateSwContainer.SoftwareSetupFile.FileName = fileName;
var file = new List<Byte>(File.ReadAllBytes(f));
_fwUpdateSwContainer.SoftwareSetupFile.FileContent = new Byte[file.Count];
_fwUpdateSwContainer.SoftwareSetupFile.FileContent = file.ToArray();
_fwUpdateSwContainer.SoftwareSetupFile.FileContentLength = file.Count;
_fwUpdateSwContainer.SoftwareSetupFile.SubDirectory = subDirectory;
_fwUpdateSwContainer.SoftwareSetupFile.FileContentCrc16CcittMsb =
Crc16Ccitt.CalculateMsb1021(_fwUpdateSwContainer.SoftwareSetupFile.FileContent);
}
// load the meter files to erase and restore
if (fileName == FwUpdateConfig.MeterFilesEraseRestoreConfigFileName)
{
_fwUpdateSwContainer.MeterFilesEraseRestore.FileName = fileName;
var file = new List<Byte>(File.ReadAllBytes(f));
_fwUpdateSwContainer.MeterFilesEraseRestore.FileContent = new Byte[file.Count];
_fwUpdateSwContainer.MeterFilesEraseRestore.FileContent = file.ToArray();
_fwUpdateSwContainer.MeterFilesEraseRestore.FileContentLength = file.Count;
_fwUpdateSwContainer.MeterFilesEraseRestore.SubDirectory = subDirectory;
_fwUpdateSwContainer.MeterFilesEraseRestore.FileContentCrc16CcittMsb =
Crc16Ccitt.CalculateMsb1021(_fwUpdateSwContainer.MeterFilesEraseRestore.FileContent);
}
// load the DLLs
if (f.EndsWith(".dll"))
{
var filePart = new FilePart { FileName = fileName };
var file = new List<Byte>(File.ReadAllBytes(f));
filePart.FileContent = new Byte[file.Count];
filePart.FileContent = file.ToArray();
filePart.FileContentLength = file.Count;
filePart.SubDirectory = subDirectory;
filePart.FileContentCrc16CcittMsb = Crc16Ccitt.CalculateMsb1021(filePart.FileContent);
_fwUpdateSwContainer.SoftwareDynLinkLibs.Add(filePart);
}
}
}
catch (Exception)
{
_processState = ProcessState.Error;
return false;
}
return true;
}
/// <summary>
/// Build the license information for any FW.
/// </summary>
/// <param name="license"></param>
/// <param name="swName"></param>
/// <param name="version"></param>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Software license element naming adapted to DB content.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Software license flexible for different software.
/// </remarks>
private void BuildSwLicense(String swName, out SoftwareLicense license, Version version = null)
{
// If version is not set, get the actual of this compilation
if (version == null)
version = Assembly.GetExecutingAssembly().GetName().Version;
license = new SoftwareLicense
{
Major = version.Major,
Minor = version.Minor,
Build = version.Build,
ValidTo = DateTimeServer.GetDateTimeFromDateString(lblFwUpdateDutyDate.Text + " 23:59:59", _cultureInfo),
Program = swName,
Description = "valid",
ProgramNameIsUnique = true
};
}
/// <summary>
/// Set the FW-Update SW license information to DB
/// </summary>
/// <param name="license"></param>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Modified for flexible licenses.
/// </remarks>
private void SetFwUpdateSwLicenseToDb(SoftwareLicense license)
{
if (_fwUpdateDbAccess != null && _fwUpdateDbAccess.SetSwLicenseToDb(license))
{
LogSuccessText($"{Resources.StrSwLicenseSetSuccessfully} {license.Program} " +
$"{license.Major}.{license.Minor}.{license.Build}");
return;
}
if (_fwUpdateDbAccess != null && !_fwUpdateDbAccess.DbIsConnected)
{
LogErrorText(Resources.StrNotConnectedToDb);
MessageBoxShow(Resources.StrNotConnectedToDb, Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
lblStatusDbConnect.Text = Resources.StrNotConnectedToDb;
lblStatusDbConnect.ForeColor = ColorProcessFailed;
}
else
{
LogErrorText(Resources.StrSwLicenseSetFailed);
MessageBoxShow(Resources.StrSwLicenseSetFailed, Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
/// <summary>
/// Get the FW-Update SW license information to DB
/// </summary>
/// <param name="programName"></param>
/// <param name="license"></param>
/// <remarks date="2021-Jan-28" author="Thomas Wiedebusch">
/// - Initial with simulated data.
/// </remarks>
/// <remarks date="2021-Apr-01" author="Thomas Wiedebusch">
/// - Modified for flexible licenses.
/// </remarks>
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
/// - License assigned in DB access.
/// </remarks>
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
/// - Messages removed.
/// </remarks>
private void GetFwUpdateSwLicenseFromDb(String programName, out SoftwareLicense license)
{
if (_fwUpdateDbAccess != null &&
_fwUpdateDbAccess.GetSwLicenseFromDb(programName, out license))
return;
license = null;
}
/// <summary>
/// Load a single firmware package from DB. The FW-package infos have to be loaded in advance.
/// Based on the FW-Packages infos of all released FW versions, the user selected FW-package
/// will be prepared and downloaded from DB. All information needed for the FW-Update safe will
/// be copied from the FW-package DB information to the Fw-package information.
/// </summary>
/// <param name="releaseName">each Cordonel tells which release to load</param>
/// <returns>true if succeeded</returns>
/// <remarks date="2021-Apr-09" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-10" author="Thomas Wiedebusch">
/// - Removed configuration.json from FW package as this is part of the SW package.
/// </remarks>
/// <remarks date="2022-Nov-29" author="Thomas Wiedebusch">
/// - Single FW-Update package.
/// </remarks>
private Boolean GetFwPackageFromDb(String releaseName)
{
// if this packages has already been downloaded, exit immediately as only one package is needed and
// allowed for one FW-update safe
if (_fwUpdatePackage.FwPackageInfo.Name == releaseName &&
_fwUpdatePackage.BinaryApplicationFiles != null &&
_fwUpdatePackage.BinaryApplicationFiles.Count > 0)
{
return true;
}
String msg;
var fileId = 0;
// extract the clustered file Id from selected fw package and copy the DB information to the
// FW-package info being used later in the FW-Update safe the single _fwUpdatePackage
foreach (var packInfo in _fwUpdateDbAccess.DbCordonelFwPackagesInfo)
{
// detect the single FW-package selected by the user at the FW package information from DB
if (!packInfo.IsSelected)
continue;
fileId = packInfo.FileClusterStoreId;
_fwUpdatePackage.FwPackageInfo.MeterSizes = new List<String> { packInfo.MeterSize };
_fwUpdatePackage.FwPackageInfo.Name = packInfo.Name;
_fwUpdatePackage.FwPackageInfo.RadioFrequencyMhz = packInfo.RadioFrequencyMhz;
_fwUpdatePackage.FwPackageInfo.Region = packInfo.Region;
_fwUpdatePackage.FwPackageInfo.CoreRevisionMax = packInfo.CoreRevisionMax;
_fwUpdatePackage.FwPackageInfo.CoreRevisionMin = packInfo.CoreRevisionMin;
_fwUpdatePackage.FwPackageInfo.MetrologyVersion = packInfo.MetrologyVersion;
_fwUpdatePackage.FwPackageInfo.Version = packInfo.Version;
_fwUpdatePackage.FwPackageInfo.FileName = packInfo.FileName;
break;
}
// search again identical FW-packages by name to set multiple frequencies of sizes
foreach (var packInfo in _fwUpdateDbAccess.DbCordonelFwPackagesInfo)
{
// detect the single FW-package selected by the user at the FW package information from DB
if (packInfo.Name != _fwUpdatePackage.FwPackageInfo.Name)
continue;
// on a package with multiple frequency support set the frequency to null to skip radio check
// in the FW-Update SW
if (packInfo.RadioFrequencyMhz != _fwUpdatePackage.FwPackageInfo.RadioFrequencyMhz)
_fwUpdatePackage.FwPackageInfo.RadioFrequencyMhz = null;
// check multiple meter sizes in the FW-Update SW
if (_fwUpdatePackage.FwPackageInfo.MeterSizes.All(size => size != packInfo.MeterSize))
{
_fwUpdatePackage.FwPackageInfo.MeterSizes.Add(packInfo.MeterSize);
}
}
// after selection of the FW-package info, the real physical FW-update package has to be downloaded
// from the DB
if (_fwUpdateDbAccess.GetCordonelFwPackageFromDb(fileId, out var fwPackage))
{
try
{
// search the cordonel firmware
_fwUpdatePackage.PackageDescriptionFile = new FilePart();
_fwUpdatePackage.BinaryApplicationFiles = new List<FilePart>();
foreach (var f in fwPackage)
{
// load the ADF (application description file, containing the information of each application)
if (f.FileName.Contains("product") && f.FileName.EndsWith(".txt"))
{
_fwUpdatePackage.PackageDescriptionFile.FileName = f.FileName;
_fwUpdatePackage.PackageDescriptionFile.FileContent = new Byte[f.FileContent.Length];
_fwUpdatePackage.PackageDescriptionFile.FileContent = f.FileContent.ToArray();
}
// load the binaries (application files)
if (f.FileName.Contains("binfile") && f.FileName.EndsWith(".bin"))
{
var filePart = new FilePart
{
FileName = f.FileName,
FileContent = new Byte[f.FileContent.Length]
};
filePart.FileContent = f.FileContent.ToArray();
_fwUpdatePackage.BinaryApplicationFiles.Add(filePart);
}
}
LogSuccessText(Resources.StrFwPackageLoadSuccess + " " + releaseName);
return true;
}
catch (Exception e)
{
msg = Resources.StrFwPackageLoadFailed + @" " + releaseName;
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK,
MessageBoxIcon.Error);
_processState = ProcessState.Error;
return false;
}
}
msg = Resources.StrFwPackageLoadFailed + @" " + releaseName;
LogErrorText(msg);
MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
#endregion --------------------------------------- Load DB Contents -------------------------------------------
#region ------------------------------------------ Language ---------------------------------------------------
/// <summary>
/// Select language at runtime: English
/// </summary>
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
/// - Change menu.
/// </remarks>
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
/// - Avoid repetition if current culture is equal to required.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Backup duty date for FW-Update during language change.
/// </remarks>
private void RadioBtnEnglishLanguage_Click(Object sender, EventArgs e)
{
if (Thread.CurrentThread.CurrentCulture.Name == "en-GB")
{
return;
}
_cultureInfo = new CultureInfo("en-GB");
ChangeLanguageControls();
}
/// <summary>
/// Select language at runtime: German
/// </summary>
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
/// - Change menu.
/// </remarks>
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
/// - Avoid repetition if current culture is equal to required.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Backup duty date for FW-Update during language change.
/// </remarks>
private void RadioBtnGermanLanguage_Click(Object sender, EventArgs e)
{
if (Thread.CurrentThread.CurrentCulture.Name == "de-DE")
{
return;
}
_cultureInfo = new CultureInfo("de-DE");
ChangeLanguageControls();
}
/// <summary>
/// Change language at runtime
/// </summary>
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
/// - Initial based on code example
/// https://stackoverflow.com/questions/52178064/winforms-localization-how-to-change-the-language-of-a-menu.
/// </remarks>
/// <remarks date="2021-Mar-02" author="Thomas Wiedebusch">
/// - Update selected table headers of tab control.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Restore duty date for FW-Update after language change.
/// </remarks>
private void ChangeLanguageControls()
{
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
Thread.CurrentThread.CurrentCulture = _cultureInfo;
var resources = new ComponentResourceManager(typeof(FrmFwUpdateBuilder));
resources.ApplyResources(this, "$this");
ControlExtensions.ChangeControlText(resources, Controls);
var rm = new ComponentResourceManager(GetType());
foreach (var control in this.AllControls())
{
if (control is ToolStrip)
{
var items = ((ToolStrip)control).AllItems().ToList();
foreach (var item in items)
{
rm.ApplyResources(item, item.Name);
}
}
rm.ApplyResources(control, control.Name);
}
if (!_fwUpdateDbAccess.DbIsConnected)
{
lblStatusDbConnect.Text = Resources.StrNotConnectedToDb;
lblStatusDbConnect.ForeColor = ColorProcessFailed;
}
else
{
lblStatusDbConnect.Text = Resources.StrConnectedToDb;
lblStatusDbConnect.ForeColor = ColorSuccess;
}
lblFwUpdateInfo.Text = $@"Version: {_version.Major}.{_version.Minor}.{_version.Build}";
_fwUpdateOperatorsPropertyChanged = true;
_customersPropertyChanged = true;
_cordonelsPropertyChanged = true;
_fwPackagePropertyChanged = true;
_fwReportsPropertyChanged = true;
_fwUpdateSafesPropertyChanged = true;
_preBuildSummaryPropertyChanged = true;
lblFwUpdateDutyDate.Text = _datePicker.Value.ToShortDateString();
}
#endregion --------------------------------------- Language ---------------------------------------------------
}
}