feat(11-refactor): extract ArgParser, fix error handling, remove dead code

Phase 11 complete:
- ArgParser.cs: CLI parsing extracted from Program.cs (pure static method)
- Program.cs: no static state; Scarica/Invia take explicit InputArgs param
- Program.cs: try/finally with connected flag — fixes connection leak on exception
- Program.cs: PrintErrors helper surfaces CncResult.Errors on every failure
- AesCrypt.cs: deleted (dead code, zero references)
- NcProgramManager.csproj: AesCrypt.cs → ArgParser.cs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dtrentin 2026-05-18 00:01:32 +02:00
parent 718c3ce8da
commit bd17ea46e5
12 changed files with 691 additions and 232 deletions

View file

@ -41,6 +41,8 @@ Machinists and operators transfer CNC programs to/from any controller without ma
- [x] License wired into Program.cs — RSA check at startup, license.lic next to EXE, -chiave= removed — Phase 8 - [x] License wired into Program.cs — RSA check at startup, license.lic next to EXE, -chiave= removed — Phase 8
- [x] LicenseGenerator standalone tool (keygen, fingerprint, sign) — Phase 9 - [x] LicenseGenerator standalone tool (keygen, fingerprint, sign) — Phase 9
- [x] Real RSA 2048-bit key pair generated; PublicKeyXml embedded in LicenseValidator.cs — Phase 9 - [x] Real RSA 2048-bit key pair generated; PublicKeyXml embedded in LicenseValidator.cs — Phase 9
- [x] ArgParser extracted — CLI parsing isolated from Program.cs; AesCrypt.cs dead code removed — Phase 11
- [x] Connection leak fixed — try/finally in Scarica/Invia; CncResult.Errors surfaced on failure — Phase 11
### Planned (Next) ### Planned (Next)
- [ ] ReadActiveProgramAsync / SelectMainProgram for Heidenhain and Siemens - [ ] ReadActiveProgramAsync / SelectMainProgram for Heidenhain and Siemens
@ -98,10 +100,10 @@ Machinists and operators transfer CNC programs to/from any controller without ma
| CN Protocol (Siemens) | FTP (FtpWebRequest) | ASCII mode, passive, `/_N_MPF_DIR/` | | CN Protocol (Siemens) | FTP (FtpWebRequest) | ASCII mode, passive, `/_N_MPF_DIR/` |
| CN Protocol (Mitsubishi) | FTP (FtpWebRequest) | Port 21, `/PRG/`, no extensions | | CN Protocol (Mitsubishi) | FTP (FtpWebRequest) | Port 21, `/PRG/`, no extensions |
| Validation | NcProgramValidator | Universal + per-manufacturer rules | | Validation | NcProgramValidator | Universal + per-manufacturer rules |
| Encryption | AES (AesCrypt.cs) | Program file protection | | Encryption | AES | AesCrypt.cs removed (dead code) — encryption not active |
| Licensing | RSA-SHA256 asymmetric | IMachineFingerprint + ILicenseValidator + LicenseGenerator; real key pair embedded | | Licensing | RSA-SHA256 asymmetric | IMachineFingerprint + ILicenseValidator + LicenseGenerator; real key pair embedded |
| Testing | NUnit 3.13.3 + Moq 4.18.4 | .NET 4.7.2 test project | | 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 9 (License Generator) — v0.2 complete* *Last updated: 2026-05-18 after Phase 11 (refactor) — v0.3 in progress*

View file

@ -130,4 +130,28 @@ Completed: 2026-05-17
**Plans:** [x] 10-01: Italian READMEs for both repos **Plans:** [x] 10-01: Italian READMEs for both repos
--- ---
*Roadmap updated: 2026-05-17 — Phase 10 readme-docs complete. Project fully documented.*
## Milestone v0.3 — Code Quality
Status: In progress
Phases: 2 of 2 planned
| Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------|
| 11 | refactor | 2 | ✅ Complete | 2026-05-18 |
| 12 | docs-cli | 1 | Not started | — |
### Phase 11: refactor ✅
**Goal:** Clean Architecture refactor of Program.cs — extract ArgParser, fix error handling in Scarica/Invia (try/finally, surface CncResult.Errors), remove AesCrypt dead code.
**Completed:** 2026-05-18
**Plans:**
- [x] 11-01: Extract ArgParser + clean Program.cs structure
- [x] 11-02: Fix Scarica/Invia error handling
### Phase 12: docs-cli
**Goal:** Complete README parameter table — all 15 CLI args with descriptions.
**Plans:**
- [ ] 12-01: README complete CLI reference
---
*Roadmap updated: 2026-05-17 — v0.3 milestone added.*

View file

