docs: map existing codebase
- STACK.md - Technologies and dependencies (.NET 4.7.2, NuGet, Costura.Fody) - ARCHITECTURE.md - Layered CLI with ICncMachine factory pattern - STRUCTURE.md - Directory layout and where to add new code - CONVENTIONS.md - C# style, naming, result wrapper pattern - TESTING.md - NUnit 3 + Moq, stubs, ignored FOCAS tests - INTEGRATIONS.md - FOCAS, LSV2, FTP, RSA licensing - CONCERNS.md - Blocking async, silent catch, no logging, Windows-only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f55d0b313b
commit
dabcf989d9
7 changed files with 913 additions and 0 deletions
156
.paul/codebase/ARCHITECTURE.md
Normal file
156
.paul/codebase/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## Pattern Overview
|
||||||
|
|
||||||
|
**Overall:** Layered CLI console application with abstract machine interface, factory dispatch, and offline license gate.
|
||||||
|
|
||||||
|
**Key Characteristics:**
|
||||||
|
- Single Windows EXE (all dependencies embedded via Costura.Fody)
|
||||||
|
- Polymorphic multi-vendor CNC support via `ICncMachine` interface
|
||||||
|
- Factory pattern for manufacturer selection at runtime
|
||||||
|
- Async/await throughout (Task-based), but blocked at CLI boundary via `.GetAwaiter().GetResult()`
|
||||||
|
- Result-wrapper pattern (`CncResult<T>`) instead of exceptions for CNC operations
|
||||||
|
- RSA license validation gate on every startup
|
||||||
|
|
||||||
|
## Layers
|
||||||
|
|
||||||
|
**CLI Entry Layer:**
|
||||||
|
- Purpose: Parse args, validate license, dispatch to action (Scarica/Invia)
|
||||||
|
- Contains: `Program.cs` (main orchestration), `ArgParser.cs` (regex arg parsing), `InputArgs.cs` (config object)
|
||||||
|
- Depends on: Licensing layer, CNC abstraction layer
|
||||||
|
- Error output: Negative exit codes (-100 to -208) mapped to Italian user messages
|
||||||
|
|
||||||
|
**Licensing Layer:**
|
||||||
|
- Purpose: Prevent execution on unlicensed machines
|
||||||
|
- Contains: `Licensing/LicenseValidator.cs`, `Licensing/MachineFingerprint.cs`, `Licensing/LicenseFile.cs`
|
||||||
|
- Depends on: WMI (System.Management), RSA crypto
|
||||||
|
- Used by: CLI entry layer (startup check only)
|
||||||
|
|
||||||
|
**CNC Abstraction Layer:**
|
||||||
|
- Purpose: Uniform interface over 4 different CNC protocols
|
||||||
|
- Contains: `Cnc/ICncMachine.cs`, `Cnc/IFanucMachine.cs`, `Cnc/CncMachineFactory.cs`, `Cnc/Models/`
|
||||||
|
- Key types: `CncResult<T>`, `CncError`, `CncManufacturer`, `ConnectionState`, `CncProgram`
|
||||||
|
- Used by: CLI entry layer
|
||||||
|
|
||||||
|
**Manufacturer Implementations:**
|
||||||
|
- Purpose: Protocol-specific CNC communication
|
||||||
|
- Contains: `Cnc/Fanuc/`, `Cnc/Heidenhain/`, `Cnc/Siemens/`, `Cnc/Mitsubishi/`
|
||||||
|
- Each implements `ICncMachine` (Fanuc also implements `IFanucMachine`)
|
||||||
|
- Depends on: Protocol libraries (FOCAS DLL, TCP socket, FtpWebRequest)
|
||||||
|
|
||||||
|
**Validation Layer:**
|
||||||
|
- Purpose: Pre-upload NC program syntax checking
|
||||||
|
- Contains: `Cnc/INcProgramValidator.cs`, `Cnc/NcProgramValidator.cs`
|
||||||
|
- Injected into machine implementations; upload blocked on Error severity
|
||||||
|
- Depends on: Nothing external
|
||||||
|
|
||||||
|
**NC Program Parsing:**
|
||||||
|
- Purpose: Parse/format Fanuc composite NC file format
|
||||||
|
- Contains: `FanucProgram.cs` (program + tooloffset + workzero sections)
|
||||||
|
- Used by: CLI entry layer post-download
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
**Scarica (Download from CNC):**
|
||||||
|
```
|
||||||
|
CLI args
|
||||||
|
→ ArgParser.Parse() → InputArgs
|
||||||
|
→ LicenseValidator.Validate() [exit -1xx on failure]
|
||||||
|
→ CncMachineFactory.Create(InputArgs) → ICncMachine
|
||||||
|
→ machine.ConnectAsync() → CncResult<bool>
|
||||||
|
→ machine.ReadProgramAsync(path) → CncResult<string>
|
||||||
|
→ [IFanucMachine cast] ReadToolDataAsync / ReadWorkZeroDataAsync
|
||||||
|
→ FanucProgram.Format() → composite file string
|
||||||
|
→ File.WriteAllText(localPath)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Invia (Upload to CNC):**
|
||||||
|
```
|
||||||
|
CLI args
|
||||||
|
→ ArgParser.Parse() → InputArgs
|
||||||
|
→ LicenseValidator.Validate() [exit -1xx on failure]
|
||||||
|
→ CncMachineFactory.Create(InputArgs) → ICncMachine
|
||||||
|
→ File.ReadAllText(localPath)
|
||||||
|
→ NcProgramValidator.Validate() [exit on Error severity]
|
||||||
|
→ machine.ConnectAsync()
|
||||||
|
→ machine.WriteProgramAsync(path, content) → CncResult<bool>
|
||||||
|
```
|
||||||
|
|
||||||
|
**State Management:**
|
||||||
|
- No persistent in-memory state between invocations (each run is independent)
|
||||||
|
- Machine state tracked per-instance: `ConnectionState` enum + `StateChanged` event
|
||||||
|
- Thread safety: `SemaphoreSlim` gate per machine instance
|
||||||
|
|
||||||
|
## Key Abstractions
|
||||||
|
|
||||||
|
**ICncMachine:**
|
||||||
|
- Purpose: Uniform contract for all CNC machines
|
||||||
|
- Location: `Cnc/ICncMachine.cs`
|
||||||
|
- Methods: `ConnectAsync`, `DisconnectAsync`, `ReadProgramAsync`, `WriteProgramAsync`, `ReadAsync`, `DeleteProgramAsync`, `ReadActiveProgramAsync`, `SelectMainProgramAsync`
|
||||||
|
- Pattern: Interface + factory dispatch
|
||||||
|
|
||||||
|
**IFanucMachine:**
|
||||||
|
- Purpose: Fanuc-specific extensions (tool offsets, work zero)
|
||||||
|
- Location: `Cnc/IFanucMachine.cs`
|
||||||
|
- Extends: `ICncMachine`
|
||||||
|
- Pattern: Interface specialization
|
||||||
|
|
||||||
|
**CncResult<T>:**
|
||||||
|
- Purpose: Typed result wrapper replaces exception-based control flow
|
||||||
|
- Location: `Cnc/Models/CncResult.cs`
|
||||||
|
- Factories: `Ok()`, `Fail()`, `Cancelled()`, `OkWithWarnings()`
|
||||||
|
- Properties: `Success`, `Errors`, `WasCancelled`, `Value`
|
||||||
|
|
||||||
|
**IFocasDialect:**
|
||||||
|
- Purpose: Abstract FOCAS version differences (Legacy vs Modern API)
|
||||||
|
- Location: `Cnc/Fanuc/IFocasDialect.cs`
|
||||||
|
- Implementations: `LegacyFocasDialect.cs`, `ModernFocasDialect.cs`
|
||||||
|
- Detection: `FanucDialectDetector.cs` at connect time
|
||||||
|
|
||||||
|
**CncMachineFactory:**
|
||||||
|
- Purpose: Create correct ICncMachine from InputArgs
|
||||||
|
- Location: `Cnc/CncMachineFactory.cs`
|
||||||
|
- Dispatches on: `CncManufacturer` enum (Fanuc, Heidenhain, Siemens, Mitsubishi)
|
||||||
|
|
||||||
|
## Entry Points
|
||||||
|
|
||||||
|
**CLI Entry:**
|
||||||
|
- Location: `Program.cs` (Main method)
|
||||||
|
- Triggers: Process execution from command line or calling application
|
||||||
|
- Responsibilities: Arg parsing → license check → machine factory → action dispatch → exit code
|
||||||
|
|
||||||
|
**Argument Parser:**
|
||||||
|
- Location: `ArgParser.cs` (static `Parse(string[])`)
|
||||||
|
- Returns: `InputArgs` with all 16 CLI parameters normalized
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**Strategy:** Result wrapper at CNC layer, exit codes at CLI boundary, ArgumentNullException for null guards
|
||||||
|
|
||||||
|
**Patterns:**
|
||||||
|
- `CncResult<T>.Fail(errors)` for CNC operation failures
|
||||||
|
- `ArgumentNullException` for constructor null guards (`Cnc/Fanuc/FanucMachine.cs`)
|
||||||
|
- Exit codes -100 to -208 in `Program.cs` for user-facing errors
|
||||||
|
- `try/finally` with `connected` flag for connection leak prevention (`Program.cs`)
|
||||||
|
- `catch { }` (silent) on disconnect cleanup paths — known weakness (see CONCERNS.md)
|
||||||
|
|
||||||
|
## Cross-Cutting Concerns
|
||||||
|
|
||||||
|
**Logging:**
|
||||||
|
- `Console.WriteLine` only — no structured logging framework
|
||||||
|
- Debug mode (`-debug=` arg): keeps terminal open after operation
|
||||||
|
|
||||||
|
**Validation:**
|
||||||
|
- `NcProgramValidator` injected into machine constructors
|
||||||
|
- Runs before upload; blocked on `ValidationSeverity.Error`
|
||||||
|
|
||||||
|
**Cancellation:**
|
||||||
|
- `CancellationToken` passed through all async methods
|
||||||
|
- CLI does not currently expose cancellation to user
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Architecture analysis: 2026-05-18*
|
||||||
|
*Update when major patterns change*
|
||||||
152
.paul/codebase/CONCERNS.md
Normal file
152
.paul/codebase/CONCERNS.md
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
# Codebase Concerns
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## Tech Debt
|
||||||
|
|
||||||
|
**Blocking async calls at CLI boundary:**
|
||||||
|
- Issue: 56+ `.GetAwaiter().GetResult()` calls block async operations synchronously
|
||||||
|
- Files: `Program.cs` (lines 90, 101, 122, 142, 166, 200, 221, 240, 262), machine classes
|
||||||
|
- Why: Main() was not async when initially written; .NET 4.7.2 supports async Main via C# 7.1
|
||||||
|
- Impact: Thread pool starvation risk, deadlock risk in certain SynchronizationContext scenarios, harder debugging
|
||||||
|
- Fix: Refactor `Program.Main` to `static async Task<int> Main(string[] args)`, propagate `await` throughout
|
||||||
|
|
||||||
|
**Silent exception handlers (catch without logging):**
|
||||||
|
- Issue: `try { ... } catch { }` swallows exceptions with no trace
|
||||||
|
- Files: `Cnc/Fanuc/FanucMachine.cs` (disconnect cleanup), `Cnc/Siemens/SiemensMachine.cs`, `Cnc/Heidenhain/HeidenhainMachine.cs`, `Cnc/Heidenhain/Lsv2Client.cs`
|
||||||
|
- Why: Cleanup paths (disconnect, logout) should not propagate to caller; silence was intentional
|
||||||
|
- Impact: Resource leaks masked, production issues undiagnosable, disconnect failures invisible
|
||||||
|
- Fix: Log at minimum to debug/trace: `catch (Exception ex) { /* log ex */ }`. Add logging framework.
|
||||||
|
|
||||||
|
**No structured logging framework:**
|
||||||
|
- Issue: Entire codebase uses `Console.WriteLine` (98+ calls in `Program.cs` alone)
|
||||||
|
- Files: `Program.cs`, all machine implementations
|
||||||
|
- Why: MVP simplicity; tool was always a CLI
|
||||||
|
- Impact: No log levels, no timestamps, no file logging, production issues undiagnosable
|
||||||
|
- Fix: Add Serilog or NLog. Replace Console.WriteLine with `_logger.Information()`. Add request/response tracing to FOCAS calls.
|
||||||
|
|
||||||
|
**Large monolithic FanucMachine:**
|
||||||
|
- Issue: `Cnc/Fanuc/FanucMachine.cs` is ~740 lines with complex state management
|
||||||
|
- Files: `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Why: FOCAS protocol has many edge cases; chunked transfer and offset handling grew organically
|
||||||
|
- Impact: Hard to test individual branches; buffer handling edge cases unclear
|
||||||
|
- Fix: Extract `IFocasTransferHandler` for chunk upload/download logic; separate offset data reader
|
||||||
|
|
||||||
|
**`string.Format()` over `$""` interpolation:**
|
||||||
|
- Issue: Old string formatting style throughout validator and some machine code
|
||||||
|
- Files: `Cnc/NcProgramValidator.cs`, `Program.cs`
|
||||||
|
- Why: Legacy code predates C# 6 adoption on this project
|
||||||
|
- Impact: Cosmetic; harder to read format strings
|
||||||
|
- Fix: Opportunistic migration to `$""` during edits
|
||||||
|
|
||||||
|
## Known Issues / Incomplete Implementations
|
||||||
|
|
||||||
|
**Heidenhain ReadActiveProgramAsync / SelectMainProgram not implemented:**
|
||||||
|
- Symptoms: Returns `CncResult.Fail("Not supported via basic LSV2")`
|
||||||
|
- Files: `Cnc/Heidenhain/HeidenhainMachine.cs`
|
||||||
|
- Root cause: LSV2 protocol complexity; feature not required for initial release
|
||||||
|
- Workaround: Users specify program path explicitly
|
||||||
|
|
||||||
|
**Siemens ReadActiveProgramAsync / SelectMainProgram not implemented:**
|
||||||
|
- Symptoms: Returns stub failure
|
||||||
|
- Files: `Cnc/Siemens/SiemensMachine.cs`
|
||||||
|
- Root cause: FTP protocol doesn't expose active program info
|
||||||
|
- Workaround: Users specify program path explicitly
|
||||||
|
|
||||||
|
**MachineSnapshot returns stub data for Heidenhain/Siemens/Mitsubishi:**
|
||||||
|
- Files: `Cnc/Heidenhain/HeidenhainMachine.cs`, `Cnc/Siemens/SiemensMachine.cs`, `Cnc/Mitsubishi/MitsubishiMachine.cs`
|
||||||
|
- Issue: All snapshot fields return Unknown/empty; no real machine state read
|
||||||
|
- Impact: No diagnostic info available for non-Fanuc machines
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
**RSA public key hardcoded in binary:**
|
||||||
|
- Risk: Key embedded as const string in binary; cannot be rotated without recompile and redeploy
|
||||||
|
- Files: `Licensing/LicenseValidator.cs`
|
||||||
|
- Current mitigation: Asymmetric RSA — private key is vendor-only; hardcoded public key is acceptable for this use case
|
||||||
|
- Recommendation: Document key rotation procedure; consider adding license version/expiration metadata for future revocation
|
||||||
|
|
||||||
|
**MachineFingerprint null dereference on unusual hardware:**
|
||||||
|
- Risk: `mo.Properties["processorID"].Value.ToString()` throws NullReferenceException if WMI property missing
|
||||||
|
- Files: `Licensing/MachineFingerprint.cs`
|
||||||
|
- Current mitigation: None
|
||||||
|
- Fix: `mo.Properties["processorID"]?.Value?.ToString() ?? "unknown"`
|
||||||
|
|
||||||
|
**Obsolete crypto APIs:**
|
||||||
|
- Risk: `RSACryptoServiceProvider` and `SHA256CryptoServiceProvider` are obsolete in .NET 6+
|
||||||
|
- Files: `Licensing/LicenseValidator.cs`
|
||||||
|
- Current mitigation: .NET 4.7.2 target — obsolete warning only, not broken
|
||||||
|
- Fix when upgrading: Use `RSA.Create()` + `VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)`
|
||||||
|
|
||||||
|
**SHA256 provider not explicitly disposed:**
|
||||||
|
- Risk: Minor resource leak in license validation path
|
||||||
|
- Files: `Licensing/LicenseValidator.cs`
|
||||||
|
- Fix: Wrap in `using (var sha = new SHA256CryptoServiceProvider()) { ... }`
|
||||||
|
|
||||||
|
## Platform Constraints
|
||||||
|
|
||||||
|
**Windows-only (hard constraint):**
|
||||||
|
- FOCAS1 DLL (`Fwlib32.dll`) is 32-bit Windows native — no Linux/macOS support
|
||||||
|
- WMI (`System.Management`) Windows-only — licensing system non-functional on Linux
|
||||||
|
- Files: `Cnc/Fanuc/libs/`, `Licensing/MachineFingerprint.cs`, `fwlib32.cs`
|
||||||
|
- Impact: Cannot run on Linux/macOS; CI on Linux can only test non-FOCAS paths
|
||||||
|
- Action if cross-platform needed: Abstract fingerprinting behind `IMachineFingerprint`; already done — add Linux implementation using `/proc/cpuinfo` or `dmidecode`
|
||||||
|
|
||||||
|
**FOCAS DLL version sensitivity:**
|
||||||
|
- Risk: FOCAS API changes between Fanuc control versions; `FanucDialectDetector` auto-detects but may fail on unknown versions
|
||||||
|
- Files: `Cnc/Fanuc/FanucDialectDetector.cs`, `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Mitigation: Dialect abstraction layer (`IFocasDialect`) isolates version differences
|
||||||
|
|
||||||
|
## Performance Bottlenecks
|
||||||
|
|
||||||
|
**In-memory accumulation for large NC programs:**
|
||||||
|
- Issue: `UploadProgramByIdentifier` appends to `StringBuilder` for entire program before writing
|
||||||
|
- Files: `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Impact: For programs >10MB, high memory allocation; streaming to file would be more efficient
|
||||||
|
- Fix: Write chunks directly to `FileStream` instead of accumulating in memory
|
||||||
|
|
||||||
|
**Polling with 100ms busy-wait during transfer:**
|
||||||
|
- Issue: `ct.WaitHandle.WaitOne(100)` loops during buffer waits
|
||||||
|
- Files: `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Impact: Wastes thread-pool slot; CPU cycles during transfer
|
||||||
|
- Fix: Use `await Task.Delay(100, ct)` instead of blocking WaitOne
|
||||||
|
|
||||||
|
**Network connection timeout via `.Wait()` with boolean return:**
|
||||||
|
- Issue: `_tcp.ConnectAsync().Wait(timeout)` — if task never completes, does not abort it
|
||||||
|
- Files: `Cnc/Heidenhain/Lsv2Client.cs`
|
||||||
|
- Fix: `await Task.WhenAny(connectTask, Task.Delay(timeout, ct))` + cancellation
|
||||||
|
|
||||||
|
## Fragile Areas
|
||||||
|
|
||||||
|
**FOCAS dialect detection (`FanucDialectDetector`):**
|
||||||
|
- Files: `Cnc/Fanuc/FanucDialectDetector.cs`
|
||||||
|
- Why fragile: Relies on probing specific FOCAS API calls to detect version; untested against full range of Fanuc control generations
|
||||||
|
- Safe modification: Always test against physical hardware after changes; log detected dialect version
|
||||||
|
|
||||||
|
**License validation (no recovery path):**
|
||||||
|
- Files: `Licensing/LicenseValidator.cs`, `Licensing/MachineFingerprint.cs`
|
||||||
|
- Why fragile: Any WMI failure or file read error produces a hard exit with no diagnostic for user
|
||||||
|
- Safe modification: Add exception handling with user-facing error message; distinguish "no license file" from "invalid license" from "fingerprint failure"
|
||||||
|
|
||||||
|
**Integration tests with hardcoded IPs:**
|
||||||
|
- Files: `NcProgramManager.Tests/Integration/SiemensMachineIntegrationTests.cs`, `HeidenhainMachineIntegrationTests.cs`, `MitsubishiMachineIntegrationTests.cs`
|
||||||
|
- Why fragile: Tests will fail in CI without physical hardware at specific IPs
|
||||||
|
- Fix: Make IPs configurable via environment variables; skip integration tests in CI by default
|
||||||
|
|
||||||
|
## Missing / Not Covered
|
||||||
|
|
||||||
|
- No CLI-level tests (no test for `Program.cs` orchestration or exit code behavior)
|
||||||
|
- No tests for `MachineFingerprint` (WMI, no mock tests)
|
||||||
|
- No tests for `LicenseFile` parsing edge cases (corrupt Base64, empty file)
|
||||||
|
- No structured logging or diagnostics framework
|
||||||
|
- No error code documentation in code (only in `README.md`)
|
||||||
|
- No architecture documentation (FOCAS dialect selection logic undocumented in code)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Overall Assessment:** Architecture is solid — clean machine abstraction, well-structured interfaces, good error propagation via `CncResult<T>`. Main concerns are operational (logging, blocking async, empty catch blocks) rather than structural. Platform constraints (Windows, FOCAS DLL) are intentional for the target environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Concerns analysis: 2026-05-18*
|
||||||
|
*Update as issues are resolved or discovered*
|
||||||
122
.paul/codebase/CONVENTIONS.md
Normal file
122
.paul/codebase/CONVENTIONS.md
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
# Coding Conventions
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## Naming Patterns
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- PascalCase for all `.cs` files: `FanucMachine.cs`, `CncResult.cs`
|
||||||
|
- Interfaces: I-prefix PascalCase: `ICncMachine.cs`, `IFocasDialect.cs`
|
||||||
|
- Tests: `{Subject}Tests.cs` or `{Subject}IntegrationTests.cs`
|
||||||
|
- Config classes: `{Manufacturer}ConnectionConfig.cs`
|
||||||
|
|
||||||
|
**Classes:**
|
||||||
|
- PascalCase for all classes
|
||||||
|
- Sealed preferred for implementations (not open to inheritance)
|
||||||
|
- Examples: `NcProgramValidator.cs`, `CncResult.cs`
|
||||||
|
- Manufacturer-prefixed where relevant: `FanucMachine`, `HeidenhainMachine`, `SiemensMachine`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- I-prefix strictly followed: `ICncMachine`, `IFanucMachine`, `ILicenseValidator`, `IFocasDialect`
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
- Private: `_camelCase` with underscore prefix
|
||||||
|
- Example: `_config`, `_validator`, `_state`, `_hndl` in `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Constants (public): PascalCase — `AlarmSlots`, `TransferChunkSize` in `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Constants (private): can be UPPER_CASE or PascalCase
|
||||||
|
- Static readonly: `_camelCase` with underscore
|
||||||
|
|
||||||
|
**Properties:**
|
||||||
|
- PascalCase, read-only preferred
|
||||||
|
- Example: `Name`, `Comment`, `Content`, `Path` in `Cnc/Models/CncProgram.cs`
|
||||||
|
|
||||||
|
**Methods:**
|
||||||
|
- PascalCase for all public methods: `ConnectAsync()`, `ReadProgramAsync()`
|
||||||
|
- `Async` suffix on all methods returning `Task<T>` or `Task`
|
||||||
|
- Private helpers: `PascalCase` (same as public, no underscore)
|
||||||
|
- Example: `NcProgramValidator.cs`
|
||||||
|
|
||||||
|
**Enums:**
|
||||||
|
- PascalCase for type name, PascalCase for values
|
||||||
|
- Examples: `CncManufacturer.Fanuc`, `ConnectionState.Connected`, `FanucTransport.Ethernet`
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
**Formatting:**
|
||||||
|
- 4-space indentation
|
||||||
|
- Allman brace style (opening brace on new line for classes and methods)
|
||||||
|
- Example: `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Single-line property getters: inline `{ get { return _x; } }`
|
||||||
|
- No `.editorconfig` or StyleCop — conventions enforced by practice
|
||||||
|
|
||||||
|
**String Formatting:**
|
||||||
|
- `string.Format()` for parameterized messages (not `$""` interpolation)
|
||||||
|
- Example: `Cnc/NcProgramValidator.cs` lines with format strings
|
||||||
|
- `+` concatenation for console output in `Program.cs`
|
||||||
|
|
||||||
|
**Async:**
|
||||||
|
- `default` parameter for `CancellationToken` on async methods
|
||||||
|
- `ConfigureAwait(false)` on library-level code
|
||||||
|
- `Task.Run()` for blocking FOCAS calls in async context (RunGuardedAsync pattern)
|
||||||
|
|
||||||
|
## Import Organization
|
||||||
|
|
||||||
|
**Order:**
|
||||||
|
1. System namespaces
|
||||||
|
2. External packages
|
||||||
|
3. Internal project namespaces
|
||||||
|
|
||||||
|
No path aliases (not applicable to .NET Framework / no source generators).
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**Patterns:**
|
||||||
|
- Null guards at constructor: `if (config == null) throw new ArgumentNullException("config")`
|
||||||
|
- Example: `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Null-coalescing for optional deps: `_validator = validator ?? new NcProgramValidator(...)`
|
||||||
|
- `CncResult<T>` wrapper for all CNC operations (not exceptions)
|
||||||
|
- Exit codes at CLI boundary: -100 to -208 in `Program.cs`
|
||||||
|
- `try/finally` with `connected` flag for connection cleanup in `Program.cs`
|
||||||
|
|
||||||
|
**Result Wrapper Factories:**
|
||||||
|
- `CncResult<T>.Ok(value)` — success
|
||||||
|
- `CncResult<T>.Fail(errors)` — failure with error list
|
||||||
|
- `CncResult<T>.Cancelled()` — cancellation
|
||||||
|
- `CncResult<T>.OkWithWarnings(value, errors)` — partial success
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
**Framework:** None — `Console.WriteLine` only
|
||||||
|
|
||||||
|
**Patterns:**
|
||||||
|
- User-facing output: `Console.WriteLine()` in `Program.cs`
|
||||||
|
- Error output: same Console (no stderr separation)
|
||||||
|
- No log levels, no timestamps, no structured logging
|
||||||
|
- Debug mode via `-debug=` arg keeps terminal open post-operation
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
**When to Comment:**
|
||||||
|
- Document intent/constraints, not obvious code
|
||||||
|
- Example: `NcProgramValidator.cs` — "Name-format check (uses just the bare filename without path)"
|
||||||
|
- Separator line style for section breaks: `// ── private helpers ─────────────────`
|
||||||
|
- Example: `NcProgramValidator.cs`
|
||||||
|
|
||||||
|
**XML Doc Comments:**
|
||||||
|
- Sparse; only on public classes and key methods
|
||||||
|
- Format: `/// <summary>Brief description.</summary>`
|
||||||
|
- Examples: `NcProgramValidator.cs`, `CncResult.cs`, `ValidationResult.cs`
|
||||||
|
|
||||||
|
**No TODO/FIXME comments found in codebase** — known deferred work tracked in STATE.md.
|
||||||
|
|
||||||
|
## Function Design
|
||||||
|
|
||||||
|
- Private async methods wrap blocking/mixed code with semaphore locking (RunGuardedAsync pattern)
|
||||||
|
- Example: `Cnc/Fanuc/FanucMachine.cs` RunGuardedAsync
|
||||||
|
- Large transfer methods split into chunked helpers (`TransferChunked`, `UploadProgramByIdentifier`)
|
||||||
|
- Interface/implementation pairs for all protocol clients (I-prefix interface + concrete)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Conventions analysis: 2026-05-18*
|
||||||
|
*Update when style decisions change*
|
||||||
87
.paul/codebase/INTEGRATIONS.md
Normal file
87
.paul/codebase/INTEGRATIONS.md
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
# External Integrations
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## CNC Machine Protocols
|
||||||
|
|
||||||
|
**Fanuc FOCAS1/FOCAS2 (proprietary):**
|
||||||
|
- Used for: Program upload/download, tool data, work zero offset, machine state
|
||||||
|
- SDK/Client: `fwlib32.cs` (11,569 lines, P/Invoke wrapper, Fanuc copyright 2002-2014)
|
||||||
|
- Native DLLs: `Cnc/Fanuc/libs/Fwlib32.dll`, `fwlibe1.dll`, `fwlibNCG.dll`
|
||||||
|
- Transport: Ethernet TCP (port 8193 default) or HSSB (legacy serial bus)
|
||||||
|
- Main implementation: `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- Dialect abstraction: `Cnc/Fanuc/IFocasDialect.cs` → `LegacyFocasDialect.cs` / `ModernFocasDialect.cs`
|
||||||
|
- Auto-detection: `Cnc/Fanuc/FanucDialectDetector.cs` (detects FOCAS version at connect time)
|
||||||
|
- Config: `Cnc/Fanuc/FanucConnectionConfig.cs` (IP, port, HSSB node, transport type)
|
||||||
|
- Platform: Windows only (DLL is 32-bit native)
|
||||||
|
|
||||||
|
**Heidenhain LSV2 (proprietary TCP protocol):**
|
||||||
|
- Used for: Program transfer to/from Heidenhain iTNC/TNC controllers
|
||||||
|
- Client: Custom TCP implementation `Cnc/Heidenhain/Lsv2Client.cs`
|
||||||
|
- Default port: 19000
|
||||||
|
- Auth: Username + password required
|
||||||
|
- Framing: STX, SIZE, CMD, data, CRC-16/CCITT
|
||||||
|
- Commands implemented: T_LS_SELECT, T_LS_LOG_IN, T_LS_LOG_OUT, T_R_FL (read file), T_S_FL (send file), T_FD
|
||||||
|
- Main implementation: `Cnc/Heidenhain/HeidenhainMachine.cs`
|
||||||
|
- Config: `Cnc/Heidenhain/HeidenhainConnectionConfig.cs`
|
||||||
|
- Note: ReadActiveProgramAsync / SelectMainProgram not implemented (LSV2 limitation)
|
||||||
|
|
||||||
|
**Siemens Sinumerik FTP:**
|
||||||
|
- Used for: Program transfer to/from Siemens Sinumerik controllers
|
||||||
|
- Protocol: Standard FTP (RFC 959) via `System.Net.FtpWebRequest`
|
||||||
|
- Default port: 21
|
||||||
|
- Default program dir: `/_N_MPF_DIR/`
|
||||||
|
- Auth: Username + password
|
||||||
|
- Client: `Cnc/Siemens/SiemensFtpClient.cs`
|
||||||
|
- Main implementation: `Cnc/Siemens/SiemensMachine.cs`
|
||||||
|
- Config: `Cnc/Siemens/SiemensConnectionConfig.cs`
|
||||||
|
- Requirement: Sinumerik FTP option must be enabled on machine
|
||||||
|
|
||||||
|
**Mitsubishi M-Series FTP:**
|
||||||
|
- Used for: Program transfer to/from Mitsubishi M-series controllers
|
||||||
|
- Protocol: Standard FTP (RFC 959) via `System.Net.FtpWebRequest`
|
||||||
|
- Default port: 21
|
||||||
|
- Default program dir: `/PRG/`
|
||||||
|
- Auth: Username + password
|
||||||
|
- Client: `Cnc/Mitsubishi/MitsubishiFtpClient.cs`
|
||||||
|
- Main implementation: `Cnc/Mitsubishi/MitsubishiMachine.cs`
|
||||||
|
- Config: `Cnc/Mitsubishi/MitsubishiConnectionConfig.cs`
|
||||||
|
|
||||||
|
## Licensing System
|
||||||
|
|
||||||
|
**RSA Offline License Validation:**
|
||||||
|
- Used for: Binding license to specific machine hardware
|
||||||
|
- Mechanism: RSA-SHA256 signature verification
|
||||||
|
- Public key: Embedded as const string in `Licensing/LicenseValidator.cs`
|
||||||
|
- Private key: Vendor-only, stored at `~/Documents/mine/c#/LicenseGenerator/private.key.xml` (gitignored)
|
||||||
|
- Machine fingerprint: WMI queries (CPU ID + motherboard serial) via `Licensing/MachineFingerprint.cs`
|
||||||
|
- License file: `license.lic` (Base64-encoded RSA signature) next to EXE
|
||||||
|
- Crypto: `RSACryptoServiceProvider` + `SHA256CryptoServiceProvider` (.NET 4.7.2 compatible)
|
||||||
|
- Generator tool: Separate repo `ssh://git@3nt-git.duckdns.org:222/davide.trentin/license-generator.git`
|
||||||
|
|
||||||
|
## Hardware Access
|
||||||
|
|
||||||
|
**WMI (Windows Management Instrumentation):**
|
||||||
|
- Used for: Machine fingerprinting in licensing
|
||||||
|
- Queries: `Win32_Processor` (processorID), `Win32_BaseBoard` (SerialNumber)
|
||||||
|
- Package: `System.Management 7.0.1`
|
||||||
|
- Files: `Licensing/MachineFingerprint.cs`
|
||||||
|
- Platform: Windows only
|
||||||
|
|
||||||
|
## Data Storage
|
||||||
|
|
||||||
|
**File System (local):**
|
||||||
|
- NC program files written/read from local path specified via `-pathprogramma=` CLI arg
|
||||||
|
- License file read from `AppDomain.CurrentDomain.BaseDirectory`
|
||||||
|
- No database, no cloud storage
|
||||||
|
|
||||||
|
## Monitoring & Observability
|
||||||
|
|
||||||
|
- No error tracking service (Sentry, etc.)
|
||||||
|
- No analytics
|
||||||
|
- Logging: `Console.WriteLine` only (no structured logging framework)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Integrations analysis: 2026-05-18*
|
||||||
|
*Update when new protocols or services are added*
|
||||||
83
.paul/codebase/STACK.md
Normal file
83
.paul/codebase/STACK.md
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Technology Stack
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## Languages
|
||||||
|
|
||||||
|
**Primary:**
|
||||||
|
- C# (.NET Framework 4.7.2) - All application code
|
||||||
|
|
||||||
|
**Secondary:**
|
||||||
|
- XML - Project files, App.config, binding redirects
|
||||||
|
|
||||||
|
## Runtime
|
||||||
|
|
||||||
|
**Environment:**
|
||||||
|
- .NET Framework 4.7.2 — Windows only (FOCAS1 DLL + WMI)
|
||||||
|
- No cross-platform support; Windows desktop/server target
|
||||||
|
|
||||||
|
**Package Manager:**
|
||||||
|
- NuGet (packages.config format, not PackageReference)
|
||||||
|
- Lockfile: `packages.config` present in root and test project
|
||||||
|
|
||||||
|
## Frameworks
|
||||||
|
|
||||||
|
**Core:**
|
||||||
|
- None (vanilla .NET console application)
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- NUnit 3.13.3 — Unit and integration tests (`NcProgramManager.Tests/packages.config`)
|
||||||
|
- NUnit3TestAdapter 4.2.1 — VS / dotnet test runner
|
||||||
|
- Moq 4.18.4 — Interface mocking
|
||||||
|
|
||||||
|
**Build/IL Weaving:**
|
||||||
|
- Fody 6.6.4 — IL code weaver (`NcProgramManager.csproj`)
|
||||||
|
- Costura.Fody 5.7.0 — Embeds NuGet assemblies + native DLLs into output EXE
|
||||||
|
|
||||||
|
## Key Dependencies
|
||||||
|
|
||||||
|
**Critical:**
|
||||||
|
- `System.Management 7.0.1` — WMI queries for machine fingerprinting (`Licensing/MachineFingerprint.cs`)
|
||||||
|
- `Microsoft.Bcl.AsyncInterfaces 7.0.0` — Async backport for .NET 4.7.2 (`packages.config`)
|
||||||
|
- `System.Memory 4.5.5` — Span<T> and Memory<T> compatibility (`packages.config`)
|
||||||
|
- `System.Runtime.CompilerServices.Unsafe 6.0.0` — Low-level ops (`packages.config`)
|
||||||
|
- `System.Net.Sockets 4.3.0` — TCP networking for LSV2/FTP (`packages.config`)
|
||||||
|
|
||||||
|
**Infrastructure:**
|
||||||
|
- `NETStandard.Library 2.0.3` — .NET Standard facade (`packages.config`)
|
||||||
|
- `System.Net.Http 4.3.4` — HTTP base infrastructure (`packages.config`)
|
||||||
|
- `Castle.Core 5.1.1` — Moq dependency (`NcProgramManager.Tests/packages.config`)
|
||||||
|
|
||||||
|
**Native (embedded via Costura.Fody):**
|
||||||
|
- `Cnc/Fanuc/libs/Fwlib32.dll` — Fanuc FOCAS1 library (32-bit, ~629KB)
|
||||||
|
- `Cnc/Fanuc/libs/fwlibe1.dll` — Extended FOCAS (~1MB)
|
||||||
|
- `Cnc/Fanuc/libs/fwlibNCG.dll` — FOCAS NCG support (~2MB)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
**Environment:**
|
||||||
|
- No environment variables
|
||||||
|
- All runtime config passed via CLI arguments (parsed by `ArgParser.cs`)
|
||||||
|
- License file: `license.lic` next to EXE (no CLI override)
|
||||||
|
|
||||||
|
**Build:**
|
||||||
|
- `NcProgramManager.csproj` — MSBuild 15.0, OutputType=Exe
|
||||||
|
- `App.config` — Runtime binding redirects only (no business settings)
|
||||||
|
- `FodyWeavers.xml` — Costura assembly embedding config
|
||||||
|
|
||||||
|
## Platform Requirements
|
||||||
|
|
||||||
|
**Development:**
|
||||||
|
- Windows (FOCAS1 DLL requires 32-bit Windows; WMI for licensing)
|
||||||
|
- Visual Studio 2017+ or MSBuild 15+
|
||||||
|
- Fanuc FOCAS DLL unavailable on Linux (test suite ignores FOCAS tests)
|
||||||
|
|
||||||
|
**Production:**
|
||||||
|
- Windows (desktop or server)
|
||||||
|
- `license.lic` placed alongside EXE
|
||||||
|
- 32-bit process or AnyCPU with 32-bit preferred (FOCAS1 DLL constraint)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Stack analysis: 2026-05-18*
|
||||||
|
*Update after major dependency changes*
|
||||||
160
.paul/codebase/STRUCTURE.md
Normal file
160
.paul/codebase/STRUCTURE.md
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
# Codebase Structure
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
nc_program_manager/
|
||||||
|
├── NcProgramManager/ # Main application
|
||||||
|
│ ├── Program.cs # CLI entry point, action dispatch
|
||||||
|
│ ├── ArgParser.cs # CLI argument parser
|
||||||
|
│ ├── InputArgs.cs # Parsed args config object
|
||||||
|
│ ├── FanucProgram.cs # Fanuc composite NC file parser
|
||||||
|
│ ├── fwlib32.cs # Fanuc FOCAS1 P/Invoke wrapper (auto-gen, 11K lines)
|
||||||
|
│ ├── NcProgramManager.csproj # Project file (.NET 4.7.2, Console)
|
||||||
|
│ ├── App.config # Runtime binding redirects
|
||||||
|
│ ├── packages.config # NuGet dependencies
|
||||||
|
│ ├── Licensing/ # License validation subsystem
|
||||||
|
│ ├── Cnc/ # CNC abstraction + implementations
|
||||||
|
│ │ ├── ICncMachine.cs
|
||||||
|
│ │ ├── IFanucMachine.cs
|
||||||
|
│ │ ├── INcProgramValidator.cs
|
||||||
|
│ │ ├── NcProgramValidator.cs
|
||||||
|
│ │ ├── CncMachineFactory.cs
|
||||||
|
│ │ ├── Models/ # DTOs and value objects
|
||||||
|
│ │ ├── Fanuc/ # FOCAS protocol
|
||||||
|
│ │ │ └── libs/ # Native DLLs (embedded)
|
||||||
|
│ │ ├── Heidenhain/ # LSV2 protocol
|
||||||
|
│ │ ├── Siemens/ # FTP protocol
|
||||||
|
│ │ └── Mitsubishi/ # FTP protocol
|
||||||
|
│ └── Properties/
|
||||||
|
│ └── AssemblyInfo.cs
|
||||||
|
├── NcProgramManager.Tests/ # Test suite
|
||||||
|
│ ├── Unit/ # Isolated unit tests
|
||||||
|
│ ├── Integration/ # Protocol integration tests
|
||||||
|
│ │ └── Stubs/ # In-process server stubs
|
||||||
|
│ ├── NcProgramManager.Tests.csproj
|
||||||
|
│ ├── packages.config
|
||||||
|
│ └── nunit.runsettings
|
||||||
|
├── NcProgramManager.sln # Solution file
|
||||||
|
├── Dockerfile.test # Containerized test runner
|
||||||
|
└── README.md # Italian operator docs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Purposes
|
||||||
|
|
||||||
|
**`Licensing/`:**
|
||||||
|
- Purpose: RSA-based offline license validation
|
||||||
|
- Key files: `LicenseValidator.cs`, `MachineFingerprint.cs`, `LicenseFile.cs`, `ILicenseValidator.cs`, `IMachineFingerprint.cs`
|
||||||
|
|
||||||
|
**`Cnc/Models/`:**
|
||||||
|
- Purpose: Shared DTOs and value objects for CNC layer
|
||||||
|
- Key files: `CncResult.cs`, `CncError.cs`, `CncManufacturer.cs`, `CncProgram.cs`, `ConnectionState.cs`, `ValidationError.cs`, `ValidationResult.cs`, `ValidationSeverity.cs`, `MachineSnapshot.cs`, `MachineMode.cs`, `MotionState.cs`, `RunState.cs`
|
||||||
|
|
||||||
|
**`Cnc/Fanuc/`:**
|
||||||
|
- Purpose: Fanuc FOCAS protocol implementation
|
||||||
|
- Key files: `FanucMachine.cs` (main), `FanucConnectionConfig.cs`, `IFocasDialect.cs`, `LegacyFocasDialect.cs`, `ModernFocasDialect.cs`, `FocasDialectFactory.cs`, `FanucDialectDetector.cs`, `FanucErrorTranslator.cs`, `IsoProgramFormatter.cs`, `FanucCapabilities.cs`, `FanucTransport.cs`
|
||||||
|
|
||||||
|
**`Cnc/Heidenhain/`:**
|
||||||
|
- Purpose: Heidenhain LSV2 protocol implementation
|
||||||
|
- Key files: `HeidenhainMachine.cs`, `Lsv2Client.cs`, `ILsv2Client.cs`, `HeidenhainConnectionConfig.cs`
|
||||||
|
|
||||||
|
**`Cnc/Siemens/`:**
|
||||||
|
- Purpose: Siemens FTP protocol implementation
|
||||||
|
- Key files: `SiemensMachine.cs`, `SiemensFtpClient.cs`, `ISiemensFtpClient.cs`, `SiemensConnectionConfig.cs`
|
||||||
|
|
||||||
|
**`Cnc/Mitsubishi/`:**
|
||||||
|
- Purpose: Mitsubishi FTP protocol implementation
|
||||||
|
- Key files: `MitsubishiMachine.cs`, `MitsubishiFtpClient.cs`, `IMitsubishiFtpClient.cs`, `MitsubishiConnectionConfig.cs`
|
||||||
|
|
||||||
|
**`NcProgramManager.Tests/Unit/`:**
|
||||||
|
- Purpose: Isolated unit tests with mocks
|
||||||
|
- Key files: `NcProgramValidatorTests.cs`, `FanucMachineTests.cs`, `FanucProgramParsingTests.cs`, `IsoProgramFormatterTests.cs`, `CncMachineFactoryTests.cs`, `LicenseValidatorTests.cs`, `SiemensMachineTests.cs`, `MitsubishiMachineTests.cs`, `HeidenhainMachineTests.cs`
|
||||||
|
|
||||||
|
**`NcProgramManager.Tests/Integration/`:**
|
||||||
|
- Purpose: Protocol-level tests using in-process stubs
|
||||||
|
- Key files: `HeidenhainMachineIntegrationTests.cs`, `SiemensMachineIntegrationTests.cs`, `MitsubishiMachineIntegrationTests.cs`
|
||||||
|
|
||||||
|
**`NcProgramManager.Tests/Integration/Stubs/`:**
|
||||||
|
- Purpose: In-process server stubs for protocol testing
|
||||||
|
- Key files: `FtpServerStub.cs` (FTP server with in-memory file store), `Lsv2ServerStub.cs` (LSV2 TCP server)
|
||||||
|
|
||||||
|
## Key File Locations
|
||||||
|
|
||||||
|
**Entry Points:**
|
||||||
|
- `Program.cs` — Main(), CLI orchestration, exit codes
|
||||||
|
- `ArgParser.cs` — static `Parse(string[]) → InputArgs`
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- `NcProgramManager.csproj` — Project metadata, NuGet refs, build config
|
||||||
|
- `App.config` — Binding redirects only
|
||||||
|
- `packages.config` — NuGet dependency list
|
||||||
|
|
||||||
|
**Core Logic:**
|
||||||
|
- `Cnc/ICncMachine.cs` — Primary abstraction interface
|
||||||
|
- `Cnc/CncMachineFactory.cs` — Manufacturer dispatch
|
||||||
|
- `Cnc/Models/CncResult.cs` — Result wrapper pattern
|
||||||
|
- `Cnc/Fanuc/FanucMachine.cs` — Largest impl (~740 lines)
|
||||||
|
- `Licensing/LicenseValidator.cs` — License check entry
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
- `NcProgramManager.Tests/Unit/NcProgramValidatorTests.cs` — Most comprehensive unit tests
|
||||||
|
- `NcProgramManager.Tests/Integration/Stubs/FtpServerStub.cs` — Reusable FTP stub
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- `README.md` — Italian operator-facing docs (CLI args, license setup, error codes)
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- PascalCase for all `.cs` files: `FanucMachine.cs`, `CncResult.cs`
|
||||||
|
- Interfaces prefixed with I: `ICncMachine.cs`, `IFocasDialect.cs`
|
||||||
|
- Config classes: `{Manufacturer}ConnectionConfig.cs`
|
||||||
|
- Test files: `{Subject}Tests.cs` or `{Subject}IntegrationTests.cs`
|
||||||
|
|
||||||
|
**Directories:**
|
||||||
|
- PascalCase for manufacturer dirs: `Fanuc/`, `Heidenhain/`, `Siemens/`, `Mitsubishi/`
|
||||||
|
- Lowercase for support: `libs/`
|
||||||
|
|
||||||
|
## Where to Add New Code
|
||||||
|
|
||||||
|
**New CNC manufacturer:**
|
||||||
|
- Implementation: `Cnc/{Manufacturer}/{Manufacturer}Machine.cs` (implement `ICncMachine`)
|
||||||
|
- Config: `Cnc/{Manufacturer}/{Manufacturer}ConnectionConfig.cs`
|
||||||
|
- Client: `Cnc/{Manufacturer}/I{Manufacturer}FtpClient.cs` + `{Manufacturer}FtpClient.cs` (if FTP)
|
||||||
|
- Wire: Add case to `Cnc/CncMachineFactory.cs`
|
||||||
|
- Enum: Add value to `Cnc/Models/CncManufacturer.cs`
|
||||||
|
- Args: Update `ArgParser.cs` if new transport args needed
|
||||||
|
|
||||||
|
**New Fanuc FOCAS feature:**
|
||||||
|
- Add to `Cnc/Fanuc/IFocasDialect.cs`, implement in `LegacyFocasDialect.cs` + `ModernFocasDialect.cs`
|
||||||
|
- Surface in `Cnc/Fanuc/FanucMachine.cs`
|
||||||
|
- If Fanuc-specific CLI exposure: update `IFanucMachine.cs`, `Program.cs`
|
||||||
|
|
||||||
|
**New validation rule:**
|
||||||
|
- Add to `Cnc/NcProgramValidator.cs`
|
||||||
|
- Add test to `NcProgramManager.Tests/Unit/NcProgramValidatorTests.cs`
|
||||||
|
|
||||||
|
**New CLI arg:**
|
||||||
|
- Add to `ArgParser.cs` regex parsing
|
||||||
|
- Add field to `InputArgs.cs`
|
||||||
|
- Update `README.md` params table
|
||||||
|
|
||||||
|
## Special Directories
|
||||||
|
|
||||||
|
**`Cnc/Fanuc/libs/`:**
|
||||||
|
- Purpose: Native Fanuc FOCAS DLLs
|
||||||
|
- Source: Fanuc-provided proprietary binaries
|
||||||
|
- Committed: Yes (required for build)
|
||||||
|
- Embedded: Via Costura.Fody into output EXE
|
||||||
|
|
||||||
|
**`.paul/`:**
|
||||||
|
- Purpose: PAUL project planning state
|
||||||
|
- Source: Generated/maintained by PAUL workflow
|
||||||
|
- Committed: Yes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Structure analysis: 2026-05-18*
|
||||||
|
*Update when directory structure changes*
|
||||||
153
.paul/codebase/TESTING.md
Normal file
153
.paul/codebase/TESTING.md
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
# Testing Patterns
|
||||||
|
|
||||||
|
**Analysis Date:** 2026-05-18
|
||||||
|
|
||||||
|
## Test Framework
|
||||||
|
|
||||||
|
**Runner:**
|
||||||
|
- NUnit 3.13.3 — `NcProgramManager.Tests/packages.config`
|
||||||
|
- NUnit3TestAdapter 4.2.1 — VS/dotnet test runner integration
|
||||||
|
- Config: `NcProgramManager.Tests/nunit.runsettings`
|
||||||
|
|
||||||
|
**Assertion Library:**
|
||||||
|
- NUnit fluent API: `Assert.That(actual, Is.True)`, `Is.EqualTo()`, `Is.InstanceOf<T>()`
|
||||||
|
|
||||||
|
**Mocking:**
|
||||||
|
- Moq 4.18.4 for interface mocking
|
||||||
|
- Castle.Core 5.1.1 (Moq dependency)
|
||||||
|
- Custom delegates for `ref` parameter workaround (Moq limitation with P/Invoke refs)
|
||||||
|
- Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`
|
||||||
|
|
||||||
|
**Run Commands:**
|
||||||
|
```bash
|
||||||
|
# Standard NUnit runner or VS Test Explorer
|
||||||
|
dotnet test NcProgramManager.Tests/NcProgramManager.Tests.csproj
|
||||||
|
# Or via Dockerfile:
|
||||||
|
docker build -f Dockerfile.test .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test File Organization
|
||||||
|
|
||||||
|
**Location:**
|
||||||
|
- All tests in `NcProgramManager.Tests/` (separate project)
|
||||||
|
- Unit tests: `NcProgramManager.Tests/Unit/`
|
||||||
|
- Integration tests: `NcProgramManager.Tests/Integration/`
|
||||||
|
- Stubs: `NcProgramManager.Tests/Integration/Stubs/`
|
||||||
|
|
||||||
|
**Naming:**
|
||||||
|
- Unit: `{Subject}Tests.cs` — `NcProgramValidatorTests.cs`, `FanucMachineTests.cs`
|
||||||
|
- Integration: `{Subject}IntegrationTests.cs` — `HeidenhainMachineIntegrationTests.cs`
|
||||||
|
- Stubs: `{Protocol}ServerStub.cs` — `FtpServerStub.cs`, `Lsv2ServerStub.cs`
|
||||||
|
|
||||||
|
## Test Structure
|
||||||
|
|
||||||
|
**Fixture Pattern:**
|
||||||
|
```csharp
|
||||||
|
[TestFixture]
|
||||||
|
public class NcProgramValidatorTests
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() { ... }
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void MethodName_Condition_ExpectedResult()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
// act
|
||||||
|
// assert (NUnit fluent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameterized Tests:**
|
||||||
|
```csharp
|
||||||
|
[TestCase(CncManufacturer.Fanuc)]
|
||||||
|
[TestCase(CncManufacturer.Siemens)]
|
||||||
|
[TestCase(CncManufacturer.Heidenhain)]
|
||||||
|
[TestCase(CncManufacturer.Mitsubishi)]
|
||||||
|
public void Validate_TabCharacter_ReturnsWarning(CncManufacturer m) { ... }
|
||||||
|
```
|
||||||
|
Example: `NcProgramManager.Tests/Unit/NcProgramValidatorTests.cs`
|
||||||
|
|
||||||
|
**Async Tests:**
|
||||||
|
- No `async Task` test methods — use `.GetAwaiter().GetResult()` to block
|
||||||
|
- Example: `NcProgramManager.Tests/Integration/HeidenhainMachineIntegrationTests.cs`
|
||||||
|
|
||||||
|
**Test Categories:**
|
||||||
|
```csharp
|
||||||
|
[Category("Integration")] // marks integration tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mocking
|
||||||
|
|
||||||
|
**Interface mocking via Moq:**
|
||||||
|
```csharp
|
||||||
|
var mockDialect = new Mock<IFocasDialect>();
|
||||||
|
mockDialect.Setup(d => d.Connect(...)).Returns(...);
|
||||||
|
var machine = new FanucMachine(config, mockDialect.Object);
|
||||||
|
```
|
||||||
|
Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`
|
||||||
|
|
||||||
|
**Reflection-based state injection (for bypassing protected fields):**
|
||||||
|
```csharp
|
||||||
|
// Set internal connection state without real hardware
|
||||||
|
typeof(FanucMachine)
|
||||||
|
.GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
|
.SetValue(machine, ConnectionState.Connected);
|
||||||
|
```
|
||||||
|
Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`
|
||||||
|
|
||||||
|
**What to mock:**
|
||||||
|
- Protocol interfaces: `IFocasDialect`, `ILsv2Client`, `ISiemensFtpClient`, `IMitsubishiFtpClient`
|
||||||
|
- Licensing: `IMachineFingerprint`, `ILicenseValidator`
|
||||||
|
|
||||||
|
## In-Process Protocol Stubs
|
||||||
|
|
||||||
|
**FtpServerStub** (`NcProgramManager.Tests/Integration/Stubs/FtpServerStub.cs`):
|
||||||
|
- Minimal FTP server for Siemens/Mitsubishi integration testing
|
||||||
|
- In-memory file store: `Dictionary<string, string> Files`
|
||||||
|
- Supported commands: USER, PASS, CWD, TYPE, PASV, LIST, NLST, RETR, STOR, DELE
|
||||||
|
- Usage: Start before test, inject port into machine config
|
||||||
|
|
||||||
|
**Lsv2ServerStub** (`NcProgramManager.Tests/Integration/Stubs/Lsv2ServerStub.cs`):
|
||||||
|
- Minimal LSV2 TCP server for Heidenhain integration testing
|
||||||
|
- Implements LSV2 framing (STX, SIZE, CMD, data, CRC)
|
||||||
|
- Thread-per-client architecture
|
||||||
|
|
||||||
|
## Ignored Tests
|
||||||
|
|
||||||
|
**FOCAS DLL-dependent tests marked `[Ignore]`:**
|
||||||
|
- All tests requiring actual `Fwlib32.dll` calls are ignored
|
||||||
|
- Reason: Cannot mock static P/Invoke calls; DLL requires real Fanuc hardware on Windows
|
||||||
|
- Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`
|
||||||
|
|
||||||
|
**Pattern for skipping:**
|
||||||
|
```csharp
|
||||||
|
[Test]
|
||||||
|
[Ignore("Requires Fanuc FOCAS DLL and real hardware")]
|
||||||
|
public void ConnectAsync_ValidConfig_Connects() { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coverage Approach
|
||||||
|
|
||||||
|
**Well-covered:**
|
||||||
|
- `NcProgramValidator` — universal rules + all 4 manufacturers, 30+ test cases
|
||||||
|
- `NcProgramManager.Tests/Unit/NcProgramValidatorTests.cs`
|
||||||
|
- `FanucProgram` parsing — `FanucProgramParsingTests.cs`
|
||||||
|
- `IsoProgramFormatter` — `IsoProgramFormatterTests.cs`
|
||||||
|
- `CncMachineFactory` dispatch — `CncMachineFactoryTests.cs`
|
||||||
|
- `LicenseValidator` — 6 unit tests via internal ctor injection — `LicenseValidatorTests.cs`
|
||||||
|
|
||||||
|
**Partially covered (blocked by hardware):**
|
||||||
|
- `FanucMachine` connect/read/write — FOCAS tests `[Ignore]`d
|
||||||
|
- Heidenhain/Siemens/Mitsubishi machines — integration tests use stubs
|
||||||
|
|
||||||
|
**Not covered:**
|
||||||
|
- `MachineFingerprint` (WMI, Windows-only, no mocked tests)
|
||||||
|
- `LicenseFile` parsing edge cases
|
||||||
|
- `Program.cs` CLI orchestration (no CLI-level tests)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Testing analysis: 2026-05-18*
|
||||||
|
*Update when test framework or patterns change*
|
||||||
Loading…
Add table
Reference in a new issue