1991 lines
85 KiB
C#
1991 lines
85 KiB
C#
|
|
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 ------------------------------------------
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
private String MethodToTest
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Marker for a private method.
|
|||
|
|
/// </summary>
|
|||
|
|
private Boolean IsPrivateMethod
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Name of the property to be checked:
|
|||
|
|
/// - This will be used to assign dynamic a property to be checked.
|
|||
|
|
/// </summary>
|
|||
|
|
private String PropertyToCheck
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
private String ObjectTypeName
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
private Boolean TestEolProgress
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// The name of the production process to test. This is needed to call the correct type in the
|
|||
|
|
/// <see cref="ExecTests"></see>
|
|||
|
|
/// </summary>
|
|||
|
|
private String TestName
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// The exec path of the assembly for debug in environment.
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly String _assemblyExecRootPath =
|
|||
|
|
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "";
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Fw package path relative to solution main folder.
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly String _mockFwPackageRootPath;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Raw initialization parameters (unsorted and unfiltered for Order, VAKO and CSD priority) path relative to
|
|||
|
|
/// solution main folder.
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly String _mockRawParamsRootPath;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Test result container
|
|||
|
|
/// </summary>
|
|||
|
|
private struct TestResult
|
|||
|
|
{
|
|||
|
|
public Int32 TestNo
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public EOLProgressModel EOLProgress
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Used if property cannot be checked by an EOL progress.
|
|||
|
|
/// </summary>
|
|||
|
|
public StatusReturn StatusReturn
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Feedback message from tested routines
|
|||
|
|
/// </summary>
|
|||
|
|
public String FeedbackMsg
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Test definition container for test-table entries
|
|||
|
|
/// </summary>
|
|||
|
|
private struct TestRequirement
|
|||
|
|
{
|
|||
|
|
public Int32 TestNo
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public String Comment
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public MockGenesis? Meter
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Production requirements for check of production status and special requirements
|
|||
|
|
/// </summary>
|
|||
|
|
public CordonelRequirements ProductionRequirements
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// EOL Progress as input where every single EOL property will be used to set the expected feedback
|
|||
|
|
/// to fulfill the single test.
|
|||
|
|
/// </summary>
|
|||
|
|
public EOLProgressModel RequiredEOLProgress
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Status of the Cordonel
|
|||
|
|
/// </summary>
|
|||
|
|
public GenesisStatus CordonelDeviceStatus
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Calibration register restorer of Cordonel
|
|||
|
|
/// </summary>
|
|||
|
|
public RegisterRestorer CalibCheckRegisterRestorer
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Register restorer of Cordonel for parametrization
|
|||
|
|
/// </summary>
|
|||
|
|
public RegisterRestorer RegisterRestorer
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Production status of the Cordonel
|
|||
|
|
/// </summary>
|
|||
|
|
public List<String> TestBenchState
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Used for partially or full compare on string result return
|
|||
|
|
/// </summary>
|
|||
|
|
public Boolean PartiallyCompareStringResult
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Loaded FW package from local disk
|
|||
|
|
/// </summary>
|
|||
|
|
public List<FilePart> FwUpdatePackage
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Used if property cannot be checked by an EOL progress.
|
|||
|
|
/// </summary>
|
|||
|
|
public StatusReturn RequiredStatusReturn
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Control to fail the Kitron radio param test
|
|||
|
|
/// </summary>
|
|||
|
|
public Boolean KillRadioParam
|
|||
|
|
{
|
|||
|
|
set; get;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// List of names needed to be executed in advance to the real test.
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly List<String> _ppNamesExecSequence = new List<String>();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Test cases are the individual tests
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly List<TestRequirement> _testCases = new List<TestRequirement>();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Test results after execution
|
|||
|
|
/// </summary>
|
|||
|
|
private readonly List<TestResult> _testResults = new List<TestResult>();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Presets needed to pass preceding tests or initialize some properties to a defined state
|
|||
|
|
/// </summary>
|
|||
|
|
private String _presetTestCaseJson = "";
|
|||
|
|
/// <summary>
|
|||
|
|
/// Presets needed to pass preceding tests or initialize some properties to a defined state
|
|||
|
|
/// </summary>
|
|||
|
|
private TestRequirement _presetTestCase;
|
|||
|
|
|
|||
|
|
#endregion ----------------------------- Production State Properties ------------------------------------------
|
|||
|
|
|
|||
|
|
#region -------------------------------- Ctor -----------------------------------------------------------------
|
|||
|
|
/// <summary>
|
|||
|
|
/// Ctor to crete directories depending on test runner.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <remarks date="2024-Dec-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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 ---------------------------------------------------------------
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Verify all test cases and compare required-result with result coming from the method under test.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <remarks date="2024-Nov-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-09" author="Thomas Wiedebusch">
|
|||
|
|
/// - Check entire EOL progress on request as this defines which tests have been executed or skipped.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-10" author="Thomas Wiedebusch">
|
|||
|
|
/// - Avoid string being null, instead set it to empty string "".
|
|||
|
|
/// </remarks>
|
|||
|
|
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<EOLStatus, EOLProgressModel>(testCase.RequiredEOLProgress,
|
|||
|
|
PropertyToCheck,
|
|||
|
|
out var eolStatus);
|
|||
|
|
status.Should().BeTrue(strErrorMsgForGetPropertyCall);
|
|||
|
|
strTestCaseExpectedStatus = GetEOLStatusNameFromStatus(eolStatus);
|
|||
|
|
break;
|
|||
|
|
case "StringReturn":
|
|||
|
|
status = ClassAccess.GetPropertyValue<String, EOLProgressModel>(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<EOLStatus, EOLProgressModel>(testResult.EOLProgress,
|
|||
|
|
PropertyToCheck, out var eolStatus);
|
|||
|
|
status.Should().BeTrue(strErrorMsgForGetPropertyCall);
|
|||
|
|
strTestResultStatus = GetEOLStatusNameFromStatus(eolStatus);
|
|||
|
|
break;
|
|||
|
|
case "StringReturn":
|
|||
|
|
status = ClassAccess.GetPropertyValue<String, EOLProgressModel>(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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Tests execution for all defined test in test data table
|
|||
|
|
/// </summary>
|
|||
|
|
/// <remarks date="2024-Nov-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-06" author="Thomas Wiedebusch">
|
|||
|
|
/// - Branch to production process type, fill with special objects and use CommonCallClassCheck.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-12" author="Thomas Wiedebusch">
|
|||
|
|
/// - Init the test result depending on test case and production process.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Feb-17" author="Thomas Wiedebusch">
|
|||
|
|
/// - Improved parameter load.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Mar-11" author="Thomas Wiedebusch">
|
|||
|
|
/// - Test the missing parameter-file and the missing CSD in registerRestorer,
|
|||
|
|
/// - Improved load of unmodified parameters.
|
|||
|
|
/// </remarks>
|
|||
|
|
private void ExecTests()
|
|||
|
|
{
|
|||
|
|
var regionForParams = "?";
|
|||
|
|
var meterSizeForParams = "?";
|
|||
|
|
var unmodifiedParamsUsableAsReferenceForNextTest = false;
|
|||
|
|
var intermediateRecoveryRegisters = new List<RecoveryRegisterItem>();
|
|||
|
|
|
|||
|
|
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<CheckProductionState>(testPp, testCase,
|
|||
|
|
ref testResult);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case "ExecFinalParametrization":
|
|||
|
|
// Load parameters
|
|||
|
|
if (meterSizeForParams != testCase.Meter?.MeterSize || regionForParams != testCase.Meter?.Region ||
|
|||
|
|
!unmodifiedParamsUsableAsReferenceForNextTest)
|
|||
|
|
{
|
|||
|
|
var rawProgParams = new List<ProgrammingParameters>();
|
|||
|
|
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<ExecFinalParametrization>(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<ExecStoreConfiguration>(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<ExecRebootCordonel>(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<UInt32>(
|
|||
|
|
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<CheckFinalParametrization>(testPp, testCase, ref testResult);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_testResults.Add(testResult);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Common class access to take the individual production processes into account
|
|||
|
|
/// </summary>
|
|||
|
|
/// <typeparam name="TInstance"></typeparam>
|
|||
|
|
/// <param name="testPp"></param>
|
|||
|
|
/// <param name="testCase"></param>
|
|||
|
|
/// <param name="testResult"></param>
|
|||
|
|
/// <remarks date="2024-Dec-06" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
private void CommonCallClassCheck<TInstance>(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, EOLStatus>((TInstance)testPp, MethodToTest, parameters,
|
|||
|
|
IsPrivateMethod);
|
|||
|
|
break;
|
|||
|
|
case "StringReturn":
|
|||
|
|
ClassAccess.CallMethode<TInstance, String>((TInstance)testPp, MethodToTest, parameters,
|
|||
|
|
IsPrivateMethod);
|
|||
|
|
break;
|
|||
|
|
case "StatusReturn":
|
|||
|
|
statusReturn = ClassAccess.CallMethode<TInstance, StatusReturn>((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]} ";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Change status return enum to string
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="statusReturn"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Nov-14" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Change naming string to status
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="strStatusReturn"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Nov-14" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Change naming string to CSD Status
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="strCsdStatusReturn"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2025-Nov-25" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Change EOLStatus enum to string
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="eolStatus"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Nov-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Change naming string to status
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="strEolStatus"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Nov-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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 ----------------------------------------------------
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Read and parse the test table to the test-requirement struct
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="table"></param>
|
|||
|
|
/// <remarks date="2024-Nov-14" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-10" author="Thomas Wiedebusch">
|
|||
|
|
/// - Login to build App list and registers.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-21" author="Thomas Wiedebusch">
|
|||
|
|
/// - New object with deep copy of presetTestCase using the Json serializer.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-22" author="Thomas Wiedebusch">
|
|||
|
|
/// - Moved meter login for new test requirement and register restorers modification to
|
|||
|
|
/// <see cref="NewTestRequirement"/>.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-25" author="Thomas Wiedebusch">
|
|||
|
|
/// - Init register restorers.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
|
|||
|
|
/// - CSD parsing.
|
|||
|
|
/// </remarks>
|
|||
|
|
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<TestRequirement>(_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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="table"></param>
|
|||
|
|
/// <remarks date="2024-Nov-14" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-10" author="Thomas Wiedebusch">
|
|||
|
|
/// - Login to build App list and registers.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-22" author="Thomas Wiedebusch">
|
|||
|
|
/// - Moved meter login to <see cref="NewTestRequirement"/>.
|
|||
|
|
/// </remarks>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Read and parse one table row
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="tableRow"></param>
|
|||
|
|
/// <param name="parsedTableRow"></param>
|
|||
|
|
/// <remarks date="2024-Nov-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Preset the register values
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="regRestorer"></param>
|
|||
|
|
/// <param name="regName"></param>
|
|||
|
|
/// <param name="writeValue"></param>
|
|||
|
|
/// <param name="readBackValue"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Nov-29" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Assign new test requirement filled with all properties
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Nov-19" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-22" author="Thomas Wiedebusch">
|
|||
|
|
/// - Init meter and enable change of values for register restorers.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
|
|||
|
|
/// - CSD.
|
|||
|
|
/// </remarks>
|
|||
|
|
private static TestRequirement NewTestRequirement()
|
|||
|
|
{
|
|||
|
|
var testCase = new TestRequirement
|
|||
|
|
{
|
|||
|
|
Meter = new MockGenesis(),
|
|||
|
|
ProductionRequirements = new CordonelRequirements(),
|
|||
|
|
RequiredEOLProgress = new EOLProgressModel(),
|
|||
|
|
FwUpdatePackage = new List<FilePart>(),
|
|||
|
|
TestBenchState = new List<String>(),
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Read and parse a floating point number from the table row
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="tableRow"></param>
|
|||
|
|
/// <param name="strHeader"></param>
|
|||
|
|
/// <param name="strValue">output read string from table-row</param>
|
|||
|
|
/// <param name="value">output Double? value converted from table-row string</param>
|
|||
|
|
/// <returns>okay if conversion is an Int32 or null</returns>
|
|||
|
|
/// <remarks date="2024-Nov-19" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Read and parse an integer from the table row
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="tableRow"></param>
|
|||
|
|
/// <param name="strHeader"></param>
|
|||
|
|
/// <param name="strValue">output read string from table-row</param>
|
|||
|
|
/// <param name="value">output Int32? value converted from table-row string</param>
|
|||
|
|
/// <returns>okay if conversion is an Int32 or null</returns>
|
|||
|
|
/// <remarks date="2024-Nov-07" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Nov-18" author="Thomas Wiedebusch">
|
|||
|
|
/// - Accept "null" as input.
|
|||
|
|
/// </remarks>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Read and parse a string from the table row
|
|||
|
|
/// - If the string is "null" or "\"\"" it will be set to empty string.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="tableRow"></param>
|
|||
|
|
/// <param name="strHeader"></param>
|
|||
|
|
/// <param name="strValue">output read string from table-row</param>
|
|||
|
|
/// <returns>okay if conversion is a String or null</returns>
|
|||
|
|
/// <remarks date="2024-Nov-18" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
/// <remarks date="2024-Dec-10" author="Thomas Wiedebusch">
|
|||
|
|
/// - Returns empty string "" instead of null.
|
|||
|
|
/// </remarks>
|
|||
|
|
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 ------------------------------------------------------------
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Copy entire directory with all elements from source to destination directory and overwrite files on command
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="sourcePath"></param>
|
|||
|
|
/// <param name="destinationPath"></param>
|
|||
|
|
/// <param name="overwrite">overwrite file if exist</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <remarks date="2024-Dec-13" author="Thomas Wiedebusch">
|
|||
|
|
/// - Initial.
|
|||
|
|
/// </remarks>
|
|||
|
|
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 ------------------------------------------------------------
|
|||
|
|
}
|
|||
|
|
}
|