3effc621db
- Update conf FTP Server x avere mirror verbosi
608 lines
22 KiB
C#
608 lines
22 KiB
C#
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
|
||
}
|
||
} |