laatzen/ServiceFwUpdate/Ui/ServiceFwUpdateLoader/FrmFwUpdateLoader.cs
2025-06-06 12:55:08 +02:00

2078 lines
88 KiB
C#

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
{
/// <summary>
/// Service firmware update loader main form.
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
[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;
/// <summary>
/// DB connection trial timer
/// </summary>
private const Int32 DbConnectionRetryDelayMs = 30000;
/// <summary>
/// DB connection trial timer
/// </summary>
private const Int32 DbConnectionTimeoutMs = 9000;
private Int32 _dbAccessDelayCtrMs;
/// <summary>
/// Avoid DB connection trial on ongoing DB access, FW-Update procedure or registration
/// </summary>
private Boolean _dbAccessIsLocked;
/// <summary>
/// Check date time to enable FW update button.
/// </summary>
private Boolean _fwUpdateEnabled;
/// <summary>
/// List for upload app list versions.
/// </summary>
private readonly List<FwUpdateAppVersionDb> _fwUpdateAppVersions = new List<FwUpdateAppVersionDb>();
/// <summary>
/// List for upload reports.
/// </summary>
private readonly List<FwUpdateReportDb> _fwUpdateReportFiles = new List<FwUpdateReportDb>();
/// <summary>
/// Project name for the DLL to load:
/// This is the base for the source folder, the destination folder, the namespace and the form
/// </summary>
private const String FwUpdateProjectName = "ServiceFwUpdateSw";
/// <summary>
/// Name 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
/// </summary>
private readonly String _meterFilesConfigPathName;
/// <summary>
/// DB access
/// </summary>
private readonly FwUpdateDb _fwUpdateDbAccess = new FwUpdateDb();
/// <summary>
/// Name for firmware update software including the namespace
/// </summary>
private const String FwUpdateSwFullName = "Xylem.ServiceFwUpdate.Ui." + FwUpdateProjectName;
/// <summary>
/// Name for firmware update dll
/// </summary>
private const String FwUpdateSwDll = FwUpdateSwFullName + ".dll";
/// <summary>
/// Name for firmware update form
/// </summary>
private const String FormFwUpdateSw = FwUpdateSwFullName + ".Frm" + FwUpdateProjectName;
/// <summary>
/// FW-Update SW interface for data exchange
/// </summary>
private const String FwUpdateSwDataExchangeInterface = "SetDataContainerJson";
/// <summary>
/// Path to firmware update software DLL
/// </summary>
private readonly String _fwUpdateSwDestinationPath;
/// <summary>
/// Object reference of FW-Update SW
/// </summary>
private Object _fwUpdateObject;
#if (USE_APP_DOMAIN)
/// <summary>
/// FW-Update SW application domain
/// </summary>
private const String AppDomainName = "AppDomain" + FwUpdateProjectName;
/// <summary>
/// App domain for FW-Update SW
/// </summary>
private AppDomain _fwUpdateSwAppDomain;
#endif
/// <summary>
/// Logger of messages
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// Separator string used for logger and history window
/// </summary>
private const String StrSeparator = "-----------------------------------------------------" +
"-----------------------------------------------------";
/// <summary>
/// remind manually changed culture setting
/// </summary>
private CultureInfo _cultureInfo;
/// <summary>
/// remind software version
/// </summary>
private readonly Version _version;
/// <summary>
/// FW-Update Safe
/// </summary>
private FwUpdateSafe _fwUpdateSafe;
/// <summary>
/// File name of FW-Update safe without extension (CustomerName_FwUpdateOrderNumber)
/// </summary>
private String _customerAndOrderInfo;
/// <summary>
/// Registered user information read from registration file.
/// </summary>
private UserInformation _regUser;
/// <summary>
/// Path to application configuration ../[user]/AppData/Roaming/Genesis/
/// </summary>
private static readonly String ApplicationConfigPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData), ProgramConfig.GenesisBaseFolder);
/// <summary>
/// Destination directory and file name for serial port configuration file
/// </summary>
private static readonly String PortConfigPathName = Path.Combine(ApplicationConfigPath,
ProgramConfig.SerialConfigFileName);
/// <summary>
/// Path and file user registration
/// </summary>
private static readonly String UserRegistrationPathFile = Path.Combine(ApplicationConfigPath,
FwUpdateConfig.UserRegistrationFileName);
/// <summary>
/// Path and file user NLog configuration
/// </summary>
private static readonly String NLogConfigurationDestPathFile = Path.Combine(ApplicationConfigPath,
ProgramConfig.NlogConfig);
/// <summary>
/// Path for FW-Update safes
/// </summary>
private static readonly String FwUpdateSafePath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile), FwUpdateConfig.DefaultFwUpdateSafePath);
/// <summary>
/// Path for FW-Update safes
/// </summary>
private static readonly String FwUpdateSafeInfoBackupPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile), FwUpdateConfig.FwUpdateSafeInfoBackupPath);
/// <summary>
/// Path and name of FW-Update safe
/// </summary>
private static String _fwUpdateSafePathName;
/// <summary>
/// Timer for cyclic status update
/// </summary>
private System.Windows.Forms.Timer _tmrLoaderCyclicStatus = new System.Windows.Forms.Timer();
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
/// <summary>
/// State machine for FW update builder:
/// Will be executed in an endless loop from the "Service FW-Update Thread" until exit. State will be executed
/// once and then locked for repeated execution. A state change is the reason for reentry.
/// NOTE:
/// Change the state immediately before calling any routine to avoid repeated execution of routine!
/// </summary>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - Upload report files,
/// - Upload app list version,
/// - Download FW-Update Safes.
/// </remarks>
/// <remarks date="2021-Jun-08" author="Roland Drabesch">
/// - Sleep on equal process state to force suspend of actual thread.
/// </remarks>
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 ---------------------------------------------
/// <summary>
/// Check the DB connection.
/// </summary>
/// <remarks date="2021-Feb-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Implemented state machine.
/// </remarks>
/// <remarks date="2021-Mar-28" author="Thomas Wiedebusch">
/// - Fill data grid with pcb id infos.
/// </remarks>
/// <remarks date="2021-Mai-07" author="Thomas Wiedebusch">
/// - Check register exported to separate function to avoid registration before check of FwUpdateSafes
/// availability on DB.
/// </remarks>
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 -------------------------------------------
/// <summary>
/// Start of FW-Update SW.
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - Exported functionality to StartFwUpdateProcess.
/// </remarks>
private void BtnFwUpdate_Click(Object sender, EventArgs e)
{
if (_fwUpdateSafe == null) return;
_tmrLoaderCyclicStatus.Tick -= TmrLoaderCyclicStatus_Tick;
_tmrLoaderCyclicStatus.Enabled = false;
_dbAccessIsLocked = true;
_processState = ProcessState.FwUpdateProcess;
}
/// <summary>
/// Register user at DB Laatzen. This can only be done if the network is connected.
/// </summary>
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Feb-04" author="Thomas Wiedebusch">
/// - Register to DB.
/// </remarks>
/// <remarks date="2021-Mar-15" author="Thomas Wiedebusch">
/// - Reg user information will be sent the registration form.
/// </remarks>
/// <remarks date="2021-Mar-16" author="Thomas Wiedebusch">
/// - Locked DB access during registration.
/// </remarks>
/// <remarks date="2021-Mar-25" author="Thomas Wiedebusch">
/// - _fwUpdateDbAccess added,
/// - new process state added.
/// </remarks>
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();
}
/// <summary>
/// Open FW-Update safe and decrypt it.
/// </summary>
/// <remarks date="2021-Feb-05" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - Exported functionality to LoadFwUpdateSafe.
/// </remarks>
private void btnLoadFwUpdSafe_Click(Object sender, EventArgs e)
{
_dbAccessIsLocked = true;
_processState = ProcessState.LoadFwUpdateSafe;
}
#endregion --------------------------------------- User Interaction -------------------------------------------
#region ------------------------------------------ Form Load Unload -------------------------------------------
/// <summary>
/// Ctor
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Feb-05" author="Thomas Wiedebusch">
/// - Moved all paths and file setups to this function.
/// </remarks>
/// <remarks date="2021-Mar-15" author="Thomas Wiedebusch">
/// - 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.
/// </remarks>
/// <remarks date="2021-Mar-16" author="Thomas Wiedebusch">
/// - DB offline message added.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Thread added.
/// </remarks>
/// <remarks date="2021-Jun-08" author="Thomas Wiedebusch">
/// - Library added to NLogConfig source path, as this is the release source, copy files only if not
/// identical.
/// </remarks>
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 = "";
}
/// <summary>
/// Exit of Registration Form.
/// </summary>
/// <remarks date="2021-Feb-04" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Fill data grid PCB IDs.
/// </remarks>
private void FormRegister_Closed(Object sender, EventArgs e)
{
Show();
Update();
CheckUserRegistration();
CheckFwUpdateSafeExistence();
CheckReportStatus();
FillDataGridWithPcbIdInfos();
_dbAccessIsLocked = false;
_processState = ProcessState.CheckPcbIdProcessState;
}
/// <summary>
/// Exit of FW-Update SW.
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Avoid second start of FW-Update SW and loading of FW-Update Safe!
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - Avoid second start of FW-Update SW and loading of FW-Update Safe but do not exit!
/// </remarks>
/// <remarks date="2021-Mar-27" author="Thomas Wiedebusch">
/// - Process state at exit to idle.
/// </remarks>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - 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.
/// </remarks>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Fill data grid PCB IDs.
/// </remarks>
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;
}
/// <summary>
/// Remove all files which are not locked from HDD
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Cancellation request added.
/// </remarks>
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 -----------------------------------------------------
/// <summary>
/// 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.
/// </summary>
/// <returns>true if version is accepted</returns>
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;
}
/// <summary>
/// Check validation date and if safe is loaded.
/// </summary>
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
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);
}
}
/// <summary>
/// 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!
/// </summary>
/// <remarks date="2021-Oct-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
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;
}
}
/// <summary>
/// Search for report files
/// </summary>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Oct-15" author="Thomas Wiedebusch">
/// - 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.
/// </remarks>
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<String>();
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<String>();
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;
}
/// <summary>
/// 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.
/// </summary>
/// <remarks date="2021-Mar-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
/// - Changed logic.
/// </remarks>
/// <remarks date="2021-Mar-30" author="Thomas Wiedebusch">
/// - Build report and app version file lists.
/// </remarks>
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
/// - Added number of upload reports.
/// - Split order number as it contains an e.g. "1234567890-10".
/// </remarks>
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<String>();
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<CordonelAppVersion>()
};
var fileContent = File.ReadAllText(f);
appVersionFile.AppVersionList = JsonConvert.DeserializeObject<List<CordonelAppVersion>>(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);
}
}
/// <summary>
/// Check the user registration from ../[user]/AppData/Roaming/Genesis//UserInformation.register
/// and compare it with the system user information.
/// </summary>
/// <remarks date="2021-Feb-05" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-16" author="Thomas Wiedebusch">
/// - Moved registration file handling to <see cref="CryptInformation"/>,
/// - Take the validation date for temporary decoding if system password hash has been changed.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - UiInvoker.
/// </remarks>
/// <remarks date="2021-Mar-30" author="Thomas Wiedebusch">
/// - Message corrected if HW changed.
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Moved lblRegistrationStatus from ToolSTrip to normal label.
/// </remarks>
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);
}
/// <summary>
/// Check DB connection timeout during connectivity check
/// </summary>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void CheckDbConnectionTimeout()
{
if (_dbAccessDelayCtrMs <= DbConnectionTimeoutMs) return;
_dbAccessDelayCtrMs = 0;
SetStatusProgressBar(0, false);
_processState = ProcessState.Idle;
}
/// <summary>
/// Check DB connectivity.
/// </summary>
/// <remarks date="2021-Mar-25" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - Lock user access during download of FW-Update Safes and/or report uploads.
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Process state set to idle if DB is connected.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Check DB connection always, even if it was connected.
/// </remarks>
/// <remarks date="2021-May-07" author="Thomas Wiedebusch">
/// - Check register exported to separate function to avoid registration before check of FwUpdateSafes
/// availability on DB.
/// </remarks>
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;
}
/// <summary>
/// Check register exported to separate function to avoid registration before check of FwUpdateSafes
/// availability on DB.
/// </summary>
/// <remarks date="2021-May-07" author="Thomas Wiedebusch">
/// Initial
/// </remarks>
private void CheckRegisterState()
{
if (_dbAccessIsLocked) return;
if (_fwUpdateDbAccess.DbIsConnected)
{
if (!btnRegister.Enabled) btnRegister.Enabled = true;
}
}
/// <summary>
/// Enable update.
/// </summary>
/// <remarks date="2021-Feb-16" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
/// - FW-Update safe load button enabled if any FwUpdateSafe exists.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - UiInvoker.
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Extended messages.
/// </remarks>
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
/// - Added number of update safes.
/// </remarks>
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 ------------------------------------------------------
/// <summary>
/// Download FW update safes from DB task.
/// </summary>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Web API timeout message.
/// </remarks>
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;
});
}
/// <summary>
/// Download FW update safes from DB task.
/// </summary>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Web API timeout message.
/// </remarks>
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<String>();
var safesToBackupName = new List<String>();
if (SystemControl.FilesExistingInPath(FwUpdateSafeInfoBackupPath))
{
var backupSafeNamesPath = new List<String>();
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
}
}
/// <summary>
/// Upload report files to DB task.
/// </summary>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Web API timeout message.
/// </remarks>
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;
});
}
/// <summary>
/// Upload report files to DB.
/// </summary>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Web API timeout message.
/// </remarks>
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);
}
}
}
/// <summary>
/// Error processes.
/// </summary>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void ErrorProcesses()
{
// take the invoker of the error state to generate the message and / or popup window
switch (_invokerProcessState)
{
default:
_processState = ProcessState.Idle;
break;
}
}
/// <summary>
/// Stop all ongoing processes.
/// </summary>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void StopProcesses()
{
// take the invoker of the error state to generate the message and / or popup window
switch (_invokerProcessState)
{
default:
_processState = ProcessState.Idle;
break;
}
}
/// <summary>
/// Common message window.
/// </summary>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - Forcing message box being modal and on top.
/// </remarks>
private static void MessageBoxShow(String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
MessageBox.Show(text, caption, buttons, icon, MessageBoxDefaultButton.Button1,
MessageBoxOptions.ServiceNotification);
}
/// <summary>
/// Extract the FW-Update SW from FW-Update Safe.
/// </summary>
/// <remarks date="2021-Mar-23" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean ExtractFwUpdateSw()
{
try
{
var files = new List<FilePart>();
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;
}
/// <summary>
/// Open FW-Update safe and decrypt it.
/// </summary>
/// <remarks date="2021-Feb-05" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
/// - Extracted FW-Update safe name as this represents the customer name and FW-Update order number.
/// </remarks>
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
/// - Logging info.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - FW-Update safe loaded marked to stop DB connection trials.
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - Exported from button click.
/// </remarks>
/// <remarks date="2021-Mar-27" author="Thomas Wiedebusch">
/// - Process state at exit to idle.
/// </remarks>
/// <remarks date="2021-Apr-04" author="Thomas Wiedebusch">
/// - Removed Boundary Castle decoding error message.
/// - Process state set to idle and removed DB access lock on abort of open, exception or exit.
/// </remarks>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Changed exit status to force fill data grid with pcb ids,
/// - Log detected pcb ids of safe.
/// </remarks>
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
/// - Output extended to customer name and valid date and colored based on valid date.
/// </remarks>
/// <remarks date="2024-Jun-06" author="Thomas Wiedebusch">
/// - Crosscheck Loader version.
/// </remarks>
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<FwUpdateSafe>(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;
}
}
/// <summary>
/// Start of FW-Update SW.
/// </summary>
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
/// - Removed test and use real data.
/// </remarks>
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
/// - Added customer name and FW-Update order number.
/// </remarks>
/// <remarks date="2021-Mar-16" author="Thomas Wiedebusch">
/// - Locked DB access during FW-Update.
/// </remarks>
/// <remarks date="2021-Mar-18" author="Thomas Wiedebusch">
/// - load configuration.json to the AppDomain "FwUpdateLoader" as it cannot be accessed if stored in the
/// subdirectory "ServiceFwUpdateSw". ALl other files can remain there.
/// </remarks>
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
/// - Logging info.
/// </remarks>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - FW-Update SW taken from FW-Update Safe.
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - FW-Update process marked to stop DB connection trials.
/// </remarks>
/// <remarks date="2021-Mar-26" author="Thomas Wiedebusch">
/// - FW-Update SW loaded to new app domain.
/// </remarks>
/// <remarks date="2021-Mar-27" author="Thomas Wiedebusch">
/// - FW-Update SW loaded to new app domain [optional],
/// - Avoid loading of assembly if already done.
/// </remarks>
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
/// - 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.
/// </remarks>
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
/// - Send safe information.
/// </remarks>
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);
}
}
/// <summary>
/// Process bar.
/// </summary>
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-25" author="Thomas Wiedebusch">
/// - Avoid repeated execution on invisible progress bar.
/// </remarks>
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;
}
///// <summary>
///// Establish the DB connection.
///// </summary>
///// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
///// - Initial
///// </remarks>
//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;
// });
//}
/// <summary>
/// Output exclusively to user update remarks text window.
/// </summary>
private void LogText(String txtHistory)
{
Invoke(new Action(() =>
{
_logger.Info(txtHistory);
}));
}
/// <summary>
/// Neutral information to label, user text box and logger.
/// </summary>
private void InfoStatusUnknown(Control label, String msg, Boolean userTextOutput = false)
{
if (userTextOutput) LogText(msg);
UiInvoker.ControlInvoker(label, ColorUnknownStatus, msg);
}
/// <summary>
/// Success information to label, user text box and logger.
/// </summary>
private void InfoProcessActive(Control label, String msg, Boolean log = true)
{
if (log) LogText(msg);
UiInvoker.ControlInvoker(label, ColorOngoingProcess, msg);
}
/// <summary>
/// Success information to label, user text box and logger.
/// </summary>
private void InfoProcessSuccess(Control label, String msg, Boolean log = true)
{
if (log) LogText(msg);
UiInvoker.ControlInvoker(label, ColorSuccess, $@"{SuccessSign} {msg}");
}
/// <summary>
/// Failed information to label, user text box and logger.
/// </summary>
private void InfoProcessFailed(Control label, String msg, Boolean log = true)
{
if (log)
{
LogText(StrSeparator);
LogText(msg);
LogText(StrSeparator);
}
UiInvoker.ControlInvoker(label, ColorProcessFailed, $@"{FailedSign} {msg}");
}
/// <summary>
/// Common routine for disable all controls.
/// </summary>
/// <remarks date="2021-Mar-16" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Mar-24" author="Thomas Wiedebusch">
/// - UiInvoker.
/// </remarks>
private void DisableControls()
{
UiInvoker.ControlEnableInvoker(btnFwUpdate, false);
UiInvoker.ControlEnableInvoker(btnLoadFwUpdSafe, false);
UiInvoker.ControlEnableInvoker(btnRegister, false);
}
#endregion --------------------------------------- Tools ------------------------------------------------------
#region ------------------------------------------ Data Grid PCB ID -------------------------------------------
/// <summary>
/// Build data grid for PCB ID
/// </summary>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Oct-15" author="Thomas Wiedebusch">
/// - Added order number to search for successfully updated device in linked to this safe.
/// </remarks>
/// <remarks date="2023-Oct-12" author="Thomas Wiedebusch">
/// - Added customer serial number in overview.
/// </remarks>
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 -------------------------------------------------
/// <summary>
/// Send data as json stream to FW-Update SW.
/// </summary>
/// <param name="data"></param>
/// <returns>true if succeeded</returns>
/// <remarks date="2020-Dec-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
/// - Added FW update safe information for report
/// </remarks>
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 ---------------------------------------------------
/// <summary>
/// Select language at runtime: English
/// </summary>
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
/// - Change menu.
/// </remarks>
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
/// - Avoid repetition if current culture is equal to the required one.
/// </remarks>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Backup safe name for language change.
/// </remarks>
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();
}
/// <summary>
/// Select language at runtime: German
/// </summary>
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
/// - Change menu.
/// </remarks>
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
/// - Avoid repetition if current culture is equal to required.
/// </remarks>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Backup safe name for language change.
/// </remarks>
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();
}
/// <summary>
/// Change language at runtime
/// </summary>
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
/// - Initial based on code example
/// https://stackoverflow.com/questions/52178064/winforms-localization-how-to-change-the-language-of-a-menu.
/// </remarks>
/// <remarks date="2021-Apr-06" author="Thomas Wiedebusch">
/// - Check user registration added.
/// </remarks>
/// <remarks date="2021-Apr-28" author="Thomas Wiedebusch">
/// - Restore safe name after language change.
/// </remarks>
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 ---------------------------------------------------
}
}