- FtpListParser: tolerant DOS/IIS regex (optional AM/PM, 2-or-4-digit year, 1-or-2-digit hour) for 24h/locale-dependent Mazak servers; NLST fallback now only accepts single-token lines (multi-token garbage/header -> skip) to avoid whole-line-as-filename. - MazakMachine.BuildTreeAsync: root-level LIST failure now propagates -> Fail (no more silent exit 0 on a diagnostic probe). Children keep best-effort inline "errore lista" swallow. Depth semantics preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
97 lines
3.9 KiB
C#
97 lines
3.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Text;
|
|
|
|
namespace NcProgramManager.Cnc.Mazak
|
|
{
|
|
/// <summary>Client FTP per trasferimento programmi Mazak CNC.</summary>
|
|
internal sealed class MazakFtpClient : IMazakFtpClient
|
|
{
|
|
private readonly MazakConnectionConfig _config;
|
|
|
|
public MazakFtpClient(MazakConnectionConfig config)
|
|
{
|
|
_config = config ?? throw new ArgumentNullException("config");
|
|
}
|
|
|
|
/// <summary>Verifica raggiungibilita server FTP listando la directory programmi.</summary>
|
|
public bool Ping()
|
|
{
|
|
try
|
|
{
|
|
var req = CreateRequest(_config.ProgramDirectory, WebRequestMethods.Ftp.ListDirectory);
|
|
using (var resp = (FtpWebResponse)req.GetResponse())
|
|
return resp.StatusCode == FtpStatusCode.DataAlreadyOpen
|
|
|| resp.StatusCode == FtpStatusCode.OpeningData
|
|
|| (int)resp.StatusCode / 100 == 2;
|
|
}
|
|
catch { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lista una directory FTP (LIST/ListDirectoryDetails). Ritorna file e
|
|
/// sotto-cartelle con nome, path completo, flag directory e dimensione.
|
|
/// Parsa sia il formato Unix (drwx...) sia quello DOS/IIS (MM-DD-YY ... <DIR>),
|
|
/// cioe' i due layout esposti dai controlli Mazak Smooth/Matrix.
|
|
/// </summary>
|
|
public IReadOnlyList<FtpEntry> ListDirectory(string path)
|
|
{
|
|
string dir = FtpListParser.NormalizeDir(path);
|
|
var req = CreateRequest(dir, WebRequestMethods.Ftp.ListDirectoryDetails);
|
|
var entries = new List<FtpEntry>();
|
|
using (var resp = (FtpWebResponse)req.GetResponse())
|
|
using (var reader = new StreamReader(resp.GetResponseStream(), Encoding.ASCII))
|
|
{
|
|
string line;
|
|
while ((line = reader.ReadLine()) != null)
|
|
{
|
|
var entry = FtpListParser.Parse(line, dir);
|
|
if (entry != null)
|
|
entries.Add(entry);
|
|
}
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
/// <summary>Scarica file dal Mazak in stringa.</summary>
|
|
public string DownloadFile(string ncPath)
|
|
{
|
|
var req = CreateRequest(ncPath, WebRequestMethods.Ftp.DownloadFile);
|
|
using (var resp = (FtpWebResponse)req.GetResponse())
|
|
using (var reader = new StreamReader(resp.GetResponseStream(), Encoding.ASCII))
|
|
return reader.ReadToEnd();
|
|
}
|
|
|
|
/// <summary>Carica contenuto stringa su Mazak.</summary>
|
|
public void UploadFile(string ncPath, string content)
|
|
{
|
|
var req = CreateRequest(ncPath, WebRequestMethods.Ftp.UploadFile);
|
|
byte[] data = Encoding.ASCII.GetBytes(content);
|
|
req.ContentLength = data.Length;
|
|
using (var stream = req.GetRequestStream())
|
|
stream.Write(data, 0, data.Length);
|
|
using ((FtpWebResponse)req.GetResponse()) { }
|
|
}
|
|
|
|
public void DeleteFile(string ncPath)
|
|
{
|
|
var req = CreateRequest(ncPath, WebRequestMethods.Ftp.DeleteFile);
|
|
using ((FtpWebResponse)req.GetResponse()) { }
|
|
}
|
|
|
|
private FtpWebRequest CreateRequest(string path, string method)
|
|
{
|
|
string uri = string.Format("ftp://{0}:{1}{2}", _config.IpAddress, _config.Port, path);
|
|
var req = (FtpWebRequest)WebRequest.Create(uri);
|
|
req.Method = method;
|
|
req.Credentials = new NetworkCredential(_config.Username, _config.Password);
|
|
req.Timeout = (int)_config.RequestTimeout.TotalMilliseconds;
|
|
req.KeepAlive = false;
|
|
req.UseBinary = false;
|
|
req.UsePassive = true;
|
|
return req;
|
|
}
|
|
}
|
|
}
|