using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using FluentAssertions; using LaaPackages.Features.Cordonel.Models; using LaaPackages.Features.Cordonel.Models.Values; using Logic.ProductionToProductMapper.Files.Fw; using NUnit.Framework; using NCrunch.Framework; using Newtonsoft.Json; using TechTalk.SpecFlow; using Xylem.Common.CommonCore.Consts; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus; using Xylem.Common.Hardware.WaterMeter.Genesis.MockGenesis; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Logic.ProductionOrderCore; using Xylem.Common.Production.ProductionUiCordonel.Model; using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses; using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses; using Xylem.Common.Utils.ClassAccess; namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps { [Binding] public class CommonSteps { #region -------------------------------- Production State Properties ------------------------------------------ /// /// Name of the method to be tested: /// - This will be used to assign dynamic a function to be checked, /// - If it is null or empty, it is not assigned. /// private String MethodToTest { set; get; } /// /// Marker for a private method. /// private Boolean IsPrivateMethod { set; get; } /// /// Name of the property to be checked: /// - This will be used to assign dynamic a property to be checked. /// private String PropertyToCheck { set; get; } /// /// Name the object type holding the results of the test including the property: /// - This will be used to assign dynamic a property to be checked. /// private String ObjectTypeName { set; get; } /// /// Required to test the EOL progress and combine it with the individual result: /// - The required EOL progress will be crosschecked with the result of the EOL progress by eventually /// several individual methods or by preceding production processes. /// private Boolean TestEolProgress { set; get; } /// /// The name of the production process to test. This is needed to call the correct type in the /// /// private String TestName { set; get; } /// /// The exec path of the assembly for debug in environment. /// private readonly String _assemblyExecRootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ""; /// /// Fw package path relative to solution main folder. /// private readonly String _mockFwPackageRootPath; /// /// Raw initialization parameters (unsorted and unfiltered for Order, VAKO and CSD priority) path relative to /// solution main folder. /// private readonly String _mockRawParamsRootPath; /// /// Test result container /// private struct TestResult { public Int32 TestNo { set; get; } public EOLProgressModel EOLProgress { set; get; } /// /// Used if property cannot be checked by an EOL progress. /// public StatusReturn StatusReturn { set; get; } /// /// Feedback message from tested routines /// public String FeedbackMsg { set; get; } } /// /// Test definition container for test-table entries /// private struct TestRequirement { public Int32 TestNo { set; get; } public String Comment { set; get; } public MockGenesis? Meter { set; get; } /// /// Production requirements for check of production status and special requirements /// public CordonelRequirements ProductionRequirements { set; get; } /// /// EOL Progress as input where every single EOL property will be used to set the expected feedback /// to fulfill the single test. /// public EOLProgressModel RequiredEOLProgress { set; get; } /// /// Status of the Cordonel /// public GenesisStatus CordonelDeviceStatus { set; get; } /// /// Calibration register restorer of Cordonel /// public RegisterRestorer CalibCheckRegisterRestorer { set; get; } /// /// Register restorer of Cordonel for parametrization /// public RegisterRestorer RegisterRestorer { set; get; } /// /// Production status of the Cordonel /// public List TestBenchState { set; get; } /// /// Used for partially or full compare on string result return /// public Boolean PartiallyCompareStringResult { set; get; } /// /// Loaded FW package from local disk /// public List FwUpdatePackage { set; get; } /// /// Used if property cannot be checked by an EOL progress. /// public StatusReturn RequiredStatusReturn { set; get; } /// /// Control to fail the Kitron radio param test /// public Boolean KillRadioParam { set; get; } } /// /// List of names needed to be executed in advance to the real test. /// private readonly List _ppNamesExecSequence = new List(); /// /// Test cases are the individual tests /// private readonly List _testCases = new List(); /// /// Test results after execution /// private readonly List _testResults = new List(); /// /// Presets needed to pass preceding tests or initialize some properties to a defined state /// private String _presetTestCaseJson = ""; /// /// Presets needed to pass preceding tests or initialize some properties to a defined state /// private TestRequirement _presetTestCase; #endregion ----------------------------- Production State Properties ------------------------------------------ #region -------------------------------- Ctor ----------------------------------------------------------------- /// /// Ctor to crete directories depending on test runner. /// /// /// - Initial. /// public CommonSteps() { MethodToTest = ""; PropertyToCheck = ""; ObjectTypeName = ""; TestName = ""; var subFolder = Path.Combine("Hardware", "WaterMeter", "Genesis", "MockGenesis"); var solutionPath = ""; if (NCrunchEnvironment.NCrunchIsResident()) { solutionPath = NCrunchEnvironment.GetOriginalSolutionPath().Replace("Common.sln", ""); } else { if (!string.IsNullOrWhiteSpace(_assemblyExecRootPath)) { // We are in the Unit test folder "..\Common\Production\ProductionUiCordonel.UnitTests\bin\Debug" // and need to go back to "..\Common" var directoryInfo = Directory.GetParent(_assemblyExecRootPath)?.Parent?.Parent?.Parent?.Parent; if (directoryInfo != null) { solutionPath = directoryInfo.FullName; } } } _mockFwPackageRootPath = Path.Combine(solutionPath, subFolder, "MockFwExamples"); _mockRawParamsRootPath = Path.Combine(solutionPath, subFolder, "MockRawParamsExamples"); Console.WriteLine($@"UNIT TEST MODULE: Search path for MockFwExamples: {_mockFwPackageRootPath}"); Console.WriteLine($@"UNIT TEST MODULE: Search path for MockRawParamsExamples: {_mockRawParamsRootPath}"); } #endregion ----------------------------- Ctor ----------------------------------------------------------------- #region -------------------------------- Test definition input interfaces from *.feature files ---------------- // Multiple *.feature files are going to use those step definitions [Given(@"Initialize new test scenario for production process '(.*)'")] public void InitializeNewTestScenario(String productionProcessName) { TestName = productionProcessName; // As here a new test step will be initialized and possibly the test-case table needs to be pre-filled for // more complex tests of a huge routine to keep running until the real test to be executed will be reached, // the test-cases and results have to be cleared here! _testCases.Clear(); _testResults.Clear(); _ppNamesExecSequence.Clear(); _presetTestCaseJson = ""; } [Given(@"Name the property to be checked for '(.*)'")] public void GivenNameThePropertyToBeCheckedFor(String propertyName) { PropertyToCheck = propertyName; } [Given(@"Name the object type holding the property '(.*)'")] public void GivenNameTheObjectTypeHoldingTheProperty(String objTypeName) { ObjectTypeName = objTypeName; } [Given(@"Name the private function to be checked for functionality '(.*)'")] public void GivenNameThePrivateFunctionToBeCheckedForFunctionality(String functionToCheck) { MethodToTest = functionToCheck; IsPrivateMethod = true; } [Given(@"Name the public function to be checked for functionality '(.*)'")] public void GivenNameThePublicFunctionToBeCheckedForFunctionality(String functionToCheck) { MethodToTest = functionToCheck; IsPrivateMethod = false; } [Given(@"Activate EOL progress check")] public void GivenActivateEOLProgressCheck() { TestEolProgress = true; } [Given(@"Define production process for sequencing before test '(.*)'")] public void GivenDefineForSequencingBeforeTest(String ppNameExecSequence) { _ppNamesExecSequence.Add(ppNameExecSequence); } [Given(@"Input initializations for the test matrix and presets to pass the preceding tests")] public void GivenInputInitializationsForTheTestMatrixAndPresetsToPassThePrecedingTests(Table table) { ReadPresetTable(table); } [Given(@"Input matrix defining the test cases and results")] public void GivenInputMatrixDefiningTheTestCasesAndResults(Table table) { ReadTestTable(table); } [When(@"Execute the generic tests")] public void WhenExecuteTheGenericTests() { ExecTests(); } [Then(@"Check the generic test results")] public void ThenCheckTheGenericTestResults() { VerifyTests(); } #endregion ----------------------------- Test definition input interfaces from *.feature file ----------------- #region -------------------------------- Common --------------------------------------------------------------- /// /// Verify all test cases and compare required-result with result coming from the method under test. /// /// /// - Initial. /// /// /// - Check entire EOL progress on request as this defines which tests have been executed or skipped. /// /// /// - Avoid string being null, instead set it to empty string "". /// private void VerifyTests() { // Loop all test cases foreach (var testCase in _testCases) { Boolean status; var strTestCaseExpectedStatus = ""; var strTestResultStatus = ""; // For StatusReturn the property to check is null var strPropertyToCheck = PropertyToCheck == null ? "" : $" of the Property: \"{PropertyToCheck}\""; var strErrorMsgForGetPropertyCall = $"TestNo: {testCase.TestNo}\n" + $"Could not access property value {strPropertyToCheck} of the Object: \"{ObjectTypeName}\""; // Loop test cases switch (ObjectTypeName) { case "EOLStatus": status = ClassAccess.GetPropertyValue(testCase.RequiredEOLProgress, PropertyToCheck, out var eolStatus); status.Should().BeTrue(strErrorMsgForGetPropertyCall); strTestCaseExpectedStatus = GetEOLStatusNameFromStatus(eolStatus); break; case "StringReturn": status = ClassAccess.GetPropertyValue(testCase.RequiredEOLProgress, PropertyToCheck, out var strStringResult); status.Should().BeTrue(strErrorMsgForGetPropertyCall); strTestCaseExpectedStatus = strStringResult; break; case "StatusReturn": strTestCaseExpectedStatus = GetStatusReturnNameFromStatus(testCase.RequiredStatusReturn); break; } // Find the according test result based on the TestNo foreach (var testResult in _testResults.Where(t => t.TestNo == testCase.TestNo)) { switch (ObjectTypeName) { case "EOLStatus": status = ClassAccess.GetPropertyValue(testResult.EOLProgress, PropertyToCheck, out var eolStatus); status.Should().BeTrue(strErrorMsgForGetPropertyCall); strTestResultStatus = GetEOLStatusNameFromStatus(eolStatus); break; case "StringReturn": status = ClassAccess.GetPropertyValue(testResult.EOLProgress, PropertyToCheck, out var strStringResult); status.Should().BeTrue(strErrorMsgForGetPropertyCall); strTestResultStatus = strStringResult; break; case "StatusReturn": strTestResultStatus = GetStatusReturnNameFromStatus(testResult.StatusReturn); break; } Boolean doesMatchCompare; String strResultHeader; // Check optional entire sequenced EOL progress if (TestEolProgress) { doesMatchCompare = EOLHandler.CompareEolProgresses(testCase.RequiredEOLProgress, testResult.EOLProgress, out _); strResultHeader = $"TestNo: {testCase.TestNo}\n" + $"Test Comment: \"{testCase.Comment}\"\n"; Assert.That(doesMatchCompare, strResultHeader + @"ERROR: Expected EOL status doesn't match the result EOL status"); Console.WriteLine(strResultHeader + @"SUCCESS: Expected EOL status equals the result EOL status"); } if (ObjectTypeName.Equals("StringReturn")) { // Avoid null for strings if (string.IsNullOrEmpty(strTestResultStatus)) strTestResultStatus = ""; if (string.IsNullOrEmpty(strTestCaseExpectedStatus)) strTestCaseExpectedStatus = ""; } doesMatchCompare = testCase.PartiallyCompareStringResult ? strTestResultStatus.Contains(strTestCaseExpectedStatus) : strTestResultStatus.Equals(strTestCaseExpectedStatus); // Generate an appropriate message as result for debug strResultHeader = !TestEolProgress ? $"TestNo: {testCase.TestNo}\n" + $"Test Comment: \"{testCase.Comment}\"" : "" + $"Test Result Status: \"{strTestResultStatus}\"" + strPropertyToCheck + $" of the Object: \"{ObjectTypeName}\""; var strMethodFeedbackMsg = ""; if (!string.IsNullOrEmpty(testResult.FeedbackMsg)) strMethodFeedbackMsg = $"\nFeedback message from method: \"{testResult.FeedbackMsg}\""; Assert.That(doesMatchCompare, strResultHeader + $"\nERROR: Expected Status: \"{strTestCaseExpectedStatus}\"" + $" - Test Result Status: \"{strTestResultStatus}\"" + strMethodFeedbackMsg); // Success output for verbosity var strResultSuccess = strResultHeader + $"\nSUCCESS: Expected Status: \"{strTestCaseExpectedStatus}\"" + $" - Test Result Status: \"{strTestResultStatus}\"" + strMethodFeedbackMsg + "\n"; Console.WriteLine(strResultSuccess); } } } /// /// Tests execution for all defined test in test data table /// /// /// - Initial. /// /// /// - Branch to production process type, fill with special objects and use CommonCallClassCheck. /// /// /// - Init the test result depending on test case and production process. /// /// /// - Improved parameter load. /// /// /// - Test the missing parameter-file and the missing CSD in registerRestorer, /// - Improved load of unmodified parameters. /// private void ExecTests() { var regionForParams = "?"; var meterSizeForParams = "?"; var unmodifiedParamsUsableAsReferenceForNextTest = false; var intermediateRecoveryRegisters = new List(); foreach (var testCase in _testCases) { // Prepare one test result for an entire sequence to test var testResult = new TestResult { TestNo = testCase.TestNo, EOLProgress = new EOLProgressModel { Id = testCase.RequiredEOLProgress.Id, PcbId = testCase.RequiredEOLProgress.PcbId, StartDate = testCase.RequiredEOLProgress.StartDate } }; // Put the test production process at the end of the list of production processes needed to be executed // in advance to this test but avoid a multiple input if (_ppNamesExecSequence.All(pp => !pp.Contains(TestName))) _ppNamesExecSequence.Add(TestName); // Marker if parameter check may pass the test var paramsCheckWillSucceed = true; // Execute a test sequence for a complex test foreach (var ppName in _ppNamesExecSequence) { IProductionProcess testPp; switch (ppName) { case "CheckProductionState": testPp = new CheckProductionState("UnitTestCheckProductionState") { // Assign test bench state TestBenchState = testCase.TestBenchState, // Assign calibration values CalibCheckRegisterRestorer = testCase.CalibCheckRegisterRestorer, // Assign firmware package FwUpdatePackage = testCase.FwUpdatePackage }; CommonCallClassCheck(testPp, testCase, ref testResult); break; case "ExecFinalParametrization": // Load parameters if (meterSizeForParams != testCase.Meter?.MeterSize || regionForParams != testCase.Meter?.Region || !unmodifiedParamsUsableAsReferenceForNextTest) { var rawProgParams = new List(); if (!RegisterRestorer.ImportRawProgrammingParametersFromFile(_mockRawParamsRootPath, testCase.Meter?.Region, testCase.Meter?.MeterSize, rawProgParams)) { var errorMsg = $"TestNo: {testCase.TestNo}\n ERROR: Could not load programming parameters\n"; Console.WriteLine(errorMsg); // Force failed test as parameters missing testCase.RequiredEOLProgress.ParametrizationDoneChecked = EOLStatus.FAIL; testCase.RequiredEOLProgress.ParametrizationCompareChecked = EOLStatus.FAIL; } else if (StatusReturn.Failed == RegisterRestorer.RawProgrammingParametersFilterAndSort(rawProgParams, testCase.RegisterRestorer.RecoveryRegisters, out _, fieldUpdate: false)) { var errorMsg = $"TestNo: {testCase.TestNo}\n ERROR: Missing programming parameters (e.g. CSD)\n"; Console.WriteLine(errorMsg); // Force failed test as CSD parameters missing testCase.RequiredEOLProgress.ParametrizationDoneChecked = EOLStatus.FAIL; testCase.RequiredEOLProgress.ParametrizationCompareChecked = EOLStatus.FAIL; } intermediateRecoveryRegisters.Clear(); intermediateRecoveryRegisters.AddRange(testCase.RegisterRestorer.RecoveryRegisters); meterSizeForParams = testCase.Meter?.MeterSize; regionForParams = testCase.Meter?.Region; unmodifiedParamsUsableAsReferenceForNextTest = true; } // Take the parameters from the last run if those haven't changed meanwhile (identical meter size and region) else { testCase.RegisterRestorer.RecoveryRegisters = intermediateRecoveryRegisters; } if (testCase.RequiredEOLProgress.ParametrizationDoneChecked == EOLStatus.FAIL) { // Modify one parameter to fail the writing as it is out of range testCase.RegisterRestorer.RecoveryRegisters.Add( new RecoveryRegisterItem("GENESISFLOW_MeterSize", new Byte[] { 0xFF })); unmodifiedParamsUsableAsReferenceForNextTest = false; } else if (testCase.KillRadioParam) { var removeProPar = testCase.RegisterRestorer.RecoveryRegisters.LastOrDefault( x => x.RegisterIdent.Equals("SENSUSRADIO_PowerLevel")); testCase.RegisterRestorer.RecoveryRegisters.Remove(removeProPar); testCase.RequiredEOLProgress.ParametrizationDoneChecked = EOLStatus.OK; } else { testCase.RequiredEOLProgress.ParametrizationDoneChecked = EOLStatus.OK; } testPp = new ExecFinalParametrization("UnitTestExecFinalParametrization"); testPp.RegisterRestorer = testCase.RegisterRestorer; CommonCallClassCheck(testPp, testCase, ref testResult); break; case "ExecStoreConfiguration": // Avoid store configuration if (testCase.RequiredEOLProgress.StoreConfigurationDoneChecked != EOLStatus.FAIL) { testCase.RequiredEOLProgress.StoreConfigurationDoneChecked = EOLStatus.OK; testPp = new ExecStoreConfiguration("UnitTestExecStoreConfiguration"); CommonCallClassCheck(testPp, testCase, ref testResult); } else { // Indication that the process wasn't executed testCase.RequiredEOLProgress.StoreConfigurationDoneChecked = EOLStatus.NULL; testResult.StatusReturn = StatusReturn.Failed; paramsCheckWillSucceed = false; } break; case "ExecRebootCordonel": // Avoid reboot if failing is required if (testCase.RequiredEOLProgress.RebootDoneChecked != EOLStatus.FAIL) { testCase.RequiredEOLProgress.RebootDoneChecked = EOLStatus.OK; testPp = new ExecRebootCordonel("UnitTestExecReboot"); CommonCallClassCheck(testPp, testCase, ref testResult); } else { // Indication that the process wasn't executed testCase.RequiredEOLProgress.RebootDoneChecked = EOLStatus.NULL; testResult.StatusReturn = StatusReturn.Failed; } if (string.IsNullOrEmpty(testResult.EOLProgress.ProductionStatusFwVersion)) testResult.EOLProgress.ProductionStatusFwVersion = testCase.Meter?.FwVersion; break; case "CheckFinalParametrization": // Modify one parameter to fail the compare if (testCase.RequiredEOLProgress.ParametrizationCompareChecked == EOLStatus.FAIL) { var registerName = "SYSTEM_UpgradePermissions"; var readValue = RegisterConverter.ByteArrayToValue( testCase.Meter?.ReadRegister(registerName)); readValue++; testCase.Meter?.WriteRegister(registerName, readValue); paramsCheckWillSucceed = false; } testCase.RequiredEOLProgress.ParametrizationCompareChecked = paramsCheckWillSucceed ? EOLStatus.OK : EOLStatus.FAIL; testPp = new CheckFinalParametrization("UnitTestCheckFinalParametrization"); CommonCallClassCheck(testPp, testCase, ref testResult); break; } } _testResults.Add(testResult); } } /// /// Common class access to take the individual production processes into account /// /// /// /// /// /// /// - Initial. /// private void CommonCallClassCheck(IProductionProcess testPp, TestRequirement testCase, ref TestResult testResult) { // Results filled by real function calls testPp.EolProgress = testResult.EOLProgress; // Assign objects filled with the values from the input table testPp.Meter = testCase.Meter; testPp.ProductionRequirements = testCase.ProductionRequirements; testPp.ProductionStatus = testCase.CordonelDeviceStatus; testPp.RegisterRestorer = testCase.RegisterRestorer; // The new Object?[]{null} is a placeholder for the out return if needed for the method type reflection var parameters = new Object?[] { null }; // Public methods do NOT return a variable if (!IsPrivateMethod) parameters = null; var statusReturn = StatusReturn.Unknown; // Call the function which should be tested switch (ObjectTypeName) { // Call the function to be tested case "EOLStatus": ClassAccess.CallMethode((TInstance)testPp, MethodToTest, parameters, IsPrivateMethod); break; case "StringReturn": ClassAccess.CallMethode((TInstance)testPp, MethodToTest, parameters, IsPrivateMethod); break; case "StatusReturn": statusReturn = ClassAccess.CallMethode((TInstance)testPp, MethodToTest, parameters, IsPrivateMethod); break; } // Status return failed will have the highest priority and will be kept for a sequence if once set // as testResult is a reference to the sequences test if (testResult.StatusReturn != StatusReturn.Failed) testResult.StatusReturn = statusReturn; if (parameters?[0] is String) testResult.FeedbackMsg += $"{parameters[0]} "; } /// /// Change status return enum to string /// /// /// /// /// - Initial. /// private static String GetStatusReturnNameFromStatus(StatusReturn statusReturn) { if (statusReturn == StatusReturn.Skipped) return "Skipped"; if (statusReturn == StatusReturn.Okay) return "Okay"; if (statusReturn == StatusReturn.Failed) return "Failed"; if (statusReturn == StatusReturn.Warning) return "Warning"; return "Unknown"; } /// /// Change naming string to status /// /// /// /// /// - Initial. /// private static StatusReturn GetStatusReturnFromName(String strStatusReturn) { switch (strStatusReturn) { case "Okay": return StatusReturn.Okay; case "Skipped": return StatusReturn.Skipped; case "Failed": return StatusReturn.Failed; case "Warning": return StatusReturn.Warning; default: return StatusReturn.Unknown; } } /// /// Change naming string to CSD Status /// /// /// /// /// - Initial. /// private static CSDStatus GetCsdStatusReturnFromName(String strCsdStatusReturn) { switch (strCsdStatusReturn) { case "Blocked": return CSDStatus.Blocked; case "New": return CSDStatus.New; case "Known": return CSDStatus.Known; case "Released": return CSDStatus.Released; default: return CSDStatus.Null; } } /// /// Change EOLStatus enum to string /// /// /// /// /// - Initial. /// private static String GetEOLStatusNameFromStatus(EOLStatus eolStatus) { if (eolStatus == EOLStatus.OK) return "Okay"; // NA stands for not skipped test by requirement or region if (eolStatus == EOLStatus.NA) return "Skipped"; if (eolStatus == EOLStatus.FAIL) return "Failed"; return "NOT EXECUTED"; } /// /// Change naming string to status /// /// /// /// /// - Initial. /// private static EOLStatus GetEOLStatusFromName(String strEolStatus) { switch (strEolStatus) { case "Okay": return EOLStatus.OK; case "Skipped": return EOLStatus.NA; case "Failed": return EOLStatus.FAIL; default: return EOLStatus.NULL; } } #endregion ----------------------------- Common --------------------------------------------------------------- #region -------------------------------- Parse test tables ---------------------------------------------------- /// /// Read and parse the test table to the test-requirement struct /// /// /// /// - Initial. /// /// /// - Login to build App list and registers. /// /// /// - New object with deep copy of presetTestCase using the Json serializer. /// /// /// - Moved meter login for new test requirement and register restorers modification to /// . /// /// /// - Init register restorers. /// /// /// - CSD parsing. /// private void ReadTestTable(Table table) { // Generate list of tests out of the "Given" table and create test-cases foreach (var row in table.Rows) { TestRequirement testCase; // Check if presets are required, if so copy all properties using the Json.Deserializer to create a // new object as deep copy!!! if (_presetTestCase.Meter != null) { if (string.IsNullOrEmpty(_presetTestCaseJson)) { _presetTestCaseJson = JsonConvert.SerializeObject(_presetTestCase); } // Create a new object with a deep copy of the _presetTestCase testCase = JsonConvert.DeserializeObject(_presetTestCaseJson); // Special meter properties which are not include in the deep copy ClassAccess.CopyAllProperties(testCase.Meter, _presetTestCase.Meter); testCase.RegisterRestorer = new RegisterRestorer(testCase.Meter); testCase.CalibCheckRegisterRestorer = new RegisterRestorer(testCase.Meter); // Enable register compare even if not physically read from meter testCase.RegisterRestorer.EnableCompareExternalSetRegisters(); testCase.CalibCheckRegisterRestorer.EnableCompareExternalSetRegisters(); // Login is needed for building the MeterAppListVersion and generating the accessible register list. // This register list will be used to check for valid ranges. testCase.Meter?.Login(); } // Preset is not required, a single testCase has to be created else { testCase = NewTestRequirement(); } // This is the multiple row test table defining individual test cases ReadTable(row, ref testCase); _testCases.Add(testCase); } } /// /// Read and parse the preset table to the test-requirement struct: /// - A preset table is limited to exclusively one row as this shall be used for every test-case, /// - There can be multiple preset tables, so the object has to remain alive. /// /// /// /// - Initial. /// /// /// - Login to build App list and registers. /// /// /// - Moved meter login to . /// private void ReadPresetTable(Table table) { if (_presetTestCase.Meter == null) { _presetTestCase = NewTestRequirement(); } // A preset table is limited to exclusively one row, those settings will be used for every test of the // test-cases! ReadTable(table.Rows[0], ref _presetTestCase); } /// /// Read and parse one table row /// /// /// /// /// - Initial. /// private void ReadTable(TableRow tableRow, ref TestRequirement parsedTableRow) { #region ----------------------------------- Parse Common Table Fields --------------------------------------- // This test number needs to be in the very first colum of the test table because this is the reference for // each individual test and will be used for every subsequent table-parsing. if (tableRow.TryGetValue("TestNo", out var strValue)) { var isNumber = int.TryParse(strValue, out var testNo); if (isNumber) parsedTableRow.TestNo = testNo; } // The comment will be used to describe the test. On failed test this can be used to identify the expected result! if (tableRow.TryGetValue("Comment", out var strComment)) { parsedTableRow.Comment = strComment; } // "Partial Compare" can be used to check if the StringResult contains the named string if true, else the string // has to be identical! if (tableRow.TryGetValue("Partial Compare", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) parsedTableRow.PartiallyCompareStringResult = true; } #endregion #region ----------------------------------- Parse EOL Settings -------------------------------------------- if (ParseInt(tableRow, "EOL Id", out strValue, out var intValue)) { if (intValue != null) parsedTableRow.RequiredEOLProgress.Id = (Int32)intValue; } var strHeader = "EOL StartDate"; if (tableRow.TryGetValue(strHeader, out strValue)) { if (strValue.Equals("UTC Now")) { parsedTableRow.RequiredEOLProgress.StartDate = DateTime.UtcNow; } } #endregion #region ----------------------------------- Parse Requirements --------------------------------------------- if (ParseInt(tableRow, "Standard Id", out strValue, out intValue)) { parsedTableRow.ProductionRequirements.StandardId = intValue; parsedTableRow.RequiredEOLProgress.StandardRequirementId = intValue; parsedTableRow.RequiredEOLProgress.RequirementRequestChecked = EOLStatus.OK; } if (ParseInt(tableRow, "Standard Version", out strValue, out intValue)) { parsedTableRow.ProductionRequirements.StandardVersion = intValue; parsedTableRow.RequiredEOLProgress.StandardRequirementVersion = intValue; } if (ParseInt(tableRow, "Special Id", out strValue, out intValue)) { parsedTableRow.ProductionRequirements.SpecialId = intValue; parsedTableRow.RequiredEOLProgress.SpecialRequirementId = intValue; } if (ParseInt(tableRow, "Special Version", out strValue, out intValue)) { parsedTableRow.ProductionRequirements.SpecialVersion = intValue; parsedTableRow.RequiredEOLProgress.SpecialRequirementVersion = intValue; } if (tableRow.TryGetValue("Standard IsActive", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) parsedTableRow.ProductionRequirements.IsStandardActive = true; } if (tableRow.TryGetValue("Standard IsApproved", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) parsedTableRow.ProductionRequirements.IsStandardApproved = true; } if (tableRow.TryGetValue("Special IsActive", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) parsedTableRow.ProductionRequirements.IsSpecialActive = true; } if (tableRow.TryGetValue("Special IsApproved", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) parsedTableRow.ProductionRequirements.IsSpecialApproved = true; } #endregion #region ----------------------------------- Parse Production Order ----------------------------------------- if (ParseInt(tableRow, "Cordonel ProdOrderNo", out strValue, out intValue)) { if (parsedTableRow.Meter != null && intValue != null) parsedTableRow.Meter.OrderNumber = (Int32)intValue; } if (ParseInt(tableRow, "Requirement ProdOrderNo", out strValue, out intValue)) { if (intValue != null) parsedTableRow.ProductionRequirements.ProductionOrderNr = (Int32)intValue; parsedTableRow.RequiredEOLProgress.RequirementOrderChecked = EOLStatus.OK; } #endregion #region ----------------------------------- Parse CSD ----------------------------------------------------- if (ParseInt(tableRow, "CSD ID", out strValue, out intValue)) { if (intValue != null) parsedTableRow.ProductionRequirements.CSD.IdentId = (Int32)intValue; } if (ParseString(tableRow, "CSD Name", out strValue)) { parsedTableRow.ProductionRequirements.CSD.Name = strValue; } if (ParseString(tableRow, "CSD Code", out strValue)) { parsedTableRow.ProductionRequirements.CSD.Code = strValue; } if (ParseString(tableRow, "CSD Status", out strValue)) { parsedTableRow.ProductionRequirements.CSD.Status = GetCsdStatusReturnFromName(strValue); } #endregion #region ----------------------------------- Parse PcbId --------------------------------------------------- if (ParseInt(tableRow, "Cordonel PcbId", out strValue, out intValue)) { if (parsedTableRow.Meter != null) parsedTableRow.Meter.PcbId = strValue; } if (ParseInt(tableRow, "Requirement PcbId", out strValue, out intValue)) { parsedTableRow.ProductionRequirements.PcbId = intValue; parsedTableRow.RequiredEOLProgress.RequirementPcbIdChecked = EOLStatus.OK; } if (ParseInt(tableRow, "EOL PcbId", out strValue, out intValue)) { if (intValue != null) parsedTableRow.RequiredEOLProgress.PcbId = (Int32)intValue; } #endregion #region ----------------------------------- Parse LUT CRC --------------------------------------------------- if (ParseString(tableRow, "Cordonel LUT CRC", out strValue)) { if (parsedTableRow.Meter != null) parsedTableRow.Meter.LutCrc = strValue; } if (ParseString(tableRow, "Requirement LUT CRC", out strValue)) { parsedTableRow.ProductionRequirements.LutCrc = strValue; parsedTableRow.RequiredEOLProgress.ProductionStatusLutCrc = strValue; } #endregion #region ----------------------------------- Parse Region --------------------------------------------------- if (ParseString(tableRow, "Cordonel Region", out strValue)) { if (parsedTableRow.Meter != null) parsedTableRow.Meter.Region = strValue; } if (ParseString(tableRow, "Requirement Region", out strValue)) { parsedTableRow.ProductionRequirements.Region = strValue; parsedTableRow.RequiredEOLProgress.RequirementRegionChecked = EOLStatus.OK; } #endregion #region ----------------------------------- Parse Meter Size ---------------------------------------------- if (ParseString(tableRow, "Cordonel Meter Size", out strValue)) { //if (parsedTableRow.Meter != null) parsedTableRow.Meter.MeterSize = strValue; if (parsedTableRow.Meter != null) { parsedTableRow.Meter.EnablePropertyChangeAbility(true); parsedTableRow.Meter.SetMeterSize(strValue); } } if (ParseString(tableRow, "Requirement Meter Size", out strValue)) { parsedTableRow.ProductionRequirements.MeterSize = strValue; parsedTableRow.RequiredEOLProgress.ProductionStatusMeterSize = strValue; } #endregion #region ----------------------------------- Parse FW and Apps --------------------------------------------- // Temporary reminders until all app depending on modifications in one table entry are set Byte? modifyAppId = null; var eraseApp = false; UInt32? appVersion = null; UInt16? appCrc = null; if (ParseInt(tableRow, "Modify AppId", out strValue, out intValue)) { modifyAppId = (Byte?)intValue; } if (tableRow.TryGetValue("Erase App", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) eraseApp = true; } if (tableRow.TryGetValue("Change App Version", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) appVersion = 0; } if (tableRow.TryGetValue("Change App CRC", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) appCrc = 0x0000; } if (ParseString(tableRow, "Cordonel FW Version", out strValue)) { // Login needed to install the FW from hard-coded package description file. This is essential as only // then one App could be erased or modified parsedTableRow.Meter?.Login(); parsedTableRow.RequiredEOLProgress.ProductionStatusFwVersion = strValue; var isInstalled = parsedTableRow.Meter != null && parsedTableRow.Meter.InstallNewMockFw(fwName: strValue); var errorMsg = $"FW \"{strValue}\" could not be found in \"MockFwExamples\", parsing or " + "installation failed!"; Assert.That(isInstalled, errorMsg); } if (ParseFloat(tableRow, "Cordonel Core Revision", out strValue, out var floatValue)) { // Initially needed to install MockFw and set up the initial core revision. If the login will be // delayed then the "Core Revision" is going to be overwritten from the content of the firmware // package description file. if (parsedTableRow.Meter != null) { parsedTableRow.Meter.Login(); if (floatValue != null) parsedTableRow.Meter.CoreRevision = (Int32)(floatValue * 100); parsedTableRow.Meter.StrCoreRevision = strValue; } } if (ParseString(tableRow, "Requirement FW Version", out strValue)) { parsedTableRow.ProductionRequirements.FwVersion = strValue; var isInMockFwExamples = MeterFwUpdate.LoadFwUpdatePackage(_mockFwPackageRootPath, strValue, parsedTableRow.FwUpdatePackage); var errorMsg = $"FW \"{strValue}\" could not be found in \"MockFwExamples\" or parsing failed!"; Assert.That(isInMockFwExamples, errorMsg); } if (modifyAppId != null) { parsedTableRow.Meter?.ModifyMockApp((Byte)modifyAppId, appVersion, appCrc, eraseApp); } #endregion #region ----------------------------------- Parse Storage Months ------------------------------------------ if (ParseFloat(tableRow, "Assembly Months before Now", out strValue, out floatValue)) { if (floatValue < 0.0) { // The seconds will be used to calculate the assembly time // (seconds * minutes * hours * DaysPerMonth * -1) the minus one creates a positive value as the // requirement for the "Assembly Months before Now" a negative value is essential. parsedTableRow.CordonelDeviceStatus.ExceededLifeTime_s = (UInt64)((Double)floatValue * GenesisStatusHandler.SECONDS_PER_MONTH * -1); // Assume 1 % usage to have a number. InitialLoad in mAh * Quantity converted to uAs divided by 100 % parsedTableRow.CordonelDeviceStatus.DrainedBatteryLoad_uAs = parsedTableRow.CordonelDeviceStatus.InitialBatteryLoad_mAh * (UInt64)parsedTableRow.CordonelDeviceStatus.BatteryQuantity * 1000 * 3600 / 100; // corresponds to 1% of usage } } if (ParseFloat(tableRow, "Max Storage Months", out strValue, out floatValue)) { if (floatValue != null) { parsedTableRow.ProductionRequirements.MaxStorageMonths = floatValue; parsedTableRow.RequiredEOLProgress.MaxStorageMonths = floatValue; parsedTableRow.RequiredEOLProgress.MaxStorageMonthsChecked = EOLStatus.OK; } } #endregion #region ----------------------------------- Parse Power Consumption --------------------------------------- if (ParseFloat(tableRow, "Normal Current [uA]", out strValue, out floatValue)) { // If null, then this will be ignored parsedTableRow.ProductionRequirements.QuiescentCurrent_uA = floatValue; } if (ParseFloat(tableRow, "Production Supplement [%]", out strValue, out floatValue)) { // If null, then this will be ignored parsedTableRow.ProductionRequirements.EstimatedProductionBatteryLoadPercent = floatValue; } if (ParseFloat(tableRow, "Actual Lifetime [days]", out strValue, out floatValue)) { if (floatValue != null) parsedTableRow.CordonelDeviceStatus.ExceededLifeTime_s = (UInt64)(floatValue * 24 * 3600); parsedTableRow.RequiredEOLProgress.ProductionStatusBatteryLoadChecked = EOLStatus.OK; parsedTableRow.RequiredEOLProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.OK; } if (ParseFloat(tableRow, "Actual Drained [%]", out strValue, out floatValue)) { if (floatValue != null) { parsedTableRow.CordonelDeviceStatus.DrainedBatteryLoad_uAs = (UInt64)(floatValue * parsedTableRow.CordonelDeviceStatus.InitialBatteryLoad_mAh * parsedTableRow.CordonelDeviceStatus.BatteryQuantity * 1000 * 3600 / 100); parsedTableRow.RequiredEOLProgress.ProductionStatusDrainedBatteryLoad = floatValue; } //GenesisStatusHandler.CalculateLifeTime(parsedTableRow.CordonelDeviceStatus); //parsedTableRow.RequiredEOLProgress.ProductionStatusRemainingLifeTime = // parsedTableRow.CordonelDeviceStatus.RemainingLifeTimeYears; } if (ParseFloat(tableRow, "Max Drained [%]", out strValue, out floatValue)) { parsedTableRow.ProductionRequirements.MaxDrainedBatteryLoadPercentShipping = floatValue; } if (ParseFloat(tableRow, "Required Lifetime [years]", out strValue, out floatValue)) { parsedTableRow.ProductionRequirements.RequiredLifeTimeYearsShipping = floatValue; } #endregion #region ----------------------------------- Parse Calibration --------------------------------------------- if (ParseString(tableRow, "Skip Calib", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.ProductionRequirements.SkipCalibration = true; parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.NA; parsedTableRow.RequiredEOLProgress.ProductionStatusZeroFlowChecked = EOLStatus.NA; } else { parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.OK; parsedTableRow.RequiredEOLProgress.ProductionStatusZeroFlowChecked = EOLStatus.OK; } } if (ParseInt(tableRow, "Test Bench Fields", out strValue, out intValue)) { parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.OK; parsedTableRow.RequiredEOLProgress.ProductionStatusZeroFlowChecked = EOLStatus.OK; if (intValue != null) { for (var ctr = 0; ctr < intValue; ctr++) parsedTableRow.TestBenchState.Add(""); } } if (ParseInt(tableRow, "Test Bench State[4]", out strValue, out intValue)) { if (parsedTableRow.TestBenchState.Count >= 5) if (strValue != null) parsedTableRow.TestBenchState[4] = strValue; } if (ParseInt(tableRow, "Test Bench State[5]", out strValue, out intValue)) { if (parsedTableRow.TestBenchState.Count >= 6) if (strValue != null) parsedTableRow.TestBenchState[5] = strValue; } if (ParseInt(tableRow, "Meter Calib Fact 1", out strValue, out intValue)) { //// Login is needed for building the MeterAppListVersion and generating the accessible register list. //// This register list will be used to check for valid ranges. //parsedTableRow.Meter.Login(); parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.OK; parsedTableRow.RequiredEOLProgress.ProductionStatusZeroFlowChecked = EOLStatus.OK; if (intValue != null) { PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor1", readBackValue: RegisterConverter.ValueToByteArray((UInt16)intValue)); } } if (ParseInt(tableRow, "Meter Calib Fact 2", out strValue, out intValue)) { if (intValue != null) { PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor2", readBackValue: RegisterConverter.ValueToByteArray((UInt16)intValue)); } } if (ParseInt(tableRow, "Meter Calib Fact 3", out strValue, out intValue)) { if (intValue != null) { PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor3", readBackValue: RegisterConverter.ValueToByteArray((UInt16)intValue)); } } if (ParseInt(tableRow, "DB Calib Fact 1", out strValue, out intValue)) { if (intValue != null) PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor1", RegisterConverter.ValueToByteArray((UInt16)intValue)); } if (ParseInt(tableRow, "DB Calib Fact 2", out strValue, out intValue)) { if (intValue != null) PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor2", RegisterConverter.ValueToByteArray((UInt16)intValue)); } if (ParseInt(tableRow, "DB Calib Fact 3", out strValue, out intValue)) { if (intValue != null) PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor3", RegisterConverter.ValueToByteArray((UInt16)intValue)); } #endregion #region ----------------------------------- Parse Kill settings ------------------------------------------- // Use the single selection lock to leave all following settings on default, as the method may exit here // on the first occurrence and all assumptions for further checks must be skipped. var singleSelectionLock = false; if (ParseString(tableRow, "Kill Registers", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.Meter?.KillConfigRegisters(); parsedTableRow.RequiredEOLProgress.ParametrizationCompareChecked = EOLStatus.FAIL; parsedTableRow.RequiredEOLProgress.ParametrizationDoneChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill WriteParams", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.RequiredEOLProgress.ParametrizationCompareChecked = EOLStatus.FAIL; parsedTableRow.RequiredEOLProgress.ParametrizationDoneChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill StoreParams", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { // Force to skip store calibration parsedTableRow.RequiredEOLProgress.StoreConfigurationDoneChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill Reboot", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { // force to skip reboot parsedTableRow.RequiredEOLProgress.RebootDoneChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill VerifyParams", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.RequiredEOLProgress.ParametrizationCompareChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill EOL", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; parsedTableRow.RequiredEOLProgress.Id = 0; } } if (ParseString(tableRow, "Kill Requ", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.StandardRequirementId = null; parsedTableRow.RequiredEOLProgress.StandardRequirementVersion = null; parsedTableRow.RequiredEOLProgress.SpecialRequirementId = null; parsedTableRow.RequiredEOLProgress.SpecialRequirementVersion = null; parsedTableRow.RequiredEOLProgress.RequirementRequestChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; parsedTableRow.ProductionRequirements.IsStandardActive = false; parsedTableRow.RequiredEOLProgress.StandardRequirementId = null; parsedTableRow.RequiredEOLProgress.StandardRequirementVersion = null; parsedTableRow.RequiredEOLProgress.SpecialRequirementId = null; parsedTableRow.RequiredEOLProgress.SpecialRequirementVersion = null; parsedTableRow.RequiredEOLProgress.RequirementRequestChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill Order", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.RequirementOrderChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X") && !singleSelectionLock) { singleSelectionLock = true; if (parsedTableRow.Meter != null) parsedTableRow.Meter.OrderNumber = 0; parsedTableRow.RequiredEOLProgress.RequirementOrderChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill PcbId", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.RequirementPcbIdChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; parsedTableRow.ProductionRequirements.PcbId = 0; parsedTableRow.RequiredEOLProgress.RequirementPcbIdChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill Region", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.RequirementRegionChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; if (parsedTableRow.Meter != null) parsedTableRow.Meter.Region = "?"; parsedTableRow.RequiredEOLProgress.RequirementRegionChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill LUT", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusLutCrc = ""; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; if (parsedTableRow.Meter != null) parsedTableRow.Meter.LutCrc = "?"; parsedTableRow.RequiredEOLProgress.ProductionStatusLutCrc = ""; } } if (ParseString(tableRow, "Kill Size", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusMeterSize = ""; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; if (parsedTableRow.Meter != null) { parsedTableRow.Meter.EnablePropertyChangeAbility(true); parsedTableRow.Meter.SetMeterSize("?"); } parsedTableRow.RequiredEOLProgress.ProductionStatusMeterSize = ""; } } if (ParseString(tableRow, "Kill FW", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusFwVersion = ""; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { singleSelectionLock = true; parsedTableRow.ProductionRequirements.FwVersion = "?"; parsedTableRow.RequiredEOLProgress.ProductionStatusFwVersion = ""; } } if (ParseString(tableRow, "Kill Load", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusBatteryLoadChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.ProductionRequirements.MaxDrainedBatteryLoadPercentShipping = 0; parsedTableRow.RequiredEOLProgress.ProductionStatusBatteryLoadChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill LifeTime", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.ProductionRequirements.RequiredLifeTimeYearsShipping = 1000; parsedTableRow.RequiredEOLProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill StoreTime", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.MaxStorageMonths = null; parsedTableRow.RequiredEOLProgress.MaxStorageMonthsChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.ProductionRequirements.MaxStorageMonths = 0; parsedTableRow.RequiredEOLProgress.MaxStorageMonths = 0; parsedTableRow.RequiredEOLProgress.MaxStorageMonthsChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill PreAdj", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusZeroFlowChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.TestBenchState[4] = ""; parsedTableRow.RequiredEOLProgress.ProductionStatusZeroFlowChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill FlowTest", out strValue)) { if (singleSelectionLock) { parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.NULL; } else if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X")) { parsedTableRow.TestBenchState[5] = ""; parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill Calib", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X") && !singleSelectionLock) { if (parsedTableRow.Meter != null) parsedTableRow.Meter.ReLogin(); PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor1", RegisterConverter.ValueToByteArray((UInt16)0), RegisterConverter.ValueToByteArray((UInt16)1)); parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.FAIL; } } if (ParseString(tableRow, "Kill RadioParam", out strValue)) { if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X") && !singleSelectionLock) { parsedTableRow.KillRadioParam = true; } } #endregion #region ----------------------------------- Parse Stati --------------------------------------------------- strHeader = "EOL Status"; if (ParseString(tableRow, "EOL Status", out strValue)) { if (ObjectTypeName.Equals(strHeader.Replace(" ", ""))) { ClassAccess.SetPropertyValue(parsedTableRow.RequiredEOLProgress, PropertyToCheck, GetEOLStatusFromName(strValue)); } } strHeader = "StringReturn"; if (ParseString(tableRow, strHeader, out strValue)) { if (ObjectTypeName.Equals(strHeader)) { ClassAccess.SetPropertyValue(parsedTableRow.RequiredEOLProgress, PropertyToCheck, strValue); } } strHeader = "StatusReturn"; if (ParseString(tableRow, strHeader, out strValue)) { parsedTableRow.RequiredStatusReturn = GetStatusReturnFromName(strValue); } #endregion } /// /// Preset the register values /// /// /// /// /// /// /// /// - Initial. /// private static void PresetRegisterRestorer(RegisterRestorer regRestorer, String regName, Byte[]? writeValue = null, Byte[]? readBackValue = null) { try { // Check if register exists and overwrite content foreach (var reg in regRestorer.RecoveryRegisters.Where(reg => reg.RegisterIdent == regName)) { if (writeValue != null) reg.WriteValue = writeValue; if (readBackValue != null) reg.ReadBackValue = readBackValue; return; } regRestorer.RecoveryRegisters.Add(new RecoveryRegisterItem(regName, writeValue, readBackValue)); } catch (Exception e) { Console.WriteLine(e.Message); } } /// /// Assign new test requirement filled with all properties /// /// /// /// - Initial. /// /// /// - Init meter and enable change of values for register restorers. /// /// /// - CSD. /// private static TestRequirement NewTestRequirement() { var testCase = new TestRequirement { Meter = new MockGenesis(), ProductionRequirements = new CordonelRequirements(), RequiredEOLProgress = new EOLProgressModel(), FwUpdatePackage = new List(), TestBenchState = new List(), CordonelDeviceStatus = new GenesisStatus { InitialBatteryLoad_mAh = 19000, BatteryQuantity = 2 } }; testCase.ProductionRequirements.CSD = new CordonelCSD(); testCase.RegisterRestorer = new RegisterRestorer(testCase.Meter); testCase.CalibCheckRegisterRestorer = new RegisterRestorer(testCase.Meter); // Enable register compare even if not physically read from meter testCase.RegisterRestorer.EnableCompareExternalSetRegisters(); testCase.CalibCheckRegisterRestorer.EnableCompareExternalSetRegisters(); // Login is needed for building the MeterAppListVersion and generating the accessible register list. // This register list will be used to check for valid ranges. testCase.Meter?.Login(); return testCase; } /// /// Read and parse a floating point number from the table row /// /// /// /// output read string from table-row /// output Double? value converted from table-row string /// okay if conversion is an Int32 or null /// /// - Initial. /// private static Boolean ParseFloat(TableRow tableRow, String strHeader, out String? strValue, out Double? value) { value = null; var isNumber = false; if (tableRow.TryGetValue(strHeader, out strValue)) { if (strValue.Equals("null") || strValue.Equals("\"\"")) { strValue = null; return true; } isNumber = double.TryParse(strValue, out var parsedValue); if (isNumber) { value = parsedValue; } } return isNumber; } /// /// Read and parse an integer from the table row /// /// /// /// output read string from table-row /// output Int32? value converted from table-row string /// okay if conversion is an Int32 or null /// /// - Initial. /// /// /// - Accept "null" as input. /// private static Boolean ParseInt(TableRow tableRow, String strHeader, out String? strValue, out Int32? value) { value = null; var isNumber = false; if (tableRow.TryGetValue(strHeader, out strValue)) { if (strValue.Equals("null") || strValue.Equals("\"\"")) { strValue = null; return true; } isNumber = int.TryParse(strValue, out var parsedValue); if (isNumber) { value = parsedValue; } } return isNumber; } /// /// Read and parse a string from the table row /// - If the string is "null" or "\"\"" it will be set to empty string. /// /// /// /// output read string from table-row /// okay if conversion is a String or null /// /// - Initial. /// /// /// - Returns empty string "" instead of null. /// private static Boolean ParseString(TableRow tableRow, String strHeader, out String strValue) { if (tableRow.TryGetValue(strHeader, out strValue)) { if (strValue.Equals("null") || strValue.Equals("\"\"") || strValue.Equals("")) { strValue = ""; } return true; } return false; } #endregion --------------------------------- Parse test tables ------------------------------------------------ #region ------------------------------------ Tools ------------------------------------------------------------ /// /// Copy entire directory with all elements from source to destination directory and overwrite files on command /// /// /// /// overwrite file if exist /// /// /// - Initial. /// private void CopyEntireDirectory(String sourcePath, String destinationPath, Boolean overwrite) { if (string.IsNullOrEmpty(sourcePath) || string.IsNullOrEmpty(destinationPath) || !Directory.Exists(sourcePath)) return; try { // First build the subdirectories in the destination folder foreach (var path in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) { Directory.CreateDirectory(path.Replace(sourcePath, destinationPath)); } // Get all files including the absolute path var sourceFilePathNames = Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories); // Build destination foreach (var sourceFilePathName in sourceFilePathNames) { var destinationFilePathName = sourceFilePathName.Replace(sourcePath, destinationPath); // Skip if file already exists and should not be overwritten if (!overwrite && !File.Exists(destinationFilePathName)) File.Copy(sourceFilePathName, destinationFilePathName, overwrite); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } #endregion --------------------------------- Tools ------------------------------------------------------------ } }