Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobOpcUaMBHCimolai.cs
T
2022-11-09 17:12:53 +01:00

702 lines
26 KiB
C#

using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using static IOB_UT_NEXT.CustomObj;
namespace IOB_WIN_NEXT
{
public class IobOpcUaMBHCimolai : IobOpcUaMBH
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per MBH (es Cimolai, Baglietto) https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public IobOpcUaMBHCimolai(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa MBH versione Cimolai (Baglietto)");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
if (!string.IsNullOrEmpty(getOptPar("VETO_SEND_SNAPSHOT")))
{
string strVETO_SEND_SNAPSHOT = getOptPar("VETO_SEND_SNAPSHOT");
int.TryParse(strVETO_SEND_SNAPSHOT, out VETO_SEND_SNAPSHOT);
}
sendKeyRichiesta = true;
// controllo se abilitare Processing automatico ODL
if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL")))
{
bool.TryParse(getOptPar("AUTO_CHANGE_ODL"), out doProcOdl);
}
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 2:2
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x salvare esito scrittura
var writeResult = base.executeTasks(task2exe);
// 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)
{
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;
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!");
}
}
return writeResult;
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
// non fa nulal (non ha contapezzi)
}
public override bool resetcontapezziPLC()
{
bool answ = false;
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actResetCounter)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
}
catch
{ }
return answ;
}
#endregion Public Methods
#region Internal Methods
internal override void procRunMode(ref string currRun)
{
// variabili RUN... se richiesto invio runMode
if (opcUaParams.runModeSend)
{
currRun = $"{currAct}";
// effettuo processing SPECIFICO currAct x gestione snapshot...
if (currAct == 10)
{
if (DateTime.Now > vetoSnapshot)
{
callUrl(urlTakeSnapshot, false);
// blocco snapshot x VETO_SEND_SNAPSHOT sec...
vetoSnapshot = DateTime.Now.AddSeconds(VETO_SEND_SNAPSHOT);
lgInfo($"Effettuata richiesta salvataggio snapshot, impostato veto nuova chiamata a {vetoSnapshot}");
}
else
{
lgInfo($"NON effettuo salvataggio snapshot perché veto attivo fino a {vetoSnapshot}");
}
}
else
{
// aggiorno veto x snapshot
vetoSnapshot = DateTime.Now;
}
// invio del valore RU MODE come FLog
if (!string.IsNullOrEmpty(currRun))
{
// se ho valore --> invio
string sVal = "";
string descr = $"RunModeVal";
DateTime locTStamp = DateTime.Now;
sVal = $"Change 04: {locTStamp.ToString()} | descr: {descr} | Id: {opcUaParams.keyExeMode} | Val: {currRun}";
// cerco se sia un dato/valore in veto
bool hasVetoLK = false;
bool hasVetoLKV = false;
hasVetoLK = opcUaParams.fluxLogKeyValVeto.ContainsKey(descr);
if (hasVetoLK)
{
// se c'è controllo il valore...
var valList = opcUaParams.fluxLogKeyValVeto[descr];
hasVetoLKV = valList.Contains(currRun);
}
if (hasVetoLKV)
{
lgTrace($"NON ACCODATO sample: veto trovato per {descr}/{currRun} in fluxLogKeyValVeto ", false);
}
else
{
accodaFLog(sVal, qEncodeFLog(descr, currRun));
// se richiesto provo a tradurre
if (opcUaParams.runModeTrad)
{
string RunModeDescr = itemTranslation("RunMode", currRun);
descr = $"RunMode";
accodaFLog(sVal, qEncodeFLog(descr, RunModeDescr));
}
}
}
}
}
#endregion Internal Methods
#region Protected Fields
protected int lastAct = 0;
protected int lastExe = 0;
protected int VETO_SEND_SNAPSHOT = 10;
protected DateTime vetoSnapshot = DateTime.Now;
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Attività corrente (INT) da keyRunMode valore di PLC/DB231/Attivita
/// </summary>
protected int currAct
{
get
{
int answ = 0;
if (!string.IsNullOrEmpty(opcUaParams.keyExeMode))
{
string currRun = getDataItemValue(opcUaParams.keyExeMode);
if (!string.IsNullOrEmpty(currRun))
{
int.TryParse(currRun, out answ);
}
}
return answ;
}
}
/// <summary>
/// Attività corrente (INT) da keyExeMode valore di PLC/DB231/InCorso
/// </summary>
protected int currExe
{
get
{
int answ = 0;
if (!string.IsNullOrEmpty(opcUaParams.keyExeMode))
{
string currExe = getDataItemValue(opcUaParams.keyExeMode);
if (!string.IsNullOrEmpty(currExe))
{
int.TryParse(currExe, out answ);
}
}
return answ;
}
}
/// <summary>
/// Indica se abbia stato MANUAL x CIMOLAI
/// </summary>
protected bool isManualCimolai
{
get
{
bool answ = false;
if (lastAct > 0 && lastAct < 10)
{
answ = (lastAct % 2 == 1);
}
return answ;
}
}
/// <summary>
/// Indica se abbia stato WORKING x CIMOLAI
/// </summary>
protected bool isWorkingCimolai
{
get
{
bool answ = false;
if (lastAct > 0 && lastAct < 10)
{
answ = (lastAct % 2 == 0);
}
return answ;
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
/* -----------------------------------------------------
* CIMOLAI CUSTOM
*------------------------------------------------------
* AUX = 2 --> emergenza armata
* AUX <> 2 --> emergenza premuta
* Aux = 2 + InCorso = 0 --> pronto
* Aux = 2 + LastAct in (2,4,6,8) --> LAVORA
*
*
* PLC/DB231/Attivita
* 0: Emergenza
* 1: Avvio registrazione ricetta
* 2: Inizio comando traslazione
* 3: Termine comando traslazione
* 4: Inizio comando di sterzatura
* 5: Termine comando di sterzatura
* 6: Inizio comando movimento carrelli
* 7: Termine comando movimento carrelli
* 8: Inizio comando sollevamento
* 9: Termine comando sollevamento
* 10: richiesta snapshot parametri
---------------------------------------------------- */
// controllo emergenza... se zero --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
else
{
// resetto last act...
lastAct = 0;
}
// verifico se aggiornare stato LAST ACTION
if (lastAct != currAct && currAct != 0)
{
// registro solo azioni > 1 e < 10
if (currAct > 1 && currAct < 10)
{
// escludo le azioni 4 e 5 (che sono anche in concomitanza con 2-3)
if (currAct < 4 || currAct > 5)
{
lastAct = currAct;
}
}
}
// Gestione ODL automatica: se abilitata --> qui con start/stop da impianto...
if (doProcOdl)
{
// se rilevo variazione exe (curr/last)
// --> registro richiesta attreazzaggio PODL da info commessa
if (lastExe != currExe)
{
// se 0--> 1 --> registro
if (currExe == 1)
{
// prendo senza stringa PODL
string sPODL = currProgName.Replace("PODL", "");
int idxPODL = 0;
int.TryParse(sPODL, out idxPODL);
// chiamo richiesta setup PODL...
trySetupPODL(idxPODL);
}
else
{
// registro chiusura ODL..
tryCloseODL();
}
// registro exe mode
lastExe = currExe;
}
}
// salvo running come = working...
isRunning = isWorkingCimolai;
// se ho setup
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorkingCimolai)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else
{
if (!isReady || isManualCimolai)
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Recupera da server set di dati specifici x IOB : qui per compilare files CSV
/// </summary>
/// <returns></returns>
protected override bool iobGetDataFromServer()
{
bool answ = false;
bool okArt = false;
bool okDoss = false;
bool okPodl = false;
bool okLVFasi = false;
List<PODLModel> listaPODL = new List<PODLModel>();
List<DossiersModel> listaDoss = new List<DossiersModel>();
List<AnagArticoli> listaArt = new List<AnagArticoli>();
List<ListVal> anagLVFasi = new List<ListVal>();
Dictionary<string, string> dictAF = new Dictionary<string, string>();
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
var rawListArt = callUrl(urlGetCurrArt, false);
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
var rawListPODL = callUrl(urlGetCurrPODL, false);
var rawLVFasi = callUrl(urlGetListValFasiPodl, false);
if (!string.IsNullOrEmpty(rawListArt))
{
try
{
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
okArt = listaArt.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListDOSS))
{
try
{
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
okDoss = listaDoss.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
okPodl = listaPODL.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawLVFasi))
{
try
{
anagLVFasi = JsonConvert.DeserializeObject<List<ListVal>>(rawLVFasi);
dictAF = anagLVFasi.ToDictionary(x => x.value, x => x.label);
okLVFasi = listaArt.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco ListVal ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco ListVal ({urlGetListValFasiPodl}) ha restituito valore vuoto");
}
answ = okPodl && okDoss && okArt && okLVFasi;
if (answ)
{
// predispongo dati PODL
ListaJobs = listaPODL
.Select(x => new JobRow() { Matricola = x.CodArticolo, Commessa = $"PODL{x.IdxPromessa:00000000}", Articolo = x.CodArticolo, Descrizione = x.CodArticolo, DataIns = $"{x.InsertDate:dd/MM/yyyy}", OraIns = $"{x.InsertDate:HH:mm}", Lavorazione = $"{getLV(dictAF, x.KeyRichiesta)} {x.Note}".Trim() })
.ToList();
// predispongo dati articoli
ListaArticoli = listaArt
.Select(a => new ArtRow() { Matricola = a.CodArticolo, Articolo = a.Disegno, Descrizione = a.DescArticolo, LimiteVel = 100 })
.Distinct()
.ToList();
// completo con dati DOSSIER
foreach (var item in ListaArticoli)
{
string codArt = item.Matricola;
var currDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
if (currDoss != null)
{
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(currDoss.Valore);
// traduco AD MENTULAM...
item.Peso_01 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso1");
item.Peso_02 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso2");
item.Peso_03 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso3");
item.Peso_04 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso4");
item.PosizCarrello_01 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr1");
item.PosizCarrello_02 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr2");
item.PosizCarrello_03 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr3");
item.PosizCarrello_04 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr4");
}
}
}
return answ;
}
/// <summary>
/// Effettua sync dati
/// </summary>
protected override void processDataSync()
{
lgInfo($"executeTasks --> syncDbData --> processDataSync");
// effettua sync fixme todo: test scrittura file csv...
var taskGet = iobGetDataFromServer();
var taskWrite = iobWriteLocalCSV();
var taskSend = iobSendFTP("");
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// abilitata gestione ODL da impianto
/// </summary>
private bool doProcOdl = false;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Azioni specifiche x indicare fine lotto di produzione
/// </summary>
/// <returns></returns>
private bool setFineLotto()
{
bool answ = false;
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actStopProd)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
}
catch
{ }
return answ;
}
/// <summary>
/// Azioni specifiche x iniziare produzione (impostazione ricetta)
/// </summary>
/// <returns></returns>
private bool setInizioProd()
{
bool answ = false;
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actSetRecipe)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
}
catch
{ }
return answ;
}
#endregion Private Methods
}
}