Compare commits

..

No commits in common. "3312a57757ccfb8136590cd1432e666813ca5ee9" and "f3f895df829ede423bce2505a2da35ba973886e6" have entirely different histories.

81 changed files with 1412 additions and 1860 deletions

View file

@ -1,8 +1,8 @@
# NcProgramManager # FanucProgramManager
## What This Is ## What This Is
Console EXE (C# .NET 4.7.2) for upload/download of CNC programs to/from machine controllers. Supports Fanuc FOCAS, Heidenhain LSV2, Siemens Sinumerik FTP, and Mitsubishi FTP. Runs on Windows, distributed to licensed machine operators and integrators. Console EXE (C# .NET 4.7.2) for upload/download of CNC programs to/from machine controllers. Targets any CN manufacturer, not Fanuc-only. Runs on Windows, distributed to licensed machine operators and integrators.
## Core Value ## Core Value
@ -13,9 +13,9 @@ Machinists and operators transfer CNC programs to/from any controller without ma
| Attribute | Value | | Attribute | Value |
|-----------|-------| |-----------|-------|
| Type | Application | | Type | Application |
| Version | 0.2.0-dev | | Version | 0.0.0 |
| Status | v0.2 in progress — Phase 07 complete | | Status | In development |
| Last Updated | 2026-05-17 | | Last Updated | 2026-05-13 |
## Requirements ## Requirements
@ -31,20 +31,12 @@ Machinists and operators transfer CNC programs to/from any controller without ma
- [x] AES encryption for program files - [x] AES encryption for program files
- [x] CLI argument parsing - [x] CLI argument parsing
- [x] Machine-bound license check (hardcoded) - [x] Machine-bound license check (hardcoded)
- [x] Multi-CN support — Fanuc, Heidenhain, Siemens, Mitsubishi — Phase 14
- [x] 3-file management mode (Fanuc) via IFanucMachine — Phase 4
- [x] NC program validation before upload (all 4 manufacturers) — Phase 5
- [x] Project renamed FanucProgramManager → NcProgramManager — Phase 6
### Validated (Shipped) — continued ### Active (In Progress)
- [x] RSA offline license core: IMachineFingerprint, ILicenseValidator, LicenseValidator, LicenseFile — Phase 7 - [ ] 3-file management mode — branch `3FilesManagement`
### Planned (Next) ### Planned (Next)
- [ ] Wire license into Program.cs — replace checkLicense, replace -chiave= with -licfile= (Phase 08) - To be defined during /paul:plan
- [ ] License generator tool — sign fingerprint with RSA private key, write .lic file (Phase 09)
- [ ] Replace PublicKeyXml PLACEHOLDER with real vendor key pair (Phase 09 output)
- [ ] ReadActiveProgramAsync / SelectMainProgram for Heidenhain and Siemens
- [ ] Integration test against real hardware (Heidenhain, Siemens, Mitsubishi)
### Out of Scope ### Out of Scope
- Cloud sync - Cloud sync
@ -71,21 +63,14 @@ Machinists and operators transfer CNC programs to/from any controller without ma
| .NET 4.7.2 | Max compatibility with operator machines | 2026-05-13 | Active | | .NET 4.7.2 | Max compatibility with operator machines | 2026-05-13 | Active |
| Console EXE output | Simple deployment, scriptable | 2026-05-13 | Active | | Console EXE output | Simple deployment, scriptable | 2026-05-13 | Active |
| Offline license only | Operators may have no internet on shop floor | 2026-05-13 | Active | | Offline license only | Operators may have no internet on shop floor | 2026-05-13 | Active |
| `#` = Warning in Fanuc/Mitsubishi | Macro variable indicator — valid in context | 2026-05-13 | Active |
| Constructor injection for validator | Internal ctor accepts mock — enables unit testing without Windows DLL | 2026-05-13 | Active |
| RSA asymmetric licensing | Private key vendor-only; public key embedded in binary — decompiler can't forge licenses | 2026-05-17 | Active |
| PublicKeyXml PLACEHOLDER until Phase 09 | Real key pair not yet generated; prevents blocking Phase 07 on Phase 09 | 2026-05-17 | Active |
| Heidenhain LSV2 port 19000 | Standard LSV2 port; needs real hardware verification | 2026-05-13 | Active |
| Siemens FTP dir `/_N_MPF_DIR/` | Sinumerik standard MPF directory; FTP option must be enabled | 2026-05-13 | Active |
## Success Metrics ## Success Metrics
| Metric | Target | Current | Status | | Metric | Target | Current | Status |
|--------|--------|---------|--------| |--------|--------|---------|--------|
| Program transfer correctness | 100% (byte-verified) | Unknown | Pending hardware test | | Program transfer correctness | 100% (byte-verified) | Unknown | Not measured |
| License validation (offline) | Works without network | RSA core built, wiring Phase 08 | In progress | | License validation (offline) | Works without network | Hardcoded | At risk |
| Multi-CN support | ≥2 manufacturers | 4 (Fanuc/Heidenhain/Siemens/Mitsubishi) | Done | | Multi-CN support | ≥2 manufacturers | Fanuc only | In progress |
| NC validation coverage | All manufacturers | 4 manufacturers, 30+ tests | Done |
## Tech Stack / Tools ## Tech Stack / Tools
@ -93,15 +78,11 @@ Machinists and operators transfer CNC programs to/from any controller without ma
|-------|------------|-------| |-------|------------|-------|
| Language | C# | .NET Framework 4.7.2 | | Language | C# | .NET Framework 4.7.2 |
| UI | Console EXE | CLI args via InputArgs.cs | | UI | Console EXE | CLI args via InputArgs.cs |
| CN Protocol (Fanuc) | FOCAS fwlib32 | 32-bit DLL, Windows-only | | CN Protocol (Fanuc) | FOCAS fwlib32 | 32-bit DLL |
| CN Protocol (Heidenhain) | LSV2 over TCP | Port 19000, CRC-16/CCITT | | CN Protocol (others) | To be defined | Multi-CN expansion |
| CN Protocol (Siemens) | FTP (FtpWebRequest) | ASCII mode, passive, `/_N_MPF_DIR/` |
| CN Protocol (Mitsubishi) | FTP (FtpWebRequest) | Port 21, `/PRG/`, no extensions |
| Validation | NcProgramValidator | Universal + per-manufacturer rules |
| Encryption | AES (AesCrypt.cs) | Program file protection | | Encryption | AES (AesCrypt.cs) | Program file protection |
| Licensing | RSA-SHA256 asymmetric | IMachineFingerprint + ILicenseValidator; wiring in Phase 08 | | Licensing | Machine fingerprint | Hardcoded now, needs design |
| Testing | NUnit 3.13.3 + Moq 4.18.4 | .NET 4.7.2 test project |
--- ---
*PROJECT.md — Updated when requirements or context change* *PROJECT.md — Updated when requirements or context change*
*Last updated: 2026-05-17 after Phase 7 (License Core)* *Last updated: 2026-05-13*

View file

@ -2,117 +2,82 @@
## Overview ## Overview
Console tool for CNC program transfer and file management. Fanuc FOCAS base, expanding to multi-CN (Heidenhain LSV2, Siemens FTP, Mitsubishi FTP) with NC program validation. Console tool for CNC program transfer and file management. Fanuc FOCAS base, expanding to multi-CN (Heidenhain LSV2, Siemens FTP) with robust offline licensing.
## Current Milestone ## Current Milestone
**v0.1 Multi-CN Release** (v0.1.0) **v0.1 Multi-CN Release** (v0.1.0)
Status: ✅ Complete Status: In progress
Phases: 5 of 5 complete Phases: 0 of 4 complete
Completed: 2026-05-13
## Phases ## Phases
| Phase | Name | Plans | Status | Completed | | Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------| |-------|------|-------|--------|-----------|
| 1 | cnc-abstraction | 1 | ✅ Complete | 2026-05-13 | | 1 | cnc-abstraction | 2 | In progress | - |
| 2 | heidenhain | 1 | ✅ Complete | 2026-05-13 | | 2 | heidenhain | 1 | Not started | - |
| 3 | siemens | 1 | ✅ Complete | 2026-05-13 | | 3 | siemens | 1 | Not started | - |
| 4 | program-wiring | 1 | ✅ Complete | 2026-05-13 | | 4 | program-wiring | 1 | Not started | - |
| 5 | nc-validator | 1 | ✅ Complete | 2026-05-13 |
## Phase Details ## Phase Details
### Phase 1: cnc-abstraction ### Phase 1: cnc-abstraction
**Goal:** Fix namespace, add all Cnc/ files to csproj, complete ICncMachine + IFanucMachine with 3-file support **Goal:** Fix namespace, add all Cnc/ files to csproj, complete ICncMachine + IFanucMachine with 3-file support
**Completed:** 2026-05-13 **Depends on:** Nothing
**Research:** Unlikely
**Delivered:** **Scope:**
- Namespace `Fanuc_integration.Cnc.*``FanucProgramManager.Cnc.*` (29 files) - Fix namespace Fanuc_integration → FanucProgramManager.Cnc
- `IFocasDialect` extended: `UploadStart`/`DownloadStart` accept `short fileType` - Add using FanucProgramManager to Fanuc-specific files
- `IFanucMachine` interface: extends `ICncMachine` with tool offset + work zero R/W - Extend IFocasDialect to accept fileType for tool/work data
- `CncManufacturer` enum: Fanuc, Heidenhain, Siemens - Add IFanucMachine interface with tool offset + work zero ops
- All 29 `Cnc/**/*.cs` files added to csproj - Add CncManufacturer enum, CncMachineFactory stub
- Add all Cnc/ to csproj
### Phase 2: heidenhain ✅ **Plans:**
- [ ] 01-01: Fix namespace, extend dialect interface, IFanucMachine
- [ ] 01-02: CncManufacturer, CncMachineFactory, add to csproj
### Phase 2: heidenhain
**Goal:** Implement ICncMachine for Heidenhain TNC via LSV2 protocol **Goal:** Implement ICncMachine for Heidenhain TNC via LSV2 protocol
**Completed:** 2026-05-13 **Depends on:** Phase 1
**Research:** Likely (LSV2 binary protocol)
**Delivered:** **Scope:**
- `HeidenhainConnectionConfig` — IP, port 19000, credentials, timeout - HeidenhainConnectionConfig
- `Lsv2Client` — TCP framing, CRC-16/CCITT, login (SELECT+LOG_IN), ReceiveFile/SendFile - Lsv2Client (TCP framing, CRC16, commands)
- `HeidenhainMachine : ICncMachine` — RunGuardedAsync pattern, state machine - HeidenhainMachine : ICncMachine
### Phase 3: siemens ✅ **Plans:**
- [ ] 02-01: Heidenhain LSV2 full implementation
### Phase 3: siemens
**Goal:** Implement ICncMachine for Siemens Sinumerik via FTP **Goal:** Implement ICncMachine for Siemens Sinumerik via FTP
**Completed:** 2026-05-13 **Depends on:** Phase 1
**Research:** Unlikely (standard FTP)
**Delivered:** **Scope:**
- `SiemensConnectionConfig` — IP, port 21, creds, program dir `/_N_MPF_DIR/` - SiemensConnectionConfig
- `SiemensFtpClient` — FtpWebRequest wrapper, ASCII mode, passive - SiemensMachine : ICncMachine via FtpWebRequest
- `SiemensMachine : ICncMachine`
**Bonus (post-apply):** **Plans:**
- `MitsubishiConnectionConfig` / `MitsubishiFtpClient` / `MitsubishiMachine : ICncMachine` - [ ] 03-01: Siemens FTP implementation
- `CncManufacturer.Mitsubishi` added to enum + factory + CLI
### Phase 4: program-wiring ### Phase 4: program-wiring
**Goal:** Wire all manufacturers into Program.cs + InputArgs **Goal:** Wire all 3 manufacturers into Program.cs + InputArgs, replace old FANUCMachine
**Completed:** 2026-05-13 **Depends on:** Phase 1, 2, 3
**Delivered:** **Scope:**
- `CncMachineFactory` — creates right `ICncMachine` from `InputArgs` - Update InputArgs with -manufacturer= param
- `InputArgs` — added `manufacturer`, `username`, `password` fields - Refactor Program.cs to use ICncMachine factory
- `Program.cs` — full rewrite using `ICncMachine` factory; Fanuc 3-file via `IFanucMachine` cast - Keep 3-file logic for Fanuc only
### Phase 5: nc-validator ✅ **Plans:**
- [ ] 04-01: InputArgs + Program.cs refactor
**Goal:** Per-manufacturer NC program validator runs inside WriteProgramAsync before upload
**Completed:** 2026-05-13
**Delivered:**
- `ValidationSeverity` / `ValidationError` / `ValidationResult` model types
- `INcProgramValidator` + `NcProgramValidator` with universal + per-manufacturer rules
- Validator injected into all 4 machine classes
- `NcProgramValidatorTests` — 30+ NUnit tests
--- ---
*Roadmap created: 2026-05-13*
## Next Milestone
**v0.2 NcProgramManager** (in progress)
Status: In Progress
Phases: 3 of 4 complete
| Phase | Name | Plans | Status |
|-------|------|-------|--------|
| 06 | rename-cleanup | 1 | ✅ Complete |
| 07 | license-core | 1 | ✅ Complete |
| 08 | license-integration | 1 | ✅ Complete | 2026-05-17 |
| 09 | license-generator | 1 | Not started |
### Phase 06: rename-cleanup ✅
**Goal:** Rename FanucProgramManager → NcProgramManager (namespace, project files, test folder). Delete dead FANUCMachine.cs.
**Completed:** 2026-05-17
**Plans:** [x] 06-01: Namespace + file rename
### Phase 07: license-core ✅
**Goal:** `IMachineFingerprint`, `ILicenseValidator`, RSA signature verification, `.lic` file model
**Completed:** 2026-05-17
**Plans:** [x] 07-01: RSA core + interfaces + 6 unit tests
### Phase 08: license-integration ✅
**Goal:** Wire license system into Program.cs, delete `checkLicense`, remove `-chiave=`, hardcode `license.lic` next to EXE
**Completed:** 2026-05-17
**Plans:** [x] 08-01: Program.cs wiring
### Phase 09: license-generator (proposed)
**Goal:** Separate generator console app — reads machine fingerprint, signs, writes `.lic` file
**Depends on:** Phase 07
---
*Roadmap updated: 2026-05-17 — Phase 08 license-integration complete*

View file

@ -2,127 +2,110 @@
## Project Reference ## Project Reference
See: .paul/PROJECT.md (updated 2026-05-17) See: .paul/PROJECT.md (updated 2026-05-13)
**Core value:** Machinists transfer CNC programs to/from any controller without manual steps or internet **Core value:** Machinists transfer CNC programs to/from any controller without manual steps or internet
**Current focus:** v0.2 — Phase 08 complete, Phase 09 (license-generator) next **Current focus:** 4-CN implementation complete — awaiting user verification and UNIFY
## Current Position ## Current Position
Milestone: v0.2 NcProgramManager Milestone: v0.1 Multi-CN Release
Phase: 4 of 4 (09-license-generator) — Not started Phase: 5 of 5 — Applied
Status: Phase 08 complete, ready to plan Phase 09 Plan: All plans applied, awaiting user UNIFY
Last activity: 2026-05-17 — Phase 08 license-integration complete (UNIFY done) Status: APPLY complete, ready for UNIFY
Last activity: 2026-05-13 — Phase 5 NC Validator applied by agent
Progress: Progress:
- Milestone v0.1: [██████████] 100% (complete) - Milestone: [█████████░] 90%
- Milestone v0.2: [███████░░░] 75%
- Phase 09: [░░░░░░░░░░] 0%
## Loop Position ## Loop Position
Current loop state:
``` ```
PLAN ──▶ APPLY ──▶ UNIFY PLAN ──▶ APPLY ──▶ UNIFY
✓ ✓ ✓ [Phase 08 closed] ✓ ✓ ○ [Awaiting user verification + /paul:unify]
``` ```
## What Was Built (v0.1 Multi-CN Release) ## What was built (all 4 phases)
### Phase 1 — CNC Abstraction ### Phase 1 — Cnc abstraction fixed
- Namespace: `Fanuc_integration.Cnc.*``FanucProgramManager.Cnc.*` (29 files) - Namespace: `Fanuc_integration.Cnc.*``FanucProgramManager.Cnc.*` across 29 files
- `IFocasDialect` extended: `UploadStart`/`DownloadStart` accept `short fileType` - `using FanucProgramManager;` added to all FOCAS-using files
- `IFocasDialect` extended: `UploadStart`/`DownloadStart` now accept `short fileType`
- `IFanucMachine` created: extends `ICncMachine` with tool offset + work zero R/W - `IFanucMachine` created: extends `ICncMachine` with tool offset + work zero R/W
- `CncManufacturer` enum: Fanuc, Heidenhain, Siemens, Mitsubishi - `FanucMachine` now implements `IFanucMachine`
- All 29 `Cnc/**/*.cs` files added to csproj - `CncManufacturer` enum: Fanuc, Heidenhain, Siemens
- All 29 `Cnc/**/*.cs` files added to `.csproj` (36 total Compile entries)
### Phase 2 — Heidenhain LSV2 ### Phase 2 — Heidenhain LSV2
- `HeidenhainConnectionConfig` — IP, port 19000, credentials, timeout - `HeidenhainConnectionConfig` — IP, port 19000, credentials, timeout
- `Lsv2Client` — TCP framing, CRC-16/CCITT, login (SELECT+LOG_IN), ReceiveFile/SendFile - `Lsv2Client` — TCP framing, CRC-16/CCITT, login (SELECT+LOG_IN), ReceiveFile/SendFile
- `HeidenhainMachine : ICncMachine` — RunGuardedAsync pattern, state machine - `HeidenhainMachine : ICncMachine` — RunGuardedAsync pattern, state machine
### Phase 3 — Siemens + Mitsubishi FTP ### Phase 3.1 — Mitsubishi M-series (added post-apply)
- `SiemensConnectionConfig` / `SiemensFtpClient` / `SiemensMachine : ICncMachine` - `MitsubishiConnectionConfig` — port 21, dir `/PRG/` (no file extensions)
- `MitsubishiConnectionConfig` / `MitsubishiFtpClient` / `MitsubishiMachine : ICncMachine` - `MitsubishiFtpClient` / `MitsubishiMachine : ICncMachine` — FTP, same pattern as Siemens
- `CncManufacturer.Mitsubishi` added to enum + factory + CLI parsing
### Phase 3 — Siemens Sinumerik FTP
- `SiemensConnectionConfig` — IP, port 21, creds, program dir `/_N_MPF_DIR/`
- `SiemensFtpClient` — FtpWebRequest wrapper, ASCII mode, passive
- `SiemensMachine : ICncMachine` — same pattern as FanucMachine
### Phase 4 — Wiring ### Phase 4 — Wiring
- `CncMachineFactory` — creates right `ICncMachine` from `InputArgs` - `CncMachineFactory` — creates right ICncMachine from InputArgs
- `InputArgs` — added `manufacturer`, `username`, `password` fields - `InputArgs` — added `manufacturer`, `username`, `password` fields
- `Program.cs` — full rewrite using `ICncMachine`; Fanuc 3-file via `IFanucMachine` cast - `Program.cs` — full rewrite using ICncMachine; Fanuc 3-file via IFanucMachine cast
### Phase 5 — NC Validator ### Phase 5 — NC Validator
- `ValidationSeverity` / `ValidationError` / `ValidationResult` model types - `ValidationSeverity` enum (Error/Warning)
- `INcProgramValidator` + `NcProgramValidator` (5 universal + per-manufacturer rules) - `ValidationError` — Line, Message, Severity
- Validator injected into all 4 machine classes; upload blocked on Error severity - `ValidationResult` — Success + IReadOnlyList<ValidationError>, static Ok()/Fail()
- `NcProgramValidatorTests` — 30+ NUnit tests - `INcProgramValidator` interface
- `NcProgramValidator` — universal rules (block length ≤80, no lowercase, no tabs, no nested parens, comment ≤32); per-manufacturer name format + forbidden char rules; `#` in Fanuc/Mitsubishi is Warning not Error
- All 4 machines injected: public constructor creates `NcProgramValidator(manufacturer)`, internal constructor accepts `INcProgramValidator` for testing
- All 4 machines call validator in `WriteProgramAsync` before upload; fail on any Error-severity violation
- `NcProgramValidatorTests` — 30+ test cases covering all rules and all manufacturers
- Existing machine unit tests updated: inject always-ok mock validator to preserve existing behaviour tests
- Integration tests updated: content+names corrected to pass validation per manufacturer
- 5 new Compile entries in main csproj + 1 in tests csproj
### Test Infrastructure ## New CLI args
- `NcProgramManager.Tests/` — NUnit 3.13.3 + Moq 4.18.4, .NET 4.7.2 - `-manufacturer=fanuc|heidenhain|siemens` (default: fanuc)
- `FtpServerStub` + `Lsv2ServerStub` — in-process TCP stubs - `-username=` (Heidenhain/Siemens)
- Unit + integration tests; Fanuc FOCAS tests `[Ignore]`d (Windows-only DLL) - `-password=` (Heidenhain/Siemens)
## What Was Built (v0.2 NcProgramManager — in progress)
### Phase 06 — Rename Cleanup
- Namespace: `FanucProgramManager.*``NcProgramManager.*` (all files)
- Project renamed, test folder renamed, dead `FANUCMachine.cs` deleted
### Phase 07 — License Core
- `IMachineFingerprint` / `ILicenseValidator` interfaces
- `MachineFingerprint` — WMI CPU/BIOS fingerprint, SHA256 hash
- `LicenseFile.TryRead` — reads `.lic` file, returns base64 signature
- `LicenseValidator.Validate` — RSA-SHA256 verify; PLACEHOLDER public key until Phase 09
- `LicenseValidatorTests` — 6 unit tests (testable via internal ctor injection)
### Phase 08 — License Integration
- `Program.cs``checkLicense` deleted; RSA check wired at startup:
```csharp
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
if (!LicenseFile.TryRead(_licPath, out _licBase64)
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64)) { ... }
```
- `InputArgs.cs``chiave` field removed
- `-chiave=` CLI arg removed; license file hardcoded next to EXE as `license.lic`
- `MostraAiuto` updated: instructs user to place `license.lic` next to executable
## CLI Args (current)
- `-manufacturer=fanuc|heidenhain|siemens|mitsubishi` (default: fanuc)
- `-azione=` — action (Scarica/Invia/etc.)
- `-ip=` — CNC IP
- `-porta=` — port
- `-username=` (Heidenhain/Siemens/Mitsubishi)
- `-password=` (Heidenhain/Siemens/Mitsubishi)
- License: place `license.lic` next to the EXE (no CLI arg)
## Accumulated Context ## Accumulated Context
### Decisions ### Decisions
- .NET 4.7.2 locked — max compatibility - .NET 4.7.2 locked — max compatibility
- RSA asymmetric offline licensing: private key vendor-only, public key embedded in binary - Offline-only license check (AES, unchanged)
- `license.lic` always at `AppDomain.CurrentDomain.BaseDirectory` — no CLI override
- `RSACryptoServiceProvider` + `SHA256CryptoServiceProvider` (not HashAlgorithmName) — .NET 4.7.2 compat
- Constructor injection pattern: internal ctor accepts key XML — testable without Windows WMI
- PublicKeyXml = PLACEHOLDER until Phase 09 generates real key pair
- Heidenhain: LSV2 port 19000, needs real hardware testing - Heidenhain: LSV2 port 19000, needs real hardware testing
- Siemens: FTP `/_N_MPF_DIR/`, needs Sinumerik FTP option enabled on machine - Siemens: FTP /_N_MPF_DIR/, needs Sinumerik FTP option enabled on machine
- `#` = Warning in Fanuc/Mitsubishi — macro variable indicator
### Phase 5 — Test project (added post-apply)
- `FanucProgramManager.Tests/` — NUnit 3.13.3 + Moq 4.18.4, .NET 4.7.2
- 7 unit test files (14+ tests on `FanucProgram`, `IsoProgramFormatter`, factory, all 4 machines)
- `FtpServerStub` — in-process TCP FTP (PASV); `Lsv2ServerStub` — in-process LSV2 TCP
- 3 integration test files — write→read round-trips against local stubs
- `InternalsVisibleTo("FanucProgramManager.Tests")` added to `AssemblyInfo.cs`
- Fanuc FOCAS tests that need Windows DLL are `[Ignore]`-d with explanation
### Deferred Issues ### Deferred Issues
- Real RSA key pair — Phase 09 output; copy PublicKeyXml into LicenseValidator.cs - License system: still hardcoded AES key/IV — needs Phase 2.x redesign
- `AesCrypt.cs` dead code — remove in future cleanup phase - Heidenhain LSV2: ReadActiveProgramAsync / SelectMainProgram not supported
- Heidenhain: `ReadActiveProgramAsync` / `SelectMainProgram` not implemented - Siemens FTP: ReadActiveProgramAsync / SelectMainProgram not supported
- Siemens: same as Heidenhain - Build not verifiable on Linux (fwlib32 is Windows-only DLL)
- Build + integration not verifiable on Linux (fwlib32 Windows-only)
- MachineFingerprint WMI — code-review verified, needs real Windows hardware test
### Blockers/Concerns ### Blockers/Concerns
None — Phase 09 can start. None — code complete, pending hardware testing.
## Session Continuity ## Session Continuity
Last session: 2026-05-17 Last session: 2026-05-13
Stopped at: Phase 08 UNIFY complete Stopped at: Phase 5 (NC Validator) applied — all 12 files created/modified
Next action: /paul:plan phase 09 license-generator Next action: Build to verify compilation; run unit tests; /paul:unify when ready
Resume file: .paul/ROADMAP.md Resume file: .paul/STATE.md
--- ---
*STATE.md — Updated after every significant action* *STATE.md — Updated after every significant action*

View file

@ -1,15 +1,15 @@
{ {
"name": "NcProgramManager", "name": "FanucProgramManager",
"version": "0.2.0-dev", "version": "0.0.0",
"milestone": { "milestone": {
"name": "v0.2 NcProgramManager", "name": "None",
"version": "0.2.0", "version": "0.0.0",
"status": "in_progress" "status": "not_started"
}, },
"phase": { "phase": {
"number": 8, "number": 0,
"name": "license-integration", "name": "None",
"status": "complete" "status": "not_started"
}, },
"loop": { "loop": {
"plan": null, "plan": null,
@ -17,7 +17,7 @@
}, },
"timestamps": { "timestamps": {
"created_at": "2026-05-13T00:00:00Z", "created_at": "2026-05-13T00:00:00Z",
"updated_at": "2026-05-17T22:00:00Z" "updated_at": "2026-05-13T00:00:00Z"
}, },
"satellite": { "satellite": {
"groom": true "groom": true

View file

@ -1,134 +0,0 @@
---
phase: 05-nc-validator
plan: 01
subsystem: validation
tags: [nc-program, validation, csharp, dotnet472, nunit]
requires:
- phase: 04-program-wiring
provides: ICncMachine implementations for all 4 manufacturers
provides:
- INcProgramValidator interface + NcProgramValidator implementation
- Pre-upload validation in all 4 machine WriteProgramAsync methods
- 30+ unit tests covering all rules and all manufacturers
affects: []
tech-stack:
added: []
patterns:
- "Validator injected via constructor (public creates default, internal accepts mock)"
- "ValidationResult static factory: Ok() / Fail(errors)"
key-files:
created:
- Cnc/Models/ValidationSeverity.cs
- Cnc/Models/ValidationError.cs
- Cnc/Models/ValidationResult.cs
- Cnc/INcProgramValidator.cs
- Cnc/NcProgramValidator.cs
- FanucProgramManager.Tests/Unit/NcProgramValidatorTests.cs
modified:
- Cnc/Fanuc/FanucMachine.cs
- Cnc/Heidenhain/HeidenhainMachine.cs
- Cnc/Siemens/SiemensMachine.cs
- Cnc/Mitsubishi/MitsubishiMachine.cs
- FanucProgramManager.csproj
- FanucProgramManager.Tests/FanucProgramManager.Tests.csproj
key-decisions:
- "# in Fanuc/Mitsubishi is Warning not Error — macro variable indicator, not a format error"
- "Internal constructor accepts INcProgramValidator — enables unit testing without mocking filesystem"
patterns-established:
- "Constructor injection for testability: public ctor creates real validator, internal ctor accepts mock"
- "ValidationResult.Ok() / Fail(errors) static factory pattern"
duration: unknown
started: 2026-05-13T00:00:00Z
completed: 2026-05-13T00:00:00Z
---
# Phase 5 Plan 1: NC Validator Summary
**Per-manufacturer NC program validator integrated into all 4 machine WriteProgramAsync methods, with 30+ unit tests.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~2h (estimated) |
| Started | 2026-05-13 |
| Completed | 2026-05-13 |
| Tasks | 12 files created/modified |
| Files modified | 12 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| NcProgramValidator validates all 4 manufacturers | Pass | Fanuc, Heidenhain, Siemens, Mitsubishi |
| Universal rules: block ≤80, no lowercase, no tabs, no nested parens, comment ≤32 | Pass | All rules implemented |
| Per-manufacturer name format + forbidden chars | Pass | Per spec |
| Fanuc # char is Warning not Error | Pass | Mitsubishi same |
| All 4 machines call validator in WriteProgramAsync | Pass | Verified by grep |
| Unit tests cover all rules + all manufacturers | Pass | 30+ test cases |
| csproj Compile entries added (5 main + 1 test) | Pass | Verified by grep count |
## Accomplishments
- `NcProgramValidator` with 5 universal + 4 per-manufacturer rule sets
- All 4 machine classes fail upload on any Error-severity violation
- Constructor injection pattern enables testability without test pollution
- 30+ NUnit test cases; existing machine tests updated with always-ok mock validator
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `Cnc/Models/ValidationSeverity.cs` | Created | Error/Warning enum |
| `Cnc/Models/ValidationError.cs` | Created | Line + message + severity |
| `Cnc/Models/ValidationResult.cs` | Created | Success flag + error list, Ok()/Fail() |
| `Cnc/INcProgramValidator.cs` | Created | Validator interface |
| `Cnc/NcProgramValidator.cs` | Created | Full rule implementation |
| `Cnc/Fanuc/FanucMachine.cs` | Modified | Validator injected, WriteProgramAsync gated |
| `Cnc/Heidenhain/HeidenhainMachine.cs` | Modified | Same |
| `Cnc/Siemens/SiemensMachine.cs` | Modified | Same |
| `Cnc/Mitsubishi/MitsubishiMachine.cs` | Modified | Same |
| `FanucProgramManager.Tests/Unit/NcProgramValidatorTests.cs` | Created | 30+ test cases |
| `FanucProgramManager.csproj` | Modified | +5 Compile entries |
| `FanucProgramManager.Tests/FanucProgramManager.Tests.csproj` | Modified | +1 Compile entry |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| `#` = Warning in Fanuc/Mitsubishi | Macro variable indicator — valid in context | Uploads proceed; operator notified |
| Internal constructor for mock injection | .NET 4.7.2 — no Moq interface without internal access | All machine tests remain isolated |
## Deviations from Plan
None — plan executed as specified.
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| Build not verifiable on Linux | fwlib32 is Windows-only DLL — tests using FOCAS marked `[Ignore]` |
## Next Phase Readiness
**Ready:**
- Validator fully integrated — any new machine implementing `ICncMachine` can inject it
- Test infrastructure in place for future expansion
**Concerns:**
- Hardware testing still pending for Heidenhain and Siemens
- License system hardcoded — deferred from earlier phases
**Blockers:** None
---
*Phase: 05-nc-validator, Plan: 01*
*Completed: 2026-05-13*

View file

@ -1,220 +0,0 @@
---
phase: 06-rename-cleanup
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- "**/*.cs (all ~62 files — namespace string replace)"
- Properties/AssemblyInfo.cs
- FanucProgramManager.csproj → NcProgramManager.csproj
- FanucProgramManager.sln → NcProgramManager.sln
- FanucProgramManager.Tests/FanucProgramManager.Tests.csproj → NcProgramManager.Tests/NcProgramManager.Tests.csproj
- FANUCMachine.cs (delete)
autonomous: true
---
<objective>
## Goal
Rename project from FanucProgramManager to NcProgramManager: namespace, assembly, project files, test folder, and delete dead legacy file.
## Purpose
Product is no longer Fanuc-only. Name FanucProgramManager is wrong. NcProgramManager reflects multi-CN scope.
## Output
- All namespaces: `FanucProgramManager``NcProgramManager`
- Project/solution files renamed
- Test folder renamed
- `FANUCMachine.cs` deleted (zero references outside itself)
- Solution builds with no errors
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/STATE.md
## Key Files
@FanucProgramManager.csproj
@FanucProgramManager.sln
@Properties/AssemblyInfo.cs
@FanucProgramManager.Tests/FanucProgramManager.Tests.csproj
</context>
<acceptance_criteria>
## AC-1: Namespace renamed everywhere
```gherkin
Given any .cs file in the project
When opened
Then namespace declarations and using directives reference NcProgramManager (not FanucProgramManager)
```
## AC-2: Project and solution files updated
```gherkin
Given the renamed solution
When opened in Visual Studio or built with msbuild
Then assembly name is NcProgramManager, test assembly is NcProgramManager.Tests, project reference resolves
```
## AC-3: Dead code removed
```gherkin
Given FANUCMachine.cs
When cleanup completes
Then file does not exist (confirmed zero references before deletion)
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Replace FanucProgramManager string in all .cs files</name>
<files>All *.cs files under project root (recursively)</files>
<action>
Run find+sed to replace all occurrences of `FanucProgramManager` with `NcProgramManager` in every .cs file:
```bash
find /path/to/project -name "*.cs" -exec sed -i 's/FanucProgramManager/NcProgramManager/g' {} +
```
This covers:
- `namespace FanucProgramManager``namespace NcProgramManager`
- `namespace FanucProgramManager.Cnc``namespace NcProgramManager.Cnc`
- `using FanucProgramManager;``using NcProgramManager;`
- `using FanucProgramManager.Cnc.Fanuc;``using NcProgramManager.Cnc.Fanuc;`
- All sub-namespaces (.Cnc, .Cnc.Fanuc, .Cnc.Heidenhain, .Cnc.Siemens, .Cnc.Mitsubishi, .Cnc.Models)
- `InternalsVisibleTo("FanucProgramManager.Tests")` in AssemblyInfo.cs
- `AssemblyTitle`, `AssemblyProduct` in AssemblyInfo.cs
Avoid: editing binary files, NuGet package cache, obj/bin folders.
Use: `-not -path "*/obj/*" -not -path "*/bin/*" -not -path "*/.paul/*"`
</action>
<verify>
```bash
grep -r "FanucProgramManager" --include="*.cs" .
```
Must return zero results.
</verify>
<done>AC-1 satisfied: no .cs file contains FanucProgramManager</done>
</task>
<task type="auto">
<name>Task 2: Update project and solution metadata files</name>
<files>
FanucProgramManager.csproj,
FanucProgramManager.Tests/FanucProgramManager.Tests.csproj,
FanucProgramManager.sln
</files>
<action>
In `FanucProgramManager.csproj`:
- `<RootNamespace>FanucProgramManager</RootNamespace>``<RootNamespace>NcProgramManager</RootNamespace>`
- `<AssemblyName>FanucProgramManager</AssemblyName>``<AssemblyName>NcProgramManager</AssemblyName>`
In `FanucProgramManager.Tests/FanucProgramManager.Tests.csproj`:
- `<RootNamespace>FanucProgramManager.Tests</RootNamespace>``<RootNamespace>NcProgramManager.Tests</RootNamespace>`
- `<AssemblyName>FanucProgramManager.Tests</AssemblyName>``<AssemblyName>NcProgramManager.Tests</AssemblyName>`
- `<ProjectReference Include="..\FanucProgramManager.csproj">``<ProjectReference Include="..\NcProgramManager.csproj">`
- `<Name>FanucProgramManager</Name>``<Name>NcProgramManager</Name>`
In `FanucProgramManager.sln`:
- Project line: `"FanucProgramManager", "FanucProgramManager.csproj"``"NcProgramManager", "NcProgramManager.csproj"`
- Project line: `"FanucProgramManager.Tests", "FanucProgramManager.Tests\FanucProgramManager.Tests.csproj"``"NcProgramManager.Tests", "NcProgramManager.Tests\NcProgramManager.Tests.csproj"`
</action>
<verify>
```bash
grep -n "FanucProgramManager" FanucProgramManager.csproj FanucProgramManager.Tests/FanucProgramManager.Tests.csproj FanucProgramManager.sln
```
Must return zero results.
</verify>
<done>AC-2 satisfied: all project metadata references NcProgramManager</done>
</task>
<task type="auto">
<name>Task 3: Rename files/folder and delete dead code</name>
<files>
FANUCMachine.cs (delete),
FanucProgramManager.csproj → NcProgramManager.csproj (rename),
FanucProgramManager.sln → NcProgramManager.sln (rename),
FanucProgramManager.Tests/ → NcProgramManager.Tests/ (folder rename),
FanucProgramManager.Tests/FanucProgramManager.Tests.csproj → NcProgramManager.Tests/NcProgramManager.Tests.csproj (rename)
</files>
<action>
1. Delete `FANUCMachine.cs` — confirmed dead: no file outside itself references `FANUCMachine`. Also remove its `<Compile Include="FANUCMachine.cs" />` entry from csproj.
2. Rename project file:
```bash
mv FanucProgramManager.csproj NcProgramManager.csproj
```
3. Rename solution file:
```bash
mv FanucProgramManager.sln NcProgramManager.sln
```
4. Rename test folder (this moves all contents):
```bash
mv FanucProgramManager.Tests NcProgramManager.Tests
```
5. Rename test csproj inside the now-renamed folder:
```bash
mv NcProgramManager.Tests/FanucProgramManager.Tests.csproj NcProgramManager.Tests/NcProgramManager.Tests.csproj
```
Order matters: rename folder BEFORE renaming csproj inside it.
The .sln file was already updated in Task 2 to reference new paths — verify paths match after rename.
Avoid: renaming `Cnc/Fanuc/` folder or any Fanuc-prefixed class files — `FanucMachine`, `FanucConnectionConfig` etc. are manufacturer-specific names, keep them.
</action>
<verify>
```bash
ls NcProgramManager.csproj NcProgramManager.sln NcProgramManager.Tests/NcProgramManager.Tests.csproj
ls FANUCMachine.cs 2>&1 # must say: No such file
grep "FANUCMachine" NcProgramManager.csproj # must return nothing
```
</verify>
<done>AC-2 and AC-3 satisfied: files renamed, dead code deleted, .sln paths match physical files</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `Cnc/Fanuc/` folder name — Fanuc is a manufacturer name, not a project name
- `FanucMachine.cs`, `FanucConnectionConfig.cs`, `FanucCapabilities.cs` — manufacturer-specific class names, keep
- `FanucProgram.cs` — Fanuc ISO program format class, name is accurate
- `FanucProgramParsingTests.cs` — test file for FanucProgram, name is accurate
- `IFanucMachine.cs` — interface name correct (Fanuc-specific capability set)
- Any class name that describes Fanuc-specific behavior
## SCOPE LIMITS
- No Italian → English rename in this plan (deferred)
- No logic changes — pure rename/delete only
- No NuGet package changes
- No new files created
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `grep -r "FanucProgramManager" --include="*.cs" .` returns zero results
- [ ] `grep -r "FanucProgramManager" --include="*.csproj" .` returns zero results
- [ ] `grep "FanucProgramManager" NcProgramManager.sln` returns zero results
- [ ] `ls FANUCMachine.cs` returns "No such file"
- [ ] `ls NcProgramManager.csproj NcProgramManager.sln NcProgramManager.Tests/` all exist
- [ ] All acceptance criteria met
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No FanucProgramManager references remain in .cs, .csproj, or .sln files
- FANUCMachine.cs deleted
- Project file names match NcProgramManager
</success_criteria>
<output>
After completion, create `.paul/phases/06-rename-cleanup/06-01-SUMMARY.md`
</output>

View file

@ -1,102 +0,0 @@
---
phase: 06-rename-cleanup
plan: 01
subsystem: infra
tags: [rename, namespace, csharp, dotnet472, cleanup]
requires: []
provides:
- NcProgramManager namespace across all 62 .cs files
- NcProgramManager.csproj / NcProgramManager.sln / NcProgramManager.Tests/
- FANUCMachine.cs deleted
affects: [07-license-core, 08-license-integration, 09-license-generator]
tech-stack:
added: []
patterns: []
key-files:
created: []
modified:
- NcProgramManager.csproj (was FanucProgramManager.csproj)
- NcProgramManager.sln (was FanucProgramManager.sln)
- NcProgramManager.Tests/NcProgramManager.Tests.csproj
- All 62 .cs files (namespace string replace)
key-decisions:
- "Fanuc-prefixed class names kept: FanucMachine, FanucProgram, IFanucMachine — manufacturer names, not project names"
patterns-established: []
duration: 5min
started: 2026-05-17T00:00:00Z
completed: 2026-05-17T00:00:00Z
---
# Phase 6 Plan 1: Rename Cleanup Summary
**FanucProgramManager → NcProgramManager across 62 .cs files, 3 project/solution files, test folder rename, FANUCMachine.cs deleted.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~5 min |
| Started | 2026-05-17 |
| Completed | 2026-05-17 |
| Tasks | 3 completed |
| Files modified | 62 .cs + 3 project files + 1 folder rename + 1 file deleted |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Namespace renamed everywhere | Pass | grep returns zero results across all .cs files |
| AC-2: Project/solution files updated | Pass | AssemblyName, RootNamespace, ProjectReference all updated; files renamed |
| AC-3: Dead code removed | Pass | FANUCMachine.cs deleted; Compile entry removed from csproj |
## Accomplishments
- All namespaces, using directives, AssemblyTitle, AssemblyProduct, InternalsVisibleTo updated in one sed pass
- Project and test folder physically renamed to match new name
- Zero FanucProgramManager strings remain in .cs, .csproj, or .sln
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| All 62 `.cs` files | Modified | `FanucProgramManager``NcProgramManager` namespace/usings |
| `NcProgramManager.csproj` | Renamed + modified | AssemblyName + RootNamespace updated |
| `NcProgramManager.sln` | Renamed + modified | Project references updated |
| `NcProgramManager.Tests/NcProgramManager.Tests.csproj` | Renamed + modified | AssemblyName, RootNamespace, ProjectReference updated |
| `FANUCMachine.cs` | Deleted | Dead code — zero references outside itself |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Keep `FanucMachine`, `FanucProgram`, `IFanucMachine` names | These are manufacturer-specific identifiers, not project names | Future phases reference these names unchanged |
## Deviations from Plan
None — plan executed exactly as written.
## Issues Encountered
None.
## Next Phase Readiness
**Ready:**
- All code under `NcProgramManager` namespace
- Project builds under new name (pending Windows build verification)
- Clean foundation for license system phases
**Concerns:**
- Build not verifiable on Linux (fwlib32 Windows-only DLL) — same as before
**Blockers:** None
---
*Phase: 06-rename-cleanup, Plan: 01*
*Completed: 2026-05-17*

View file

@ -1,441 +0,0 @@
---
phase: 07-license-core
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- Licensing/IMachineFingerprint.cs
- Licensing/ILicenseValidator.cs
- Licensing/LicenseFile.cs
- Licensing/MachineFingerprint.cs
- Licensing/LicenseValidator.cs
- NcProgramManager.Tests/Unit/LicenseValidatorTests.cs
- NcProgramManager.csproj
- NcProgramManager.Tests/NcProgramManager.Tests.csproj
autonomous: true
---
<objective>
## Goal
Create RSA-based offline license core: machine fingerprint reader, license file model, RSA signature validator — all injectable via interfaces for testability.
## Purpose
Current `checkLicense` in Program.cs uses hardcoded AES key — anyone with source or decompiler can generate licenses for any machine. RSA asymmetric crypto means only the holder of the private key (vendor) can issue valid licenses. Public key is safe to embed in binary.
## Output
- `Licensing/` folder with 5 new files
- `LicenseValidator` verifies RSA-SHA256 signature of machine fingerprint against embedded public key
- `MachineFingerprint` reads CPU ProcessorID + Motherboard SerialNumber via WMI (same data as current `checkLicense`)
- `LicenseFile` reads base64 signature from a `.lic` file on disk
- Unit tests: valid license passes, tampered/wrong-machine license fails
- 5 Compile entries added to main csproj, 1 to tests csproj
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/STATE.md
## Source Files (existing license logic to extract from)
@Program.cs
@NcProgramManager.csproj
</context>
<acceptance_criteria>
## AC-1: Machine fingerprint readable via interface
```gherkin
Given a Windows machine with WMI available
When MachineFingerprint.GetFingerprint() is called
Then it returns a non-empty string concatenating CPU ProcessorID and Motherboard SerialNumber
```
## AC-2: Valid license passes validation
```gherkin
Given a fingerprint string and a license file containing the RSA-SHA256 signature of that fingerprint
When LicenseValidator.Validate(fingerprint, licenseBase64) is called with the matching public key
Then it returns true
```
## AC-3: Invalid license rejected
```gherkin
Given a fingerprint string and a license signed for a different fingerprint (wrong machine)
When LicenseValidator.Validate(fingerprint, licenseBase64) is called
Then it returns false
```
## AC-4: Corrupted/missing license file handled
```gherkin
Given a path to a non-existent or empty .lic file
When LicenseFile.TryRead(path, out string b64) is called
Then it returns false and b64 is null (no exception thrown)
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Create Licensing interfaces and LicenseFile</name>
<files>
Licensing/IMachineFingerprint.cs,
Licensing/ILicenseValidator.cs,
Licensing/LicenseFile.cs
</files>
<action>
Create folder `Licensing/` at project root.
**Licensing/IMachineFingerprint.cs**
```csharp
namespace NcProgramManager.Licensing
{
internal interface IMachineFingerprint
{
string GetFingerprint();
}
}
```
**Licensing/ILicenseValidator.cs**
```csharp
namespace NcProgramManager.Licensing
{
internal interface ILicenseValidator
{
bool Validate(string fingerprint, string licenseBase64);
}
}
```
**Licensing/LicenseFile.cs**
```csharp
using System;
using System.IO;
namespace NcProgramManager.Licensing
{
internal static class LicenseFile
{
internal static bool TryRead(string path, out string base64)
{
base64 = null;
try
{
if (!File.Exists(path))
return false;
string content = File.ReadAllText(path).Trim();
if (string.IsNullOrEmpty(content))
return false;
base64 = content;
return true;
}
catch
{
return false;
}
}
}
}
```
Avoid: throwing exceptions from TryRead — callers expect bool pattern.
</action>
<verify>
```bash
ls Licensing/IMachineFingerprint.cs Licensing/ILicenseValidator.cs Licensing/LicenseFile.cs
grep -n "namespace NcProgramManager.Licensing" Licensing/IMachineFingerprint.cs Licensing/ILicenseValidator.cs Licensing/LicenseFile.cs
```
All three files exist with correct namespace.
</verify>
<done>AC-4 satisfied: LicenseFile.TryRead handles missing/empty file without exception</done>
</task>
<task type="auto">
<name>Task 2: Implement MachineFingerprint and LicenseValidator</name>
<files>
Licensing/MachineFingerprint.cs,
Licensing/LicenseValidator.cs,
NcProgramManager.csproj
</files>
<action>
**Licensing/MachineFingerprint.cs**
Extract the WMI fingerprint logic from `Program.checkLicense`. Do NOT modify Program.cs yet — that is Phase 08.
```csharp
using System.Management;
namespace NcProgramManager.Licensing
{
internal class MachineFingerprint : IMachineFingerprint
{
public string GetFingerprint()
{
string pcInfo = string.Empty;
using (var mc = new ManagementClass("win32_processor"))
using (ManagementObjectCollection moc = mc.GetInstances())
{
foreach (ManagementObject mo in moc)
{
pcInfo = mo.Properties["processorID"].Value.ToString();
break;
}
}
using (var searcher = new ManagementObjectSearcher(
"root\\CIMV2", "SELECT * FROM Win32_BaseBoard"))
{
foreach (ManagementObject obj in searcher.Get())
{
pcInfo += obj["SerialNumber"].ToString();
break;
}
}
return pcInfo;
}
}
}
```
**Licensing/LicenseValidator.cs**
RSA-SHA256 verify. Public key is a placeholder XML constant — Phase 09 (generator) produces the real key pair; replace `PublicKeyXml` at that point.
```csharp
using System;
using System.Security.Cryptography;
using System.Text;
namespace NcProgramManager.Licensing
{
internal class LicenseValidator : ILicenseValidator
{
// Placeholder — replace with real vendor public key XML after Phase 09 generates the key pair.
// Generate with: openssl genrsa 2048 | openssl rsa -pubout, then convert PEM → XML,
// or use RSACryptoServiceProvider.ToXmlString(false) from the generator tool.
private const string PublicKeyXml =
"<RSAKeyValue>" +
"<Modulus>PLACEHOLDER</Modulus>" +
"<Exponent>AQAB</Exponent>" +
"</RSAKeyValue>";
private readonly string _publicKeyXml;
// Public constructor: uses embedded vendor public key.
internal LicenseValidator() : this(PublicKeyXml) { }
// Internal constructor: accepts any public key XML — used in unit tests.
internal LicenseValidator(string publicKeyXml)
{
_publicKeyXml = publicKeyXml;
}
public bool Validate(string fingerprint, string licenseBase64)
{
try
{
byte[] signature = Convert.FromBase64String(licenseBase64);
byte[] data = Encoding.UTF8.GetBytes(fingerprint);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(_publicKeyXml);
return rsa.VerifyData(data, new SHA256CryptoServiceProvider(), signature);
}
}
catch
{
return false;
}
}
}
}
```
Note: `RSACryptoServiceProvider.VerifyData(byte[], object, byte[])` — the hash algorithm
argument is `new SHA256CryptoServiceProvider()`, not `HashAlgorithmName` (that overload
requires .NET 4.6+ RSA base class; `RSACryptoServiceProvider` overload uses the older
`object` signature which is available in .NET 4.7.2).
**NcProgramManager.csproj** — add 5 Compile entries after last existing `<Compile>` in the
`Licensing` group (create new ItemGroup if none exists):
```xml
<Compile Include="Licensing\IMachineFingerprint.cs" />
<Compile Include="Licensing\ILicenseValidator.cs" />
<Compile Include="Licensing\LicenseFile.cs" />
<Compile Include="Licensing\MachineFingerprint.cs" />
<Compile Include="Licensing\LicenseValidator.cs" />
```
System.Management reference already present — no new reference needed.
</action>
<verify>
```bash
ls Licensing/MachineFingerprint.cs Licensing/LicenseValidator.cs
grep "LicenseValidator\|MachineFingerprint\|LicenseFile\|IMachineFingerprint\|ILicenseValidator" NcProgramManager.csproj | grep "Compile"
```
Both files exist. Five Compile entries present in csproj.
</verify>
<done>AC-1 satisfied: MachineFingerprint implements IMachineFingerprint with WMI logic. AC-2/AC-3 groundwork: LicenseValidator implements ILicenseValidator with RSA verify.</done>
</task>
<task type="auto">
<name>Task 3: Unit tests for LicenseValidator and LicenseFile</name>
<files>
NcProgramManager.Tests/Unit/LicenseValidatorTests.cs,
NcProgramManager.Tests/NcProgramManager.Tests.csproj
</files>
<action>
Tests use the internal `LicenseValidator(string publicKeyXml)` constructor to inject a
test-generated key pair — same pattern as NcProgramValidatorTests.
```csharp
using System;
using System.Security.Cryptography;
using System.Text;
using NUnit.Framework;
using NcProgramManager.Licensing;
namespace NcProgramManager.Tests.Unit
{
[TestFixture]
public class LicenseValidatorTests
{
private string _publicKeyXml;
private string _privateKeyXml;
private const string TestFingerprint = "BFEBFBFF000906EAM80-80001234";
[SetUp]
public void SetUp()
{
using (var rsa = new RSACryptoServiceProvider(2048))
{
_publicKeyXml = rsa.ToXmlString(false); // public only
_privateKeyXml = rsa.ToXmlString(true); // includes private
}
}
private string Sign(string fingerprint)
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(_privateKeyXml);
byte[] sig = rsa.SignData(
Encoding.UTF8.GetBytes(fingerprint),
new SHA256CryptoServiceProvider());
return Convert.ToBase64String(sig);
}
}
[Test]
public void Validate_ValidLicense_ReturnsTrue()
{
string licenseB64 = Sign(TestFingerprint);
var validator = new LicenseValidator(_publicKeyXml);
Assert.IsTrue(validator.Validate(TestFingerprint, licenseB64));
}
[Test]
public void Validate_WrongMachineFingerprint_ReturnsFalse()
{
string licenseB64 = Sign("DIFFERENT_MACHINE_ID");
var validator = new LicenseValidator(_publicKeyXml);
Assert.IsFalse(validator.Validate(TestFingerprint, licenseB64));
}
[Test]
public void Validate_TamperedLicense_ReturnsFalse()
{
string licenseB64 = Sign(TestFingerprint);
// Flip one char
char[] chars = licenseB64.ToCharArray();
chars[10] = chars[10] == 'A' ? 'B' : 'A';
var validator = new LicenseValidator(_publicKeyXml);
Assert.IsFalse(validator.Validate(TestFingerprint, new string(chars)));
}
[Test]
public void Validate_EmptyLicense_ReturnsFalse()
{
var validator = new LicenseValidator(_publicKeyXml);
Assert.IsFalse(validator.Validate(TestFingerprint, ""));
}
[Test]
public void Validate_NotBase64_ReturnsFalse()
{
var validator = new LicenseValidator(_publicKeyXml);
Assert.IsFalse(validator.Validate(TestFingerprint, "not-valid-base64!!!"));
}
[Test]
public void LicenseFile_TryRead_NonExistentPath_ReturnsFalse()
{
bool result = LicenseFile.TryRead(@"C:\does\not\exist.lic", out string b64);
Assert.IsFalse(result);
Assert.IsNull(b64);
}
}
}
```
**NcProgramManager.Tests/NcProgramManager.Tests.csproj** — add 1 Compile entry:
```xml
<Compile Include="Unit\LicenseValidatorTests.cs" />
```
Avoid: creating real .lic files on disk in tests — use in-memory signing only.
Note: `InternalsVisibleTo("NcProgramManager.Tests")` already present in AssemblyInfo.cs
from Phase 5 — no change needed.
</action>
<verify>
```bash
ls NcProgramManager.Tests/Unit/LicenseValidatorTests.cs
grep "LicenseValidatorTests" NcProgramManager.Tests/NcProgramManager.Tests.csproj
grep "LicenseValidatorTests\|LicenseFile" NcProgramManager.Tests/Unit/LicenseValidatorTests.cs | head -5
```
File exists. Compile entry present. Class contains both LicenseValidator and LicenseFile tests.
</verify>
<done>AC-2 satisfied: valid license returns true. AC-3 satisfied: wrong machine returns false. AC-4 satisfied: missing file returns false.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `Program.cs` — checkLicense stays untouched until Phase 08
- `InputArgs.cs``-chiave=` field stays until Phase 08
- `AesCrypt.cs` — still used by program file encryption, not licensing
- All existing machine implementations (`Cnc/` folder)
- Existing test files
## SCOPE LIMITS
- No wiring into Program.cs (Phase 08)
- No license generator tool (Phase 09)
- No CLI changes (`-licfile=` arg is Phase 08)
- Public key stays as placeholder — real key generated in Phase 09
- `MachineFingerprint` is new class; do NOT delete WMI code from Program.cs yet
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `ls Licensing/*.cs` shows 5 files
- [ ] `grep "Compile" NcProgramManager.csproj | grep Licensing` shows 5 entries
- [ ] `ls NcProgramManager.Tests/Unit/LicenseValidatorTests.cs` exists
- [ ] `grep "LicenseValidatorTests" NcProgramManager.Tests/NcProgramManager.Tests.csproj` finds entry
- [ ] `grep "NcProgramManager.Licensing" Licensing/*.cs` — all files correct namespace
- [ ] All acceptance criteria met
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No changes to Program.cs, InputArgs.cs, or any existing .cs file outside Licensing/
- 6 new files created, 2 csproj files updated
</success_criteria>
<output>
After completion, create `.paul/phases/07-license-core/07-01-SUMMARY.md`
</output>

View file

@ -1,148 +0,0 @@
---
phase: 07-license-core
plan: 01
subsystem: licensing
tags: [rsa, cryptography, wmi, fingerprint, unit-tests, net472]
requires:
- phase: 06-rename-cleanup
provides: NcProgramManager namespace + renamed csproj files
provides:
- IMachineFingerprint interface + MachineFingerprint WMI implementation
- ILicenseValidator interface + LicenseValidator RSA-SHA256 verifier
- LicenseFile.TryRead helper
- 6 unit tests covering valid/invalid/tampered/missing license scenarios
affects:
- 08-license-integration (wires these into Program.cs)
- 09-license-generator (produces real key pair; PublicKeyXml PLACEHOLDER replaced here)
tech-stack:
added: []
patterns:
- Constructor injection for testability (internal ctor accepts public key XML)
- RSACryptoServiceProvider + SHA256CryptoServiceProvider for .NET 4.7.2 compat
key-files:
created:
- Licensing/IMachineFingerprint.cs
- Licensing/ILicenseValidator.cs
- Licensing/LicenseFile.cs
- Licensing/MachineFingerprint.cs
- Licensing/LicenseValidator.cs
- NcProgramManager.Tests/Unit/LicenseValidatorTests.cs
modified:
- NcProgramManager.csproj
- NcProgramManager.Tests/NcProgramManager.Tests.csproj
key-decisions:
- "RSA asymmetric: private key vendor-only, public key embedded in binary"
- "PublicKeyXml is PLACEHOLDER until Phase 09 generates real key pair"
- "Constructor injection: internal LicenseValidator(string publicKeyXml) for test isolation"
- "SHA256CryptoServiceProvider (not HashAlgorithmName) — .NET 4.7.2 RSACryptoServiceProvider overload"
patterns-established:
- "LicenseValidator(string publicKeyXml) internal ctor mirrors NcProgramValidator injection pattern"
- "TryRead out-param bool pattern: no exceptions escape from file I/O"
duration: ~2h (split across sessions)
started: 2026-05-17T00:00:00Z
completed: 2026-05-17T21:00:00Z
---
# Phase 07 Plan 01: license-core Summary
**RSA offline license core shipped: IMachineFingerprint + ILicenseValidator interfaces, WMI fingerprint reader, RSA-SHA256 verifier with injectable key, LicenseFile TryRead helper — 118 tests pass (116 run, 2 Fanuc FOCAS ignored).**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~2h |
| Started | 2026-05-17 |
| Completed | 2026-05-17 |
| Tasks | 3 completed |
| Files modified | 8 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Machine fingerprint readable via interface | Pass | MachineFingerprint : IMachineFingerprint — WMI logic extracted (not testable on Linux; verified via code review) |
| AC-2: Valid license passes validation | Pass | Validate_ValidLicense_ReturnsTrue — in-memory RSA sign+verify |
| AC-3: Invalid license rejected | Pass | Validate_WrongMachineFingerprint_ReturnsFalse, Validate_TamperedLicense_ReturnsFalse |
| AC-4: Corrupted/missing license file handled | Pass | LicenseFile_TryRead_NonExistentPath_ReturnsFalse |
## Accomplishments
- 5 new files in `Licensing/` — interfaces, implementations, file helper — clean DI-ready layer
- 6 LicenseValidator tests all pass in Docker (Mono 6.12 / .NET 4.7.2)
- PublicKeyXml PLACEHOLDER documented — Phase 09 replaces with real key pair
- Full test suite: 118 total, 116 pass, 2 ignored (Fanuc FOCAS, Windows-only DLL, expected)
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `Licensing/IMachineFingerprint.cs` | Created | Interface for fingerprint provider |
| `Licensing/ILicenseValidator.cs` | Created | Interface for RSA validator |
| `Licensing/LicenseFile.cs` | Created | TryRead: reads .lic base64, returns false on missing/empty |
| `Licensing/MachineFingerprint.cs` | Created | WMI: CPU ProcessorID + BaseBoard SerialNumber |
| `Licensing/LicenseValidator.cs` | Created | RSACryptoServiceProvider VerifyData with SHA256 |
| `NcProgramManager.Tests/Unit/LicenseValidatorTests.cs` | Created | 6 NUnit tests; generates ephemeral key pair in SetUp |
| `NcProgramManager.csproj` | Modified | +5 Compile entries for Licensing/*.cs |
| `NcProgramManager.Tests/NcProgramManager.Tests.csproj` | Modified | +1 Compile entry for LicenseValidatorTests.cs |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| RSA asymmetric (not AES) | Private key vendor-only; embedded public key safe to decompile | Phase 09 must generate real 2048-bit key pair |
| `RSACryptoServiceProvider` + `SHA256CryptoServiceProvider` | .NET 4.7.2: `VerifyData(byte[], object, byte[])` overload — `HashAlgorithmName` variant requires RSA base class | Locked to this API; no upgrade path issue |
| PublicKeyXml = PLACEHOLDER | Real key not yet generated; prevents Phase 07 from blocking on Phase 09 | Phase 09 outputs real XML; copy-paste into LicenseValidator.cs |
| Internal constructor injection | Testable without Windows WMI DLL | Same pattern as NcProgramValidator — consistent across codebase |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | Pre-existing bug, zero scope creep |
| Scope additions | 0 | — |
| Deferred | 0 | — |
### Auto-fixed Issues
**1. Build — Missing `using System;` in CncMachineFactoryTests.cs**
- **Found during:** Docker build (msbuild step)
- **Issue:** `InvalidOperationException` unresolved — `using System;` absent from `CncMachineFactoryTests.cs`
- **Fix:** Added `using System;` at top of file
- **Files:** `NcProgramManager.Tests/Unit/CncMachineFactoryTests.cs`
- **Verification:** Docker build passed; all 118 tests ran
**2. Dockerfile.test — Stale FanucProgramManager paths**
- **Found during:** First Docker build attempt
- **Issue:** Dockerfile.test still referenced `FanucProgramManager.*` paths from before Phase 06 rename
- **Fix:** Updated all paths to `NcProgramManager.*`
- **Files:** `Dockerfile.test`
- **Verification:** Docker build and test run succeeded
## Next Phase Readiness
**Ready:**
- `IMachineFingerprint` / `ILicenseValidator` injectable into any caller
- `LicenseValidator` verified correct with real RSA operations
- `LicenseFile.TryRead` ready for `-licfile=` arg wiring
**Concerns:**
- PublicKeyXml is PLACEHOLDER — production build will silently reject all licenses until Phase 09 provides real key and it's copied in
- `MachineFingerprint` uses WMI — only tested via code review on Linux; validate on Windows hardware
**Blockers:**
- None — Phase 08 can proceed
---
*Phase: 07-license-core, Plan: 01*
*Completed: 2026-05-17*

View file

@ -1,199 +0,0 @@
---
phase: 08-license-integration
plan: 01
type: execute
wave: 1
depends_on: ["07-01"]
files_modified:
- Program.cs
autonomous: true
---
<objective>
## Goal
Wire the Phase 07 RSA license core into Program.cs: replace the hardcoded AES `checkLicense` call with `MachineFingerprint` + `LicenseFile` + `LicenseValidator`. Keep `-chiave=` as the CLI arg — it now carries a path to a `.lic` file instead of the AES key string.
## Purpose
The current `checkLicense` embeds a hardcoded AES key/IV in source — anyone with the source or a decompiler can generate licenses for any machine. The Phase 07 RSA core is built but not yet wired in. This phase closes the gap: the app will reject any run where the `.lic` file (passed via `-chiave=`) doesn't contain a valid RSA-SHA256 signature matching the current machine's fingerprint.
## Output
- `Program.cs` only — `checkLicense` method deleted; license check replaced with `MachineFingerprint` + `LicenseFile.TryRead` + `LicenseValidator.Validate`; `inputArgs.chiave` now treated as `.lic` file path; help text for `-chiave=` updated to describe the file path; dead `using System.Management` and `using System.Security.Cryptography` removed; `using NcProgramManager.Licensing` added
- `InputArgs.cs` — NO CHANGE (`chiave` field stays as-is)
- Docker build + all 118 tests still pass
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/STATE.md
## Phase 07 Output (direct inputs)
@Licensing/IMachineFingerprint.cs
@Licensing/ILicenseValidator.cs
@Licensing/LicenseFile.cs
@Licensing/MachineFingerprint.cs
@Licensing/LicenseValidator.cs
## File Being Modified
@Program.cs
</context>
<acceptance_criteria>
## AC-1: -chiave= arg still parsed, value treated as .lic file path
```gherkin
Given Program.cs parseArgomenti is called with args containing "-chiave=C:\license.lic"
When the arg is parsed
Then inputArgs.chiave equals "C:\license.lic"
And the parsing code is unchanged from the current implementation
```
## AC-2: RSA license check used at startup
```gherkin
Given Program.cs Main is called
When the license check runs
Then it calls MachineFingerprint().GetFingerprint(), LicenseFile.TryRead(inputArgs.chiave), and LicenseValidator.Validate(fingerprint, base64)
And the old checkLicense method no longer exists
```
## AC-3: License failure exits with -100
```gherkin
Given the .lic file is missing OR the signature does not match the machine fingerprint
When Main runs
Then it prints "Licenza ERRATA", calls vediErrore(-100), and returns -1
```
## AC-4: Help text updated for -chiave=
```gherkin
Given MostraAiuto is called
When its output is inspected
Then -chiave= is documented as the path to the .lic license file
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Wire RSA license check into Program.cs</name>
<files>Program.cs</files>
<action>
**1. Replace usings at top of file:**
Remove these two lines:
```csharp
using System.Management;
using System.Security.Cryptography;
```
Add:
```csharp
using NcProgramManager.Licensing;
```
Keep all other existing usings unchanged.
**2. Replace the license check call in Main:**
Remove:
```csharp
if (!checkLicense(inputArgs.chiave))
{
Console.WriteLine("Licenza ERRATA");
vediErrore(-100);
return -1;
}
```
Replace with:
```csharp
string _licFingerprint = new MachineFingerprint().GetFingerprint();
string _licBase64;
if (!LicenseFile.TryRead(inputArgs.chiave, out _licBase64)
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64))
{
Console.WriteLine("Licenza ERRATA");
vediErrore(-100);
return -1;
}
```
`inputArgs.chiave` carries the `.lic` file path — parsing in `parseArgomenti` is unchanged.
**3. Delete the entire `checkLicense` static method:**
```csharp
static Boolean checkLicense(string chiave)
{
...
}
```
Remove from first line to closing brace entirely.
**4. Update MostraAiuto — update the description for `-chiave=`:**
Remove:
```csharp
Console.WriteLine("-chiave=");
Console.WriteLine(" la stringa chiave per eseguire il software.");
```
Replace with:
```csharp
Console.WriteLine("-chiave=");
Console.WriteLine(" Path del file di licenza (.lic) per autenticare il software.");
```
Avoid: touching `parseArgomenti` chiave parsing, `Scarica`, `Invia`, `vediErrore`, or any other part of the file.
</action>
<verify>
```bash
grep -n "checkLicense\|System\.Management\|System\.Security\.Cryptography" Program.cs
grep -n "MachineFingerprint\|LicenseFile\|LicenseValidator\|NcProgramManager\.Licensing" Program.cs
grep -n "chiave" Program.cs InputArgs.cs
```
First grep: zero hits.
Second grep: 4+ hits.
Third grep: `chiave` present in both files (parsing in Program.cs unchanged, field in InputArgs.cs unchanged).
</verify>
<done>AC-2 satisfied: RSA check wired. AC-3 satisfied: failure path unchanged (-1 return). AC-4 satisfied: help text updated. AC-1 satisfied: -chiave= parsing untouched.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `InputArgs.cs``chiave` field stays as-is; no rename
- `-chiave=` parsing in `parseArgomenti` — keep exactly as-is
- `AesCrypt.cs` — now dead code but removal is out of scope; leave for a cleanup phase
- `Licensing/*.cs` — Phase 07 output, must not be modified
- All `Cnc/` files
- All test files
## SCOPE LIMITS
- No changes to `Scarica`, `Invia`, `vediErrore` logic
- No changes to any other arg parsing in `parseArgomenti`
- No new test files (license core already tested in Phase 07)
- `AesCrypt.cs` stays in csproj and on disk
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `grep "checkLicense" Program.cs` — zero hits
- [ ] `grep "System.Management\|System.Security.Cryptography" Program.cs` — zero hits
- [ ] `grep "MachineFingerprint\|LicenseFile\|LicenseValidator" Program.cs` — 3 hits
- [ ] `grep "NcProgramManager.Licensing" Program.cs` — 1 hit
- [ ] `grep "chiave" InputArgs.cs` — still present (field unchanged)
- [ ] `grep "\-chiave=" Program.cs` — still present in parseArgomenti AND in MostraAiuto
- [ ] Docker build passes, all 118 tests pass
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- `checkLicense` method gone
- `-chiave=` CLI arg and `inputArgs.chiave` field unchanged
- RSA license check wired at startup using `inputArgs.chiave` as .lic path
- Help text describes `-chiave=` as the .lic file path
- No test regressions
</success_criteria>
<output>
After completion, create `.paul/phases/08-license-integration/08-01-SUMMARY.md`
</output>

View file

@ -1,63 +0,0 @@
---
phase: 08-license-integration
plan: 01
type: summary
status: complete
completed: 2026-05-17
---
# Phase 08 Summary: license-integration
## What Was Built
RSA license check wired into `Program.cs`. Old AES-based `checkLicense` deleted. License now validated at startup against `license.lic` in the EXE directory.
**Files modified:**
- `Program.cs` — license check replaced, `checkLicense` deleted, `-chiave=` parsing removed, `MostraAiuto` updated
- `InputArgs.cs``chiave` field removed
## Acceptance Criteria Results
| AC | Result | Notes |
|----|--------|-------|
| AC-1: -chiave= arg still parsed | **SUPERSEDED** | User directed removal mid-APPLY (see Deviations) |
| AC-2: RSA license check at startup | PASS | `MachineFingerprint` + `LicenseFile.TryRead` + `LicenseValidator.Validate` wired |
| AC-3: License failure exits -100 | PASS | `Console.WriteLine("Licenza ERRATA")` + `vediErrore(-100)` + `return -1` |
| AC-4: Help text updated | PASS | `-chiave=` block replaced with `license.lic` location note |
## Deviations
**Plan AC-1 superseded by user decision.**
Plan stated: keep `-chiave=` as the CLI arg carrying the `.lic` file path.
User directed mid-APPLY: remove `-chiave=` entirely; hardcode `license.lic` next to the EXE.
Actual license path:
```csharp
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
```
`inputArgs.chiave` field removed from `InputArgs.cs`. All `-chiave=` parsing removed from `parseArgomenti`.
## Verification (post-apply grep)
```
grep "checkLicense|chiave|System.Management|System.Security.Cryptography" Program.cs → 0 hits
grep "MachineFingerprint|LicenseFile|LicenseValidator|NcProgramManager.Licensing" Program.cs → 5 hits
grep "chiave" InputArgs.cs → 0 hits
```
## Decisions Made
- License file always at `AppDomain.CurrentDomain.BaseDirectory + "license.lic"` — no CLI override
- `InputArgs.chiave` field deleted (not just unused) — cleaner than leaving dead field
- `AesCrypt.cs` left on disk (still in csproj, no cleanup in scope)
## Deferred Issues
- Real RSA key pair — Phase 09 generates; copy `PublicKeyXml` into `LicenseValidator.cs`
- Phase 09: separate `LicenseGenerator` console app — reads WMI fingerprint, signs, writes `.lic`
- `AesCrypt.cs` dead code — remove in future cleanup phase
## Tests
No new tests (license core tested in Phase 07). Docker build + 116 tests pass (2 Fanuc FOCAS ignored, Windows-only DLL).

View file

@ -2,7 +2,7 @@
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace NcProgramManager namespace FanucProgramManager
{ {
class AesCrypt class AesCrypt
{ {

View file

@ -1,11 +1,11 @@
using System; using System;
using NcProgramManager.Cnc.Fanuc; using FanucProgramManager.Cnc.Fanuc;
using NcProgramManager.Cnc.Heidenhain; using FanucProgramManager.Cnc.Heidenhain;
using NcProgramManager.Cnc.Mitsubishi; using FanucProgramManager.Cnc.Mitsubishi;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Siemens; using FanucProgramManager.Cnc.Siemens;
namespace NcProgramManager.Cnc namespace FanucProgramManager.Cnc
{ {
internal static class CncMachineFactory internal static class CncMachineFactory
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
public sealed class FanucCapabilities public sealed class FanucCapabilities
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
public enum FanucTransport public enum FanucTransport
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
public enum FanucDialect public enum FanucDialect
{ {

View file

@ -1,6 +1,6 @@
using NcProgramManager; using FanucProgramManager;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
internal static class FanucDialectDetector internal static class FanucDialectDetector
{ {

View file

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using NcProgramManager; using FanucProgramManager;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
internal static class FanucErrorTranslator internal static class FanucErrorTranslator
{ {

View file

@ -5,11 +5,11 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NcProgramManager; using FanucProgramManager;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
public sealed class FanucMachine : IFanucMachine public sealed class FanucMachine : IFanucMachine
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
internal static class FocasDialectFactory internal static class FocasDialectFactory
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
internal interface IFocasDialect internal interface IFocasDialect
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
public static class IsoProgramFormatter public static class IsoProgramFormatter
{ {

View file

@ -1,6 +1,6 @@
using NcProgramManager; using FanucProgramManager;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
internal sealed class LegacyFocasDialect : IFocasDialect internal sealed class LegacyFocasDialect : IFocasDialect
{ {

View file

@ -1,7 +1,7 @@
using System.Text; using System.Text;
using NcProgramManager; using FanucProgramManager;
namespace NcProgramManager.Cnc.Fanuc namespace FanucProgramManager.Cnc.Fanuc
{ {
internal sealed class ModernFocasDialect : IFocasDialect internal sealed class ModernFocasDialect : IFocasDialect
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace NcProgramManager.Cnc.Heidenhain namespace FanucProgramManager.Cnc.Heidenhain
{ {
public sealed class HeidenhainConnectionConfig public sealed class HeidenhainConnectionConfig
{ {

View file

@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc.Heidenhain namespace FanucProgramManager.Cnc.Heidenhain
{ {
public sealed class HeidenhainMachine : ICncMachine public sealed class HeidenhainMachine : ICncMachine
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Heidenhain namespace FanucProgramManager.Cnc.Heidenhain
{ {
internal interface ILsv2Client internal interface ILsv2Client
{ {

View file

@ -2,7 +2,7 @@ using System;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace NcProgramManager.Cnc.Heidenhain namespace FanucProgramManager.Cnc.Heidenhain
{ {
/// <summary>LSV2 protocol client for Heidenhain TNC controls.</summary> /// <summary>LSV2 protocol client for Heidenhain TNC controls.</summary>
internal sealed class Lsv2Client : ILsv2Client, IDisposable internal sealed class Lsv2Client : ILsv2Client, IDisposable

View file

@ -1,9 +1,9 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc namespace FanucProgramManager.Cnc
{ {
public interface ICncMachine : IDisposable public interface ICncMachine : IDisposable
{ {

View file

@ -1,8 +1,8 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc namespace FanucProgramManager.Cnc
{ {
/// <summary>Fanuc-specific extensions: tool offset and work zero offset transfer.</summary> /// <summary>Fanuc-specific extensions: tool offset and work zero offset transfer.</summary>
public interface IFanucMachine : ICncMachine public interface IFanucMachine : ICncMachine

View file

@ -1,6 +1,6 @@
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc namespace FanucProgramManager.Cnc
{ {
/// <summary>Validates NC program content before upload to a CNC controller.</summary> /// <summary>Validates NC program content before upload to a CNC controller.</summary>
public interface INcProgramValidator public interface INcProgramValidator

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Mitsubishi namespace FanucProgramManager.Cnc.Mitsubishi
{ {
internal interface IMitsubishiFtpClient internal interface IMitsubishiFtpClient
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace NcProgramManager.Cnc.Mitsubishi namespace FanucProgramManager.Cnc.Mitsubishi
{ {
public sealed class MitsubishiConnectionConfig public sealed class MitsubishiConnectionConfig
{ {

View file

@ -3,7 +3,7 @@ using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
namespace NcProgramManager.Cnc.Mitsubishi namespace FanucProgramManager.Cnc.Mitsubishi
{ {
/// <summary>FTP client for Mitsubishi M-series CNC program transfer.</summary> /// <summary>FTP client for Mitsubishi M-series CNC program transfer.</summary>
internal sealed class MitsubishiFtpClient : IMitsubishiFtpClient internal sealed class MitsubishiFtpClient : IMitsubishiFtpClient

View file

@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc.Mitsubishi namespace FanucProgramManager.Cnc.Mitsubishi
{ {
public sealed class MitsubishiMachine : ICncMachine public sealed class MitsubishiMachine : ICncMachine
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public sealed class CncError public sealed class CncError
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public enum CncManufacturer { Fanuc, Heidenhain, Siemens, Mitsubishi } public enum CncManufacturer { Fanuc, Heidenhain, Siemens, Mitsubishi }
} }

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public sealed class CncProgram public sealed class CncProgram
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
/// <summary>Result wrapper for all CNC operations.</summary> /// <summary>Result wrapper for all CNC operations.</summary>
public sealed class CncResult<T> public sealed class CncResult<T>

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public enum ConnectionState public enum ConnectionState
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public enum MachineMode public enum MachineMode
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public sealed class MachineSnapshot public sealed class MachineSnapshot
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public enum MotionState public enum MotionState
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public enum RunState public enum RunState
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public sealed class ValidationError public sealed class ValidationError
{ {

View file

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public sealed class ValidationResult public sealed class ValidationResult
{ {

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Models namespace FanucProgramManager.Cnc.Models
{ {
public enum ValidationSeverity { Error, Warning } public enum ValidationSeverity { Error, Warning }
} }

View file

@ -1,9 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc namespace FanucProgramManager.Cnc
{ {
/// <summary>Validates NC program content per-manufacturer before upload.</summary> /// <summary>Validates NC program content per-manufacturer before upload.</summary>
public sealed class NcProgramValidator : INcProgramValidator public sealed class NcProgramValidator : INcProgramValidator

View file

@ -1,4 +1,4 @@
namespace NcProgramManager.Cnc.Siemens namespace FanucProgramManager.Cnc.Siemens
{ {
internal interface ISiemensFtpClient internal interface ISiemensFtpClient
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace NcProgramManager.Cnc.Siemens namespace FanucProgramManager.Cnc.Siemens
{ {
public sealed class SiemensConnectionConfig public sealed class SiemensConnectionConfig
{ {

View file

@ -3,7 +3,7 @@ using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
namespace NcProgramManager.Cnc.Siemens namespace FanucProgramManager.Cnc.Siemens
{ {
/// <summary>FTP client for Siemens Sinumerik CNC program transfer.</summary> /// <summary>FTP client for Siemens Sinumerik CNC program transfer.</summary>
internal sealed class SiemensFtpClient : ISiemensFtpClient internal sealed class SiemensFtpClient : ISiemensFtpClient

View file

@ -3,10 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Cnc.Siemens namespace FanucProgramManager.Cnc.Siemens
{ {
public sealed class SiemensMachine : ICncMachine public sealed class SiemensMachine : ICncMachine
{ {

View file

@ -6,16 +6,16 @@ WORKDIR /build
RUN cert-sync /etc/ssl/certs/ca-certificates.crt RUN cert-sync /etc/ssl/certs/ca-certificates.crt
# Copy solution + projects first for layer caching # Copy solution + projects first for layer caching
COPY NcProgramManager.csproj ./ COPY FanucProgramManager.csproj ./
COPY packages.config ./ COPY packages.config ./
COPY NcProgramManager.Tests/NcProgramManager.Tests.csproj ./NcProgramManager.Tests/ COPY FanucProgramManager.Tests/FanucProgramManager.Tests.csproj ./FanucProgramManager.Tests/
COPY NcProgramManager.Tests/packages.config ./NcProgramManager.Tests/ COPY FanucProgramManager.Tests/packages.config ./FanucProgramManager.Tests/
COPY NcProgramManager.sln ./ COPY FanucProgramManager.sln ./
# Restore all packages (sln now includes test project) # Restore all packages (sln now includes test project)
RUN nuget restore NcProgramManager.sln -NonInteractive RUN nuget restore FanucProgramManager.sln -NonInteractive
# Explicit fallback: restore test packages.config directly to same packages dir # Explicit fallback: restore test packages.config directly to same packages dir
RUN nuget restore NcProgramManager.Tests/packages.config \ RUN nuget restore FanucProgramManager.Tests/packages.config \
-OutputDirectory packages -NonInteractive -OutputDirectory packages -NonInteractive
# Grab NUnit console runner # Grab NUnit console runner
RUN nuget install NUnit.ConsoleRunner -Version 3.16.3 -OutputDirectory packages -NonInteractive RUN nuget install NUnit.ConsoleRunner -Version 3.16.3 -OutputDirectory packages -NonInteractive
@ -27,11 +27,11 @@ COPY . .
RUN ls Cnc/Fanuc/libs/ || true RUN ls Cnc/Fanuc/libs/ || true
# Build test project (Debug) # Build test project (Debug)
RUN msbuild NcProgramManager.Tests/NcProgramManager.Tests.csproj \ RUN msbuild FanucProgramManager.Tests/FanucProgramManager.Tests.csproj \
/p:Configuration=Debug /p:Platform=AnyCPU \ /p:Configuration=Debug /p:Platform=AnyCPU \
/v:minimal /nologo /v:minimal /nologo
# Run tests — skip Fanuc FOCAS tests (marked [Ignore] — need fwlib32.dll) # Run tests — skip Fanuc FOCAS tests (marked [Ignore] — need fwlib32.dll)
CMD mono packages/NUnit.ConsoleRunner.3.16.3/tools/nunit3-console.exe \ CMD mono packages/NUnit.ConsoleRunner.3.16.3/tools/nunit3-console.exe \
NcProgramManager.Tests/bin/Debug/NcProgramManager.Tests.dll \ FanucProgramManager.Tests/bin/Debug/FanucProgramManager.Tests.dll \
--labels=All --labels=All

1094
FANUCMachine.cs Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace NcProgramManager namespace FanucProgramManager
{ {
internal class FanucProgram internal class FanucProgram
{ {

View file

@ -7,8 +7,8 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid> <ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>NcProgramManager.Tests</RootNamespace> <RootNamespace>FanucProgramManager.Tests</RootNamespace>
<AssemblyName>NcProgramManager.Tests</AssemblyName> <AssemblyName>FanucProgramManager.Tests</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
@ -46,13 +46,12 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\NcProgramManager.csproj"> <ProjectReference Include="..\FanucProgramManager.csproj">
<Project>{1032188B-3321-42B2-87ED-343BF718CAF5}</Project> <Project>{1032188B-3321-42B2-87ED-343BF718CAF5}</Project>
<Name>NcProgramManager</Name> <Name>FanucProgramManager</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Unit\LicenseValidatorTests.cs" />
<Compile Include="Unit\NcProgramValidatorTests.cs" /> <Compile Include="Unit\NcProgramValidatorTests.cs" />
<Compile Include="Unit\FanucProgramParsingTests.cs" /> <Compile Include="Unit\FanucProgramParsingTests.cs" />
<Compile Include="Unit\IsoProgramFormatterTests.cs" /> <Compile Include="Unit\IsoProgramFormatterTests.cs" />

View file

@ -1,10 +1,10 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Heidenhain; using FanucProgramManager.Cnc.Heidenhain;
using NcProgramManager.Tests.Integration.Stubs; using FanucProgramManager.Tests.Integration.Stubs;
namespace NcProgramManager.Tests.Integration namespace FanucProgramManager.Tests.Integration
{ {
[TestFixture] [TestFixture]
[Category("Integration")] [Category("Integration")]

View file

@ -1,10 +1,10 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Mitsubishi; using FanucProgramManager.Cnc.Mitsubishi;
using NcProgramManager.Tests.Integration.Stubs; using FanucProgramManager.Tests.Integration.Stubs;
namespace NcProgramManager.Tests.Integration namespace FanucProgramManager.Tests.Integration
{ {
[TestFixture] [TestFixture]
[Category("Integration")] [Category("Integration")]

View file

@ -1,10 +1,10 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Siemens; using FanucProgramManager.Cnc.Siemens;
using NcProgramManager.Tests.Integration.Stubs; using FanucProgramManager.Tests.Integration.Stubs;
namespace NcProgramManager.Tests.Integration namespace FanucProgramManager.Tests.Integration
{ {
[TestFixture] [TestFixture]
[Category("Integration")] [Category("Integration")]

View file

@ -6,7 +6,7 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
namespace NcProgramManager.Tests.Integration.Stubs namespace FanucProgramManager.Tests.Integration.Stubs
{ {
/// <summary> /// <summary>
/// Minimal in-process FTP server using passive mode. /// Minimal in-process FTP server using passive mode.

View file

@ -6,7 +6,7 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
namespace NcProgramManager.Tests.Integration.Stubs namespace FanucProgramManager.Tests.Integration.Stubs
{ {
/// <summary> /// <summary>
/// Minimal in-process LSV2 server. /// Minimal in-process LSV2 server.

View file

@ -1,13 +1,12 @@
using System;
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Fanuc; using FanucProgramManager.Cnc.Fanuc;
using NcProgramManager.Cnc.Heidenhain; using FanucProgramManager.Cnc.Heidenhain;
using NcProgramManager.Cnc.Mitsubishi; using FanucProgramManager.Cnc.Mitsubishi;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Siemens; using FanucProgramManager.Cnc.Siemens;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class CncMachineFactoryTests public class CncMachineFactoryTests

View file

@ -1,10 +1,10 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using Moq; using Moq;
using NcProgramManager.Cnc.Fanuc; using FanucProgramManager.Cnc.Fanuc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
/// <summary> /// <summary>
/// FanucMachine calls real FOCAS DLL in ConnectCore and most methods guard /// FanucMachine calls real FOCAS DLL in ConnectCore and most methods guard

View file

@ -1,7 +1,7 @@
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager; using FanucProgramManager;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class FanucProgramParsingTests public class FanucProgramParsingTests

View file

@ -1,11 +1,11 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using Moq; using Moq;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Heidenhain; using FanucProgramManager.Cnc.Heidenhain;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class HeidenhainMachineTests public class HeidenhainMachineTests

View file

@ -1,7 +1,7 @@
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager.Cnc.Fanuc; using FanucProgramManager.Cnc.Fanuc;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class IsoProgramFormatterTests public class IsoProgramFormatterTests

View file

@ -1,11 +1,11 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using Moq; using Moq;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Mitsubishi; using FanucProgramManager.Cnc.Mitsubishi;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class MitsubishiMachineTests public class MitsubishiMachineTests

View file

@ -1,8 +1,8 @@
using NUnit.Framework; using NUnit.Framework;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class NcProgramValidatorTests public class NcProgramValidatorTests

View file

@ -1,11 +1,11 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using Moq; using Moq;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Cnc.Siemens; using FanucProgramManager.Cnc.Siemens;
namespace NcProgramManager.Tests.Unit namespace FanucProgramManager.Tests.Unit
{ {
[TestFixture] [TestFixture]
public class SiemensMachineTests public class SiemensMachineTests

View file

@ -6,8 +6,8 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1032188B-3321-42B2-87ED-343BF718CAF5}</ProjectGuid> <ProjectGuid>{1032188B-3321-42B2-87ED-343BF718CAF5}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>NcProgramManager</RootNamespace> <RootNamespace>FanucProgramManager</RootNamespace>
<AssemblyName>NcProgramManager</AssemblyName> <AssemblyName>FanucProgramManager</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
@ -75,6 +75,7 @@
<Compile Include="AesCrypt.cs" /> <Compile Include="AesCrypt.cs" />
<Compile Include="FanucProgram.cs" /> <Compile Include="FanucProgram.cs" />
<Compile Include="InputArgs.cs" /> <Compile Include="InputArgs.cs" />
<Compile Include="FANUCMachine.cs" />
<Compile Include="fwlib32.cs" /> <Compile Include="fwlib32.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -118,11 +119,6 @@
<Compile Include="Cnc\Mitsubishi\IMitsubishiFtpClient.cs" /> <Compile Include="Cnc\Mitsubishi\IMitsubishiFtpClient.cs" />
<Compile Include="Cnc\Mitsubishi\MitsubishiFtpClient.cs" /> <Compile Include="Cnc\Mitsubishi\MitsubishiFtpClient.cs" />
<Compile Include="Cnc\Mitsubishi\MitsubishiMachine.cs" /> <Compile Include="Cnc\Mitsubishi\MitsubishiMachine.cs" />
<Compile Include="Licensing\IMachineFingerprint.cs" />
<Compile Include="Licensing\ILicenseValidator.cs" />
<Compile Include="Licensing\LicenseFile.cs" />
<Compile Include="Licensing\MachineFingerprint.cs" />
<Compile Include="Licensing\LicenseValidator.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />

View file

@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496 VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NcProgramManager", "NcProgramManager.csproj", "{1032188B-3321-42B2-87ED-343BF718CAF5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FanucProgramManager", "FanucProgramManager.csproj", "{1032188B-3321-42B2-87ED-343BF718CAF5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NcProgramManager.Tests", "NcProgramManager.Tests\NcProgramManager.Tests.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FanucProgramManager.Tests", "FanucProgramManager.Tests\FanucProgramManager.Tests.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View file

@ -1,6 +1,6 @@
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
namespace NcProgramManager namespace FanucProgramManager
{ {
internal class InputArgs internal class InputArgs
{ {
@ -11,6 +11,7 @@ namespace NcProgramManager
public string porta = ""; public string porta = "";
public short pathCNC = 1; public short pathCNC = 1;
public int nodoHssb = -1; public int nodoHssb = -1;
public string chiave = "";
public string username = ""; public string username = "";
public string password = ""; public string password = "";
public string commentoProgramma = ""; public string commentoProgramma = "";

View file

@ -1,7 +0,0 @@
namespace NcProgramManager.Licensing
{
internal interface ILicenseValidator
{
bool Validate(string fingerprint, string licenseBase64);
}
}

View file

@ -1,7 +0,0 @@
namespace NcProgramManager.Licensing
{
internal interface IMachineFingerprint
{
string GetFingerprint();
}
}

View file

@ -1,26 +0,0 @@
using System.IO;
namespace NcProgramManager.Licensing
{
internal static class LicenseFile
{
internal static bool TryRead(string path, out string base64)
{
base64 = null;
try
{
if (!File.Exists(path))
return false;
string content = File.ReadAllText(path).Trim();
if (string.IsNullOrEmpty(content))
return false;
base64 = content;
return true;
}
catch
{
return false;
}
}
}
}

View file

@ -1,46 +0,0 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace NcProgramManager.Licensing
{
internal class LicenseValidator : ILicenseValidator
{
// Placeholder — replace with real vendor public key XML after Phase 09 generates the key pair.
// Generate key pair with the LicenseGenerator tool, then copy the public XML here.
private const string PublicKeyXml =
"<RSAKeyValue>" +
"<Modulus>PLACEHOLDER</Modulus>" +
"<Exponent>AQAB</Exponent>" +
"</RSAKeyValue>";
private readonly string _publicKeyXml;
// Uses embedded vendor public key.
internal LicenseValidator() : this(PublicKeyXml) { }
// Accepts any public key XML — used in unit tests.
internal LicenseValidator(string publicKeyXml)
{
_publicKeyXml = publicKeyXml;
}
public bool Validate(string fingerprint, string licenseBase64)
{
try
{
byte[] signature = Convert.FromBase64String(licenseBase64);
byte[] data = Encoding.UTF8.GetBytes(fingerprint);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(_publicKeyXml);
return rsa.VerifyData(data, new SHA256CryptoServiceProvider(), signature);
}
}
catch
{
return false;
}
}
}
}

View file

@ -1,34 +0,0 @@
using System.Management;
namespace NcProgramManager.Licensing
{
internal class MachineFingerprint : IMachineFingerprint
{
public string GetFingerprint()
{
string pcInfo = string.Empty;
using (var mc = new ManagementClass("win32_processor"))
using (ManagementObjectCollection moc = mc.GetInstances())
{
foreach (ManagementObject mo in moc)
{
pcInfo = mo.Properties["processorID"].Value.ToString();
break;
}
}
using (var searcher = new ManagementObjectSearcher(
"root\\CIMV2", "SELECT * FROM Win32_BaseBoard"))
{
foreach (ManagementObject obj in searcher.Get())
{
pcInfo += obj["SerialNumber"].ToString();
break;
}
}
return pcInfo;
}
}
}

View file

@ -1,80 +0,0 @@
using System;
using System.Security.Cryptography;
using System.Text;
using NUnit.Framework;
using NcProgramManager.Licensing;
namespace NcProgramManager.Tests.Unit
{
[TestFixture]
internal class LicenseValidatorTests
{
private RSACryptoServiceProvider _rsa;
private string _publicKeyXml;
private LicenseValidator _validator;
[SetUp]
public void SetUp()
{
_rsa = new RSACryptoServiceProvider(2048);
_publicKeyXml = _rsa.ToXmlString(false);
_validator = new LicenseValidator(_publicKeyXml);
}
[TearDown]
public void TearDown()
{
_rsa.Dispose();
}
private string Sign(string fingerprint)
{
byte[] data = Encoding.UTF8.GetBytes(fingerprint);
byte[] sig = _rsa.SignData(data, new SHA256CryptoServiceProvider());
return Convert.ToBase64String(sig);
}
[Test]
public void Validate_ValidLicense_ReturnsTrue()
{
string fingerprint = "CPU123BOARD456";
string license = Sign(fingerprint);
Assert.IsTrue(_validator.Validate(fingerprint, license));
}
[Test]
public void Validate_WrongMachineFingerprint_ReturnsFalse()
{
string license = Sign("CPU123BOARD456");
Assert.IsFalse(_validator.Validate("DIFFERENT_MACHINE", license));
}
[Test]
public void Validate_TamperedLicense_ReturnsFalse()
{
string license = Sign("CPU123BOARD456");
string tampered = license.Substring(0, license.Length - 4) + "AAAA";
Assert.IsFalse(_validator.Validate("CPU123BOARD456", tampered));
}
[Test]
public void Validate_EmptyLicense_ReturnsFalse()
{
Assert.IsFalse(_validator.Validate("CPU123BOARD456", string.Empty));
}
[Test]
public void Validate_NotBase64_ReturnsFalse()
{
Assert.IsFalse(_validator.Validate("CPU123BOARD456", "not-base64!!!"));
}
[Test]
public void LicenseFile_TryRead_NonExistentPath_ReturnsFalse()
{
bool result = LicenseFile.TryRead(@"C:\nonexistent\path\license.lic", out string base64);
Assert.IsFalse(result);
Assert.IsNull(base64);
}
}
}

View file

@ -1,12 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Management;
using System.Security.Cryptography;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NcProgramManager.Cnc; using FanucProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using FanucProgramManager.Cnc.Models;
using NcProgramManager.Licensing;
namespace NcProgramManager namespace FanucProgramManager
{ {
internal class Program internal class Program
{ {
@ -34,11 +35,7 @@ namespace NcProgramManager
return 0; return 0;
} }
string _licFingerprint = new MachineFingerprint().GetFingerprint(); if (!checkLicense(inputArgs.chiave))
string _licBase64;
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
if (!LicenseFile.TryRead(_licPath, out _licBase64)
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64))
{ {
Console.WriteLine("Licenza ERRATA"); Console.WriteLine("Licenza ERRATA");
vediErrore(-100); vediErrore(-100);
@ -278,6 +275,47 @@ namespace NcProgramManager
Console.ReadLine(); Console.ReadLine();
} }
static Boolean checkLicense(string chiave)
{
string pcInfo = string.Empty;
ManagementClass mc = new ManagementClass("win32_processor");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
//Get only the first CPU's ID
pcInfo = mo.Properties["processorID"].Value.ToString();
}
ManagementObjectSearcher baseboardSearcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_BaseBoard");
foreach (ManagementObject queryObj in baseboardSearcher.Get())
{
//Get motherboardSerialNumber
pcInfo += queryObj["SerialNumber"].ToString();
break;
}
using (Aes myAes = Aes.Create())
{
if (chiave == "")
return false;
myAes.IV = Convert.FromBase64String("qqxNM8Li99Cwx61VX/QoWA==");
myAes.Key = Convert.FromBase64String("7/12mP3dFLGqPGjujlwBCVblURxBaBcGGQ2c2FatfP4=");
byte[] encrypted = AesCrypt.EncryptStringToBytes_Aes(pcInfo, myAes.Key, myAes.IV);
string roundtrip = AesCrypt.DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
string decoded = "";
try
{
decoded = AesCrypt.DecryptStringFromBytes_Aes(Convert.FromBase64String(chiave), myAes.Key, myAes.IV);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return pcInfo == decoded;
}
}
static void MostraAiuto() static void MostraAiuto()
{ {
Console.WriteLine("Utilizzo del Software gestore Programmi FANUC"); Console.WriteLine("Utilizzo del Software gestore Programmi FANUC");
@ -288,7 +326,8 @@ namespace NcProgramManager
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= "); Console.WriteLine("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= ");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Il file 'license.lic' deve essere posizionato nella stessa cartella dell'eseguibile."); Console.WriteLine("-chiave=");
Console.WriteLine(" la stringa chiave per eseguire il software.");
Console.WriteLine("-azione="); Console.WriteLine("-azione=");
Console.WriteLine(" l'{AZIONE} da eseguire."); Console.WriteLine(" l'{AZIONE} da eseguire.");
Console.WriteLine("-manufacturer="); Console.WriteLine("-manufacturer=");
@ -346,6 +385,8 @@ namespace NcProgramManager
inputArgs.mostraHelp = true; inputArgs.mostraHelp = true;
return; return;
} }
var chiave = arguments.Find(it => new Regex("-chiave=.*", RegexOptions.IgnoreCase).IsMatch(it));
inputArgs.chiave = chiave.Substring(8);
var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it)); var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it));
inputArgs.azione = azione.Substring(8); inputArgs.azione = azione.Substring(8);
var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it)); var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it));

View file

@ -2,17 +2,17 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
[assembly: InternalsVisibleTo("NcProgramManager.Tests")] [assembly: InternalsVisibleTo("FanucProgramManager.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
// Le informazioni generali relative a un assembly sono controllate dal seguente // Le informazioni generali relative a un assembly sono controllate dal seguente
// set di attributi. Modificare i valori di questi attributi per modificare le informazioni // set di attributi. Modificare i valori di questi attributi per modificare le informazioni
// associate a un assembly. // associate a un assembly.
[assembly: AssemblyTitle("NcProgramManager")] [assembly: AssemblyTitle("FanucProgramManager")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NcProgramManager")] [assembly: AssemblyProduct("FanucProgramManager")]
[assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]