using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Input; using Logic.ProductionToProductMapper.Cordonel; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore; using Xylem.Common.Utils.ProcessExec.EventArguments; 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 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 _tryConnectPcb; // 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; SetCordonelAccessLocked(); tbxPcbId.Visible = false; } private void FrmLowLevelTools_Load(Object sender, EventArgs e) { var version = Assembly.GetExecutingAssembly().GetName().Version; lblFwUpdateInfo.Text = $@"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); } 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() { btnClearDisplay.Enabled = false; btnSetDateTime.Enabled = false; btnGetDateTime.Enabled = false; btnFixBattery.Enabled = false; btnRebootMeter.Enabled = false; btnStoreConfiguration.Enabled = false; btnUploadConfig.Enabled = false; btnReadLogFiles.Enabled = false; btnListFileDetails.Enabled = false; btnTidyFile.Enabled = false; btnEraseFile.Enabled = false; tbxFileToEraseName.Enabled = false; tbxFileToEraseDrive.Enabled = false; } /// /// Enable all buttons except the connect button /// private void EnableToolButtons() { btnClearDisplay.Enabled = true; btnSetDateTime.Enabled = true; btnGetDateTime.Enabled = true; btnFixBattery.Enabled = true; btnRebootMeter.Enabled = true; btnStoreConfiguration.Enabled = true; btnUploadConfig.Enabled = true; btnReadLogFiles.Enabled = true; btnListFileDetails.Enabled = true; btnTidyFile.Enabled = true; btnEraseFile.Enabled = true; tbxFileToEraseName.Enabled = true; tbxFileToEraseDrive.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; SetCordonelAccessEnabled(); } #endregion #region BoardControls /// /// Connect the Cordonel /// private void Connect() { try { Init(); if (string.IsNullOrEmpty(cbComSlot.SelectedItem.ToString()) || !int.TryParse(cbComSlot.SelectedItem.ToString(), out var slotNr)) { return; } lblOverall.Text = @"Login to Cordonel..."; lblActualProcess.Text = @"Establish data base connection..."; Task.Factory.StartNew(() => { ViewProgressPcb(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.SetupFromConfigFile(slotNr); _meterBatch.AddMeter(_currentGenesis); _currentGenesis.Configuration.UseRegisterWatchService = false; _currentGenesis.Configuration.UseMinMaxCheck = false; Invoke(new Action(() => { lblActualProcess.Text = @"Connecting to PCB..."; })); _meterBatch.MetersLogin(); if (!_currentGenesis.IsLoggedOn) { //this is needed from meter as an unsuccessfully login requires a delay for the next trial Thread.Sleep(2000); _meterBatch.MetersLogin(); } if (!_currentGenesis.IsLoggedOn) { return; } _meterFile = new MeterFile(_currentGenesis); 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: " + _currentGenesis.FwVersion); LogText("Meter LUT CRC: " + _currentGenesis.LutCrc); LogText("Meter Size: " + _currentGenesis.MeterSize); LogText("Meter Region: " + _currentGenesis.Region); if (_currentGenesis.RadioFrequencyMhz != null) { tbxRadio.Text = $@"{_currentGenesis.RadioFrequencyMhz}"; LogText($"Meter radio frequency [MHz]: {_currentGenesis.RadioFrequencyMhz}"); } lblConnectPcb.ForeColor = Color.Green; CheckUpdateEnabled(); LogPcAndCordonelTime(); LogInstalledMeterFw(); })); }).ContinueWith(delegate { ViewProgressPcb(false); }); } catch (Exception ex) { MessageBox.Show(ex.Message); LogErrorText(ex.Message); } } /// /// 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.ConvertTo( _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; })); } } 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(() => { _tryConnectPcb = true; ViewProcessControl(true); })); } else { Invoke(new Action(() => { _tryConnectPcb = 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: {_currentGenesis.PcbId}"); LogText($"Core Version: {GenesisMeter.BuildFwVersionString(_currentGenesis.CoreRevision)}"); 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"; 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 == MeterFwUpdateCapability.MetrologyName && _currentGenesis.MetrologyUpgradePermission != MeterFwUpdateCapability.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}"; LogText($@"PC date time [UTC]: {msg}"); CalculateMeterDateTime(RegisterConverter.ConvertTo( _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); } /// /// Calculate and log Cordonel time. /// /// /// - Init. /// private void CalculateMeterDateTime(Int32 secSince2000) { var ts = TimeSpan.FromSeconds(secSince2000); var meterDateTime = new DateTime(2000, 1, 1, 0, 0, 0).Add(ts); var msg = $@"{meterDateTime:yyyy-MM-dd hh:mm:ss}"; Invoke(new Action(() => { tbxDateTime.Text = msg; })); LogText($@"Meter date time [UTC]: {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. /// private void LogText(String txtHistory) { InfoWindowColoredText(txtHistory, ColorDefault); } /// /// Set the actual process and log the text. /// /// /// - Color added. /// private void SetActualProcessAndLog(String message) { Invoke(new Action(() => { lblActualProcess.Text = message; })); LogText(message); } /// /// 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; } /// /// List all meter log files covered by the log index file /// /// /// - Initial /// private List ListLogFiles() { SetActualProcessAndLog(@"Flushing the logger"); _currentGenesis.WriteRegister("LOGGER_TriggerLogFlush", 1); CalculateMeterDateTime(RegisterConverter.ConvertTo( _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.ConvertTo(sample):X8}"; LogText($"Log file catalogue: {name}"); fileNames.Add(name); } 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}, File size: {fileSize} 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 (_tryConnectPcb || _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 }); // store configuration will logout btnStoreConfiguration_Click(this, null); } /// /// Set battery dependencies /// /// /// /// /// - Initial /// 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(GenesisMeter.Alarm.ALL); SetActualProcessAndLog(@"Set project end time to meter"); _currentGenesis.WriteRegister("POWERMON_WarnFromClamp", (UInt32)630720000); // 20 years in seconds SetActualProcessAndLog(@"Set meter battery quantity to 2"); _currentGenesis.WriteRegister("POWERMON_BatteryQuantity", (UInt32)2); 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); // 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; LowLevelActionControl(true); _currentGenesis.ReLogin(); _currentGenesis.StoreAllConfigurations(); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); 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); }); } /// /// 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(); // store configuration will logout btnStoreConfiguration_Click(this, null); } /// /// 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; var statusMap = new Dictionary(); if (File.Exists("Status.json")) { var dict = JsonConvert.DeserializeObject>(File.ReadAllText("Status.json")); foreach (var item in dict) { if (item.Value.First != null) statusMap.Add(int.Parse(item.Value.First.Values().First().ToString()), item.Key); } } LowLevelActionControl(true); var msg = @"Upload engineering log file from meter"; LogText(msg); lblOverall.Text = msg; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); var fileNames = ListLogFiles(); foreach (var fileName in fileNames) { SetActualProcessAndLog($@"Reading and analyzing {fileName}"); 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())}"); } 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]; 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}"); LogText(statusMap.ContainsKey(status) ? $"Status: ({status}) {statusMap[status]}" : $"Status: {status}"); LogText($"Count: {count}"); LogText($"Extra data size: {extraDataSize}"); //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())}"); } LogText(" "); ctr += (5 + extraDataSize); //exit if index for next run is out of range if (ctr + 5 >= data.Count) break; } } else { LogText("File is empty"); } } }).ContinueWith(delegate { //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); 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, System.Windows.Forms.KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { btnEraseFile_Click(this, null); } } #endregion } }