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 Logic.ProductionToProductMapper.Files.Fw; using Newtonsoft.Json; using NLog; using Xylem.Common.CommonCore.Consts; using Xylem.Common.CommonCore.ThreadWatcher; using Xylem.Common.Cryptology.Security; using Xylem.Common.Utils.AssemblyLoader; using Xylem.Common.Utils.Crc16Ccitt; using Xylem.Common.Utils.Logging; using Xylem.Common.Utils.DateTimeServer; using Xylem.Common.Utils.FileIo; using Xylem.Common.Utils.UiLanguageControl; using Xylem.Common.Utils.UiInvoker; using Xylem.ServiceFwUpdate.Common.FwUpdateConfig.Consts; using Xylem.ServiceFwUpdate.Common.FwUpdateDb; using Xylem.ServiceFwUpdate.Common.FwUpdateSafe; using Xylem.ServiceFwUpdate.Common.FwUpdateSafe.Consts; using Xylem.ServiceFwUpdate.Ui.FwUpdateLoader.Const; using Xylem.ServiceFwUpdate.Ui.FwUpdateLoader.Properties; namespace Xylem.ServiceFwUpdate.Ui.FwUpdateLoader { /// /// Service firmware update loader main form. /// /// /// - Initial /// [Serializable] public partial class FrmFwUpdateLoader : Form { #region ------------------------------------------ Variables -------------------------------------------------- //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 ColorProcessRequired = Color.Blue; private static readonly Color ColorOngoingProcess = Color.Blue; private static readonly Color ColorUnknownStatus = Color.Gray; private const String SuccessSign = @"✔"; private const String FailedSign = @"✘"; private const String WebApiTimeout = "Web API Timeout"; private readonly CancellationTokenSource _processToken = new CancellationTokenSource(); private readonly Thread _fwUpdateLoaderThread; private AssemblyLoader _asmLoader; private DataTable _dataTablePcbIds; private String _backupSafeNameForLanguageChange; // 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; /// /// DB connection trial timer /// private const Int32 DbConnectionRetryDelayMs = 30000; /// /// DB connection trial timer /// private const Int32 DbConnectionTimeoutMs = 9000; private Int32 _dbAccessDelayCtrMs; /// /// Avoid DB connection trial on ongoing DB access, FW-Update procedure or registration /// private Boolean _dbAccessIsLocked; /// /// Check date time to enable FW update button. /// private Boolean _fwUpdateEnabled; /// /// List for upload app list versions. /// private readonly List _fwUpdateAppVersions = new List(); /// /// List for upload reports. /// private readonly List _fwUpdateReportFiles = new List(); /// /// 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 of the file which contains the meter file names that can be erased before the FW-Update /// to free disk space and have to be restored after the FW-Update /// private readonly String _meterFilesConfigPathName; /// /// DB access /// private readonly FwUpdateDb _fwUpdateDbAccess = new FwUpdateDb(); /// /// Name for firmware update software including the namespace /// private const String FwUpdateSwFullName = "Xylem.ServiceFwUpdate.Ui." + FwUpdateProjectName; /// /// Name for firmware update dll /// private const String FwUpdateSwDll = FwUpdateSwFullName + ".dll"; /// /// Name for firmware update form /// private const String FormFwUpdateSw = FwUpdateSwFullName + ".Frm" + FwUpdateProjectName; /// /// FW-Update SW interface for data exchange /// private const String FwUpdateSwDataExchangeInterface = "SetDataContainerJson"; /// /// Path to firmware update software DLL /// private readonly String _fwUpdateSwDestinationPath; /// /// Object reference of FW-Update SW /// private Object _fwUpdateObject; #if (USE_APP_DOMAIN) /// /// FW-Update SW application domain /// private const String AppDomainName = "AppDomain" + FwUpdateProjectName; /// /// App domain for FW-Update SW /// private AppDomain _fwUpdateSwAppDomain; #endif /// /// Logger of messages /// private readonly ILogger _logger; /// /// Separator string used for logger and history window /// private const String StrSeparator = "-----------------------------------------------------" + "-----------------------------------------------------"; /// /// remind manually changed culture setting /// private CultureInfo _cultureInfo; /// /// remind software version /// private readonly Version _version; /// /// FW-Update Safe /// private FwUpdateSafe _fwUpdateSafe; /// /// File name of FW-Update safe without extension (CustomerName_FwUpdateOrderNumber) /// private String _customerAndOrderInfo; /// /// Registered user information read from registration file. /// private UserInformation _regUser; /// /// Path to application configuration ../[user]/AppData/Roaming/Genesis/ /// private static readonly String ApplicationConfigPath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData), ProgramConfig.GenesisBaseFolder); /// /// Destination directory and file name for serial port configuration file /// private static readonly String PortConfigPathName = Path.Combine(ApplicationConfigPath, ProgramConfig.SerialConfigFileName); /// /// Path and file user registration /// private static readonly String UserRegistrationPathFile = Path.Combine(ApplicationConfigPath, FwUpdateConfig.UserRegistrationFileName); /// /// Path and file user NLog configuration /// private static readonly String NLogConfigurationDestPathFile = Path.Combine(ApplicationConfigPath, ProgramConfig.NlogConfig); /// /// Path for FW-Update safes /// private static readonly String FwUpdateSafePath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.UserProfile), FwUpdateConfig.DefaultFwUpdateSafePath); /// /// Path for FW-Update safes /// private static readonly String FwUpdateSafeInfoBackupPath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.UserProfile), FwUpdateConfig.FwUpdateSafeInfoBackupPath); /// /// Path and name of FW-Update safe /// private static String _fwUpdateSafePathName; /// /// Timer for cyclic status update /// private System.Windows.Forms.Timer _tmrLoaderCyclicStatus = new System.Windows.Forms.Timer(); #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 /// /// /// - Upload report files, /// - Upload app list version, /// - Download FW-Update Safes. /// /// /// - Sleep on equal process state to force suspend of actual thread. /// private void FwUpdateLoaderStateMachine() { 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: // nothing to do than to wait for state change 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.UploadReportFiles: UploadReportFilesToDbTask(); break; case ProcessState.DownloadFwUpdateSafe: DownloadFwUpdateSafesFromDbTask(); break; case ProcessState.LoadFwUpdateSafe: LoadFwUpdateSafe(); break; case ProcessState.FwUpdateProcess: StartFwUpdateProcess(); break; case ProcessState.CheckPcbIdProcessState: // catch the state but do nothing as this is a separate task, // state is going to be handled by the timer break; default: _processState = ProcessState.Idle; break; } } catch (ThreadAbortException) { throw; } catch (Exception e) { if (_fwUpdateLoaderThread.ThreadState == ThreadState.Aborted || _fwUpdateLoaderThread.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 --------------------------------------------- /// /// Check the DB connection. /// /// /// - Initial. /// /// /// - Implemented state machine. /// /// /// - Fill data grid with pcb id infos. /// /// /// - Check register exported to separate function to avoid registration before check of FwUpdateSafes /// availability on DB. /// private void TmrLoaderCyclicStatus_Tick(Object sender, EventArgs e) { Thread.CurrentThread.CurrentUICulture = _cultureInfo; Thread.CurrentThread.CurrentCulture = _cultureInfo; _dbAccessDelayCtrMs += _tmrLoaderCyclicStatus.Interval; switch (_processState) { case ProcessState.Idle: CheckDbConnectivity(); CheckFwUpdateSafeExistence(); CheckReportStatus(); CheckRegisterState(); break; case ProcessState.UploadReportFiles: case ProcessState.DownloadFwUpdateSafe: CheckDbConnectionTimeout(); SetStatusProgressBar(ProgressBarStatus.Value + 1 > 100 ? 0 : ProgressBarStatus.Value + 1); break; case ProcessState.CheckPcbIdProcessState: FillDataGridWithPcbIdInfos(); _processState = ProcessState.Idle; break; } } #endregion --------------------------------------- Timer Controls --------------------------------------------- #region ------------------------------------------ User Interaction ------------------------------------------- /// /// Start of FW-Update SW. /// /// /// - Initial /// /// /// - Exported functionality to StartFwUpdateProcess. /// private void BtnFwUpdate_Click(Object sender, EventArgs e) { if (_fwUpdateSafe == null) return; _tmrLoaderCyclicStatus.Tick -= TmrLoaderCyclicStatus_Tick; _tmrLoaderCyclicStatus.Enabled = false; _dbAccessIsLocked = true; _processState = ProcessState.FwUpdateProcess; } /// /// Register user at DB Laatzen. This can only be done if the network is connected. /// /// /// - Initial /// /// /// - Register to DB. /// /// /// - Reg user information will be sent the registration form. /// /// /// - Locked DB access during registration. /// /// /// - _fwUpdateDbAccess added, /// - new process state added. /// private void BtnRegister_Click(Object sender, EventArgs e) { // The user registration file needs to be created if it doesn't exists, // therefore the registration form has to be started. if (string.IsNullOrEmpty(UserRegistrationPathFile)) return; _processState = ProcessState.UserRegistration; _dbAccessIsLocked = true; var frmRegister = new FrmRegister(UserRegistrationPathFile, SystemControl.FilesExistingInPath(FwUpdateSafePath), _regUser, _fwUpdateDbAccess); DisableControls(); frmRegister.Show(); frmRegister.Closed += FormRegister_Closed; Hide(); } /// /// Open FW-Update safe and decrypt it. /// /// /// - Initial. /// /// /// - Exported functionality to LoadFwUpdateSafe. /// private void btnLoadFwUpdSafe_Click(Object sender, EventArgs e) { _dbAccessIsLocked = true; _processState = ProcessState.LoadFwUpdateSafe; } #endregion --------------------------------------- User Interaction ------------------------------------------- #region ------------------------------------------ Form Load Unload ------------------------------------------- /// /// Ctor /// /// /// - Initial /// /// /// - Moved all paths and file setups to this function. /// /// /// - Moved FW-Update SW directory directly to the FW-Update Loader directory, /// - Check for ../[user]/AppData/Roaming/Genesis/ folder existence and create it, /// - Copy needed files from execution directory to this directory. /// /// /// - DB offline message added. /// /// /// - Thread added. /// /// /// - Library added to NLogConfig source path, as this is the release source, copy files only if not /// identical. /// public FrmFwUpdateLoader() { var fwUpdateLoaderExePath = AppDomain.CurrentDomain.BaseDirectory; var fwUpdateLoaderConfigPath = Path.Combine(fwUpdateLoaderExePath, FwUpdateConfig.ConfigSubFolderName); // Set the FW-Update SW destination folder to the FW-Update Loader exe path _fwUpdateSwDestinationPath = Path.Combine(fwUpdateLoaderExePath, FwUpdateProjectName); // files for erase before FW-Update and restore after _meterFilesConfigPathName = Path.Combine(_fwUpdateSwDestinationPath, FwUpdateConfig.MeterFilesEraseRestoreConfigFileName); var nLogConfigSourcePathName = Path.Combine(fwUpdateLoaderConfigPath, ProgramConfig.NlogConfig); // Build the configuration directory and copy at least the NLog config to it try { if (!Directory.Exists(ApplicationConfigPath)) { Directory.CreateDirectory(ApplicationConfigPath); } // 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); // create the location for the FW-Update safes as they may be loaded from the DB if online if (!Directory.Exists(FwUpdateSafePath)) { Directory.CreateDirectory(FwUpdateSafePath); } if (!Directory.Exists(FwUpdateConfig.ReportedPath)) { Directory.CreateDirectory(FwUpdateConfig.ReportedPath); } } catch (Exception) { // nothing to do } _logger = NLogHelper.CreateOrGetLogger("ServiceFwUpdateLoader"); _version = Assembly.GetExecutingAssembly().GetName().Version; _logger.Info(StrSeparator); _logger.Info($"Service FW-Update Loader Version: {_version.Major}.{_version.Minor}.{_version.Build}.{_version.Revision}"); _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}"; DisableControls(); CheckUserRegistration(); CheckFwUpdateSafeExistence(); CheckReportStatus(); _dbAccessDelayCtrMs = DbConnectionRetryDelayMs; _dbAccessIsLocked = false; //_userAccessLocked = true; // set locked repeat initially different from process state to unlock first entry to state machine _lastProcessState = ProcessState.Unspecified; _invokerProcessState = ProcessState.Unspecified; _processState = ProcessState.Init; _fwUpdateLoaderThread = new Thread(FwUpdateLoaderStateMachine); if (!string.IsNullOrEmpty(_fwUpdateLoaderThread.Name)) _fwUpdateLoaderThread.Name = "State Machine Service FW-Update Loader thread"; ThreadWatcher.Instance.Start(_fwUpdateLoaderThread); _tmrLoaderCyclicStatus.Interval = 200; _tmrLoaderCyclicStatus.Tick += TmrLoaderCyclicStatus_Tick; _tmrLoaderCyclicStatus.Enabled = true; lblPcName.ForeColor = ColorSuccess; lblPcName.Text = @"PC Name: " + CryptInformation.GetSysPcName(); lblCustomerName.Text = ""; lblSafeValidDate.Text = ""; } /// /// Exit of Registration Form. /// /// /// - Initial /// /// /// - Fill data grid PCB IDs. /// private void FormRegister_Closed(Object sender, EventArgs e) { Show(); Update(); CheckUserRegistration(); CheckFwUpdateSafeExistence(); CheckReportStatus(); FillDataGridWithPcbIdInfos(); _dbAccessIsLocked = false; _processState = ProcessState.CheckPcbIdProcessState; } /// /// Exit of FW-Update SW. /// /// /// - Initial /// /// /// - Avoid second start of FW-Update SW and loading of FW-Update Safe! /// /// /// - Avoid second start of FW-Update SW and loading of FW-Update Safe but do not exit! /// /// /// - Process state at exit to idle. /// /// /// - Do not delete the FW-Update SW as this may be used with a restart in single app domain, /// in multiple app domain these files are unlocked and should be erased. /// /// /// - Fill data grid PCB IDs. /// private void FormFwUpdate_Closed(Object sender, EventArgs e) { #if (USE_APP_DOMAIN) if (_fwUpdateSwAppDomain != null ) AppDomain.Unload(_fwUpdateSwAppDomain); try { // erase DLLs and remove directory if empty SystemControl.RemoveDirectory(_fwUpdateSwDestinationPath); } catch (Exception ex) { MessageBoxShow(ex.ToString(), Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); throw; } #endif _tmrLoaderCyclicStatus.Tick += TmrLoaderCyclicStatus_Tick; _tmrLoaderCyclicStatus.Enabled = true; Show(); Update(); CheckUserRegistration(); CheckFwUpdateSafeExistence(); CheckReportStatus(); _dbAccessIsLocked = false; _processState = ProcessState.CheckPcbIdProcessState; } /// /// Remove all files which are not locked from HDD /// /// /// - Initial /// /// /// - Cancellation request added. /// private void FrmFwUpdateLoader_FormClosed(Object sender, FormClosedEventArgs e) { #if (USE_APP_DOMAIN) if (_fwUpdateSwAppDomain != null) AppDomain.Unload(_fwUpdateSwAppDomain); #endif _processToken?.Cancel(); try { // erase DLLs and remove directory if empty SystemControl.RemoveDirectory(_fwUpdateSwDestinationPath); } catch (Exception ex) { MessageBoxShow(ex.ToString(), Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); throw; } finally { _tmrLoaderCyclicStatus?.Dispose(); Dispose(); } } #endregion --------------------------------------- Form Load Unload ------------------------------------------- #region ------------------------------------------ Checks ----------------------------------------------------- /// /// The loader version needs to be checked to avoid an incompatible Software to Loader version as the Loader /// release contains some dlls to keep the FwUpdateSafe as small as possible. /// /// true if version is accepted private Boolean CrosscheckLoaderVersion() { if (_fwUpdateSafe?.License != null && _version.Major == _fwUpdateSafe.License.Major && _version.Minor == _fwUpdateSafe.License.Minor && _version.Build == _fwUpdateSafe.License.Build ) { return true; } return false; } /// /// Check validation date and if safe is loaded. /// /// /// - Initial /// void CheckFwUpdateEnabled() { if (_fwUpdateSafe?.SafeInfo != null) { Color color; // check valid date if (DateTime.Compare(_fwUpdateSafe.SafeInfo.FwUpdateValidDate, DateTime.Now) >= 1) { _fwUpdateEnabled = true; color = ColorSuccess; } else { _fwUpdateEnabled = false; color = ColorProcessFailed; } UiInvoker.ControlInvoker(lblLoadedSafe, color, _customerAndOrderInfo); UiInvoker.ControlInvoker(lblCustomerName, color, _fwUpdateSafe.SafeInfo.CustomerName); UiInvoker.ControlInvoker(lblSafeValidDate, color, Resources.StrSafeValidDate + $@" {_fwUpdateSafe.SafeInfo.FwUpdateValidDate:dddd, dd-MMM-yyyy HH:mm:ss} UTC"); } else { UiInvoker.ControlInvoker(lblLoadedSafe, ColorSuccess, _customerAndOrderInfo); UiInvoker.ControlInvoker(lblCustomerName, ColorSuccess); UiInvoker.ControlInvoker(lblSafeValidDate, ColorSuccess); } } /// /// Search "Firmware is up to date!" string in file to guarantee the successful execution of the update /// process. Get success string for all language settings! /// /// /// - Initial /// private Boolean IsSuccessStringInFile(String fileNamePath) { try { // Get search strings from resources StrFwUpToDateMessage from ServiceFwUpdateSw var fileLines = File.ReadAllLines(fileNamePath); var fwIsUpToDate = false; foreach (var searchString in FwUpdateConfig.FirmwareUpToDateSearchStrings) { if (fileLines.Any(line => line.Contains(searchString))) fwIsUpToDate = true; } return fwIsUpToDate; } catch (Exception) { return false; } } /// /// Search for report files /// /// /// - Initial /// /// /// - Check file content for "success" string to mark exclusively the validated updates. /// - Check order number to enable more than one update for a specific pcbId. /// private Boolean IsPcbIdProcessed(String pcbId, String orderNo) { try { // If this directory is existent and contains files, these files have to be checked if (SystemControl.FilesExistingInPath(FwUpdateConfig.UnreportedPath)) { var fileList = new List(); SystemControl.GetFilesOfDirectoryAndSubDirectory(FwUpdateConfig.UnreportedPath, fileList, $"*{FwUpdateConfig.ReportFileExtension}"); if (fileList.Any(file => file.Contains(pcbId) && file.Contains(orderNo) && IsSuccessStringInFile(file))) return true; } // If this directory is existent and contains files, these files have to be checked if (SystemControl.FilesExistingInPath(FwUpdateConfig.ReportedPath)) { var fileList = new List(); SystemControl.GetFilesOfDirectoryAndSubDirectory(FwUpdateConfig.ReportedPath, fileList, $"*{FwUpdateConfig.ReportFileExtension}"); if (fileList.Any(file => file.Contains(pcbId) && file.Contains(orderNo) && IsSuccessStringInFile(file))) return true; } } catch (Exception) { return false; } return false; } /// /// Check the status of the report files: /// - If any file is in the c:/GenesisLog/Unreported folder, these have to be uploaded to the DB with the next /// established DB connection. /// /// /// - Initial. /// /// /// - Changed logic. /// /// /// - Build report and app version file lists. /// /// /// - Added number of upload reports. /// - Split order number as it contains an e.g. "1234567890-10". /// public void CheckReportStatus() { if (_regUser == null) return; if (_fwUpdateReportFiles.Count > 0) _fwUpdateReportFiles.Clear(); if (_fwUpdateAppVersions.Count > 0) _fwUpdateAppVersions.Clear(); // If this directory is existent and contains files, these files have to be reported if (SystemControl.FilesExistingInPath(FwUpdateConfig.UnreportedPath)) { try { var fileList = new List(); SystemControl.GetFilesOfDirectoryAndSubDirectory(FwUpdateConfig.UnreportedPath, fileList, $"*{FwUpdateConfig.ReportFileExtension}"); foreach (var f in fileList) { // the naming is c:\\...\\PcbID_CustomerName_OrderNumber-Position.report var fileName = Path.GetFileNameWithoutExtension(f); var split = fileName.Split(FwUpdateConfig.FwUpdateFileFieldSeparator); split[0] = Regex.Replace(split[0], "[^0-9]", ""); var orderSplit = split[2].Split('-'); orderSplit[0] = Regex.Replace(orderSplit[0], "[^0-9]", ""); orderSplit[1] = Regex.Replace(orderSplit[1], "[^0-9]", ""); var reportFile = new FwUpdateReportDb { UserId = _regUser.Id, FileName = Path.GetFileName(f), PcbId = Convert.ToInt64(string.IsNullOrEmpty(split[0]) ? "0" : split[0]), OrderNr = Convert.ToInt64(string.IsNullOrEmpty(orderSplit[0]) ? "0" : orderSplit[0]), OrderPos = Convert.ToInt64(string.IsNullOrEmpty(orderSplit[1]) ? "0" : orderSplit[1]), Content = File.ReadAllText(f) }; _fwUpdateReportFiles.Add(reportFile); } fileList.Clear(); SystemControl.GetFilesOfDirectoryAndSubDirectory(FwUpdateConfig.UnreportedPath, fileList, $"*{FwUpdateConfig.AppVersionFileExtension}"); foreach (var f in fileList) { // the naming is c:\\...\\PcbID_CustomerName_OrderNumber-Position.applist var fileName = Path.GetFileNameWithoutExtension(f); var split = fileName.Split(FwUpdateConfig.FwUpdateFileFieldSeparator); split[0] = Regex.Replace(split[0], "[^0-9]", ""); var orderSplit = split[2].Split('-'); orderSplit[0] = Regex.Replace(orderSplit[0], "[^0-9]", ""); orderSplit[1] = Regex.Replace(orderSplit[1], "[^0-9]", ""); var appVersionFile = new FwUpdateAppVersionDb { UserId = _regUser.Id, FileName = Path.GetFileName(f), PcbId = Convert.ToInt64(string.IsNullOrEmpty(split[0]) ? "0" : split[0]), OrderNr = Convert.ToInt64(string.IsNullOrEmpty(orderSplit[0]) ? "0" : orderSplit[0]), OrderPos = Convert.ToInt64(string.IsNullOrEmpty(orderSplit[1]) ? "0" : orderSplit[1]), AppVersionList = new List() }; var fileContent = File.ReadAllText(f); appVersionFile.AppVersionList = JsonConvert.DeserializeObject>(fileContent); _fwUpdateAppVersions.Add(appVersionFile); } } catch (Exception) { UiInvoker.ControlInvoker(lblReportUploadStatus, ColorProcessRequired, visible: false); return; } if (_fwUpdateReportFiles.Count > 0 || _fwUpdateAppVersions.Count > 0) { UiInvoker.ControlInvoker(lblReportUploadStatus, ColorProcessRequired, $"{Resources.StrReportsUploadNeeded} " + $"{SystemControl.NumberOfFilesInPath(FwUpdateConfig.UnreportedPath)}"); } } else { UiInvoker.ControlInvoker(lblReportUploadStatus, ColorProcessRequired, visible: false); } } /// /// Check the user registration from ../[user]/AppData/Roaming/Genesis//UserInformation.register /// and compare it with the system user information. /// /// /// - Initial. /// /// /// - Moved registration file handling to , /// - Take the validation date for temporary decoding if system password hash has been changed. /// /// /// - UiInvoker. /// /// /// - Message corrected if HW changed. /// /// /// - Moved lblRegistrationStatus from ToolSTrip to normal label. /// public void CheckUserRegistration() { // Case 1: User is not locally registered -> Exit as it cannot be solved without registration, // The cyclic DB connection detection will enable the [Registration] button! // Check if user is locally registered in the "UserRegistration.register" file and fill _regUser if (!CryptInformation.ReadUserRegistration(UserRegistrationPathFile, out _regUser) || _regUser == null || string.IsNullOrEmpty(_regUser.FullName)) { UiInvoker.ControlEnableInvoker(btnFwUpdate, false); UiInvoker.ControlEnableInvoker(btnLoadFwUpdSafe, false); UiInvoker.ControlInvoker(lblRegistrationStatus, ColorProcessFailed, Resources.StrUserNotRegistered); return; } // Case 2: User is locally registered -> check if decoding is possible // If the system user isn't identical to the registered user, this has to be fixed as a decoding // is not feasible! The registration file is the "OFFLINE" information of the user info stored // to the DB and therefore used by the FW-Update Builder for encryption! // This may occur if the validation has outdated, the password hash or the HW has changed. if (!CryptInformation.IsDecodingPossible(_regUser)) { UiInvoker.ControlInvoker(lblRegistrationStatus, ColorProcessFailed, _regUser.FullName + Resources.StrUserRegistrationExpired); return; } // Case 3: User is locally registered and pwd hash has changed // After changing the password, this shall be updated to the DB as well as to the registration file. var date = DateTimeServer.GetDateStringFromDateTimeOffset(_regUser.ValidDate, _cultureInfo); if (_regUser.PasswordHash != CryptInformation.GetSysUserPasswordHash(_regUser)) { //TODO THW avoid automatic registration until all FW-Update Safes have been processed UiInvoker.ControlInvoker(lblRegistrationStatus, ColorProcessRequired, _regUser.FullName + @" " + Resources.StrUserRegistrationValid + @" " + date); return; } // Case 4: User is locally registered and identical to the system user information UiInvoker.ControlInvoker(lblRegistrationStatus, ColorSuccess, _regUser.FullName + @" " + Resources.StrUserRegistrationValid + @" " + date); } /// /// Check DB connection timeout during connectivity check /// /// /// - Initial /// private void CheckDbConnectionTimeout() { if (_dbAccessDelayCtrMs <= DbConnectionTimeoutMs) return; _dbAccessDelayCtrMs = 0; SetStatusProgressBar(0, false); _processState = ProcessState.Idle; } /// /// Check DB connectivity. /// /// /// - Initial /// /// /// - Lock user access during download of FW-Update Safes and/or report uploads. /// /// /// - Process state set to idle if DB is connected. /// /// /// - Check DB connection always, even if it was connected. /// /// /// - Check register exported to separate function to avoid registration before check of FwUpdateSafes /// availability on DB. /// private void CheckDbConnectivity() { if (_dbAccessIsLocked) return; if (_fwUpdateDbAccess.DbIsConnected) { if (ProgressBarStatus.Visible) ProgressBarStatus.Visible = false; if (lblStatusDbConnect.Text != Resources.StrConnectedToDb) { lblStatusDbConnect.ForeColor = ColorSuccess; lblStatusDbConnect.Text = Resources.StrConnectedToDb; } //#if (!DEBUG) if ((_fwUpdateReportFiles != null && _fwUpdateReportFiles.Count > 0) || (_fwUpdateAppVersions != null && _fwUpdateAppVersions.Count > 0)) { UiInvoker.ToolStripLabelInvoker(lblStatusDbConnect, ColorSuccess, Resources.StrUploadingReports); SetStatusProgressBar(); _processState = ProcessState.UploadReportFiles; return; } //#endif } else { if (ProgressBarStatus.Visible) ProgressBarStatus.Visible = false; if (lblStatusDbConnect.Text != Resources.StrNotConnectedToDb) { lblStatusDbConnect.ForeColor = ColorOngoingProcess; lblStatusDbConnect.Text = Resources.StrNotConnectedToDb; } if (btnRegister.Enabled) btnRegister.Enabled = false; } if (_dbAccessDelayCtrMs <= DbConnectionRetryDelayMs || _dbAccessIsLocked) return; // kick off new connectivity check if (btnRegister.Enabled) btnRegister.Enabled = false; lblStatusDbConnect.ForeColor = ColorOngoingProcess; lblStatusDbConnect.Text = Resources.StrConnectingToDb; SetStatusProgressBar(); _dbAccessDelayCtrMs = 0; _processState = ProcessState.DownloadFwUpdateSafe; } /// /// Check register exported to separate function to avoid registration before check of FwUpdateSafes /// availability on DB. /// /// /// Initial /// private void CheckRegisterState() { if (_dbAccessIsLocked) return; if (_fwUpdateDbAccess.DbIsConnected) { if (!btnRegister.Enabled) btnRegister.Enabled = true; } } /// /// Enable update. /// /// /// - Initial /// /// /// - FW-Update safe load button enabled if any FwUpdateSafe exists. /// /// /// - UiInvoker. /// /// /// - Extended messages. /// /// /// - Added number of update safes. /// private void CheckFwUpdateSafeExistence() { UiInvoker.ControlEnableInvoker(btnFwUpdate, _fwUpdateSafe != null && _fwUpdateEnabled); if (SystemControl.FilesExistingInPath(FwUpdateSafePath)) { UiInvoker.ControlInvoker(lblLocalSafesStatus, ColorSuccess, $"{Resources.StrFwUpdateSafesLocallyExisting} " + $"{SystemControl.NumberOfFilesInPath(FwUpdateSafePath)}"); } else { UiInvoker.ControlInvoker(lblLocalSafesStatus, ColorProcessFailed, Resources.StrFwUpdateSafesLocallyMissing); } UiInvoker.ControlEnableInvoker(btnLoadFwUpdSafe, SystemControl.FilesExistingInPath(FwUpdateSafePath)); } #endregion --------------------------------------- Checks ----------------------------------------------------- #region ------------------------------------------ Tools ------------------------------------------------------ /// /// Download FW update safes from DB task. /// /// /// - Initial /// /// /// - Web API timeout message. /// private void DownloadFwUpdateSafesFromDbTask() { _invokerProcessState = _processState; Task.Factory.StartNew(() => { Thread.CurrentThread.CurrentUICulture = _cultureInfo; Thread.CurrentThread.CurrentCulture = _cultureInfo; try { _dbAccessIsLocked = true; // check DB connection in advance to overcome the timing issues for DB service startup if (!_fwUpdateDbAccess.CheckDbConnection()) LogText(WebApiTimeout); DownloadFwUpdateSafesFromDb(); } catch (Exception) { _processState = ProcessState.Error; } }).ContinueWith(delegate { _dbAccessIsLocked = false; _processState = ProcessState.Idle; }); } /// /// Download FW update safes from DB task. /// /// /// - Initial /// /// /// - Web API timeout message. /// private void DownloadFwUpdateSafesFromDb() { if (_fwUpdateDbAccess == null || _regUser == null || !_fwUpdateDbAccess.DbIsConnected) return; try { if (!Directory.Exists(FwUpdateSafeInfoBackupPath)) { Directory.CreateDirectory(FwUpdateSafeInfoBackupPath); } if (!Directory.Exists(FwUpdateSafePath)) { Directory.CreateDirectory(FwUpdateSafePath); } // Check in special folder for accessed safes. if this directory is existent and contains files, // these files have to be checked. var backupSafeNames = new List(); var safesToBackupName = new List(); if (SystemControl.FilesExistingInPath(FwUpdateSafeInfoBackupPath)) { var backupSafeNamesPath = new List(); SystemControl.GetFilesOfDirectoryAndSubDirectory(FwUpdateSafeInfoBackupPath, backupSafeNamesPath, $"*{FwUpdateConfig.LoadedSafesBackupFileExtension}"); backupSafeNames = backupSafeNamesPath.Select(Path.GetFileNameWithoutExtension).ToList(); } // load the safe information from DB if (_fwUpdateDbAccess != null && _fwUpdateDbAccess.ListAllFwUpdateSafesOfUserFromDb(_regUser.Id, out var userFwUpdateSafes)) { foreach (var safe in userFwUpdateSafes) { if (backupSafeNames.All(x => x != Path.GetFileNameWithoutExtension(safe.Name))) { // it is only one safe (even if it is a list!) var fwUpdateSafes = _fwUpdateDbAccess.DownloadFwUpdateSafeFromDb(safe.ContainerId); // store to file if it not already exists if (fwUpdateSafes != null && fwUpdateSafes.Count > 0) { if (Directory.Exists(FwUpdateSafePath)) { var sourceFile = Path.Combine(FwUpdateSafePath, fwUpdateSafes[0].Name); if (File.Exists(sourceFile)) continue; var fs = File.Open(sourceFile, FileMode.Create); fs.Write(fwUpdateSafes[0].Content, 0, fwUpdateSafes[0].Content.Length); fs.Close(); safesToBackupName.Add(Path.GetFileNameWithoutExtension(fwUpdateSafes[0].Name) + FwUpdateConfig.LoadedSafesBackupFileExtension); } } if (!_fwUpdateDbAccess.DbIsConnected) LogText(WebApiTimeout); } } // copy backup information of downloaded safes to special folder to exclude these on next trial foreach (var s in safesToBackupName) { if (Directory.Exists(FwUpdateSafeInfoBackupPath)) { var sourceFile = Path.Combine(FwUpdateSafeInfoBackupPath, s); var fs = File.Open(sourceFile, FileMode.Create); fs.Close(); } } } else { LogText(WebApiTimeout); } } catch (Exception) { // nothing to do } } /// /// Upload report files to DB task. /// /// /// - Initial /// /// /// - Web API timeout message. /// private void UploadReportFilesToDbTask() { _invokerProcessState = _processState; Task.Factory.StartNew(() => { Thread.CurrentThread.CurrentUICulture = _cultureInfo; Thread.CurrentThread.CurrentCulture = _cultureInfo; try { _dbAccessIsLocked = true; // check DB connection in advance to overcome the timing issues for DB service startup if (!_fwUpdateDbAccess.CheckDbConnection()) LogText(WebApiTimeout); UploadReportFilesToDb(); } catch (Exception) { _processState = ProcessState.Error; } }).ContinueWith(delegate { _dbAccessIsLocked = false; _processState = ProcessState.Idle; }); } /// /// Upload report files to DB. /// /// /// - Initial /// /// /// - Web API timeout message. /// private void UploadReportFilesToDb() { if (_fwUpdateDbAccess == null || !_fwUpdateDbAccess.DbIsConnected) return; foreach (var appVersionFile in _fwUpdateAppVersions) { if (_fwUpdateDbAccess.UploadCordonelAppVersionsToDb(appVersionFile.PcbId, appVersionFile.AppVersionList)) { var sourceFilePathName = Path.Combine(FwUpdateConfig.UnreportedPath + appVersionFile.FileName); var destinationFilePathName = Path.Combine(FwUpdateConfig.ReportedPath + appVersionFile.FileName); if (SystemControl.CopyFile(sourceFilePathName, destinationFilePathName)) { File.Delete(sourceFilePathName); } } else { LogText(WebApiTimeout); } } foreach (var reportFile in _fwUpdateReportFiles) { if (_fwUpdateDbAccess.UploadFwUpdateReportToDb(reportFile)) { var sourceFilePathName = Path.Combine(FwUpdateConfig.UnreportedPath + reportFile.FileName); var destinationFilePathName = Path.Combine(FwUpdateConfig.ReportedPath + reportFile.FileName); if (SystemControl.CopyFile(sourceFilePathName, destinationFilePathName)) { File.Delete(sourceFilePathName); } } else { LogText(WebApiTimeout); } } } /// /// 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, MessageBoxIcon icon) { MessageBox.Show(text, caption, buttons, icon, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification); } /// /// Extract the FW-Update SW from FW-Update Safe. /// /// /// - Initial. /// public Boolean ExtractFwUpdateSw() { try { var files = new List(); if (_fwUpdateSafe?.Software?.SoftwareDynLinkLibs == null || _fwUpdateSafe.Software.SoftwareDynLinkLibs.Count == 0) return false; foreach (var fp in _fwUpdateSafe.Software.SoftwareDynLinkLibs.Where(f => f.FileName.EndsWith(".dll"))) { if (fp.FileContentLength == fp.FileContent.Length && fp.FileContentCrc16CcittMsb == Crc16Ccitt.CalculateMsb1021(fp.FileContent)) { files.Add(fp); } } if (_fwUpdateSafe?.Software?.MeterFilesEraseRestore == null) return false; var file = _fwUpdateSafe.Software.MeterFilesEraseRestore; if (file.FileContentLength == file.FileContent.Length && file.FileContentCrc16CcittMsb == Crc16Ccitt.CalculateMsb1021(file.FileContent)) { files.Add(file); } if (_fwUpdateSafe?.Software?.SoftwareSetupFile == null) return false; file = _fwUpdateSafe.Software.SoftwareSetupFile; if (file.FileContentLength == file.FileContent.Length && file.FileContentCrc16CcittMsb == Crc16Ccitt.CalculateMsb1021(file.FileContent)) { files.Add(file); } if (_fwUpdateSafe?.Software?.RegisterDefinitionFile == null) return false; file = _fwUpdateSafe.Software.RegisterDefinitionFile; if (file.FileContentLength == file.FileContent.Length && file.FileContentCrc16CcittMsb == Crc16Ccitt.CalculateMsb1021(file.FileContent)) { files.Add(file); } foreach (var f in files) { var destinationPath = _fwUpdateSwDestinationPath; if (!string.IsNullOrEmpty(f.SubDirectory)) { destinationPath = Path.Combine(_fwUpdateSwDestinationPath, f.SubDirectory); } if (!Directory.Exists(destinationPath)) { Directory.CreateDirectory(destinationPath); } if (Directory.Exists(destinationPath)) { var sourceFile = Path.Combine(destinationPath, f.FileName); var fs = File.Open(sourceFile, FileMode.Create); fs.Write(f.FileContent, 0, f.FileContent.Length); fs.Close(); } // if one path cannot be created, exit else return false; } } catch (Exception) { return false; } return true; } /// /// Open FW-Update safe and decrypt it. /// /// /// - Initial. /// /// /// - Extracted FW-Update safe name as this represents the customer name and FW-Update order number. /// /// /// - Logging info. /// /// /// - FW-Update safe loaded marked to stop DB connection trials. /// /// /// - Exported from button click. /// /// /// - Process state at exit to idle. /// /// /// - Removed Boundary Castle decoding error message. /// - Process state set to idle and removed DB access lock on abort of open, exception or exit. /// /// /// - Changed exit status to force fill data grid with pcb ids, /// - Log detected pcb ids of safe. /// /// /// - Output extended to customer name and valid date and colored based on valid date. /// /// /// - Crosscheck Loader version. /// private void LoadFwUpdateSafe() { Byte[] encryptedStream = null; try { Invoke(new Action(() => { dlgOpenFwUpdateSafe.InitialDirectory = FwUpdateSafePath; var fileExtension = FwUpdateConfig.FwUpdateFileExtension; dlgOpenFwUpdateSafe.Filter = $@"FW-Update Safe (*{fileExtension})|*{fileExtension}"; if (dlgOpenFwUpdateSafe.ShowDialog() != DialogResult.OK) { MessageBoxShow(Resources.StrLoadingFwUpdateSafeFailed, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _dbAccessIsLocked = false; _processState = ProcessState.Idle; return; } _fwUpdateSafePathName = dlgOpenFwUpdateSafe.FileName; _customerAndOrderInfo = ""; if (File.Exists(_fwUpdateSafePathName)) { encryptedStream = File.ReadAllBytes(_fwUpdateSafePathName); // extract _customerAndOrderInfo from FW-Update safe name _customerAndOrderInfo = Path.GetFileNameWithoutExtension(_fwUpdateSafePathName); } else { LogText(Resources.StrLoadingFwUpdateSafeFailed + " " + _customerAndOrderInfo); MessageBoxShow(Resources.StrLoadingFwUpdateSafeFailed, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _dbAccessIsLocked = false; _processState = ProcessState.Idle; } })); } catch (Exception) { LogText(Resources.StrLoadingFwUpdateSafeFailed + " " + _customerAndOrderInfo); //MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK, // MessageBoxIcon.Error); MessageBoxShow(Resources.StrLoadingFwUpdateSafeFailed, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _processState = ProcessState.Idle; } if (encryptedStream == null) return; try { var primaryKey = FwUpdateCrypt.BuildPrimaryKey(_regUser.HardwareId, _regUser.Domain, _regUser.LogInName, _regUser.PasswordHash); var decryptedStream = FwUpdateCrypt.Decrypt(encryptedStream, primaryKey); var decryptedData = Encoding.UTF8.GetString(decryptedStream, 0, decryptedStream.Length); _fwUpdateSafe = JsonConvert.DeserializeObject(decryptedData); LogText(Resources.StrDecryptFwUpdateSafeSuccess + " " + _customerAndOrderInfo); if (_fwUpdateSafe?.Updates?.CordonelDeviceInfos != null) { foreach (var cordonel in _fwUpdateSafe.Updates.CordonelDeviceInfos) { LogText($@"{Resources.StrTableCordonelPcbId}: {cordonel.PcbId}"); } } Thread.CurrentThread.CurrentUICulture = _cultureInfo; Thread.CurrentThread.CurrentCulture = _cultureInfo; // The loader version needs to be checked to avoid an incompatible Software to Loader version if (CrosscheckLoaderVersion()) { CheckFwUpdateEnabled(); _dbAccessIsLocked = false; _processState = ProcessState.CheckPcbIdProcessState; } else { var versionMajor = "?"; var versionMinor = "?"; var versionBuild = "?"; if (_fwUpdateSafe?.License != null) { versionMajor = _fwUpdateSafe.License.Major.ToString(); versionMinor = _fwUpdateSafe.License.Minor.ToString(); versionBuild = _fwUpdateSafe.License.Build.ToString(); } var msg = Resources.StrSoftwareVersionExpired + $@" Version: {versionMajor}.{versionMinor}.{versionBuild}"; LogText(msg); MessageBoxShow(msg, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _dbAccessIsLocked = false; _processState = ProcessState.Idle; } } catch (Exception ex) { LogText(ex.Message); LogText(Resources.StrDecryptFwUpdateSafeFailed + " " + _customerAndOrderInfo); //MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK, // MessageBoxIcon.Error); MessageBoxShow(Resources.StrDecryptFwUpdateSafeFailed, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); _dbAccessIsLocked = false; _processState = ProcessState.Idle; } } /// /// Start of FW-Update SW. /// /// /// - Initial /// /// /// - Removed test and use real data. /// /// /// - Added customer name and FW-Update order number. /// /// /// - Locked DB access during FW-Update. /// /// /// - load configuration.json to the AppDomain "FwUpdateLoader" as it cannot be accessed if stored in the /// subdirectory "ServiceFwUpdateSw". ALl other files can remain there. /// /// /// - Logging info. /// /// /// - FW-Update SW taken from FW-Update Safe. /// /// /// - FW-Update process marked to stop DB connection trials. /// /// /// - FW-Update SW loaded to new app domain. /// /// /// - FW-Update SW loaded to new app domain [optional], /// - Avoid loading of assembly if already done. /// /// /// - If files are already loaded, skip the ExtractFwUpdateSw process in single domain mode, as the assembly /// is loaded and these files are locked for processing. /// /// /// - Send safe information. /// private void StartFwUpdateProcess() { if (_fwUpdateSafe == null) return; try { #if (USE_APP_DOMAIN) // create and copy all DLLs to this temporary working directory, copy register definition // file to current AppDomain if (ExtractFwUpdateSw()) { { // Copy the actual setup var domainSetup = AppDomain.CurrentDomain.SetupInformation; domainSetup.PrivateBinPath = _fwUpdateSwDestinationPath; var appDomainEvidence = AppDomain.CurrentDomain.Evidence; _fwUpdateSwAppDomain = AppDomain.CreateDomain(AppDomainName, appDomainEvidence, domainSetup); _asmLoader = (AssemblyLoader)_fwUpdateSwAppDomain.CreateInstanceAndUnwrap( typeof(AssemblyLoader).Assembly.FullName, typeof(AssemblyLoader).FullName ?? string.Empty); _fwUpdateObject = _asmLoader.LoadAssemblyGetClassObject(_fwUpdateSwDestinationPath, FwUpdateSwDll, FormFwUpdateSw); if (_fwUpdateObject == null) return; } #else // If files are already loaded, skip the ExtractFwUpdateSw process in single domain mode, as the assembly // is loaded and these files are locked for processing! if (_asmLoader != null || ExtractFwUpdateSw()) { { // avoid loading of assembly if already done if (_asmLoader == null) { _asmLoader = new AssemblyLoader(); // loading the FwUpdateSw DLL from HDD if (!_asmLoader.LoadAssembly(_fwUpdateSwDestinationPath, FwUpdateSwDll)) return; } // retrieving the address of a specific assembly _fwUpdateObject = _asmLoader.GetAssemblyFnObject(FormFwUpdateSw, null, null); } #endif // inform the FW-Update SW where to find the setups // relative to C:\[user]\AppData\Roaming\ which is here \Genesis var basePaths = new FwUpdatePaths { PortConfigFilePathName = PortConfigPathName, MeterFilesConfigFilePathName = _meterFilesConfigPathName, FwUpdateSwPathName = _fwUpdateSwDestinationPath, CustomerAndOrderNumber = _customerAndOrderInfo }; SendDataToFwUpdateSw(basePaths); // send CordonelFirmware to FW-Update SW if (_fwUpdateSafe.Updates.CordonelFwPackage != null) { SendDataToFwUpdateSw(_fwUpdateSafe.Updates.CordonelFwPackage); } // send Cordonel device information foreach (var di in _fwUpdateSafe.Updates.CordonelDeviceInfos) { SendDataToFwUpdateSw(di); } // send safe information if (_fwUpdateSafe.SafeInfo != null) { SendDataToFwUpdateSw(_fwUpdateSafe.SafeInfo); } if (!(_fwUpdateObject is Form formFwUpdate)) return; LogText(Resources.StrLoadingFwUpdateSwSuccess); DisableControls(); formFwUpdate.Closed += FormFwUpdate_Closed; Invoke(new Action(() => { formFwUpdate.Show(); Hide(); })); SendDataToFwUpdateSw(_fwUpdateSafe.License); }// build and copy entire FwUpdateSw directory and register definition file else { LogText(Resources.StrLoadingFwUpdateSwFailed); MessageBoxShow(Resources.StrLoadingFwUpdateSwFailed, Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception e) { LogText(Resources.StrLoadingFwUpdateSwFailed); MessageBoxShow(e.ToString(), Resources.StrError, MessageBoxButtons.OK, MessageBoxIcon.Error); // erase DLLs and remove directory if empty // SystemControl.RemoveDirectory(_fwUpdateSwDestinationPath); } } /// /// Process bar. /// /// /// - Initial /// /// /// - Avoid repeated execution on invisible progress bar. /// private void SetStatusProgressBar(Int32 value = 0, Boolean visible = true) { if (ProgressBarStatus == null || (!ProgressBarStatus.Visible && !visible)) return; if (ProgressBarStatus.Visible && visible && ProgressBarStatus.Value == value) return; ProgressBarStatus.Value = value; ProgressBarStatus.Visible = visible; } ///// ///// Establish the DB connection. ///// ///// ///// - Initial ///// //private void EstablishDbConnectionTask() //{ // 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.Idle; // } // }).ContinueWith(delegate // { // if (_processState == ProcessState.ConnectDb) _processState = ProcessState.Idle; // }); //} /// /// Output exclusively to user update remarks text window. /// private void LogText(String txtHistory) { Invoke(new Action(() => { _logger.Info(txtHistory); })); } /// /// Neutral information to label, user text box and logger. /// private void InfoStatusUnknown(Control label, String msg, Boolean userTextOutput = false) { if (userTextOutput) LogText(msg); UiInvoker.ControlInvoker(label, ColorUnknownStatus, msg); } /// /// Success information to label, user text box and logger. /// private void InfoProcessActive(Control label, String msg, Boolean log = true) { if (log) LogText(msg); UiInvoker.ControlInvoker(label, ColorOngoingProcess, msg); } /// /// Success information to label, user text box and logger. /// private void InfoProcessSuccess(Control label, String msg, Boolean log = true) { if (log) LogText(msg); UiInvoker.ControlInvoker(label, ColorSuccess, $@"{SuccessSign} {msg}"); } /// /// Failed information to label, user text box and logger. /// private void InfoProcessFailed(Control label, String msg, Boolean log = true) { if (log) { LogText(StrSeparator); LogText(msg); LogText(StrSeparator); } UiInvoker.ControlInvoker(label, ColorProcessFailed, $@"{FailedSign} {msg}"); } /// /// Common routine for disable all controls. /// /// /// - Initial /// /// /// - UiInvoker. /// private void DisableControls() { UiInvoker.ControlEnableInvoker(btnFwUpdate, false); UiInvoker.ControlEnableInvoker(btnLoadFwUpdSafe, false); UiInvoker.ControlEnableInvoker(btnRegister, false); } #endregion --------------------------------------- Tools ------------------------------------------------------ #region ------------------------------------------ Data Grid PCB ID ------------------------------------------- /// /// Build data grid for PCB ID /// /// /// - Initial. /// /// /// - Added order number to search for successfully updated device in linked to this safe. /// /// /// - Added customer serial number in overview. /// private void FillDataGridWithPcbIdInfos() { if (_fwUpdateSafe?.Updates?.CordonelDeviceInfos == null) return; grpLanguageSelection.Enabled = false; _dataTablePcbIds?.Dispose(); Thread.CurrentThread.CurrentUICulture = _cultureInfo; Thread.CurrentThread.CurrentCulture = _cultureInfo; _dataTablePcbIds = new DataTable(); _dataTablePcbIds.Columns.Add(Resources.StrTableUpdated, typeof(Boolean)); _dataTablePcbIds.Columns.Add(Resources.StrTableCordonelPcbId, typeof(String)); _dataTablePcbIds.Columns.Add(Resources.StrTableCustomerSerialNumber, typeof(String)); try { foreach (var cordSearch in _fwUpdateSafe.Updates.CordonelDeviceInfos) { var row = _dataTablePcbIds.NewRow(); row[Resources.StrTableUpdated] = IsPcbIdProcessed(cordSearch.PcbId, _customerAndOrderInfo); row[Resources.StrTableCordonelPcbId] = cordSearch.PcbId ?? Constants.StrUnknown; row[Resources.StrTableCustomerSerialNumber] = cordSearch.CustomerSerialNumber ?? Constants.StrUnknown; _dataTablePcbIds.Rows.Add(row); } gridViewPcbIds.DataSource = _dataTablePcbIds; foreach (DataGridViewColumn column in gridViewPcbIds.Columns) { column.SortMode = DataGridViewColumnSortMode.Automatic; } } catch (Exception) { _processState = ProcessState.Error; } finally { grpLanguageSelection.Enabled = true; } } #endregion --------------------------------------- Data Grid PCB ID ------------------------------------------- #region ------------------------------------------ Interfaces ------------------------------------------------- /// /// Send data as json stream to FW-Update SW. /// /// /// true if succeeded /// /// - Initial /// /// /// - Added FW update safe information for report /// private Boolean SendDataToFwUpdateSw(Object data) { if (_fwUpdateObject == null) return false; var parameters = new Object[2]; // As a complex field cannot be passed between a dynamic loaded assembly due to // the unknown signature or assembly version, a simple data type will be passed and // then converted to the complex type. switch (data) { case FwUpdateSafeInfo _: parameters[0] = (Int32)DataContainerName.FwUpdateSafeInfo; break; case CordonelDeviceInfo _: parameters[0] = (Int32)DataContainerName.CordonelDeviceInfo; break; case SoftwareLicense _: parameters[0] = (Int32)DataContainerName.SoftwareLicense; break; case FwUpdatePaths _: parameters[0] = (Int32)DataContainerName.UpdatePackageBasePath; break; case CordonelFirmware _: parameters[0] = (Int32)DataContainerName.UpdatePackage; break; default: return false; } parameters[1] = JsonConvert.SerializeObject(data); #if (USE_APP_DOMAIN) return _asmLoader.InvokeMember(FormFwUpdateSw, FwUpdateSwDataExchangeInterface, parameters); #else return (Boolean)_fwUpdateObject.GetType().InvokeMember(FwUpdateSwDataExchangeInterface, BindingFlags.InvokeMethod, null, _fwUpdateObject, parameters); #endif } #endregion --------------------------------------- Interfaces ------------------------------------------------- #region ------------------------------------------ Language --------------------------------------------------- /// /// Select language at runtime: English /// /// /// - Initial /// /// /// - Change menu. /// /// /// - Avoid repetition if current culture is equal to the required one. /// /// /// - Backup safe name for language change. /// private void RadioBtnEnglishLanguage_Click(Object sender, EventArgs e) { if (Thread.CurrentThread.CurrentCulture.Name == "en-GB") return; _cultureInfo = new CultureInfo("en-GB"); _backupSafeNameForLanguageChange = lblLoadedSafe.Text; ChangeLanguageControls(); } /// /// Select language at runtime: German /// /// /// - Initial /// /// /// - Change menu. /// /// /// - Avoid repetition if current culture is equal to required. /// /// /// - Backup safe name for language change. /// private void RadioBtnGermanLanguage_Click(Object sender, EventArgs e) { if (Thread.CurrentThread.CurrentCulture.Name == "de-DE") return; _cultureInfo = new CultureInfo("de-DE"); _backupSafeNameForLanguageChange = lblLoadedSafe.Text; 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. /// /// /// - Check user registration added. /// /// /// - Restore safe name after language change. /// private void ChangeLanguageControls() { Thread.CurrentThread.CurrentUICulture = _cultureInfo; Thread.CurrentThread.CurrentCulture = _cultureInfo; var resources = new ComponentResourceManager(typeof(FrmFwUpdateLoader)); 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 = ColorProcessRequired; } else { lblStatusDbConnect.Text = Resources.StrConnectedToDb; lblStatusDbConnect.ForeColor = ColorSuccess; } lblFwUpdateInfo.Text = $@"Version: {_version.Major}.{_version.Minor}.{_version.Build}"; lblPcName.Text = @"PC Name: " + CryptInformation.GetSysPcName(); lblCustomerName.Text = ""; lblSafeValidDate.Text = ""; CheckReportStatus(); CheckUserRegistration(); CheckFwUpdateSafeExistence(); if (_fwUpdateSafe?.SafeInfo != null) { lblLoadedSafe.Text = _backupSafeNameForLanguageChange; lblCustomerName.Text = _fwUpdateSafe.SafeInfo.CustomerName; lblSafeValidDate.Text = Resources.StrSafeValidDate + $@" {_fwUpdateSafe.SafeInfo.FwUpdateValidDate:dddd, dd-MMM-yyyy HH:mm:ss} UTC"; } FillDataGridWithPcbIdInfos(); } #endregion --------------------------------------- Language --------------------------------------------------- } }