This guide walks you through creating a custom file converter that imports test results into WATS. A file converter is a .NET class library that the WATS Client Service calls automatically for every new file that appears in a monitored folder.
Prerequisites
- WATS Client installed and registered to a WATS server
- .NET SDK 8 (or .NET Framework 4.8 for legacy deployments)
- Visual Studio 2022 or VS Code with C# Dev Kit
- An operation type (test process) configured in WATS → Control Panel → Processes
Step 1 — Create the project
Create a class library targeting net8.0 (or net48 for .NET Framework):
dotnet new classlib -n MyConverter --framework net8.0
cd MyConverter
Add the WATS NuGet package. Add the following to your .csproj:
<ItemGroup>
<!-- Compile-only reference — WATS Client provides the runtime DLL -->
<PackageReference Include="Virinco.WATS.ClientAPI" Version="7.*">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
The PrivateAssets=all attribute is important: it prevents the WATS API DLL from being copied into your output folder. The WATS Client Service already has its own copy and the two must not conflict.
Step 2 — Implement IReportConverter_v2
Your converter class must implement IReportConverter_v2 from the Virinco.WATS.Interface namespace:
using System;
using System.Collections.Generic;
using System.IO;
using Virinco.WATS.Interface;
namespace MyOrg.MyConverter
{
public class MyReportConverter : IReportConverter_v2
{
// Parameters exposed in the WATS Client configurator UI.
// Keys and default values are defined here.
public Dictionary<string, string> ConverterParameters { get; } = new()
{
["defaultOperationType"] = "Final Test",
["defaultRevision"] = "A",
};
// Called by the WATS Client Service for every matching file.
public Report? ImportReport(TDM api, Stream file)
{
// 1. Parse the file
using var reader = new StreamReader(file);
string content = reader.ReadToEnd();
// 2. Extract required fields from the file
string serialNumber = ExtractField(content, "Serial Number");
string partNumber = ExtractField(content, "Part Number");
bool passed = content.Contains("Result: Passed");
// 3. Create the UUT report — ALWAYS use api.CreateUUTReport(), never new UUTReport()
UUTReport uut = api.CreateUUTReport(
operatorName: "",
partNumber: partNumber,
revision: ConverterParameters["defaultRevision"],
serialNumber: serialNumber,
operationType: ConverterParameters["defaultOperationType"],
sequenceFileName: "",
sequenceFileVersion: ""
);
uut.StartDateTimeUTC = DateTime.UtcNow;
uut.Status = passed ? UUTStatusType.Passed : UUTStatusType.Failed;
// 4. Return the report — WATS Client submits it automatically
return uut;
}
// Called after the report is submitted. Clean up temp files here if needed.
public void CleanUp() { }
private static string ExtractField(string content, string key) =>
System.Text.RegularExpressions.Regex.Match(
content, $@"(?i)^{key}:\s*(.+)$", System.Text.RegularExpressions.RegexOptions.Multiline)
.Groups[1].Value.Trim();
}
}
Step 3 — Build test steps
Use the sequence API to record individual test measurements:
SequenceCall root = uut.GetRootSequenceCall();
// Pass/fail step — binary result only
PassFailStep visual = root.AddPassFailStep("Visual Inspection");
visual.Status = StepStatusType.Passed;
// Numeric measurement with limits
NumericLimitStep voltage = root.AddNumericLimitStep("3.3V Rail");
voltage.AddTest(
numericValue: 3.31,
compOperator: CompOperatorType.GELE, // ≥ low AND ≤ high
lowLimit: 3.25,
highLimit: 3.35,
units: "V",
status: StepStatusType.Passed
);
// String value — records a text result without comparison
StringValueStep fwVersion = root.AddStringValueStep("Firmware Version");
fwVersion.AddTest("v2.1.3");
fwVersion.Status = StepStatusType.Passed;
// Group steps into a sub-sequence
SequenceCall subSeq = root.AddSequenceCall("Power Supply Tests");
NumericLimitStep v5 = subSeq.AddNumericLimitStep("5V Rail");
v5.AddTest(5.02, CompOperatorType.GELE, 4.90, 5.10, "V", StepStatusType.Passed);
Step 4 — Build and deploy
Build in Release mode:
dotnet build -c Release
Copy the output DLL to the WATS Client converters folder:
%ProgramData%\Virinco\WATS\Converters\
Register the converter in %ProgramData%\Virinco\WATS\converters.xml. Add a <converter> entry inside the root element:
<converter assembly="MyConverter.dll" class="MyOrg.MyConverter.MyReportConverter">
<Source>
<UploadFolder>C:\WATS\Upload\MyConverter\</UploadFolder>
<FileFilter>*.txt</FileFilter>
<PostProcessAction>Move</PostProcessAction>
<PostProcessFolder>C:\WATS\Processed\MyConverter\</PostProcessFolder>
</Source>
<Destination>
<Argument key="defaultOperationType" value="Final Test"/>
</Destination>
</converter>
Restart the WATS Client Service to pick up the new converter:
net stop "WATS Client Service" && net start "WATS Client Service"
Step 5 — Test locally
Drop a sample file into the upload folder. The WATS Client Service will call your converter and submit the result. Verify it appears in WATS with the correct serial number, part number, and test steps.
For automated unit testing, see How to Test a WATS Converter.
Working examples
The following fully annotated examples are available in the TheWATSCompany GitHub repository:
- FileConverterExample_CSV — CSV file with header section and step table. Demonstrates dynamic column detection and multiple step types.
- FileConverterExample_XML — XML file parsed with LINQ to XML. Best starting point for structured formats.
- FileConverterExample_TXT — Line-oriented log file using the TextConverterBase helper class.
Common mistakes
| Wrong | Correct |
|---|---|
new UUTReport() | api.CreateUUTReport(...) |
step.SetNumericValue(5, "V") | step.AddTest(5, "V", status) |
step.SetLimits(4.9, 5.1) | step.AddTest(5, CompOperatorType.GELE, 4.9, 5.1, "V", status) |
uut.UUTResult = ... | uut.Status = ... |
| Include WATS DLL in output | Set PrivateAssets=all on the NuGet reference |
Comments
0 comments
Please sign in to leave a comment.