# 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# · 离线仿真工作台
**简体中文** | [English](README.en.md)
[](https://github.com/userqz1/OpenPnP.CSharp/releases)
[](https://dotnet.microsoft.com/)
[](#运行环境)
[](#质量门禁)
[](#安全边界)
[](LICENSE.txt)
**独立的 C#/.NET 贴片机离线工作台。加载真实 OpenPnP 配置与工程文件,运行确定性机器仿真与录制图像视觉,在 WPF 桌面应用中完整呈现取放流程。**
---
> [!WARNING]
> ### 硬件锁定(Hardware Locked)
>
> 本项目**不包含**串口、TCP、USB、实时相机或设备发现的任何后端实现,**不得**用于连接或控制真实贴片机。
> 仿真通过并不构成物理安全认证或贴装精度认证。
---
## 目录
- [这是什么](#这是什么)
- [功能特性](#功能特性)
- [界面预览](#界面预览)
- [运行环境](#运行环境)
- [快速开始](#快速开始)
- [系统架构](#系统架构)
- [安全边界](#安全边界)
- [质量门禁](#质量门禁)
- [与上游 OpenPnP 的关系](#与上游-openpnp-的关系)
- [常见问题](#常见问题)
- [许可证](#许可证)
---
## 这是什么
上游 [OpenPnP](https://github.com/openpnp/openpnp) 是一套 Java/Swing 的开源贴片机软件。本项目是它的**独立 C#/.NET 重实现**,范围限定在**离线部分**:
- 读取真实的 `machine.xml`、`parts.xml`、`packages.xml`、`boards.xml`、`panels.xml`、`vision-settings.xml` 与 `.job.xml`;
- 用**确定性仿真器**执行完整取放工程,不接触任何硬件;
- 用**录制图像**运行视觉流水线,不打开相机;
- 在 WPF 桌面应用中呈现工程、视觉、仿真与诊断四个工作区。
它的定位不是「OpenPnP 的替代品」,而是一个**可离线验证的工程沙箱**:在没有机器、没有耗材、没有风险的前提下,把一套配置和工程完整跑一遍。
### 为什么强调「确定性」
同一份输入,任意两次运行产生**逐字节相同**的输出。仿真不读墙钟、不用随机数、不依赖线程调度顺序——时间是逻辑滴答,随机是固定种子。
这不是实现细节,而是本项目的核心价值:**输出可以直接作为回归基线**。任何一次行为变化都会被字节比对发现,而不是被「看起来差不多」掩盖。
---
## 功能特性
| 能力 | 说明 |
|---|---|
| **配置加载** | 严格模式解析 OpenPnP 配置族;未知元素/属性以具名错误码拒绝,不静默丢弃 |
| **字节级往返** | 读取后写回与原文档逐字节一致(3 空格缩进、LF、5 字符转义集) |
| **确定性仿真** | 逻辑时钟 + 固定种子;双进程输出字节一致 |
| **取放作业** | 完整 Job Processor:面板、拼板、嵌套引用、跳过与失败处理 |
| **供料器运行时** | FeedTicket 一次性票据、三级嵌套重试(part/feed/pick)、多喷嘴批处理 |
| **录制图像视觉** | 有限阶段流水线:模板匹配、圆形检测、底部视觉、基准点定位 |
| **G-code 渲染** | 完整命令流渲染与转录,走内存脚本传输,**不打开串口** |
| **连接生命周期** | Connect → Handshake → ReadPosition → Enable → Home 六态显式授权 |
| **配置管理** | 元件、封装、视觉设置的增删改与「另存副本」 |
| **导入器** | 无头文本贴装导入(KiCad/Altium 风格坐标文件) |
| **诊断导出** | 确定性、非覆盖式的运行证据导出 |
---
## 界面预览
### 工程工作区 · Job
面板/拼板树、板面布局、贴装列表与实时进度。

### 仿真工作区 · Simulator
记录的机器轨迹回放、时间线审计与供料器库存。

### 视觉工作区 · Vision
录制图像上的流水线阶段与结果叠加。

### 诊断工作区 · Diagnostics
机器命令、作业事件与安全门状态。
> 右上角常驻的 `OFFLINE ONLY` 徽标与 `EMERGENCY STOP` 按钮不是装饰:前者由组合根强制,后者独立于 UI 与调度器。
---
## 运行环境
### 运行应用
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows 10 1809+/Windows 11,**x64** |
| 运行时 | [.NET 10 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/10.0) |
| 显示 | 建议 1366×768 以上;已在 1366/1920 与 100%/125%/150% DPI 下验证 |
| 硬件 | **无需**任何贴片机、串口设备或相机 |
发布包为**框架依赖**(framework-dependent),不含 .NET 运行时,需先安装上表的 Desktop Runtime。
### 从源码构建
| 项目 | 版本 |
|---|---|
| .NET SDK | 10.0.302(由 `global.json` 锁定) |
| 操作系统 | Windows(WPF 与 `net10.0-windows` 目标框架所需) |
| 原生依赖 | OpenCvSharp(随包提供 win-x64/win-x86 原生库) |
> 非 Windows 平台可以构建并测试除 `OpenPnP.Desktop.Wpf` 之外的所有项目,但无法构建 WPF 桌面壳。
---
## 快速开始
### 方式一:下载预览包(推荐)
1. 从 [Releases](https://github.com/userqz1/OpenPnP.CSharp/releases) 下载最新预览包;
2. 解压到任意目录;
3. 运行 `OpenPnP.Desktop.Wpf.exe`;
4. 点击 **Open**,选择一个 `.job.xml`(仓库内 `test-data/` 提供了可直接使用的样例工程)。
### 方式二:从源码运行
```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 构建(零警告即为通过)、全量测试、离线安全门(扫描源码中是否出现被禁的设备 API)。
### 方式三:无界面冒烟验证
```bash
dotnet run --project tools/OpenPnP.ProductSmoke -c Release -- verify product/fixtures result.json
```
该命令加载三套内置夹具、跑完整工程、输出确定性证据 JSON,全程不触碰硬件。
---
## 系统架构
### 分层与依赖方向
依赖方向单向向下,**WPF 绝不出现在 Domain 或 Application 层**,由架构测试强制:
```
┌──────────────────────────────────────────────────────────────┐
│ Desktop.Wpf MVVM 桌面壳,无业务逻辑 │
│ net10.0-windows │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ Application.Offline 离线工作台服务 │
│ 加载 → 作业 → 视觉 → 诊断 编排 │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ Application 硬件中立契约 │
│ MachineSessionStateMachine │
│ MachineCommandScheduler(单一 FIFO)│
│ DeterministicPnpJobProcessor │
└──────┬─────────────────┬──────────────────┬──────────────────┘
│ │ │
┌──────▼──────┐ ┌───────▼────────┐ ┌──────▼─────────┐
│ Hardware. │ │ Hardware. │ │ Vision │
│ Simulator │ │ Gcode │ │ 有限阶段流水线 │
│ 确定性模型 │ │ IGcodeTransport│ │ Vision. │
│ │ │ 仅内存脚本传输 │ │ OpenCvSharp │
└──────┬──────┘ └───────┬────────┘ └──────┬─────────┘
│ │ │
┌──────▼─────────────────▼──────────────────▼──────────────────┐
│ Persistence.OpenPnpXml 手写严格读写器 │
│ 字节级复刻 Simple-XML 输出 │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ Domain Length/LengthUnit/Location │
│ Package/Part/Footprint │
│ Job/Board/Placement │
│ JavaDouble/JavaTextFormat │
└──────────────────────────────────────────────────────────────┘
```
### 关键设计约束
**1. 数值语义与 Java 一致**
`JavaDouble` 与 `JavaTextFormat` 复刻 Java 的 double 与格式化语义(含半值进位方向的差异)。所有数值格式化必须走它们,禁止在调用点手写 `double.ToString`。
**2. 显式文化不变**
`InvariantGlobalization` **故意不启用**。每个格式化/解析调用点都必须显式传 `CultureInfo.InvariantCulture`,对应 Java 侧显式的 `Locale.US`。
**3. 物理量保留单位**
`Length` 与 `Location` 始终携带单位,禁止退化为裸数字。
**4. 单一命令调度器**
所有机器命令经由同一个 FIFO 调度器与状态机,不存在旁路。
**5. 连接生命周期是显式授权**
`MachineSessionStateMachine` 不做任何 I/O,也不持有传输、计时器或时钟。调用方先申请授权,再自行 I/O,最后回报结果。授权被拒绝时抛出发生在返回**之前**——因此被拒绝的操作不可能已经写过一个字节。
**6. XML 写出字节级复刻**
3 空格缩进、LF 换行、无 XML 声明、5 字符转义集(`& < > " '`)、`@Version` 驱动的严格/宽松行为。未知数据以具名错误码拒绝,而非静默丢弃。
---
## 安全边界
这是一个**硬件锁定**产品,边界由代码结构而非文档约定保证:
| 边界 | 保证方式 |
|---|---|
| 无串口/TCP/USB | 源码中不存在 `SerialPort`、`TcpClient`、`Socket` 等 API;由 `Check-NoHardware.ps1` 扫描门禁 |
| 无实时相机 | 不存在 `VideoCapture` 或设备枚举;视觉仅消费录制图像 |
| 无设备发现 | 组合根不提供任何端点选择器 |
| G-code 仅渲染 | `IGcodeTransport` 唯一实现是内存脚本传输 |
| 急停独立 | 急停不经过 UI 与调度器 |
| 证据可审计 | 每份运行证据记录 `hardwareTouched=false` |
> **通过本项目的仿真不等于通过物理安全验证。** 轴向、归零、限位、加速度、IO 映射、碰撞与恢复逻辑均需人工评审后才能用于真实机器。
---
## 质量门禁
| 门禁 | 当前状态 |
|---|---|
| .NET 全量测试 | **1170/1170** |
| Java 特征化测试(上游行为基准) | **617/617** |
| Release 构建 | **0 警告 0 错误**(`TreatWarningsAsErrors`) |
| 被禁设备 API 扫描 | **0 命中** |
| 独立仓库测试 | **1158/1158** |
| 默认数值容差 | **0**(精确比对) |
### 迁移方法学
本项目采用**逐切片可验证迁移**:每个切片都必须以 Java 行为为准绳产出证据,而非「看起来对了」。
- **Golden Master 基线**:每个切片记录 raw/normalized 双层输出、SHA-256 清单与比对结果;
- **双进程可重复性**:必须跨两个独立进程验证——同进程两次运行可能因偶然一致而掩盖顺序缺陷;
- **不可变基线**:已审核的基线永不修改,行为变化只能新建版本并记录 supersede 理由;
- **零容差默认**:任何非零容差都需要经批准的逐字段策略。
---
## 与上游 OpenPnP 的关系
- 上游 Java 实现是**行为基准(oracle)**,本项目从不修改它来迁就 C#;
- 已迁移的部分以 Java 特征化测试与字节级基线证明等价;
- **未迁移**的范围明确包括:真实串口/TCP 传输、实时相机、供料器实际执行、Swing UI、脚本引擎的完整表面;
- 本项目不声称覆盖上游全部功能,也不声称可以替代上游控制真实机器。
---
## 常见问题
**Q:可以用它控制我的贴片机吗?**
不能,且这是设计使然。产品中不存在任何硬件后端,源码扫描门禁会阻止它们被引入。
**Q:为什么发布包这么大?**
包含 OpenCvSharp 的 win-x64 与 win-x86 原生库。托管程序集本身很小。
**Q:支持 Linux/macOS 吗?**
核心库(Domain、Persistence、Application、Vision、Hardware.\*)是跨平台的 `net10.0`,可在任意平台构建与测试;WPF 桌面壳仅限 Windows。
**Q:能加载我现有的 OpenPnP 配置吗?**
可以,这正是设计目标。解析器是严格模式:遇到不认识的元素或属性会以具名错误码报错,而不是静默忽略——这是为了保证往返不丢数据。
**Q:仿真结果可以信任到什么程度?**
可以信任它**确定性地复现了被特征化的行为**。它没有建模真实机器的物理误差、机械间隙与工艺变量,也不是贴装精度的预测工具。
---
## 许可证
[GPL-3.0](LICENSE.txt),与上游 OpenPnP 保持一致。
上游项目版权归 [OpenPnP 贡献者](https://github.com/openpnp/openpnp) 所有;本仓库重实现部分的说明见 [SOURCE-NOTICE.md](SOURCE-NOTICE.md)。