913 lines
36 KiB
C#
913 lines
36 KiB
C#
using MapoSDK;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using static IOB_UT_NEXT.CustomObj;
|
|
|
|
namespace IOB_WIN_NEXT.IobOpc
|
|
{
|
|
public class OpcUaMBHCimolai : OpcUaMBH
|
|
{
|
|
#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 OpcUaMBHCimolai(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");
|
|
lgInfo($"CHANGE_ODL_MODE: {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);
|
|
lgInfo($"VETO_SEND_SNAPSHOT: {VETO_SEND_SNAPSHOT}");
|
|
}
|
|
// gestione restart OpcUa client...
|
|
if (!string.IsNullOrEmpty(getOptPar("ENABLE_CLI_RESTART")))
|
|
{
|
|
bool.TryParse(getOptPar("ENABLE_CLI_RESTART"), out enableCliRestart);
|
|
}
|
|
// init lastCurrentMaxElapsed
|
|
if (!string.IsNullOrEmpty(getOptPar("MAX_ELAPSED_TIME_SEC")))
|
|
{
|
|
int.TryParse(getOptPar("MAX_ELAPSED_TIME_SEC"), out lastCurrentMaxElapsed);
|
|
}
|
|
|
|
sendKeyRichiesta = true;
|
|
|
|
// controllo se abilitare Processing automatico ODL
|
|
if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL")))
|
|
{
|
|
bool.TryParse(getOptPar("AUTO_CHANGE_ODL"), out doProcOdl);
|
|
lgInfo($"AUTO_CHANGE_ODL: {doProcOdl}");
|
|
}
|
|
|
|
// controllo se disabilitare check exe mode
|
|
if (!string.IsNullOrEmpty(getOptPar("EXEMODE_CHECK_BYPASS")))
|
|
{
|
|
bool.TryParse(getOptPar("EXEMODE_CHECK_BYPASS"), out EXEMODE_CHECK_BYPASS);
|
|
lgInfo($"EXEMODE_CHECK_BYPASS: {EXEMODE_CHECK_BYPASS}");
|
|
}
|
|
}
|
|
|
|
#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;
|
|
#if 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
|
|
{ }
|
|
#endif
|
|
return answ;
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Protected Fields
|
|
|
|
protected bool EXEMODE_CHECK_BYPASS = false;
|
|
protected int lastAct = 0;
|
|
|
|
protected bool lastIsInCorso = false;
|
|
|
|
/// <summary>
|
|
/// Ultimo rum mode rilevato x decidere SE inviare variazione...
|
|
/// </summary>
|
|
protected int lastRunMode = -999;
|
|
|
|
protected int VETO_SEND_SNAPSHOT = 10;
|
|
|
|
protected DateTime vetoSnapshot = DateTime.Now;
|
|
|
|
#endregion Protected Fields
|
|
|
|
#region Protected Enums
|
|
|
|
/// <summary>
|
|
/// Definizione stati travel
|
|
/// </summary>
|
|
protected enum travelState
|
|
{
|
|
Idle = 0,
|
|
StartOdl,
|
|
UpLift,
|
|
Move,
|
|
DownLift,
|
|
WaitClose
|
|
}
|
|
|
|
#endregion Protected Enums
|
|
|
|
#region Protected Properties
|
|
|
|
/// <summary>
|
|
/// Attività corrente (INT) da keyRunMode valore di PLC/DB231/Attivita
|
|
/// </summary>
|
|
protected int currRunMode
|
|
{
|
|
get
|
|
{
|
|
int answ = 0;
|
|
if (!string.IsNullOrEmpty(opcUaParams.keyRunMode))
|
|
{
|
|
string currRun = getDataItemValue(opcUaParams.keyRunMode);
|
|
if (!string.IsNullOrEmpty(currRun))
|
|
{
|
|
int.TryParse(currRun, out answ);
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
}
|
|
|
|
protected travelState currTravelState { get; set; } = travelState.Idle;
|
|
|
|
protected DateTime currTravelStateStart { get; set; } = DateTime.Today.AddYears(-1);
|
|
|
|
/// <summary>
|
|
/// Lavorazione in CORSO = da keyExeMode valore di PLC/DB231/InCorso
|
|
/// </summary>
|
|
protected bool isInCorso
|
|
{
|
|
get
|
|
{
|
|
bool answ = false;
|
|
if (!string.IsNullOrEmpty(opcUaParams.keyExeMode))
|
|
{
|
|
string currExe = getDataItemValue(opcUaParams.keyExeMode);
|
|
if (!string.IsNullOrEmpty(currExe))
|
|
{
|
|
bool.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
|
|
*
|
|
*------------------------------------------------------
|
|
* SEMPLIFICAZIONE POST CERTIFICAZIONE
|
|
*------------------------------------------------------
|
|
*
|
|
* Visto che la rete potrebbe saltare ad intermittenza, conviene gestire in modo semplificato il lavora
|
|
* torno a condizione work base: AUX = 2, marcia = in corso
|
|
*
|
|
*
|
|
---------------------------------------------------- */
|
|
|
|
// 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 != currRunMode && currRunMode != 0)
|
|
{
|
|
// registro solo azioni > 1 e < 10
|
|
if (currRunMode > 1 && currRunMode < 10)
|
|
{
|
|
// escludo le azioni 4 e 5 (che sono anche in concomitanza con 2-3)
|
|
if (currRunMode < 4 || currRunMode > 5)
|
|
{
|
|
lastAct = currRunMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gestione ODL automatica: se abilitata --> qui con start/stop da impianto...
|
|
if (doProcOdl)
|
|
{
|
|
lgTrace($"inizio process verifica presa in carico PODL | isInCorso: {isInCorso} | lastIsInCorso: {lastIsInCorso} | currProgName {currProgName}");
|
|
// se rilevo variazione exe (curr/last)
|
|
// --> registro richiesta attreazzaggio PODL da info commessa
|
|
if (lastIsInCorso != isInCorso)
|
|
{
|
|
// se 0 --> 1 --> registro
|
|
if (isInCorso)
|
|
{
|
|
lgInfo("--------------------------------------------");
|
|
// prendo senza stringa PODL
|
|
string sPODL = currProgName.Replace("PODL", "");
|
|
int idxPODL = 0;
|
|
int.TryParse(sPODL, out idxPODL);
|
|
// chiamo richiesta setup PODL...
|
|
lgInfo($"Inizio trySetupPODL per {idxPODL}");
|
|
trySetupPODL(idxPODL);
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
else
|
|
{
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("Inizio tryCloseODL");
|
|
// registro chiusura ODL..
|
|
tryCloseCurrODL();
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
// registro exe mode
|
|
lastIsInCorso = isInCorso;
|
|
}
|
|
}
|
|
|
|
// 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 || isWorking || isInCorso)
|
|
{
|
|
// RUN = LAVORA!
|
|
B_input += (1 << 1);
|
|
}
|
|
else
|
|
{
|
|
if (!isReady || isManualCimolai || isManual)
|
|
{
|
|
// se NON ready --> manual
|
|
B_input += (1 << 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
// controllo se non ho dati buoni da > lastCurrentMaxElapsed sec --> disconnetto
|
|
if (adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed)
|
|
{
|
|
lgInfo($"Timeout per mancata comunicazione da oltre {lastCurrentMaxElapsed} sec --> disconnessione adapter OpcUa!");
|
|
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 PODL 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("--------------------------------------------");
|
|
lgInfo($"executeTasks --> syncDbData --> processDataSync");
|
|
lgInfo("--------------------------------------------");
|
|
// effettua sync fixme todo: test scrittura file csv...
|
|
var taskGet = iobGetDataFromServer();
|
|
var taskWrite = iobWriteLocalCSV();
|
|
var taskSend = iobSendFTP("");
|
|
}
|
|
|
|
protected override void procRunMode(ref string currRun)
|
|
{
|
|
// variabili RUN... se richiesto invio runMode
|
|
if (opcUaParams.runModeSend)
|
|
{
|
|
currRun = $"{currRunMode}";
|
|
// effettuo processing SPECIFICO currRunMode x gestione snapshot...
|
|
if (currRunMode == 10)
|
|
{
|
|
if (DateTime.Now > vetoSnapshot)
|
|
{
|
|
lgInfo("--------------------------------------------");
|
|
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}");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
else
|
|
{
|
|
lgInfo($"NON effettuo salvataggio snapshot perché veto attivo fino a {vetoSnapshot}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// aggiorno veto x snapshot
|
|
vetoSnapshot = DateTime.Now;
|
|
}
|
|
|
|
// invio del valore RUN MODE come FLog, SE variato...
|
|
if (!string.IsNullOrEmpty(currRun))
|
|
{
|
|
if (currReadErrors != lastRunMode)
|
|
{
|
|
// aggiorno last...
|
|
lastRunMode = currRunMode;
|
|
// 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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//verifica preliminare durata minima stato
|
|
DateTime adesso = DateTime.Now;
|
|
bool canChange = adesso.Subtract(currTravelStateStart).TotalSeconds > opcUaParams.minSecStatusDuration;
|
|
// gestione dei macro-stati del travel... SOLO se in esecuzione da travel... BYPASS x ora!!!
|
|
if (isInCorso || EXEMODE_CHECK_BYPASS)
|
|
{
|
|
double pesoTot = 0;
|
|
// può cambiare stato se per ALMENO minSecStatus è rimasto nello status corrente...
|
|
if (canChange)
|
|
{
|
|
lgTrace($"start checkExe | TRAVEL STATE: {currTravelState}");
|
|
// STEP 1: verifico se sia iniziato caricamento...
|
|
if (currTravelState == travelState.Idle)
|
|
{
|
|
if (currIdxODL > 0)
|
|
{
|
|
currTravelState = travelState.StartOdl;
|
|
currTravelStateStart = adesso;
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("TRAVEL STATE: StartOdl");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
}
|
|
// sono nell'esecuzione ODL, controllo il resto
|
|
else
|
|
{
|
|
// nb: vedere se usare isWorkingCimolai / isManualCimolai
|
|
|
|
// verifico il valore della variabile calcolata del PESO totale...
|
|
if (dataItemMem.ContainsKey("PesoTot"))
|
|
{
|
|
pesoTot = getDataItemValueDouble("PesoTot");
|
|
}
|
|
// ...oppuresommo direttamente
|
|
else
|
|
{
|
|
pesoTot += getDataItemValueDouble("PLC/DB231/peso1");
|
|
pesoTot += getDataItemValueDouble("PLC/DB231/peso2");
|
|
pesoTot += getDataItemValueDouble("PLC/DB231/peso3");
|
|
pesoTot += getDataItemValueDouble("PLC/DB231/peso4");
|
|
}
|
|
|
|
lgTrace($"TState: Test 03 | pesoTot: {pesoTot}");
|
|
|
|
// STEP 2: verifico se sia iniziato caricamento...
|
|
if (currTravelState == travelState.StartOdl)
|
|
{
|
|
if (pesoTot > 0)
|
|
{
|
|
currTravelState = travelState.UpLift;
|
|
currTravelStateStart = adesso;
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("TRAVEL STATE: UpLift");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
}
|
|
// STEP 3: verifico se sia terminato caricamento...
|
|
else if (currTravelState == travelState.UpLift)
|
|
{
|
|
if (pesoTot == 0)
|
|
{
|
|
currTravelState = travelState.Move;
|
|
currTravelStateStart = adesso;
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("TRAVEL STATE: Move");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
}
|
|
// STEP 4: verifico se sia terminato movimento...
|
|
else if (currTravelState == travelState.Move)
|
|
{
|
|
if (pesoTot > 0)
|
|
{
|
|
currTravelState = travelState.DownLift;
|
|
currTravelStateStart = adesso;
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("TRAVEL STATE: DownLift");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
}
|
|
// STEP 5: verifico se sia terminato scaricamento...
|
|
else if (currTravelState == travelState.DownLift)
|
|
{
|
|
if (pesoTot == 0)
|
|
{
|
|
currTravelState = travelState.WaitClose;
|
|
currTravelStateStart = adesso;
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("TRAVEL STATE: WaitClose");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (canChange)
|
|
{
|
|
lgTrace("TRAVEL STATE | Nessuna gestione status travel: inCorso = 0");
|
|
}
|
|
}
|
|
|
|
// ora verifico solo condizione finale x eventuale chiusura...
|
|
bool canSendAskClose = adesso.Subtract(currTravelStateStart).TotalSeconds > opcUaParams.minSecFinalWait;
|
|
if (canSendAskClose && currTravelState == travelState.WaitClose)
|
|
{
|
|
// STEP 6: verifico se sia tolto ODL e quindi ha già chiuso...
|
|
if (currIdxODL == 0)
|
|
{
|
|
currTravelState = travelState.Idle;
|
|
currTravelStateStart = adesso;
|
|
lgInfo("--------------------------------------------");
|
|
lgInfo("TRAVEL STATE: Idle");
|
|
lgInfo("--------------------------------------------");
|
|
}
|
|
// altrimenti manda richiesta chiusura
|
|
else
|
|
{
|
|
// chiama richiesta chiusura x utente
|
|
tryAskCloseCurrODL();
|
|
// resetta contatore x nuova richeista finale eventuale...
|
|
currTravelStateStart = adesso;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (canChange)
|
|
{
|
|
// altrimenti forzo a "Idle" state
|
|
currTravelState = travelState.Idle;
|
|
currTravelStateStart = adesso;
|
|
// trace opzionale
|
|
lgTrace($"TRAVEL STATE: {currTravelState}");
|
|
}
|
|
}
|
|
}
|
|
|
|
#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;
|
|
#if 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
|
|
{ }
|
|
#endif
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Azioni specifiche x iniziare produzione (impostazione ricetta)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool setInizioProd()
|
|
{
|
|
bool answ = false;
|
|
#if 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
|
|
{ }
|
|
#endif
|
|
return answ;
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
} |