Add ReadStableMassOp unit test and enhance MettlerToledo Scale support
- Introduced `ReadStableMassOpTest` to validate mass reading operations. - Refactored `Scale` to support `ISerialPort` for improved testability. - Added `SerialPortDevice` and `SerialPortDeviceFake` implementations, with `GetComponent` overload in `Factory`. - Enhanced `StateMachine` with `InitializeBoardEtc_Fake` for custom setups. - Updated project files to include new classes.
This commit is contained in:
parent
6311df49c3
commit
c7cba20de0
@ -14,6 +14,8 @@ namespace TBF.Rig.Scales.MettlerToledo
|
||||
public IComponent DummyComponent() { return new Scale(); }
|
||||
|
||||
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new Scale(cfg); }
|
||||
// wrapping to use in unit tests
|
||||
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components, ISerialPort serialPort) { return new Scale(cfg, serialPort); }
|
||||
|
||||
public IComponentCfg DefaultConfig() { return new ScaleCfg("WT", this); }
|
||||
|
||||
|
||||
14
TBF/Rig/Scales/MettlerToledo/ISerialPort.cs
Normal file
14
TBF/Rig/Scales/MettlerToledo/ISerialPort.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TBF.Rig.Scales.MettlerToledo
|
||||
{
|
||||
public interface ISerialPort
|
||||
{
|
||||
void Open();
|
||||
void Close();
|
||||
void Write(string text);
|
||||
string ReadExisting();
|
||||
bool IsOpen { get; }
|
||||
Handshake Handshake { get; set; }
|
||||
}
|
||||
}
|
||||
@ -50,7 +50,7 @@ namespace TBF.Rig.Scales.MettlerToledo
|
||||
///
|
||||
public IDrawingItem DrawingItem { get { return scaleCfg as IDrawingItem; } }
|
||||
|
||||
protected SerialPort serialPort;
|
||||
protected ISerialPort serialPort;
|
||||
protected StringBuilder stringBuilder;
|
||||
|
||||
/// <summary>The state of the mass measurement</summary>
|
||||
@ -93,6 +93,13 @@ namespace TBF.Rig.Scales.MettlerToledo
|
||||
{
|
||||
scaleCfg = cfg as ScaleCfg;
|
||||
}
|
||||
|
||||
public Scale(Generic.IComponentCfg cfg, ISerialPort serialPort)
|
||||
: base(cfg)
|
||||
{
|
||||
scaleCfg = cfg as ScaleCfg;
|
||||
this.serialPort = serialPort;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@ -111,7 +118,8 @@ namespace TBF.Rig.Scales.MettlerToledo
|
||||
if (scaleCfg.DebugLevel == DebugMode.Normal)
|
||||
{
|
||||
string comPortName = "COM" + scaleCfg.ComPortNr.ToString();
|
||||
serialPort = new SerialPort(comPortName, scaleCfg.BaudRate, scaleCfg.Parity, scaleCfg.DataBits, scaleCfg.StopBits);
|
||||
serialPort ??= new SerialPortDevice(comPortName, scaleCfg.BaudRate, scaleCfg.Parity, scaleCfg.DataBits,
|
||||
scaleCfg.StopBits);
|
||||
serialPort.Handshake = scaleCfg.Handshake;
|
||||
serialPort.Open();
|
||||
log.FatalFormat("{0} - Device successfully initialized", Name);
|
||||
|
||||
13
TBF/Rig/Scales/MettlerToledo/SerialPortDevice.cs
Normal file
13
TBF/Rig/Scales/MettlerToledo/SerialPortDevice.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TBF.Rig.Scales.MettlerToledo
|
||||
{
|
||||
public class SerialPortDevice : SerialPort, ISerialPort
|
||||
{
|
||||
public SerialPortDevice(string comPortName, int scaleCfgBaudRate, Parity scaleCfgParity, int scaleCfgDataBits, StopBits scaleCfgStopBits)
|
||||
: base(comPortName, scaleCfgBaudRate, scaleCfgParity, scaleCfgDataBits, scaleCfgStopBits)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
84
TBF/Rig/Scales/MettlerToledo/SerialPortDeviceFake.cs
Normal file
84
TBF/Rig/Scales/MettlerToledo/SerialPortDeviceFake.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System.IO.Ports;
|
||||
using log4net;
|
||||
using log4net.Core;
|
||||
using log4net.Repository.Hierarchy;
|
||||
using NHibernate;
|
||||
|
||||
namespace TBF.Rig.Scales.MettlerToledo
|
||||
{
|
||||
public class SerialPortDeviceFake : ISerialPort
|
||||
{
|
||||
// write dirrectly into cmd
|
||||
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private bool bIsOpen = false;
|
||||
public void Open()
|
||||
{
|
||||
log.Debug( "SerialPortDeviceFake.Open()");
|
||||
bIsOpen = true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
log.Debug( "SerialPortDeviceFake.Close()");
|
||||
bIsOpen = false;
|
||||
}
|
||||
|
||||
private string lastWrite = "";
|
||||
private string lastRead = "";
|
||||
|
||||
public string LastWrite { get => lastWrite; set => lastWrite = value;}
|
||||
public string LastRead { get => lastRead; set => lastRead = value;}
|
||||
|
||||
public void Write(string text)
|
||||
{
|
||||
log.DebugFormat("SerialPortDeviceFake.Write({0})", text);
|
||||
lastWrite = text;
|
||||
}
|
||||
|
||||
private string forcedText = null;
|
||||
public void ForceReturnAnswer(string text)
|
||||
{
|
||||
forcedText = text;
|
||||
}
|
||||
|
||||
public string ReadExisting()
|
||||
{
|
||||
string result = "", end = "\r\n";
|
||||
if (forcedText != null)
|
||||
{
|
||||
result = forcedText;
|
||||
forcedText = null;
|
||||
return result + end;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(lastWrite))
|
||||
{
|
||||
if (lastWrite.Contains("S"+end))
|
||||
{
|
||||
result = "S D 170.725 kg"; // correct stable scale mass answer
|
||||
}
|
||||
else if (lastWrite.Contains("R"))
|
||||
{
|
||||
result = "RD";
|
||||
}
|
||||
|
||||
//store result
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
result = result + end;
|
||||
lastRead = result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsOpen
|
||||
{
|
||||
get { return bIsOpen; }
|
||||
}
|
||||
|
||||
Handshake handshakeVal = Handshake.None;
|
||||
public Handshake Handshake { get => handshakeVal; set => handshakeVal = value; }
|
||||
}
|
||||
}
|
||||
@ -323,6 +323,22 @@ namespace TBF.Rig
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use InitializeBoardEtc() instead.")]
|
||||
public static void InitializeBoardEtc_Fake(IList<IComponent> componentList)
|
||||
{
|
||||
components = componentList;
|
||||
|
||||
// base find components - find control board!, ...
|
||||
foreach (var cmpnt in components)
|
||||
{
|
||||
if (cmpnt is ControlBoard.IControlBoard) ControlBoardMain = cmpnt as ControlBoard.IControlBoard;
|
||||
if (cmpnt is IBenchInfo) ProcessData.BenchInfo = cmpnt as IBenchInfo;
|
||||
if (cmpnt is IErrorFlags) ProcessData.ErrorFlagsComp = cmpnt as IErrorFlags;
|
||||
if (cmpnt is IStatisticsMonitoring)
|
||||
ProcessData.StatisticsMonitoringComp = cmpnt as IStatisticsMonitoring;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string CurrentlyInitializedComponentName;
|
||||
///
|
||||
|
||||
@ -1406,11 +1406,14 @@
|
||||
<Compile Include="Rig\RegisterReaders\StandingStartStop\Factory.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\StandingStartStop\RRProcParams.cs" />
|
||||
<Compile Include="Rig\RegValvePosition.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ISerialPort.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ScaleCfg.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\Scale.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\Factory.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\GetSerNumOp.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOp.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\SerialPortDevice.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\SerialPortDeviceFake.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\SetUnitsOp.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\TaringOp.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ZeroOp.cs" />
|
||||
|
||||
168
TBFTests/Rig/Scales/MettlerToledo/ReadStableMassOpTest.cs
Normal file
168
TBFTests/Rig/Scales/MettlerToledo/ReadStableMassOpTest.cs
Normal file
@ -0,0 +1,168 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Common;
|
||||
using Config.Entities;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using TBF.Boxes;
|
||||
using TBF.Rig;
|
||||
using TBF.Rig.BuiltIn.Valve;
|
||||
using TBF.Rig.ControlBoard.Uni;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.GenericDevices;
|
||||
using TBF.Rig.Scales.MettlerToledo;
|
||||
using Factory = TBF.Rig.Scales.MettlerToledo.Factory;
|
||||
|
||||
namespace TBFTests.Rig.Scales.MettlerToledo
|
||||
{
|
||||
[TestClass]
|
||||
[TestSubject(typeof(ReadStableMassOp))]
|
||||
public class ReadStableMassOpTest
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public void Run_ReadStableMassOp()
|
||||
{
|
||||
//
|
||||
var mockControlBoard = new Mock<TBF.Rig.ControlBoard.IControlBoard>();
|
||||
mockControlBoard.Setup(v => v.Name).Returns("UniCB");
|
||||
|
||||
IList<IComponent> componentsBoard = new List<IComponent>() { mockControlBoard.Object};
|
||||
StateMachine.InitializeBoardEtc_Fake(componentsBoard);
|
||||
|
||||
Factory factory = new Factory();
|
||||
ScaleCfg cfg = new ScaleCfg("Scale0", factory);
|
||||
cfg.Evaporation = "Evaporation0";
|
||||
//
|
||||
var mockValve = new Mock<IValve>();
|
||||
mockValve.Setup(v => v.Name).Returns(cfg.DrainValve);
|
||||
//
|
||||
var mockEvaporation = new Mock<IEvaporation>();
|
||||
mockEvaporation.Setup(v => v.Name).Returns(cfg.Evaporation);
|
||||
|
||||
|
||||
|
||||
//components
|
||||
IComponent valveObject = mockValve.Object;
|
||||
IComponent evaporationObject = mockEvaporation.Object;
|
||||
IList<IComponent> components = new List<IComponent>() { valveObject, mockControlBoard.Object, evaporationObject };
|
||||
StateMachine.InitializeBoardEtc_Fake(components);
|
||||
|
||||
ValveCfg valveCfg = new ValveCfg(new ValveFactory());
|
||||
var valveFactory = new ValveFactory();
|
||||
IComponent iComponent = valveFactory.GetComponent(valveCfg,components);
|
||||
|
||||
|
||||
SerialPortDeviceFake serialPort = new SerialPortDeviceFake();
|
||||
Scale scale = factory.GetComponent(cfg,null, serialPort) as Scale;
|
||||
scale.DebugLevel = DebugMode.Normal;
|
||||
|
||||
|
||||
|
||||
|
||||
scale.Initialize();
|
||||
|
||||
|
||||
|
||||
DoubleBox finalMass = new DoubleBox();
|
||||
ReadStableMassOp readStableMassOp = new ReadStableMassOp(scale, ref finalMass, 2, MassMethod.Scale, 4, 0.05);
|
||||
|
||||
//////////////////////////////////
|
||||
/// Start
|
||||
|
||||
readStableMassOp.Start();
|
||||
|
||||
/// Run
|
||||
bool stopLoop = false;
|
||||
while(!stopLoop)
|
||||
{
|
||||
Event run = readStableMassOp.Run();
|
||||
|
||||
if (run == Event.Abort || run == Event.ScaleDone)
|
||||
{
|
||||
stopLoop = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (StateMachine.Time >= 2 && StateMachine.Time < 3) // delay
|
||||
{
|
||||
//must be send command to stabilisate mass
|
||||
Assert.IsTrue(scale.Activity == Activity.RunningOperation);
|
||||
Assert.IsTrue(serialPort.IsOpen);
|
||||
Assert.IsTrue(serialPort.LastWrite.Contains("S\r\n"));
|
||||
Assert.IsTrue(string.IsNullOrEmpty(serialPort.LastRead));
|
||||
}
|
||||
|
||||
//time gap between measurements - 60 sec
|
||||
|
||||
if (StateMachine.Time == 61) //valid measurement
|
||||
{
|
||||
//FIRST measurement
|
||||
Assert.IsTrue(scale.Activity == Activity.RunningOperation);
|
||||
//try simulate immediate measurement
|
||||
scale.Activity = Activity.ImmediateMeasurement;
|
||||
scale.RunDeviceBefore();
|
||||
Assert.IsTrue(scale.Activity == Activity.Idle);
|
||||
Assert.IsTrue(scale.MsrmntState == MsrmntState.Valid);
|
||||
Assert.IsTrue(scale.Mass-0.1 < 170.725 && scale.Mass+0.1 > 170.724); //value from fake serial port
|
||||
}
|
||||
|
||||
if (StateMachine.Time == 62) //valid measurement
|
||||
{
|
||||
//SECOND measurement - forced answer
|
||||
scale.Activity = Activity.ImmediateMeasurement;
|
||||
serialPort.ForceReturnAnswer("S S 0.000 kg");
|
||||
scale.RunDeviceBefore();
|
||||
Assert.IsTrue(scale.Activity == Activity.Idle);
|
||||
Assert.IsTrue(scale.MsrmntState == MsrmntState.Valid);
|
||||
Assert.IsTrue(scale.Mass-0.1 < 0 && scale.Mass+0.1 > 0);
|
||||
}
|
||||
|
||||
////////// ERROR CASES
|
||||
if (StateMachine.Time >= 63 && StateMachine.Time < 65) // 2x invalid parameter S_I
|
||||
{
|
||||
//FAILED in customer measurement - forced answer
|
||||
scale.Activity = Activity.ImmediateMeasurement;
|
||||
serialPort.ForceReturnAnswer("S I"); //not ready scale
|
||||
scale.RunDeviceBefore();
|
||||
Assert.IsTrue(scale.MsrmntState == MsrmntState.Failed);
|
||||
Assert.IsTrue(scale.Activity == Activity.Idle);
|
||||
}
|
||||
// SET Correct answer
|
||||
if (StateMachine.Time >= 66 && StateMachine.Time < 70) //valid parametrs - IF 4 times valid measurement finish and calculate average final mass
|
||||
{
|
||||
//FAILED in customer measurement - forced answer
|
||||
scale.Activity = Activity.ImmediateMeasurement;
|
||||
serialPort.ForceReturnAnswer("S S 120.56 kg"); //not ready scale
|
||||
scale.RunDeviceBefore();
|
||||
Assert.IsTrue(scale.MsrmntState == MsrmntState.Valid);
|
||||
Assert.IsTrue(scale.Activity == Activity.Idle);
|
||||
}
|
||||
////////// ~ ERROR CASES
|
||||
|
||||
|
||||
if (StateMachine.Time >= 70)
|
||||
{
|
||||
Assert.Fail( "Timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep(10);
|
||||
StateMachine.Time += 1;
|
||||
}
|
||||
/// Stop
|
||||
readStableMassOp.Stop();
|
||||
|
||||
Assert.IsTrue(scale.Activity == Activity.Idle);
|
||||
Assert.IsTrue(scale.MsrmntState == MsrmntState.Valid);
|
||||
Assert.IsTrue(finalMass.Val-0.1 < 60.28 && finalMass.Val+0.1 > 60.28);
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,6 +106,7 @@
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />
|
||||
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
@ -124,6 +125,10 @@
|
||||
<Project>{9d0dcc88-dc81-47eb-9fdd-4c3907871bfb}</Project>
|
||||
<Name>Results</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SchematicDrawing\SchematicDrawing.csproj">
|
||||
<Project>{0f79ca69-9dbc-41f3-a6fc-5a2937365343}</Project>
|
||||
<Name>SchematicDrawing</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\TBF\TBF.csproj">
|
||||
<Project>{8648FD92-CDA1-4C3A-B5F9-FE547CE1FA48}</Project>
|
||||
<Name>TBF</Name>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user