Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobNet/Ftp.cs
T
Samuele Locatelli 3effc621db EgwCoreLib.FTP
- Update conf FTP Server x avere mirror verbosi
2024-10-07 17:36:38 +02:00

608 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using EgwProxy.Ftp;
using FluentFTP;
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
namespace IOB_WIN_NEXT.IobNet
{
/// <summary>
/// Classe gestione sync via FTP
/// </summary>
public class Ftp : Iob.Generic
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto EgwCoreLib.Ftp
/// - gestione dei task da svolgere da configurazione json specifica
/// - specializzazione da conf e non da codice
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public Ftp(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init IobFtp Client");
// init datetime counters
DateTime adesso = DateTime.Now;
lastPzCountSend = adesso;
lastWarnODL = adesso;
vetoCheckStatus = adesso;
// 2023.09.05 imposto anche primo ping e check disconnected...
lastPING = adesso;
lastDisconnCheck = adesso;
var VETO_PING_SEC = getOptPar("VETO_PING_SEC");
if (!string.IsNullOrEmpty(VETO_PING_SEC))
{
int.TryParse(VETO_PING_SEC, out vetoPingSec);
}
var POWEROFF_TIMEOUT_SEC = getOptPar("POWEROFF_TIMEOUT_SEC");
if (!string.IsNullOrEmpty(POWEROFF_TIMEOUT_SEC))
{
int.TryParse(POWEROFF_TIMEOUT_SEC, out PoweroffTimeoutSec);
}
// fix coda ping
PingQueue = new DataQueue("000", "PingQueue", false);
// carico conf specifica steps FTP
string ftpConfFile = getOptPar("FTP_PARAM");
if (!string.IsNullOrEmpty(ftpConfFile))
{
loadFtpConfFile(ftpConfFile);
}
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// il compito della richiesta è SEMPRE scrivere folder dell'ODL corrente SE NON LA TROVASSE
// uso metodo base x salvare esito scrittura
var writeResult = base.executeTasks(task2exe);
#if false
/*---------------------------------------------------------------------------
* va creata una folder x ogni ODL (una volta LANCIATO da tablet) APERTO
* - nella folder scriviamo un file con articolo, qta, commessa
* - la folder sarà usata x salvare OGNI file necessario e di rilevazione
*---------------------------------------------------------------------------*/
// aggiungo comportamento custom: se ho impostato nome ricetta (programma) --> imposto
// richiesta caricamento se ho richiesto reset o fine lavoro --> imposto azzeramento
// esco restituendo risutlato scrittura iniziali
if (task2exe != null)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici x OMP
foreach (var item in task2exe)
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
#if false
case taskType.setProg:
// recupero dati da memMap...
if (memMap != null && memMap.mMapWrite != null)
{
if (memMap.mMapWrite.ContainsKey(item.Key))
{
dataConf currMem = memMap.mMapWrite[item.Key];
string addr = currMem.memAddr;
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = item.Value;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
}
// salvo in currProd..
saveProdData(new KeyValuePair<string, string>(item.Key, item.Value));
break;
case taskType.startSetup:
setFineLotto();
break;
case taskType.stopSetup:
setInizioProd();
break;
case taskType.syncDbData:
processDataSync();
break;
#endif
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
}
}
else
{
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
}
}
#endif
return writeResult;
}
/// <summary>
/// Effettua processing CUSTOM x FTP:
/// - prende ogni conf specifica
/// - esegue step
/// - registra eventuali DynData da salvare
/// </summary>
public override void processCustomTaskLF()
{
lgInfo($"Richiesto processCustomTaskLF");
// verifico di avere compiti da svolgere...
if (currFtpTaskList != null && currFtpTaskList.ListTask != null && currFtpTaskList.ListTask.Count > 0)
{
string sVal = "";
foreach (var srvFtp in currFtpTaskList.ListTask)
{
// verifico eventuale veto all'esecuzione...
if (ActionEnabled(srvFtp))
{
// imposto nuovo veto...
ActionResetVeto(srvFtp);
// ora setup server FTP x item...
ftpClient = new Manager(srvFtp.ServerAddr, srvFtp.ConnUser, srvFtp.ConnPasswd, srvFtp.RawCert, srvFtp.SkipCert);
// test server ok...
if (ftpClient.serverOk())
{
int stepDone = 0;
string srvType = ftpClient.serverType();
lgTrace($"Connesso a server {srvType} | {srvFtp.ServerAddr} | inizio processing {srvFtp.StepsList.Count} steps");
// ciclo tra i vari steps!
foreach (var step in srvFtp.StepsList)
{
bool fatto = doStep(step);
stepDone += fatto ? 1 : 0;
// modificare: ricevere elenco di TUTTI i file con esito
#if false
// per i file SCARICATI registrare in FLog...
string key = "FtpSync";
string value = "SRC --> DEST | MB | ms";
sVal = $"{key} | {value}";
accodaFLog(sVal, qEncodeFLog(key, value));
#endif
}
lgInfo($"Completata esecuzione steps FTP | {srvFtp.ActionId} | {stepDone}/{srvFtp.StepsList.Count} Done/Req");
}
else
{
//connectionOk = false;
//tryDisconnect();
lgError($"Impossibile connettersi al server {srvFtp.ServerAddr}");
}
}
else
{
lgTrace($"Saltata esecuzione {srvFtp.ActionId} | veto attivo");
}
}
}
lastReadPLC = DateTime.Now;
}
/// <summary>
/// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
/// aggiornare x display in form</paramref>
/// </summary>
public override void readSemafori(ref newDisplayData currDispData)
{
DateTime adesso = DateTime.Now;
// salto se fosse attivo il veto ping...
if (lastPING.AddSeconds(vetoPingSec) < adesso)
{
// lo stato è come ping machine, x ora puntato a IP unico (WiFi?)
byte[] MemBlock = new byte[2];
try
{
currDispData.semIn = Semaforo.SV;
// in primis salvo data ping comunque...
lastPING = adesso;
// salvo esito ping
bool pingOK = testPingMachine == IPStatus.Success;
addTest(pingOK);
// se passa il ping faccio il resto...
if (pingStatusOk())
{
connectionOk = true;
lastReadPLC = adesso;
lastWatchDog = adesso;
}
else
{
connectionOk = false;
}
if (connectionOk)
{
B_input = 1;
}
else
{
B_input = 0;
}
// annullo lettura bit signal IN pre/post x evitare invio automatico...
B_output = B_input;
B_previous = B_input;
}
catch
{
currDispData.semIn = Semaforo.SR;
}
}
}
public override void startAdapter(bool resetQueue)
{
base.startAdapter(resetQueue);
// 2023.09.05 imposto anche primo ping e check disconnected...
DateTime adesso = DateTime.Now;
lastWatchDog = adesso;
//lastPING = adesso;
lastReadPLC = adesso;
lastDisconnCheck = adesso;
// faccio un primo check POST ritardo
tryConnect();
}
/// <summary>
/// Override connessione
/// </summary>
public override void tryConnect()
{
bool doLog = (verboseLog || periodicLog);
lgDebug($"FTP: tryConnect step 01 | connectionOk: {connectionOk}");
if (!connectionOk)
{
//// resetto coda...
//PingQueue = new DataQueue("000", "PingQueue", false);
// controllo che il ping sia stato tentato almeno pingTestSec fa...
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
{
if (doLog)
{
lgInfo("FTP: ConnKO - tryConnect");
}
lgDebug("FTP: tryConnect step 04");
lgDebug("FTP: Reset PingQueue");
bool pingOK = testPingMachine == IPStatus.Success;
addTest(pingOK);
// se passa il ping faccio il resto...
if (pingStatusOk())
{
// in primis salvo data ping...
lastPING = DateTime.Now;
connectionOk = true;
queueInEnabCurr = true;
lgInfo("FTP OK");
}
else
{
// loggo no risposta ping ...
lgError("FTP KO");
}
}
}
}
/// <summary>
/// Override disconnessione
/// </summary>
public override void tryDisconnect()
{
lgInfo("Richiesta disconnessione adapter PING!");
connectionOk = false;
queueInEnabCurr = false;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Dimensione coda di ping x valutazione
/// </summary>
protected int maxQueuePing = 11;
/// <summary>
/// Coda degli esiti di ping x calcolo stato macchina
/// </summary>
protected DataQueue PingQueue = new DataQueue("000", "PingQueue", false);
protected int PoweroffTimeoutSec = 100;
/// <summary>
/// Veto controllo status x log...
/// </summary>
protected DateTime vetoCheckStatus = DateTime.Now;
#endregion Protected Fields
#region Protected Methods
protected void addTest(bool pingOk)
{
int score = pingOk ? 1 : 0;
// controllo: se era spenta e risulta ping ok --> reset coda!
if (B_input == 0 && pingOk)
{
B_input = 1;
PingQueue = new DataQueue("000", "PingQueue", false);
lgTrace($"PingQueue resetted on addTest");
}
PingQueue.Enqueue($"{score}");
while (PingQueue.Count > maxQueuePing)
{
string res = "";
PingQueue.TryDequeue(out res);
}
}
/// <summary>
/// Calcola status ping:
/// - se ha 50% coda richiesta -- true
/// - se ha 50% coda richiesta -- true se è maggior parte a 1 (true)
/// </summary>
/// <returns></returns>
protected bool pingStatusOk()
{
bool answ = true;
int numVal = PingQueue.Count;
if (numVal > maxQueuePing / 2)
{
var listaValori = PingQueue.ToList();
int numOk = listaValori.Where(x => x == "1").Count();
int numKo = numVal - numOk;
answ = numOk >= numKo;
lgTrace($"PING ok per: {numOk} > {numKo}");
}
else
{
lgTrace("PING ok per mancanza dati minimi test");
}
return answ;
}
#endregion Protected Methods
#region Private Fields
private static Stopwatch sw = new Stopwatch();
/// <summary>
/// Dizionario dei divieti di esecuzione x i vari step
/// </summary>
private Dictionary<string, DateTime> StepsVeto = new Dictionary<string, DateTime>();
#endregion Private Fields
#region Private Properties
/// <summary>
/// Oggetto configurazione gestione FTP
/// </summary>
private FtpTaskList currFtpTaskList { get; set; } = new FtpTaskList();
/// <summary>
/// CLient connessioni FTP
/// </summary>
private Manager ftpClient { get; set; } = new Manager("", "", "", "", false);
#endregion Private Properties
#region Private Methods
/// <summary>
/// Verifica se l'azione sia permessa o in stato veto a tempo
/// </summary>
/// <param name="currAct"></param>
/// <returns></returns>
private bool ActionEnabled(FtpActConf currAct)
{
bool enabled = true;
// se veto presente
if (StepsVeto.ContainsKey(currAct.ActionId))
{
// controllo scadenza
enabled = StepsVeto[currAct.ActionId] < DateTime.Now;
}
return enabled;
}
/// <summary>
/// Imposta veto azione corrente
/// </summary>
/// <param name="currAct"></param>
/// <returns></returns>
private bool ActionResetVeto(FtpActConf currAct)
{
bool fatto = false;
if (StepsVeto.ContainsKey(currAct.ActionId))
{
StepsVeto[currAct.ActionId] = DateTime.Now.AddSeconds(currAct.ReExecVeto);
}
else
{
StepsVeto.Add(currAct.ActionId, DateTime.Now.AddSeconds(currAct.ReExecVeto));
}
return fatto;
}
/// <summary>
/// Esegue l'azione configurata
/// </summary>
/// <param name="step"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
private bool doStep(ActionConfig step)
{
bool fatto = false;
// faccio switch in base al tipo di azione da eseguire...
switch (step.Action)
{
case ActType.CheckDir:
break;
case ActType.CheckFile:
break;
case ActType.CreateDir:
break;
case ActType.DelDir:
break;
case ActType.DelFile:
break;
case ActType.DownloadDir:
break;
case ActType.DownloadFile:
break;
case ActType.GenRandomDir:
break;
case ActType.ListContent:
break;
case ActType.MirrorDirL2R:
break;
case ActType.MirrorDirR2L:
// eseguo mirroring directory
string remoteDir = "";
string localDir = "";
if (step.ParamList != null && step.ParamList.Count > 1)
{
sw.Restart();
remoteDir = step.ParamList["RemoteDir"] ?? "";
localDir = step.ParamList["LocalDir"] ?? "";
// verifico esistenza dir locale...
if (!Directory.Exists(localDir))
{
Directory.CreateDirectory(localDir);
}
//verifico dir remota
var preTest = ftpClient.dirExists(remoteDir);
if (preTest)
{
fatto = ftpClient.getDir(localDir, remoteDir, FluentFTP.FtpFolderSyncMode.Mirror);
if (!fatto)
{
lgError($"Error: {remoteDir} NOT mirrored!");
}
}
else
{
lgError($"Dir remota non trovata! remDir: {remoteDir}");
}
}
else
{
lgError("Error: missing parameters!");
}
sw.Stop();
if (fatto)
{
lgInfo($"Directory {remoteDir} mirrored R2L! | {sw.ElapsedMilliseconds:N1} ms");
}
break;
case ActType.PingServer:
break;
case ActType.UploadDir:
break;
case ActType.UploadFile:
break;
default:
break;
}
return fatto;
}
/// <summary>
/// Effettuo lettura file di conf
/// </summary>
/// <param name="fileName"></param>
private void loadFtpConfFile(string fileName)
{
string jsonFullPath = Path.Combine(System.Windows.Forms.Application.StartupPath, "DATA", "CONF", fileName);
lgInfo($"Apertura file {jsonFullPath}");
using (StreamReader reader = new StreamReader(jsonFullPath))
{
string jsonData = reader.ReadToEnd().Replace("\n", "").Replace("\r", "");
if (!string.IsNullOrEmpty(jsonData))
{
lgDebug($"File json composto da {jsonData.Length} caratteri");
try
{
currFtpTaskList = JsonConvert.DeserializeObject<FtpTaskList>(jsonData);
lgDebug($"Decodifica aree FtpTaskList: trovati {currFtpTaskList.ListTask.Count} gruppi di task FTP");
}
catch (Exception exc)
{
lgError($"Eccezione in decodifica conf json FTP:{Environment.NewLine}{exc}");
}
}
else
{
lgError("Errore in loadFtpConfFile: file json vuoto!");
}
}
}
#endregion Private Methods
}
}