feat(15-logging): NLog structured logging — all output through NLog (v0.5 complete)

Phase 15 complete (2 plans):
- 15-01: NLog 5.2.8 + nlog.config (rolling file daily/7d + console); logger fields in
  FanucMachine, HeidenhainMachine, SiemensMachine, MitsubishiMachine, Lsv2Client, Program.cs;
  SetState event handler catches → _log.Warn; cleanup catches annotated; ISS-003/ISS-004 closed
- 15-02: All Console.Write/WriteLine in Program.cs replaced with _log.Info (103 calls);
  nlog.config console target layout=${message}, levels=Info only — Warn/Error file-only;
  operator console output identical; every interaction now timestamped in log file

Milestone v0.5 Operational Quality complete.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
dtrentin 2026-05-18 23:57:27 +02:00
parent 45ca8f5a47
commit 60ce1c9d57
18 changed files with 1040 additions and 169 deletions

View file

@ -24,29 +24,17 @@ Enhancements and known issues discovered during execution and codebase analysis.
--- ---
### ISS-003: Silent exception handlers swallow failures ### ~~ISS-003: Silent exception handlers swallow failures~~ ✅ CLOSED
- **Discovered:** Codebase analysis (2026-05-18) - **Closed:** 2026-05-18 (Phase 15)
- **Type:** Reliability / Observability - **Fix applied:** Event handler catches (`SetState`) → `_log.Warn(ex, "StateChanged handler threw")`; cleanup/Dispose catches annotated with `/* cleanup — intentionally swallowed */`
- **Description:** 23 `try { ... } catch { }` blocks swallow exceptions silently. Disconnect/cleanup paths fail with no trace. Production issues become undiagnosable.
- **Files:** `Cnc/Fanuc/FanucMachine.cs`, `Cnc/Siemens/SiemensMachine.cs`, `Cnc/Heidenhain/HeidenhainMachine.cs`, `Cnc/Heidenhain/Lsv2Client.cs`
- **Fix:** At minimum `catch (Exception ex) { /* log or Debug.WriteLine */ }` — better with logging framework (ISS-004)
- **Impact:** High — hidden failures, connection leaks masked
- **Effort:** Quick-Medium (dependent on ISS-004)
- **Suggested phase:** Same phase as ISS-004
--- ---
### ISS-004: No structured logging framework ### ~~ISS-004: No structured logging framework~~ ✅ CLOSED
- **Discovered:** Codebase analysis (2026-05-18) - **Closed:** 2026-05-18 (Phase 15)
- **Type:** Observability / Reliability - **Fix applied:** NLog 5.2.8 wired into all 5 production classes; rolling file target (daily, 7-day archive) + console target (Warn+); `_log.Error` in Program.cs error catch blocks
- **Description:** Entire codebase uses `Console.WriteLine` (98+ calls). No log levels, no timestamps, no file output. Production issues undiagnosable without attaching a terminal.
- **Files:** `Program.cs`, all machine implementations
- **Fix:** Add Serilog or NLog. Replace `Console.WriteLine` with leveled logging. Add request/response tracing for FOCAS/LSV2/FTP operations.
- **Impact:** Medium — works today, but operational burden grows with deployments
- **Effort:** Substantial (half day+)
- **Suggested phase:** v0.4 Operational Quality
--- ---
@ -128,6 +116,8 @@ Enhancements and known issues discovered during execution and codebase analysis.
| ISS-005 | SHA256 crypto provider not disposed | 13 | `4cd7715` | | ISS-005 | SHA256 crypto provider not disposed | 13 | `4cd7715` |
| ISS-006 | LSV2 connect timeout doesn't abort hung task | 13 | `4cd7715` | | ISS-006 | LSV2 connect timeout doesn't abort hung task | 13 | `4cd7715` |
| ISS-007 | Integration tests use hardcoded IPs | 14 | `32f68c5` | | ISS-007 | Integration tests use hardcoded IPs | 14 | `32f68c5` |
| ISS-003 | Silent exception handlers swallow failures | 15 | (Phase 15 commit) |
| ISS-004 | No structured logging framework | 15 | (Phase 15 commit) |
--- ---

View file

@ -13,8 +13,8 @@ Machinists and operators transfer CNC programs to/from any controller without ma
| Attribute | Value | | Attribute | Value |
|-----------|-------| |-----------|-------|
| Type | Application | | Type | Application |
| Version | 0.4.0 | | Version | 0.5.0 |
| Status | v0.4 complete | | Status | v0.5 complete |
| Last Updated | 2026-05-18 | | Last Updated | 2026-05-18 |
## Requirements ## Requirements
@ -53,9 +53,15 @@ Machinists and operators transfer CNC programs to/from any controller without ma
- [x] LicenseFile edge-case tests (empty, whitespace, valid, nonexistent) — Phase 14 - [x] LicenseFile edge-case tests (empty, whitespace, valid, nonexistent) — Phase 14
- [x] Empty-fingerprint guard in LicenseValidator (ISS-008 partial) — Phase 14 - [x] Empty-fingerprint guard in LicenseValidator (ISS-008 partial) — Phase 14
### Validated (Shipped) — v0.5 Operational Quality
- [x] NLog 5.2.8 structured logging — rolling file + console targets; all 4 machine classes + Program.cs (ISS-004) — Phase 15
- [x] Event handler silent catches surfaced — SetState catch → `_log.Warn`; cleanup catches annotated (ISS-003) — Phase 15
- [x] All Console.Write/WriteLine replaced with `_log.Info`; console target `${message}` plain layout, `levels="Info"` only — Phase 15
### Planned (Next) ### Planned (Next)
- [ ] ReadActiveProgramAsync / SelectMainProgram for Heidenhain and Siemens - [ ] ReadActiveProgramAsync / SelectMainProgram for Heidenhain and Siemens
- [ ] Full hardware test run on real Windows machine (ISS-008 full closure) - [ ] Full hardware test run on real Windows machine (ISS-008 full closure)
- [ ] Async refactor — replace `.GetAwaiter().GetResult()` calls (ISS-002)
### Out of Scope ### Out of Scope
- Cloud sync - Cloud sync
@ -88,6 +94,8 @@ Machinists and operators transfer CNC programs to/from any controller without ma
| LicenseGenerator as standalone separate repo | Vendor tool, never shipped to customers; separate lifecycle from NcProgramManager | 2026-05-17 | Active | | LicenseGenerator as standalone separate repo | Vendor tool, never shipped to customers; separate lifecycle from NcProgramManager | 2026-05-17 | Active |
| Heidenhain LSV2 port 19000 | Standard LSV2 port; needs real hardware verification | 2026-05-13 | Active | | Heidenhain LSV2 port 19000 | Standard LSV2 port; needs real hardware verification | 2026-05-13 | Active |
| Siemens FTP dir `/_N_MPF_DIR/` | Sinumerik standard MPF directory; FTP option must be enabled | 2026-05-13 | Active | | Siemens FTP dir `/_N_MPF_DIR/` | Sinumerik standard MPF directory; FTP option must be enabled | 2026-05-13 | Active |
| NLog console target levels="Info", layout="${message}" | All operator output through NLog; console shows plain text identical to before; Warn/Error file only | 2026-05-18 | Active |
| Cleanup catches: comment-only, no log | Dispose must not throw; logging infra can fail | 2026-05-18 | Active |
## Success Metrics ## Success Metrics
@ -111,8 +119,9 @@ Machinists and operators transfer CNC programs to/from any controller without ma
| Validation | NcProgramValidator | Universal + per-manufacturer rules | | Validation | NcProgramValidator | Universal + per-manufacturer rules |
| Encryption | AES | AesCrypt.cs removed (dead code) — encryption not active | | 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 |
| Logging | NLog 5.2.8 | Rolling file (daily, 7-day archive) + console Info-only plain layout; all production classes |
| 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-18 after Phase 14 (test-hardening) — v0.4 complete* *Last updated: 2026-05-18 after Phase 15 plan 02 — v0.5 complete, all operator output through NLog*

View file

@ -185,4 +185,25 @@ Completed: 2026-05-18
- [x] 14-01: HardwareTestHelper + LicenseFileTests + Validate_EmptyFingerprint - [x] 14-01: HardwareTestHelper + LicenseFileTests + Validate_EmptyFingerprint
--- ---
*Roadmap updated: 2026-05-18 — v0.4 milestone complete.*
## Milestone v0.5 — Operational Quality ✅
**Status:** Complete
**Phases:** 1 of 1 complete
**Completed:** 2026-05-18
| Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------|
| 15 | logging | 2 | ✅ Complete | 2026-05-18 |
### Phase 15: logging ✅
**Goal:** Add structured logging (ISS-004), surface silent catch blocks (ISS-003), route all operator output through NLog.
**Issues addressed:** ISS-003 (closed), ISS-004 (closed)
**Completed:** 2026-05-18
**Plans:**
- [x] 15-01: NLog 5.2.8 + nlog.config + logger fields in 5 classes + event handler catches + Program.cs error logging
- [x] 15-02: Replace all Console.Write/WriteLine with _log.Info; console target Info-only plain layout
---
*Roadmap updated: 2026-05-18 — v0.5 milestone complete; Phase 15 (logging) complete.*

View file

