# OpenPnP.CSharp
**Repository Path**: flyingtoad/open-pn-p.-csharp
## Basic Information
- **Project Name**: OpenPnP.CSharp
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: GPL-3.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-08-05
- **Last Updated**: 2026-08-05
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# OpenPnP C# · Offline Simulation Workbench
[简体中文](README.md) | **English**
[](https://github.com/userqz1/OpenPnP.CSharp/releases)
[](https://dotnet.microsoft.com/)
[](#runtime-requirements)
[](#quality-gates)
[](#safety-boundary)
[](LICENSE.txt)
**An independent C#/.NET offline workbench for pick-and-place. It loads real OpenPnP configuration and job files, runs deterministic machine simulation and recorded-image vision, and presents the whole flow in a WPF desktop application.**
---
> [!WARNING]
> ### Hardware Locked
>
> This project contains **no** serial, TCP, USB, live-camera or device-discovery
> backend, and must **not** be used to connect to or control a physical machine.
> Passing the simulator is not physical-safety or placement-accuracy
> certification.
---
## What this is
Upstream [OpenPnP](https://github.com/openpnp/openpnp) is a Java/Swing pick-and-place application. This is an **independent C#/.NET reimplementation** scoped to the **offline** part of it:
- reads real `machine.xml`, `parts.xml`, `packages.xml`, `boards.xml`, `panels.xml`, `vision-settings.xml` and `.job.xml`;
- runs a complete pick-and-place job through a **deterministic simulator**, touching no hardware;
- runs vision pipelines over **recorded images**, opening no camera;
- presents Job, Vision, Simulator and Diagnostics workspaces in a WPF desktop app.
It is not a replacement for OpenPnP. It is an **offline verification sandbox**: run a configuration and a job end to end with no machine, no consumables and no risk.
### Why determinism matters
The same input produces **byte-identical** output on any two runs. The simulation reads no wall clock, uses no random numbers and does not depend on thread scheduling — time is a logical tick and randomness is a fixed seed.
That is the point rather than an implementation detail: **the output can serve as a regression baseline**. Any behavioural change is caught by a byte comparison instead of being hidden behind "looks about right".
---
## Features
| Capability | Notes |
|---|---|
| **Configuration loading** | Strict parsing of the OpenPnP configuration family; unknown elements/attributes are rejected with a named error code, never silently dropped |
| **Byte-exact round trip** | Read and write back reproduces the source document byte for byte (3-space indent, LF, 5-character escape set) |
| **Deterministic simulation** | Logical clock plus fixed seed; two independent processes agree byte for byte |
| **Pick-and-place jobs** | Full job processor: panels, nested references, skip and failure handling |
| **Feeder runtime** | One-shot FeedTickets, three-level nested retry (part / feed / pick), multi-nozzle batching |
| **Recorded-image vision** | Finite stage pipeline: template match, circular detection, bottom vision, fiducial location |
| **G-code rendering** | Full command stream rendered and transcribed over an in-memory scripted transport — **no port is opened** |
| **Connection lifecycle** | Connect → Handshake → ReadPosition → Enable → Home, each explicitly authorized |
| **Configuration administration** | Add, edit and remove parts, packages and vision settings, plus save-a-copy |
| **Importers** | Headless placement text import (KiCad / Altium style coordinate files) |
| **Diagnostics export** | Deterministic, non-overwriting run evidence |
---
## Screenshots
### Job workspace

### Simulator workspace

### Vision workspace

### Diagnostics workspace
> The permanent `OFFLINE ONLY` badge and `EMERGENCY STOP` button are not decoration: the first is enforced by the composition root, and the second is independent of the UI and the scheduler.
---
## Runtime requirements
### Running the application
| Item | Requirement |
|---|---|
| OS | Windows 10 1809+ / Windows 11, **x64** |
| Runtime | [.NET 10 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/10.0) |
| Display | 1366×768 or above; verified at 1366/1920 and 100%/125%/150% DPI |
| Hardware | **None.** No machine, serial device or camera is needed |
The release package is framework-dependent and does not bundle the .NET runtime.
### Building from source
| Item | Version |
|---|---|
| .NET SDK | 10.0.302 (pinned by `global.json`) |
| OS | Windows (required by WPF and the `net10.0-windows` target) |
| Native dependency | OpenCvSharp (win-x64 / win-x86 natives included) |
> Non-Windows platforms can build and test everything except `OpenPnP.Desktop.Wpf`.
---
## Quick start
### Option 1 — download the preview package
1. Download the latest package from [Releases](https://github.com/userqz1/OpenPnP.CSharp/releases);
2. extract it anywhere;
3. run `OpenPnP.Desktop.Wpf.exe`;
4. click **Open** and pick a `.job.xml` — sample projects ship in `test-data/`.
### Option 2 — run from source
```bash
git clone https://github.com/userqz1/OpenPnP.CSharp.git
cd OpenPnP.CSharp
```
```bash
pwsh tools/Build.ps1
```
```bash
pwsh tools/Test.ps1
```
```bash
pwsh tools/Check-NoHardware.ps1
```
Release build (zero warnings is the pass condition), full test run, and the offline gate that scans the sources for forbidden device APIs.
### Option 3 — headless smoke
```bash
dotnet run --project tools/OpenPnP.ProductSmoke -c Release -- verify product/fixtures result.json
```
Loads three built-in fixtures, runs complete jobs and writes deterministic evidence JSON, touching no hardware.
---
## Architecture
Dependencies point one way. **WPF never appears in Domain or Application**, and architecture tests enforce it:
```
┌──────────────────────────────────────────────────────────────┐
│ Desktop.Wpf MVVM shell, no business logic │
│ net10.0-windows │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ Application.Offline offline workbench service │
│ load → job → vision → diagnostics │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ Application hardware-neutral contracts │
│ MachineSessionStateMachine │
│ MachineCommandScheduler (one FIFO) │
│ DeterministicPnpJobProcessor │
└──────┬─────────────────┬──────────────────┬──────────────────┘
│ │ │
┌──────▼───────┐ ┌───────▼────────┐ ┌──────▼─────────┐
│ Hardware. │ │ Hardware. │ │ Vision │
│ Simulator │ │ Gcode │ │ finite stages │
│ deterministic│ │ IGcodeTransport│ │ Vision. │
│ models │ │ in-memory only │ │ OpenCvSharp │
└──────┬───────┘ └───────┬────────┘ └──────┬─────────┘
│ │ │
┌──────▼─────────────────▼──────────────────▼──────────────────┐
│ Persistence.OpenPnpXml hand-written strict readers and │
│ byte-exact Simple-XML writers │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ Domain Length / LengthUnit / Location │
│ Package / Part / Footprint │
│ Job / Board / Placement │
│ JavaDouble / JavaTextFormat │
└──────────────────────────────────────────────────────────────┘
```
### Design constraints
1. **Java numeric semantics.** `JavaDouble` and `JavaTextFormat` reproduce Java's double and formatting behaviour, including where half-way rounding differs. All numeric formatting goes through them; `double.ToString` at a call site is forbidden.
2. **Explicit invariance.** `InvariantGlobalization` is deliberately **not** set, so every format and parse site must pass `CultureInfo.InvariantCulture` explicitly, mirroring Java's explicit `Locale.US`.
3. **Quantities keep their units.** `Length` and `Location` never degrade to bare numbers.
4. **One scheduler.** Every machine command goes through the same FIFO scheduler and state machine; there is no bypass.
5. **The connection lifecycle is explicit authorization.** `MachineSessionStateMachine` performs no I/O and holds no transport, timer or clock. A caller authorizes a transition, does its own I/O, then reports the outcome. A refusal throws *before* returning, so a refused operation cannot have written a byte.
6. **Byte-exact XML.** 3-space indent, LF, no declaration, the 5-character escape set (`& < > " '`), `@Version`-driven strict/lenient behaviour. Unknown data is rejected with a named error code rather than silently dropped.
---
## Safety boundary
This is a hardware-locked product, and the boundary is held by structure rather than by convention:
| Boundary | How it is held |
|---|---|
| No serial / TCP / USB | No `SerialPort`, `TcpClient` or `Socket` exists in the sources; `Check-NoHardware.ps1` gates it |
| No live camera | No `VideoCapture` or device enumeration; vision consumes recorded images only |
| No device discovery | The composition root offers no endpoint selector |
| G-code is rendered only | The single `IGcodeTransport` implementation is an in-memory scripted transport |
| Emergency stop is independent | It does not pass through the UI or the scheduler |
| Evidence is auditable | Every run records `hardwareTouched=false` |
> **Passing this simulator is not physical-safety approval.** Axis direction, homing, limits, acceleration, IO mapping, collision and recovery logic all require human review before any real machine is involved.
---
## Quality gates
| Gate | Current |
|---|---|
| .NET suite | **1170 / 1170** |
| Java characterization (upstream oracle) | **617 / 617** |
| Release build | **0 warnings, 0 errors** (`TreatWarningsAsErrors`) |
| Forbidden device API scan | **0 hits** |
| Standalone repository suite | **1158 / 1158** |
| Default numeric tolerance | **0** (exact) |
### Migration method
The port proceeds one verifiable slice at a time, and every slice produces evidence against Java behaviour rather than an impression that it works.
- **Golden Master baselines** record raw and normalized output, a SHA-256 manifest and comparison results;
- **Two-process repeatability** is required — two runs in one process can agree by accident and hide ordering defects;
- **Baselines are immutable** — a behavioural change creates a new version with a recorded supersede reason;
- **Zero tolerance by default** — any nonzero tolerance needs an approved per-field policy.
---
## Relationship to upstream OpenPnP
- The Java implementation is the **behavioural oracle** and is never modified to make C# pass;
- ported areas are backed by Java characterization tests and byte-exact baselines;
- **not ported**, explicitly: real serial/TCP transports, live cameras, feeder execution, the Swing UI, and the full scripting surface;
- this project does not claim feature parity with upstream, and does not claim to replace it for controlling a real machine.
---
## FAQ
**Can I control my machine with this?**
No, by design. No hardware backend exists in the product, and a source scan gate prevents one from being introduced.
**Why is the package large?**
It carries the win-x64 and win-x86 OpenCvSharp natives. The managed assemblies are small.
**Linux / macOS?**
The core libraries target `net10.0` and build and test anywhere. The WPF shell is Windows-only.
**Will it load my existing OpenPnP configuration?**
Yes — that is the point. The parser is strict: an element or attribute it does not know is a named error rather than a silent skip, which is what keeps the round trip lossless.
**How far can I trust the simulation?**
As far as "it deterministically reproduces characterized behaviour". It does not model physical error, mechanical backlash or process variation, and it is not a placement-accuracy predictor.
---
## License
[GPL-3.0](LICENSE.txt), matching upstream OpenPnP.
Upstream is copyright the [OpenPnP contributors](https://github.com/openpnp/openpnp); see [SOURCE-NOTICE.md](SOURCE-NOTICE.md) for what this repository reimplements.