@ -9,24 +9,35 @@ See: .paul/PROJECT.md (updated 2026-05-17)
## Current Position ## Current Position
Milestone: v0.2 NcProgramManager — COMPLETE Milestone: v0.3 Code Quality
Phase: 10 (readme-docs) — Complete Phase: 12 of 2 (docs-cli) — Not started
Plan: 10-01 complete Plan: Not started
Status: All phases done. Project complete. Status: Ready to plan Phase 12
Last activity: 2026-05-17 — Italian READMEs written for both repos Last activity: 2026-05-18 — Phase 11 (refactor) complete, transitioned to Phase 12
Progress: Progress:
- Milestone v0.1: [██████████] 100% (complete) - Milestone v0.1: [██████████] 100% (complete)
- Milestone v0.2: [██████████] 100% (complete) - Milestone v0.2: [██████████] 100% (complete)
- Phase 10 (readme-docs): [██████████] 100% (complete) - Milestone v0.3: [█████░░░░░] 50%
- Phase 11 (refactor): [██████████] 100% (complete)
- Phase 12 (docs-cli): [░░░░░░░░░░] 0%
## Loop Position ## Loop Position
``` ```
PLAN ──▶ APPLY ──▶ UNIFY PLAN ──▶ APPLY ──▶ UNIFY
✓ ✓ ✓ [Loop complete — project fully documented] ✓ ✓ ✓ [Phase 11 complete — ready to plan Phase 12]
``` ```
## What Was Built (v0.3 Code Quality — in progress)
### Phase 11 — Refactor
- `ArgParser.cs` — static class, `Parse(string[]) → InputArgs`; all CLI regex parsing extracted from Program.cs
- `Program.cs` — no static state; `Scarica`/`Invia` take explicit `InputArgs`; `VediErrore` takes `bool debugMode`
- `Program.cs` — try/finally with `connected` flag in `Scarica` and `Invia`; connection leak fixed
- `Program.cs``PrintErrors(IReadOnlyList<CncError>)` helper; `CncResult.Errors` printed on every failure
- `AesCrypt.cs` — deleted (dead code, no references)
## What Was Built (v0.1 Multi-CN Release) ## What Was Built (v0.1 Multi-CN Release)
### Phase 1 — CNC Abstraction ### Phase 1 — CNC Abstraction
@ -111,7 +122,6 @@ PLAN ──▶ APPLY ──▶ UNIFY
- `#` = Warning in Fanuc/Mitsubishi — macro variable indicator - `#` = Warning in Fanuc/Mitsubishi — macro variable indicator
### Deferred Issues ### Deferred Issues
- `AesCrypt.cs` dead code — remove in future cleanup phase
- Heidenhain: `ReadActiveProgramAsync` / `SelectMainProgram` not implemented - Heidenhain: `ReadActiveProgramAsync` / `SelectMainProgram` not implemented
- Siemens: same as Heidenhain - Siemens: same as Heidenhain
- Build + integration not verifiable on Linux (fwlib32 Windows-only) - Build + integration not verifiable on Linux (fwlib32 Windows-only)
@ -129,9 +139,9 @@ Branch: main
## Session Continuity ## Session Continuity
Last session: 2026-05-17 Last session: 2026-05-17
Stopped at: Phase 10 complete — all phases done Stopped at: Phase 11 complete, phase 12 ready
Next action: git commit docs(10-readme-docs), then project is fully shipped Next action: /paul:plan for Phase 12 (docs-cli — README complete CLI reference)
Resume file: .paul/phases/10-readme-docs/10-01-SUMMARY.md Resume file: .paul/ROADMAP.md
--- ---
*STATE.md — Updated after every significant action* *STATE.md — Updated after every significant action*

View file

