Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobFile/FileEurom63.cs
T
Samuele Locatelli 8bb0f158b5 SPLIT PROGETTO!!!
- proj di base con le 2 form da ereditare
- progetto globale che contiene TUTTI gli adapter (pronto a venire spezzettato
- gettate le basi x "portare fuori" i vari componenti oppure fare compilazione condizonale
2024-12-20 10:16:32 +01:00

923 lines
36 KiB
C#

using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace IOB_WIN_NEXT.IobFile
{
public class FileEurom63 : IobFile.FileGen
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base...
/// </summary>
/// <param name="caller"></param>
/// <param name="adpConf"></param>
public FileEurom63(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("INIT IobFileEurom63");
appPath = Path.GetDirectoryName(Application.ExecutablePath);
string MAX_DELAY_SEC = getOptPar("MAX_DELAY_SEC");
string CACHE_MULT = getOptPar("CACHE_MULT");
int.TryParse(MAX_DELAY_SEC, out maxDelaySec);
int.TryParse(CACHE_MULT, out cacheMult);
#if DEBUG
maxDelaySec = 60 * 60 * 24;
#endif
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if (utils.CRB("enableContapezzi"))
{
try
{
// controllo se sono in sampling della produzione
if (actLevel >= Eurom63.ComLevel.ProdRequested)
{
/************************************************************
*
* EXAMPLE
* DATE, TIME, ActCntCyc, ActTimCyc, ActTimFill, @OutXhour, SetDescJob
* 20201007, 21:29:52, 5302, 8.61, 0.50, 10058, Nr. 1000987654.01
*
* devo prendere il 3° valore
*
*
************************************************************/
// leggo il file della produzione HARD CODED...
var sessProd = confE63.ActiveSessions[5];
string currPzCount = "";
if (sessProd != null)
{
if (sessProd.Active)
{
// nome file...
string fileName = $"{BaseDir}\\{sessProd.SessionName}.DAT";
if (File.Exists(fileName))
{
string rawData = "";
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var textReader = new StreamReader(fileStream))
{
rawData = textReader.ReadToEnd();
}
// ora splitto in linee
string[] rawLines = Regex.Split(rawData, "\r\n|\r|\n");
int numRow = rawLines.Length;
// devo avere almeno 2 righe...
if (numRow >= 2)
{
string[] statusData = rawLines[1].Split(',');
currPzCount = statusData[2].Trim();
// salvo se valido
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
Int32.TryParse(currPzCount, out newVal);
// verifico SE il contapezzi vada moltiplicato x il
// fattore pzPallet...
if (confE63.PzPallet > 1)
{
newVal = newVal * confE63.PzPallet;
}
// aggiorno contapezzi
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
// ora verifico SE siano validi anche le dataora dei valori
// letti (< 20 sec ritardo da ora...)
string data = statusData[0].Trim();
string ora = statusData[1].Trim();
DateTime adesso = DateTime.Now;
DateTime lastPub = adesso.AddMinutes(-1);
CultureInfo provider = CultureInfo.InvariantCulture;
try
{
lastPub = DateTime.ParseExact($"{data} {ora}", "yyyyMMdd HH:mm:ss", provider);
}
catch
{ }
if (Math.Abs(lastPub.Subtract(adesso).TotalSeconds) > maxDelaySec)
{
sessProd.SessionValidUntil = adesso;
// elimino file RSP...
cleanupResp(sessProd.SessionName);
}
}
}
}
else
{
actLevel = Eurom63.ComLevel.StatusRequested;
}
}
else
{
actLevel = Eurom63.ComLevel.StatusRequested;
}
}
}
catch (Exception exc)
{
lgError($"Eccezione in processContapezzi:{Environment.NewLine}{exc}");
}
}
}
/// <summary>
/// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
/// aggiornare x display in form</paramref>
/// </summary>
public override void readSemafori(ref newDisplayData currDispData)
{
base.readSemafori(ref currDispData);
// in primis controllo status...
checkCommStatus();
// init a zero...
B_input = 0;
string currStatus = "99999";
bool readDone = false;
DateTime adesso = DateTime.Now;
// leggo il file dela produzione HARD CODED...
var sessStatus = confE63.ActiveSessions[4];
// ciclo!
try
{
// controllo se sono in sampling dello stato
if (actLevel >= Eurom63.ComLevel.StatusRequested)
{
/* -----------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: allarme TCiclo (SLOW)
* B6: avvio/spegnimento
* B7: Emergenza armata (1= pronto, 0 = emergenza) --> DA INVIARE!!!
----------------------------------------------------- */
/******************************************************************
*
* EXAMPLE file content
* DATE, TIME, ActStsMach
* 20201007, 21:28:10, 0A000
*
* Configurazione array status: 5 char status decoding
*
* Pos1: (status)
* 0: poweron
* 1: poweroff
*
* Pos2: (mode)
* A: AUTO
* S: SEMI auto
* M: Manual
* U: Setup
* H: Hord
* C: Maintenance
* 0: Unknown
* I: Idle
*
* Pos3: (assist call)
* 0: No assistance
* 2: Assistance required
*
* Pos4: (Bad part)
* 0: last cycle not bad
* 1: last cycle bad
*
* Pos5: Active Alarm
* 0: No alarm
* 1: Alarm
*
*
*
*******************************************************************/
if (sessStatus != null)
{
if (sessStatus.Active)
{
// nome file...
string fileName = $"{BaseDir}\\{sessStatus.SessionName}.DAT";
if (File.Exists(fileName))
{
string rawData = "";
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var textReader = new StreamReader(fileStream))
{
rawData = textReader.ReadToEnd();
}
if (!string.IsNullOrEmpty(rawData))
{
// ora splitto in linee
string[] rawLines = Regex.Split(rawData, "\r\n|\r|\n");
int numRow = rawLines.Length;
// devo avere almeno 2 righe...
if (numRow >= 2)
{
string[] statusData = rawLines[1].Split(',');
if (statusData.Length >= 3)
{
currStatus = statusData[2].Trim();
if (!string.IsNullOrEmpty(currStatus))
{
currDispData.semIn = Semaforo.SV;
}
// salvo in cache!
Last_CurrStatus.Value = currStatus;
Last_CurrStatus.ValidUntil = DateTime.Now.AddSeconds(maxDelaySec * cacheMult);
// ora verifico SE siano validi anche le dataora dei
// valori letti (< 20 sec ritardo da ora...)
string data = statusData[0].Trim();
string ora = statusData[1].Trim();
DateTime lastPub = adesso.AddMinutes(-1);
CultureInfo provider = CultureInfo.InvariantCulture;
try
{
lastPub = DateTime.ParseExact($"{data} {ora}", "yyyyMMdd HH:mm:ss", provider);
}
catch
{ }
if (Math.Abs(lastPub.Subtract(adesso).TotalSeconds) > maxDelaySec)
{
sessStatus.SessionValidUntil = adesso;
// elimino file RSP...
cleanupResp(sessStatus.SessionName);
}
readDone = true;
}
else
{
lgError($"Decodifica StatusData in errore: trovati {statusData.Length} campi in {rawLines}");
// se valido RILEGGO ultimo curr status
if (Last_CurrStatus.ValidUntil > adesso)
{
currStatus = Last_CurrStatus.Value;
}
}
}
else
{
lgError($"Lettura file stato in errore: trovate {numRow} linee");
}
}
// se valido RILEGGO ultimo curr status
if (Last_CurrStatus.ValidUntil > adesso)
{
currStatus = Last_CurrStatus.Value;
}
}
else
{
// se valido RILEGGO ultimo curr status
if (Last_CurrStatus.ValidUntil > adesso)
{
currStatus = Last_CurrStatus.Value;
}
// abbasso status...
actLevel--;
}
}
else
{
// se valido RILEGGO ultimo curr status
if (Last_CurrStatus.ValidUntil > adesso)
{
currStatus = Last_CurrStatus.Value;
}
}
}
else
{
// se valido RILEGGO ultimo curr status
if (Last_CurrStatus.ValidUntil > adesso)
{
currStatus = Last_CurrStatus.Value;
}
}
// processo il currentStatus... parto da poweron
B_input = currStatus[0] == '1' ? 0 : 1;
// aggiungo il bit NON emergenza
B_input += (1 << 7);
// ora MODE
switch (currStatus[1])
{
case 'A':
B_input += (1 << 1);
break;
case 'S':
case 'M':
case 'U':
B_input += (1 << 4);
break;
default:
// loggo cosa trovo (CREDO
break;
}
// ora cerco allarmi
if (currStatus[4] == '1')
{
B_input += (1 << 3);
}
// controllo se diverso faccio log!
if (!B_input.Equals(Last_B_Input.Value))
{
lgInfo($"B_Input variato: {Last_B_Input.Value} --> {B_input} | currStatus: {currStatus}");
}
// salvo B_Input in cache!
Last_B_Input.Value = B_input;
Last_B_Input.ValidUntil = DateTime.Now.AddSeconds(maxDelaySec * cacheMult);
}
// se disponibile riporto B_Input precedente
else
{
if (Last_B_Input.ValidUntil > adesso)
{
B_input = Last_B_Input.Value;
}
}
// riporto bitmap...
reportRawInput(ref currDispData);
}
catch (Exception exc)
{
lgError(exc, "Errore in readSemafori x IOB FILE");
if (currDispData != null)
currDispData.semIn = Semaforo.SR;
}
// ultimo controllo...
if (!readDone)
{
// se valido RILEGGO ultimo B_INPUT
if (Last_B_Input.ValidUntil > adesso)
{
B_input = Last_B_Input.Value;
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON PERMESSO per EM63 (read only)
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON PERMESSO per EM63 (read only)
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
/// <summary>
/// Connessione
/// </summary>
public override void tryConnect()
{
var nextLevel = Eurom63.ComLevel.IsConnected;
var connectSession = confE63.ActiveSessions[0];
processSession(nextLevel, ref connectSession);
queueInEnabCurr = true;
}
/// <summary>
/// Disconnessione
/// </summary>
public override void tryDisconnect()
{
connectionOk = false;
queueInEnabCurr = false;
try
{
abortPrevJob();
cleanupFolder();
}
catch (Exception exc)
{
lgError(exc, "Eccezione in tryDisconnect");
}
}
#endregion Public Methods
#region Internal Methods
/// <summary>
/// Metodi preliminari x comunicazione:
/// - richiesta connessione
/// - richiesta stato attivo
/// </summary>
internal void checkCommStatus()
{
// init obj display
newDisplayData currDispData = new newDisplayData();
currDispData.semIn = Semaforo.ND;
switch (actLevel)
{
case Eurom63.ComLevel.None:
tryConnect();
currDispData.semIn = Semaforo.SS;
break;
case Eurom63.ComLevel.IsConnected:
requestInfo();
currDispData.semIn = Semaforo.SS;
break;
case Eurom63.ComLevel.HasInfo:
setMachineTime();
currDispData.semIn = Semaforo.SS;
break;
case Eurom63.ComLevel.TimeSet:
abortPrevJob();
currDispData.semIn = Semaforo.SG;
break;
case Eurom63.ComLevel.ChannelOk:
requestStatusData();
currDispData.semIn = Semaforo.SG;
break;
case Eurom63.ComLevel.StatusRequested:
requestProdData();
currDispData.semIn = Semaforo.SV;
break;
case Eurom63.ComLevel.ProdRequested:
checkSampling();
currDispData.semIn = Semaforo.SV;
break;
default:
break;
}
if (utils.CRB("verbose"))
{
lgInfo($"DONE checkCommStatus | actLevel {actLevel}");
}
raiseRefresh(currDispData);
}
/// <summary>
/// Pulizia preliminare folder comunicazione
/// </summary>
internal override void cleanupFolder()
{
// elimino OGNI file per tipo configurato
foreach (var cleanExt in confE63.cleanupExt)
{
string[] file2del = Directory.GetFiles(BaseDir, cleanExt);
foreach (var file in file2del)
{
try
{
File.Delete(file);
}
catch
{ }
}
}
}
/// <summary>
/// Pulizia folder dai file RSP della sessione
/// </summary>
internal void cleanupResp(string sessionName)
{
string[] file2del = Directory.GetFiles(BaseDir, $"{sessionName}.RSP");
foreach (var file in file2del)
{
try
{
File.Delete(file);
}
catch
{ }
}
}
/// <summary>
/// Ricarica conf adapter...
/// </summary>
internal override void reloadAdapterConf()
{
// init obj display
newDisplayData currDispData = new newDisplayData();
lgInfo("BEGIN reloadAdapterConf");
// inizializzo LUT decodifica
string jsonConf = getOptPar("LUT_CONF");
if (!string.IsNullOrEmpty(jsonConf))
{
string jsonFullPath = $"{Application.StartupPath}/DATA/CONF/{jsonConf}";
lgInfo($"Apertura file {jsonFullPath}");
StreamReader reader = new StreamReader(jsonFullPath);
string jsonData = reader.ReadToEnd();
if (!string.IsNullOrEmpty(jsonData))
{
try
{
confE63 = JsonConvert.DeserializeObject<Eurom63.ProtoConf>(jsonData);
// salvo baseUri
BaseDir = confE63.BaseDir;
lgInfo($"baseDir = {BaseDir}");
// imposto a zero la bitmap x riavvio!
B_input = 0;
// FORZO invio dati...
accodaSigIN(ref currDispData);
// loggo!
lgInfo($"init input bitmap to zero: {B_input}");
}
catch (Exception exc)
{
lgError(exc, "Eccezione in decodifica conf json");
}
}
reader.Dispose();
}
lgInfo("DONE reloadAdapterConf");
raiseRefresh(currDispData);
}
#endregion Internal Methods
#region Protected Fields
protected static DateTime lastStatusDecr = DateTime.Now;
/// <summary>
/// step di comunicazione attivo
/// </summary>
protected Eurom63.ComLevel actLevel = Eurom63.ComLevel.None;
/// <summary>
/// DIrectory eseguibile corrente
/// </summary>
protected string appPath = Directory.GetCurrentDirectory();
/// <summary>
/// Moltiplicatore durata cache
/// </summary>
protected int cacheMult = 4;
/// <summary>
/// Oggetti decodificati da pagina
/// </summary>
protected Eurom63.ProtoConf confE63;
/// <summary>
/// Valore currStatus validato (per gestione "disconnessioni")
/// </summary>
protected CachedInt Last_B_Input = new CachedInt() { Value = 0 };
/// <summary>
/// Valore currStatus validato (per gestione "disconnessioni")
/// </summary>
protected CachedString Last_CurrStatus = new CachedString() { Value = "00000" };
/// <summary>
/// Massimo delay lettura dati prima di considerarli scaduti (30 sec, ma x test 1 gg)
/// </summary>
protected int maxDelaySec = 30;
#endregion Protected Fields
#region Protected Methods
protected void abortPrevJob()
{
var nextLevel = Eurom63.ComLevel.ChannelOk;
var connectSession = confE63.ActiveSessions[3];
processSession(nextLevel, ref connectSession);
#if false
// qui per sicurezza PULISCE TUTTO
cleanupFolder();
#endif
}
/// <summary>
/// Verifica una sessione configurata (ovvero la comunicazione su TUTTI i file associati)
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected bool checkRequest(Eurom63.Session session)
{
bool answ = false;
string fileName = "";
if (session != null)
{
fileName = $"{BaseDir}\\{session.SessionName}.REQ";
answ = File.Exists(fileName);
}
return answ;
}
/// <summary>
/// Verifica se ci sia una risposta POSITIVA
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected bool checkResp(Eurom63.Session session)
{
bool answ = false;
string fileName = "";
if (session != null)
{
fileName = $"{BaseDir}\\{session.SessionName}.RSP";
if (File.Exists(fileName))
{
// verifico contenuto
//string rawData = File.ReadAllText(fileName);
string rawData = "";
// lettura in modo NON esclusivo...
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var textReader = new StreamReader(fileStream))
{
rawData = textReader.ReadToEnd();
}
// se la stringa session.RespOk contiene | significa ci sono + valori ammessi e
// li controlla tutti
if (session.RespOk.Contains("|"))
{
// splitto e testo tutti...
string[] validRespOk = session.RespOk.Split('|');
foreach (var item in validRespOk)
{
answ = answ || rawData.Contains(item);
}
}
else
{
answ = rawData.Contains(session.RespOk);
}
}
}
return answ;
}
/// <summary>
/// verifica periodica dei campionamenti:
/// - i due jobs devono essere attivi e non scaduti
/// - non devo aver già resettato...
/// </summary>
protected void checkSampling()
{
var currSession = confE63.ActiveSessions[4];
checkSessionActive(currSession, Eurom63.ComLevel.ChannelOk);
currSession = confE63.ActiveSessions[5];
checkSessionActive(currSession, Eurom63.ComLevel.ChannelOk);
}
/// <summary>
/// Elimina i file della sessione indicata (SE è un task ciclico --&gt; solo RSP)
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected bool cleanupSession(Eurom63.Session session)
{
bool answ = false;
if (session != null)
{
// solo se scaduta validità...
if (session.SessionValidUntil < DateTime.Now)
{
string searchPattern = $"{session.SessionName}.*";
// task ciclico?
if (session.Cycle)
{
// solo risposta!
searchPattern = $"{session.SessionName}.RSP";
}
string[] file2del = Directory.GetFiles(BaseDir, searchPattern);
foreach (var file in file2del)
{
try
{
File.Delete(file);
}
catch
{ }
}
}
}
return answ;
}
/// <summary>
/// Processa una sessione
/// - andando a verificare l'esistenza della REQ + se esito positivo pulizia
/// - andando a richeidere di nuovo risposta
/// </summary>
/// <param name="nextLevel"></param>
/// <param name="connectSession"></param>
protected void processSession(Eurom63.ComLevel nextLevel, ref Eurom63.Session connectSession)
{
if (connectSession != null)
{
// controllo esistenza directory --> segno connected...
connectionOk = Directory.Exists(BaseDir);
DateTime adesso = DateTime.Now;
if (connectionOk)
{
// verifico se ci sia risp CONNECT
if (checkResp(connectSession))
{
// aggiorno livello
actLevel = nextLevel;
parentForm.displayTaskAndLog($"Adp Level: {nextLevel}");
// elimino file sessione
cleanupSession(connectSession);
connectSession.Active = connectSession.Cycle;
connectSession.Passed = true;
}
// verifico SE ci sia la richiesta sennò la chiedo...
else if (!checkRequest(connectSession))
{
copyRequestFiles(connectSession, adesso);
}
// richiedo SE non ci fosse i dati CONNECT...
else
{
// evito di richiedere SE non fosse già scaduta richiesta...
if (adesso > connectSession.RetryVeto || !checkRequest(connectSession))
{
// pulisco eventuali risp vecchie
cleanupResp(connectSession.SessionName);
copyRequestFiles(connectSession, adesso);
}
}
}
else
{
// aspetto prima di riprovare...
Thread.Sleep(50);
}
}
else
{
// aspetto prima di riprovare...
Thread.Sleep(50);
}
}
/// <summary>
/// Processa i file della sessione indicata (copy + transform)
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected bool processSessionFile(Eurom63.Session session)
{
bool answ = false;
if (session != null)
{
string fileFrom = "";
string fileTo = "";
// processo OGNI file sessione x farne copia
foreach (var file2Proc in session.FileList)
{
fileFrom = $"{appPath}\\{file2Proc.Path}";
fileTo = $"{BaseDir}\\{Path.GetFileName(file2Proc.Path)}";
string rawData = File.ReadAllText(fileFrom);
if (file2Proc.OprReq == Eurom63.FileOpr.Copy)
{
// scrivo!
File.WriteAllText(fileTo, rawData);
}
else
{
// leggo file originale... e processo sostituzioni
rawData = rawData.Replace("{DTNow}", DateTime.Now.ToString("HHmmssyyyyMMdd"));
// ora in posizione definitiva
File.WriteAllText(fileTo, rawData);
}
}
}
return answ;
}
/// <summary>
/// Effettua richiesta info x macchina (validare startup process)
/// </summary>
protected void requestInfo()
{
var nextLevel = Eurom63.ComLevel.HasInfo;
var connectSession = confE63.ActiveSessions[1];
processSession(nextLevel, ref connectSession);
}
protected void requestProdData()
{
var nextLevel = Eurom63.ComLevel.ProdRequested;
var connectSession = confE63.ActiveSessions[5];
processSession(nextLevel, ref connectSession);
}
protected void requestStatusData()
{
var nextLevel = Eurom63.ComLevel.StatusRequested;
var connectSession = confE63.ActiveSessions[4];
processSession(nextLevel, ref connectSession);
}
protected void setMachineTime()
{
var nextLevel = Eurom63.ComLevel.TimeSet;
var connectSession = confE63.ActiveSessions[2];
processSession(nextLevel, ref connectSession);
}
#endregion Protected Methods
#region Private Methods
/// <summary>
/// Se la sessione fosse scaduta o non attiva --&gt; torna al livello indicato
/// </summary>
/// <param name="currSession"></param>
private void checkSessionActive(Eurom63.Session currSession, Eurom63.ComLevel nextLevel)
{
// SOLO SE ha senso che controllo (sono in sampling...)
if (actLevel > Eurom63.ComLevel.HasInfo)
{
DateTime adesso = DateTime.Now;
// devono essere ATTIVE le sessioni di campionamento... e NON scadute
if (!currSession.Active || currSession.SessionValidUntil < adesso)
{
// controllo ultimo downgrade status
if (lastStatusDecr.AddSeconds(3) < adesso)
{
// elimino TUTTE le risposte...
cleanupResp(currSession.SessionName);
// registro downgrade status...
lastStatusDecr = adesso;
// imposto livellotornando indietro di 1 alla volta... senza andare in negativoS
actLevel = actLevel - 1;
actLevel = actLevel > 0 ? actLevel : 0;
lgInfo($"Sessione inattiva, {actLevel + 1} --> {actLevel}");
}
}
}
}
/// <summary>
/// Effettua copia file richeiste + update timing
/// </summary>
/// <param name="connectSession"></param>
/// <param name="adesso"></param>
private void copyRequestFiles(Eurom63.Session connectSession, DateTime adesso)
{
// processo richiesta
processSessionFile(connectSession);
if (adesso > connectSession.SessionValidUntil)
{
connectSession.Active = !connectSession.Cycle;
}
connectSession.Passed = false;
connectSession.SessionStarted = adesso;
connectSession.SessionValidUntil = adesso.AddMinutes(connectSession.ValidityMinutes);
connectSession.RetryVeto = adesso.AddSeconds(connectSession.RetrySec);
}
#endregion Private Methods
}
}