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:
parent
45ca8f5a47
commit
60ce1c9d57
18 changed files with 1040 additions and 169 deletions
|
|
@ -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) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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*
|
||||||
|
|
|
||||||
|
|
@ -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.*
|
||||||
|
|
|
||||||
|
|
@ -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*
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
317
.paul/phases/15-logging/15-01-PLAN.md
Normal file
317
.paul/phases/15-logging/15-01-PLAN.md
Normal 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>
|
||||||
159
.paul/phases/15-logging/15-01-SUMMARY.md
Normal file
159
.paul/phases/15-logging/15-01-SUMMARY.md
Normal 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*
|
||||||
194
.paul/phases/15-logging/15-02-PLAN.md
Normal file
194
.paul/phases/15-logging/15-02-PLAN.md
Normal 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>
|
||||||
114
.paul/phases/15-logging/15-02-SUMMARY.md
Normal file
114
.paul/phases/15-logging/15-02-SUMMARY.md
Normal 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*
|
||||||
|
|
@ -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"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
215
Program.cs
215
Program.cs
|
|
@ -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
27
nlog.config
Normal 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>
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue