https://github.com/tofupilot/csharp
Upload hardware test data to TofuPilot from C#.
https://github.com/tofupilot/csharp
csharp dotnet functional hardware testing tofupilot
Last synced: 3 months ago
JSON representation
Upload hardware test data to TofuPilot from C#.
- Host: GitHub
- URL: https://github.com/tofupilot/csharp
- Owner: tofupilot
- Created: 2026-03-31T12:38:35.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-02T11:36:34.000Z (3 months ago)
- Last Synced: 2026-04-03T22:33:55.562Z (3 months ago)
- Topics: csharp, dotnet, functional, hardware, testing, tofupilot
- Language: C#
- Homepage: https://tofupilot.com
- Size: 242 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# TofuPilot C# Client
[](https://opensource.org/licenses/MIT)
[](https://dotnet.microsoft.com/)
[](https://github.com/tofupilot/csharp)
The official C# client for [TofuPilot](https://tofupilot.com). Integrate your hardware test runs into one app with just a few lines of C#.
## Installation
Add a project reference:
```bash
dotnet add reference path/to/TofuPilot.csproj
```
## Quick Start
```csharp
using TofuPilot;
using TofuPilot.Models.Requests;
var client = new TofuPilot(apiKey: Environment.GetEnvironmentVariable("TOFUPILOT_API_KEY")!);
var run = await client.Runs.CreateAsync(new RunCreateRequest
{
ProcedureId = "your-procedure-id",
SerialNumber = "SN001",
PartNumber = "PN001",
Outcome = RunCreateOutcome.Pass,
StartedAt = DateTime.UtcNow.AddMinutes(-5),
EndedAt = DateTime.UtcNow,
});
Console.WriteLine($"Run created: {run.Id}");
```
All async methods support `CancellationToken`:
```csharp
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var run = await client.Runs.CreateAsync(request, cts.Token);
```
## Authentication
Set your API key as an environment variable:
```bash
export TOFUPILOT_API_KEY="your-api-key"
```
Or pass it directly:
```csharp
var client = new TofuPilot(apiKey: "your-api-key");
```
To point to a different server (e.g. self-hosted):
```csharp
var client = new TofuPilot(
apiKey: "your-api-key",
serverUrl: "https://your-instance.com/api"
);
```
### Custom certificates
```csharp
using System.Security.Cryptography.X509Certificates;
using TofuPilot.Utils;
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(new X509Certificate2("client.pfx", "password"));
var client = new TofuPilot(
apiKey: "your-api-key",
client: new TofuPilotHttpClient(handler)
);
```
## Available Resources
| Resource | Operations |
|----------|-----------|
| `client.Runs` | List, Create, Get, Delete, Update |
| `client.Units` | List, Create, Get, Delete, Update, AddChild, RemoveChild |
| `client.Parts` | List, Create, Get, Delete, Update |
| `client.Parts.Revisions` | Create, Get, Delete, Update |
| `client.Procedures` | List, Create, Get, Delete, Update |
| `client.Procedures.Versions` | Create, Get, Delete |
| `client.Batches` | List, Create, Get, Delete, Update |
| `client.Stations` | List, Create, Get, GetCurrent, Remove, Update |
| `client.Attachments` | Initialize, Finalize, Delete |
| `client.User` | List |
## Usage Examples
### Create a run with measurements
```csharp
var run = await client.Runs.CreateAsync(new RunCreateRequest
{
ProcedureId = procedureId,
SerialNumber = "SN-001",
PartNumber = "PCB-V1",
Outcome = RunCreateOutcome.Pass,
StartedAt = DateTime.UtcNow.AddMinutes(-5),
EndedAt = DateTime.UtcNow,
Phases = new List
{
new()
{
Name = "Voltage Test",
Outcome = RunCreatePhasesOutcome.Pass,
StartedAt = DateTime.UtcNow.AddMinutes(-5),
EndedAt = DateTime.UtcNow,
Measurements = new List
{
new()
{
Name = "Output Voltage",
Outcome = RunCreateMeasurementsOutcome.Pass,
MeasuredValue = 3.3,
Units = new RunCreateUnits { Value = "V" },
Validators = new List
{
new() { Operator = ">=", ExpectedValue = RunCreateExpectedValue.CreateNumber(3.0) },
new() { Operator = "<=", ExpectedValue = RunCreateExpectedValue.CreateNumber(3.6) },
},
},
},
},
},
});
```
### List and filter runs
```csharp
var result = await client.Runs.ListAsync(
partNumbers: new List { "PCB-V1" },
outcomes: new List { RunListQueryParamOutcome.Pass },
limit: 10
);
foreach (var run in result.Data)
Console.WriteLine($"{run.Id} — {run.Unit.SerialNumber}");
```
### Manage units and sub-units
```csharp
// Create part and revision
await client.Parts.CreateAsync(new PartCreateRequest { Number = "PCB-V1", Name = "Main Board" });
await client.Parts.Revisions.CreateAsync("PCB-V1", new PartCreateRevisionRequestBody { Number = "REV-A" });
// Create units
await client.Units.CreateAsync(new UnitCreateRequest
{
SerialNumber = "PARENT-001",
PartNumber = "PCB-V1",
RevisionNumber = "REV-A",
});
await client.Units.CreateAsync(new UnitCreateRequest
{
SerialNumber = "CHILD-001",
PartNumber = "PCB-V1",
RevisionNumber = "REV-A",
});
// Link parent-child
await client.Units.AddChildAsync("PARENT-001", new UnitAddChildRequestBody
{
ChildSerialNumber = "CHILD-001",
});
```
### Upload and download attachments
```csharp
// Upload a file (one line)
var attachmentId = await client.Attachments.UploadAsync("report.pdf");
// Link to a run
await client.Runs.UpdateAsync(runId, new RunUpdateRequestBody
{
Attachments = new List { attachmentId },
});
// Download an attachment
await client.Attachments.DownloadAsync(downloadUrl, "local-copy.pdf");
```
## Error Handling
API errors throw typed exceptions:
```csharp
using TofuPilot.Models.Errors;
try
{
await client.Runs.GetAsync("nonexistent-id");
}
catch (NotFoundException ex)
{
Console.WriteLine($"Not found: {ex.Message}");
}
catch (BadRequestException ex)
{
Console.WriteLine($"Bad request: {ex.Message}");
}
catch (ApiException ex)
{
Console.WriteLine($"API error {ex.StatusCode}: {ex.Body}");
}
```
| Exception | Status Code |
|-----------|------------|
| `BadRequestException` | 400 |
| `UnauthorizedException` | 401 |
| `ForbiddenException` | 403 |
| `NotFoundException` | 404 |
| `ConflictException` | 409 |
| `UnprocessableContentException` | 422 |
| `InternalServerErrorException` | 500 |
| `ApiException` | Any other |
## Running Tests
```bash
cd clients/csharp/tests
# Create .env.local with your API key and URL:
# TOFUPILOT_URL=http://localhost:3000
# TOFUPILOT_API_KEY_USER=your-api-key
dotnet test
```
## Documentation
- [Getting Started](https://tofupilot.com/docs/dashboard)
- [API Reference](https://tofupilot.com/docs/dashboard/api/v2)
- [Changelog](https://tofupilot.com/changelog)
## Acknowledgments
This package builds on the original C# client created by [@Hylaean](https://github.com/Hylaean) (versions 1.x).
## License
MIT