@ -1,15 +1,15 @@
{ {
"name": "NcProgramManager", "name": "NcProgramManager",
"version": "0.2.0", "version": "0.3.0",
"milestone": { "milestone": {
"name": "v0.2 NcProgramManager", "name": "v0.3 Code Quality",
"version": "0.2.0", "version": "0.3.0",
"status": "complete" "status": "in_progress"
}, },
"phase": { "phase": {
"number": 10, "number": 12,
"name": "readme-docs", "name": "docs-cli",
"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-17T00:00:00Z" "updated_at": "2026-05-18T00:00:00Z"
}, },
"satellite": { "satellite": {
"groom": true "groom": true

View file

@ -0,0 +1,154 @@
---
phase: 11-refactor
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- InputArgs.cs
- Program.cs
- AesCrypt.cs
autonomous: true
---
<objective>
## Goal
Extract arg parsing into dedicated `ArgParser` static class; clean `Program.cs` control flow; delete dead `AesCrypt.cs`.
## Purpose
Program.cs currently mixes arg parsing, license check, business dispatch, and output formatting in one file. Separating concerns makes each part testable and readable. No behavior change — same inputs produce same outputs.
## Output
- New `ArgParser.cs` (static class, pure function: `string[] → InputArgs`)
- `Program.cs` simplified: Main calls `ArgParser.Parse`, then dispatches
- `AesCrypt.cs` deleted
- All existing tests still pass
</objective>
<context>
@.paul/PROJECT.md
@Program.cs
@InputArgs.cs
@AesCrypt.cs
</context>
<acceptance_criteria>
## AC-1: ArgParser extracted
```gherkin
Given string[] args with valid parameters
When ArgParser.Parse(args) is called
Then returns populated InputArgs with correct field values
```
## AC-2: Program.cs simplified
```gherkin
Given Program.cs
When read
Then Main has no inline regex or arg-parsing logic — delegates entirely to ArgParser.Parse
```
## AC-3: AesCrypt deleted
```gherkin
Given AesCrypt.cs (dead code, nothing references it)
When file is deleted
Then project still compiles with no errors
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Create ArgParser.cs</name>
<files>ArgParser.cs</files>
<action>
Create static class `NcProgramManager.ArgParser` with single public method:
`public static InputArgs Parse(string[] args)`
Move ALL logic from `parseArgomenti` in Program.cs into this method verbatim,
then adapt signature (returns InputArgs instead of mutating field).
Rules:
- Keep same regex patterns and substring offsets — no behavior change
- Throw same exceptions on parse failure (caller catches in Main)
- Class is internal static, method is internal static
- No new dependencies
</action>
<verify>ArgParser.cs compiles; `ArgParser.Parse(new[]{"-help"}).mostraHelp == true`</verify>
<done>AC-1 satisfied: ArgParser.Parse returns correct InputArgs</done>
</task>
<task type="auto">
<name>Task 2: Simplify Program.cs</name>
<files>Program.cs</files>
<action>
Replace `parseArgomenti` call with `inputArgs = ArgParser.Parse(args)`.
Delete `parseArgomenti` method body from Program.cs entirely.
Remove the `private static InputArgs inputArgs = new InputArgs()` field —
declare `InputArgs inputArgs` as local variable in Main instead.
Pass `inputArgs` explicitly to `Scarica(machine, inputArgs)` and `Invia(machine, inputArgs)`.
Update Scarica/Invia signatures to accept `InputArgs` parameter (no longer use static field).
`vediErrore` already uses `inputArgs.debugMode` — pass it as parameter too,
or make vediErrore accept `bool debugMode`.
Do NOT change license check, MostraAiuto, vediErrore content, or error codes.
</action>
<verify>Project compiles. `dotnet build` (or msbuild) reports 0 errors. Scarica/Invia no longer reference static field.</verify>
<done>AC-2 satisfied: Main delegates to ArgParser, no inline regex</done>
</task>
<task type="auto">
<name>Task 3: Delete AesCrypt.cs</name>
<files>AesCrypt.cs</files>
<action>
Verify no file in the project references `AesCrypt` (grep before deleting).
Delete AesCrypt.cs.
Remove its entry from NcProgramManager.csproj `<Compile>` list if present.
</action>
<verify>
grep -r "AesCrypt" . --include="*.cs" returns no results.
Project compiles.
</verify>
<done>AC-3 satisfied: dead code removed, project still compiles</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Cnc/* — all machine implementation files
- Licensing/* — license system
- FanucProgram.cs, fwlib32.cs
- Error codes in vediErrore
- MostraAiuto content (README task handles docs)
- NcProgramManager.Tests/*
## SCOPE LIMITS
- No behavior change — same CLI args produce same results
- Do not refactor Scarica/Invia error handling (that is Plan 11-02)
- Do not add new error messages or change exit codes
</boundaries>
<verification>
Before declaring plan complete:
- [ ] grep -r "AesCrypt" . --include="*.cs" returns 0 results
- [ ] grep "parseArgomenti" Program.cs returns 0 results
- [ ] Project compiles (msbuild or dotnet build)
- [ ] ArgParser.cs exists at project root
- [ ] All tests pass (NcProgramManager.Tests)
</verification>
<success_criteria>
- ArgParser.cs created, all parsing logic moved
- Program.cs Main has no regex or substring parsing
- AesCrypt.cs deleted and removed from csproj
- Zero compilation errors
- Zero test regressions
</success_criteria>
<output>
After completion, create `.paul/phases/11-refactor/11-01-SUMMARY.md`
</output>

View file

@ -0,0 +1,86 @@
---
phase: 11-refactor
plan: 01
subsystem: cli
tags: [arg-parsing, refactor, cleanup]
requires: []
provides:
- ArgParser static class — all CLI arg parsing isolated from Program.cs
- Program.cs with no static state, no inline regex
- AesCrypt.cs removed (dead code)
affects: [11-02-error-handling]
tech-stack:
added: []
patterns: ["ArgParser static method: string[] → InputArgs (pure function)"]
key-files:
created: [ArgParser.cs]
modified: [Program.cs, NcProgramManager.csproj]
key-decisions:
- "VediErrore: changed signature to accept bool debugMode parameter (no longer reads static field)"
- "Scarica/Invia: now accept InputArgs parameter explicitly"
patterns-established:
- "ArgParser.Parse(args) is the single entry point for all CLI parsing"
duration: ~15min
started: 2026-05-17T00:00:00Z
completed: 2026-05-17T00:00:00Z
---
# Phase 11 Plan 01: ArgParser extraction + AesCrypt removal
**Extracted all CLI arg parsing into `ArgParser.cs`; `Program.cs` no longer holds static state or inline regex; dead `AesCrypt.cs` deleted.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~15min |
| Tasks | 3 completed |
| Files modified | 3 (+ 1 created, 1 deleted) |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: ArgParser extracted | Pass | `ArgParser.Parse(string[]) → InputArgs` — all parsing logic moved |
| AC-2: Program.cs simplified | Pass | No inline regex, no static `inputArgs` field; delegates to `ArgParser.Parse` |
| AC-3: AesCrypt deleted | Pass | File deleted; csproj entry replaced with `ArgParser.cs`; 0 refs in codebase |
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `ArgParser.cs` | Created | Static class, single `Parse(string[]) → InputArgs` method |
| `Program.cs` | Modified | `inputArgs` now local var in Main; `Scarica`/`Invia`/`VediErrore` accept `InputArgs` param |
| `NcProgramManager.csproj` | Modified | Replaced `AesCrypt.cs` compile entry with `ArgParser.cs` |
| `AesCrypt.cs` | Deleted | Dead code — no references anywhere in project |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| `VediErrore` takes `bool debugMode` param | Removes last dependency on static state | Cleaner signature; no behaviour change |
| `Scarica`/`Invia` take explicit `InputArgs` param | Eliminates static field coupling | Methods now testable in isolation |
## Deviations from Plan
None — plan executed exactly as written.
## Next Phase Readiness
**Ready:**
- `Program.cs` clean entry point — easy to add error surfacing in 11-02
- `ArgParser` isolated — can be unit-tested independently
**Concerns:** None.
**Blockers:** None.
---
*Phase: 11-refactor, Plan: 01*
*Completed: 2026-05-17*

View file

@ -0,0 +1,153 @@
---
phase: 11-refactor
plan: 02
type: execute
wave: 2
depends_on: ["11-01"]
files_modified:
- Program.cs
autonomous: true
---
<objective>
## Goal
Fix `Scarica` and `Invia` in Program.cs: use `try/finally` for guaranteed disconnect and print `CncResult.Errors` on every failure.
## Purpose
Current code manually calls `DisconnectAsync` before each error return — but the `catch` block has no disconnect, so any unhandled exception after connect leaks the connection. Also, `CncResult.Errors` is never printed; operators get a numeric code with no machine-level detail.
## Output
- `Program.cs` — both methods refactored: single `try/catch/finally` with `connected` flag; all `CncResult` failures print errors before returning
- Private helper `PrintErrors(IReadOnlyList<CncError>)` added
</objective>
<context>
@.paul/PROJECT.md
@Program.cs
@.paul/phases/11-refactor/11-01-SUMMARY.md
</context>
<acceptance_criteria>
## AC-1: Disconnect always runs after connect
```gherkin
Given Scarica or Invia successfully calls ConnectAsync
When any exception is thrown during the operation
Then DisconnectAsync is called exactly once (via finally block)
```
## AC-2: No manual disconnect before early returns
```gherkin
Given Scarica or Invia
When read
Then no call to DisconnectAsync exists inside the try block — only in the finally
```
## AC-3: CncResult errors printed on failure
```gherkin
Given any CncResult<T> with Success == false and non-empty Errors list
When the result is checked and found failed
Then each error is printed via CncError.ToString() before VediErrore is called
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Add PrintErrors helper + refactor Scarica</name>
<files>Program.cs</files>
<action>
Add private static helper at bottom of Program class:
```
static void PrintErrors(System.Collections.Generic.IReadOnlyList<CncError> errors)
{
if (errors == null) return;
foreach (var e in errors)
Console.WriteLine(" " + e.ToString());
}
```
Refactor Scarica:
1. Declare `bool connected = false;` before try.
2. Wrap ALL operation logic in a single `try { } catch (Exception ex) { } finally { }`.
3. After `ConnectAsync` succeeds, set `connected = true`.
4. Remove ALL `macchina.DisconnectAsync().GetAwaiter().GetResult()` calls from inside the try block.
5. In `finally`: `if (connected) macchina.DisconnectAsync().GetAwaiter().GetResult();`
6. After every `!result.Success` check, call `PrintErrors(result.Errors)` before `VediErrore`.
Early-return on `!connResult.Success` does NOT set connected=true, so finally skips disconnect — correct.
Avoid: catching and swallowing the disconnect exception in finally (let it propagate naturally).
</action>
<verify>
Read Program.cs Scarica method:
- `connected` flag declared
- `finally { if (connected) macchina.DisconnectAsync()... }` present
- Zero `DisconnectAsync` calls inside try block
- `PrintErrors` called before each `VediErrore(-2XX)` that follows a CncResult check
</verify>
<done>AC-1, AC-2, AC-3 satisfied for Scarica</done>
</task>
<task type="auto">
<name>Task 2: Refactor Invia with same pattern</name>
<files>Program.cs</files>
<action>
Apply identical refactor to Invia:
1. Declare `bool connected = false;` before try.
2. Single `try/catch/finally` — set `connected = true` after `ConnectAsync` succeeds.
3. Remove ALL `macchina.DisconnectAsync()` calls from inside try.
4. `finally { if (connected) macchina.DisconnectAsync().GetAwaiter().GetResult(); }`
5. Call `PrintErrors(result.Errors)` before each `VediErrore` that follows a failed CncResult.
Note: `File.ReadAllText` in Invia can throw IOException — the finally will still run and disconnect safely.
</action>
<verify>
Read Program.cs Invia method:
- Same structure as Scarica post-refactor
- Zero `DisconnectAsync` calls inside try
- `PrintErrors` called on all failed CncResult checks
</verify>
<done>AC-1, AC-2, AC-3 satisfied for Invia</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- ArgParser.cs
- InputArgs.cs
- Cnc/* — all machine implementation files
- Licensing/*
- VediErrore content or error codes
- MostraAiuto
- NcProgramManager.Tests/*
## SCOPE LIMITS
- Only Program.cs touched
- Do not change method signatures
- Do not add new error codes
- Do not change the connection/operation logic — only the cleanup and error display
</boundaries>
<verification>
Before declaring plan complete:
- [ ] grep "DisconnectAsync" Program.cs — all results inside `finally` blocks only
- [ ] grep "PrintErrors" Program.cs — called before every VediErrore that follows a CncResult check
- [ ] `connected` flag declared in both Scarica and Invia
- [ ] Project compiles (no new errors)
</verification>
<success_criteria>
- Both Scarica and Invia use try/catch/finally with connected flag
- DisconnectAsync called only in finally
- PrintErrors called on every CncResult failure
- Zero compilation errors
</success_criteria>
<output>
After completion, create `.paul/phases/11-refactor/11-02-SUMMARY.md`
</output>

View file

@ -0,0 +1,85 @@
---
phase: 11-refactor
plan: 02
subsystem: cli
tags: [error-handling, refactor, connection-management]
requires:
- phase: 11-01
provides: "Scarica/Invia with explicit InputArgs parameter — enables this plan's refactor"
provides:
- try/finally disconnect guarantee in Scarica and Invia
- PrintErrors helper — surfaces CncResult.Errors on every failure
- Connection leak bug fixed (catch block previously had no disconnect)
affects: []
tech-stack:
added: []
patterns: ["try/finally with connected flag for resource cleanup after async connect"]
key-files:
created: []
modified: [Program.cs]
key-decisions:
- "connected flag (bool) preferred over nested try — simpler than tracking state across multiple early returns"
- "PrintErrors prints to stdout before VediErrore — operator sees machine detail then error code"
patterns-established:
- "Resource cleanup pattern: bool connected = false; try { ... connected = true; } finally { if (connected) Disconnect(); }"
duration: ~10min
started: 2026-05-18T00:00:00Z
completed: 2026-05-18T00:00:00Z
---
# Phase 11 Plan 02: Scarica/Invia error handling refactor
**Fixed connection leak in Scarica/Invia (try/finally with `connected` flag); `CncResult.Errors` now printed before every error code.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~10min |
| Tasks | 2 completed |
| Files modified | 1 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Disconnect always runs after connect | Pass | `finally { if (connected) DisconnectAsync() }` in both methods |
| AC-2: No manual disconnect inside try | Pass | Zero `DisconnectAsync` calls inside try blocks — verified by grep |
| AC-3: CncResult errors printed on failure | Pass | `PrintErrors(result.Errors)` called at all 8 CncResult failure sites |
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `Program.cs` | Modified | try/finally pattern, `connected` flag, `PrintErrors` helper added |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| `bool connected` flag over nested try | Simpler than inner try; clearly marks connect/disconnect boundary | Easy to read, no double-catch complexity |
| `PrintErrors` before `VediErrore` | Operator sees machine-level detail first, then summary code | Better diagnostics; no behavior change otherwise |
## Deviations from Plan
None — plan executed exactly as written.
## Next Phase Readiness
**Ready:**
- Phase 11 complete — `Program.cs` clean, `ArgParser` isolated, errors surfaced
- Phase 12 (docs-cli) can proceed: README needs full CLI param table
**Concerns:** None.
**Blockers:** None.
---
*Phase: 11-refactor, Plan: 02*
*Completed: 2026-05-18*

View file

@ -1,93 +0,0 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace NcProgramManager
{
class AesCrypt
{
public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
public static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
}

91
ArgParser.cs Normal file
View file

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NcProgramManager.Cnc.Models;
namespace NcProgramManager
{
internal static class ArgParser
{
public static InputArgs Parse(string[] args)
{
var inputArgs = new InputArgs();
var arguments = new List<string>(args);
var help = arguments.Find(it => new Regex("-help.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(help))
{
inputArgs.mostraHelp = true;
return inputArgs;
}
var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it));
inputArgs.azione = azione.Substring(8);
var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(manufacturer))
{
string mfgVal = manufacturer.Substring(14).Trim().ToLowerInvariant();
switch (mfgVal)
{
case "heidenhain": inputArgs.manufacturer = CncManufacturer.Heidenhain; break;
case "siemens": inputArgs.manufacturer = CncManufacturer.Siemens; break;
case "mitsubishi": inputArgs.manufacturer = CncManufacturer.Mitsubishi; break;
default: inputArgs.manufacturer = CncManufacturer.Fanuc; break;
}
}
string tipo = arguments.Find(it => new Regex("-tipo=.*", RegexOptions.IgnoreCase).IsMatch(it));
inputArgs.tipo = !string.IsNullOrEmpty(tipo) ? Int32.Parse(tipo.Substring(6)) : 3;
var ip = arguments.Find(it => new Regex("-ip=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(ip))
inputArgs.ip = ip.Substring(4);
var porta = arguments.Find(it => new Regex("-porta=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(porta))
inputArgs.porta = porta.Substring(7);
var nodo = arguments.Find(it => new Regex("-nodo=.*", RegexOptions.IgnoreCase).IsMatch(it));
inputArgs.nodoHssb = !string.IsNullOrEmpty(nodo) ? Int32.Parse(nodo.Substring(6)) : -1;
var username = arguments.Find(it => new Regex("-username=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(username))
inputArgs.username = username.Substring(10);
var password = arguments.Find(it => new Regex("-password=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(password))
inputArgs.password = password.Substring(10);
var compatibility = arguments.Find(it => new Regex("-compatibility.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(compatibility))
inputArgs.compatibilityMode = true;
var commento = arguments.Find(it => new Regex("-commento=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(commento))
inputArgs.commentoProgramma = commento.Substring(10);
var path = arguments.Find(it => new Regex("-path=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(path))
inputArgs.pathCNC = Int16.Parse(path.Substring(6));
var pathprogramma = arguments.Find(it => new Regex("-pathprogramma=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(pathprogramma))
inputArgs.pathLocaleProgramma = pathprogramma.Substring(15);
var hasToolOffsetData = arguments.Find(it => new Regex("-tooloffset.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(hasToolOffsetData))
inputArgs.hasToolOffsetData = true;
var hasWorkZeroOffsetData = arguments.Find(it => new Regex("-workzero.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(hasWorkZeroOffsetData))
inputArgs.hasWorkZeroOffsetData = true;
var debugMode = arguments.Find(it => new Regex("-debug.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(debugMode))
inputArgs.debugMode = true;
return inputArgs;
}
}
}

View file

@ -72,7 +72,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AesCrypt.cs" /> <Compile Include="ArgParser.cs" />
<Compile Include="FanucProgram.cs" /> <Compile Include="FanucProgram.cs" />
<Compile Include="InputArgs.cs" /> <Compile Include="InputArgs.cs" />
<Compile Include="fwlib32.cs" /> <Compile Include="fwlib32.cs" />

View file

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions;
using NcProgramManager.Cnc; using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using NcProgramManager.Cnc.Models;
using NcProgramManager.Licensing; using NcProgramManager.Licensing;
@ -10,46 +9,44 @@ namespace NcProgramManager
{ {
internal class Program internal class Program
{ {
private static InputArgs inputArgs = new InputArgs();
public static int Main(string[] args) public static int Main(string[] args)
{ {
InputArgs inputArgs;
try try
{ {
parseArgomenti(args); inputArgs = ArgParser.Parse(args);
} }
catch (Exception e) catch (Exception e)
{ {
Console.Write("Gestore Programmi FANUC: "); Console.Write("Gestore Programmi FANUC: ");
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
Console.WriteLine("Errore parametri input. Consulta `-help' per maggiori informazioni."); Console.WriteLine("Errore parametri input. Consulta `-help' per maggiori informazioni.");
vediErrore(-101); VediErrore(-101, false);
return -101; return -101;
} }
if (inputArgs.mostraHelp) if (inputArgs.mostraHelp)
{ {
MostraAiuto(); MostraAiuto();
vediErrore(0); VediErrore(0, inputArgs.debugMode);
return 0; return 0;
} }
string _licFingerprint = new MachineFingerprint().GetFingerprint(); string licFingerprint = new MachineFingerprint().GetFingerprint();
string _licBase64; string licBase64;
string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic"); string licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
if (!LicenseFile.TryRead(_licPath, out _licBase64) if (!LicenseFile.TryRead(licPath, out licBase64)
|| !new LicenseValidator().Validate(_licFingerprint, _licBase64)) || !new LicenseValidator().Validate(licFingerprint, licBase64))
{ {
Console.WriteLine("Licenza ERRATA"); Console.WriteLine("Licenza ERRATA");
vediErrore(-100); VediErrore(-100, inputArgs.debugMode);
return -1; return -1;
} }
// Validate required params
bool needsIp = inputArgs.manufacturer != CncManufacturer.Fanuc || inputArgs.nodoHssb == -1; bool needsIp = inputArgs.manufacturer != CncManufacturer.Fanuc || inputArgs.nodoHssb == -1;
if (inputArgs.azione == "" || (needsIp && string.IsNullOrEmpty(inputArgs.ip)) || inputArgs.pathLocaleProgramma == "") if (inputArgs.azione == "" || (needsIp && string.IsNullOrEmpty(inputArgs.ip)) || inputArgs.pathLocaleProgramma == "")
{ {
vediErrore(-102); VediErrore(-102, inputArgs.debugMode);
return -102; return -102;
} }
@ -70,10 +67,10 @@ namespace NcProgramManager
switch (inputArgs.azione.ToUpperInvariant()) switch (inputArgs.azione.ToUpperInvariant())
{ {
case "SCARICA": return Scarica(machine); case "SCARICA": return Scarica(machine, inputArgs);
case "INVIA": return Invia(machine); case "INVIA": return Invia(machine, inputArgs);
default: default:
vediErrore(-103); VediErrore(-103, inputArgs.debugMode);
return -103; return -103;
} }
} }
@ -84,31 +81,33 @@ namespace NcProgramManager
} }
} }
static int Scarica(ICncMachine macchina) static int Scarica(ICncMachine macchina, InputArgs inputArgs)
{ {
Console.WriteLine("RICEVI DA CNC-->"); Console.WriteLine("RICEVI DA CNC-->");
bool connected = false;
try try
{ {
var connResult = macchina.ConnectAsync().GetAwaiter().GetResult(); var connResult = macchina.ConnectAsync().GetAwaiter().GetResult();
if (!connResult.Success) if (!connResult.Success)
{ {
Console.WriteLine("Macchina non connessa"); Console.WriteLine("Macchina non connessa");
vediErrore(-201); PrintErrors(connResult.Errors);
VediErrore(-201, inputArgs.debugMode);
return -201; return -201;
} }
connected = true;
Console.WriteLine("Inizio RICEZIONE NCProgram"); Console.WriteLine("Inizio RICEZIONE NCProgram");
var progResult = macchina.ReadProgramAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult(); var progResult = macchina.ReadProgramAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult();
Console.WriteLine("Fine RICEZIONE NCProgram"); Console.WriteLine("Fine RICEZIONE NCProgram");
if (!progResult.Success || string.IsNullOrEmpty(progResult.Value)) if (!progResult.Success || string.IsNullOrEmpty(progResult.Value))
{ {
macchina.DisconnectAsync().GetAwaiter().GetResult(); PrintErrors(progResult.Errors);
vediErrore(-204); VediErrore(-204, inputArgs.debugMode);
return -204; return -204;
} }
FanucProgram programma = new FanucProgram(progResult.Value); FanucProgram programma = new FanucProgram(progResult.Value);
IFanucMachine fanuc = macchina as IFanucMachine; IFanucMachine fanuc = macchina as IFanucMachine;
if (inputArgs.hasToolOffsetData) if (inputArgs.hasToolOffsetData)
@ -116,8 +115,7 @@ namespace NcProgramManager
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("ToolOffset non supportato per questo produttore"); Console.WriteLine("ToolOffset non supportato per questo produttore");
macchina.DisconnectAsync().GetAwaiter().GetResult(); VediErrore(-205, inputArgs.debugMode);
vediErrore(-205);
return -205; return -205;
} }
Console.WriteLine("Inizio RICEZIONE ToolOffsetData"); Console.WriteLine("Inizio RICEZIONE ToolOffsetData");
@ -125,8 +123,8 @@ namespace NcProgramManager
Console.WriteLine("Fine RICEZIONE ToolOffsetData"); Console.WriteLine("Fine RICEZIONE ToolOffsetData");
if (!toolResult.Success || string.IsNullOrEmpty(toolResult.Value)) if (!toolResult.Success || string.IsNullOrEmpty(toolResult.Value))
{ {
macchina.DisconnectAsync().GetAwaiter().GetResult(); PrintErrors(toolResult.Errors);
vediErrore(-205); VediErrore(-205, inputArgs.debugMode);
return -205; return -205;
} }
programma.setValue(toolResult.Value, FanucProgram.FILE_TYPE.TOOL_OFFEST_DATA); programma.setValue(toolResult.Value, FanucProgram.FILE_TYPE.TOOL_OFFEST_DATA);
@ -137,8 +135,7 @@ namespace NcProgramManager
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("WorkZeroOffset non supportato per questo produttore"); Console.WriteLine("WorkZeroOffset non supportato per questo produttore");
macchina.DisconnectAsync().GetAwaiter().GetResult(); VediErrore(-206, inputArgs.debugMode);
vediErrore(-206);
return -206; return -206;
} }
Console.WriteLine("Inizio RICEZIONE WorkZeroOffsetData"); Console.WriteLine("Inizio RICEZIONE WorkZeroOffsetData");
@ -146,38 +143,45 @@ namespace NcProgramManager
Console.WriteLine("Fine RICEZIONE WorkZeroOffsetData"); Console.WriteLine("Fine RICEZIONE WorkZeroOffsetData");
if (!workResult.Success || string.IsNullOrEmpty(workResult.Value)) if (!workResult.Success || string.IsNullOrEmpty(workResult.Value))
{ {
macchina.DisconnectAsync().GetAwaiter().GetResult(); PrintErrors(workResult.Errors);
vediErrore(-206); VediErrore(-206, inputArgs.debugMode);
return -206; return -206;
} }
programma.setValue(workResult.Value, FanucProgram.FILE_TYPE.WORK_ZERO_OFFSET); programma.setValue(workResult.Value, FanucProgram.FILE_TYPE.WORK_ZERO_OFFSET);
} }
macchina.DisconnectAsync().GetAwaiter().GetResult();
File.WriteAllText(inputArgs.pathLocaleProgramma, programma.ToString()); File.WriteAllText(inputArgs.pathLocaleProgramma, programma.ToString());
vediErrore(0); VediErrore(0, inputArgs.debugMode);
return 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); Console.WriteLine(ex.Message);
vediErrore(-202); VediErrore(-202, inputArgs.debugMode);
return -202; return -202;
} }
finally
{
if (connected)
macchina.DisconnectAsync().GetAwaiter().GetResult();
}
} }
static int Invia(ICncMachine macchina) static int Invia(ICncMachine macchina, InputArgs inputArgs)
{ {
Console.WriteLine("INVIA A CNC-->"); Console.WriteLine("INVIA A CNC-->");
bool connected = false;
try try
{ {
var connResult = macchina.ConnectAsync().GetAwaiter().GetResult(); var connResult = macchina.ConnectAsync().GetAwaiter().GetResult();
if (!connResult.Success) if (!connResult.Success)
{ {
Console.WriteLine("Macchina non connessa"); Console.WriteLine("Macchina non connessa");
vediErrore(-201); PrintErrors(connResult.Errors);
VediErrore(-201, inputArgs.debugMode);
return -201; return -201;
} }
connected = true;
FanucProgram programma = new FanucProgram(File.ReadAllText(inputArgs.pathLocaleProgramma)); FanucProgram programma = new FanucProgram(File.ReadAllText(inputArgs.pathLocaleProgramma));
if (!string.IsNullOrEmpty(inputArgs.commentoProgramma)) if (!string.IsNullOrEmpty(inputArgs.commentoProgramma))
@ -197,8 +201,8 @@ namespace NcProgramManager
Console.WriteLine("Fine INVIO NCProgram"); Console.WriteLine("Fine INVIO NCProgram");
if (!writeResult.Success) if (!writeResult.Success)
{ {
macchina.DisconnectAsync().GetAwaiter().GetResult(); PrintErrors(writeResult.Errors);
vediErrore(-204); VediErrore(-204, inputArgs.debugMode);
return -204; return -204;
} }
} }
@ -210,8 +214,7 @@ namespace NcProgramManager
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("ToolOffset non supportato per questo produttore"); Console.WriteLine("ToolOffset non supportato per questo produttore");
macchina.DisconnectAsync().GetAwaiter().GetResult(); VediErrore(-205, inputArgs.debugMode);
vediErrore(-205);
return -205; return -205;
} }
Console.WriteLine("Inizio INVIO ToolOffsetData"); Console.WriteLine("Inizio INVIO ToolOffsetData");
@ -219,8 +222,8 @@ namespace NcProgramManager
Console.WriteLine("Fine INVIO ToolOffsetData"); Console.WriteLine("Fine INVIO ToolOffsetData");
if (!toolResult.Success) if (!toolResult.Success)
{ {
macchina.DisconnectAsync().GetAwaiter().GetResult(); PrintErrors(toolResult.Errors);
vediErrore(-205); VediErrore(-205, inputArgs.debugMode);
return -205; return -205;
} }
} }
@ -230,8 +233,7 @@ namespace NcProgramManager
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("WorkZeroOffset non supportato per questo produttore"); Console.WriteLine("WorkZeroOffset non supportato per questo produttore");
macchina.DisconnectAsync().GetAwaiter().GetResult(); VediErrore(-206, inputArgs.debugMode);
vediErrore(-206);
return -206; return -206;
} }
Console.WriteLine("Inizio INVIO WorkZeroOffsetData"); Console.WriteLine("Inizio INVIO WorkZeroOffsetData");
@ -239,25 +241,36 @@ namespace NcProgramManager
Console.WriteLine("Fine INVIO WorkZeroOffsetData"); Console.WriteLine("Fine INVIO WorkZeroOffsetData");
if (!workResult.Success) if (!workResult.Success)
{ {
macchina.DisconnectAsync().GetAwaiter().GetResult(); PrintErrors(workResult.Errors);
vediErrore(-206); VediErrore(-206, inputArgs.debugMode);
return -206; return -206;
} }
} }
macchina.DisconnectAsync().GetAwaiter().GetResult(); VediErrore(0, inputArgs.debugMode);
vediErrore(0);
return 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); Console.WriteLine(ex.Message);
vediErrore(-202); VediErrore(-202, inputArgs.debugMode);
return -202; return -202;
} }
finally
{
if (connected)
macchina.DisconnectAsync().GetAwaiter().GetResult();
}
} }
static void vediErrore(int errore) static void PrintErrors(IReadOnlyList<CncError> errors)
{
if (errors == null) return;
foreach (var e in errors)
Console.WriteLine(" " + e.ToString());
}
static void VediErrore(int errore, bool debugMode)
{ {
Console.WriteLine("Errore:" + errore); Console.WriteLine("Errore:" + errore);
Console.WriteLine(); Console.WriteLine();
@ -274,7 +287,7 @@ namespace NcProgramManager
Console.WriteLine("-206 : Errore in fase invio/ricezione work zero offest data"); Console.WriteLine("-206 : Errore in fase invio/ricezione work zero offest data");
Console.WriteLine("-207 : Posizionare il CN in MDI o EDIT per eseguire l'invio di un programma"); Console.WriteLine("-207 : Posizionare il CN in MDI o EDIT per eseguire l'invio di un programma");
Console.WriteLine("-208 : Programma CN Caricato ma non settato a main."); Console.WriteLine("-208 : Programma CN Caricato ma non settato a main.");
if (inputArgs.debugMode) if (debugMode)
Console.ReadLine(); Console.ReadLine();
} }
@ -336,71 +349,5 @@ namespace NcProgramManager
Console.WriteLine("-207 : Posizionare il CN in MDI o EDIT per eseguire l'invio di un programma"); Console.WriteLine("-207 : Posizionare il CN in MDI o EDIT per eseguire l'invio di un programma");
Console.WriteLine("-208 : Programma CN Caricato ma non settato a main."); Console.WriteLine("-208 : Programma CN Caricato ma non settato a main.");
} }
private static void parseArgomenti(string[] args)
{
List<string> arguments = new List<string>(args);
var help = arguments.Find(it => new Regex("-help.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(help))
{
inputArgs.mostraHelp = true;
return;
}
var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it));
inputArgs.azione = azione.Substring(8);
var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(manufacturer))
{
string mfgVal = manufacturer.Substring(14).Trim().ToLowerInvariant();
switch (mfgVal)
{
case "heidenhain": inputArgs.manufacturer = CncManufacturer.Heidenhain; break;
case "siemens": inputArgs.manufacturer = CncManufacturer.Siemens; break;
case "mitsubishi": inputArgs.manufacturer = CncManufacturer.Mitsubishi; break;
default: inputArgs.manufacturer = CncManufacturer.Fanuc; break;
}
}
string tipo = arguments.Find(it => new Regex("-tipo=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(tipo))
inputArgs.tipo = Int32.Parse(tipo.Substring(6));
else inputArgs.tipo = 3;
var ip = arguments.Find(it => new Regex("-ip=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(ip))
inputArgs.ip = ip.Substring(4);
var porta = arguments.Find(it => new Regex("-porta=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(porta))
inputArgs.porta = porta.Substring(7);
var nodo = arguments.Find(it => new Regex("-nodo=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(nodo))
inputArgs.nodoHssb = Int32.Parse(nodo.Substring(6));
else inputArgs.nodoHssb = -1;
var username = arguments.Find(it => new Regex("-username=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(username))
inputArgs.username = username.Substring(10);
var password = arguments.Find(it => new Regex("-password=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(password))
inputArgs.password = password.Substring(10);
var compatibility = arguments.Find(it => new Regex("-compatibility.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(compatibility))
inputArgs.compatibilityMode = true;
var commento = arguments.Find(it => new Regex("-commento=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(commento))
inputArgs.commentoProgramma = commento.Substring(10);
var path = arguments.Find(it => new Regex("-path=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(path))
inputArgs.pathCNC = Int16.Parse(path.Substring(6));
var pathprogramma = arguments.Find(it => new Regex("-pathprogramma=.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(pathprogramma))
inputArgs.pathLocaleProgramma = pathprogramma.Substring(15);
var hasToolOffestData = arguments.Find(it => new Regex("-tooloffset.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(hasToolOffestData))
inputArgs.hasToolOffsetData = true;
var hasWorkZeroOffsetData = arguments.Find(it => new Regex("-workzero.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(hasWorkZeroOffsetData))
inputArgs.hasWorkZeroOffsetData = true;
var debugMode = arguments.Find(it => new Regex("-debug.*", RegexOptions.IgnoreCase).IsMatch(it));
if (!string.IsNullOrEmpty(debugMode))
inputArgs.debugMode = true;
}
} }
} }