using Logic.ProductionToProductMapper.Cordonel; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Xylem.Common.CommonCore.Configuration; using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore; using Xylem.Common.Logic.SoftwareAccessHelper; using Xylem.Common.Utils.ProcessExec.EventArguments; using Register = Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Register; using String = System.String; namespace Xylem.Common.Ui.GenesisToolBox { /// /// FW update form /// public partial class FrmLowLevelTools : Form { #region Variables private readonly MeterBatch _meterBatch = new MeterBatch(); private GenesisMeter _currentGenesis; private const String StrNotConnected = "NOT CONNECTED"; private const String StrPartPcbConnected = "PCB ID: "; private const String StrBurnUpgrade = "Waiting for meter response"; private MeterFile _meterFile; private MeterPowerCorrectionFile _meterPowCorrFile; //private String _lastFwUpdateState; //private Int32 _lastSingleProgress; private Boolean _progressBarOn; private DateTimeOffset _startTime; private Boolean _resetTimeMeasurement; private readonly Boolean _updateGui = true; private MeterResetPsu _meterResetPsu; private Boolean _meterResetProcessActive; private Boolean _rebootSucceeded; private Boolean _repairPasswordEnabled; private Boolean _autoProgressBar; // external update remarks form private readonly FrmHistory _frmHistory = new FrmHistory(); private readonly Version _version; // status information private static readonly Color ColorDefault = Color.Black; private static readonly Color ColorSuccess = Color.Green; private static readonly Color ColorProcessFailed = Color.Red; //private static readonly Color ColorOngoingProcess = Color.Blue; //private static readonly Color ColorUnknownStatus = Color.Gray; //private const String SuccessSign = @"✔"; //private const String FailedSign = @"✘"; private const String StrSeparator = "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------"; #endregion #region FormControls /// /// Ctor FW update /// public FrmLowLevelTools() { InitializeComponent(); var cultureInfo = new CultureInfo("en-GB"); Thread.CurrentThread.CurrentUICulture = cultureInfo; Thread.CurrentThread.CurrentCulture = cultureInfo; _version = Assembly.GetExecutingAssembly().GetName().Version; } /// /// Clear data table and set genesis to not connected /// private void Init() { ViewProcessControl(false); lblConnectPcb.Text = StrNotConnected; lblConnectPcb.ForeColor = Color.Red; lblWaitingForMeterResponse.Visible = false; tbxDateTime.Text = ""; tbxMeterFw.Text = ""; tbxMeterLutCrc.Text = ""; tbxMeterMeterSize.Text = ""; tbxRadio.Text = ""; tbxRegion.Text = ""; SetCordonelAccessLocked(); tbxPcbId.Visible = false; btnConnect.Focus(); } private void FrmLowLevelTools_Load(Object sender, EventArgs e) { var version = Assembly.GetExecutingAssembly().GetName().Version; lblFwUpdateInfo.Text = $@"GTB Version: {version.Major}.{version.Minor}.{version.Build}"; _resetTimeMeasurement = true; Init(); var xPosition = Location.X + Size.Width; var yPosition = Location.Y; if (_frmHistory != null) { _frmHistory.SetDesktopLocation(xPosition, yPosition); _frmHistory.Show(); } LogText($"GenesisToolBox: {_version.Major}.{_version.Minor}.{_version.Build}"); LogText(StrSeparator); ddlLocation.SelectedIndex = 0; } private void FrmLowLevelTools_FormClosing(Object sender, FormClosingEventArgs e) { _frmHistory?.Close(); _currentGenesis?.Logout(); _meterBatch?.RemoveAllMeters(); _meterBatch?.Dispose(); if (_meterFile != null) { _meterFile.OnProcessUpdate -= ProcessUpdate_Event; _meterFile = null; } Dispose(); } private void cbComSlot_SelectedIndexChanged(Object sender, EventArgs e) { _resetTimeMeasurement = true; Init(); } #endregion #region ActivationControls /// /// Disable all buttons except the connect button /// private void DisableToolButtons() { btnReadPowCorr.Enabled = false; btnPulseModeOff.Enabled = false; btnClearDisplay.Enabled = false; btnSetDateTime.Enabled = false; btnGetDateTime.Enabled = false; btnFixBattery.Enabled = false; btnRebootMeter.Enabled = false; btnStoreConfiguration.Enabled = false; btnUploadConfig.Enabled = false; btnReadLogFiles.Enabled = false; btnReadBatAndLog.Enabled = false; btnListFileDetails.Enabled = false; btnTidyFile.Enabled = false; btnEraseFile.Enabled = false; tbxFileToEraseName.Enabled = false; tbxFileToEraseDrive.Enabled = false; btnUploadPWFile.Enabled = false; btnReadStatus.Enabled = false; btnRepairPassword.Enabled = false; ddlLocation.Enabled = false; } /// /// Enable all buttons except the connect button /// private void EnableToolButtons() { btnReadPowCorr.Enabled = true; btnPulseModeOff.Enabled = true; btnClearDisplay.Enabled = true; btnSetDateTime.Enabled = true; btnGetDateTime.Enabled = true; btnFixBattery.Enabled = true; btnRebootMeter.Enabled = true; btnStoreConfiguration.Enabled = true; btnUploadConfig.Enabled = true; btnReadLogFiles.Enabled = true; btnReadBatAndLog.Enabled = true; btnListFileDetails.Enabled = true; btnTidyFile.Enabled = true; btnEraseFile.Enabled = true; tbxFileToEraseName.Enabled = true; tbxFileToEraseDrive.Enabled = true; btnUploadPWFile.Enabled = true; btnReadStatus.Enabled = true; ddlLocation.Enabled = true; if (_repairPasswordEnabled) btnRepairPassword.Enabled = true; } /// /// Lock all buttons, enable the connect button /// private void SetCordonelAccessLocked() { btnConnect.Enabled = true; DisableToolButtons(); } /// /// Enable all buttons, Cordonel has to be connected /// private void SetCordonelAccessEnabled() { btnConnect.Enabled = true; EnableToolButtons(); } /// /// Disable all buttons during operation with device /// private void SetControlsLowLevelOpOngoing() { btnConnect.Enabled = false; DisableToolButtons(); } /// /// Restore setting of buttons and timeout after operation with device /// private void SetControlsLowLevelOpIsFinished() { if (_currentGenesis != null) { _currentGenesis.RequestProtocol.AdditionalRetryTimeoutMs = 0; if (!string.IsNullOrEmpty(_currentGenesis.PcbId)) SetCordonelAccessEnabled(); } } #endregion #region BoardControls /// /// Establish connection /// /// /// - Compare the max supported FW versions of the configuration.json. /// /// /// - Catch error message on unknown data type and kill meter. /// /// /// - Actions on pwdContainer error. /// /// /// - Password checks extended. /// private void Connect() { try { var sb = new StringBuilder(); Invoke(new Action(() => { Init(); if (string.IsNullOrEmpty(cbComSlot.SelectedItem.ToString()) || !int.TryParse(cbComSlot.SelectedItem.ToString(), out var slotNr)) { return; } lblOverall.Text = @"Login to Cordonel..."; lblActualProcess.Text = @"Connecting to PCB..."; LowLevelActionControl(true); _currentGenesis?.DisposeMeter(); //dispose old meter _meterBatch.RemoveAllMeters(); _currentGenesis = null; //assign new meter and assign meter to FW update file if this exists _currentGenesis = new GenesisMeter(); _currentGenesis.UseOfflinePasswords = cbxUseOfflinePwds.Checked; _currentGenesis.SetupFromConfigFile(slotNr); _meterBatch.AddMeter(_currentGenesis); _currentGenesis.Configuration.UseRegisterWatchService = false; _currentGenesis.Configuration.UseMinMaxCheck = false; })); Task.Factory.StartNew(() => { // The password needs to be cleared for recurrent connections as otherwise the last password will be used. _currentGenesis.ClearPassword(); _currentGenesis.Login(); if (!_currentGenesis.IsLoggedOn && String.IsNullOrEmpty(_currentGenesis.PcbId)) { LowLevelActionControl(false); LogErrorText("ERROR: Cannot read out PcbId! Access to Cordonel denied!"); LogText(StrSeparator); return; } var retValPwdBuild = MeterPwdHandlerDb.RequestPwdFileFromDb(_currentGenesis.PcbId, out var pwdContainer); // Login failed and password cannot be acquired, there isn't any chance to login without additional info if (!_currentGenesis.IsLoggedOn && (pwdContainer?.ListOfPasswords == null || pwdContainer.ListOfPasswords.Count < 8 || string.IsNullOrEmpty(pwdContainer.Skeleton))) { LowLevelActionControl(false); if (!_currentGenesis.UseOfflinePasswords) LogErrorText($"ERROR: Login to PcbId:{_currentGenesis.PcbId} failed and passwords cannot be acquired " + "from the database!"); else LogErrorText($"ERROR: Login with 'offline password' to PcbId:{_currentGenesis.PcbId} failed!"); LogText(StrSeparator); return; } if (_currentGenesis.IsLoggedOn) { // The login succeeded with the 'SkeletonKey', the 'Lvl8 password' or the 'offline password' if (retValPwdBuild) // Passwords exist in database { // Remind the 'Lvl8 password' from passwords var pwdLvl8FromPasswords = Encoding.UTF8.GetString(pwdContainer.ListOfPasswords.Last()); // Check if the 'offline password' is in use if (!string.IsNullOrWhiteSpace(_currentGenesis.OfflinePassword)) { LogText("Successfully logged in with the 'offline password'"); if (_currentGenesis.OfflinePassword.Equals(pwdLvl8FromPasswords)) { LogText("The 'offline password' is the 'Lvl8 password'"); if (pwdLvl8FromPasswords.Equals(pwdContainer.Password)) { // CASE 1: - The 'offline password' is valid and equals the 'Lvl8 password' and the // 'production password' LogText("The 'production password' is the 'Lvl8 password'\n" + "Installed password file is valid and compared with the database"); } else { // CASE 2: - The 'offline password' is valid and equals the 'Lvl8 password' but the // 'production password' is not correctly updated ShowPasswordRepairMessage( "The 'production password' is not correctly set to the 'Lvl8 password'\n" + "Installed password file is valid and compared with the database\n" + "[Repair Password] enabled"); } } // CASE 3: - The 'offline password' is valid and equals the 'SkeletonKey' else if (_currentGenesis.OfflinePassword.Equals(pwdContainer.Skeleton)) ShowPasswordRepairMessage( "The 'offline password' is the 'SkeletonKey'\n" + "Password exists on the database but password file not installed'\n" + "[Repair Password] enabled"); }// The 'offline password' is in use else // The 'production password' is in use { if (pwdContainer.Password.Equals(pwdLvl8FromPasswords)) { // CASE 4: - The 'production password' is the 'Lvl8 password' as the login succeeded. LogText( "Successfully logged in with the 'production password' which equals the 'Lvl8 password'\n" + "Installed password file is valid and compared with the database"); } else if (pwdContainer.Password.Equals(pwdContainer.Skeleton)) { // If the 'password file' is initially not installed and the 'production password' returned the // 'SkeletonKey', then the login has been successfully executed with this 'SkeletonKey', // a repair of the 'password file' needs to be enabled! // CASE 5: - The 'production password' is the 'SkeletonKey' as the login succeeded. ShowPasswordRepairMessage( "Successfully logged in with the 'production password' which equals the 'SkeletonKey'\n" + "Password exists on the database but password file not installed'\n" + "[Repair Password] enabled"); } } }// Database returned valid passwords else { if (string.IsNullOrEmpty(_currentGenesis.OfflinePassword)) { // CASE 6: - The login succeeded but the password file request from database failed and no offline pwd LogText("Successfully logged in with the 'production password'\n" + "Passwords cannot be acquired from the database and therefore not compared"); } else { // CASE 7: - The login succeeded but the password file request from database failed and offline pwd LogText("Successfully logged in with the 'offline password'\n" + "Passwords cannot be acquired from the database and therefore not compared"); } } }// Initial login succeeded // Login can only be tried on valid database collection of passwords if (!retValPwdBuild) { sb.AppendLine(@"Passwords cannot be acquired from the database [E0]!"); } // As the initial login wasn't successfully, try the acquired combinations from the database passwords else if (!_currentGenesis.IsLoggedOn && !_currentGenesis.UseOfflinePasswords) { // This is needed from meter as an unsuccessful login requires a delay for the next trial Thread.Sleep(2000); // Try to log in with password level 8 which may not be published to the database var retVal = _currentGenesis.Login(Encoding.UTF8.GetString(pwdContainer.ListOfPasswords.Last())); // CASE 8: - Passwords are generated in database and correctly installed. The login check will be done // with the generated password, but the 'production password' is still the 'SkeletonKey'. // - The database update of the production password went wrong! if (_currentGenesis.IsLoggedOn && retVal) { ShowPasswordRepairMessage("Successfully logged in with 'Lvl8 password'\n" + "Installed password file is valid and compared with the database!\n" + "'production password' in the database not actual, update required\n" + "[Repair Password] enabled"); } else { sb.AppendLine("Login with 'LVL8 password' failed [M1]!"); } if (!_currentGenesis.IsLoggedOn) { // This is needed from meter as an unsuccessful login requires a delay for the next trial Thread.Sleep(4000); // CASE 9: - The production password didn't work, so the SkeletonKey will be tried. if (_currentGenesis.Login(pwdContainer.Skeleton)) { ShowPasswordRepairMessage("Successfully logged in with 'SkeletonKey'\n" + "'production password' in the database not actual, update required\n" + "[Repair Password] enabled"); } else { sb.AppendLine(@"Login with 'SkeletonKey' failed [M2]!"); } if (_currentGenesis.Region.Equals("NA")) { var url = ServiceUrls.GetExportDataServiceUrl(); var retStr = LocalWebRequest.GetRequestWithError(url + _currentGenesis.PcbId, out var errorCode, 30000); if (errorCode == HttpStatusCode.OK.GetHashCode()) { var retExport = JsonConvert.DeserializeObject>(retStr); if (retExport.Count == 0) { sb.AppendLine(@"Export entry hasn't been found [E1]"); //request sb.AppendLine(@"Export returns no password [E2]"); } else { if (retExport.Count == 1) { sb.AppendLine(@"Found export entry"); } else { sb.AppendLine(@"More than one export entry was found [E4]"); } foreach (var item in retExport) { if (item.PWD == pwdContainer.Password) { sb.AppendLine($"{item.ID} is the 'Lvl8 password'"); } else if (item.PWD == pwdContainer.Skeleton) { sb.AppendLine($"{item.ID} is the 'SkeletonKey' [E3]"); } else { sb.AppendLine( $"{item.ID} is neither the 'Lvl8 password' nor the 'SkeletonKey' [E3]"); } } } } } MessageBox.Show(sb.ToString()); } // trial with skeletonKey }// trial with Lvl8 if (!_currentGenesis.IsLoggedOn) { if (!_currentGenesis.UseOfflinePasswords) LogErrorText($"ERROR: Login to PcbId:{_currentGenesis.PcbId} failed with 'production password', " + "'Lvl8 password' and 'SkeletonKey'!"); else LogErrorText($"ERROR: Login to PcbId:{_currentGenesis.PcbId} with 'offline password' failed!"); LogText(StrSeparator); return; } _meterFile = new MeterFile(_currentGenesis); LogText(StrSeparator); Invoke(new Action(() => { lblConnectPcb.Text = StrPartPcbConnected; tbxPcbId.Text = _currentGenesis.PcbId; tbxPcbId.Visible = true; tbxMeterFw.Text = _currentGenesis.FwVersion; tbxMeterMeterSize.Text = _currentGenesis.MeterSize; tbxMeterLutCrc.Text = _currentGenesis.LutCrc; tbxRegion.Text = _currentGenesis.Region; LogText("Meter FW Version:\t" + _currentGenesis.FwVersion); LogText("Meter LUT CRC:\t" + _currentGenesis.LutCrc); LogText("Meter Size:\t" + _currentGenesis.MeterSize); LogText("Meter Region:\t" + _currentGenesis.Region); if (_currentGenesis.RadioFrequencyMhz != null) { tbxRadio.Text = $@"{_currentGenesis.RadioFrequencyMhz}"; LogText("Radio frequency:\t" + $"{_currentGenesis.RadioFrequencyMhz} MHz"); } LogText(""); LogText("'Configuration.json' version: " + _currentGenesis.InterfaceInfo.InterfaceVersion); lblConfigVersion.Text = @"Interface Version: " + _currentGenesis.InterfaceInfo.InterfaceVersion; lblConfigVersion.ForeColor = _currentGenesis.InterfaceSupportsFwVersion ? Color.Green : Color.Red; if (!_currentGenesis.InterfaceSupportsFwVersion) { var text = "INTERFACE VERSION OUTDATED!\n" + "The loaded \'configuration.json\' " + $"version: {_currentGenesis.InterfaceInfo.InterfaceVersion}\n" + $"does NOT support the Cordonel FW version: {_currentGenesis.FwVersion}!"; MessageBox.Show(text, @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error); LogText(text); } else { LogText($"\'Configuration.json\' supports installed FW version: {_currentGenesis.FwVersion}"); } LogText(""); lblConnectPcb.ForeColor = Color.Green; LogPcAndCordonelTime(); LogInstalledMeterFw(); })); }).ContinueWith(delegate { LowLevelActionControl(false); }); } catch (Exception ex) { LowLevelActionControl(false); MessageBox.Show(ex.Message, @"FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error); LogErrorText(ex.Message); // Dispose meter _meterBatch.RemoveAllMeters(); // If meter is not already assigned to batch as the config reader may fail _currentGenesis?.DisposeMeter(); } } private void ShowPasswordRepairMessage(String msg) { LogText(msg); MessageBox.Show(msg); _repairPasswordEnabled = true; } /// /// Wait for reboot is completed by polling of PcbId /// private void WaitForReboot() { if (_currentGenesis == null) { return; } SetActualProcessAndLog("Update Applications and Restart Meter"); ViewProcessControl(true); //wait until meter update is completed and meter is re-booted const Double maxBootDelayMs = 10000.0; const Double loopDelayMs = 200.0; var bootDelayMs = 0.0; var text = ""; while (bootDelayMs < maxBootDelayMs && string.IsNullOrEmpty(text)) { SetTimeDisplay(); text = RegisterConverter.ByteArrayToValue( _currentGenesis.ReadRegister(Register.Configexchange.PcbSerialNumber, 12)); barOverallProgressUpdate.Value = barOverallProgressUpdate.Value + 4 > 100 ? 0 : barOverallProgressUpdate.Value + 4; var bootProcess = (Int32)(bootDelayMs * 100.0 / maxBootDelayMs); barSingleProgressUpdate.Value = barSingleProgressUpdate.Value + bootProcess > 100 ? 0 : barSingleProgressUpdate.Value + bootProcess; Update(); bootDelayMs += loopDelayMs; Thread.Sleep((Int32)loopDelayMs); } //add a blank line to separate next operation LogText(""); ViewProcessControl(false); } #endregion #region ProcessControls private void LowLevelActionControl(Boolean isActive) { if (isActive) { Invoke(new Action(() => { ViewProcessControl(true); tmrProgressUpdate.Enabled = true; _progressBarOn = true; })); } else { Invoke(new Action(() => { ViewProcessControl(false); _progressBarOn = false; CheckUpdateEnabled(); })); } } private void RebootControl(Boolean isActive) { if (isActive) { Invoke(new Action(() => { ViewProcessControl(true); tmrProgressUpdate.Enabled = true; })); } else { Invoke(new Action(() => { ViewProcessControl(false); if (_rebootSucceeded) { WaitForReboot(); btnConnect_Click(this, null); } else { var msg = "Reboot failed!"; LogText(msg); MessageBoxShow(msg, "FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error); } })); } } private void ViewProgressPcb(Boolean isActive) { if (_updateGui) { if (isActive) { Invoke(new Action(() => { _autoProgressBar = true; ViewProcessControl(true); })); } else { Invoke(new Action(() => { _autoProgressBar = false; ViewProcessControl(false); })); } } } /// /// View all process bars and labels /// private void ViewProcessControl(Boolean view) { if (view) { lblActualProcess.Visible = true; lblOverall.Visible = true; barOverallProgressUpdate.Visible = true; barSingleProgressUpdate.Visible = true; //_lastFwUpdateState = ""; tmrProgressUpdate.Enabled = true; SetControlsLowLevelOpOngoing(); } else { lblActualProcess.Visible = false; lblOverall.Visible = false; barOverallProgressUpdate.Visible = false; barSingleProgressUpdate.Visible = false; tmrProgressUpdate.Enabled = false; //_lastFwUpdateState = ""; lblWaitingForMeterResponse.Visible = false; lblActualProcess.Text = ""; lblOverall.Text = ""; SetControlsLowLevelOpIsFinished(); } //common actions and settings lblActualProcess.Update(); lblOverall.Update(); barOverallProgressUpdate.Value = 0; barOverallProgressUpdate.Update(); barSingleProgressUpdate.Value = 0; barSingleProgressUpdate.Update(); Update(); } private void StartStopDownload(Boolean isActive) { if (isActive) { Invoke(new Action(() => { //_lastFwUpdateState = ""; ViewProcessControl(true); tmrProgressUpdate.Enabled = true; })); } else { Invoke(new Action(() => { ViewProcessControl(false); })); } } /// /// Display installed meter FW. /// /// /// - Init. /// private void LogInstalledMeterFw() { if (_currentGenesis == null) { return; } LogText(StrSeparator); LogText($"PCB ID :\t{_currentGenesis.PcbId}"); LogText($"Core Version:\t{_currentGenesis.StrCoreRevision}"); LogText(""); LogText("Installed Applications:"); foreach (var app in _currentGenesis.MeterAppListVersion) { var versionString = app.IsInstalled ? $"V: {app.StrVersion} - CRC: 0x{app.Crc:X4}" : "App NOT Installed"; if (app.Status == MeterAppState.Unknown) versionString = "COMMUNICATION ERROR"; LogText($"AppId: 0x{app.AppId:X2} - {versionString} - " + $"AppName: {app.AppName}"); if (app.IsInstalled) { var appVersion = new CordonelAppVersion(app.AppId, app.StrVersion); // Copy metrology update permission if (app.AppName == MetrologyDefinition.MetrologyName && _currentGenesis.MetrologyUpgradePermission != MetrologyDefinition.MetrologyUpgradePermitted) { appVersion.IsUpdateable = false; } } } LogText(StrSeparator); } /// /// Calculate and log the PC and Cordonel time. /// /// /// - Init. /// private void LogPcAndCordonelTime() { if (_currentGenesis == null) { return; } _currentGenesis.ReLogin(); var dt = DateTime.UtcNow; var msg = $"{dt:yyyy-MM-dd HH:mm:ss} UTC"; LogText($"PC date time:\t{msg}"); CalculateMeterDateTime(RegisterConverter.ByteArrayToValue( _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); } /// /// Calculate and log Cordonel time. /// /// /// - Init. /// /// /// - Used to calculate time in UTC based on 01. Jan 2000 /// and the given offset in seconds. /// private void CalculateMeterDateTime(Int32 secSince2000) { var meterDateTime = new TimeT { SecondsSince2000 = secSince2000 }; var msg = meterDateTime.ToString(); Invoke(new Action(() => { tbxDateTime.Text = msg; })); LogText($"Meter date time:\t{msg}"); } /// /// Clear history window. /// /// /// - Initial /// private void ClearHistoryWindow() { if (_frmHistory != null && _frmHistory.rtbHistory.InvokeRequired) { _frmHistory.rtbHistory.Invoke(new Action(() => { _frmHistory.rtbHistory.Clear(); })); } else { _frmHistory?.rtbHistory.Clear(); } } /// /// Output exclusively to user update remarks text window. /// /// /// - Color added. /// /// /// - File output added. /// private void LogText(String txtHistory, String filename = null) { InfoWindowColoredText(txtHistory, ColorDefault); if (!string.IsNullOrEmpty(filename)) { File.AppendAllLines(filename, new[] { txtHistory }); } } /// /// Set the actual process and log the text. /// /// /// - Color added. /// private void SetActualProcessAndLog(String message, String logFileName = null) { Invoke(new Action(() => { lblActualProcess.Text = message; })); LogText(message, logFileName); } /// /// Output exclusively to user update remarks text window. /// private void LogErrorText(String txtHistory) { InfoWindowColoredText(txtHistory, ColorProcessFailed); } /// /// Output exclusively to user update remarks text window. /// private void LogSuccessText(String txtHistory) { InfoWindowColoredText(txtHistory, ColorSuccess); } /// /// Output exclusively to user update remarks text window. /// private void InfoWindowColoredText(String txtHistory, Color color) { if (_frmHistory?.rtbHistory == null) { return; } Invoke(new Action(() => { _frmHistory.rtbHistory.SuspendLayout(); _frmHistory.rtbHistory.SelectionStart = _frmHistory.rtbHistory.Text.Length; _frmHistory.rtbHistory.SelectionLength = 0; _frmHistory.rtbHistory.SelectionColor = color; _frmHistory.rtbHistory.AppendText($"{txtHistory}{Environment.NewLine}"); _frmHistory.rtbHistory.SelectionColor = _frmHistory.rtbHistory.ForeColor; _frmHistory.rtbHistory.ScrollToCaret(); _frmHistory.rtbHistory.ResumeLayout(); })); } #endregion #region Tools private void CheckUpdateEnabled() { //this check has been placed here to force display update if (_meterFile != null && _currentGenesis != null) { SetCordonelAccessEnabled(); return; } SetCordonelAccessLocked(); } /// /// List all meter files /// /// /// - Initial /// private List ListMeterFiles() { SetActualProcessAndLog("List meter files drive 0"); _meterFile.ReadMeterFileCatalog(out var fileNamesDrive0, MeterFile.StrMeterDrive0); if (fileNamesDrive0.Count == 0) { LogText("Could not read drive 0"); } else { foreach (var fileName in fileNamesDrive0) { LogText(fileName); } } SetActualProcessAndLog("List meter files drive 1"); _meterFile.ReadMeterFileCatalog(out var fileNamesDrive1, MeterFile.StrMeterDrive1); if (fileNamesDrive1.Count == 0) { LogText("Could not read drive 1"); } else { foreach (var fileName in fileNamesDrive1) { LogText(fileName); } } var fileNames = new List(); fileNames.AddRange(fileNamesDrive0); fileNames.AddRange(fileNamesDrive1); return fileNames; } /// /// Read engineering log files from meter and analyzes the contents. /// /// /// - Initial /// private void ReadLogFiles(String logFileName = null) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } var statusMap = new Dictionary(); if (File.Exists("Status.json")) { var dict = JsonConvert.DeserializeObject>(File.ReadAllText("Status.json")); if (dict != null) { foreach (var item in dict) { if (item.Value.First != null) { statusMap.Add(int.Parse(item.Value.First.Values().First().ToString()), item.Key); } } } } Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); var fileNames = ListLogFiles(logFileName); var rawDic = new List>(); foreach (var fileName in fileNames) { SetActualProcessAndLog($"Reading and analyzing {fileName}", logFileName); var success = _meterFile.ReadMeterFile(fileName, out var data); if (!success) { _currentGenesis.Logout(); _currentGenesis.ReLogin(); } if (data.Count > 0) { LogText($"Data: {ByteArrayStyle.ByteStyler.ToString(data.ToArray())}", logFileName); } if (data.Count >= 5) { for (var ctr = 0; ctr < data.Count;) { var logType = data[ctr]; var status = (data[ctr + 2] << 8) + data[ctr + 1]; var count = data[ctr + 3] + 1; var extraDataSize = data[ctr + 4]; String msg; switch (logType) { case 0: msg = $"({logType}) Informational"; break; case 1: msg = $"({logType}) Warning"; break; case 2: msg = $"({logType}) Critical"; break; default: msg = $"({logType}) Unknown log type"; break; } LogText($"Log type: {msg}", logFileName); LogText(statusMap.ContainsKey(status) ? $"Status: ({status}) {statusMap[status]}" : $"Status: {status}", logFileName); LogText($"Count: {count}", logFileName); LogText($"Extra data size: {extraDataSize}", logFileName); //avoid access beyond the buffer and check for data to display if (ctr + 5 + extraDataSize < data.Count && extraDataSize > 0) { var extraData = data.GetRange(ctr + 5, extraDataSize); LogText($"Extra data: {ByteArrayStyle.ByteStyler.ToString(extraData.ToArray())}", logFileName); if (status == 261) { rawDic.Add(new Tuple(fileName, ByteArrayStyle.ByteStyler.ToString(extraData.ToArray()))); } } LogText("", logFileName); ctr += (5 + extraDataSize); //exit if index for next run is out of range if (ctr + 5 >= data.Count) { break; } } } else { LogText("File is empty", logFileName); } //foreach (var rawBat in rawDic) //{ //} } var sb = new StringBuilder(); foreach (var rawBat in rawDic) { var dateSec = Convert.ToInt32(rawBat.Item1.Substring(2, rawBat.Item1.Length - 2), 16); var dt = new DateTime(2000, 01, 01).AddSeconds(dateSec); //var dateByte = ByteArrayStyle.ByteStyler.Parse(,false); sb.AppendLine($"{dt};{rawBat.Item2}"); } File.AppendAllLines(logFileName + "BAT.CSV", new[] { sb.ToString() }); }).ContinueWith(delegate { //add a blank line to separate next operation LogText("", logFileName); _currentGenesis.Logout(); LowLevelActionControl(false); }); } /// /// List all meter log files covered by the log index file /// /// /// - Initial /// private List ListLogFiles(String logFileName = null) { SetActualProcessAndLog("Flushing the logger"); _currentGenesis.WriteRegister("LOGGER_TriggerLogFlush", 1); CalculateMeterDateTime(RegisterConverter.ByteArrayToValue( _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); SetActualProcessAndLog("Reading log index file"); _meterFile.ReadMeterFile(MeterFile.StrMeterEngLogIndexFile, out var data); var fileNames = new List(); var rawData = data.ToArray(); var sample = new Byte[4]; for (var ctr = 0; ctr < data.Count; ctr += 4) { for (var x = 0; x < 4; x++) { sample[x] = rawData[ctr + x]; } var name = $"1\\{RegisterConverter.ByteArrayToValue(sample):X8}"; LogText($"Log file catalogue: {name}", logFileName); fileNames.Add(name); } LogText("", logFileName); fileNames.Sort(); return fileNames; } /// /// Analyze all meter files /// /// /// - Initial /// private void AnalyzeMeterFiles(List meterFiles) { if (meterFiles.Count == 0) { return; } LogText("Analyzing meter files"); foreach (var fileName in meterFiles) { Invoke(new Action(() => { lblActualProcess.Text = $@"Analyze file {fileName}"; })); var success = _meterFile.GetFileSize(fileName, out var fileSize); LogText(success ? $"File name: {fileName}, \tFile size: {fileSize:N0} byte" : $"File name: {fileName} Unable to access file"); if (success) { continue; } _currentGenesis.Logout(); _currentGenesis.ReLogin(); } } #endregion #region Events /// /// Process update event for displaying messages in history window /// /// /// /// /// - Initial /// public virtual void ProcessUpdate_Event(Object sender, ProcessExecEventArgs e) { LogText(e.OverallProcessMessage); } #endregion #region TimerControls private void tmrProgressUpdate_Tick(Object sender, EventArgs e) { //cbxManualControl.Visible = true; if (_autoProgressBar || _progressBarOn) { barOverallProgressUpdate.Value = barOverallProgressUpdate.Value + 1 > 100 ? 0 : barOverallProgressUpdate.Value + 1; barSingleProgressUpdate.Value = barSingleProgressUpdate.Value + 4 > 100 ? 0 : barSingleProgressUpdate.Value + 4; } else if (_meterResetProcessActive) { lblActualProcess.Text = _meterResetPsu?.GetActualOperation(); barOverallProgressUpdate.Value = barOverallProgressUpdate.Value + 1 > 100 ? 0 : barOverallProgressUpdate.Value + 1; barOverallProgressUpdate.Update(); var singleProgress = (Int32)(_meterResetPsu?.SingleFileProcessCtrPercent ?? 0); barSingleProgressUpdate.Value = singleProgress > 100 ? 100 : singleProgress; //_lastSingleProgress = singleProgress; barSingleProgressUpdate.Update(); } SetTimeDisplay(); Update(); } private void SetTimeDisplay() { var time = DateTimeOffset.UtcNow; var timeSpan = time - _startTime; lblUpdateTime.Text = $@"{(UInt32)timeSpan.TotalMinutes}:{timeSpan.Seconds:00}"; } #endregion #region ExternalCalls /// /// /// public MsgEventArgs LastMsgEvent; /// public class MsgEventArgs : EventArgs { /// /// Text to display /// public String Text; /// /// Headline /// public String Caption; /// /// Buttons /// public MessageBoxButtons Buttons; /// /// Icon /// public MessageBoxIcon Icon; } /// /// Message pop up event handler /// public event EventHandler OnMsgPopUp; private void MessageBoxShow(String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon) { if (_updateGui) { MessageBox.Show(text, caption, buttons, icon); } else { LastMsgEvent = new MsgEventArgs() { Text = text, Caption = caption, Buttons = buttons, Icon = icon }; OnMsgPopUp?.Invoke(this, LastMsgEvent); } } #endregion #region Buttons private void btnConnect_Click(Object sender, EventArgs e) { if (_resetTimeMeasurement) { _startTime = DateTimeOffset.UtcNow; _resetTimeMeasurement = false; } Connect(); } /// /// Releases the display to normal operation /// /// /// /// /// - Initial /// private void btnClearDisplay_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } SetActualProcessAndLog("Set display of meter to normal operation"); LowLevelActionControl(true); _currentGenesis.ReLogin(); _currentGenesis.SetLcdText(true, new Byte[] { 0x00, 0x00, 0x00, 0x00 }); _currentGenesis.WriteRegister(Register.Genesisflow.StoreConfiguration, 1); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); } /// /// Set battery dependencies /// /// /// /// /// - Initial /// /// /// - WarnFromClamp from 20 to 22 years (based on CSD value with 365,00 days a year /// and not a more accurate 365,25 days/year), /// - StoreConfiguration. /// private void btnFixBattery_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } //reset timer _startTime = DateTimeOffset.UtcNow; var msg = "Fix battery settings"; LogText(msg); lblOverall.Text = msg; LowLevelActionControl(true); _currentGenesis.ReLogin(); SetActualProcessAndLog("Seal display for time of setup"); _currentGenesis.WriteRegister("GENESISFLOW_SealDisplay", (UInt32)0); SetActualProcessAndLog("Reset alarms in meter"); _currentGenesis.ResetAlarm(Alarm.ALL); SetActualProcessAndLog("Set meter lifetime before empty warning"); _currentGenesis.WriteRegister("POWERMON_WarnFromClamp", (UInt32)693792000); // 22 years in seconds SetActualProcessAndLog("Set meter battery quantity to 2"); _currentGenesis.WriteRegister("POWERMON_BatteryQuantity", (UInt32)2); SetActualProcessAndLog("Store settings"); _currentGenesis.WriteRegister("POWERMON_StoreConfiguration", (UInt32)1); SetActualProcessAndLog("Trigger idle to release function to normal operation"); _currentGenesis.WriteRegister("GENESISFLOW_TriggerIdle", (UInt32)0); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); LowLevelActionControl(false); // reboot the meter to take the new settings btnRebootMeter_Click(this, null); } /// /// Store all configurations /// /// /// /// /// - Initial /// private void btnStoreConfiguration_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } //reset timer _startTime = DateTimeOffset.UtcNow; var msg = "Store all configurations in meter"; LogText(msg); lblActualProcess.Text = msg; Task.Factory.StartNew(() => { LowLevelActionControl(true); _currentGenesis.ReLogin(); _currentGenesis.StoreAllConfigurations(); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); }).ContinueWith(delegate { LowLevelActionControl(false); }); } /// /// Reboot the meter /// /// /// /// /// - Initial /// private void btnRebootMeter_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } //reset timer _startTime = DateTimeOffset.UtcNow; var msg = "Reboot meter with pseudo FW update"; LogText(msg); lblOverall.Text = msg; // clear display before store configuration will include store all configurations btnClearDisplay_Click(this, null); _meterResetPsu = new MeterResetPsu(_currentGenesis) { StoreConfigEnable = false }; _meterResetProcessActive = true; SetControlsLowLevelOpOngoing(); RebootControl(true); Task.Factory.StartNew(() => { _rebootSucceeded = _meterResetPsu.MeterResetPsuExecute(); Thread.Sleep(1000); _meterResetProcessActive = false; }).ContinueWith(delegate { RebootControl(false); _meterResetPsu.Dispose(); }); } /// /// Read config file from meter and store it to disk. /// /// /// - Initial /// private void btnUploadConfig_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "Upload configuration file from meter"; LogText(msg); lblOverall.Text = msg; lblActualProcess.Text = @"Read config file"; var rawData = new List(); Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); _meterFile.ReadMeterFile(MeterFile.StrMeterConfigFile, out var data); rawData.AddRange(data); }).ContinueWith(delegate { Invoke(new Action(() => { if (saveConfigFile.ShowDialog() == DialogResult.OK) { var fs = new FileStream(saveConfigFile.FileName, FileMode.OpenOrCreate); var bw = new BinaryWriter(fs); foreach (var t in rawData) { bw.Write(t); } fs.Close(); } })); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); }); } /// /// Read power correction information. /// /// /// - Initial /// private void btnReadPowCorr_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterPowCorrFile == null) { _meterPowCorrFile = new MeterPowerCorrectionFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "Upload power correction file from meter"; LogText(msg); lblOverall.Text = msg; lblActualProcess.Text = @"Read power correction file"; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); if (_meterPowCorrFile.ReadPowCorrMeterFile()) { foreach (var line in _meterPowCorrFile.FileContent) { LogText(line); } } else { LogText("Power correction file not existing!"); } }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); }); } /// /// Read config file from meter and store it to disk. /// /// /// - Initial /// /// /// - Redirect pwd hash to logging window. /// private void btnUploadPasswordFile_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "Upload hashed password file from meter:"; LogText(msg); lblOverall.Text = msg; lblActualProcess.Text = @"Read password file"; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); var retVal = _meterFile.ReadMeterFile(MeterPwdFile.StrPasswordFileName, out var hashedMeterPwdFile); // Separate all hashed passwords for logging each in an individual line var hashedPwdList = new List(); if (retVal && hashedMeterPwdFile != null && hashedMeterPwdFile.Count > 0) { for (var idx = 0; idx < hashedMeterPwdFile.Count; idx += MeterPwdDb.HashedPwdLength) { var hashedPwd = BitConverter.ToString(hashedMeterPwdFile.GetRange(idx, MeterPwdDb.HashedPwdLength).ToArray()); hashedPwdList.Add(hashedPwd); } foreach (var line in hashedPwdList) { LogText(line); } } else { LogText("Password file not existing!"); } }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); }); } /// /// Set date and time from PC to Cordonel /// /// /// /// /// - Initial /// private void btnSetDateTime_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } var msg = "Set actual PC date and time in UTC"; LogText(msg); lblOverall.Text = msg; SetActualProcessAndLog("Set seconds since 2000-01-01 00:00:00 to meter"); LowLevelActionControl(true); _currentGenesis.ReLogin(); var ts = DateTimeOffset.UtcNow - new DateTimeOffset(2000, 1, 1, 0, 0, 0, new TimeSpan(0)); _currentGenesis.WriteRegister("SYSTEM_CalendarSeconds", Convert.ToInt32(ts.TotalSeconds)); LogPcAndCordonelTime(); //add a blank line to separate next operation LogText(""); // SYSTEM does not support store configuration, just logout _currentGenesis.Logout(); LowLevelActionControl(false); } /// /// Get date and time from PC and Cordonel and record it /// /// /// /// /// - Initial /// private void btnGetDateTime_Click(Object sender, EventArgs e) { LogPcAndCordonelTime(); } /// /// Read engineering log files from meter and analyzes the contents. /// /// /// - Initial /// private void btnReadLogFiles_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "Upload engineering log file from meter"; LogText(msg); lblOverall.Text = msg; ReadLogFiles(); } /// /// Collect lifetime information and production status. /// /// /// - Initial /// private void btnReadLogFilesBattery_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } LowLevelActionControl(true); //reset timer _startTime = DateTimeOffset.UtcNow; var sbMSG = new StringBuilder(); var genesisStatus = new GenesisStatus(); var msg = "Collect all information"; LogText(msg); lblOverall.Text = msg; var filename = $"LogAndCurrentBattery_{_currentGenesis.PcbId}_Station-{ddlLocation.Items[ddlLocation.SelectedIndex]}.log"; File.AppendAllLines(filename, _frmHistory.rtbHistory.Lines); LogText($"Station: {ddlLocation.Items[ddlLocation.SelectedIndex]}", filename); Task.Factory.StartNew(() => { sbMSG.Append(GenesisStatusHandler.LifeTimeInformationString(_currentGenesis, genesisStatus)); if (sbMSG.ToString().Contains(GenesisStatusHandler.ERROR_MARKER)) { LogErrorText(sbMSG.ToString()); } else { LogSuccessText(sbMSG.ToString()); } ReadLogFiles(filename); try { var resp = LocalWebRequest.GetRequest($"http://10.49.40.25/MeterProcessState/api/FinalCheck/GetKitronProductionResults?PcbID={_currentGenesis.PcbId}", 8000); LogText(resp, filename); var sbRegister = new StringBuilder(); var LedMode = RegisterConverter.ByteArrayToValue(_currentGenesis.ReadRegister("GENESISFLOW_LedMode")); var SampleRate = RegisterConverter.ByteArrayToValue(_currentGenesis.ReadRegister("GENESISFLOW_SampleRate")); sbRegister.AppendLine($"LedMode is {LedMode}"); sbRegister.AppendLine($"SampleRate is {SampleRate}"); if (LedMode != 0) { _currentGenesis.WriteRegister("GENESISFLOW_LedMode", 0); sbRegister.AppendLine("Change Led mode to CustomerMode (0)"); } if (SampleRate != 2) { _currentGenesis.WriteRegister("GENESISFLOW_SampleRate", 2); sbRegister.AppendLine("Change SampleRate mode to CustomerMode (2)"); } } catch (Exception exception) { _currentGenesis.WriteLog(exception.Message); } }).ContinueWith(delegate { Invoke(new Action(() => { LowLevelActionControl(false); })); }); } /// /// List file details from meter. /// /// /// - Initial /// private void btnListFileDetails_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "List file details of meter"; LogText(msg); lblOverall.Text = msg; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); var fileNames = ListMeterFiles(); AnalyzeMeterFiles(fileNames); }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); }); } /// /// Tidy the file system: /// - Erasing upgrade files left over from unsuccessfully FW update, /// - Erase logging files for versions not covered by the actual region as /// during development a reprogramming from EMEA to NA and vice versa will /// leave the logs for thr other version in, /// - Remove the test file for EMEA, as this is the placeholder for the FW /// update over the air to keep the space reserved for this process (250 kB), /// - Keep important files listed in the log index. /// /// /// - Initial /// private void btnTidyFile_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "Tidy file system"; LogText(msg); lblOverall.Text = msg; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); // catalogue before cleaning SetActualProcessAndLog("List meter files before cleaning"); // build file erase candidates for all releases var fileEraseCandidates = new List(); // put initially all files in as erase candidates var meterFiles = ListMeterFiles(); AnalyzeMeterFiles(meterFiles); fileEraseCandidates.AddRange(meterFiles); // build list of files needed to keep var filesToKeep = new List(); filesToKeep.AddRange(MeterFile.FilesToKeep); // for EMEA the NALogs are waste if (_currentGenesis.Region.Contains("EMEA")) { LogText("EMEA version detected"); filesToKeep.AddRange(MeterFile.EmeaLogs); } // for EMEA the EMEALogs are waste if (_currentGenesis.Region.Contains("NA")) { LogText("NA version detected"); filesToKeep.AddRange(MeterFile.NaLogs); } // red the log files and leave it in var logFiles = ListLogFiles(); filesToKeep.AddRange(logFiles); // remove files which should be kept in place foreach (var meterFile in meterFiles) { foreach (var fileToKeep in filesToKeep) { if (meterFile.Contains(fileToKeep)) { fileEraseCandidates.Remove(meterFile); } } } // check if something to clean if (fileEraseCandidates.Count == 0) { LogText("File system is clean"); } else { // erase files foreach (var fileToErase in fileEraseCandidates) { Invoke(new Action(() => { lblActualProcess.Text = $@"Erase {fileToErase}"; })); _meterFile.UnlockEraseWriteMeterFile(fileToErase); var success = _meterFile.EraseMeterFile(fileToErase); LogText(success ? $"File {fileToErase} successfully erased" : $"File {fileToErase} erasure failed"); if (success) { continue; } _currentGenesis.Logout(); _currentGenesis.ReLogin(); } // catalogue after cleaning SetActualProcessAndLog("List meter files after cleaning"); ListMeterFiles(); LogText("File system is cleaned up"); } }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); }); } /// /// Erase a specified file /// /// /// /// /// - Initial /// private void btnEraseFile_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } if (_meterFile == null) { _meterFile = new MeterFile(_currentGenesis); } //reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var file = $@"{tbxFileToEraseDrive.Text}\{tbxFileToEraseName.Text}"; var msg = $"Erase file {file}"; LogText(msg); lblOverall.Text = msg; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); // erase files _meterFile.UnlockEraseWriteMeterFile(file); var success = _meterFile.EraseMeterFile(file); LogText(success ? $"File {file} successfully erased" : $"File {file} erasure failed"); // catalogue after cleaning SetActualProcessAndLog("List meter files"); ListMeterFiles(); }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); Invoke(new Action(() => { tbxFileToEraseName.Focus(); })); }); } private void tbxFileToEraseName_KeyDown(Object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { btnEraseFile_Click(this, null); } } /// /// Switch pulse mode to OFF /// /// /// /// /// - Initial /// private void btnPulseModeOff_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } //reset timer _startTime = DateTimeOffset.UtcNow; var msg = "Switch pulse mode OFF"; LogText(msg); lblActualProcess.Text = msg; LowLevelActionControl(true); _currentGenesis.ReLogin(); _currentGenesis.WriteRegister(Register.Metrologyasst.PulseMode, 0); _currentGenesis.WriteRegister(Register.Metrologyasst.StoreConfiguration, 1); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); } /// /// Read status /// /// /// /// /// - Initial /// private void btnReadStatus_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } LowLevelActionControl(true); var sbMSG = new StringBuilder(); var genesisStatus = new GenesisStatus(); Task.Factory.StartNew(() => { sbMSG.Append(GenesisStatusHandler.LifeTimeInformationString(_currentGenesis, genesisStatus)); }).ContinueWith(delegate { Invoke(new Action(() => { LowLevelActionControl(false); if (sbMSG.ToString().Contains(GenesisStatusHandler.ERROR_MARKER)) { LogErrorText(sbMSG.ToString()); } else { LogSuccessText(sbMSG.ToString()); } })); }); } /// /// Read status /// /// /// /// /// - Initial /// /// /// - Check if new password is in the meter with login level 8 even if the file write wasn't successful, /// - Set new password in production database, /// - Check password level 3 as all applications need to use this, /// - Validate production password. /// private void btnRepairPassword_Click(Object sender, EventArgs e) { if (_currentGenesis == null) { return; } // Reset timer _startTime = DateTimeOffset.UtcNow; LowLevelActionControl(true); var msg = "Repair password"; LogText(msg); lblOverall.Text = msg; Task.Factory.StartNew(() => { // Request password file and force generation SetActualProcessAndLog("Request passwords from the database"); var ret = MeterPwdHandlerDb.RequestPwdFileFromDb(_currentGenesis.GetPcbId(), out var pwdContainer); if (pwdContainer?.ListOfHashes == null || pwdContainer.ListOfPasswords == null || !ret) { LogText(@"Passwords from server corrupted [M3]!"); LowLevelActionControl(false); return; } // Build password file SetActualProcessAndLog("Build 'password file'"); var _meterPwdFile = new MeterPwdFile(_currentGenesis); var retValBol = _meterPwdFile.BuildPwdFile(pwdContainer.ListOfPasswords, 1, pwdContainer.Skeleton); if (!retValBol) { LogText(@"Unable to build 'password file' [M3]!"); LowLevelActionControl(false); return; } // Install password file and verify byte wise SetActualProcessAndLog("Install 'password file' into meter, read it out and verify it"); _currentGenesis.ReLogin(); retValBol = _meterPwdFile.UnlockEraseWriteMeterPwdFile(); retValBol &= _meterPwdFile.WriteMeterPwdFile() && _meterPwdFile.VerifyMeterPwdFile(); if (!retValBol) { LogErrorText(@"ERROR: Installed 'password file' check failed [M4]!"); return; } // Check if new password is in the meter with login level 8 even if the file write wasn't successful SetActualProcessAndLog("Validate 'Lvl8 password' from 'password file'"); _currentGenesis.Logout(); retValBol = _currentGenesis.Login(Encoding.UTF8.GetString(pwdContainer.ListOfPasswords.Last())); if (!retValBol) { LogErrorText(@"ERROR: Login with 'Lvl8 password' failed [M4]!"); return; } LogText(@"Successfully installed the 'password file'"); // Set new password in production database SetActualProcessAndLog( "Replace 'SkeletonKey' with 'Lvl8 password' as 'production password' in the database"); retValBol = MeterPwdHandlerDb.SetPwdFromSkeletonToLvl8inDd(_currentGenesis.PcbId, pwdContainer); if (!retValBol) { LogErrorText(@"ERROR: Update of 'production password' in the database failed [E3]!"); return; } LogText(@"Successfully updated 'production password' in the database"); // Check password level 3 as all applications need to use this SetActualProcessAndLog("Validate 'Lv3 password' as 'SkeletonKey' in meter (used by meter-apps)"); retValBol = _currentGenesis.WriteRegister("SYSTEM_ExitReason", new Byte[] { 21 }); var exitResult = RegisterConverter.ByteArrayToValue(_currentGenesis.ReadRegister("SYSTEM_ExitReason")); if (exitResult != 0 || !retValBol) { LogErrorText(@"ERROR: Meter reports wrong 'SkeletonKey' at 'Lvl3 password' [M4]!"); return; } LogText(@"Meter reports correct 'SkeletonKey' as 'Lvl3 password'"); // Clear intermediate stored password to force password collection from DB on simple login without // given new password. Then the password will be taken from the DB or offline file. SetActualProcessAndLog("Crosscheck 'production password' with meter 'Lvl8 password"); _currentGenesis.Logout(); _currentGenesis.ClearPassword(); // If the password is not set (Meter.ClearPassword) the actual valid production password will be // requested from the DB in the GenesisMeter Login() without parameter. Thread.Sleep(1000); // Validate production password retValBol = _currentGenesis.Login(); if (!retValBol) { LogErrorText(@"ERROR: Invalid 'production password' in the production database [E3]!"); return; } LogText(@"Successfully validated 'Lvl8 password' of the production database"); _repairPasswordEnabled = false; }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); Connect(); }); } #endregion public class PwdExport { public Int32 ID; public String PWD; public DateTime ExportDate; public PwdExport(Int32 id, String pwd, DateTime exportDate) { ID = id; PWD = pwd; ExportDate = exportDate; } } private void cbxUseOfflinePwds_CheckedChanged(Object sender, EventArgs e) { cbxUseOfflinePwds.ForeColor = cbxUseOfflinePwds.Checked ? Color.Red : Color.Black; } } }