@ -5,31 +5,41 @@
See: .paul/PROJECT.md (updated 2026-05-18) See: .paul/PROJECT.md (updated 2026-05-18)
**Core value:** Machinists transfer CNC programs to/from any controller without manual steps or internet **Core value:** Machinists transfer CNC programs to/from any controller without manual steps or internet
**Current focus:** v0.4 complete — all milestones done **Current focus:** v0.5 complete — all milestones shipped; ready for v0.6 planning
## Current Position ## Current Position
Milestone: v0.4 Robustness — **Complete** Milestone: v0.5 Operational Quality — **Complete**
Phase: 14 of 14 (test-hardening) — Complete Phase: 15 of 15 (logging) — Complete
Plan: 14-01 unified Plan: 15-02 complete
Status: Milestone complete — ready for next milestone or release Status: Milestone complete — ready for v0.6 planning
Last activity: 2026-05-18 — Phase 14 complete, v0.4 milestone closed Last activity: 2026-05-18 — Phase 15 complete; v0.5 milestone complete
Progress: Progress:
- Milestone v0.1: [██████████] 100% (complete) - Milestone v0.1: [██████████] 100% (complete)
- Milestone v0.2: [██████████] 100% (complete) - Milestone v0.2: [██████████] 100% (complete)
- Milestone v0.3: [██████████] 100% (complete) - Milestone v0.3: [██████████] 100% (complete)
- Milestone v0.4: [██████████] 100% (complete) - Milestone v0.4: [██████████] 100% (complete)
- Phase 13: [██████████] 100% (complete) - Milestone v0.5: [██████████] 100% (complete)
- Phase 14: [██████████] 100% (complete) - Phase 15: [██████████] 100%
## Loop Position ## Loop Position
``` ```
PLAN ──▶ APPLY ──▶ UNIFY PLAN ──▶ APPLY ──▶ UNIFY
✓ ✓ ✓ [Loop complete — v0.4 milestone closed] ✓ ✓ ✓ [Loop complete — v0.5 milestone complete]
``` ```
## What Was Built (v0.5 Operational Quality — complete)
### Phase 15 — Logging
- `nlog.config` — rolling file target (daily, 7-day archive) + console (`levels="Info"`, `${message}` layout); `throwConfigExceptions=true`
- `packages.config` / `NcProgramManager.csproj` — NLog 5.2.8 reference + Content/CopyAlways entry
- `FanucMachine.cs`, `HeidenhainMachine.cs`, `SiemensMachine.cs`, `MitsubishiMachine.cs` — logger field + SetState catch → `_log.Warn`; Dispose catches annotated
- `Lsv2Client.cs` — logger field + Info at connect/disconnect + cleanup catch annotations
- `Program.cs` — logger field + 3× `_log.Error` in catch blocks + 103× `_log.Info` (all Console.Write/WriteLine replaced); zero Console.Write/WriteLine remain
- ISS-003 closed; ISS-004 closed
## What Was Built (v0.4 Robustness — complete) ## What Was Built (v0.4 Robustness — complete)
### Phase 14 — Test Hardening ### Phase 14 — Test Hardening
@ -148,23 +158,23 @@ PLAN ──▶ APPLY ──▶ UNIFY
- Heidenhain: LSV2 port 19000, needs real hardware testing - Heidenhain: LSV2 port 19000, needs real hardware testing
- Siemens: FTP `/_N_MPF_DIR/`, needs Sinumerik FTP option enabled on machine - Siemens: FTP `/_N_MPF_DIR/`, needs Sinumerik FTP option enabled on machine
- `#` = Warning in Fanuc/Mitsubishi — macro variable indicator - `#` = Warning in Fanuc/Mitsubishi — macro variable indicator
- NLog console target `levels="Info"`, `${message}` layout — all operator output through NLog; Warn/Error file only
- Cleanup catches: comment-only, no log — Dispose must not throw; logging infra can fail
### Deferred Issues ### Deferred Issues
Tracked in `.paul/ISSUES.md` — 11 issues total; 5 closed (ISS-001, ISS-005, ISS-006, ISS-007, ISS-008 partial). Tracked in `.paul/ISSUES.md` — 11 issues total; 7 closed (ISS-001, ISS-003, ISS-004, ISS-005, ISS-006, ISS-007, ISS-008 partial).
Key open items: Key open items:
- ISS-002: Blocking .GetAwaiter().GetResult() — Medium, refactor - ISS-002: Blocking .GetAwaiter().GetResult() — Medium, refactor
- ISS-003: Silent catch {} blocks — High, needs logging (ISS-004)
- ISS-004: No logging framework — Medium, Substantial effort
- ISS-008: Full closure needs real Windows hardware run with env vars set - ISS-008: Full closure needs real Windows hardware run with env vars set
- ISS-009/010: Heidenhain/Siemens ReadActiveProgram not implemented — deferred - ISS-009/010: Heidenhain/Siemens ReadActiveProgram not implemented — deferred
- `private.key.xml` owned by root (external repo) — run `sudo chown $USER ~/Documents/mine/c#/LicenseGenerator/private.key.xml` - `private.key.xml` owned by root (external repo) — run `sudo chown $USER ~/Documents/mine/c#/LicenseGenerator/private.key.xml`
### Blockers/Concerns ### Blockers/Concerns
None — all milestones complete. None — v0.5 complete.
### Git State ### Git State
Last commit: `32f68c5` — feat(14-test-hardening): env-var hardware test scaffolding + LicenseFile edge cases Last commit: (Phase 15 commit — see below after git commit)
LicenseGenerator repo: commit `2ef68dd` — feat: initial LicenseGenerator LicenseGenerator repo: commit `2ef68dd` — feat: initial LicenseGenerator
Branch: main Branch: main
@ -176,9 +186,9 @@ Documents: `.paul/codebase/` (7 files — STACK, ARCHITECTURE, STRUCTURE, CONVEN
## Session Continuity ## Session Continuity
Last session: 2026-05-18 Last session: 2026-05-18
Stopped at: v0.4 Robustness milestone complete — all 14 phases done Stopped at: v0.5 milestone complete — Phase 15 both plans done
Next action: /paul:milestone for next milestone, or review open issues (ISS-002/003/004/008) Next action: /paul:milestone for v0.6
Resume file: .paul/phases/14-test-hardening/14-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,14 +1,14 @@
{ {
"name": "NcProgramManager", "name": "NcProgramManager",
"version": "0.4.0", "version": "0.5.0",
"milestone": { "milestone": {
"name": "v0.4 Robustness", "name": "v0.5 Operational Quality",
"version": "0.4.0", "version": "0.5.0",
"status": "complete" "status": "complete"
}, },
"phase": { "phase": {
"number": 14, "number": 15,
"name": "test-hardening", "name": "logging",
"status": "complete" "status": "complete"
}, },
"loop": { "loop": {

View file

@ -0,0 +1,317 @@
---
phase: 15-logging
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- packages.config
- NcProgramManager.csproj
- nlog.config
- Program.cs
- Cnc/Fanuc/FanucMachine.cs
- Cnc/Heidenhain/HeidenhainMachine.cs
- Cnc/Heidenhain/Lsv2Client.cs
- Cnc/Siemens/SiemensMachine.cs
- Cnc/Mitsubishi/MitsubishiMachine.cs
autonomous: true
---
<objective>
## Goal
Add NLog file+console logging and surface silent exception swallows in event handler paths across all 4 machine classes.
## Purpose
ISS-003: 18 silent `catch {}` blocks mean production failures are undiagnosable. ISS-004: all output goes to console only — operators cannot send a log file for support. Adding NLog with a rolling file target gives a persistent record for debugging.
## Output
- `nlog.config` — rolling file log + console, next to EXE
- NLog wired into 5 production classes via `LogManager.GetCurrentClassLogger()`
- Event handler catch blocks changed from silent swallow to `_log.Warn`
- Dispose/cleanup catch blocks annotated as intentionally silent (no logging — Dispose must not rethrow)
- Program.cs error catch blocks emit `_log.Error` in addition to existing `Console.WriteLine`
</objective>
<context>
@.paul/PROJECT.md
@.paul/ISSUES.md
@Cnc/Siemens/SiemensMachine.cs
@Cnc/Heidenhain/Lsv2Client.cs
</context>
<acceptance_criteria>
## AC-1: NLog config present and wired
```gherkin
Given nlog.config is placed next to NcProgramManager.exe
When the application starts
Then NLog initialises without throwing (no "NLog configuration file not found" in console)
And a logs/ subdirectory is created containing nc_program_manager.log
```
## AC-2: Event handler exceptions reach the log
```gherkin
Given a StateChanged handler that throws
When SetState() fires in any machine class
Then the exception is caught and logged at WARN level (not silently swallowed)
```
## AC-3: Dispose cleanup catch blocks are annotated
```gherkin
Given all Dispose() and disconnect cleanup catch blocks
When reviewed in code
Then each has a // cleanup — intentionally swallowed comment (no log call — Dispose must not rethrow)
```
## AC-4: Program.cs errors reach the log file
```gherkin
Given any unhandled exception in Scarica, Invia, or arg parsing
When the catch block runs
Then _log.Error(ex, ...) is called BEFORE Console.WriteLine
And all existing Console.WriteLine calls remain unchanged
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Add NLog package and nlog.config</name>
<files>
packages.config,
NcProgramManager.csproj,
nlog.config
</files>
<action>
1. In `packages.config`, add inside `<packages>`:
```xml
<package id="NLog" version="5.2.8" targetFramework="net472" />
```
2. In `NcProgramManager.csproj`, inside the existing `<ItemGroup>` that contains `<Reference>` entries, add:
```xml
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>packages\NLog.5.2.8\lib\net46\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
```
Also in `NcProgramManager.csproj`, inside the `<ItemGroup>` that contains `<Compile>` entries (or a new `<ItemGroup>`), add:
```xml
<Content Include="nlog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
```
3. Create `nlog.config` in the project root:
```xml
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true">
<targets>
<target xsi:type="File"
name="logfile"
fileName="${basedir}/logs/nc_program_manager.log"
archiveFileName="${basedir}/logs/nc_program_manager.{#}.log"
archiveEvery="Day"
archiveNumbering="Date"
maxArchiveFiles="7"
layout="${longdate} ${level:uppercase=true:padding=5} [${logger:shortName=true}] ${message}${onexception:${newline}${exception:format=tostring}}" />
<target xsi:type="Console"
name="console"
layout="${level:uppercase=true:padding=5} [${logger:shortName=true}] ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
<logger name="*" minlevel="Warn" writeTo="console" />
</rules>
</nlog>
```
Constraints:
- NLog package version is 5.2.8 — supports .NET 4.6+ (project is 4.7.2)
- lib/net46 is the highest .NET Framework target in the NLog 5.x package
- Console rule minlevel=Warn: logger output must NOT duplicate user-facing Console.WriteLine in Program.cs
- `throwConfigExceptions="true"` surfaces config errors at startup rather than silently failing
</action>
<verify>
- Read packages.config: NLog 5.2.8 entry present
- Read NcProgramManager.csproj: Reference to NLog with HintPath present; nlog.config Content entry with CopyToOutputDirectory present
- Read nlog.config: file target with archiving, console target, two rules present
</verify>
<done>AC-1 satisfied: NLog wired into build; nlog.config copied to output directory on build</done>
</task>
<task type="auto">
<name>Task 2: Wire logger into machine classes and fix event handler catches</name>
<files>
Cnc/Fanuc/FanucMachine.cs,
Cnc/Heidenhain/HeidenhainMachine.cs,
Cnc/Heidenhain/Lsv2Client.cs,
Cnc/Siemens/SiemensMachine.cs,
Cnc/Mitsubishi/MitsubishiMachine.cs
</files>
<action>
For each of the 5 files:
**A. Add using directive at top:**
```csharp
using NLog;
```
**B. Add logger field** inside the class, after existing private fields:
```csharp
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
```
**C. Fix event handler catch blocks** — there is one `SetState` method in each machine class containing:
```csharp
try { h(this, next); } catch { }
```
Replace with:
```csharp
try { h(this, next); } catch (Exception ex) { _log.Warn(ex, "StateChanged handler threw"); }
```
**D. Annotate Dispose() cleanup catches** — do NOT add logging (Dispose must not rethrow):
```csharp
try { DisconnectAsync().GetAwaiter().GetResult(); } catch { /* cleanup — intentionally swallowed */ }
try { _gate.Dispose(); } catch { /* cleanup — intentionally swallowed */ }
```
**For Lsv2Client specifically (no SetState, no _gate):**
- Add `using NLog;` and `private static readonly Logger _log = LogManager.GetCurrentClassLogger();`
- In `Connect()` timeout branch: `try { _tcp.Close(); } catch { /* cleanup — intentionally swallowed */ }`
- In `Disconnect()`: annotate all 3 cleanup catches with `/* cleanup — intentionally swallowed */`
- Add connect/disconnect info logging:
```csharp
// At start of Connect(), before _tcp = new TcpClient():
_log.Info("Connecting to {0}:{1}", _config.IpAddress, _config.Port);
// After successful Login():
_log.Info("Connected to {0}", _config.IpAddress);
// At start of Disconnect():
_log.Info("Disconnecting from {0}", _config.IpAddress);
```
Constraints:
- Do NOT add logging inside `Dispose()` or disposal catch blocks — Dispose must not throw and logging adds failure risk
- Do NOT change `catch (Exception ex) { _errors.Add(...); }` blocks — already handled correctly
- Do NOT change `catch (OperationCanceledException)` blocks — correct behavior
- Logger is `static readonly` (one per class type, not per instance) — NLog standard pattern
- Do NOT add `using NLog;` if already present (check first)
</action>
<verify>
- Grep for `LogManager.GetCurrentClassLogger()` in all 5 files — must appear exactly once per file
- Grep for `try { h(this, next); } catch { }` — must return 0 results (all replaced)
- Grep for `_log.Warn(ex` — must appear in FanucMachine, HeidenhainMachine, SiemensMachine, MitsubishiMachine
- Read Lsv2Client.cs: verify info logging at connect/disconnect, cleanup catch annotations present
</verify>
<done>AC-2 and AC-3 satisfied: event handler exceptions logged at WARN; cleanup catches annotated as intentional</done>
</task>
<task type="auto">
<name>Task 3: Add file logging to Program.cs error catch blocks</name>
<files>
Program.cs
</files>
<action>
1. Add `using NLog;` to the using directives at top of Program.cs.
2. Add logger field inside the `Program` class (or at the start of Main — static field):
```csharp
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
```
3. In the arg parsing catch block (currently around line 19):
```csharp
catch (Exception e)
{
_log.Error(e, "Arg parsing failed");
Console.Write("Gestore Programmi FANUC: ");
Console.WriteLine(e.Message);
// ... rest unchanged
```
4. In the `Scarica` catch block (currently around line 157):
```csharp
catch (Exception ex)
{
_log.Error(ex, "Scarica failed");
Console.WriteLine(ex.Message);
// ... rest unchanged
```
5. In the `Invia` catch block (currently around line 253):
```csharp
catch (Exception ex)
{
_log.Error(ex, "Invia failed");
Console.WriteLine(ex.Message);
// ... rest unchanged
```
Constraints:
- `_log.Error(ex, ...)` MUST come BEFORE `Console.WriteLine(ex.Message)` — log first, then display
- ALL existing Console.WriteLine calls remain exactly as-is — do not modify or remove any
- Do NOT add logging for license failure (`Console.WriteLine("Licenza ERRATA")`) — that's a known condition, not an exception
- Do NOT add a `NLog.LogManager.Shutdown()` call — not needed for a short-lived console EXE
</action>
<verify>
- Read Program.cs: `using NLog;` present
- Read Program.cs: `_log` field present
- Grep for `_log.Error` in Program.cs — must appear 3 times
- Grep for `Console.WriteLine` in Program.cs — must still be 97 (unchanged count)
</verify>
<done>AC-4 satisfied: all 3 Program.cs error paths log to file before Console.WriteLine; no Console.WriteLine removed</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `Licensing/LicenseValidator.cs` — public key constant must not be modified
- `Licensing/MachineFingerprint.cs` — fixed in Phase 13; not in scope
- `NcProgramManager.Tests/` — no test changes in this phase
- `Cnc/**/` catch blocks already handling exceptions via `_errors.Add(...)` — correct, leave alone
- All existing `Console.WriteLine` calls — supplemented only, never replaced
## SCOPE LIMITS
- No logging inside Dispose() or cleanup catch paths — comment only
- No replacement of user-facing Console.WriteLine — file logging supplements via NLog
- No logging for known control-flow conditions (license failure, validation failure) — only unexpected exceptions
- No async/await changes — ISS-002 is a separate phase
- No NuGet package other than NLog — no wrapper abstraction, no DI container
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `packages.config` contains NLog 5.2.8 entry
- [ ] `NcProgramManager.csproj` has NLog Reference with HintPath and nlog.config as Content/CopyAlways
- [ ] `nlog.config` exists with file target (archiving) + console target (Warn+) + rules
- [ ] All 5 machine/client files: `LogManager.GetCurrentClassLogger()` field present
- [ ] `try { h(this, next); } catch { }` — zero occurrences remain in codebase
- [ ] Dispose cleanup catches in all 4 machine classes: annotated with comment
- [ ] Lsv2Client.Disconnect() catches: annotated with comment
- [ ] Program.cs: 3× `_log.Error` added before Console.WriteLine; 97× Console.WriteLine count unchanged
</verification>
<success_criteria>
- All 3 tasks completed
- All 4 ACs covered
- ISS-003 addressed: event handler swallows surfaced; cleanup swallows documented
- ISS-004 addressed: NLog file+console logging wired into all production classes
- No existing tests broken (no test files modified)
- No production behavior changed for operators (Console.WriteLine output identical)
</success_criteria>
<output>
After completion, create `.paul/phases/15-logging/15-01-SUMMARY.md`
</output>

View file

@ -0,0 +1,159 @@
---
phase: 15-logging
plan: 01
subsystem: observability
tags: [nlog, logging, exception-handling, csharp, net472]
requires:
- phase: 14-test-hardening
provides: stable test base, no regressions introduced
provides:
- NLog 5.2.8 wired into all 4 machine classes and Program.cs
- Rolling file log (daily, 7 days) + console (Warn+)
- Event handler catches surface to log instead of silent swallow
- Cleanup catches annotated as intentionally silent
affects: future-phases, deployment, support
tech-stack:
added: [NLog 5.2.8]
patterns: [static readonly Logger per class via LogManager.GetCurrentClassLogger()]
key-files:
created: [nlog.config]
modified:
- packages.config
- NcProgramManager.csproj
- Program.cs
- Cnc/Fanuc/FanucMachine.cs
- Cnc/Heidenhain/HeidenhainMachine.cs
- Cnc/Heidenhain/Lsv2Client.cs
- Cnc/Siemens/SiemensMachine.cs
- Cnc/Mitsubishi/MitsubishiMachine.cs
key-decisions:
- "NLog console target minlevel=Warn — logger output does not duplicate operator Console.WriteLine"
- "Dispose/cleanup catches: comment-only, no log calls — Dispose must not throw"
- "throwConfigExceptions=true in nlog.config — config errors surface at startup"
patterns-established:
- "Logger field: private static readonly Logger _log = LogManager.GetCurrentClassLogger();"
- "Event handler catch: catch (Exception ex) { _log.Warn(ex, 'StateChanged handler threw'); }"
- "Cleanup catch annotation: catch { /* cleanup — intentionally swallowed */ }"
duration: ~30min
started: 2026-05-18T00:00:00Z
completed: 2026-05-18T00:00:00Z
---
# Phase 15 Plan 01: Logging Summary
**NLog 5.2.8 wired into all 5 production classes; rolling file log + console target; event handler silent swallows surfaced; cleanup swallows annotated.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~30 min |
| Tasks | 3 completed |
| Files created | 1 (nlog.config) |
| Files modified | 8 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: NLog config present and wired | Pass | nlog.config created; Content/CopyAlways in csproj; NLog 5.2.8 in packages.config |
| AC-2: Event handler exceptions reach log | Pass | All 4 SetState catches → `_log.Warn(ex, "StateChanged handler threw")`; zero silent handler catches remain |
| AC-3: Dispose cleanup catches annotated | Pass | All Dispose/disconnect cleanup catches have `/* cleanup — intentionally swallowed */`; no log call in Dispose |
| AC-4: Program.cs errors reach log file | Pass | 3× `_log.Error(ex, ...)` added before Console.WriteLine in arg-parse, Scarica, Invia catch blocks; 97× Console.WriteLine unchanged |
## Accomplishments
- ISS-003 addressed: all event handler silent swallows surfaced; cleanup swallows intentionally annotated
- ISS-004 addressed: NLog rolling file + console logging wired into all production classes
- 97 operator-facing `Console.WriteLine` calls unchanged — zero behavior change for operators
- Lsv2Client also gains Info-level connect/disconnect tracing
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `nlog.config` | Created | Rolling daily file target + Warn+ console; 7-day archive |
| `packages.config` | Modified | NLog 5.2.8 entry added |
| `NcProgramManager.csproj` | Modified | NLog Reference with HintPath; nlog.config as Content/CopyAlways |
| `Program.cs` | Modified | `using NLog;`, `_log` field, 3× `_log.Error` in catch blocks |
| `Cnc/Fanuc/FanucMachine.cs` | Modified | Logger field, SetState fix, cleanup catch annotations |
| `Cnc/Heidenhain/HeidenhainMachine.cs` | Modified | Logger field, SetState fix, Dispose annotations |
| `Cnc/Heidenhain/Lsv2Client.cs` | Modified | Logger field, Info logging at connect/disconnect, cleanup annotations |
| `Cnc/Siemens/SiemensMachine.cs` | Modified | Logger field, SetState fix, Dispose annotations |
| `Cnc/Mitsubishi/MitsubishiMachine.cs` | Modified | Logger field, SetState fix, Dispose annotations |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Console target minlevel=Warn | Logger output must not duplicate operator Console.WriteLine (98+ calls are user-facing) | Operators see only their output; log file captures all Debug+ |
| No logging in Dispose/cleanup catches | Dispose must not throw; logging infrastructure could itself fail | Cleanup stays silent, marked with comment for code reviewers |
| throwConfigExceptions=true | Surfaces nlog.config errors at startup rather than silently failing | Config mistakes visible immediately |
| NLog 5.2.8 / net46 DLL | .NET 4.7.2 target; net46 is highest .NET Framework lib in NLog 5.x package | Build compatible without targeting pack changes |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Scope additions | 2 | Minor — consistent with AC-3 intent |
| Deferred | 0 | — |
### Scope Additions
**1. FanucMachine ConnectCore cancellation cleanup catch annotated**
- `try { Focas1.cnc_freelibhndl(_hndl); } catch { }` in ConnectCore cancellation path
- Plan only named Dispose catches; this is also fire-and-forget cleanup
- Annotated with `/* cleanup — intentionally swallowed */` for consistency
**2. FanucMachine Record() disconnect cleanup catch annotated**
- `try { Focas1.cnc_freelibhndl(_hndl); } catch { }` in Record() disconnect error path
- Same rationale as above — fire-and-forget, not event handler
- Annotated consistently
## Verification Results
```
grep -c "NLog" packages.config → 1 ✓
grep -c "NLog" NcProgramManager.csproj → 2 (Reference + Content) ✓
nlog.config exists ✓
LogManager.GetCurrentClassLogger() in all 5 files ✓
try { h(this, next); } catch { } → 0 occurrences remain ✓
_log.Warn(ex in 4 machine classes (1 each) ✓
_log.Error in Program.cs → 3 ✓
Console.WriteLine count → 97 (unchanged) ✓
```
## Issues Closed
| Issue | Status |
|-------|--------|
| ISS-003: Silent exception handlers | Closed — event handler catches logged; cleanup catches annotated |
| ISS-004: No logging framework | Closed — NLog 5.2.8 rolling file + console wired |
## Next Phase Readiness
**Ready:**
- All production classes have NLog logger
- Log file at `logs/nc_program_manager.log` next to EXE
- Pattern established for future classes: `private static readonly Logger _log = LogManager.GetCurrentClassLogger();`
**Concerns:**
- No build verification on Linux (no dotnet/msbuild) — NLog DLL path assumes NuGet restore to `packages\NLog.5.2.8\lib\net46\`; must restore on Windows before first build
- ISS-002 (blocking `.GetAwaiter().GetResult()`) still open — separate phase
**Blockers:**
- None for code; Windows NuGet restore needed before first build with NLog
---
*Phase: 15-logging, Plan: 01*
*Completed: 2026-05-18*

View file

@ -0,0 +1,194 @@
---
phase: 15-logging
plan: 02
type: execute
wave: 1
depends_on: ["15-01"]
files_modified:
- nlog.config
- Program.cs
autonomous: true
---
<objective>
## Goal
Replace all `Console.Write`/`Console.WriteLine` calls in Program.cs with `_log.Info(...)` and reconfigure the NLog console target so operator output is plain text (Info only), while Warn/Error routes to logfile only.
## Purpose
Operator output currently bypasses NLog — no timestamps, no log file record of what was shown on screen. Moving all output through NLog means every operator interaction is in the log file with a timestamp, while the console still shows plain text identical to the current experience.
## Output
- `nlog.config` — console target: layout=`${message}`, rule limited to Info level only
- `Program.cs` — zero `Console.Write`/`Console.WriteLine` calls; all replaced with `_log.Info(...)`
</objective>
<context>
@.paul/PROJECT.md
@.paul/phases/15-logging/15-01-SUMMARY.md
@nlog.config
@Program.cs
</context>
<acceptance_criteria>
## AC-1: Console output unchanged for operator
```gherkin
Given Program.cs has no Console.Write/Console.WriteLine calls
When any operator-facing message is emitted (startup, progress, error text)
Then operator sees exactly the same text on screen as before
And no level/logger prefix appears on any message
```
## AC-2: All operator output recorded in log file
```gherkin
Given nlog.config file target captures Info+
When any operator-facing message is emitted
Then that same message appears in logs/nc_program_manager.log with timestamp
```
## AC-3: Warn/Error stay out of operator console
```gherkin
Given _log.Warn and _log.Error calls in machine classes and Program.cs
When those calls execute
Then they do NOT appear on the operator console (file only)
And they DO appear in the log file
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Reconfigure nlog.config console target to Info-only plain layout</name>
<files>nlog.config</files>
<action>
Make two changes to nlog.config:
**A. Console target layout** — change from level+logger prefix to plain message:
Current:
```xml
<target xsi:type="Console"
name="console"
layout="${level:uppercase=true:padding=5} [${logger:shortName=true}] ${message}" />
```
Replace with:
```xml
<target xsi:type="Console"
name="console"
layout="${message}" />
```
**B. Console rule** — restrict to Info level only (not Warn/Error):
Current:
```xml
<logger name="*" minlevel="Warn" writeTo="console" />
```
Replace with:
```xml
<logger name="*" levels="Info" writeTo="console" />
```
Constraints:
- File target and its rule (`minlevel="Debug" writeTo="logfile"`) MUST NOT change — file still captures everything
- `levels="Info"` (not `minlevel`) — this restricts console to exactly Info, so Warn/Error go to file only
- Layout `${message}` with no newline suffix — NLog appends newline automatically
</action>
<verify>
- Read nlog.config: console target layout is `${message}`
- Read nlog.config: console rule uses `levels="Info"`
- Read nlog.config: file target rule unchanged (`minlevel="Debug"`)
</verify>
<done>AC-1 partial (layout ready), AC-3 satisfied: Warn/Error no longer route to console</done>
</task>
<task type="auto">
<name>Task 2: Replace all Console.Write/WriteLine with _log.Info in Program.cs</name>
<files>Program.cs</files>
<action>
Replace every `Console.Write` and `Console.WriteLine` call with `_log.Info(...)`.
**Step 1 — Merge the one Console.Write (non-Line) pair at lines 25-26:**
Current:
```csharp
Console.Write("Gestore Programmi FANUC: ");
Console.WriteLine(e.Message);
```
Replace with:
```csharp
_log.Info("Gestore Programmi FANUC: {0}", e.Message);
```
**Step 2 — Replace all remaining Console.WriteLine calls:**
Pattern rules:
- `Console.WriteLine("literal string")``_log.Info("literal string")`
- `Console.WriteLine(variable)``_log.Info("{0}", variable)` — safe against variables containing `{`
- `Console.WriteLine("literal {0}", arg)``_log.Info("literal {0}", arg)` — format args pass through unchanged
- `Console.WriteLine()` (empty — blank line) → `_log.Info("")`
**Step 3 — For the 3 existing error catch blocks** (lines ~163, ~259 after prior edits):
The pattern is now:
```csharp
_log.Error(ex, "Scarica failed");
Console.WriteLine(ex.Message);
```
`Console.WriteLine(ex.Message)``_log.Info("{0}", ex.Message)`
This is correct: _log.Error goes to file only (Warn/Error not on console per Task 1); _log.Info shows ex.Message on console — same behavior as the original Console.WriteLine.
Constraints:
- Do NOT change `_log.Error(...)` or `_log.Warn(...)` calls — those were added in 15-01 and must stay
- Do NOT change `VediErrore(...)` calls
- Do NOT change `MostraAiuto()` or any non-Console calls
- `Console.WriteLine()` with no args (blank separator lines, if any) → `_log.Info("")`
- After replacement, `grep -c "Console\." Program.cs` MUST return 0
</action>
<verify>
- `grep -c "Console\." Program.cs` → 0
- `grep -c "_log.Info" Program.cs` → 98 (97 former WriteLine + 1 merged from Console.Write pair)
- Read lines 22-30 Program.cs: single `_log.Info("Gestore Programmi FANUC: {0}", e.Message)` present; no Console.Write calls
</verify>
<done>AC-1 satisfied: all operator output through NLog; AC-2 satisfied: Info goes to file target too</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `nlog.config` file target and its rule — file still captures Debug+
- `_log.Error(...)` and `_log.Warn(...)` calls added in 15-01 — correct, leave alone
- `NcProgramManager.Tests/` — no test changes
- All machine classes — no changes needed (they have no Console.WriteLine)
- `Licensing/`, `Cnc/`, `ArgParser.cs`, `InputArgs.cs` — not in scope
## SCOPE LIMITS
- Only `nlog.config` and `Program.cs` modified
- No new NLog targets beyond updating the existing console target
- No changes to log levels on the file target
- No changes to the `_log` field or `using NLog` already in Program.cs
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `grep -c "Console\." Program.cs` → 0
- [ ] `grep -c "_log.Info" Program.cs` → 98
- [ ] `grep -c "_log.Error" Program.cs` → 3 (unchanged from 15-01)
- [ ] nlog.config console target: layout=`${message}`
- [ ] nlog.config console rule: `levels="Info"` (not minlevel)
- [ ] nlog.config file rule: `minlevel="Debug"` (unchanged)
</verification>
<success_criteria>
- Both tasks completed
- All 3 ACs covered
- Zero Console.Write/Console.WriteLine remain in Program.cs
- Operator console experience identical (plain text, no level prefix)
- All output now in log file
- No existing _log.Error/_log.Warn calls modified
</success_criteria>
<output>
After completion, create `.paul/phases/15-logging/15-02-SUMMARY.md`
</output>

View file

@ -0,0 +1,114 @@
---
phase: 15-logging
plan: 02
subsystem: logging
tags: [nlog, console, operator-output]
requires:
- phase: 15-01
provides: NLog wired into all classes; _log field + _log.Error in Program.cs
provides:
- All operator output through NLog (_log.Info)
- Console shows plain text identical to before (${message} layout)
- All output timestamped in log file
affects: []
tech-stack:
added: []
patterns: ["All Console.Write/WriteLine replaced with _log.Info; operator output fully NLog-routed"]
key-files:
modified: [nlog.config, Program.cs]
key-decisions:
- "Multi-line Console.WriteLine block split into 7 individual _log.Info calls — one per field"
- "Console.ReadLine() in VediErrore preserved — debug pause, not output"
- "console rule uses levels='Info' not minlevel — Warn/Error to file only"
duration: ~30min
started: 2026-05-18T00:00:00Z
completed: 2026-05-18T00:00:00Z
---
# Phase 15 Plan 02: Console → NLog Summary
**All 97 Console.Write/WriteLine calls in Program.cs replaced with `_log.Info`; nlog.config console target reconfigured to plain `${message}` layout, Info-only.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~30min |
| Tasks | 2 completed |
| Files modified | 2 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Console output unchanged for operator | Pass | layout=`${message}`; no level/logger prefix; text identical |
| AC-2: All operator output recorded in log file | Pass | file rule minlevel="Debug" unchanged; Info calls go to file |
| AC-3: Warn/Error stay out of operator console | Pass | console rule `levels="Info"` (not minlevel) — Warn/Error file only |
## Accomplishments
- Program.cs: zero Console.Write/Console.WriteLine calls remain
- nlog.config: console target layout=`${message}`, rule `levels="Info"` (exactly Info only)
- Full operator session now timestamped in `logs/nc_program_manager.log`
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `nlog.config` | Modified | Console target: layout `${message}`, rule `levels="Info"` |
| `Program.cs` | Modified | 97 Console.* calls → _log.Info; 103 _log.Info total; 3 _log.Error unchanged |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Multi-line Console.WriteLine → 7 _log.Info calls | One call per field is cleaner; log file shows each field on its own timestamped line | _log.Info count 103 vs plan-predicted 98 |
| Console.ReadLine() preserved | Debug pause, not output — out of scope per plan constraints | grep Console. = 1, not 0 |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 0 | — |
| Scope additions | 0 | — |
| Count discrepancy | 1 | Immaterial |
### Detail
**_log.Info count: 103 vs plan-predicted 98**
- Plan expected: 97 former WriteLine + 1 merged pair = 98
- Actual: multi-line `Console.WriteLine("manufacturer: " + ... "\n" + ...)` was 1 call but split to 7 `_log.Info` calls (one per field)
- Impact: none; all output correct; count difference is additive not subtractive
**grep -c "Console\." = 1, not 0**
- Plan verification regex `Console\.` matches Console.ReadLine() which must stay (debug pause)
- Zero `Console.Write` / `Console.WriteLine` remain — plan intent fully met
- Plan's verify command was slightly too broad
## Issues Encountered
None.
## Next Phase Readiness
**Ready:**
- Phase 15 logging complete — both plans done
- All operator output through NLog with timestamps
- Warn/Error isolated to log file; console clean
**Concerns:**
- None
**Blockers:**
- None — Phase 15 complete; v0.5 milestone complete
---
*Phase: 15-logging, Plan: 02*
*Completed: 2026-05-18*

View file

@ -5,6 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using NcProgramManager; using NcProgramManager;
using NcProgramManager.Cnc; using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using NcProgramManager.Cnc.Models;
@ -24,6 +25,8 @@ namespace NcProgramManager.Cnc.Fanuc
private const string SeedProgramName = "OSTD"; private const string SeedProgramName = "OSTD";
private const string SeedProgramBody = "%\n<OSTD>\n%"; private const string SeedProgramBody = "%\n<OSTD>\n%";
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly FanucConnectionConfig _config; private readonly FanucConnectionConfig _config;
private readonly INcProgramValidator _validator; private readonly INcProgramValidator _validator;
private readonly List<CncError> _currentErrors = new List<CncError>(); private readonly List<CncError> _currentErrors = new List<CncError>();
@ -116,8 +119,8 @@ namespace NcProgramManager.Cnc.Fanuc
public void Dispose() public void Dispose()
{ {
try { DisconnectAsync().GetAwaiter().GetResult(); } catch { } try { DisconnectAsync().GetAwaiter().GetResult(); } catch { /* cleanup — intentionally swallowed */ }
try { _gate.Dispose(); } catch { } try { _gate.Dispose(); } catch { /* cleanup — intentionally swallowed */ }
} }
private async Task<CncResult<T>> RunGuardedAsync<T>(Func<T> body, CancellationToken ct) private async Task<CncResult<T>> RunGuardedAsync<T>(Func<T> body, CancellationToken ct)
@ -186,7 +189,7 @@ namespace NcProgramManager.Cnc.Fanuc
if (ct.IsCancellationRequested) if (ct.IsCancellationRequested)
{ {
try { Focas1.cnc_freelibhndl(_hndl); } catch { } try { Focas1.cnc_freelibhndl(_hndl); } catch { /* cleanup — intentionally swallowed */ }
SetState(ConnectionState.Disconnected); SetState(ConnectionState.Disconnected);
ct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
} }
@ -720,7 +723,7 @@ namespace NcProgramManager.Cnc.Fanuc
if (FanucErrorTranslator.IsDisconnectError(code)) if (FanucErrorTranslator.IsDisconnectError(code))
{ {
SetState(ConnectionState.Faulted); SetState(ConnectionState.Faulted);
try { Focas1.cnc_freelibhndl(_hndl); } catch { } try { Focas1.cnc_freelibhndl(_hndl); } catch { /* cleanup — intentionally swallowed */ }
SetState(ConnectionState.Disconnected); SetState(ConnectionState.Disconnected);
} }
} }
@ -732,7 +735,7 @@ namespace NcProgramManager.Cnc.Fanuc
var h = StateChanged; var h = StateChanged;
if (h != null) if (h != null)
{ {
try { h(this, next); } catch { } try { h(this, next); } catch (Exception ex) { _log.Warn(ex, "StateChanged handler threw"); }
} }
} }
} }

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using NcProgramManager.Cnc; using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using NcProgramManager.Cnc.Models;
@ -10,6 +11,8 @@ namespace NcProgramManager.Cnc.Heidenhain
{ {
public sealed class HeidenhainMachine : ICncMachine public sealed class HeidenhainMachine : ICncMachine
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly HeidenhainConnectionConfig _config; private readonly HeidenhainConnectionConfig _config;
private readonly INcProgramValidator _validator; private readonly INcProgramValidator _validator;
private readonly List<CncError> _errors = new List<CncError>(); private readonly List<CncError> _errors = new List<CncError>();
@ -135,8 +138,8 @@ namespace NcProgramManager.Cnc.Heidenhain
public void Dispose() public void Dispose()
{ {
try { DisconnectAsync().GetAwaiter().GetResult(); } catch { } try { DisconnectAsync().GetAwaiter().GetResult(); } catch { /* cleanup — intentionally swallowed */ }
try { _gate.Dispose(); } catch { } try { _gate.Dispose(); } catch { /* cleanup — intentionally swallowed */ }
} }
// ── private ────────────────────────────────────────────────────────── // ── private ──────────────────────────────────────────────────────────
@ -186,7 +189,7 @@ namespace NcProgramManager.Cnc.Heidenhain
var h = StateChanged; var h = StateChanged;
if (h != null) if (h != null)
{ {
try { h(this, next); } catch { } try { h(this, next); } catch (Exception ex) { _log.Warn(ex, "StateChanged handler threw"); }
} }
} }

View file

@ -1,12 +1,15 @@
using System; using System;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using NLog;
namespace NcProgramManager.Cnc.Heidenhain namespace NcProgramManager.Cnc.Heidenhain
{ {
/// <summary>LSV2 protocol client for Heidenhain TNC controls.</summary> /// <summary>LSV2 protocol client for Heidenhain TNC controls.</summary>
internal sealed class Lsv2Client : ILsv2Client, IDisposable internal sealed class Lsv2Client : ILsv2Client, IDisposable
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private TcpClient _tcp; private TcpClient _tcp;
private NetworkStream _stream; private NetworkStream _stream;
private readonly HeidenhainConnectionConfig _config; private readonly HeidenhainConnectionConfig _config;
@ -27,24 +30,28 @@ namespace NcProgramManager.Cnc.Heidenhain
public bool Connect() public bool Connect()
{ {
_log.Info("Connecting to {0}:{1}", _config.IpAddress, _config.Port);
_tcp = new TcpClient(); _tcp = new TcpClient();
_tcp.SendTimeout = (int)_config.ConnectTimeout.TotalMilliseconds; _tcp.SendTimeout = (int)_config.ConnectTimeout.TotalMilliseconds;
_tcp.ReceiveTimeout = (int)_config.ConnectTimeout.TotalMilliseconds; _tcp.ReceiveTimeout = (int)_config.ConnectTimeout.TotalMilliseconds;
var connectTask = _tcp.ConnectAsync(_config.IpAddress, _config.Port); var connectTask = _tcp.ConnectAsync(_config.IpAddress, _config.Port);
if (!connectTask.Wait((int)_config.ConnectTimeout.TotalMilliseconds)) if (!connectTask.Wait((int)_config.ConnectTimeout.TotalMilliseconds))
{ {
try { _tcp.Close(); } catch { } try { _tcp.Close(); } catch { /* cleanup — intentionally swallowed */ }
return false; return false;
} }
_stream = _tcp.GetStream(); _stream = _tcp.GetStream();
return Login(); bool ok = Login();
if (ok) _log.Info("Connected to {0}", _config.IpAddress);
return ok;
} }
public void Disconnect() public void Disconnect()
{ {
try { SendCommand(T_LS_LOG_OUT, new byte[0]); } catch { } _log.Info("Disconnecting from {0}", _config.IpAddress);
try { if (_stream != null) _stream.Close(); } catch { } try { SendCommand(T_LS_LOG_OUT, new byte[0]); } catch { /* cleanup — intentionally swallowed */ }
try { if (_tcp != null) _tcp.Close(); } catch { } try { if (_stream != null) _stream.Close(); } catch { /* cleanup — intentionally swallowed */ }
try { if (_tcp != null) _tcp.Close(); } catch { /* cleanup — intentionally swallowed */ }
} }
/// <summary>Upload file from NC to PC. Returns file content or null on failure.</summary> /// <summary>Upload file from NC to PC. Returns file content or null on failure.</summary>

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using NcProgramManager.Cnc; using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using NcProgramManager.Cnc.Models;
@ -10,6 +11,8 @@ namespace NcProgramManager.Cnc.Mitsubishi
{ {
public sealed class MitsubishiMachine : ICncMachine public sealed class MitsubishiMachine : ICncMachine
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly MitsubishiConnectionConfig _config; private readonly MitsubishiConnectionConfig _config;
private readonly IMitsubishiFtpClient _ftp; private readonly IMitsubishiFtpClient _ftp;
private readonly INcProgramValidator _validator; private readonly INcProgramValidator _validator;
@ -149,8 +152,8 @@ namespace NcProgramManager.Cnc.Mitsubishi
public void Dispose() public void Dispose()
{ {
try { DisconnectAsync().GetAwaiter().GetResult(); } catch { } try { DisconnectAsync().GetAwaiter().GetResult(); } catch { /* cleanup — intentionally swallowed */ }
try { _gate.Dispose(); } catch { } try { _gate.Dispose(); } catch { /* cleanup — intentionally swallowed */ }
} }
private async Task<CncResult<T>> RunGuardedAsync<T>(Func<T> body, CancellationToken ct) private async Task<CncResult<T>> RunGuardedAsync<T>(Func<T> body, CancellationToken ct)
@ -192,7 +195,7 @@ namespace NcProgramManager.Cnc.Mitsubishi
var h = StateChanged; var h = StateChanged;
if (h != null) if (h != null)
{ {
try { h(this, next); } catch { } try { h(this, next); } catch (Exception ex) { _log.Warn(ex, "StateChanged handler threw"); }
} }
} }

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using NcProgramManager.Cnc; using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using NcProgramManager.Cnc.Models;
@ -10,6 +11,8 @@ namespace NcProgramManager.Cnc.Siemens
{ {
public sealed class SiemensMachine : ICncMachine public sealed class SiemensMachine : ICncMachine
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly SiemensConnectionConfig _config; private readonly SiemensConnectionConfig _config;
private readonly ISiemensFtpClient _ftp; private readonly ISiemensFtpClient _ftp;
private readonly INcProgramValidator _validator; private readonly INcProgramValidator _validator;
@ -149,8 +152,8 @@ namespace NcProgramManager.Cnc.Siemens
public void Dispose() public void Dispose()
{ {
try { DisconnectAsync().GetAwaiter().GetResult(); } catch { } try { DisconnectAsync().GetAwaiter().GetResult(); } catch { /* cleanup — intentionally swallowed */ }
try { _gate.Dispose(); } catch { } try { _gate.Dispose(); } catch { /* cleanup — intentionally swallowed */ }
} }
private async Task<CncResult<T>> RunGuardedAsync<T>(Func<T> body, CancellationToken ct) private async Task<CncResult<T>> RunGuardedAsync<T>(Func<T> body, CancellationToken ct)
@ -192,7 +195,7 @@ namespace NcProgramManager.Cnc.Siemens
var h = StateChanged; var h = StateChanged;
if (h != null) if (h != null)
{ {
try { h(this, next); } catch { } try { h(this, next); } catch (Exception ex) { _log.Warn(ex, "StateChanged handler threw"); }
} }
} }

View file

@ -70,6 +70,10 @@
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>packages\NLog.5.2.8\lib\net46\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ArgParser.cs" /> <Compile Include="ArgParser.cs" />
@ -129,6 +133,9 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="nlog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Cnc\Fanuc\libs\Fwlib32.dll"> <Content Include="Cnc\Fanuc\libs\Fwlib32.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using NLog;
using NcProgramManager.Cnc; using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models; using NcProgramManager.Cnc.Models;
using NcProgramManager.Licensing; using NcProgramManager.Licensing;
@ -9,6 +10,8 @@ namespace NcProgramManager
{ {
internal class Program internal class Program
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
public static int Main(string[] args) public static int Main(string[] args)
{ {
InputArgs inputArgs; InputArgs inputArgs;
@ -18,9 +21,9 @@ namespace NcProgramManager
} }
catch (Exception e) catch (Exception e)
{ {
Console.Write("Gestore Programmi FANUC: "); _log.Error(e, "Arg parsing failed");
Console.WriteLine(e.Message); _log.Info("Gestore Programmi FANUC: {0}", e.Message);
Console.WriteLine("Errore parametri input. Consulta `-help' per maggiori informazioni."); _log.Info("Errore parametri input. Consulta `-help' per maggiori informazioni.");
VediErrore(-101, false); VediErrore(-101, false);
return -101; return -101;
} }
@ -38,7 +41,7 @@ namespace NcProgramManager
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"); _log.Info("Licenza ERRATA");
VediErrore(-100, inputArgs.debugMode); VediErrore(-100, inputArgs.debugMode);
return -1; return -1;
} }
@ -50,15 +53,13 @@ namespace NcProgramManager
return -102; return -102;
} }
Console.WriteLine( _log.Info("manufacturer: {0}", inputArgs.manufacturer);
"manufacturer: " + inputArgs.manufacturer + "\n" + _log.Info("IndirizzoIP: {0}", inputArgs.ip);
"IndirizzoIP: " + inputArgs.ip + "\n" + _log.Info("Porta: {0}", inputArgs.porta);
"Porta: " + inputArgs.porta + "\n" + _log.Info("Compatibility: {0}", inputArgs.compatibilityMode);
"Compatibility: " + inputArgs.compatibilityMode + "\n" + _log.Info("commentoProgramma: {0}", inputArgs.commentoProgramma);
"commentoProgramma: " + inputArgs.commentoProgramma + "\n" + _log.Info("pathCNC: {0}", inputArgs.pathCNC);
"pathCNC: " + inputArgs.pathCNC + "\n" + _log.Info("pathLocaleProgramma: {0}", inputArgs.pathLocaleProgramma);
"pathLocaleProgramma: " + inputArgs.pathLocaleProgramma + "\n"
);
ICncMachine machine = null; ICncMachine machine = null;
try try
@ -83,23 +84,23 @@ namespace NcProgramManager
static int Scarica(ICncMachine macchina, InputArgs inputArgs) static int Scarica(ICncMachine macchina, InputArgs inputArgs)
{ {
Console.WriteLine("RICEVI DA CNC-->"); _log.Info("RICEVI DA CNC-->");
bool connected = false; 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"); _log.Info("Macchina non connessa");
PrintErrors(connResult.Errors); PrintErrors(connResult.Errors);
VediErrore(-201, inputArgs.debugMode); VediErrore(-201, inputArgs.debugMode);
return -201; return -201;
} }
connected = true; connected = true;
Console.WriteLine("Inizio RICEZIONE NCProgram"); _log.Info("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"); _log.Info("Fine RICEZIONE NCProgram");
if (!progResult.Success || string.IsNullOrEmpty(progResult.Value)) if (!progResult.Success || string.IsNullOrEmpty(progResult.Value))
{ {
PrintErrors(progResult.Errors); PrintErrors(progResult.Errors);
@ -114,13 +115,13 @@ namespace NcProgramManager
{ {
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("ToolOffset non supportato per questo produttore"); _log.Info("ToolOffset non supportato per questo produttore");
VediErrore(-205, inputArgs.debugMode); VediErrore(-205, inputArgs.debugMode);
return -205; return -205;
} }
Console.WriteLine("Inizio RICEZIONE ToolOffsetData"); _log.Info("Inizio RICEZIONE ToolOffsetData");
var toolResult = fanuc.ReadToolDataAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult(); var toolResult = fanuc.ReadToolDataAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult();
Console.WriteLine("Fine RICEZIONE ToolOffsetData"); _log.Info("Fine RICEZIONE ToolOffsetData");
if (!toolResult.Success || string.IsNullOrEmpty(toolResult.Value)) if (!toolResult.Success || string.IsNullOrEmpty(toolResult.Value))
{ {
PrintErrors(toolResult.Errors); PrintErrors(toolResult.Errors);
@ -134,13 +135,13 @@ namespace NcProgramManager
{ {
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("WorkZeroOffset non supportato per questo produttore"); _log.Info("WorkZeroOffset non supportato per questo produttore");
VediErrore(-206, inputArgs.debugMode); VediErrore(-206, inputArgs.debugMode);
return -206; return -206;
} }
Console.WriteLine("Inizio RICEZIONE WorkZeroOffsetData"); _log.Info("Inizio RICEZIONE WorkZeroOffsetData");
var workResult = fanuc.ReadWorkZeroDataAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult(); var workResult = fanuc.ReadWorkZeroDataAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult();
Console.WriteLine("Fine RICEZIONE WorkZeroOffsetData"); _log.Info("Fine RICEZIONE WorkZeroOffsetData");
if (!workResult.Success || string.IsNullOrEmpty(workResult.Value)) if (!workResult.Success || string.IsNullOrEmpty(workResult.Value))
{ {
PrintErrors(workResult.Errors); PrintErrors(workResult.Errors);
@ -156,7 +157,8 @@ namespace NcProgramManager
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); _log.Error(ex, "Scarica failed");
_log.Info("{0}", ex.Message);
VediErrore(-202, inputArgs.debugMode); VediErrore(-202, inputArgs.debugMode);
return -202; return -202;
} }
@ -169,14 +171,14 @@ namespace NcProgramManager
static int Invia(ICncMachine macchina, InputArgs inputArgs) static int Invia(ICncMachine macchina, InputArgs inputArgs)
{ {
Console.WriteLine("INVIA A CNC-->"); _log.Info("INVIA A CNC-->");
bool connected = false; 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"); _log.Info("Macchina non connessa");
PrintErrors(connResult.Errors); PrintErrors(connResult.Errors);
VediErrore(-201, inputArgs.debugMode); VediErrore(-201, inputArgs.debugMode);
return -201; return -201;
@ -189,7 +191,7 @@ namespace NcProgramManager
if (programma.Program != null) if (programma.Program != null)
{ {
Console.WriteLine("Inizio INVIO NCProgram"); _log.Info("Inizio INVIO NCProgram");
var cncProg = new CncProgram var cncProg = new CncProgram
{ {
Name = programma.GetProgramTitle(), Name = programma.GetProgramTitle(),
@ -198,7 +200,7 @@ namespace NcProgramManager
Path = inputArgs.pathCNC.ToString() Path = inputArgs.pathCNC.ToString()
}; };
var writeResult = macchina.WriteProgramAsync(inputArgs.pathCNC.ToString(), cncProg).GetAwaiter().GetResult(); var writeResult = macchina.WriteProgramAsync(inputArgs.pathCNC.ToString(), cncProg).GetAwaiter().GetResult();
Console.WriteLine("Fine INVIO NCProgram"); _log.Info("Fine INVIO NCProgram");
if (!writeResult.Success) if (!writeResult.Success)
{ {
PrintErrors(writeResult.Errors); PrintErrors(writeResult.Errors);
@ -213,13 +215,13 @@ namespace NcProgramManager
{ {
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("ToolOffset non supportato per questo produttore"); _log.Info("ToolOffset non supportato per questo produttore");
VediErrore(-205, inputArgs.debugMode); VediErrore(-205, inputArgs.debugMode);
return -205; return -205;
} }
Console.WriteLine("Inizio INVIO ToolOffsetData"); _log.Info("Inizio INVIO ToolOffsetData");
var toolResult = fanuc.WriteToolDataAsync(inputArgs.pathCNC.ToString(), programma.ToolOffsetData).GetAwaiter().GetResult(); var toolResult = fanuc.WriteToolDataAsync(inputArgs.pathCNC.ToString(), programma.ToolOffsetData).GetAwaiter().GetResult();
Console.WriteLine("Fine INVIO ToolOffsetData"); _log.Info("Fine INVIO ToolOffsetData");
if (!toolResult.Success) if (!toolResult.Success)
{ {
PrintErrors(toolResult.Errors); PrintErrors(toolResult.Errors);
@ -232,13 +234,13 @@ namespace NcProgramManager
{ {
if (fanuc == null) if (fanuc == null)
{ {
Console.WriteLine("WorkZeroOffset non supportato per questo produttore"); _log.Info("WorkZeroOffset non supportato per questo produttore");
VediErrore(-206, inputArgs.debugMode); VediErrore(-206, inputArgs.debugMode);
return -206; return -206;
} }
Console.WriteLine("Inizio INVIO WorkZeroOffsetData"); _log.Info("Inizio INVIO WorkZeroOffsetData");
var workResult = fanuc.WriteWorkZeroDataAsync(inputArgs.pathCNC.ToString(), programma.WorkZeroOffsetData).GetAwaiter().GetResult(); var workResult = fanuc.WriteWorkZeroDataAsync(inputArgs.pathCNC.ToString(), programma.WorkZeroOffsetData).GetAwaiter().GetResult();
Console.WriteLine("Fine INVIO WorkZeroOffsetData"); _log.Info("Fine INVIO WorkZeroOffsetData");
if (!workResult.Success) if (!workResult.Success)
{ {
PrintErrors(workResult.Errors); PrintErrors(workResult.Errors);
@ -252,7 +254,8 @@ namespace NcProgramManager
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); _log.Error(ex, "Invia failed");
_log.Info("{0}", ex.Message);
VediErrore(-202, inputArgs.debugMode); VediErrore(-202, inputArgs.debugMode);
return -202; return -202;
} }
@ -267,87 +270,87 @@ namespace NcProgramManager
{ {
if (errors == null) return; if (errors == null) return;
foreach (var e in errors) foreach (var e in errors)
Console.WriteLine(" " + e.ToString()); _log.Info(" {0}", e.ToString());
} }
static void VediErrore(int errore, bool debugMode) static void VediErrore(int errore, bool debugMode)
{ {
Console.WriteLine("Errore:" + errore); _log.Info("Errore:{0}", errore);
Console.WriteLine(); _log.Info("");
Console.WriteLine("Codici Errori:"); _log.Info("Codici Errori:");
Console.WriteLine("-100 : Licenza Errata/non valida."); _log.Info("-100 : Licenza Errata/non valida.");
Console.WriteLine("-101 : Impossibile parsare i parametri."); _log.Info("-101 : Impossibile parsare i parametri.");
Console.WriteLine("-102 : Parametri mancanti. "); _log.Info("-102 : Parametri mancanti. ");
Console.WriteLine("-103 : Codice connessione errato: 3->Ethernet 2->HSSB. "); _log.Info("-103 : Codice connessione errato: 3->Ethernet 2->HSSB. ");
Console.WriteLine("-201 : Macchina non connessa. "); _log.Info("-201 : Macchina non connessa. ");
Console.WriteLine("-202 : Eccezione nell'esecuzione dell'AZIONE. "); _log.Info("-202 : Eccezione nell'esecuzione dell'AZIONE. ");
Console.WriteLine("-203 : L'azione è fallita. "); _log.Info("-203 : L'azione è fallita. ");
Console.WriteLine("-204 : Errore in fase invio/ricezione Programma CN. "); _log.Info("-204 : Errore in fase invio/ricezione Programma CN. ");
Console.WriteLine("-205 : Errore in fase invio/ricezione tool offset data "); _log.Info("-205 : Errore in fase invio/ricezione tool offset data ");
Console.WriteLine("-206 : Errore in fase invio/ricezione work zero offest data"); _log.Info("-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"); _log.Info("-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."); _log.Info("-208 : Programma CN Caricato ma non settato a main.");
if (debugMode) if (debugMode)
Console.ReadLine(); Console.ReadLine();
} }
static void MostraAiuto() static void MostraAiuto()
{ {
Console.WriteLine("Utilizzo del Software gestore Programmi FANUC"); _log.Info("Utilizzo del Software gestore Programmi FANUC");
Console.WriteLine("Le funzioni pricipali sono due."); _log.Info("Le funzioni pricipali sono due.");
Console.WriteLine("INVIA: caricamento di programma su CNC."); _log.Info("INVIA: caricamento di programma su CNC.");
Console.WriteLine("SCARICA: scaricamento di programma su CNC."); _log.Info("SCARICA: scaricamento di programma su CNC.");
Console.WriteLine(); _log.Info("");
Console.WriteLine(); _log.Info("");
Console.WriteLine("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= "); _log.Info("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= ");
Console.WriteLine(); _log.Info("");
Console.WriteLine("Il file 'license.lic' deve essere posizionato nella stessa cartella dell'eseguibile."); _log.Info("Il file 'license.lic' deve essere posizionato nella stessa cartella dell'eseguibile.");
Console.WriteLine("-azione="); _log.Info("-azione=");
Console.WriteLine(" l'{AZIONE} da eseguire."); _log.Info(" l'{AZIONE} da eseguire.");
Console.WriteLine("-manufacturer="); _log.Info("-manufacturer=");
Console.WriteLine(" Il produttore del CNC: fanuc (default), heidenhain, siemens, mitsubishi"); _log.Info(" Il produttore del CNC: fanuc (default), heidenhain, siemens, mitsubishi");
Console.WriteLine("-tipo="); _log.Info("-tipo=");
Console.WriteLine(" Il tipo di connessione utilizzato dalla macchina: 3 per ethernet\n 2 per HSSB (solo Fanuc, legacy)"); _log.Info(" Il tipo di connessione utilizzato dalla macchina: 3 per ethernet\n 2 per HSSB (solo Fanuc, legacy)");
Console.WriteLine("-ip="); _log.Info("-ip=");
Console.WriteLine(" Dato per Ethernet indirizzo IP della macchina"); _log.Info(" Dato per Ethernet indirizzo IP della macchina");
Console.WriteLine("-porta="); _log.Info("-porta=");
Console.WriteLine(" Dato per Ethernet PORTA di connessione"); _log.Info(" Dato per Ethernet PORTA di connessione");
Console.WriteLine("-nodo="); _log.Info("-nodo=");
Console.WriteLine(" Dato per HSSB Nodo di connesione (solo Fanuc)"); _log.Info(" Dato per HSSB Nodo di connesione (solo Fanuc)");
Console.WriteLine("-username="); _log.Info("-username=");
Console.WriteLine(" Username per autenticazione (Heidenhain/Siemens)"); _log.Info(" Username per autenticazione (Heidenhain/Siemens)");
Console.WriteLine("-password="); _log.Info("-password=");
Console.WriteLine(" Password per autenticazione (Heidenhain/Siemens)"); _log.Info(" Password per autenticazione (Heidenhain/Siemens)");
Console.WriteLine("-compatibility"); _log.Info("-compatibility");
Console.WriteLine(" Attivazione modalità compatibilità"); _log.Info(" Attivazione modalità compatibilità");
Console.WriteLine("-commento="); _log.Info("-commento=");
Console.WriteLine(" Commento da applicare al programma scelto"); _log.Info(" Commento da applicare al programma scelto");
Console.WriteLine("-path="); _log.Info("-path=");
Console.WriteLine(" Path dei programmi del CNC dove vengono salvati i programmi, se non definito è impostato a 1 cioè //CNC_MEM/USER/PATH1/"); _log.Info(" Path dei programmi del CNC dove vengono salvati i programmi, se non definito è impostato a 1 cioè //CNC_MEM/USER/PATH1/");
Console.WriteLine("-pathprogramma="); _log.Info("-pathprogramma=");
Console.WriteLine(" Necessario per INVIO: Path del programma locale da caricare sul CNC"); _log.Info(" Necessario per INVIO: Path del programma locale da caricare sul CNC");
Console.WriteLine("-workzero="); _log.Info("-workzero=");
Console.WriteLine(" Invia/Scarica anche i dati relativi al workzero offset"); _log.Info(" Invia/Scarica anche i dati relativi al workzero offset");
Console.WriteLine("-tooloffset="); _log.Info("-tooloffset=");
Console.WriteLine(" Invia/Scarica anche i dati relativi al tool offset"); _log.Info(" Invia/Scarica anche i dati relativi al tool offset");
Console.WriteLine("-debug="); _log.Info("-debug=");
Console.WriteLine(" Mantiene il terminale aperto alla fine delle operazioni"); _log.Info(" Mantiene il terminale aperto alla fine delle operazioni");
Console.WriteLine("-help"); _log.Info("-help");
Console.WriteLine(" Mostra questo messaggio e termina"); _log.Info(" Mostra questo messaggio e termina");
Console.WriteLine(); _log.Info("");
Console.WriteLine("Codici Errori:"); _log.Info("Codici Errori:");
Console.WriteLine("-100 : Licenza Errata/non valida."); _log.Info("-100 : Licenza Errata/non valida.");
Console.WriteLine("-101 : Impossibile parsare i parametri."); _log.Info("-101 : Impossibile parsare i parametri.");
Console.WriteLine("-102 : Parametri mancanti. "); _log.Info("-102 : Parametri mancanti. ");
Console.WriteLine("-103 : Codice connessione errato: 3->Ethernet 2->HSSB. "); _log.Info("-103 : Codice connessione errato: 3->Ethernet 2->HSSB. ");
Console.WriteLine("-201 : Macchina non connessa. "); _log.Info("-201 : Macchina non connessa. ");
Console.WriteLine("-202 : Eccezione nell'esecuzione dell'AZIONE. "); _log.Info("-202 : Eccezione nell'esecuzione dell'AZIONE. ");
Console.WriteLine("-203 : L'azione è fallita. "); _log.Info("-203 : L'azione è fallita. ");
Console.WriteLine("-204 : Errore in fase invio/ricezione Programma CN. "); _log.Info("-204 : Errore in fase invio/ricezione Programma CN. ");
Console.WriteLine("-205 : Errore in fase invio/ricezione tool offset data "); _log.Info("-205 : Errore in fase invio/ricezione tool offset data ");
Console.WriteLine("-206 : Errore in fase invio/ricezione work zero offest data"); _log.Info("-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"); _log.Info("-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."); _log.Info("-208 : Programma CN Caricato ma non settato a main.");
} }
} }
} }

27
nlog.config Normal file
View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true">
<targets>
<target xsi:type="File"
name="logfile"
fileName="${basedir}/logs/nc_program_manager.log"
archiveFileName="${basedir}/logs/nc_program_manager.{#}.log"
archiveEvery="Day"
archiveNumbering="Date"
maxArchiveFiles="7"
layout="${longdate} ${level:uppercase=true:padding=5} [${logger:shortName=true}] ${message}${onexception:${newline}${exception:format=tostring}}" />
<target xsi:type="Console"
name="console"
layout="${message}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
<logger name="*" levels="Info" writeTo="console" />
</rules>
</nlog>

View file

@ -5,6 +5,7 @@
<package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0" targetFramework="net472" /> <package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0" targetFramework="net472" />
<package id="Microsoft.NETCore.Platforms" version="7.0.2" targetFramework="net472" /> <package id="Microsoft.NETCore.Platforms" version="7.0.2" targetFramework="net472" />
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net472" /> <package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net472" />
<package id="NLog" version="5.2.8" targetFramework="net472" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net472" /> <package id="NETStandard.Library" version="2.0.3" targetFramework="net472" />
<package id="System.AppContext" version="4.3.0" targetFramework="net472" /> <package id="System.AppContext" version="4.3.0" targetFramework="net472" />
<package id="System.Buffers" version="4.5.1" targetFramework="net472" /> <package id="System.Buffers" version="4.5.1" targetFramework="net472" />