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 Logic.ProductionToProductMapper.Cordonel; using Xylem.Common.CommonCore.Consts; using Xylem.Common.CommonCore.ThreadWatcher; using Xylem.Common.Cryptology.Security; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile; 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 { /// /// FW update builder /// 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 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; /// /// remind manually changed culture setting /// 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 = "-----------------------------------------------------" + "-----------------------------------------------------"; /// /// Registered user information read from registration file. /// 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; /// /// Collection of software needed for the FW-Update safe! /// private SoftwareContainer _fwUpdateSwContainer; /// /// All customer specific production order numbers to search mask from the DB! /// This information will only be used by the FW Update Builder! /// private readonly List _cordonelCustomerOrdersSearch = new List(); /// /// All information acquired from the DB! This information will only be used by the FW Update Builder /// as search! /// private readonly List _cordonelProductionSearchInfos = new List(); /// /// All information acquired from the DB! This information will only be used by the FW Update Builder /// as pre-selection! /// private readonly List _cordonelProductionPreSelectInfos = new List(); /// /// 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. /// private readonly List _cordonelUpdateList = new List(); private String _fwPackageCordonelPcbId; /// /// 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. /// private readonly List _fwUpdatePackages = new List(); /// /// 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! /// private readonly List _cordonelFwPackageInfoSearch = new List(); private readonly List _metrologySearchItems = new List(); private readonly List _coreSearchItems = new List(); private readonly List _regionSearchItems = new List(); private readonly List _radioSearchItems = new List(); private readonly List _sizeSearchItems = new List(); /// /// 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! /// private readonly List _fwUpdateCapability = new List(); 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; private readonly List _meterFwUpdateRuler = new List(); /// /// 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!!!!!! /// private String _customerNumberBeforeLastCordonelDbSearch = ""; /// /// Feedback from any task that an update of all tables is required /// private Boolean _fwUpdateOperatorsPropertyChanged; private Boolean _customersPropertyChanged; private Boolean _cordonelsPropertyChanged; private Boolean _fwPackagePropertyChanged; private Boolean _fwReportFilesPropertyChanged; private Boolean _fwUpdateSafesPropertyChanged; private Boolean _preBuildSummaryPropertyChanged; /// /// DB connection retry timer if DB is not connected /// private const Int32 DbConnectionRetryDelayMs = 6000; /// /// DB connection timeout on active DB connectivity test /// private const Int32 DbConnectionTimeoutMs = 5000; private Int32 _dbAccessDelayCtrMs; /// /// Project name for the DLL to load: /// This is the base for the source folder, the destination folder, the namespace and the form /// private const String FwUpdateProjectName = "ServiceFwUpdateSw"; /// /// Name for firmware update software including the namespace /// public const String FwUpdateSwFullName = "Xylem.ServiceFwUpdate.Ui." + FwUpdateProjectName; /// /// Path for FW-Update safes /// private static readonly String FwUpdateSafePath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.UserProfile), FwUpdateConfig.DefaultFwUpdateSafePath); /// /// Path to application configuration ../[user]/AppData/Roaming/Genesis/ /// private static readonly String ApplicationConfigPath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData), ProgramConfig.GenesisBaseFolder); /// /// Path and file user NLog configuration /// private static readonly String NLogConfigurationDestPathFile = Path.Combine(ApplicationConfigPath, ProgramConfig.NlogConfig); /// /// Source folder of software package if taken from local [user]/downloads/ServiceFwUpdateSw /// private static readonly String FwUpdateSwSourcePath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.UserProfile), FwUpdateConfig.DefaultFwUpdateSwSourcePath); #endregion --------------------------------------- Variables -------------------------------------------------- #region ------------------------------------------ State Machine ---------------------------------------------- /// /// 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! /// /// /// - Initial /// /// /// - Sleep on equal process state to force suspend of actual thread. /// /// /// - Added report files and FW-Update safes load from DB. /// 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.GetReportFilesFromDb: LoadDbReportFilesTask(); break; case ProcessState.GetFwUpdateSafesFromDb: LoadDbOutstandingFwUpdateSafesTask(); 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.GetFwPackagesFromDb: LoadDbCordonelFwPackagesTask(); 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; 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 --------------------------------------------- /// /// The timer for progress bar. /// /// /// - Initial /// /// /// - Changed logic /// /// /// - Checks exported /// /// /// - Added checks for FW-update safes and report files. /// 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(); CheckReportFilesChange(); 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.GetFwPackagesFromDb: case ProcessState.GetCordonelSerialNumbersFromDb: case ProcessState.GetFwUpdateSafesFromDb: case ProcessState.GetReportFilesFromDb: case ProcessState.UploadFwUpdateSafeToDb: CheckDbConnectionTimeout(); SetStatusProgressBar(ProgressBarStatus.Value + 1 > 100 ? 0 : ProgressBarStatus.Value + 1); break; } } #endregion --------------------------------------- Timer Controls --------------------------------------------- #region ------------------------------------------ User Interaction ------------------------------------------- /// /// Select all content for quick change /// /// /// - Initial. /// private void tbxCordonelProductionOrdersSearch_Enter(Object sender, EventArgs e) { tbxCordonelProductionOrdersSearch.Select(0, tbxCordonelProductionOrdersSearch.Text.Length); } /// /// Select all content for quick change /// /// /// - Initial. /// private void tbxCordonelProductionOrdersSearch_MouseClick(Object sender, MouseEventArgs e) { tbxCordonelProductionOrdersSearch.Select(0, tbxCordonelProductionOrdersSearch.Text.Length); } /// /// Validate input /// /// /// - Initial. /// private void tbxOrderNumber_TextChanged(Object sender, EventArgs e) { tbxOrderNumber.Text = Regex.Match(tbxOrderNumber.Text, "[0-9]+").Groups[0].Value; } /// /// Validate input /// /// /// - Initial. /// private void tbxOrderPosition_TextChanged(Object sender, EventArgs e) { tbxOrderPosition.Text = Regex.Match(tbxOrderPosition.Text, "[0-9]+").Groups[0].Value; } /// /// Display Info /// /// /// - Initial. /// private void infoToolStripMenuItem_Click(Object sender, EventArgs e) { _processState = ProcessState.UserRegistration; var frmRegister = new FrmRegister(_regUser); DisableAllControlsInvoked(); frmRegister.Show(); frmRegister.Closed += FormRegister_Closed; Hide(); } /// /// Exit of Registration Form. /// /// /// - Initial /// private void FormRegister_Closed(Object sender, EventArgs e) { Show(); Update(); CheckUserRegistration(); } /// /// Exit the program /// /// /// - Initial. /// private void exitToolStripMenuItem_Click(Object sender, EventArgs e) { Close(); } /// /// Kick off customer search on key [Enter] pressed /// /// /// - Initial. /// private void tbxCustomerSearchMask_KeyDown(Object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { btnSearchCustomer_Click(this, null); } } /// /// Kick off Cordonel search on key [Enter] pressed /// /// /// - Initial. /// 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 == @"*") { _cordonelsPropertyChanged = true; return; } btnCordonelSearch_Click(this, null); } } /// /// Kick off Cordonel search referenced by order number /// /// /// - Initial. /// /// /// - Removed DB access delay. /// 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() == @"*") { _cordonelCustomerOrdersSearch.AddRange(_fwUpdateDbAccess.DbCordonelCustomerOrders); } else { // format is orderNumber-position e.g. 12345678-10 var orderText = cbxCordonelProductionOrdersSearch.SelectedItem.ToString().Split('-'); if (Int64.TryParse(orderText[0], out var orderNumber) && Int64.TryParse(orderText[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 } } /// /// Kick off customer search. /// /// /// - Initial. /// /// /// - Clear last search result. /// 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; } /// /// Select DEBUG Cordonels. /// /// /// - Initial. /// private void cbxAddDebugCordonels_Click(Object sender, EventArgs e) { //DebugDeviceAndFwPackageSelection(); } /// /// Kick off FW-Update package search. /// /// /// - Initial. /// private void btnFwPackageSearch_Click(Object sender, EventArgs e) { // Load fw packages only if not selected indicated in count if (_fwUpdatePackages.Count > 0) return; DisableAllControlsInvoked(); _dataTableFwPackages?.Clear(); UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingFwPackages); SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; _processState = ProcessState.GetFwPackagesFromDb; } /// /// Mask already loaded FW-Update packages. /// /// /// - Initial. /// private void allFwPackageSearch_Event(Object sender, EventArgs e) { _fwPackagePropertyChanged = true; } /// /// Tab page selection event (replaced of tabPage_Enter event of a selected tab page as this /// fires on all data sets copied to it). /// /// /// - Initial. /// /// /// - FW update package selection. /// /// /// - Enable retry if DB connection lost. /// /// /// - FW packages not loaded if already done on page change. /// private void tabControlSelection_Selected(Object sender, TabControlEventArgs e) { if (e.TabPage == tabPageFwUpdateSafes) { DisableAllControlsInvoked(); UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingFwUpdateSafes); SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; _processState = ProcessState.GetFwUpdateSafesFromDb; } if (e.TabPage == tabPageReports) { DisableAllControlsInvoked(); UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingReportFiles); SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; _processState = ProcessState.GetReportFilesFromDb; } 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) { _preBuildSummaryPropertyChanged = true; } if (e.TabPage == tabPageFwPackageSelection) { // Load fw packages only if not selected indicated in count if (_fwUpdatePackages.Count > 0 || _cordonelFwPackageInfoSearch != null && _cordonelFwPackageInfoSearch.Count > 0) return; DisableAllControlsInvoked(); _dataTableFwPackages?.Clear(); UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrReadingFwPackages); SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; _processState = ProcessState.GetFwPackagesFromDb; } 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; } } /// /// Upload FW-Update Safe to DB. /// /// /// - Initial. /// private void btnUploadSafeToDb_Click(Object sender, EventArgs e) { UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrFwUpdateSafeUploading); SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; _processState = ProcessState.UploadFwUpdateSafeToDb; } /// /// License the actual FW-Update Builder which is this product! /// /// /// - Initial. /// /// /// - Add 10 years for valid date. /// 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); } } /// /// License the actual FW-Update SW. /// /// /// - Initial. /// 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); } /// /// Build the update safe. /// /// /// /// /// - Initial. /// /// /// - Clear content of FW package to force reload and output of loaded package on rebuild of safe. /// private void btnBuildFwUpdateSafe_Click(Object sender, EventArgs e) { SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; // clear FW packages content to force reload from data base and therefore report generation, // this is essential for rebuilding of a safe foreach (var package in _fwUpdatePackages) { package?.BinaryApplicationFiles?.Clear(); } rtbReport.Clear(); rtbReport.ForeColor = ColorDefault; lblStatusDbConnect.Text = Resources.StrFwUpdateSafeBuilding; _processState = ProcessState.BuildFwUpdateSafe; } /// /// Close the date picker for the FW-Update validation date. /// /// /// - Initial. /// 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(); } /// /// Close the date picker for the user validation date. /// /// /// - Initial. /// private void datePickerUserValidationDate_CloseUp(Object sender, EventArgs e) { //_userInfo.ValidDate = DateTimeServer.GetDateTimeOffsetFromDateString(_datePicker.Text); _fwUpdateOperatorsPropertyChanged = true; HideDatePicker(); } /// /// Activate date time picker. /// /// /// - Initial. /// 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 ------------------------------------------- /// /// Ctor /// /// /// - Initial /// /// /// - Invisible progress bar at start. /// /// /// - Directory for FW-Update Safes created. /// /// /// - Grid color for update capability checks. /// /// /// - Pre-filled FW-Update package search lists. /// /// /// - MeterFwUpdateRuler.json class changed to nullable DateTime. /// /// /// - Library added to NLogConfig source path, as this is the release source, copy files only if not /// identical. /// public FrmFwUpdateBuilder() { var fwUpdateBuilderExePath = AppDomain.CurrentDomain.BaseDirectory; var fwUpdateBuilderLibraryPath = Path.Combine(fwUpdateBuilderExePath, "Library"); var fwUpdateBuilderConfigPath = Path.Combine(fwUpdateBuilderExePath, "Config"); 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); var rulerPathName = Path.Combine(fwUpdateBuilderLibraryPath, FwUpdateConfig.MeterFwUpdateRulerConfigFileName); if (!File.Exists(rulerPathName)) rulerPathName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FwUpdateConfig.MeterFwUpdateRulerConfigFileName); if (File.Exists(rulerPathName)) { var rulerFile = File.ReadAllText(rulerPathName, Encoding.UTF8); _meterFwUpdateRuler = JsonConvert.DeserializeObject>(rulerFile); } } 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}"; _logger.Info(StrSeparator); _logger.Info(_versionString); _logger.Info(StrSeparator); InitializeComponent(); _cultureInfo = Thread.CurrentThread.CurrentCulture; radioBtnEnglishLanguage.Checked = true; if (_cultureInfo.IetfLanguageTag == "de-DE") { radioBtnGermanLanguage.Checked = true; } 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 = @"1234567890"; 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); } /// /// Exit /// /// /// - Initial /// private void FrmFwUpdateBuilder_FormClosing(Object sender, FormClosingEventArgs e) { _datePicker?.Dispose(); _cbxFwReleaseSelection?.Dispose(); _processToken?.Cancel(); } #endregion --------------------------------------- Form Load Unload ------------------------------------------- #region ------------------------------------------ Build FW-Update Safe --------------------------------------- /// /// Build the firmware update safe and stores it to a file and to the DB. /// /// /// - Initial. /// /// /// - Logging added. /// /// /// - FW-Update SW container added. /// /// /// - Order number and customer text added added, /// - Disable all controls during FW-UpdateSafe build. /// /// /// - Additional logging. /// /// /// - Output short file name. /// /// /// - Action on missing FwUpdateSw. /// /// /// - Error process state introduced. /// /// /// - Replaced _userInfo by lblUpdateOperator. /// /// /// - Taking FW-Update SW license from DB. /// /// /// - Added FwUpdateSafeDb preparation /// /// /// - Added BuildFwUpdateSwContainerFromDb, not longer taken from locally file system! /// public Boolean BuildFwUpdateSafe() { if (string.IsNullOrEmpty(lblUpdateOperator.Text)) return false; DisableAllControlsInvoked(); Invoke(new Action(() => { tabControlSelection.SelectTab(tabPageSafeBuildReport); tabControlSelection.Refresh(); })); try { var fwUpdateSafe = new FwUpdateSafe { //License = new SoftwareLicense(), Software = new SoftwareContainer(), Updates = new UpdateContainer() }; // check DB connection in advance to overcome the timing issues for DB service startup _fwUpdateDbAccess.CheckDbConnection(); LogText(StrSeparator); LogText(Resources.StrCustomerName + @" " + tbxCustomerName.Text); LogText(Resources.StrCustomerNumber + @" " + lblCustomerNumber.Text); LogText($@"{Resources.StrOrderNumber} {tbxOrderNumber.Text}-{tbxOrderPosition.Text}"); LogText(Resources.StrBuilderOperator + @" " + _regUser.FullName); LogText(Resources.StrUpdateOperator + @" " + lblUpdateOperator.Text); LogText(Resources.StrValidDate + @" " + lblFwUpdateDutyDate.Text); LogText(StrSeparator); // 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); LogSuccessText($@"{Resources.StrSwLicenseLoadedSuccessfully} {FwUpdateProjectName}" + $" {fwUpdateSafe.License.Major}.{fwUpdateSafe.License.Minor}.{fwUpdateSafe.License.Build}"); // 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; } if (_fwUpdateCapability.All(x => x.ReleaseContainerLoaded == false)) { _processState = ProcessState.Error; return false; } fwUpdateSafe.Updates = new UpdateContainer { CordonelFwPackages = new List(), CordonelDeviceInfos = new List() }; fwUpdateSafe.Updates.CordonelFwPackages = _fwUpdatePackages; fwUpdateSafe.Updates.CordonelDeviceInfos = _cordonelUpdateList; 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); // 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(), ""); // leave all word characters (including the umlaut in for multilingual support), numbers the "-" and the white space in fileName = Regex.Replace(fileName, @"[^\w\d\-\s]", ""); // The safe shall look like "Thames Water_01234567890-10.safe" _fwUpdateSafeName = $@"{fileName}{FwUpdateConfig.FwUpdateFileFieldSeparator}" + $@"{tbxOrderNumber.Text}-{tbxOrderPosition.Text}" + FwUpdateConfig.FwUpdateFileExtension; // prepare FwUpdateSafeDb if (string.IsNullOrEmpty(_regUser.FullName)) _regUser.FullName = ""; _fwUpdateSafeDb = new FwUpdateSafeDb { OperatorNameBuilder = _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.StrFwUpdateSafeBuildFailed); MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _processState = ProcessState.Error; return false; } } /// /// Collect cordonel device info, the user has to input the required pcbIds and release versions in advance! /// /// /// - Initial. /// /// /// - Load from DB. /// /// /// - Kick off DB connection with dummy request. /// /// /// - Password container directly assigned. /// public void CompleteCordonelDeviceInfos() { // establish DB connection with dummy request _fwUpdateDbAccess?.DownloadCordonelPwdFromDb("0", out _); _fwUpdateDbAccess?.DownloadCordonelPwdFromDb("0", out _); // 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.RequiredRelease); _fwUpdateCapability.Add(fwUpdateCapability); } } #endregion --------------------------------------- Build FW-Update Safe --------------------------------------- #region ------------------------------------------ Checks ----------------------------------------------------- /// /// Check the user registration from DB. /// /// /// - Initial. /// 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; break; } if (_regUser.AccountActive) { EnableInputsInvoked(); _processState = ProcessState.Idle; return; } DisableControlsExceptLanguageInvoked(); _processState = ProcessState.DbAccessLocked; } /// /// Check the update capability of all selected Cordonels /// /// /// - Initial /// private void CheckUpdateCapability() { var success = true; foreach (var cordonel in _cordonelProductionPreSelectInfos) { // load the actual installed app versions // check metrology // check core revision // check radio frequency if (success) { LogSuccessText(Resources.StrCordonelUpdateCapabilityCheckSuccess + " " + cordonel.PcbId); return; } var msg = Resources.StrCordonelUpdateCapabilityCheckFailed + @" " + cordonel.PcbId; LogErrorText(msg); MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// Check DB connection timeout during connectivity check /// /// /// - Initial /// /// /// - Avoid interruption if DB is connected. /// 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; } /// /// Check DB license refresh. /// /// /// - Initial /// private void RefreshLicense() { if (DateTimeOffset.Compare(_licenseValidDateTimeOffset, DateTimeOffset.Now) < 0) _licenseUnchecked = true; } /// /// Check DB connectivity. /// /// /// - Initial /// /// /// - Progress bar off if connection lost. /// /// /// - On initialized DB connection fire the tabControl event to update user view, /// - dbAccessLocked introduced. /// /// /// - Disabled: On initialized DB connection fire the tabControl event to update user view. /// /// /// - Check for license. /// 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; } } /// /// 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. /// /// /// - Initial /// /// /// - [Build FW-Update Safe] check extended /// /// /// - checks extended, /// - avoid repeated setup of item if already done. /// /// /// - Added builder license check. /// /// /// - FW-Update package status added. /// /// /// - Check order numbers. /// /// /// - Check fw packages. /// /// /// - Lock customer search button if customer is selected to avoid lose of information on new search. /// /// /// - Today is a valid day. /// /// /// - Metrology search items added. /// /// /// - Pre-Build summary check. /// /// /// - Cordonel selection check moved to production pre select. /// /// /// - Cordonel selection check and Cordonel approval check differentiate. /// /// /// - Use selected Cordonels to fill the FW package search infos. /// 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 = @"*"; cbxCordonelProductionOrdersSearch.Items.Add("*"); 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?.DbCordonelFwPackages != null && _fwUpdatePackages != null && _cbxFwReleaseSelection != null) { if (_cbxFwReleaseSelection.Items.Count != _fwUpdatePackages.Count) { _cbxFwReleaseSelection.Items.Clear(); foreach (var fw in _fwUpdatePackages) { _cbxFwReleaseSelection.Items.Add(fw.ReleaseNameVersion); } } } // Check if FW packages are selected if (_fwUpdatePackages != null && lblSelectedFwPackages.Text != _fwUpdatePackages.Count.ToString()) lblSelectedFwPackages.Text = _fwUpdatePackages.Count.ToString(); backGroundColor = _fwUpdatePackages != null && _fwUpdatePackages.Count == 0 ? 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.Text = @"*"; cbxFwPackageRadioMask.Items.Add("*"); foreach (var item in _radioSearchItems) { if (!string.IsNullOrEmpty(item) && item != "?") cbxFwPackageRadioMask.Items.Add(item); } } // 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.Text = @"*"; cbxFwPackageRegionMask.Items.Add("*"); foreach (var item in _regionSearchItems) { if (!string.IsNullOrEmpty(item) && item != "?") cbxFwPackageRegionMask.Items.Add(item); } } // 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.Text = @"*"; cbxFwPackageSizeMask.Items.Add("*"); foreach (var item in _sizeSearchItems) { if (!string.IsNullOrEmpty(item) && item != "?") cbxFwPackageSizeMask.Items.Add(item); } } // check if FW packages are loaded from DB and parsed to the search info to extract metrology info if (_metrologySearchItems.Count + 1 != cbxFwPackageSearchMetrology.Items.Count) { cbxFwPackageSearchMetrology.Items.Clear(); cbxFwPackageSearchMetrology.Text = @"*"; cbxFwPackageSearchMetrology.Items.Add("*"); foreach (var item in _metrologySearchItems) { if (!string.IsNullOrEmpty(item) && item != "?") cbxFwPackageSearchMetrology.Items.Add(item); } } // check if FW packages are loaded from DB and parsed to the search info to extract core info if (_coreSearchItems.Count + 1 != cbxFwPackageSearchCore.Items.Count) { cbxFwPackageSearchCore.Items.Clear(); cbxFwPackageSearchCore.Text = @"*"; cbxFwPackageSearchCore.Items.Add("*"); foreach (var item in _coreSearchItems) { if (!string.IsNullOrEmpty(item) && item != "?") cbxFwPackageSearchCore.Items.Add(item); } } // 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.RequiredRelease) || updCord.RequiredRelease == "?"); 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 || _fwUpdatePackages.Count == 0 || _cordonelUpdateList.Count == 0 || updateLocked) { if (btnBuildFwUpdateSafe.Enabled) btnBuildFwUpdateSafe.Enabled = false; return; } if (!btnBuildFwUpdateSafe.Enabled) btnBuildFwUpdateSafe.Enabled = true; } } /// /// Check for change in user account. /// /// /// - Initial /// /// /// - Locked if operators are not loaded. /// private void CheckUpdateOperatorAccountChange() { if (!_fwUpdateOperatorsPropertyChanged || _fwUpdateDbAccess?.DbFullQualifiedUpdateOperators == null || _fwUpdateDbAccess.DbFullQualifiedUpdateOperators.Count == 0) return; _fwUpdateOperatorsPropertyChanged = false; // update output information FillDataGridWithUpdateOperatorInfos(); } /// /// Check for change of customer. /// /// /// - Initial /// /// /// - Locked if customers are not loaded. /// private void CheckCustomerChange() { if (!_customersPropertyChanged || _fwUpdateDbAccess?.DbSearchedCordonelCustomers == null || _fwUpdateDbAccess.DbSearchedCordonelCustomers.Count == 0) return; _customersPropertyChanged = false; // update output information FillDataGridWithCustomerInfos(); } /// /// Check for change of Cordonels. /// /// /// - Initial /// /// /// - Locked if Cordonels are not loaded. /// /// /// - Added pre build summary. /// private void CheckCordonelChange() { if (_cordonelsPropertyChanged) { _cordonelsPropertyChanged = false; // update output information FillDataGridWithCordonelInfos(); } if (!_preBuildSummaryPropertyChanged) return; _preBuildSummaryPropertyChanged = false; // update output information FillDataGridWithPreBuildSummaryInfos(); } /// /// Check for change of FW-Update packages. /// /// /// - Initial /// /// /// - Locked if FW-Packages are not loaded. /// private void CheckFwPackageChange() { if (!_fwPackagePropertyChanged || _fwUpdateDbAccess?.DbCordonelFwPackages == null || _fwUpdateDbAccess.DbCordonelFwPackages.Count == 0) return; _fwPackagePropertyChanged = false; // update output information FillDataGridWithFwPackageInfos(); } /// /// Check for change of report files. /// /// /// - Initial /// private void CheckReportFilesChange() { if (!_fwReportFilesPropertyChanged) return; _fwReportFilesPropertyChanged = false; // update output information FillDataGridWithReportFilesInfos(); } /// /// Check for change of Fw update safes. /// /// /// - Initial /// private void CheckFwUpdateSafesChange() { if (!_fwUpdateSafesPropertyChanged) return; _fwUpdateSafesPropertyChanged = false; // update output information FillDataGridWithFwUpdateSafesInfos(); } #endregion --------------------------------------- Checks ----------------------------------------------------- #region ------------------------------------------ Tools ------------------------------------------------------ ///// ///// Build the meter file update ruler to define releases which can be updated to another release. ///// ///// ///// - Initial. ///// //public void BuildMeterFwUpdateRulerInfo() //{ // var meterFwUpdateRuler = new List(); // var rule = new MeterFwUpdateRuler // { // Release = "R1066", // Region = "EMEA", // RadioFrequencyMhz = "433", // CoreVersionMin = "162", // CoreVersionMax = "164", // MetrologyVersion = "287", // MeterSizes = new List // { // "DN50" // }, // ApproverName = "Ami A Arsalan", // ApprovalDate = new DateTime(2020, 12, 31) // }; // meterFwUpdateRuler.Add(rule); // rule = new MeterFwUpdateRuler // { // Release = "R1100", // Region = "EMEA", // RadioFrequencyMhz = "433", // CoreVersionMin = "162", // CoreVersionMax = "167", // MetrologyVersion = "287", // MeterSizes = null, // ApproverName = "Willi Weber", // ApprovalDate = new DateTime(2021, 04, 01) // }; // meterFwUpdateRuler.Add(rule); // rule = new MeterFwUpdateRuler // { // Release = "R106F", // Region = "EMEA", // RadioFrequencyMhz = "868", // CoreVersionMin = "162", // CoreVersionMax = "166", // MetrologyVersion = "287", // MeterSizes = new List // { // "DN50", // "DN60", // "DN80", // "DN150" // }, // ApproverName = "Oliver Ohlsen", // ApprovalDate = new DateTime(2021, 01, 08) // }; // meterFwUpdateRuler.Add(rule); // rule = new MeterFwUpdateRuler // { // Release = "B1070", // Region = "NA", // RadioFrequencyMhz = null, // CoreVersionMin = "162", // CoreVersionMax = "166", // MetrologyVersion = "508", // MeterSizes = new List // { // "DN50" // }, // ApproverName = "Mike M Miller", // ApprovalDate = new DateTime(2021, 01, 13) // }; // meterFwUpdateRuler.Add(rule); // var text = JsonConvert.SerializeObject(meterFwUpdateRuler); // var asciiStream = Encoding.UTF8.GetBytes(text); // File.WriteAllBytes(FwUpdateConfig.MeterFwUpdateRulerConfigFileName, asciiStream); //} ///// ///// Build the meter file erase restore information and export to MeterFilesConfigFilePathName. ///// ///// ///// - Initial. ///// //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 // { // "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 // }; // var text = JsonConvert.SerializeObject(meterFilesEraseRestore); // var asciiStream = Encoding.UTF8.GetBytes(text); // File.WriteAllBytes(FwUpdateConfig.MeterFilesEraseRestoreConfigFileName, asciiStream); //} /// /// Error processes. /// /// /// - Initial /// 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; } } /// /// Stop all ongoing processes. /// /// /// - Initial /// 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; } } /// /// Common message window. /// /// /// - Forcing message box being modal and on top. /// private static void MessageBoxShow(String text, String caption, MessageBoxButtons buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Asterisk) { MessageBox.Show(text, caption, buttons, icon, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification); } /// /// Output exclusively to user update remarks text window. /// /// /// - Color added. /// private void LogText(String txtHistory) { InfoWindowColoredText(txtHistory, ColorDefault); _logger.Info(txtHistory); } /// /// Output exclusively to user update remarks text window. /// private void LogErrorText(String txtHistory) { InfoWindowColoredText(txtHistory, ColorProcessFailed); _logger.Info(txtHistory); } /// /// Output exclusively to user update remarks text window. /// private void LogSuccessText(String txtHistory) { InfoWindowColoredText(txtHistory, ColorSuccess); _logger.Info(txtHistory); } /// /// Output exclusively to user update remarks text window. /// 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(); })); } /// /// Process bar - NOT invoked, just call from Form main thread. /// /// /// - Initial /// /// /// - Avoid repeated execution on invisible progress bar. /// private void SetStatusProgressBar(Int32 value = 0, Boolean visible = true) { if (ProgressBarStatus == null || ProgressBarStatus.Visible == false && visible == false) return; ProgressBarStatus.Value = value; ProgressBarStatus.Visible = visible; } /// /// Common routine to hide the data picker and uninstall all events. /// /// /// - Initial. /// /// /// - Check user input. /// private void HideDatePicker() { _datePicker.Visible = false; _datePicker.CloseUp -= datePickerFwUpdateValidationDate_CloseUp; _datePicker.CloseUp -= datePickerUserValidationDate_CloseUp; // display the label lblFwUpdateDutyDate.Visible = true; CheckDataCollectionStatus(); } /// /// Common routine to disable all controls. /// /// /// - Initial. /// /// /// - Added new buttons. /// 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); } /// /// Common routine to disable all controls except the language setting. /// This should be used, if user is not licensed. /// /// /// - Initial. /// private void DisableControlsExceptLanguageInvoked() { UiInvoker.ControlEnableInvoker(grpLanguageSelection, true); 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); } /// /// Common routine to enable inputs. /// /// /// - Initial. /// /// /// - Avoid activation on unchecked license. /// private void EnableInputsInvoked() { if (_licenseUnchecked || _regUser == null || !_regUser.AccountActive) return; UiInvoker.ControlEnableInvoker(grpLanguageSelection, true); UiInvoker.ControlEnableInvoker(lblFwUpdateDutyDate, true); UiInvoker.ControlEnableInvoker(tbxOrderNumber, true); UiInvoker.ControlEnableInvoker(tbxOrderPosition, true); UiInvoker.ControlEnableInvoker(tabControlSelection, true); } #endregion --------------------------------------- Tools ------------------------------------------------------ #region ------------------------------------------ Threads and Tasks ------------------------------------------ /// /// Upload FW-Update Safe to DB Task. /// /// /// - Initial /// 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(); }); } /// /// Start FW-Update Safe Builder Task. /// /// /// - Initial /// /// /// - Error process state introduced. /// 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.StrFwUpdateSafeBuildSuccessfully} \"{_fwUpdateSafeName}\""; LogText(StrSeparator); LogSuccessText(msg); LogText(StrSeparator); MessageBoxShow($"{Resources.StrFwUpdateSafeBuildSuccessfully}\n\n \"{_fwUpdateSafeName}\"", Resources.StrSuccess); _processState = ProcessState.Idle; } else { var msg = $"{Resources.StrFwUpdateSafeBuildFailed}"; LogText(StrSeparator); LogErrorText(msg); LogText(StrSeparator); MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _processState = ProcessState.Error; } EnableInputsInvoked(); }); } /// /// Establish the DB connection. /// /// /// - Initial /// /// /// - dbAccessLocked introduced. /// /// /// - Error process state introduced. /// 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; }); } /// /// Acquire all FW-Update packages. /// /// /// - Initial /// /// /// - dbAccessLocked introduced. /// /// /// - Error process state introduced. /// 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; }); } /// /// Check the software license. /// /// /// - Initial /// 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; }); } /// /// Acquire all FW packages from DB. /// /// /// - Initial /// /// /// - Load configuration files from DB as the latest MeterFwUpdateRuler.json is needed for the /// FwUpdateBuilder FW package check and validation routine! This step has to be done in advance /// to the FW package load, as the Builder uses the information of the MeterFwUpdateRuler to /// create the package information! /// private void LoadDbCordonelFwPackagesTask() { _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(); // has to be done first to extract the MeterFwUpdateRuler GetFwUpdateConfigFilesFromDb(); // load the packages and use the MeterFwUpdateRuler to assign the DN, EMEA, NA, 868, 433.. GetCordonelFwPackagesFromDb(); } catch (Exception) { _processState = ProcessState.Error; } }).ContinueWith(delegate { _fwPackagePropertyChanged = true; _processState = ProcessState.Idle; }); } /// /// Acquire all report files from DB. /// /// /// - Initial /// private void LoadDbReportFilesTask() { _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(); GetReportFilesFromDb(); } catch (Exception) { _processState = ProcessState.Error; } }).ContinueWith(delegate { _fwReportFilesPropertyChanged = true; _processState = ProcessState.Idle; }); } /// /// Acquire all FW-Update safes from DB. /// /// /// - Initial /// private void LoadDbOutstandingFwUpdateSafesTask() { _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(); GetOutstandingFwUpdateSafesFromDb(); } catch (Exception) { _processState = ProcessState.Error; } }).ContinueWith(delegate { _fwUpdateSafesPropertyChanged = true; _processState = ProcessState.Idle; }); } /// /// Acquire all Customers specified by search mask from DB. /// /// /// - Initial /// /// /// - dbAccessLocked introduced. /// /// /// - Error process state introduced. /// 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; }); } /// /// Acquire all Cordonel serial numbers of a specific production number. /// /// /// - Initial /// /// /// - dbAccessLocked introduced. /// /// /// - Error process state introduced. /// 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; }); } /// /// Acquire all customer specific production orders from DB. /// /// /// - Initial /// /// /// - Error process state introduced. /// 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 -------------------------------------- /// /// 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! /// /// /// /// /// - Initial. /// /// /// - Search column index starting with 0. /// /// /// - User search from Cordonel FW-Packages as this contains lot more selected information than the clustered /// file. /// private void gridViewFwPackagesSelection_CellClick(Object sender, DataGridViewCellEventArgs e) { try { if (e.ColumnIndex < 0 || e.RowIndex < 0 || _fwUpdatePackages == null) { return; } // the e.RowIndex is referenced to the (sorted) data grid view var fwPackage = new CordonelFirmware(); for (var columnIndex = 0; columnIndex < gridViewFwPackageSelection.ColumnCount; columnIndex++) { if (gridViewFwPackageSelection.Columns[columnIndex].Name == Resources.StrTableCordonelFwToInstall) fwPackage.ReleaseNameVersion = gridViewFwPackageSelection.Rows[e.RowIndex].Cells[columnIndex].Value.ToString(); } // select FW package if (gridViewFwPackageSelection.Columns[e.ColumnIndex].Name == Resources.StrTableSelect) { // Search the FW package name in data table, select if found foreach (var fw in _cordonelFwPackageInfoSearch) { if (fw.ReleaseNameVersion == fwPackage.ReleaseNameVersion) { // toggle selection and set information on main screen if (fw.IsSelected) { // remove FW package from list fw.IsSelected = false; foreach (var x in _fwUpdatePackages.Where(x => x.ReleaseNameVersion == fw.ReleaseNameVersion)) { _fwUpdatePackages.Remove(x); break; } } else { // add selected package to list if not already in list if (_fwUpdatePackages.All(x => x.ReleaseNameVersion != fw.ReleaseNameVersion)) _fwUpdatePackages.Add(fwPackage); fw.IsSelected = true; } //search FW package in data table for update for (var idx = 0; idx < _dataTableFwPackages.Rows.Count; idx++) { var dataRow = _dataTableFwPackages.Rows[idx]; if (fw.ReleaseNameVersion != (String)dataRow[Resources.StrTableCordonelFwToInstall]) continue; dataRow[Resources.StrTableSelect] = fw.IsSelected; break; } break; } // FW package found } // Cordonel search list files } _fwPackagePropertyChanged = false; } catch (Exception) { _processState = ProcessState.Error; } } /// /// Build data grid for FW-Update packages /// /// /// - Initial. /// /// /// - User search from Cordonel FW-Packages as this contains lot more selected information than the clustered /// file. /// /// /// - Search extended to metrology. /// 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.StrTableCordonelFwIsLatest, typeof(Boolean)); _dataTableFwPackages.Columns.Add(Resources.StrTableCordonelFwIsReleased, typeof(Boolean)); _dataTableFwPackages.Columns.Add(Resources.StrTableCordonelFwVersion, typeof(String)); _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)); try { foreach (var fw in _cordonelFwPackageInfoSearch) { // pre-selection of displayed FW packages var requiredCoreVersion = 0; if (cbxFwPackageSearchCore.Text != @"*") { var version = cbxFwPackageSearchCore.Text; version = version.Replace(".", ""); Int32.TryParse(version, out requiredCoreVersion); } if (fw.Region != cbxFwPackageRegionMask.Text && cbxFwPackageRegionMask.Text != @"*" || fw.RadioFrequencyMhz != cbxFwPackageRadioMask.Text && cbxFwPackageRadioMask.Text != @"*" || fw.MeterSize != cbxFwPackageSizeMask.Text && cbxFwPackageSizeMask.Text != @"*" || (fw.CoreVersionMin > requiredCoreVersion || requiredCoreVersion > fw.CoreVersionMax) && cbxFwPackageSearchCore.Text != @"*" || fw.MetrologyVersion != cbxFwPackageSearchMetrology.Text && cbxFwPackageSearchMetrology.Text != @"*") continue; var row = _dataTableFwPackages.NewRow(); row[Resources.StrTableSelect] = fw.IsSelected; row[Resources.StrTableCordonelFwToInstall] = fw.ReleaseNameVersion; row[Resources.StrTableCordonelFwIsLatest] = fw.FileIsLatest; row[Resources.StrTableCordonelFwIsReleased] = fw.FwIsReleased; row[Resources.StrTableCordonelFwVersion] = fw.ReleaseVersion; row[Resources.StrTableRegion] = fw.Region; row[Resources.StrTableRadioFrequency] = fw.RadioFrequencyMhz; row[Resources.StrTableCordonelMetrology] = fw.MetrologyVersion; row[Resources.StrTableFwPackageCoreMin] = $"{fw.CoreVersionMin / 100}.{fw.CoreVersionMin % 100}"; row[Resources.StrTableFwPackageCoreMax] = $"{fw.CoreVersionMax / 100}.{fw.CoreVersionMax % 100}"; row[Resources.StrTableCordonelDiameter] = fw.MeterSize; row[Resources.StrTableReleaseDate] = fw.ReleaseDate.Date; _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 = true; } } #endregion --------------------------------------- Data Grid FW Packages --------------------------------------- #region ------------------------------------------ Data Grid Update Safes -------------------------------------- /// /// Build data grid for FW-Update update safes /// /// /// - Initial. /// private void FillDataGridWithFwUpdateSafesInfos() { } #endregion --------------------------------------- Data Grid Update Safes -------------------------------------- #region ------------------------------------------ Data Grid Report Files -------------------------------------- /// /// Build data grid for FW-Update report files /// /// /// - Initial. /// private void FillDataGridWithReportFilesInfos() { } #endregion --------------------------------------- Data Grid Report Files -------------------------------------- #region ------------------------------------------ Data Grid Pre Build 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! /// /// /// /// /// - Initial. /// /// /// - Production pre select infos from production search infos. /// 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.RequiredRelease = gridViewPreBuildSummary.Rows[e.RowIndex].Cells[columnIndex].Value.ToString(); } // select FW package with combo box if (gridViewPreBuildSummary.Columns[e.ColumnIndex].Name == Resources.StrTableCordonelFwToInstall) { // take the actual cell value as default _cbxFwReleaseSelection.Text = cordonelDeviceInfo.RequiredRelease; // Adding combo box control into DataGridView gridViewPreBuildSummary.Controls.Add(_cbxFwReleaseSelection); // Rectangular area that represents the display area for a cell var location = gridViewPreBuildSummary.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true); // Setting Location and size to fit within the cell _cbxFwReleaseSelection.Height = location.Height; _cbxFwReleaseSelection.Width = 200; _cbxFwReleaseSelection.Location = new Point(location.X, location.Y); // The final selection of the fw package _fwPackageCordonelPcbId = cordonelDeviceInfo.PcbId; _cbxFwReleaseSelection.DropDownClosed += CbxFwReleaseSelection_DropDownClosed; _cbxFwReleaseSelection.Visible = true; } // 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; // add cordonel to list if (cordonel.IsApproved && cordonel.IsSelected && _cordonelUpdateList.All(x => x.PcbId != cordonelDeviceInfo.PcbId)) _cordonelUpdateList.Add(cordonelDeviceInfo); //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 } // select Cordonel 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 } // select Cordonel if (gridViewPreBuildSummary.Columns[e.ColumnIndex].Name == Resources.StrTableSelect) { // Search the cordonel in the production pre selected list foreach (var cordPre in _cordonelProductionPreSelectInfos) { if (cordPre.PcbId == cordonelDeviceInfo.PcbId) { // toggle selection and set information on main screen if (cordPre.IsSelected) { // remove cordonel from list cordPre.IsSelected = false; foreach (var x in _cordonelUpdateList.Where(x => x.PcbId == cordonelDeviceInfo.PcbId)) { _cordonelUpdateList.Remove(x); break; } } else { // add cordonel to list if (cordPre.IsApproved && _cordonelUpdateList.All(x => x.PcbId != cordonelDeviceInfo.PcbId)) _cordonelUpdateList.Add(cordonelDeviceInfo); cordPre.IsSelected = true; } //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 (cordPre.PcbId != (String)dataRow[Resources.StrTableCordonelPcbId]) continue; dataRow[Resources.StrTableSelect] = cordPre.IsSelected; break; } _cordonelsPropertyChanged = true; break; }// Cordonel found }// Search the cordonel in the production list } PreBuildSummaryInformationStyleSet(); } catch (Exception) { _processState = ProcessState.Error; } } /// /// Build data grid for pre build summary /// /// /// - Initial. /// /// /// - Production pre select infos from production search infos. /// /// /// - Fill search masks of GFW packages for core revision, diameter, radio, region and metrology based /// on selected Cordonels /// private void FillDataGridWithPreBuildSummaryInfos() { grpLanguageSelection.Enabled = false; _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.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)); try { foreach (var cordonel in _cordonelProductionPreSelectInfos) { // fill search masks for FW package selection if (!string.IsNullOrEmpty(cordonel.Diameter) && cordonel.Diameter != "?" && !_sizeSearchItems.Contains(cordonel.Diameter)) { _sizeSearchItems.Add(cordonel.Diameter); } // fill search masks for FW package selection if (!string.IsNullOrEmpty(cordonel.RadioFrequency) && cordonel.RadioFrequency != "?" && !_radioSearchItems.Contains(cordonel.RadioFrequency)) { _radioSearchItems.Add(cordonel.RadioFrequency); } // fill search masks for FW package selection if (!string.IsNullOrEmpty(cordonel.Region) && cordonel.Region != "?" && !_regionSearchItems.Contains(cordonel.Region)) { _regionSearchItems.Add(cordonel.Region); } // fill search masks for FW package selection if (!string.IsNullOrEmpty(cordonel.CoreRevision) && cordonel.CoreRevision != "?" && !_coreSearchItems.Contains(cordonel.CoreRevision)) { _coreSearchItems.Add(cordonel.CoreRevision); } // fill search masks for FW package selection if (!string.IsNullOrEmpty(cordonel.Metrology) && cordonel.Metrology != "?" && !_metrologySearchItems.Contains(cordonel.Metrology)) { _metrologySearchItems.Add(cordonel.Metrology); } // search if this Cordonel is already in cordonelDeviceInfo list as this is the marker // to update it and a new selection may overwrite the update (IsSelected == true) status if (_cordonelUpdateList.Any(x => x.PcbId == cordonel.PcbId)) cordonel.IsSelected = true; var row = _dataTableSummary.NewRow(); row[Resources.StrTableSelect] = cordonel.IsSelected; row[Resources.StrTableApprove] = cordonel.IsApproved; row[Resources.StrTableRemove] = cordonel.IsRemoved; row[Resources.StrTableCordonelCustomerSerialNumber] = cordonel.CustomerSerialNumber ?? "?"; row[Resources.StrTableCordonelPcbId] = cordonel.PcbId ?? "?"; row[Resources.StrTableCordonelFwToInstall] = cordonel.RequiredReleaseNameVersion ?? "?"; row[Resources.StrTableCordonelMetrology] = cordonel.Metrology ?? "?"; row[Resources.StrTableCordonelMetrologyIsUpdateable] = cordonel.MetrologyIsUpdateable; row[Resources.StrTableCordonelCore] = cordonel.CoreRevision ?? "?"; row[Resources.StrTableRegion] = cordonel.Region ?? "?"; row[Resources.StrTableRadioFrequency] = cordonel.RadioFrequency ?? "?"; row[Resources.StrTableCordonelDiameter] = cordonel.Diameter ?? "?"; row[Resources.StrTableReason] = cordonel.ReasonForUpdateDenied ?? ""; _dataTableSummary.Rows.Add(row); } gridViewPreBuildSummary.DataSource = _dataTableSummary; foreach (DataGridViewColumn column in gridViewPreBuildSummary.Columns) { column.SortMode = DataGridViewColumnSortMode.Automatic; } // color the results of the compare PreBuildSummaryInformationStyleSet(); } catch (Exception) { _processState = ProcessState.Error; } finally { grpLanguageSelection.Enabled = true; } } /// /// Color the rows depending on the state /// /// /// - Initial. /// 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 != "?") { 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; } } } /// /// After sort event to update styles /// /// /// - Initial. /// private void gridViewPreBuildSummary_Sorted(Object sender, EventArgs e) { PreBuildSummaryInformationStyleSet(); } #endregion --------------------------------------- Data Grid Pre Build Summary -------------------------------- #region ------------------------------------------ Data Grid Cordonels ---------------------------------------- /// /// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for /// Cordonel selection. Multiple Cordonels can be selected! /// /// /// /// /// - Initial. /// /// /// - Idx search index started with 0 instead of 1. /// /// /// - PCB Id may not always be unambiguously (Sensus GmbH Hannover PCB Id 19080071), /// - Improved search speed. /// /// /// - Check data table assignment. /// /// /// - Added FW release picker, /// - Search column index starting with 0. /// /// /// - Production pre select infos from production search infos. /// 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(); // add here the required release if (gridViewCordonelSelection.Columns[columnIndex].Name == Resources.StrTableCordonelFwToInstall) cordSelect.RequiredReleaseNameVersion = gridViewCordonelSelection.Rows[e.RowIndex].Cells[columnIndex].Value.ToString(); } // select FW package with combo box if (gridViewCordonelSelection.Columns[e.ColumnIndex].Name == Resources.StrTableCordonelFwToInstall) { // take the actual cell value as default _cbxFwReleaseSelection.Text = cordSelect.RequiredReleaseNameVersion; // Adding combo box control into DataGridView gridViewCordonelSelection.Controls.Add(_cbxFwReleaseSelection); // Rectangular area that represents the display area for a cell var location = gridViewCordonelSelection.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true); // Setting Location and size to fit within the cell _cbxFwReleaseSelection.Height = location.Height; _cbxFwReleaseSelection.Width = 200; _cbxFwReleaseSelection.Location = new Point(location.X, location.Y); // The final selection of the fw package _fwPackageCordonelPcbId = cordSelect.PcbId; _cbxFwReleaseSelection.DropDownClosed += CbxFwReleaseSelection_DropDownClosed; _cbxFwReleaseSelection.Visible = true; } // 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 from list cordSearch.IsSelected = 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; if (_cordonelProductionPreSelectInfos.All(x => x.PcbId != cordSelect.PcbId)) _cordonelProductionPreSelectInfos.Add(cordSearch); _preBuildSummaryPropertyChanged = true; } //search cordonel in data table for update of "select" 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; break; } break; }// Cordonel found }// Search the cordonel in the production search list } _cordonelsPropertyChanged = false; // _preBuildSummaryPropertyChanged = false; } catch (Exception) { _processState = ProcessState.Error; } } /// /// Event on closing the fw package selection /// /// /// /// /// - Initial /// /// /// - Pre build summary changed /// /// /// - Production pre select infos from production search infos. /// private void CbxFwReleaseSelection_DropDownClosed(Object sender, EventArgs e) { _cbxFwReleaseSelection.DropDownClosed -= CbxFwReleaseSelection_DropDownClosed; if (!string.IsNullOrEmpty(_fwPackageCordonelPcbId) && _cbxFwReleaseSelection.SelectedItem != null) { // attach fw update package name to eventually unselected in production search list Cordonels foreach (var cordonel in _cordonelProductionSearchInfos.Where(cordonel => cordonel.PcbId == _fwPackageCordonelPcbId)) { cordonel.RequiredReleaseNameVersion = _cbxFwReleaseSelection.SelectedItem.ToString(); break; } // attach fw update package name in production pre selected list Cordonels foreach (var cordonel in _cordonelProductionPreSelectInfos.Where(cordonel => cordonel.PcbId == _fwPackageCordonelPcbId)) { cordonel.RequiredReleaseNameVersion = _cbxFwReleaseSelection.SelectedItem.ToString(); break; } // attach fw update package name in update list Cordonels foreach (var cordonel in _cordonelUpdateList.Where(cordonel => cordonel.PcbId == _fwPackageCordonelPcbId)) { cordonel.RequiredRelease = _cbxFwReleaseSelection.SelectedItem.ToString(); break; } } _cbxFwReleaseSelection.Visible = false; _cordonelsPropertyChanged = true; _preBuildSummaryPropertyChanged = true; } /// /// Build data grid for cordonels /// /// /// - Initial. /// /// /// - Order position added. /// /// /// - New data tables, important for language change. /// /// /// - Added region and radio frequency. /// /// /// - Added customer serial number search. /// 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.StrTableCordonelCustomerSerialNumber, typeof(String)); _dataTableCordonels.Columns.Add(Resources.StrTableCordonelPcbId, typeof(String)); _dataTableCordonels.Columns.Add(Resources.StrTableCordonelFwToInstall, 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 != @"*") 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; break; } var row = _dataTableCordonels.NewRow(); row[Resources.StrTableSelect] = cordSearch.IsSelected; row[Resources.StrTableCordonelCustomerSerialNumber] = cordSearch.CustomerSerialNumber ?? "?"; row[Resources.StrTableCordonelPcbId] = cordSearch.PcbId ?? "?"; row[Resources.StrTableCordonelFwToInstall] = cordSearch.RequiredReleaseNameVersion ?? "?"; row[Resources.StrTableCordonelInstalledFw] = cordSearch.InstalledReleaseNameVersion ?? "?"; row[Resources.StrTableCordonelMetrology] = cordSearch.Metrology ?? "?"; row[Resources.StrTableCordonelMetrologyIsUpdateable] = cordSearch.MetrologyIsUpdateable; row[Resources.StrTableCordonelCore] = cordSearch.CoreRevision ?? "?"; row[Resources.StrTableRegion] = cordSearch.Region ?? "?"; row[Resources.StrTableRadioFrequency] = cordSearch.RadioFrequency ?? "?"; row[Resources.StrTableCordonelDiameter] = cordSearch.Diameter ?? "?"; row[Resources.StrTableCordonelProductionOrder] = cordSearch.CustomerOrderNumber; row[Resources.StrTableCordonelProductionPos] = cordSearch.CustomerOrderPos; row[Resources.StrTableCordonelSize] = cordSearch.Length ?? "?"; row[Resources.StrTableCordonelCatalogueNumber] = cordSearch.CatalogNumber ?? "?"; _dataTableCordonels.Rows.Add(row); } gridViewCordonelSelection.DataSource = _dataTableCordonels; foreach (DataGridViewColumn column in gridViewCordonelSelection.Columns) { column.SortMode = DataGridViewColumnSortMode.Automatic; } } catch (Exception) { _processState = ProcessState.Error; } finally { grpLanguageSelection.Enabled = true; } } #endregion --------------------------------------- Data Grid Cordonels ---------------------------------------- #region ------------------------------------------ Data Grid Controls Update Operators ------------------------ /// /// 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. /// /// /// /// /// - Initial. /// /// /// - Check for e.RowIndex below 0. /// /// /// - Removed validation date for user as it should not be set by the FwUpdateBuilder operator, /// - Avoid selection of outdated users. /// /// /// - Embedded in try catch block, /// - used data grid as cell reference to allow data grid sorting without refresh of data source! /// /// /// - Idx search index started with 0 instead of 1. /// /// /// - Check data table assignment. /// /// /// - Removed _userInfo. /// /// /// - Search column index starting with 0. /// /// /// - Added _updateOperatorId needed to assign the fwUpdateSafeDb. /// 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 UpdateUpdateOperatorsInformationStyleSet(); } catch (Exception) { _processState = ProcessState.Error; } } /// /// Build data grid for all fully qualified users /// /// /// - Initial. /// /// /// - Disable language change during update. /// /// /// - New data tables, important for language change. /// /// /// - Added PC name. /// /// /// - Registration and valid date to date time. /// 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 UpdateUpdateOperatorsInformationStyleSet(); } catch (Exception) { _processState = ProcessState.Error; } finally { grpLanguageSelection.Enabled = true; } } /// /// Color the rows depending on the state /// /// /// - Initial. /// private void UpdateUpdateOperatorsInformationStyleSet() { 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; } } } /// /// After sort event to update styles /// /// /// - Initial. /// private void gridViewUpdateOperators_Sorted(Object sender, EventArgs e) { UpdateUpdateOperatorsInformationStyleSet(); } #endregion --------------------------------------- Data Grid Controls Update Operators ------------------------ #region ------------------------------------------ Data Grid Controls Customer Selection ---------------------- /// /// Overwrite cell click, because edit of cells is denied (read only == true). This is needed for customer /// selection. Exclusively one customer can be selected. /// /// /// /// /// - Initial. /// /// /// - Idx search index started with 0 instead of 1. /// - Clear list of cordonels on new customer. /// /// /// - clear production order numbers. /// /// /// - Check data table assignment. /// /// /// - Search column index starting with 0. /// /// /// - Production pre select infos from production search infos. /// private void gridViewCustomerSelection_CellClick(Object sender, DataGridViewCellEventArgs e) { try { if (e.ColumnIndex < 0 || e.RowIndex < 0 || _dataTableCustomers == null) { return; } // 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; } } /// /// Build data grid for selected customers. /// /// /// - Initial. /// /// /// - New data tables, important for language change. /// /// /// - Initially sorted by name. /// 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 = true; } } #endregion --------------------------------------- Data Grid Controls Customer Selection ---------------------- #region ------------------------------------------ Load DB Contents ------------------------------------------- /// /// Get the Cordonel customer orders from DB. /// /// true if successful /// /// - Initial. /// /// /// - Getting orders directly from DB. /// 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; } /// /// Get the Cordonel serial numbers from DB. /// /// /// - Initial. /// /// /// - App versions added. /// /// /// - Size and length applied to DN50, US2, 2" and 220 mm. /// /// /// - Added radio frequency. /// /// /// - Clear search info on new search. /// 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.ConvertOrderSizeNumberToSizeName(order.Diameter), Length = 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 { // this is always a number like 1.23 or 10.2D or 60.2D, a msb higher 5 is // indicating "B" version var msb = appVersion.Version.Substring(0, 1); var lsb = appVersion.Version.Substring(1, appVersion.Version.Length - 1); if (Int16.TryParse(msb, out var msbNumber)) { if (msbNumber >= 6) { msbNumber -= 5; cordonel.InstalledReleaseNameVersion = $"B{msbNumber}{lsb}"; } else cordonel.InstalledReleaseNameVersion = "R" + appVersion.Version; } } } } cordonel.Region = string.IsNullOrEmpty(cordonel.RadioFrequency) ? "NA" : "EMEA"; _cordonelProductionSearchInfos.Add(cordonel); } } } }// orders from customer could be acquired _processState = ProcessState.Idle; } /// /// Get the Cordonel customers from DB. /// /// /// - Initial. /// /// /// - Search pattern added. /// private void GetAllCordonelCustomersFromDb() { var searchPattern = tbxCustomerSearchMask.Text.Replace(" ", ""); _fwUpdateDbAccess?.GetAllCordonelCustomersFromDb(searchPattern); _processState = ProcessState.Idle; } /// /// Get Cordonel FW packages from DB end extract all needed information to select a package to the /// CordonelFwPackageInfo search list. /// /// /// - Initial. /// /// /// - Meter FW update ruler. /// /// /// - Configuration files loading from DB: - MeterFwUpdateRuler, configuration.json and MeterEraseRestore. /// private void GetCordonelFwPackagesFromDb() { if (_fwUpdateDbAccess == null || !_fwUpdateDbAccess.GetAllCordonelFwInfoFilesFromDb() || _fwUpdateDbAccess.DbCordonelFwPackages.Count == 0 || _cordonelFwPackageInfoSearch == null) return; _cordonelFwPackageInfoSearch.Clear(); // 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). // GetConfigurationFilesFromDb(); // Start with the last package, as this will be the latest for (var idx = _fwUpdateDbAccess.DbCordonelFwPackages.Count - 1; idx > _fwUpdateDbAccess.DbCordonelFwPackages.Count - 40; idx--) { // read the package without the content, skip all if fwPackageFilesInfo returns null var dbCordonelFwFile = _fwUpdateDbAccess.DbCordonelFwPackages[idx]; var fileId = dbCordonelFwFile.FileId; // Split to PROD_EMEA_433_DN50_R101D var nameSplit = dbCordonelFwFile.FileName.Split('_'); if (_fwUpdateDbAccess.GetCordonelFwPackageFromDb(fileId, out var fwPackageFilesInfo, false) && fwPackageFilesInfo != null && fwPackageFilesInfo.Count > 0) { // parse file information if (dbCordonelFwFile.CordonelFwFile_Dn != null) { var fwPackInfo = new CordonelFwPackageInfo { ReleaseNameVersion = dbCordonelFwFile.FileName, Region = dbCordonelFwFile.FileName.Contains("EMEA") ? "EMEA" : "NA", RadioFrequencyMhz = dbCordonelFwFile.FileName.Contains("433") ? "433" : dbCordonelFwFile.FileName.Contains("868") ? "868" : "-", FileIsLatest = dbCordonelFwFile.FileIsLatest, MeterSize = MeterSizeConverter.ConvertOrderSizeNumberToSizeName( MeterSizeConverter.ConvertToOrder((MeterSize)dbCordonelFwFile.CordonelFwFile_Dn)), ReleaseDate = dbCordonelFwFile.FileDate, ReleaseVersion = $"{nameSplit[nameSplit.Length - 1].Substring(0, 3)}." + $"{nameSplit[nameSplit.Length - 1].Substring(3, 2)}" }; // the release version is always the last element of the name split foreach (var binfile in fwPackageFilesInfo) { // extract metrology version if (binfile.FileName.Contains("binfile0F_")) { var msb = binfile.FileName.Substring(10, 2); // remove leading 0 var msbMsb = msb.Substring(0, 1); if (msbMsb == "0") msb = msb.Remove(0, 1); var lsb = binfile.FileName.Substring(12, 2); fwPackInfo.MetrologyVersion = $"{msb}.{lsb}"; break; } } // take the meter fw update ruler to extract core min /max and released foreach (var rule in _meterFwUpdateRuler) { var releaseVersion = $"{rule.Release.Substring(0, 3)}." + $"{rule.Release.Substring(3, 2)}"; if (releaseVersion == fwPackInfo.ReleaseVersion) { int.TryParse(rule.CoreVersionMin, out var x); fwPackInfo.CoreVersionMin = x; int.TryParse(rule.CoreVersionMax, out x); fwPackInfo.CoreVersionMax = x; fwPackInfo.FwIsReleased = !string.IsNullOrEmpty(rule.ApproverName); } } _cordonelFwPackageInfoSearch.Add(fwPackInfo); } } } } /// /// Get the license information from DB. In DEBUG mode the license of the software will be skipped, /// but user license is of importance. /// /// /// - Initial. /// private void GetLicenseFromDb() { // FW-Update Builder validation with DB access var access = false; var msg = $"{Resources.StrStartMessageSwLicenseExpired}\n\n{_versionString}"; try { 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(); } /// /// Upload the generated FW Update safe to DB. /// /// true if successful /// /// - Initial. /// private Boolean UploadFwUpdateSafeToDb() { var pcbIds = _cordonelUpdateList.Select(pcb => pcb.PcbId).ToList(); return _fwUpdateDbAccess != null && _fwUpdateDbAccess.UploadFwUpdateSafeToDb(_fwUpdateSafeDb, pcbIds); } /// /// Get all FW update report files from DB. /// /// /// - Initial. /// private void GetReportFilesFromDb() { var fwUpdateReports = new List(); _fwUpdateDbAccess?.DownloadFwUpdateReportsFromDb(out fwUpdateReports); } /// /// Get all active FW update safes from DB. Outdated safes cannot be accessed. /// /// /// - Initial. /// private void GetOutstandingFwUpdateSafesFromDb() { var fwUpdateSafes= new List(); if (_fwUpdateDbAccess != null) { foreach (var user in _fwUpdateDbAccess.DbFullQualifiedUpdateOperators) { _fwUpdateDbAccess.ListAllFwUpdateSafesOfUserFromDb(user.Id, out var fwUpdateSafesOfUser); fwUpdateSafes.AddRange(fwUpdateSafesOfUser); } } } /// /// Get the full qualified user information from DB to generate the primary key for encryption. /// /// /// - Initial. /// /// /// - DB access. /// /// /// - Call of get all users from DB. /// /// /// - Removed DB status. /// private void GetAllUpdateOperatorsFromDb() { _fwUpdateDbAccess?.GetAllUpdateOperatorsFromDb(); } /// /// 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! /// /// /// - Initial. /// /// /// - Get fully qualified user information from DB searched by the users FullName. /// /// /// - Replaced userInfo by name of update operator. /// 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); } /// /// Collect order number, radio address, skeleton key, password hashes and passwords from DB. /// /// /// password container /// /// - Initial. /// /// /// - Returns bool. /// /// /// - Check content of password container. /// /// /// - Using software access helper class. /// /// /// - Exported base function to FwUpdateDb. /// /// /// - Retries on missing DB connection or reading of file failed. /// /// /// - Password container directly assigned. /// private Boolean GetPwdFromDb(CordonelDeviceInfo cordonel) { 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); return false; } /// /// Get Cordonel Fa update configuration files from DB. /// 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). /// Copies the MeterFwUpdateRuler.json to the [exe-root] or [exe-root]/Library of the FwUpdateBuilder. /// /// /// - Initial. /// private void GetFwUpdateConfigFilesFromDb() { if (_fwUpdateDbAccess == null || !_fwUpdateDbAccess.DownloadFwUpdateConfigurationFilesFromDb() || _fwUpdateDbAccess.DbFwUpdateConfigFiles.Count == 0) return; try { // extract the MeterFwUpdateRuler foreach (var f in _fwUpdateDbAccess.DbFwUpdateConfigFiles) { if (f.FileName == FwUpdateConfig.MeterFwUpdateRulerConfigFileName) { // Copy MeterFwUpdateRuler either to exe-path or if existing to Library-subfolder var fwUpdateBuilderExePath = AppDomain.CurrentDomain.BaseDirectory; var fwUpdateBuilderLibraryPath = Path.Combine(fwUpdateBuilderExePath, FwUpdateConfig.LibrarySubFolderName); // check the Library path existence (will be used in Release build) var meterFwUpdateRulerPath = Directory.Exists(fwUpdateBuilderLibraryPath) ? fwUpdateBuilderLibraryPath : fwUpdateBuilderExePath; var sourceFile = Path.Combine(meterFwUpdateRulerPath, f.FileName); var fs = File.Open(sourceFile, FileMode.Create); fs.Write(f.FileContent, 0, f.FileContent.Length); fs.Close(); } } } catch (Exception) { // nothing to do } } /// /// 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 SubDiractories. /// /// license information of valid software /// true if successful /// /// - Initial. /// private Boolean BuildFwUpdateSwContainerFromDb(SoftwareLicense fwUpdateSwLicense) { 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(), 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; } /// /// Build the FW-Update SW container /// /// true if successful /// /// - Init. /// /// /// - User license builder. /// /// /// - Changed return signature. /// /// /// - Take license from DB. /// // 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(), 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(); 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(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(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(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(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; } /// /// Build the license information for any FW. /// /// /// /// /// /// - Initial. /// /// /// - Software license element naming adapted to DB content. /// /// /// - Software license flexible for different software. /// 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 }; } /// /// Set the FW-Update SW license information to DB /// /// /// /// - Initial. /// /// /// - Modified for flexible licenses. /// 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); } } /// /// Get the FW-Update SW license information to DB /// /// /// /// /// - Initial with simulated data. /// /// /// - Modified for flexible licenses. /// /// /// - License assigned in DB access. /// /// /// - Messages removed. /// private void GetFwUpdateSwLicenseFromDb(String programName, out SoftwareLicense license) { if (_fwUpdateDbAccess != null && _fwUpdateDbAccess.GetSwLicenseFromDb(programName, out license)) return; license = null; } /// /// Load a single firmware package from DB. /// /// each Cordonel tells wich release to load /// true if succeeded /// /// - Initial. /// /// /// - Removed configuration.json from FW package as this is part of the SW package. /// private Boolean GetFwPackageFromDb(String releaseName) { // if this packages has already been loaded, exit immediately if (_fwUpdatePackages.Any(package => package.ReleaseNameVersion == releaseName && package.BinaryApplicationFiles != null && package.BinaryApplicationFiles.Count > 0)) { return true; } String msg; // search file id in ClusteredFiles but as the name in unambiguous and files are stored several times // with this file name, the last and therefor latest item has to be taken if (_fwUpdateDbAccess?.DbCordonelFwPackages != null) { var fileId = (from cu in _fwUpdateDbAccess.DbCordonelFwPackages where cu.FileName == releaseName select cu.FileId).LastOrDefault(); if (_fwUpdateDbAccess.GetCordonelFwPackageFromDb(fileId, out var fwPackage)) { try { // search the cordonel firmware foreach (var fw in _fwUpdatePackages.Where(fw => fw.ReleaseNameVersion == releaseName)) { fw.PackageDescriptionFile = new FilePart(); fw.BinaryApplicationFiles = new List(); foreach (var f in fwPackage) { // load the ADF if (f.FileName.Contains("product") && f.FileName.EndsWith(".txt")) { fw.PackageDescriptionFile.FileName = f.FileName; fw.PackageDescriptionFile.FileContent = new Byte[f.FileContent.Length]; fw.PackageDescriptionFile.FileContent = f.FileContent.ToArray(); } // load the binaries 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(); fw.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 --------------------------------------------------- /// /// Select language at runtime: English /// /// /// - Initial /// /// /// - Change menu. /// /// /// - Avoid repetition if current culture is equal to required. /// /// /// - Backup duty date for FW-Update during language change. /// private void RadioBtnEnglishLanguage_Click(Object sender, EventArgs e) { if (Thread.CurrentThread.CurrentCulture.Name == "en-GB") { return; } _cultureInfo = new CultureInfo("en-GB"); ChangeLanguageControls(); } /// /// Select language at runtime: German /// /// /// - Initial /// /// /// - Change menu. /// /// /// - Avoid repetition if current culture is equal to required. /// /// /// - Backup duty date for FW-Update during language change. /// private void RadioBtnGermanLanguage_Click(Object sender, EventArgs e) { if (Thread.CurrentThread.CurrentCulture.Name == "de-DE") { return; } _cultureInfo = new CultureInfo("de-DE"); ChangeLanguageControls(); } /// /// Change language at runtime /// /// /// - Initial based on code example /// https://stackoverflow.com/questions/52178064/winforms-localization-how-to-change-the-language-of-a-menu. /// /// /// - Update selected table headers of tab control. /// /// /// - Restore duty date for FW-Update after language change. /// 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(this.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; _fwReportFilesPropertyChanged = true; _fwUpdateSafesPropertyChanged = true; _preBuildSummaryPropertyChanged = true; lblFwUpdateDutyDate.Text = _datePicker.Value.ToShortDateString(); } #endregion --------------------------------------- Language --------------------------------------------------- } }