update x test gestione scrittura Siemens OPC-UA

This commit is contained in:
Samuele Locatelli
2024-11-28 18:27:36 +01:00
parent 650091918d
commit da340ee047
8 changed files with 478 additions and 300 deletions
+11 -11
View File
@@ -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
}
+2 -2
View File
@@ -55,14 +55,14 @@
</TransportQuotas>
<ClientConfiguration>
<DefaultSessionTimeout>60000</DefaultSessionTimeout>
<DefaultSessionTimeout>600000</DefaultSessionTimeout>
<WellKnownDiscoveryUrls>
<ua:String>opc.tcp://{0}:4840</ua:String>
<ua:String>http://{0}:52601/UADiscovery</ua:String>
<ua:String>http://{0}/UADiscovery/Default.svc</ua:String>
</WellKnownDiscoveryUrls>
<DiscoveryServers></DiscoveryServers>
<MinSubscriptionLifetime>10000</MinSubscriptionLifetime>
<MinSubscriptionLifetime>120000</MinSubscriptionLifetime>
</ClientConfiguration>
<Extensions>
+47 -3
View File
@@ -635,6 +635,26 @@ namespace IOB_WIN_NEXT.Iob
}
}
/// <summary>
/// URL per recupero PODL NEXT = ATTIVABILI x macchina...
/// </summary>
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;
}
}
/// <summary>
/// URL per recupero ARTICOLI correnti x macchina...
/// </summary>
@@ -696,8 +716,9 @@ namespace IOB_WIN_NEXT.Iob
}
/// <summary>
/// URL per recupero PODL correnti x macchina...
/// URL per recupero PODL ATTIVABILI (quindi correnti in senso di pronti) x macchina...
/// </summary>
[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
}
}
/// <summary>
/// URL per recupero PODL NEXT = ATTIVABILI x macchina...
/// </summary>
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;
}
}
/// <summary>
/// URL per recupero traduzione CodArt in numerico...
/// </summary>
@@ -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;
}
+356 -8
View File
@@ -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
/// <summary>
/// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non
@@ -1984,7 +2011,7 @@ namespace IOB_WIN_NEXT.IobOpc
/// <param name="varDef">Stringa definizione variabile</param>
/// <param name="pSep">Separatore nella definizione</param>
/// <returns></returns>
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;
}
/// <summary>
/// Conversione da valore string a corrispettivo byteArray nel tipo obj richiesto
/// </summary>
/// <param name="sVal">Valore da inviare come string</param>
/// <param name="varDef">Stringa definizione variabile</param>
/// <param name="pSep">Separatore nella definizione</param>
/// <returns></returns>
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;
}
/// <summary>
/// Vera connessione ad OpcUa
/// </summary>
@@ -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;
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> 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<string, byte[]> memWriteCache = new Dictionary<string, byte[]>();
Dictionary<string, WriteValue> nodes2Write = new Dictionary<string, WriteValue>();
// 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<WriteValue> listWriteReq = nodes2Write.Select(x => x.Value).ToList();
UA_ref.WriteNodes(listWriteReq);
}
}
}
}
#endregion Private Methods
}
}
+6 -127
View File
@@ -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;
}
/// <summary>
/// Recupera da server set di dati specifici x IOB : qui per compilare ricette via OPC-UA
/// </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;
}
#endregion Public Constructors
}
+7 -1
View File
@@ -291,6 +291,7 @@ namespace IOB_WIN_NEXT.IobOpc
}
}
#if false
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
@@ -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<string, byte[]> memWriteCache = new Dictionary<string, byte[]>();
List<WriteValue> nodes2Write = new List<WriteValue>();
// 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
}
+17 -147
View File
@@ -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
}
/// <summary>
/// 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...
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Recupera da server set di dati specifici x IOB : qui per compilare ricette via OPC-UA
/// </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;
}
#endregion Public Constructors
}
+32 -1
View File
@@ -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();
}
}
}
}