diff --git a/IOB-WIN-NEXT/DATA/CONF/Tend_RAMA_100_47.json b/IOB-WIN-NEXT/DATA/CONF/Tend_RAMA_100_47.json index d8d36f8e..663b2be0 100644 --- a/IOB-WIN-NEXT/DATA/CONF/Tend_RAMA_100_47.json +++ b/IOB-WIN-NEXT/DATA/CONF/Tend_RAMA_100_47.json @@ -974,77 +974,77 @@ "OPC_SP_Temp_Burner1": { "name": "OPC_SP_Temp_Burner1", "description": "Burner 1", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.0", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.0", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner2": { "name": "OPC_SP_Temp_Burner2", "description": "Burner 2", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.4", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.4", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner3": { "name": "OPC_SP_Temp_Burner3", "description": "Burner 3", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.8", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.8", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner4": { "name": "OPC_SP_Temp_Burner4", "description": "Burner 4", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.12", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.12", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner5": { "name": "OPC_SP_Temp_Burner5", "description": "Burner 5", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.16", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.16", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner6": { "name": "OPC_SP_Temp_Burner6", "description": "Burner 6", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.20", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.20", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner7": { "name": "OPC_SP_Temp_Burner7", "description": "Burner 7", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.24", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.24", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner8": { "name": "OPC_SP_Temp_Burner8", "description": "Burner 8", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.28", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.28", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Temp_Burner9": { "name": "OPC_SP_Temp_Burner9", "description": "Burner 9", - "memAddr": "\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.32", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Temperature\"#GEN.Float.32", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Termoset_ThermoTime": { "name": "OPC_SP_Termoset_ThermoTime", "description": "Thermoset Time", - "memAddr": "\"Recipe_Table_DB_RW\".\"Thermoset\"#GEN.Float.0", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Thermoset\"#GEN.Float.0", "tipoMem": "FloatABCD", "factor": 1 }, "OPC_SP_Termoset_ThermoTemp": { "name": "OPC_SP_Termoset_ThermoTemp", "description": "Thermoset Temperature", - "memAddr": "\"Recipe_Table_DB_RW\".\"Thermoset\"#GEN.Float.4", + "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Thermoset\"#GEN.Float.4", "tipoMem": "FloatABCD", "factor": 1 } diff --git a/IOB-WIN-NEXT/DATA/IobOpcUaClient.Config.xml b/IOB-WIN-NEXT/DATA/IobOpcUaClient.Config.xml index e4d2b0b7..17669aa0 100644 --- a/IOB-WIN-NEXT/DATA/IobOpcUaClient.Config.xml +++ b/IOB-WIN-NEXT/DATA/IobOpcUaClient.Config.xml @@ -55,14 +55,14 @@ - 60000 + 600000 opc.tcp://{0}:4840 http://{0}:52601/UADiscovery http://{0}/UADiscovery/Default.svc - 10000 + 120000 diff --git a/IOB-WIN-NEXT/Iob/Generic.cs b/IOB-WIN-NEXT/Iob/Generic.cs index 4b31087f..630c5b69 100644 --- a/IOB-WIN-NEXT/Iob/Generic.cs +++ b/IOB-WIN-NEXT/Iob/Generic.cs @@ -635,6 +635,26 @@ namespace IOB_WIN_NEXT.Iob } } + /// + /// URL per recupero PODL NEXT = ATTIVABILI x macchina... + /// + public string urlGetActPODL + { + get + { + string answ = ""; + try + { + answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getPOdlAct/{cIobConf.codIOB}"; + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlGetActPODL"); + } + return answ; + } + } + /// /// URL per recupero ARTICOLI correnti x macchina... /// @@ -696,8 +716,9 @@ namespace IOB_WIN_NEXT.Iob } /// - /// URL per recupero PODL correnti x macchina... + /// URL per recupero PODL ATTIVABILI (quindi correnti in senso di pronti) x macchina... /// + [Obsolete("Metodo obsoleto (dubbia interpretazione): sostituire con urlGetNextPODL se si vogliono PROSSIMI POdl attivabili (significato originale) o con urlGetActPODL x il POdl attualmente in produzione (per casi come Rama OPC-UA Siemens)")] public string urlGetCurrPODL { get @@ -735,6 +756,26 @@ namespace IOB_WIN_NEXT.Iob } } + /// + /// URL per recupero PODL NEXT = ATTIVABILI x macchina... + /// + public string urlGetNextPODL + { + get + { + string answ = ""; + try + { + answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getPOdlNext/{cIobConf.codIOB}"; + } + catch (Exception exc) + { + lgError(exc, "Errore in composizione urlGetNextPODL"); + } + return answ; + } + } + /// /// URL per recupero traduzione CodArt in numerico... /// @@ -4113,8 +4154,11 @@ namespace IOB_WIN_NEXT.Iob currProdData.Add(chiave, valore); done = true; } - // salvo in redis... - redisMan.redSaveHashDict(rKeyCurrProdData, currProdData); + if (done) + { + // salvo in redis... + redisMan.redSaveHashDict(rKeyCurrProdData, currProdData); + } return done; } diff --git a/IOB-WIN-NEXT/IobOpc/OpcUa.cs b/IOB-WIN-NEXT/IobOpc/OpcUa.cs index 0db84258..8db14fbe 100644 --- a/IOB-WIN-NEXT/IobOpc/OpcUa.cs +++ b/IOB-WIN-NEXT/IobOpc/OpcUa.cs @@ -3,6 +3,7 @@ using MapoSDK; using Newtonsoft.Json; using NLog.Time; using Opc.Ua; +using Opc.Ua.Client; using Opc.Ua.Configuration; using S7.Net.Types; using System; @@ -14,6 +15,7 @@ using System.Security.AccessControl; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Web.Util; using System.Windows.Forms; using static IOB_UT_NEXT.ByteDataConverter; @@ -172,7 +174,7 @@ namespace IOB_WIN_NEXT.IobOpc string varDef = opcUaParams.keyPartCount.Substring(iPos + 1); // recupero byte byte[] bArray = getDataItemValueBytes(kRef); - answ = ConvBArr(bArray, varDef, opcUaParams.memDelim); + answ = ConvFromBArr(bArray, varDef, opcUaParams.memDelim); } else { @@ -483,7 +485,7 @@ namespace IOB_WIN_NEXT.IobOpc string varDef = opcUaParams.keyPartCount.Substring(iPos + 1); // recupero byte byte[] bArray = getDataItemValueBytes(kRef); - currPzCount = ConvBArr(bArray, varDef, opcUaParams.memDelim); + currPzCount = ConvFromBArr(bArray, varDef, opcUaParams.memDelim); } else { @@ -912,11 +914,11 @@ namespace IOB_WIN_NEXT.IobOpc var rByte = dataItemMem[uuid].rawByte; int iPos = item.Value.memAddr.IndexOf(opcUaParams.kvDelim); string varDef = item.Value.memAddr.Substring(iPos + 1); - string convVal = ConvBArr(rByte, varDef, opcUaParams.memDelim); + string convVal = ConvFromBArr(rByte, varDef, opcUaParams.memDelim); // verifica SE ci sia deadBand da validare... if (valueOverDBand(item.Key, convVal)) { - // metto in array currData... + // metto in array memData... if (upsertKey(item.Key, convVal)) { // accodo se variato! @@ -946,11 +948,11 @@ namespace IOB_WIN_NEXT.IobOpc var rByte = dataItemMem[uuid].rawByte; int iPos = item.Value.memAddr.IndexOf(opcUaParams.kvDelim); string varDef = item.Value.memAddr.Substring(iPos + 1); - string convVal = ConvBArr(rByte, varDef, opcUaParams.memDelim); + string convVal = ConvFromBArr(rByte, varDef, opcUaParams.memDelim); // verifica SE ci sia deadBand da validare... if (valueOverDBand(item.Key, convVal)) { - // metto in array currData... + // metto in array memData... if (upsertKey(item.Key, convVal)) { // accodo se variato! @@ -1735,7 +1737,7 @@ namespace IOB_WIN_NEXT.IobOpc try { var wrapVal = ((DataValue)obj).WrappedValue; - //rawByte = ObjectToByteArray(rawVal); + //rawByte = ObjectToByteArray(sVal); var bodyVal = ((Opc.Ua.ExtensionObject)wrapVal.Value).Body; rawByte = (byte[])bodyVal; } @@ -1744,6 +1746,31 @@ namespace IOB_WIN_NEXT.IobOpc } return rawByte; } +#if false + protected DataValue setByteRaw(DataValue obj, byte[] newData, int startPos) + { + if (obj != null) + { + byte[] rawByte = new byte[1]; + if (obj != null) + { + try + { + var wrapVal = ((DataValue)obj).WrappedValue; + var bodyVal = ((Opc.Ua.ExtensionObject)wrapVal.Value).Body; + rawByte = (byte[])bodyVal; + // sostituisco + Array.Copy(newData, 0, rawByte, startPos, newData.Length); + // aggiorno oggetto originale... + ((Opc.Ua.ExtensionObject)wrapVal.Value).Body = rawByte; + } + catch + { } + } + } + return obj; + } +#endif /// /// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non @@ -1984,7 +2011,7 @@ namespace IOB_WIN_NEXT.IobOpc /// Stringa definizione variabile /// Separatore nella definizione /// - private string ConvBArr(byte[] bArray, string varDef, char pSep) + private string ConvFromBArr(byte[] bArray, string varDef, char pSep) { string answ = ""; // converto prendendo solo parte richiesta... @@ -2093,6 +2120,123 @@ namespace IOB_WIN_NEXT.IobOpc return answ; } + /// + /// Conversione da valore string a corrispettivo byteArray nel tipo obj richiesto + /// + /// Valore da inviare come string + /// Stringa definizione variabile + /// Separatore nella definizione + /// + private byte[] ConvToBArr(string sVal, string varDef, char pSep) + { + byte[] answ = new byte[1]; + // converto prendendo solo parte richiesta... + if (!string.IsNullOrEmpty(sVal)) + { + // splitto in primis la definizione + MemStruct currMem = new MemStruct(varDef, pSep); + + // S7.Double.B0 ora eseguo riconoscimento (da portare in base? FixMe ToDo...) + if (currMem.Product == "S7") + { +#if false + // converto! + plcDataType tVal = plcDataType.Int; + Enum.TryParse(currMem.Type, out tVal); + // ora gestisco seconda parte... + switch (tVal) + { + case plcDataType.Boolean: + var boolVal = S7.Net.Types.Boolean.GetValue(bArray[currMem.Idx], currMem.Num); + answ = $"{boolVal}"; + break; + + case plcDataType.Int: + var intVal = S7.Net.Types.Int.FromByteArray(bArray.Skip(currMem.Idx).Take(2).ToArray()); + answ = $"{intVal}"; + break; + //case plcDataType.IntLH: + // break; + case plcDataType.DInt: + var dIntVal = S7.Net.Types.Int.FromByteArray(bArray.Skip(currMem.Idx).Take(4).ToArray()); + answ = $"{dIntVal}"; + break; + //case plcDataType.DIntLH: + // break; + case plcDataType.Word: + var wordVal = S7.Net.Types.Word.FromByteArray((bArray.Skip(currMem.Idx).Take(2).ToArray())); + answ = $"{wordVal}"; + break; + + case plcDataType.DWord: + var dWordVal = S7.Net.Types.Word.FromByteArray((bArray.Skip(currMem.Idx).Take(4).ToArray())); + answ = $"{dWordVal}"; + break; + + case plcDataType.Real: + var dblVal = S7.Net.Types.Double.FromByteArray(bArray.Skip(currMem.Idx).Take(4).ToArray()); + answ = $"{dblVal:N3}"; + break; + //case plcDataType.RealHL: + // break; + //case plcDataType.RealLH: + // break; + case plcDataType.String: + answ = S7.Net.Types.String.FromByteArray(bArray.Skip(currMem.Idx).Take(currMem.Num).ToArray()); + break; + //case plcDataType.HLPInt: + // break; + //case plcDataType.FloatABCD: + // break; + //case plcDataType.FloatBADC: + // break; + //case plcDataType.FloatCDAB: + // break; + //case plcDataType.FloatDCBA: + // break; + //case plcDataType.BitMap: + // break; + //case plcDataType.Byte: + // break; + //case plcDataType.UInt: + // break; + //case plcDataType.UDInt: + // break; + default: + break; + } +#endif + } + else if (currMem.Product == "GEN") + { + // ora gestisco seconda parte... + switch (currMem.Type) + { + case "Byte": + answ = new byte[1]; + bool bVal = false; + bool.TryParse(sVal, out bVal); + answ[0] = bVal ? (byte)1 : (byte)0; + break; + + case "Float": + float fVal = 0; + float.TryParse(sVal, out fVal); + answ = BitConverter.GetBytes(fVal); + break; + + default: + break; + } + } + else + { + // gestire casi NON S7... + } + } + return answ; + } + /// /// Vera connessione ad OpcUa /// @@ -2117,6 +2261,11 @@ namespace IOB_WIN_NEXT.IobOpc // load the application configuration. string confPath = $"{Application.StartupPath}\\DATA\\IobOpcUaClient.Config.xml"; await application.LoadApplicationConfiguration(confPath, silent: false).ConfigureAwait(false); +#if false + // impostazioni speciali client + application.ApplicationConfiguration.ClientConfiguration.DefaultSessionTimeout = 1000 * 60 * 60; + application.ApplicationConfiguration.ClientConfiguration.MinSubscriptionLifetime = 1000 * 60; +#endif // check the application certificate. await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0).ConfigureAwait(false); @@ -2424,6 +2573,205 @@ namespace IOB_WIN_NEXT.IobOpc readErrorCurr = 0; } + + /// + /// Effettua vera scrittura parametri + /// + /// + protected override void plcWriteParams(ref List updatedPar) + { + // solo se connesso! + if (connectionOk) + { + dataConf currMem = null; + int byteSize = 0; + string memAddrWrite = ""; + string serObj = ""; + if (updatedPar != null) + { + // cache dei valori da scrivere in memoria x i valori composti come byte[] + Dictionary memWriteCache = new Dictionary(); + Dictionary nodes2Write = new Dictionary(); + // controllo i parametri... ne gestisco 4... + foreach (var item in updatedPar) + { + if (connectionOk) + { + try + { + memAddrWrite = ""; + int valInt = 0; + double valReal = 0; + // cerco in area memMapWrite... + if (memMap.mMapWrite.ContainsKey(item.uid)) + { + // recupero! + currMem = memMap.mMapWrite[item.uid]; + byteSize = currMem.size; + memAddrWrite = currMem.memAddr; + + // init obj scrittura + WriteValue commWriteVal = new WriteValue(); + commWriteVal.Value = new DataValue(); + commWriteVal.AttributeId = Attributes.Value; + + + // faccio preliminarmente upsertKey... + upsertKey(memAddrWrite, currMem.value); + serObj = JsonConvert.SerializeObject(item); + lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}"); + lgInfo($"{Environment.NewLine}---------------UPDATED PARAM:{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); + serObj = JsonConvert.SerializeObject(currMem); + lgInfo($"{Environment.NewLine}---------------MEMORY CONTENT:{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); + + // verifico se devo processare byte[]... + bool isByteArr = memAddrWrite.Contains(opcUaParams.kvDelim); + byte[] memData = new byte[0]; + byte[] newData = new byte[0]; + if (isByteArr) + { + // ciclo e calcolo valore + int iPos = memAddrWrite.IndexOf(opcUaParams.kvDelim); + string memName = memAddrWrite.Substring(0, iPos); + string varDef = memAddrWrite.Substring(iPos + 1); + // verifico se ho in array.. + if (!memWriteCache.ContainsKey(memName)) + { + // leggo come byte[] + NodeId reqNode = new NodeId(memName); + var rawVal = UA_ref.ReadNodeRaw(reqNode); + if (rawVal != null) + { + memData = getByteRaw((DataValue)rawVal); + } + else + { + // registro x uscire subito! +#if false + numErroriCheck = maxErroriCheck; +#endif + numErroriCheck += 10; + } + } + // sennò leggo da server... + else + { + memData = memWriteCache[memName]; + } + + // se ho dati scrivo e salvo in memoria + if (memData != null && memData.Length > 0) + { + // recupero il valore come byte[] + MemStruct mStruc = new MemStruct(varDef, opcUaParams.memDelim); + newData = ConvToBArr(item.reqValue, varDef, opcUaParams.memDelim); + // sostituisco blocco new in byte[] + Array.Copy(newData, 0, memData, mStruc.Num, newData.Length); + + // scrittura nell'oggetto OPC-UA direttamente + commWriteVal.NodeId = new NodeId(memName); + //commWriteVal.Value.Value = memData; + //commWriteVal.Value.WrappedValue = memData; + + // impacchetto + ExtensionObject extObject = new ExtensionObject + { + Body = memData + }; + + commWriteVal.Value.Value = extObject; + + //((Opc.Ua.ExtensionObject)commWriteVal.Value.WrappedValue.Value).Body = memData; + // salvo! + memWriteCache.Add(memName, memData); + } + } + else + { + // init generico x scrittura nell'oggetto OPC-UA direttamente + commWriteVal.NodeId = new NodeId(memAddrWrite); + commWriteVal.Value.Value = item.reqValue; + + // check tipo specifico + switch (currMem.tipoMem) + { + case plcDataType.Boolean: + break; + + case plcDataType.Int: + case plcDataType.DInt: + case plcDataType.Word: + case plcDataType.DWord: + int.TryParse(item.reqValue, out valInt); + commWriteVal.Value.Value = valInt; + break; + + case plcDataType.Real: + double.TryParse(item.reqValue, out valReal); + commWriteVal.Value.Value = valReal; + break; + + case plcDataType.String: + commWriteVal.Value.Value = item.reqValue; + break; + + default: + break; + } + } + lgInfo($"---------------{Environment.NewLine}OPC-UA data:{Environment.NewLine}NodeId: {commWriteVal.NodeId}{Environment.NewLine}Value: {commWriteVal.Value.Value}{Environment.NewLine}---------------"); + + // se supero soglia errori lettura --> disconnetto e resetto + if (numErroriCheck >= maxErroriCheck) + { + lgInfo($"numErroriCheck: {numErroriCheck} --> richiesta disconnessione adapter con tryDisconnect"); + + numErroriCheck = 0; + tryDisconnect(); + } + else + { + if (!string.IsNullOrEmpty(memAddrWrite) && !string.IsNullOrEmpty($"{commWriteVal.NodeId}")) + { + // cerco se ci fosse già --> aggiorno + if (nodes2Write.ContainsKey($"{commWriteVal.NodeId}")) + { + nodes2Write[$"{commWriteVal.NodeId}"] = commWriteVal; + } + else + { + nodes2Write.Add($"{commWriteVal.NodeId}", commWriteVal); + } + // 2023.09.19 resetto richiesta + item.reqValue = ""; + } + else + { + lgInfo($"Errore: memAddrWrite vuoto!"); + } + } + } + else + { + lgInfo($"Errore uid non trovato in area write memory: {item.uid}, ci sono {memMap.mMapWrite.Count} in area write"); + } + } + catch (Exception exc) + { + lgError($"Eccezione in fase di plcWriteParams per item {item.uid} con valore {item.value}{Environment.NewLine}{exc}"); + } + } + } + + if (connectionOk && nodes2Write.Count > 0) + { + List listWriteReq = nodes2Write.Select(x => x.Value).ToList(); + UA_ref.WriteNodes(listWriteReq); + } + } + } + } + #endregion Private Methods } } \ No newline at end of file diff --git a/IOB-WIN-NEXT/IobOpc/OpcUaKpwRama.cs b/IOB-WIN-NEXT/IobOpc/OpcUaKpwRama.cs index 52cc93f4..89f993a8 100644 --- a/IOB-WIN-NEXT/IobOpc/OpcUaKpwRama.cs +++ b/IOB-WIN-NEXT/IobOpc/OpcUaKpwRama.cs @@ -71,9 +71,9 @@ namespace IOB_WIN_NEXT.IobOpc * - recupero elenco DOSSIER last x macchina (legato ai PODL attivi) * - prendo SOLO PODL attivi, e prendo il + vecchio * - se trovo dossier x articolo del PODL + vecchio (e dovrebbe...) procedo - * - uso Dictionary RecipeKeyTranslate x tradurre l'elenco fluxLog in nuovi valori + * - uso Dictionary dei memWrite x tradurre l'elenco fluxLog in nuovi valori * - preparo il set di valori da scrivere con traduzione - * - invio scritture (sim test --> scrivo su file?) + * - invio scritture * - aggiungo info articolo (+ ODL...) * - aggiungo il valore di "scrittura effettuata" *----------------------------------------- */ @@ -87,7 +87,8 @@ namespace IOB_WIN_NEXT.IobOpc // 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); + // fixme todo verificare se usare urlGetActPODL + var rawListPODL = callUrl(urlGetNextPODL, false); if (!string.IsNullOrEmpty(rawListArt)) { try @@ -134,7 +135,7 @@ namespace IOB_WIN_NEXT.IobOpc } else { - lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto"); + lg.Error($"Errore: chiamata elenco PODL ({urlGetNextPODL}) ha restituito valore vuoto"); } answ = okArt && okPodl && okDoss; @@ -187,129 +188,7 @@ namespace IOB_WIN_NEXT.IobOpc return answ; } - /// - /// Recupera da server set di dati specifici x IOB : qui per compilare ricette via OPC-UA - /// - /// - protected override bool iobGetDataFromServer() - { - bool answ = false; - bool okArt = false; - bool okDoss = false; - bool okPodl = false; - bool okLVFasi = false; - List listaPODL = new List(); - List listaDoss = new List(); - List listaArt = new List(); - List anagLVFasi = new List(); - Dictionary dictAF = new Dictionary(); - // 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>(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>(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>(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>(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(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; - } - + #endregion Public Constructors } diff --git a/IOB-WIN-NEXT/IobOpc/OpcUaSiemens.cs b/IOB-WIN-NEXT/IobOpc/OpcUaSiemens.cs index f7964f60..c9be612c 100644 --- a/IOB-WIN-NEXT/IobOpc/OpcUaSiemens.cs +++ b/IOB-WIN-NEXT/IobOpc/OpcUaSiemens.cs @@ -291,6 +291,7 @@ namespace IOB_WIN_NEXT.IobOpc } } +#if false /// /// Effettua vera scrittura parametri /// @@ -303,6 +304,8 @@ namespace IOB_WIN_NEXT.IobOpc string serObj = ""; if (updatedPar != null) { + // cache dei valori da scrivere in memoria x i valori composti come byte[] + Dictionary memWriteCache = new Dictionary(); List nodes2Write = new List(); // controllo i parametri... ne gestisco 4... foreach (var item in updatedPar) @@ -320,6 +323,8 @@ namespace IOB_WIN_NEXT.IobOpc byteSize = currMem.size; memAddrWrite = currMem.memAddr; + // rifare: se è "Recipe_Table_DB_RW"."Thermoset"#GEN.Float.4 --> devo leggere un datablock di byte, aggiornare solo un pezzo... POI scrivere l'intero datablock... + WriteValue commWriteVal = new WriteValue(); commWriteVal.NodeId = new NodeId(memAddrWrite); commWriteVal.AttributeId = Attributes.Value; @@ -388,7 +393,8 @@ namespace IOB_WIN_NEXT.IobOpc UA_ref.WriteNodes(nodes2Write); } } - } + } +#endif #endregion Protected Methods } diff --git a/IOB-WIN-NEXT/IobOpc/OpcUaSiemensRama.cs b/IOB-WIN-NEXT/IobOpc/OpcUaSiemensRama.cs index e8975e43..6f8e3bc9 100644 --- a/IOB-WIN-NEXT/IobOpc/OpcUaSiemensRama.cs +++ b/IOB-WIN-NEXT/IobOpc/OpcUaSiemensRama.cs @@ -36,23 +36,6 @@ namespace IOB_WIN_NEXT.IobOpc * QUANDO FUNZIONA verificare se e come rendere parametrica la gestione oggetti/byte configurabili * *-------------------------------------------*/ -#if false - // inizializzo classe base... - if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE"))) - { - CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE"); - } - // 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); - } -#endif sendKeyRichiesta = true; } @@ -70,14 +53,15 @@ namespace IOB_WIN_NEXT.IobOpc } /// - /// Recupero dati da server x invio dati ricetta a KepWare (Ex RAMA x TFT) + /// Recupero dati da server x invio dati ricetta a OPC-UA Siemens (RAMA x Tenditalia) + /// NB: modificato rispetto a TFT: invia PODL correntemente in esecuzione (SE DISPONIBILE) oppure il PRIMO di quelli attivabili... /// /// private bool iobGetSendDossierKepware() { bool answ; - lg.Info("iobGetSendDossierKepware: Inizio scrittura dati a KepWare"); + lgInfo("iobGetSendDossierKepware: Inizio scrittura dati a KepWare"); /*----------------------------------------- * STEPS processo: * - recupero ARTICOLI (solo quelli dei PODL attivi) @@ -85,11 +69,11 @@ namespace IOB_WIN_NEXT.IobOpc * - recupero elenco DOSSIER last x macchina (legato ai PODL attivi) * - prendo SOLO PODL attivi, e prendo il + vecchio * - se trovo dossier x articolo del PODL + vecchio (e dovrebbe...) procedo - * - uso Dictionary RecipeKeyTranslate x tradurre l'elenco fluxLog in nuovi valori + * - uso Dictionary dei memWrite x tradurre l'elenco fluxLog in nuovi valori * - preparo il set di valori da scrivere con traduzione - * - invio scritture (sim test --> scrivo su file?) + * - invio scritture * - aggiungo info articolo (+ ODL...) - * - aggiungo il valore di "scrittura effettuata" + * - aggiungo i valori BIT di "reset contapezzi " + "nuova ricetta" *----------------------------------------- */ bool okArt = false; @@ -101,7 +85,7 @@ namespace IOB_WIN_NEXT.IobOpc // 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 rawListPODL = callUrl(urlGetActPODL, false); if (!string.IsNullOrEmpty(rawListArt)) { try @@ -148,7 +132,7 @@ namespace IOB_WIN_NEXT.IobOpc } else { - lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto"); + lg.Error($"Errore: chiamata elenco PODL ({urlGetActPODL}) ha restituito valore vuoto"); } answ = okArt && okPodl && okDoss; @@ -184,6 +168,14 @@ namespace IOB_WIN_NEXT.IobOpc ListaCalcParams.Add(currData); lgInfo($"Add param: {currData.uid} | reqVal: {currData.reqValue}"); } + // aggiungo ultimo parametro di resetCounter... + objItem resetCnt = new objItem() + { + uid = "ResetCounter", + reqValue = "true", + name = "ResetCounter" + }; + ListaCalcParams.Add(resetCnt); // aggiungo ultimo parametro di newRecipe... objItem newRecipeData = new objItem() { @@ -201,129 +193,7 @@ namespace IOB_WIN_NEXT.IobOpc return answ; } - /// - /// Recupera da server set di dati specifici x IOB : qui per compilare ricette via OPC-UA - /// - /// - protected override bool iobGetDataFromServer() - { - bool answ = false; - bool okArt = false; - bool okDoss = false; - bool okPodl = false; - bool okLVFasi = false; - List listaPODL = new List(); - List listaDoss = new List(); - List listaArt = new List(); - List anagLVFasi = new List(); - Dictionary dictAF = new Dictionary(); - // 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>(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>(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>(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>(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(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; - } - + #endregion Public Constructors } diff --git a/IOB-WIN-NEXT/UAClient.cs b/IOB-WIN-NEXT/UAClient.cs index 2c00176c..29397116 100644 --- a/IOB-WIN-NEXT/UAClient.cs +++ b/IOB-WIN-NEXT/UAClient.cs @@ -334,7 +334,7 @@ namespace IOB_WIN_NEXT Session session = await Session.Create( m_configuration, endpoint, - false, + true, false, m_configuration.ApplicationName, 60 * 60 * 1000, @@ -342,6 +342,20 @@ namespace IOB_WIN_NEXT null ); + +#if false + Session session = await Session.Create( + m_configuration, + endpoint, + false, + false, + m_configuration.ApplicationName, + 60 * 60 * 1000, + CurrUserIdentity, + null + ); +#endif + // Assign the created session if (session != null && session.Connected) { @@ -484,6 +498,7 @@ namespace IOB_WIN_NEXT if (m_session == null || m_session.Connected == false) { lgError("Session not connected!"); + numErrors++; return answ; } @@ -500,6 +515,14 @@ namespace IOB_WIN_NEXT { // Log Error lgError($"ReadNodeRaw Error 01: {Environment.NewLine}{exc}"); + numErrors++; + + // provare disconnect?!? + if (numErrors >= maxErrors) + { + numErrors = 0; + Disconnect(); + } } #endregion Read the Value attribute of a node by calling the Session.ReadValue method @@ -678,6 +701,14 @@ namespace IOB_WIN_NEXT { // Log Error lgError($"Write Nodes Error : {ex.Message}"); + numErrors++; + + // provare disconnect?!? + if (numErrors >= maxErrors) + { + numErrors = 0; + Disconnect(); + } } } }