diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 54ec401..7918262 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,51 +28,43 @@ jobs:
retention-days: 7
path: "ResultsAndTiming/*.chm"
Build:
- runs-on:
- group: OpenTAP-SpokeVPC
- labels: [Linux, X64]
- container: ghcr.io/opentap/oci-images/build-dotnet:latest
+ runs-on: windows-latest
env:
KS8500_USER_TOKEN: ${{ secrets.KS8500_USER_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
- fetch-depth: 0
-
+ fetch-depth: 0
+
- name: Fix tags
if: startsWith(github.ref, 'refs/tags/v')
run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} # Fixes an issue with actions/checkout@v4. See https://github.com/actions/checkout/issues/290
-
- - name: Setup OpenTAP
- uses: opentap/setup-opentap@main
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 8.x
-
+
- name: Build
+ run: dotnet build -c Release
+
+ - name: Create Package
run: |
- dotnet restore
- dotnet publish -c Release
- mv bin/Release/*.TapPackage .
-
+ cd bin/Release
+ ./tap package create ../../package.xml -v
+
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: tap_package
retention-days: 7
- path: "*.TapPackage"
+ path: "bin/Release/*.TapPackage"
Package-Test:
- runs-on:
- group: OpenTAP-SpokeVPC
- labels: [Linux, X64]
- container: ghcr.io/opentap/oci-images/build-dotnet:latest
+ runs-on: ubuntu-latest
needs:
- Build
steps:
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
- name: Setup OpenTAP
uses: opentap/setup-opentap@main
@@ -85,17 +77,18 @@ jobs:
- name: Package
run: |
cp Demonstration.*.TapPackage DemonstrationTest.TapPackage
- tap package install DemonstrationTest.TapPackage --force
+ tap package install DemonstrationTest.TapPackage
tap package test Demonstration -v
TestPlan-Test:
- runs-on:
- group: OpenTAP-SpokeVPC
- labels: [Linux, X64]
- container: ghcr.io/opentap/oci-images/build-dotnet:latest
+ runs-on: ubuntu-latest
needs:
- Build
steps:
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
- name: Setup OpenTAP
uses: opentap/setup-opentap@main
@@ -108,7 +101,7 @@ jobs:
- name: Package
run: |
cp Demonstration.*.TapPackage DemonstrationTest.TapPackage
- tap package install DemonstrationTest.TapPackage --force
+ tap package install DemonstrationTest.TapPackage
cd /opt/tap
tap run Packages/Demonstration/DataGenForResultsViewer.TapPlan
tap run Packages/Demonstration/DataGenForTimingAnalysis.TapPlan --settings "../../Packages/Demonstration/Tests/Test Bench Profile"
diff --git a/.gitversion b/.gitversion
index 787a5ff..c0aee99 100644
--- a/.gitversion
+++ b/.gitversion
@@ -3,7 +3,7 @@
# This is the version number that will be used. Prerelease numbers are calculated by
# counting git commits since the last change in this value.
-version = 9.0.7
+version = 9.1.0
# A version is determined to be a "beta" prerelease if it originates from the default branch
# The default branch is the first branch that matches the following regular expession.
diff --git a/Battery/BatteryDut.cs b/Battery/BatteryDut.cs
new file mode 100644
index 0000000..2796124
--- /dev/null
+++ b/Battery/BatteryDut.cs
@@ -0,0 +1,42 @@
+namespace OpenTap.Plugins.Demo.Battery
+{
+ [Display("Battery", "This DUT represents the battery itself.")]
+ public class BatteryDut : Dut
+ {
+ #region Settings
+
+ [Display("Capacity", "A larger cell size will result in faster charging and discharging.")]
+ [Unit("Ah")]
+ public double Capacity { get; set; } = 0.3;
+
+ [Display("Base Voltage", "The battery voltage when discharged.")]
+ [Unit("V")]
+ public double BaseVoltage { get; set; } = 3.0;
+
+ [Display("Charged Voltage", "The battery voltage when charged.")]
+ [Unit("V")]
+ public double ChargedVoltage { get; set; } = 4.2;
+
+ [Display("Initial Charge")]
+ [Unit("Ah")]
+ public double InitialCharge { get; set; } = 0.01;
+
+ #endregion
+
+ internal BatteryModel Model { get; private set; }
+ public BatteryDut()
+ {
+ Name = "Bat";
+ Rules.Add(() => Capacity >= 0, "Capacity must be greater than 0", nameof(Capacity));
+ Rules.Add(() => BaseVoltage >= 0, "Base Voltage must be greater than 0", nameof(BaseVoltage));
+ Rules.Add(() => ChargedVoltage >= BaseVoltage, "Charted Voltage must be greater than 0", nameof(ChargedVoltage));
+ Model = new BatteryModel();
+ }
+
+ public override void Open()
+ {
+ Model = new BatteryModel(initialCharge_Ah: InitialCharge, capacity_Ah: Capacity, chargedVoltage: ChargedVoltage, baseVoltage: BaseVoltage);
+ base.Open();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battery/BatteryModel.cs b/Battery/BatteryModel.cs
new file mode 100644
index 0000000..c6577ee
--- /dev/null
+++ b/Battery/BatteryModel.cs
@@ -0,0 +1,109 @@
+using System;
+
+namespace OpenTap.Plugins.Demo.Battery
+{
+ ///
+ /// This is a relatively inaccurate physical model of a Lithium-ion-like battery.
+ ///
+ class BatteryModel
+ {
+ // --- Physical constants ---
+ private const double Rgas = 8.314; // J/mol·K
+ private const double Tref = 298.15; // 25°C in Kelvin
+
+ // --- Simulation
+ private double baseVoltage = 3.0;
+ private double chargedVoltage = 4.2;
+
+ // --- Nominal parameters ---
+ private readonly double nominalCapacity; // Ah
+ private readonly double R25; // Internal resistance (Ohm) at 25°C
+ private readonly double beta; // Temperature coefficient for resistance
+ private readonly double Ea; // Activation energy (J/mol)
+ private readonly double selfDischarge25; // Self-discharge rate at 25°C (fraction/min)
+
+ // --- State variables ---
+ public double Charge_Ah { get; private set; } // Current stored charge
+ public double Voltage_V { get; private set; } // Terminal voltage
+ public double Current_A { get; private set; } // Charging (+) or discharging (-)
+ public double Voc { get; private set; } // Voltage open-circuit
+ public double SOC { get; private set; } // State of Charge (0–1)
+ public double CoulombicEfficiency { get; private set; }
+
+ public BatteryModel(
+ double capacity_Ah = 3.0,
+ double initialCharge_Ah = 1.5,
+ double internalResistance25 = 0.05,
+ double temperatureCoeff = 0.04,
+ double activationEnergy = 35000.0,
+ double selfDischargeRate25 = 0.0001,
+ double baseVoltage = 3.0,
+ double chargedVoltage = 4.2)
+ {
+ nominalCapacity = capacity_Ah;
+ Charge_Ah = initialCharge_Ah;
+ R25 = internalResistance25;
+ beta = temperatureCoeff;
+ Ea = activationEnergy;
+ selfDischarge25 = selfDischargeRate25;
+ this.baseVoltage = baseVoltage;
+ this.chargedVoltage = chargedVoltage;
+
+ double capacity = nominalCapacity * 1;
+ SOC = Charge_Ah / capacity;
+ SOC = Math.Min(Math.Max(SOC, 0.0), 1.0);
+ Voc = baseVoltage + (chargedVoltage - baseVoltage) * SOC + 0.05 * Math.Sin(5 * SOC);
+ }
+
+ ///
+ /// Update the battery state given an applied terminal voltage, temperature, and timestep.
+ ///
+ /// Applied terminal voltage (V)
+ /// Timestep (minutes)
+ /// Cell temperature (°C)
+ /// Current limited by the generator.
+ public void Update(double appliedVoltage, double dt_min, double temperature_C, double current_limit)
+ {
+ double T_K = temperature_C + 273.15;
+
+ // external resistance
+ double R_external = 0.02;
+
+ // --- Temperature-dependent parameters ---
+ double R_internal = R25 * Math.Exp(beta * (25 - temperature_C));
+ double capacity = nominalCapacity * (1 - 0.002 * Math.Abs(temperature_C - 25));
+ double k_self = selfDischarge25 * Math.Exp(0.05 * (temperature_C - 25));
+
+ // --- Compute open-circuit voltage based on SOC ---
+ SOC = Charge_Ah / capacity;
+ SOC = Math.Min(Math.Max(SOC, 0.0), 1.0);
+ Voc = baseVoltage + (chargedVoltage - baseVoltage) * SOC + 0.05 * Math.Sin(5 * SOC);
+
+ // --- Solve current from voltage equation ---
+ // I is clamped by +/- current_limit.
+ // appliedVoltage = Voc - I * R_internal
+ Current_A = Math.Max(Math.Min((Voc- appliedVoltage) / (R_internal + R_external), current_limit), -current_limit);
+
+ // --- Temperature-dependent efficiency (Arrhenius relation) ---
+ CoulombicEfficiency = Math.Exp(-Ea / Rgas * (1.0 / T_K - 1.0 / Tref));
+ CoulombicEfficiency = Math.Min(Math.Max(CoulombicEfficiency, 0.7), 1.0);
+
+ // --- Effective current (charging losses) ---
+ double effectiveCurrent = Current_A;
+ if (Current_A > 0) // Charging
+ effectiveCurrent *= CoulombicEfficiency;
+
+ // --- Update charge (Ah) ---
+ Charge_Ah += -effectiveCurrent * dt_min / 60.0;
+
+ // --- Apply self-discharge ---
+ Charge_Ah -= Charge_Ah * k_self * dt_min;
+
+ // --- Clamp charge ---
+ Charge_Ah = Math.Min(Math.Max(Charge_Ah, 0.0), capacity);
+
+ // --- Update terminal voltage ---
+ Voltage_V = appliedVoltage;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battery/ChargeStep.cs b/Battery/ChargeStep.cs
index b02dcbe..7700ae9 100644
--- a/Battery/ChargeStep.cs
+++ b/Battery/ChargeStep.cs
@@ -4,8 +4,6 @@
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
using System;
-using OpenTap;
-
using System.Diagnostics; // Use Platform infrastructure/core components (log,TestStep definition, etc)
namespace OpenTap.Plugins.Demo.Battery
@@ -21,28 +19,33 @@ public class ChargeStep : SamplingStepBase
[Unit("V")]
public double Voltage { get; set; }
- [Display("Target Voltage Margin", Group: "Cell", Order: -1)]
+ [Display("Target Voltage", Group: "Power Supply", Order: -1)]
[Unit("V")]
- public double TargetCellVoltageMargin { get; set; }
+ public double TargetVoltage { get; set; }
[Display("Charge Time", Group: "Output", Order: 0)]
[Unit("s")]
[Output]
public double ChargeTime { get; private set; }
+
+
#endregion
public ChargeStep()
{
Voltage = 4.2;
Current = 10;
- TargetCellVoltageMargin = 0.1;
- Rules.Add(() => (Voltage >= 0) && (Voltage <= 10), "Voltage must be >= 0 and <= 10", "Voltage");
- Rules.Add(() => (Current >= 0) && (Current <= 20), "Current must be >= 0 and <= 20", "Current");
- Rules.Add(() => (TargetCellVoltageMargin >= 0) && (TargetCellVoltageMargin <= 1), "TargetCellVoltageMargin must be >= 0 and <= 1", "TargetCellVoltageMargin");
+ TargetVoltage = 4.1;
+ Rules.Add(() => (Voltage >= 0) && (Voltage <= 10), "Voltage must be >= 0 and <= 10", nameof(Voltage));
+ Rules.Add(() => (Current >= 0) && (Current <= 20), "Current must be >= 0 and <= 20", nameof(Current));
+ Rules.Add(() => TargetVoltage < Voltage, "Target voltage must be less than the voltage", nameof(TargetVoltage));
+
}
+ public double accumulatedCharge;
public override void Run()
{
+ accumulatedCharge = 0;
var sw = Stopwatch.StartNew();
PowerAnalyzer.Setup(Voltage, Current);
PowerAnalyzer.EnableOutput();
@@ -50,11 +53,12 @@ public override void Run()
base.Run(); // Most of the work is being done here, with callbacks to this class.
PowerAnalyzer.DisableOutput();
ChargeTime = sw.Elapsed.TotalSeconds;
+ UpgradeVerdict(Verdict.Pass);
}
protected override void WhileSampling()
{
- while(Math.Abs(PowerAnalyzer.MeasureVoltage() - Voltage) > TargetCellVoltageMargin)
+ while(Dut.Model.Voc < TargetVoltage)
{
TapThread.Sleep(50);
}
@@ -66,9 +70,21 @@ public class ChargeResult
[Display("Sample Number")]
public int SampleNo { get; set; }
[Display("Voltage")]
+
+ [Unit("V")]
public double Voltage { get; set; }
+
[Display("Current")]
+ [Unit("A")]
public double Current { get; set; }
+
+ [Display("Power")]
+ [Unit("W")]
+ public double Power { get; set; }
+
+ [Display("Acc. Charge")]
+ [Unit("J")]
+ public double AccumulatedCharge { get; set; }
}
protected override void OnSample(double voltage, double current, int sampleNo)
@@ -78,8 +94,8 @@ protected override void OnSample(double voltage, double current, int sampleNo)
barVoltage.LowerLimit = 2; //Cell voltage defined in PowerAnalyzer
barVoltage.UpperLimit = 4.7;
Log.Info("Voltage: " + barVoltage.GetBar(voltage));
-
- Results.Publish(new ChargeResult { SampleNo = sampleNo, Voltage = Math.Truncate(voltage * 100) / 100, Current = Math.Truncate(current * 100) / 100});
+ accumulatedCharge += voltage * current * MeasurementInterval;
+ Results.Publish(new ChargeResult { SampleNo = sampleNo, Voltage = Math.Round(voltage, 2), Current = Math.Round(current,2), Power = voltage * current, AccumulatedCharge = accumulatedCharge});
}
}
diff --git a/Battery/DischargeStep.cs b/Battery/DischargeStep.cs
index 112447a..a40a9c3 100644
--- a/Battery/DischargeStep.cs
+++ b/Battery/DischargeStep.cs
@@ -5,7 +5,6 @@
// warranty, obligations or liability for any sample application files.
using System;
using System.Diagnostics; // Use Platform infrastructure/core components (log,TestStep definition, etc)
-using OpenTap;
namespace OpenTap.Plugins.Demo.Battery
{
@@ -19,10 +18,11 @@ public class DischargeStep : SamplingStepBase
[Display("Voltage", Group: "Power Supply", Order: -1)]
[Unit("V")]
public double Voltage { get; set; }
-
- [Display("Target Voltage Margin", Group: "Cell", Order: -1)]
+
+ [Display("Target Voltage", Group: "Power Supply", Order: -1)]
[Unit("V")]
- public double TargetCellVoltageMargin { get; set; }
+ public double TargetVoltage { get; set; }
+
[Display("Discharge Time", Group: "Output", Order: 0)]
[Unit("s")]
@@ -35,10 +35,10 @@ public DischargeStep()
{
Voltage = 2.2;
Current = 5;
- TargetCellVoltageMargin = 0.8;
+ TargetVoltage = 3.1;
Rules.Add(() => (Voltage >= 0) && (Voltage <= 10), "Voltage must be >= 0 and <= 10", "Voltage");
Rules.Add(() => (Current >= 0) && (Current <= 20), "Current must be >= 0 and <= 20", "Current");
- Rules.Add(() => (TargetCellVoltageMargin >= 0) && (TargetCellVoltageMargin <= 1), "TargetCellVoltageMargin must be >= 0 and <= 1", "TargetCellVoltageMargin");
+ Rules.Add(() => (TargetVoltage > Voltage), "Target Voltage must be greater than the voltage.", "TargetCellVoltageMargin");
}
public override void Run()
@@ -50,11 +50,12 @@ public override void Run()
base.Run();
PowerAnalyzer.DisableOutput();
DischargeTime = sw.Elapsed.TotalSeconds;
+ UpgradeVerdict(Verdict.Pass);
}
protected override void WhileSampling()
{
- while(Math.Abs(PowerAnalyzer.MeasureVoltage() - Voltage) > TargetCellVoltageMargin)
+ while(Dut.Model.Voc > TargetVoltage)
{
TapThread.Sleep(50);
}
diff --git a/Battery/OpenTap.Plugins.Demo.Battery.csproj b/Battery/OpenTap.Plugins.Demo.Battery.csproj
index de127bb..9b1c3a7 100644
--- a/Battery/OpenTap.Plugins.Demo.Battery.csproj
+++ b/Battery/OpenTap.Plugins.Demo.Battery.csproj
@@ -15,7 +15,8 @@
-
+
+
diff --git a/Battery/PowerAnalyzer.cs b/Battery/PowerAnalyzer.cs
index 263859e..0d14f21 100644
--- a/Battery/PowerAnalyzer.cs
+++ b/Battery/PowerAnalyzer.cs
@@ -4,25 +4,24 @@
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
+using System;
using System.Diagnostics;
-using OpenTap;
+using OpenTap.Metrics;
namespace OpenTap.Plugins.Demo.Battery
{
- [Display("Power Analyzer", "Simulated power analyzer instrument used for charge/discharge demo steps.", Groups: new[] { "Demo", "Battery Test" })]
+ [Display("Power Analyzer", "Simulated power analyzer instrument used for charge/discharge demo steps.",
+ Groups: new[] { "Demo", "Battery Test" })]
public class PowerAnalyzer : Instrument
{
- #region Settings
- [Display("Cell Size Factor", "A larger cell size will result in faster charging and discharging.")]
- public double CellSizeFactor { get; set; }
- #endregion
-
+ private double idleVoltage = 0.0;
+
+ [Metric] [Unit("V")]
+ [Display("Idle Voltage")]
+ public double? IdleVoltage => Math.Round( lastBattery?.Model?.Voc ?? idleVoltage, 2);
public PowerAnalyzer()
{
- Name = "PSU";
-
- CellSizeFactor = 0.005;
- Rules.Add(() => (CellSizeFactor >= 0) && (CellSizeFactor <= .1), "CellSizeFactor must be >= 0 and <= .1", "Voltage");
+ Name = "PSU";
}
///
@@ -32,8 +31,10 @@ public override void Open()
{
base.Open();
_voltage = 0;
- _cellVoltage = 2.7;
Log.Info("Device PSU opened");
+ _sw = new Stopwatch();
+
+
}
///
@@ -49,23 +50,27 @@ public override void Close()
base.Close();
}
- public double MeasureCurrent()
- {
- UpdateCurrentAndVoltage();
- return _current;
- }
-
- public double MeasureVoltage()
+ private BatteryDut lastBattery;
+ public (double voltage, double current) Measure(BatteryDut dut)
{
- UpdateCurrentAndVoltage();
- return _cellVoltage;
+ if (_sw.IsRunning == false)
+ {
+ dut.Model.Update(_voltage, 0.0, TemperatureChamber.Temperature, _currentLimit);
+ _sw.Start();
+ }
+ lastBattery = dut;
+ if(_sw.ElapsedMilliseconds > 1)
+ {
+ dut.Model.Update(_voltage, _sw.Elapsed.TotalSeconds, TemperatureChamber.Temperature, _currentLimit);
+ _sw.Restart();
+ }
+ return (dut.Model.Voc, dut.Model.Current_A);
}
internal void Setup(double voltage, double current)
{
_voltage = voltage;
_currentLimit = current;
- _current = current;
}
internal void EnableOutput()
@@ -81,29 +86,7 @@ internal void DisableOutput()
}
private double _voltage;
- private double _cellVoltage = 2.7;
- private double _current = 10;
- private double _currentLimit;
Stopwatch _sw;
- private void UpdateCurrentAndVoltage()
- {
- if (_sw == null || !_sw.IsRunning) // Only update if output is enabled
- return;
-
- // Generates a somewhat random curve that gradually approaches the limit.
- _current = (_currentLimit * (_voltage - _cellVoltage)*2 + RandomNumber.Generate()*_currentLimit/50);
-
- if (_current >= _currentLimit)
- {
- _current = _currentLimit;
- }
- else if (_current < 0-_currentLimit)
- {
- _current = 0- _currentLimit;
- }
-
- _cellVoltage += CellSizeFactor * _current * _sw.Elapsed.TotalSeconds * 10;
- _sw.Restart();
- }
+ private double _currentLimit;
}
}
diff --git a/Battery/RatingStep.cs b/Battery/RatingStep.cs
index ed767ef..d162082 100644
--- a/Battery/RatingStep.cs
+++ b/Battery/RatingStep.cs
@@ -5,7 +5,6 @@
// warranty, obligations or liability for any sample application files.
using System.ComponentModel;
using System.Xml.Serialization;
-using OpenTap;
namespace OpenTap.Plugins.Demo.Battery
{
diff --git a/Battery/SamplingStepBase.cs b/Battery/SamplingStepBase.cs
index 787e238..4b0417a 100644
--- a/Battery/SamplingStepBase.cs
+++ b/Battery/SamplingStepBase.cs
@@ -3,8 +3,6 @@
// the sample application files (and/or any modified version) in any way
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
-using System.Timers;
-using OpenTap;
namespace OpenTap.Plugins.Demo.Battery
{
@@ -19,6 +17,9 @@ public abstract class SamplingStepBase : TestStep
[Display("Power Analyzer", Group: "Resources", Order: -100)]
public PowerAnalyzer PowerAnalyzer { get; set; }
+ [Display("DUT")]
+ public BatteryDut Dut { get; set; }
+
public SamplingStepBase()
{
MeasurementInterval = .2;
@@ -27,9 +28,19 @@ public SamplingStepBase()
public override void Run()
{
_sampleNo = 0;
- Timer timer = new Timer((int)(MeasurementInterval * 1000));
- timer.Elapsed += timer_Elapsed;
- timer.Start();
+ bool measuring = true;
+ var trd = TapThread.Start(() =>
+ {
+ PowerAnalyzer.Measure(Dut);
+ TapThread.Sleep(10);
+ while (measuring)
+ {
+ var (voltage, current) = PowerAnalyzer.Measure(Dut);
+ OnSample(voltage, current, _sampleNo++);
+ TapThread.Sleep((int)(1000.0 * MeasurementInterval));
+ }
+
+ });
try
{
// Sleep, while the timer thread generates data.
@@ -39,17 +50,10 @@ public override void Run()
}
finally
{
- timer.Stop();
+ measuring = false;
}
}
- private void timer_Elapsed(object sender, ElapsedEventArgs e)
- {
- double voltage = PowerAnalyzer.MeasureVoltage();
- double current = PowerAnalyzer.MeasureCurrent();
- OnSample(voltage, current, _sampleNo++);
- }
-
protected abstract void WhileSampling();
protected abstract void OnSample(double voltage, double current, int sampleNo);
diff --git a/Battery/SetTemperatureStep.cs b/Battery/SetTemperatureStep.cs
index 3a7b6a3..d38e3e6 100644
--- a/Battery/SetTemperatureStep.cs
+++ b/Battery/SetTemperatureStep.cs
@@ -4,8 +4,7 @@
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
-using OpenTap;
-
+using System.Diagnostics;
namespace OpenTap.Plugins.Demo.Battery
{
[Display("Set Temperature", "Configure the temperature chamber.", Groups: new[] {"Demo", "Battery Test" })]
@@ -36,9 +35,23 @@ public SetTemperatureStep()
public override void Run()
{
Log.Info("Temperature set to: " + Temperature + " °C");
- TapThread.Sleep(3000);
- Log.Info("Temperature reached: " + Temperature + " °C [3000 ms]");
- RunChildSteps(); //If step has child steps.
+ bool waiting = true;
+ TapThread.Start(() =>
+ {
+ while (waiting)
+ {
+ Log.Info($"Current temperature: {TemperatureChamber.Temperature:F1} °C");
+ TapThread.Sleep(1000);
+ }
+
+ });
+ Chamber.SetTarget(Temperature, Humidity);
+ var sw = Stopwatch.StartNew();
+
+ Chamber.WaitForConditions();
+ waiting = false;
+
+ Log.Info(sw, "Temperature reached: " + Temperature + " °C");
}
}
}
diff --git a/Battery/TemperatureChamber.cs b/Battery/TemperatureChamber.cs
index 8d6ad6a..7478e34 100644
--- a/Battery/TemperatureChamber.cs
+++ b/Battery/TemperatureChamber.cs
@@ -3,13 +3,24 @@
// the sample application files (and/or any modified version) in any way
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
-using OpenTap;
+
+using System;
+using OpenTap.Metrics;
namespace OpenTap.Plugins.Demo.Battery
{
[Display("Temperature Chamber", "Simulated temperature chamber instrument used for SetTemperature demo step.", Groups: new[] { "Demo", "Battery Test" })]
public class TemperatureChamber : Instrument
{
+ private double humidityTarget = 50;
+ private double temperatureTarget = 50;
+ public static double Temperature = 25;
+ public static double Humidity = 25;
+ [Metric]
+ [Unit("°C")]
+ [Display("Temperature")]
+ public double TemperatureMetric => Math.Round(Temperature, 2);
+
public TemperatureChamber()
{
Name = "TEMP";
@@ -32,5 +43,40 @@ public override void Close()
Log.Info("Device TEMP closed");
base.Close();
}
+
+ public void SetTarget(double temperature, double humidity)
+ {
+ this.temperatureTarget = temperature;
+ this.humidityTarget = humidity;
+ }
+
+ public void WaitForConditions()
+ {
+ var rnd = new Random();
+ if (temperatureTarget > Temperature)
+ {
+ Log.Debug("Heating...");
+ while (temperatureTarget > Temperature)
+ {
+ // heating up.
+ Temperature += rnd.NextDouble() * 0.02 + 0.05;
+ TapThread.Sleep(10);
+ OnActivity();
+ }
+ }else if (temperatureTarget < Temperature)
+ {
+ Log.Debug("Cooling...");
+ while (temperatureTarget < Temperature)
+ {
+
+ Temperature -= (rnd.NextDouble() * 0.02 + 0.05);
+ TapThread.Sleep(10);
+ OnActivity();
+ }
+
+ }
+
+ Temperature = temperatureTarget;
+ }
}
}
diff --git a/Demos.sln b/Demos.sln
index d883716..eb62656 100644
--- a/Demos.sln
+++ b/Demos.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTap.Plugins.Demo.Batter
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTap.Plugins.Demo.ResultsAndTiming", "ResultsAndTiming\OpenTap.Plugins.Demo.ResultsAndTiming.csproj", "{400E13F0-E63E-496B-9E3F-955BECC22444}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTap.Plugins.Demo.Battery.UI", "OpenTap.Plugins.Demo.Battery.UI\OpenTap.Plugins.Demo.Battery.UI.csproj", "{E7549526-BA5E-4F56-8E03-ADF310FBD6B4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -22,6 +24,10 @@ Global
{400E13F0-E63E-496B-9E3F-955BECC22444}.Debug|Any CPU.Build.0 = Debug|Any CPU
{400E13F0-E63E-496B-9E3F-955BECC22444}.Release|Any CPU.ActiveCfg = Release|Any CPU
{400E13F0-E63E-496B-9E3F-955BECC22444}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E7549526-BA5E-4F56-8E03-ADF310FBD6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7549526-BA5E-4F56-8E03-ADF310FBD6B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7549526-BA5E-4F56-8E03-ADF310FBD6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7549526-BA5E-4F56-8E03-ADF310FBD6B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Directory.Build.props b/Directory.Build.props
index 553d82e..f6c255c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
netstandard2.0
false
false
- 9.25.1
+ 9.31.1-rc.4+d1f598be
$(MSBuildThisFileDirectory)/bin/$(Configuration)
diff --git a/OpenTap.Plugins.Demo.Battery.UI/BatteryDemoPanel.cs b/OpenTap.Plugins.Demo.Battery.UI/BatteryDemoPanel.cs
new file mode 100644
index 0000000..df2f840
--- /dev/null
+++ b/OpenTap.Plugins.Demo.Battery.UI/BatteryDemoPanel.cs
@@ -0,0 +1,17 @@
+using System.Windows;
+using Keysight.OpenTap.Wpf;
+
+namespace OpenTap.Plugins.Demo.Battery.UI;
+
+[Display("Battery Demonstration", Group:"Getting Started", Description: "This is a demonstration panel for the battery test.")]
+public class BatteryDemoPanel : IGettingStartedPanel
+{
+
+ public FrameworkElement CreateElement(ITapDockContext context)
+ {
+ return new DemonstrationPanelTest(context);
+ }
+
+ public double? DesiredWidth => 600;
+ public double? DesiredHeight => 800;
+}
diff --git a/OpenTap.Plugins.Demo.Battery.UI/DemonstrationPanelTest.xaml b/OpenTap.Plugins.Demo.Battery.UI/DemonstrationPanelTest.xaml
new file mode 100644
index 0000000..e10f378
--- /dev/null
+++ b/OpenTap.Plugins.Demo.Battery.UI/DemonstrationPanelTest.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+ Battery Test Demonstration
+
+ In this demonstration, we will show how a basic battery test can be set up.
+ Here we are testing how different temperatures affect charging and discharging the battery.
+ It consists of the following simulated instruments:
+
+
+
+ • A battery device under test (DUT).
+
+
+ • A power supply / analyzer
+
+
+ • A temperature chamber
+
+
+
+
+ • Load the demonstration.
+
+
+ • Run the test plan.
+
+
+ • Show Results Viewer.
+
+
+
+
+
+
+
+
diff --git a/OpenTap.Plugins.Demo.Battery.UI/DemonstrationPanelTest.xaml.cs b/OpenTap.Plugins.Demo.Battery.UI/DemonstrationPanelTest.xaml.cs
new file mode 100644
index 0000000..0febe92
--- /dev/null
+++ b/OpenTap.Plugins.Demo.Battery.UI/DemonstrationPanelTest.xaml.cs
@@ -0,0 +1,167 @@
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using Keysight.OpenTap.Gui;
+using Keysight.OpenTap.Plugins.Csv;
+using Keysight.OpenTap.Plugins.ResultListeners;
+using Keysight.OpenTap.Wpf;
+
+namespace OpenTap.Plugins.Demo.Battery.UI;
+
+public partial class DemonstrationPanelTest : UserControl
+{
+ private readonly ITapDockContext _context;
+
+ public DemonstrationPanelTest(ITapDockContext context)
+ {
+ _context = context;
+ InitializeComponent();
+ }
+
+ private void LoadResources()
+ {
+ ComponentSettings.SetSettingsProfile("Bench", Path.GetFullPath(Path.Combine("Settings","Bench","Battery Demo")));
+ InstrumentSettings.Current.Clear();
+ InstrumentSettings.Current.Add(new PowerAnalyzer());
+ InstrumentSettings.Current.Add(new TemperatureChamber());
+ InstrumentSettings.Current.Save();
+ DutSettings.Current.Clear();
+ DutSettings.Current.Add(new BatteryDut());
+
+ DutSettings.Current.Save();
+ if (!ResultSettings.Current.OfType().Any())
+ ResultSettings.Current.Add(new CsvResultListener());
+
+ if (!ResultSettings.Current.OfType().Any())
+ {
+ ResultSettings.Current.Add(new SQLiteDatabase());
+ ResultSettings.Current.Save();
+ }
+
+ InstrumentSettings.Current.Invalidate();
+ DutSettings.Current.Invalidate();
+
+ }
+
+ private void LoadTestPlan()
+ {
+ var plan = new TestPlan();
+ _context.Plan = plan;
+
+ var tempSweep = new BasicSteps.SweepParameterStep();
+ var temp = new SetTemperatureStep();
+ var charge = new ChargeStep();
+ var discharge = new DischargeStep();
+ tempSweep.ChildTestSteps.Add(temp);
+ tempSweep.ChildTestSteps.Add(charge);
+ tempSweep.ChildTestSteps.Add(discharge);// set the sweep loop to being expanded.
+ ChildItemVisibility.SetVisibility(tempSweep, ChildItemVisibility.Visibility.Visible);
+
+ plan.ChildTestSteps.Add(tempSweep);
+
+ TypeData.GetTypeData(temp)
+ .GetMember(nameof(temp.Temperature))
+ .Parameterize(tempSweep, temp, "Temperature");
+
+ tempSweep.SweepValues.Add(new BasicSteps.SweepRow(){Values = {["Temperature"] = -10.0}});
+ tempSweep.SweepValues.Add(new BasicSteps.SweepRow(){Values = {["Temperature"] = 0.0}});
+ tempSweep.SweepValues.Add(new BasicSteps.SweepRow(){Values = {["Temperature"] = 10.0}});
+ tempSweep.SweepValues.Add(new BasicSteps.SweepRow(){Values = {["Temperature"] = 24.0}});
+ tempSweep.SweepValues.Add(new BasicSteps.SweepRow(){Values = {["Temperature"] = 30.0}});
+ tempSweep.SweepValues.Add(new BasicSteps.SweepRow(){Values = {["Temperature"] = 45.0}});
+
+ plan.Save("Battery Demo.TapPlan");
+
+ var testPlanGridType = TypeData.GetTypeData("Keysight.OpenTap.Gui.TestPlanPlugin");
+ TapPanel.Focus(testPlanGridType);
+
+
+ }
+
+ private void OnRunTestPlan(object sender, RoutedEventArgs e)
+ {
+ _context.Run();
+ var testPlanGridType = TypeData.GetTypeData("Keysight.OpenTap.Gui.TestPlanPlugin");
+ TapPanel.Focus(testPlanGridType);
+ }
+
+ private void LoadViewPreset()
+ {
+ var destPath = Path.Combine(ViewPreset.PresetDir, "batterydemo.xml");
+ var srcPath = Path.Combine("Packages", "Demonstration", "battery_demo_preset.xml");
+ File.Delete(destPath);
+ File.Copy(srcPath, destPath);
+
+ ViewPreset.SelectPreset(Path.GetFileNameWithoutExtension(destPath), raiseEvent: true);
+
+ }
+ private void OnShowResultsViewer(object sender, RoutedEventArgs e)
+ {
+ var templatePath = Path.Combine("Packages", "Demonstration", "battery_demo.TapReport");
+ var store = ResultSettings.Current.OfType().FirstOrDefault();
+ ResultsViewer.Open(store, Application.Current.MainWindow, templatePath, 1);
+ }
+
+ private void OnLoadDemo(object sender, RoutedEventArgs e)
+ {
+ var response = ShowMessage("Load Demonstration?",
+ " Loading the battery test demonstration will cause the following changes:\n" +
+ " - New bench profile: a DUT, a power analyzer and a temperature chamber.\n" +
+ " - Added result listeners: Adding CSV and SQLite database result storage.\n"+
+ " - New view preset: A view preset will be applied providing an optimized view.\n" +
+ " - New test plan: A demonstration test plan will be loaded.\n\n" +
+
+ "This can be undone by selecting the previous bench profile and view preset.",
+ ["OK", "Cancel"]);
+ if (response == "Cancel")
+ return;
+ LoadResources();
+ LoadTestPlan();
+ LoadViewPreset();
+ }
+
+ public class MessageBox : IDisplayAnnotation
+ {
+ string IDisplayAnnotation.Description => null;
+ string[] IDisplayAnnotation.Group => null;
+
+ double IDisplayAnnotation.Order => 0.0;
+ bool IDisplayAnnotation.Collapsed => false;
+
+
+ public string Name { get; internal set; }
+
+ [Layout(LayoutMode.FullRow)]
+ [Browsable(true)]
+ public string Message { get; internal set; }
+
+ public bool ShowFilePath { get; internal set; }
+
+ [EnabledIf(nameof(ShowFilePath), true, HideIfDisabled = true)]
+ [FilePath(FilePathAttribute.BehaviorChoice.Open)]
+ [Display ("File Path")]
+ public string FilePath { get; set; }
+
+
+ public string[] Options { get; internal set; } = ["OK"];
+
+ [Layout(LayoutMode.FloatBottom|LayoutMode.FullRow)]
+ [Submit]
+ [AvailableValues(nameof(Options))]
+ public string SelectedOption { get; set; }
+ }
+
+ string ShowMessage(string title, string message, string[] options = null)
+ {
+ var box = new MessageBox
+ {
+ Name = title, Message = message,
+ Options = options ?? ["OK"],
+ SelectedOption = options?.FirstOrDefault() ?? "OK"
+ };
+ UserInput.Request(box);
+ return box.SelectedOption;
+ }
+}
\ No newline at end of file
diff --git a/OpenTap.Plugins.Demo.Battery.UI/OpenTap.Plugins.Demo.Battery.UI.csproj b/OpenTap.Plugins.Demo.Battery.UI/OpenTap.Plugins.Demo.Battery.UI.csproj
new file mode 100644
index 0000000..681a48f
--- /dev/null
+++ b/OpenTap.Plugins.Demo.Battery.UI/OpenTap.Plugins.Demo.Battery.UI.csproj
@@ -0,0 +1,37 @@
+
+
+
+ net9.0-windows
+ True
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+ Packages/Demonstration/battery_demo.TapReport
+
+
+ Always
+ Packages/Demonstration/battery_demo_preset.xml
+
+
+
+
diff --git a/OpenTap.Plugins.Demo.Battery.UI/Resources/battery_demo.TapReport b/OpenTap.Plugins.Demo.Battery.UI/Resources/battery_demo.TapReport
new file mode 100644
index 0000000..2376046
--- /dev/null
+++ b/OpenTap.Plugins.Demo.Battery.UI/Resources/battery_demo.TapReport
@@ -0,0 +1,169 @@
+
+
+
+
+
+ UseDarkTheme=True
+ LegendPlacement=0
+ LegendPosition=2
+ LegendAxisName=True
+ LegendVisible=True
+ DatetimeAxis=False
+
+
+
+
+
+
+
+ Sample Number
+
+
+
+
+ Voltage
+
+
+
+
+
+
+
+
+
+ ResultType=Charge
+
+
+
+
+ Column=Temperature
+
+
+
+ true
+
+
+
+ UseDarkTheme=True
+ LegendPlacement=0
+ LegendPosition=2
+ LegendAxisName=True
+ LegendVisible=True
+ DatetimeAxis=False
+
+
+
+
+
+
+
+ Sample Number
+
+
+
+
+ Current
+
+
+
+
+
+
+
+
+
+ ResultType=Charge
+
+
+
+
+ Column=Temperature
+
+
+
+ true
+
+
+
+ UseDarkTheme=True
+ LegendPlacement=0
+ LegendPosition=2
+ LegendAxisName=True
+ LegendVisible=True
+ DatetimeAxis=False
+
+
+
+
+
+
+
+ Sample Number
+
+
+
+
+ Voltage
+
+
+
+
+
+
+
+
+
+ ResultType=Discharge
+
+
+
+
+ Column=Temperature
+
+
+
+ true
+
+
+
+ UseDarkTheme=True
+ LegendPlacement=0
+ LegendPosition=2
+ LegendAxisName=True
+ LegendVisible=True
+ DatetimeAxis=False
+
+
+
+
+
+
+
+ Sample Number
+
+
+
+
+ Current
+
+
+
+
+
+
+
+
+
+ ResultType=Discharge
+
+
+
+
+ Column=Temperature
+
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/OpenTap.Plugins.Demo.Battery.UI/Resources/battery_demo_preset.xml b/OpenTap.Plugins.Demo.Battery.UI/Resources/battery_demo_preset.xml
new file mode 100644
index 0000000..ab03ad9
--- /dev/null
+++ b/OpenTap.Plugins.Demo.Battery.UI/Resources/battery_demo_preset.xml
@@ -0,0 +1,631 @@
+
+
+
+ Keysight.OpenTap.Gui.PanelSettings
+
+
+ Layout
+
+ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxMYXlvdXRSb290Pg0KICA8Um9vdFBhbmVsIE9yaWVudGF0aW9uPSJIb3Jpem9udGFsIj4NCiAgICA8TGF5b3V0UGFuZWwgT3JpZW50YXRpb249IlZlcnRpY2FsIiBEb2NrV2lkdGg9IjEuNTY1NDAwODQzODgxODU2NSoiPg0KICAgICAgPExheW91dFBhbmVsIE9yaWVudGF0aW9uPSJIb3Jpem9udGFsIiBEb2NrSGVpZ2h0PSIxLjMxNzE5Mzk0NzczMDM5ODkqIiBEb2NrTWluV2lkdGg9IjEwMCIgRG9ja01pbkhlaWdodD0iMTAwIj4NCiAgICAgICAgPExheW91dEFuY2hvcmFibGVQYW5lIElkPSIzNTZiNzcyOS1kYThiLTQ0OTAtODJmOS0zYTU0MDVkYmIzYjIiIERvY2tXaWR0aD0iMS44MjU5MzcxNjMyMjEzMTg4KiIgRG9ja01pbldpZHRoPSIxMDAiIERvY2tNaW5IZWlnaHQ9IjEwMCI+DQogICAgICAgICAgPExheW91dEFuY2hvcmFibGUgQXV0b0hpZGVNaW5XaWR0aD0iMTAwIiBBdXRvSGlkZU1pbkhlaWdodD0iMTAwIiBUaXRsZT0iV2VsY29tZSIgQ29udGVudElkPSJLZXlzaWdodC5PcGVuVGFwLkd1aS5XZWxjb21lU2NyZWVuUGFuZWwiIExhc3RBY3RpdmF0aW9uVGltZVN0YW1wPSIxMS8xMC8yMDI1IDE0OjQ5OjAwIiAvPg0KICAgICAgICAgIDxMYXlvdXRBbmNob3JhYmxlIEF1dG9IaWRlTWluV2lkdGg9IjEwMCIgQXV0b0hpZGVNaW5IZWlnaHQ9IjEwMCIgVGl0bGU9IlRlc3QgUGxhbiIgSXNTZWxlY3RlZD0iVHJ1ZSIgQ29udGVudElkPSJLZXlzaWdodC5PcGVuVGFwLkd1aS5UZXN0UGxhblBsdWdpbiIgVG9vbFRpcD0iVGVzdCBQbGFuIFBhbmVsIiBMYXN0QWN0aXZhdGlvblRpbWVTdGFtcD0iMTEvMTEvMjAyNSAwODoyMzozMCIgLz4NCiAgICAgICAgPC9MYXlvdXRBbmNob3JhYmxlUGFuZT4NCiAgICAgICAgPExheW91dEFuY2hvcmFibGVQYW5lIElkPSIxNDRmYWE0MS02YmRkLTRmNzQtYWY5Yy0wZGZlMzY1MTZiMzUiIERvY2tXaWR0aD0iMS44NzQxMzE5NDQ0NDQ0NDM1KiIgRG9ja01pbldpZHRoPSIxMDAiIERvY2tNaW5IZWlnaHQ9IjEwMCI+DQogICAgICAgICAgPExheW91dEFuY2hvcmFibGUgQXV0b0hpZGVNaW5XaWR0aD0iMTAwIiBBdXRvSGlkZU1pbkhlaWdodD0iMTAwIiBUaXRsZT0iTG9nIiBJc1NlbGVjdGVkPSJUcnVlIiBDb250ZW50SWQ9IktleXNpZ2h0Lk9wZW5UYXAuR3VpLkxvZ1BhbmVsUGx1Z2luIiBUb29sVGlwPSJMb2cgUGFuZWwiIEZsb2F0aW5nTGVmdD0iMTEzOC42NjY2NjY2NjY2NjY1IiBGbG9hdGluZ1RvcD0iMzk3LjMzMzMzMzMzMzMzMzMiIEZsb2F0aW5nV2lkdGg9IjUwMiIgRmxvYXRpbmdIZWlnaHQ9IjQzMC42NjY2NjY2NjY2NjY2MyIgTGFzdEFjdGl2YXRpb25UaW1lU3RhbXA9IjExLzEwLzIwMjUgMTQ6NTc6MDgiIFByZXZpb3VzQ29udGFpbmVySWQ9ImY4N2UxNTNjLTQ5ZTAtNDdkMC1iMDFhLTVjY2U5NWIwODE4OCIgUHJldmlvdXNDb250YWluZXJJbmRleD0iMSIgLz4NCiAgICAgICAgICA8TGF5b3V0QW5jaG9yYWJsZSBBdXRvSGlkZU1pbldpZHRoPSIxMDAiIEF1dG9IaWRlTWluSGVpZ2h0PSIxMDAiIFRpdGxlPSJUZXN0IFN0ZXAgU2V0dGluZ3MiIENvbnRlbnRJZD0iS2V5c2lnaHQuT3BlblRhcC5HdWkuU3RlcFNldHRpbmdzUGx1Z2luIiBUb29sVGlwPSJTdGVwIFNldHRpbmdzIFBhbmVsIiBMYXN0QWN0aXZhdGlvblRpbWVTdGFtcD0iMTEvMTAvMjAyNSAxNDo1Njo0MCIgLz4NCiAgICAgICAgPC9MYXlvdXRBbmNob3JhYmxlUGFuZT4NCiAgICAgIDwvTGF5b3V0UGFuZWw+DQogICAgICA8TGF5b3V0QW5jaG9yYWJsZVBhbmVHcm91cCBPcmllbnRhdGlvbj0iSG9yaXpvbnRhbCIgRG9ja0hlaWdodD0iMi4wODI1MDgyNTA4MjUwODI3KiI+DQogICAgICAgIDxMYXlvdXRBbmNob3JhYmxlUGFuZUdyb3VwIE9yaWVudGF0aW9uPSJIb3Jpem9udGFsIiBGbG9hdGluZ1dpZHRoPSI3OTQiIEZsb2F0aW5nSGVpZ2h0PSI1MDAiIEZsb2F0aW5nTGVmdD0iNjMyLjY2NjY2NjY2NjY2NjYiIEZsb2F0aW5nVG9wPSI4MjYuNjY2NjY2NjY2NjY2NiI+DQogICAgICAgICAgPExheW91dEFuY2hvcmFibGVQYW5lIEZsb2F0aW5nV2lkdGg9Ijc5NCIgRmxvYXRpbmdIZWlnaHQ9IjUwMCIgRmxvYXRpbmdMZWZ0PSI2MzIuNjY2NjY2NjY2NjY2NiIgRmxvYXRpbmdUb3A9IjgyNi42NjY2NjY2NjY2NjY2Ij4NCiAgICAgICAgICAgIDxMYXlvdXRBbmNob3JhYmxlIEF1dG9IaWRlTWluV2lkdGg9IjEwMCIgQXV0b0hpZGVNaW5IZWlnaHQ9IjEwMCIgVGl0bGU9IkxpdmUgUmVzdWx0cyAgMSIgSXNTZWxlY3RlZD0iVHJ1ZSIgQ29udGVudElkPSJLZXlzaWdodC5PcGVuVGFwLkxpdmVSZXN1bHRzUGFuZWwuTGl2ZVJlc3VsdHMiIEZsb2F0aW5nTGVmdD0iNjMyLjY2NjY2NjY2NjY2NjYiIEZsb2F0aW5nVG9wPSI4MjYuNjY2NjY2NjY2NjY2NiIgRmxvYXRpbmdXaWR0aD0iNzk0IiBGbG9hdGluZ0hlaWdodD0iNTAwIiBMYXN0QWN0aXZhdGlvblRpbWVTdGFtcD0iMTEvMTAvMjAyNSAxNDo1ODo1NyIgLz4NCiAgICAgICAgICA8L0xheW91dEFuY2hvcmFibGVQYW5lPg0KICAgICAgICA8L0xheW91dEFuY2hvcmFibGVQYW5lR3JvdXA+DQogICAgICAgIDxMYXlvdXRBbmNob3JhYmxlUGFuZSBJZD0iZjg3ZTE1M2MtNDllMC00N2QwLWIwMWEtNWNjZTk1YjA4MTg4IiBEb2NrV2lkdGg9IjEuMDU5NDgzMTM2ODM1OTU1OSoiIERvY2tNaW5XaWR0aD0iMTAwIiBEb2NrTWluSGVpZ2h0PSIxMDAiPg0KICAgICAgICAgIDxMYXlvdXRBbmNob3JhYmxlIEF1dG9IaWRlTWluV2lkdGg9IjEwMCIgQXV0b0hpZGVNaW5IZWlnaHQ9IjEwMCIgVGl0bGU9IkxpdmUgUmVzdWx0cyAyIiBJc1NlbGVjdGVkPSJUcnVlIiBDb250ZW50SWQ9IktleXNpZ2h0Lk9wZW5UYXAuTGl2ZVJlc3VsdHNQYW5lbC5MaXZlUmVzdWx0czIiIEZsb2F0aW5nTGVmdD0iMTI5MCIgRmxvYXRpbmdUb3A9IjgzMiIgRmxvYXRpbmdXaWR0aD0iNjI4LjY2NjY2NjY2NjY2NjYiIEZsb2F0aW5nSGVpZ2h0PSI0OTQiIExhc3RBY3RpdmF0aW9uVGltZVN0YW1wPSIxMS8xMS8yMDI1IDEwOjU0OjExIiAvPg0KICAgICAgICA8L0xheW91dEFuY2hvcmFibGVQYW5lPg0KICAgICAgICA8TGF5b3V0QW5jaG9yYWJsZVBhbmUgRG9ja1dpZHRoPSIwLjkzOTg2MjU0Mjk1NTMyNjEqIiBGbG9hdGluZ1dpZHRoPSI1MDAiIEZsb2F0aW5nSGVpZ2h0PSI1MDAiIEZsb2F0aW5nTGVmdD0iMTE1MCIgRmxvYXRpbmdUb3A9IjcxOC42NjY2NjY2NjY2NjY2Ij4NCiAgICAgICAgICA8TGF5b3V0QW5jaG9yYWJsZSBBdXRvSGlkZU1pbldpZHRoPSIxMDAiIEF1dG9IaWRlTWluSGVpZ2h0PSIxMDAiIFRpdGxlPSJMaXZlIFJlc3VsdHMgMyIgSXNTZWxlY3RlZD0iVHJ1ZSIgQ29udGVudElkPSJLZXlzaWdodC5PcGVuVGFwLkxpdmVSZXN1bHRzUGFuZWwuTGl2ZVJlc3VsdHMzIiBGbG9hdGluZ0xlZnQ9IjExNTAiIEZsb2F0aW5nVG9wPSI3MTguNjY2NjY2NjY2NjY2NiIgRmxvYXRpbmdXaWR0aD0iNTAwIiBGbG9hdGluZ0hlaWdodD0iNTAwIiBMYXN0QWN0aXZhdGlvblRpbWVTdGFtcD0iMTEvMTEvMjAyNSAxMDo1NDoyMiIgLz4NCiAgICAgICAgPC9MYXlvdXRBbmNob3JhYmxlUGFuZT4NCiAgICAgIDwvTGF5b3V0QW5jaG9yYWJsZVBhbmVHcm91cD4NCiAgICA8L0xheW91dFBhbmVsPg0KICAgIDxMYXlvdXRBbmNob3JhYmxlUGFuZUdyb3VwIE9yaWVudGF0aW9uPSJIb3Jpem9udGFsIiBEb2NrV2lkdGg9IjAuNDM0NTk5MTU2MTE4MTQzNSoiIEZsb2F0aW5nV2lkdGg9IjQwOCIgRmxvYXRpbmdIZWlnaHQ9IjcyOC42NjY2NjY2NjY2NjY2IiBGbG9hdGluZ0xlZnQ9IjE1MjYuNjY2NjY2NjY2NjY2NSIgRmxvYXRpbmdUb3A9IjU5My4zMzMzMzMzMzMzMzMzIj4NCiAgICAgIDxMYXlvdXRBbmNob3JhYmxlUGFuZSBEb2NrV2lkdGg9IjIuNyoiIERvY2tNaW5XaWR0aD0iMTAwIiBEb2NrTWluSGVpZ2h0PSIxMDAiIEZsb2F0aW5nV2lkdGg9IjQwOCIgRmxvYXRpbmdIZWlnaHQ9IjcyOC42NjY2NjY2NjY2NjY2IiBGbG9hdGluZ0xlZnQ9IjE1MjYuNjY2NjY2NjY2NjY2NSIgRmxvYXRpbmdUb3A9IjU5My4zMzMzMzMzMzMzMzMzIj4NCiAgICAgICAgPExheW91dEFuY2hvcmFibGUgQXV0b0hpZGVNaW5XaWR0aD0iMTAwIiBBdXRvSGlkZU1pbkhlaWdodD0iMTAwIiBUaXRsZT0iQmF0dGVyeSBEZW1vIiBJc1NlbGVjdGVkPSJUcnVlIiBDb250ZW50SWQ9Ik9wZW5UYXAuUGx1Z2lucy5EZW1vLkJhdHRlcnkuVUkuQmF0dGVyeURlbW9QYW5lbCIgVG9vbFRpcD0iVGhpcyBpcyBhIGRlbW9uc3RyYXRpb24gcGFuZWwgZm9yIHRoZSBiYXR0ZXJ5IHRlc3QuIiBGbG9hdGluZ0xlZnQ9IjE1MjYuNjY2NjY2NjY2NjY2NSIgRmxvYXRpbmdUb3A9IjU5My4zMzMzMzMzMzMzMzMzIiBGbG9hdGluZ1dpZHRoPSI0MDgiIEZsb2F0aW5nSGVpZ2h0PSI3MjguNjY2NjY2NjY2NjY2NiIgTGFzdEFjdGl2YXRpb25UaW1lU3RhbXA9IjExLzExLzIwMjUgMTA6NTM6MDQiIFByZXZpb3VzQ29udGFpbmVySWQ9IjM1NmI3NzI5LWRhOGItNDQ5MC04MmY5LTNhNTQwNWRiYjNiMiIgUHJldmlvdXNDb250YWluZXJJbmRleD0iMCIgLz4NCiAgICAgIDwvTGF5b3V0QW5jaG9yYWJsZVBhbmU+DQogICAgPC9MYXlvdXRBbmNob3JhYmxlUGFuZUdyb3VwPg0KICA8L1Jvb3RQYW5lbD4NCiAgPFRvcFNpZGUgLz4NCiAgPFJpZ2h0U2lkZSAvPg0KICA8TGVmdFNpZGUgLz4NCiAgPEJvdHRvbVNpZGUgLz4NCiAgPEZsb2F0aW5nV2luZG93cyAvPg0KICA8SGlkZGVuIC8+DQo8L0xheW91dFJvb3Q+
+
+
+
+
+
+ Keysight.OpenTap.LiveResultsPanel.LiveResultPanelSettings
+
+
+ Charts
+
+
+ false
+ false
+ false
+ false
+
+ 1000
+ true
+
+
+
+
+
+
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYScatter
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYScatter
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+
+
+ true
+ false
+ false
+ false
+ Charge
+ 1000
+ true
+
+
+
+
+
+
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYScatter
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+ Charge
+
+ Sample Number
+ Voltage
+
+ IA==
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYLine
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+ Sample Number
+ Voltage
+
+ IA==
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYLine
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+
+
+ true
+ false
+ false
+ false
+ Charge
+ 1000
+ false
+
+
+ Discharge
+
+ [Index]
+ Current
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYScatter
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+ Charge
+
+ Sample Number
+ Current
+
+
+ 10
+ false
+
+ Test Plan \ Name
+ Test Plan \ Station
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYLine
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+ Sample Number
+ Current
+
+
+ 10
+ false
+
+ Test Plan \ Name
+ Test Plan \ Station
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYLine
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+
+
+ true
+ false
+ false
+ false
+ Discharge
+ 1000
+ false
+
+
+ Discharge
+
+ Sample Number
+ Voltage
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYLine
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+ Sample Number
+ Voltage
+
+
+ 10
+ false
+
+ false
+ false
+
+ XYScatter
+ XYLine
+ Histogram
+ DataTable
+
+ XYLine
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+
+ None
+ 0
+
+
+
+
+ Split
+ 0
+ Test Step \ Temperature
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+ None
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenTap.Plugins.Demo.Battery.UI/Resources/batterydemo.png b/OpenTap.Plugins.Demo.Battery.UI/Resources/batterydemo.png
new file mode 100644
index 0000000..e9b23c0
Binary files /dev/null and b/OpenTap.Plugins.Demo.Battery.UI/Resources/batterydemo.png differ
diff --git a/ResultsAndTiming/DemoBaseStep.cs b/ResultsAndTiming/DemoBaseStep.cs
index 6d1c89a..c46c6da 100644
--- a/ResultsAndTiming/DemoBaseStep.cs
+++ b/ResultsAndTiming/DemoBaseStep.cs
@@ -3,7 +3,6 @@
// the sample application files (and/or any modified version) in any way
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/LimitCheckAndNoiseBaseStep.cs b/ResultsAndTiming/LimitCheckAndNoiseBaseStep.cs
index 7fb2c7d..b1f3ab9 100644
--- a/ResultsAndTiming/LimitCheckAndNoiseBaseStep.cs
+++ b/ResultsAndTiming/LimitCheckAndNoiseBaseStep.cs
@@ -3,7 +3,6 @@
// the sample application files (and/or any modified version) in any way
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/OpenTap.Plugins.Demo.ResultsAndTiming.csproj b/ResultsAndTiming/OpenTap.Plugins.Demo.ResultsAndTiming.csproj
index 7e40591..3d967ba 100644
--- a/ResultsAndTiming/OpenTap.Plugins.Demo.ResultsAndTiming.csproj
+++ b/ResultsAndTiming/OpenTap.Plugins.Demo.ResultsAndTiming.csproj
@@ -16,7 +16,7 @@
- true
+ false
diff --git a/ResultsAndTiming/Properties/AssemblyInfo.cs b/ResultsAndTiming/Properties/AssemblyInfo.cs
index a630e37..1b50976 100644
--- a/ResultsAndTiming/Properties/AssemblyInfo.cs
+++ b/ResultsAndTiming/Properties/AssemblyInfo.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
diff --git a/ResultsAndTiming/RampResultsStep.cs b/ResultsAndTiming/RampResultsStep.cs
index 5ffb8a1..9b504cb 100644
--- a/ResultsAndTiming/RampResultsStep.cs
+++ b/ResultsAndTiming/RampResultsStep.cs
@@ -4,7 +4,6 @@
// you find useful, provided that you agree that Keysight Technologies has no
// warranty, obligations or liability for any sample application files.
using System.Collections.Generic;
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/SineResultsStep.cs b/ResultsAndTiming/SineResultsStep.cs
index 6ef133b..31140ca 100644
--- a/ResultsAndTiming/SineResultsStep.cs
+++ b/ResultsAndTiming/SineResultsStep.cs
@@ -5,7 +5,6 @@
// warranty, obligations or liability for any sample application files.
using System;
using System.Collections.Generic;
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/Tests/DemonstrationAllSteps.TapPlan b/ResultsAndTiming/Tests/DemonstrationAllSteps.TapPlan
index 9587c5a..c8ddf3e 100644
--- a/ResultsAndTiming/Tests/DemonstrationAllSteps.TapPlan
+++ b/ResultsAndTiming/Tests/DemonstrationAllSteps.TapPlan
@@ -3,23 +3,17 @@
- 10
True
0.2
Charge
PSU
- 0.1
- 4.2
- 5
True
0.2
Discharge
PSU
- 0.8
- 2.2
diff --git a/ResultsAndTiming/Tests/Test Bench Profile/DUTs.xml b/ResultsAndTiming/Tests/Test Bench Profile/DUTs.xml
index 15f742d..c184460 100644
--- a/ResultsAndTiming/Tests/Test Bench Profile/DUTs.xml
+++ b/ResultsAndTiming/Tests/Test Bench Profile/DUTs.xml
@@ -8,6 +8,15 @@
TimeDut
0.1
+
+ 0.3
+ 3
+ 4.2
+ 0.01
+
+
+ Bat
+
diff --git a/ResultsAndTiming/TimeActivityStep.cs b/ResultsAndTiming/TimeActivityStep.cs
index e144d45..947b24e 100644
--- a/ResultsAndTiming/TimeActivityStep.cs
+++ b/ResultsAndTiming/TimeActivityStep.cs
@@ -5,7 +5,6 @@
// warranty, obligations or liability for any sample application files.
using System.Collections.Generic;
using System.Threading;
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/TimeDut.cs b/ResultsAndTiming/TimeDut.cs
index 6b23fe0..1365274 100644
--- a/ResultsAndTiming/TimeDut.cs
+++ b/ResultsAndTiming/TimeDut.cs
@@ -5,7 +5,6 @@
// warranty, obligations or liability for any sample application files.
using System.Diagnostics;
using System.Threading;
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/TimeInst.cs b/ResultsAndTiming/TimeInst.cs
index 600f409..ec399d0 100644
--- a/ResultsAndTiming/TimeInst.cs
+++ b/ResultsAndTiming/TimeInst.cs
@@ -5,7 +5,6 @@
// warranty, obligations or liability for any sample application files.
using System.Diagnostics;
using System.Threading;
-using OpenTap;
namespace OpenTap.Plugins.Demo.ResultsAndTiming
{
diff --git a/ResultsAndTiming/TimeResourceActivityStep.cs b/ResultsAndTiming/TimeResourceActivityStep.cs
index 859e79a..8f66e78 100644
--- a/ResultsAndTiming/TimeResourceActivityStep.cs
+++ b/ResultsAndTiming/TimeResourceActivityStep.cs
@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
-using OpenTap;
using OpenTap.Plugins.Demo.ResultsAndTiming;
diff --git a/ResultsAndTiming/package.xml b/package.xml
similarity index 95%
rename from ResultsAndTiming/package.xml
rename to package.xml
index aa3f9eb..9922290 100644
--- a/ResultsAndTiming/package.xml
+++ b/package.xml
@@ -7,9 +7,6 @@
Emulated PSU
-
-
-
Keysight Technologies, Inc.
https://github.com/opentap/Demonstration
@@ -21,6 +18,9 @@
+
+
+