629 lines
25 KiB
C#
629 lines
25 KiB
C#
#if false
|
|
using EgwProxy.SqlDb.DbModels;
|
|
#endif
|
|
using IOB_UT_NEXT;
|
|
using MapoSDK;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using static IOB_UT_NEXT.CustomObj;
|
|
|
|
namespace IOB_WIN_FILE.IobFile
|
|
{
|
|
/// <summary>
|
|
/// Adapter specializzato per SOITAAB x la SOLA lettura stato macchina da log, da accoppiare a
|
|
/// adapter x IOB LANTEK x scrittura PODL
|
|
/// - IN: LOGFile macchina diretto
|
|
/// - OUT: --> sigLog
|
|
/// --> fluxLog
|
|
/// </summary>
|
|
|
|
public class IobFileSoitaab : Iob.GenericNext
|
|
{
|
|
#region Public Constructors
|
|
|
|
/// <summary>
|
|
/// Costruttore dell'IOB FileBased SOITAAB
|
|
/// </summary>
|
|
/// <param name="caller">AdapterForm chiamante</param>
|
|
/// <param name="IOBConf">Configurazione IOB per avvio</param>
|
|
public IobFileSoitaab(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
string VetoReadSec = getOptPar("VetoReadSec");
|
|
|
|
setupSpecialParams();
|
|
if (pathList.ContainsKey("path-remBase"))
|
|
{
|
|
logDirPath = pathList["path-remBase"];
|
|
}
|
|
// fix parametri veto
|
|
if (!string.IsNullOrEmpty(VetoReadSec))
|
|
{
|
|
int.TryParse(VetoReadSec, out vetoReadFileSec);
|
|
}
|
|
|
|
string sFluxOnRead = getOptPar("sendFluxOnRead");
|
|
if (!string.IsNullOrEmpty("sendFluxOnRead"))
|
|
{
|
|
bool.TryParse(sFluxOnRead, out sendFluxOnRead);
|
|
}
|
|
|
|
// init a zero....
|
|
B_input = 0;
|
|
|
|
// provo prima lettura logfile
|
|
try
|
|
{
|
|
// eseguo subito un ciclo acquisizione log + processing
|
|
bool acquireLog = getRemoteLog();
|
|
if (acquireLog)
|
|
{
|
|
bool sentSignLog = processSignLogTable(adesso);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in IobFileSoitaab{Environment.NewLine}{exc}");
|
|
}
|
|
|
|
lastPING = DateTime.Now.AddHours(-1);
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Implementazione custom esecuzione task specifici
|
|
/// </summary>
|
|
/// <param name="task2exe"></param>
|
|
/// <returns></returns>
|
|
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// NON fa nulla... anche se non dovrebbe richiamarlo
|
|
Dictionary<string, string> taskDone = new Dictionary<string, string>();
|
|
lastReadPLC = DateTime.Now;
|
|
return taskDone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupero dati dinamici...
|
|
/// </summary>
|
|
public override Dictionary<string, string> getDynData()
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// dizionario vuoto / faccio direttamente accodamento in FluxLog
|
|
Dictionary<string, string> outVal = new Dictionary<string, string>();
|
|
// processo ed accodo!
|
|
processFluxLogTable(adesso);
|
|
lastReadPLC = DateTime.Now;
|
|
return outVal;
|
|
}
|
|
|
|
/// <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;
|
|
if (connectionOk)
|
|
{
|
|
// controllo veto checkDB
|
|
if (adesso > vetoDataRead)
|
|
{
|
|
// predispongo prox veto...
|
|
vetoDataRead = adesso.AddSeconds(vetoReadFileSec);
|
|
// semaforo
|
|
currDispData.semIn = Semaforo.SV;
|
|
// verifico SignLog e processo
|
|
bool acquireLog = getRemoteLog();
|
|
bool sentSignLog = processSignLogTable(adesso);
|
|
// verifico ProdData e processo
|
|
bool sentProdData = processProdDataTable(adesso);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
B_input = 0;
|
|
currDispData.semIn = Semaforo.SR;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override connessione
|
|
/// </summary>
|
|
public override void tryConnect()
|
|
{
|
|
if (!connectionOk)
|
|
{
|
|
// controllo che il ping sia stato tentato almeno pingTestSec fa...
|
|
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
|
|
{
|
|
if (verboseLog || periodicLog)
|
|
{
|
|
lgInfo("FileSoitaab: ConnKO - tryConnect");
|
|
}
|
|
// in primis salvo data ping...
|
|
lastPING = DateTime.Now;
|
|
// se passa il ping faccio il resto...
|
|
if (testPingMachine == IPStatus.Success)
|
|
{
|
|
string szStatusConnection = "";
|
|
try
|
|
{
|
|
// ora provo connessione...
|
|
parentForm.commPlcActive = true;
|
|
|
|
// connessione OK se oltre al PING riesco a leggere la folder di base
|
|
if (Directory.Exists(logDirPath))
|
|
{
|
|
parentForm.commPlcActive = false;
|
|
connectionOk = true;
|
|
}
|
|
// refresh stato connessione!!!
|
|
if (connectionOk)
|
|
{
|
|
queueInEnabCurr = true;
|
|
if (adpRunning)
|
|
{
|
|
lgInfo($"Connessione OK alla folder {logDirPath}");
|
|
lastReadPLC = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgError("Impossibile procedere, connessione mancante...");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgFatal($"Errore nella connessione all'adapter IobFileSoitaab: {szStatusConnection}{Environment.NewLine}{exc}");
|
|
connectionOk = false;
|
|
lgInfo($"Eccezione in TryConnect, Adapter IobFileSoitaab NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// loggo no risposta ping ...
|
|
connectionOk = false;
|
|
B_input = 0;
|
|
if (verboseLog || periodicLog)
|
|
{
|
|
lgInfo($"Attenzione: IobFileSoitaab controllo PING fallito per IP {cIobConf.cncPingAddr}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
needRefresh = true;
|
|
}
|
|
}
|
|
|
|
public override void tryDisconnect()
|
|
{
|
|
// registro solo che è disconnesso
|
|
connectionOk = false;
|
|
queueInEnabCurr = false;
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Protected Fields
|
|
|
|
protected bool sendFluxOnRead = false;
|
|
protected int vetoReadFileSec = 3;
|
|
|
|
#endregion Protected Fields
|
|
|
|
#region Protected Properties
|
|
|
|
#if false
|
|
/// <summary>
|
|
/// Stato di sync delle tab gestite
|
|
/// </summary>
|
|
protected List<SyncStateModel> elencoSyncState { get; set; } = new List<SyncStateModel>();
|
|
#endif
|
|
|
|
#endregion Protected Properties
|
|
|
|
#region Protected Methods
|
|
|
|
/// <summary>
|
|
/// Conversione string row in log generico
|
|
/// </summary>
|
|
/// <param name="dailyLog"></param>
|
|
/// <returns></returns>
|
|
protected override GenLogRow convertToMachineLog(string dailyLog)
|
|
{
|
|
GenLogRow answ = new GenLogRow();
|
|
if (!string.IsNullOrEmpty(dailyLog))
|
|
{
|
|
// preventivamente: doppio ";;" --> ";"
|
|
dailyLog = dailyLog.Replace(";;", ";");
|
|
var sSplit = dailyLog.Split(';');
|
|
answ.dtRif = DateTime.ParseExact($"{sSplit[0]} {sSplit[1]}", "yyyy-M-d HH:mm:ss", null);
|
|
answ.valString = $"{sSplit[2]};{sSplit[3]}";
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupera file log da analizzare
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override bool getRemoteLog()
|
|
{
|
|
bool answ = false;
|
|
bool pingOk = testPingMachine == IPStatus.Success;
|
|
if (pingOk && Directory.Exists(logDirPath))
|
|
{
|
|
string filePath = Path.Combine(logDirPath, $"Report-{DateTime.Today.Day}.txt");
|
|
bool hasDaily = File.Exists(filePath);
|
|
// cerco file quotidiano...
|
|
if (!hasDaily)
|
|
{
|
|
var fileList = Directory.GetFiles(logDirPath, "Report-*.txt");
|
|
DateTime lastMod = DateTime.Today.AddYears(-1);
|
|
foreach (var cFile in fileList)
|
|
{
|
|
var tempFile = Path.Combine(logDirPath, cFile);
|
|
var lwTime = File.GetLastWriteTime(tempFile);
|
|
if (lwTime >= lastMod)
|
|
{
|
|
lastMod = lwTime;
|
|
filePath = tempFile;
|
|
}
|
|
}
|
|
}
|
|
// leggo file
|
|
string rawVal = File.ReadAllText(Path.Combine(logDirPath, filePath));
|
|
if (!string.IsNullOrEmpty(rawVal))
|
|
{
|
|
// salvo in redis
|
|
string redKey = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Act");
|
|
redisMan.setRSV(redKey, rawVal);
|
|
answ = true;
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Private Properties
|
|
|
|
private string logDirPath { get; set; } = "\\\\ecs900\\Logs";
|
|
|
|
#endregion Private Properties
|
|
|
|
#region Private Methods
|
|
|
|
private static int decodeSoitaabLog(string val2test)
|
|
{
|
|
// di default NON EMERGENZA...
|
|
int valInt = 128;
|
|
switch (val2test)
|
|
{
|
|
case "START;1":
|
|
valInt = 1 + 2 + 128;
|
|
break;
|
|
|
|
case "END;1":
|
|
valInt = 1 + 128;
|
|
break;
|
|
|
|
case "START;0":
|
|
case "END;0":
|
|
case "HOLD;":
|
|
valInt = 1 + 16 + 128;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return valInt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue processing + invio dati tab SignLog
|
|
/// </summary>
|
|
/// <param name="adesso"></param>
|
|
/// <returns></returns>
|
|
private bool processFluxLogTable(DateTime adesso)
|
|
{
|
|
bool fatto = false;
|
|
// leggo eventuali dati di fluxlog ed invio...
|
|
if (fluxLogData != null && fluxLogData.Count > 0)
|
|
{
|
|
string sVal = "";
|
|
foreach (var fLog2send in fluxLogData)
|
|
{
|
|
var kvpFlux = fLog2send.valString.Split(';');
|
|
if (kvpFlux.Count() > 1)
|
|
{
|
|
sVal = $"[DYNDATA] |{fLog2send.dtRif:yyyy-MM-dd HH:mm:ss}|{kvpFlux[0]}|{kvpFlux[1]}";
|
|
// chiamo accodamento con dataora corretta...
|
|
accodaFLog(sVal, qEncodeFLog(fLog2send.dtRif, kvpFlux[0], kvpFlux[1]));
|
|
}
|
|
}
|
|
// svuoto coda invio fluxlog...
|
|
fluxLogData = new List<GenLogRow>();
|
|
// fatto!
|
|
fatto = true;
|
|
}
|
|
return fatto;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue processing + invio dati tab ProdData
|
|
/// </summary>
|
|
/// <param name="adesso"></param>
|
|
/// <returns></returns>
|
|
private bool processProdDataTable(DateTime adesso)
|
|
{
|
|
bool fatto = false;
|
|
return fatto;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esegue processing + invio dati tab SignLog
|
|
/// </summary>
|
|
/// <param name="adesso"></param>
|
|
/// <returns></returns>
|
|
private bool processSignLogTable(DateTime adesso)
|
|
{
|
|
bool fatto = false;
|
|
// init oggetto x processing
|
|
Dictionary<DateTime, int> sigLogFromFile = new Dictionary<DateTime, int>();
|
|
// recupero i dati dai logs files, attuale e nuovo...
|
|
string fileAct = redisMan.getRSV(redKeyLogfileAct);
|
|
string fileLast = redisMan.getRSV(redKeyLogfileLast);
|
|
// confronto con i dati dell'ultimo LOG FILE processato
|
|
int lenghtAct = fileAct != null ? fileAct.Length : 0;
|
|
int lenghtLast = fileLast != null ? fileLast.Length : 0;
|
|
//if (lenghtAct > 0 && lenghtAct != lenghtLast)
|
|
if (lenghtAct > 0)
|
|
{
|
|
List<GenLogRow> logNew = new List<GenLogRow>();
|
|
List<GenLogRow> sigLogData = new List<GenLogRow>();
|
|
List<GenLogRow> logLast = getGenLogFromMachineLog(fileLast);
|
|
List<GenLogRow> logAct = getGenLogFromMachineLog(fileAct);
|
|
// SE c'è prendo ultima riga inviata x confronto...
|
|
var fileLastEnd = logLast.LastOrDefault();
|
|
if (fileLastEnd != null)
|
|
{
|
|
// ora prendo SOLAMENTE le righe nuove
|
|
logNew = logAct
|
|
.Where(x => x.dtRif > fileLastEnd.dtRif)
|
|
.OrderBy(x => x.dtRif)
|
|
.ToList();
|
|
}
|
|
else
|
|
{
|
|
logNew = logAct;
|
|
}
|
|
// solo SE ho nuovi dati....
|
|
if (logNew.Count > 0)
|
|
{
|
|
// processo le righe nuove e le accodo in redis come elenco FluxLog da inviare
|
|
// (appoggio in redis)... andando ad accodarle...
|
|
var currFLD = fluxLogData;
|
|
currFLD.AddRange(logNew);
|
|
fluxLogData = currFLD;
|
|
|
|
// estraggo solo info x sig log
|
|
sigLogData = logNew
|
|
.Where(x => x.valString.StartsWith("START") || x.valString.StartsWith("HOLD") || x.valString.StartsWith("END"))
|
|
.OrderBy(x => x.dtRif)
|
|
.ToList();
|
|
|
|
// processo le righe nuove trasformando al volo SOLO eventi...
|
|
foreach (var sLog2send in sigLogData)
|
|
{
|
|
/* -----------------------------------------------------
|
|
* bitmap MAPO STANDARD 60
|
|
* B0: 001 POWER_ON
|
|
* B1: 002 RUN
|
|
* B2: 004 pzCount
|
|
* B3: 008 allarme
|
|
* B4: 016 manuale
|
|
* B5: 032 slowTC
|
|
* B6: 064 WarmUpCoolDown
|
|
* B7: 128 EmergArmata
|
|
*
|
|
----------------------------------------------------- */
|
|
|
|
int valInt = decodeSoitaabLog(sLog2send.valString);
|
|
|
|
string currVal = getEncodSigLog(sLog2send.dtRif, valInt, counterSigIN);
|
|
// verifico non sia in veto invio iniziale...
|
|
if (queueInEnabCurr)
|
|
{
|
|
// --> accodo (valore già formattato)!
|
|
QueueIN.Enqueue(currVal);
|
|
// loggo!
|
|
lgTrace(string.Format("[QUEUE-IN] {0}", currVal));
|
|
counterSigIN++;
|
|
if (counterSigIN > 9999)
|
|
{
|
|
counterSigIN = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgDebug($"[VETO FOR QUEUE-IN] | {currVal} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}");
|
|
checkVetoQueueIn();
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------
|
|
* processo righe x costruire una lista di eventi produzione:
|
|
* - elenco di eventi contapezzi
|
|
* - lista xODL come articolo + quantità prodotta (_1, _2, ...)
|
|
* - eventuale ultimo xODL che resta aperto
|
|
*
|
|
* successivamente processing + invio info raccolte
|
|
* - recupero PODL aperti
|
|
* - apertura/chiusura PODL esistenti
|
|
* - creazione PODL mancanti
|
|
* - dichiarazione contapezzi
|
|
*
|
|
* ---------------------------------------------------------- */
|
|
|
|
string sVal = "";
|
|
string descrArt = "";
|
|
List<ProdBatchData> elencoProdBatch = new List<ProdBatchData>();
|
|
ProdBatchData lastProdBatch = new ProdBatchData();
|
|
|
|
// ora processo TUTTO x il generico caso fluxLog......
|
|
foreach (var fLog2send in logNew)
|
|
{
|
|
// separo per ";"
|
|
var currData = fLog2send.valString.Split(';');
|
|
if (currData.Length > 1)
|
|
{
|
|
if (sendFluxOnRead)
|
|
{
|
|
// per prima cosa fluxLog a prescindere...
|
|
sVal = $"[LOGFILE]{currData[0]}|{currData[1]}";
|
|
// ...e chiamo accodamento
|
|
accodaFLog(sVal, qEncodeFLog(fLog2send.dtRif, currData[0], currData[1]));
|
|
}
|
|
|
|
switch (currData[0])
|
|
{
|
|
case "END":
|
|
// se è un end --> è comunque NON + runnning
|
|
isRunningState = false; // chiudo batch precedente + in elenco (SE era valido...)
|
|
if (!string.IsNullOrEmpty(lastProdBatch.codArt))
|
|
{
|
|
lastProdBatch.dtEnd = fLog2send.dtRif;
|
|
elencoProdBatch.Add(lastProdBatch);
|
|
}
|
|
// reset articolo..
|
|
lastArtDescr = "";
|
|
// nuovo batch VUOTO
|
|
lastProdBatch = new ProdBatchData();
|
|
contapezziPLC = 0;
|
|
break;
|
|
|
|
case "START":
|
|
isRunningState = currData[1] == "1";
|
|
break;
|
|
|
|
case "PARTCODE":
|
|
// inizio confronto articolo con precedente x capire se sia NUOVO
|
|
descrArt = currData[1];
|
|
// devo scartare i "doppi percorsi"
|
|
if (descrArt != lastArtDescr)
|
|
{
|
|
// verifico se sia DAVVERO cambiato articolo... prendo
|
|
// articolo senza indice (_1, _2, _3, ...)
|
|
var artDataLast = lastArtDescr.Split('_');
|
|
var artDataNew = descrArt.Split('_');
|
|
// nuovo articolo (NON vuoto)
|
|
if (!string.IsNullOrEmpty(artDataNew[0]) && artDataLast[0] != artDataNew[0])
|
|
{
|
|
// chiudo batch precedente + in elenco (SE era valido...)
|
|
if (!string.IsNullOrEmpty(lastProdBatch.codArt))
|
|
{
|
|
// chiudo prod
|
|
lastProdBatch.dtEnd = fLog2send.dtRif;
|
|
elencoProdBatch.Add(lastProdBatch);
|
|
}
|
|
// nuovo batch
|
|
lastProdBatch = new ProdBatchData()
|
|
{
|
|
codArt = artDataNew[0],
|
|
dtStart = fLog2send.dtRif,
|
|
numPz = 1
|
|
};
|
|
}
|
|
// solo nuova copia articolo
|
|
else
|
|
{
|
|
// aumento numPezzi batch
|
|
lastProdBatch.numPz++;
|
|
}
|
|
|
|
// salvo nuovo art processato
|
|
lastArtDescr = descrArt;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// recupero elenco PODL corrente...
|
|
List<PODLModel> reqPOdlList = MachineNextPodl();
|
|
// adesso processo tutti i prodBatch collezionati x invio
|
|
foreach (var item in elencoProdBatch)
|
|
{
|
|
int currIdxPOdl = 0;
|
|
// cerco se articolo sia in elenco PODL
|
|
var listPOdl = reqPOdlList
|
|
.Where(x => x.CodArticolo.Equals(item.codArt, StringComparison.InvariantCultureIgnoreCase))
|
|
.ToList();
|
|
|
|
// se trovato --> invio start/stop PODL
|
|
if (listPOdl != null && listPOdl.Count > 0)
|
|
{
|
|
var currPodl = listPOdl.FirstOrDefault();
|
|
if (currPodl != null)
|
|
{
|
|
currIdxPOdl = currPodl.IdxPromessa;
|
|
// elimino da elenco PODL
|
|
reqPOdlList.Remove(currPodl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// se NON trovato --> creo PODL, poi avvio/chiudo
|
|
currIdxPOdl = TryCreatePodl(item.codArt, CodGruppoIob, item.numPz);
|
|
}
|
|
// se idxPOdl valido
|
|
if (currIdxPOdl > 0)
|
|
{
|
|
// mando apertura PODL
|
|
SendStartPodl(currIdxPOdl, item.dtStart);
|
|
// chiudo PODL...
|
|
if (item.dtEnd != null)
|
|
{
|
|
// invio pezzi 3 sec prima di chiusura
|
|
SendPzIncrAtDate(item.numPz, ((DateTime)item.dtEnd).AddSeconds(-3));
|
|
// chiudo
|
|
SendClosePOdl(currIdxPOdl, (DateTime)item.dtEnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// salvo in last il valore di act...
|
|
redisMan.setRSV(redKeyLogfileLast, fileAct);
|
|
|
|
// calcolo B_input valido
|
|
var lastRec = sigLogData.LastOrDefault();
|
|
if (lastRec != null)
|
|
{
|
|
// salvo in B_input ultimo valore letto...
|
|
int lastValInt = decodeSoitaabLog(lastRec.valString);
|
|
B_input = lastValInt;
|
|
}
|
|
}
|
|
fatto = true;
|
|
}
|
|
return fatto;
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
} |