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..5494ae87 100644
--- a/IOB-WIN-NEXT/DATA/CONF/Tend_RAMA_100_47.json
+++ b/IOB-WIN-NEXT/DATA/CONF/Tend_RAMA_100_47.json
@@ -813,238 +813,238 @@
"OPC_SP_FabSpeed_ChainSpeed": {
"name": "OPC_SP_FabSpeed_ChainSpeed",
"description": "Chain Speed",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fabric Transport\"#GEN.Float.0",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fabric Transport\"#Chain Speed - m_min",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_FabSpeed_TOffOverFeed": {
"name": "OPC_SP_FabSpeed_TOffOverFeed",
"description": "TakeOff Overfeeding",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fabric Transport\"#GEN.Float.24",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fabric Transport\"#Take Off Overfeeding - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthMast": {
"name": "OPC_SP_Spind_WidthMast",
"description": "Width Master",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.0",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Width Master - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV1": {
"name": "OPC_SP_Spind_WidthV1",
"description": "Diff V1",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.4",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V1 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV2": {
"name": "OPC_SP_Spind_WidthV2",
"description": "Diff V2",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.8",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V2 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV3": {
"name": "OPC_SP_Spind_WidthV3",
"description": "Diff V3",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.12",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V3 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV4": {
"name": "OPC_SP_Spind_WidthV4",
"description": "Diff V4",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.16",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V4 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV5": {
"name": "OPC_SP_Spind_WidthV5",
"description": "Diff V5",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.20",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V5 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV6": {
"name": "OPC_SP_Spind_WidthV6",
"description": "Diff V6",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.24",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V6 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_Spind_WidthV7": {
"name": "OPC_SP_Spind_WidthV7",
"description": "Diff V7",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#GEN.Float.28",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Width\"#Differentiation V7 - cm",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedCoolCell1": {
"name": "OPC_SP_OvenSpeedCoolCell1",
"description": "Cooling Fan",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.0",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Cooling Fan 1 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedExFan1": {
"name": "OPC_SP_OvenSpeedExFan1",
"description": "Exhaust Fan 1",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.8",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Exaust Fan 1 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedExFan2": {
"name": "OPC_SP_OvenSpeedExFan2",
"description": "Exhaust Fan 2",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.12",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Exaust Fan 2 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan101": {
"name": "OPC_SP_OvenSpeedFan101",
"description": "Fan V1",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.20",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V1 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan101A": {
"name": "OPC_SP_OvenSpeedFan101A",
"description": "Fan V1A",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.24",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V1A - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan103": {
"name": "OPC_SP_OvenSpeedFan103",
"description": "Fan V2",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.28",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V2 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan103A": {
"name": "OPC_SP_OvenSpeedFan103A",
"description": "Fan V2A",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.32",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V2A - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan105": {
"name": "OPC_SP_OvenSpeedFan105",
"description": "Fan V3",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.36",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V3 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan105A": {
"name": "OPC_SP_OvenSpeedFan105A",
"description": "Fan V3A",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.40",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V3A - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan107": {
"name": "OPC_SP_OvenSpeedFan107",
"description": "Fan V4",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.44",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V4 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan107A": {
"name": "OPC_SP_OvenSpeedFan107A",
"description": "Fan V4A",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.48",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V4A - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan109": {
"name": "OPC_SP_OvenSpeedFan109",
"description": "Fan V5",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.52",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V5 - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"OPC_SP_OvenSpeedFan109A": {
"name": "OPC_SP_OvenSpeedFan109A",
"description": "Fan V5A",
- "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#GEN.Float.56",
+ "memAddr": "ns=3;s=\"Recipe_Table_DB_RW\".\"Fans\"#Fan V5A - %",
"tipoMem": "FloatABCD",
"factor": 1
},
"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\"#Burner 1 Temperature - °C",
"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\"#Burner 2 Temperature - °C",
"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\"#Burner 3 Temperature - °C",
"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\"#Burner 4 Temperature - °C",
"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\"#Burner 5 Temperature - °C",
"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\"#Burner 6 Temperature - °C",
"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\"#Burner 7 Temperature - °C",
"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\"#Burner 8 Temperature - °C",
"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\"#Burner 9 Temperature - °C",
"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\"#Thermoset Time",
"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\"#Thermoset Temperature",
"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..6a839703 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
@@ -1871,7 +1898,7 @@ namespace IOB_WIN_NEXT.IobOpc
{
try
{
- currVal = UA_ref.ReadNode(item.StartNodeId);
+ currVal = UA_ref.ReadNodeString(item.StartNodeId);
changed = checkAndSend(item, currVal, true);
if (changed)
{
@@ -1895,7 +1922,7 @@ namespace IOB_WIN_NEXT.IobOpc
// controllo read error ed eventuale disconnect
if (readErrorCurr > readErrorMax)
{
- lgError($"Effettuo disconnessione x superamento errori lettura UA_ref.ReadNode, sleetp 15 sec");
+ lgError($"Effettuo disconnessione x superamento errori lettura UA_ref.ReadNodeString, sleetp 15 sec");
tryDisconnect();
Thread.Sleep(readErrorSleepTime);
readErrorCurr = 0;
@@ -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
///
@@ -2286,7 +2430,7 @@ namespace IOB_WIN_NEXT.IobOpc
{
try
{
- currVal = UA_ref.ReadNode(item.StartNodeId);
+ currVal = UA_ref.ReadNodeString(item.StartNodeId);
changed = checkAndSend(item, currVal, true);
if (readErrorCurr > 0)
{
@@ -2295,7 +2439,7 @@ namespace IOB_WIN_NEXT.IobOpc
}
catch (Exception exc)
{
- lgError($"Eccezione durante UA_ref.ReadNode per {item.StartNodeId}{Environment.NewLine}{exc}");
+ lgError($"Eccezione durante UA_ref.ReadNodeString per {item.StartNodeId}{Environment.NewLine}{exc}");
readErrorCurr++;
}
}
@@ -2323,7 +2467,7 @@ namespace IOB_WIN_NEXT.IobOpc
// controllo read error ed eventuale disconnect
if (readErrorCurr > readErrorMax)
{
- lgError($"Effettuo disconnessione x superamento errori lettura UA_ref.ReadNode, sleep {readErrorSleepTime}msec");
+ lgError($"Effettuo disconnessione x superamento errori lettura UA_ref.ReadNodeString, sleep {readErrorSleepTime}msec");
tryDisconnect();
Thread.Sleep(readErrorSleepTime);
readErrorCurr = 0;
@@ -2418,12 +2562,196 @@ namespace IOB_WIN_NEXT.IobOpc
///
private void UA_ref_eh_nullExceed(object sender, int e)
{
- lgError($"Effettuo disconnessione x superamento letture UA_ref.ReadNode a NULL ({e}), sleep {2 * readErrorSleepTime}msec");
+ lgError($"Effettuo disconnessione x superamento letture UA_ref.ReadNodeString a NULL ({e}), sleep {2 * readErrorSleepTime}msec");
tryDisconnect();
Thread.Sleep(2 * readErrorSleepTime);
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)
+ {
+ // valori da scrivere in memoria
+ Dictionary nodes2Write = new Dictionary();
+ Dictionary> nodesUdt2Write = new Dictionary>();
+ Dictionary currUdsDict = new Dictionary();
+ // controllo i parametri... ne gestisco 4...
+ foreach (var item in updatedPar)
+ {
+ if (connectionOk)
+ {
+ Opc.Ua.DataValue currNodeVal = new DataValue();
+ 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 isUds = memAddrWrite.Contains(opcUaParams.kvDelim);
+
+ if (isUds)
+ {
+ // ciclo e calcolo valore
+ int iPos = memAddrWrite.IndexOf(opcUaParams.kvDelim);
+ string nodeId = memAddrWrite.Substring(0, iPos);
+ string varName = memAddrWrite.Substring(iPos + 1);
+
+ //cerco se ci sia già un record Uds...
+ if (nodesUdt2Write.ContainsKey(nodeId))
+ {
+ currUdsDict = nodesUdt2Write[nodeId];
+ // controllo valore...
+ if (currUdsDict.ContainsKey(varName))
+ {
+ currUdsDict[varName] = item.reqValue;
+ }
+ else
+ {
+ currUdsDict.Add(varName, item.reqValue);
+ }
+ nodesUdt2Write[nodeId] = currUdsDict;
+ }
+ else
+ {
+ currUdsDict = new Dictionary();
+ currUdsDict.Add(varName, item.reqValue);
+ nodesUdt2Write.Add(nodeId, currUdsDict);
+ }
+ }
+ 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 + nodesUdt2Write.Count) > 0)
+ {
+ List listWriteReq = nodes2Write.Select(x => x.Value).ToList();
+ // valori "diretti"
+ if (listWriteReq.Count > 0)
+ {
+ UA_ref.WriteNodes(listWriteReq);
+ }
+ // se valori Udt con modalità alternativa
+ if (nodesUdt2Write.Count > 0)
+ {
+ foreach (var item in nodesUdt2Write)
+ {
+ UA_ref.WriteNodesUdt(item.Key, item.Value);
+ }
+ }
+
+ }
+ }
+ }
+
+ }
+
#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..2f9cc00d 100644
--- a/IOB-WIN-NEXT/UAClient.cs
+++ b/IOB-WIN-NEXT/UAClient.cs
@@ -4,6 +4,8 @@ using Opc.Ua.Client;
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
namespace IOB_WIN_NEXT
@@ -334,7 +336,7 @@ namespace IOB_WIN_NEXT
Session session = await Session.Create(
m_configuration,
endpoint,
- false,
+ true,
false,
m_configuration.ApplicationName,
60 * 60 * 1000,
@@ -342,6 +344,19 @@ 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)
{
@@ -393,84 +408,82 @@ namespace IOB_WIN_NEXT
}
///
- /// Read a SINGLE of nodes value from Server
+ /// Reads a node by node Id
///
- ///
- ///
- public string ReadNode(NodeId reqNodeId)
+ /// The node Id as string
+ /// The read node
+ /// Throws and forwards any exception with short error description.
+ public Node ReadNode(String nodeIdString)
{
- string answ = "";
- if (m_session == null || m_session.Connected == false)
+ //Create a nodeId using the identifier string
+ NodeId nodeId = new NodeId(nodeIdString);
+ //Create a node
+ Node node = new Node();
+ //Read the dataValue
+ node = m_session.ReadNode(nodeId);
+ return node;
+ }
+
+ /// Reads a node by node Id
+ /// The node Id as string
+ /// A dictionary containing the attribute values of the node using the attribute uint identifier as key;
+ /// Throws and forwards any exception with short error description.
+ public Dictionary ReadNodeAttributes(String nodeIdString)
+ {
+ //Create a nodeId using the identifier string
+ NodeId nodeId = new NodeId(nodeIdString);
+ return ReadNodeAttributes(nodeId);
+ }
+
+ /// Reads a node by node Id
+ /// The node Id
+ /// A dictionary containing the attribute values of the node using the attribute uint identifier as key;
+ /// Throws and forwards any exception with short error description.
+ public Dictionary ReadNodeAttributes(NodeId nodeId)
+ {
+ Dictionary attributeDictionary = new Dictionary()
{
- lgError("Session not connected!");
- return answ;
- }
+ {1, Attributes.NodeId },
+ {2, Attributes.NodeClass },
+ {3, Attributes.BrowseName },
+ {4, Attributes.DisplayName },
+ {5, Attributes.Value },
+ {6, Attributes.DataType},
+ {7, Attributes.ValueRank},
+ {8, Attributes.ArrayDimensions},
+ {9, Attributes.AccessLevel }
+ };
- try
+ //Create a read value id collection to store the nodes to read
+ ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
+
+ //Create a StatusCodeCollection
+ DataValueCollection dataValueCollection = new DataValueCollection();
+
+ //Create a DiagnosticInfoCollection
+ DiagnosticInfoCollection diag = new DiagnosticInfoCollection();
+
+ //Go through the attribute dictionary and create the nodes accordingly
+ for (int i = 0; i < attributeDictionary.Count; i++)
{
- //// build a list of nodes to be read
- //ReadValueIdCollection nodesToRead = new ReadValueIdCollection()
- //{
- // // Value of ServerStatus
- // new ReadValueId() { NodeId = Variables.Server_ServerStatus, AttributeId = Attributes.Value },
- // // BrowseName of ServerStatus_StartTime
- // new ReadValueId() { NodeId = Variables.Server_ServerStatus_StartTime, AttributeId = Attributes.BrowseName },
- // // Value of ServerStatus_StartTime
- // new ReadValueId() { NodeId = Variables.Server_ServerStatus_StartTime, AttributeId = Attributes.Value }
- //};
-
- //// Read the node attributes
- //lgInfo("Reading nodes...");
-
- //// Call Read Service
- //m_session.Read(
- // null,
- // 0,
- // TimestampsToReturn.Both,
- // nodesToRead,
- // out DataValueCollection resultsValues,
- // out DiagnosticInfoCollection diagnosticInfos);
-
- //// Validate the results
- //m_validateResponse(resultsValues, nodesToRead);
-
- //// Display the results.
- //foreach (DataValue result in resultsValues)
- //{
- // lgInfo("Read Value = {0} , StatusCode = {1}", result.Value, result.StatusCode);
- //}
-
- try
- {
- DataValue resp = m_session.ReadValue(reqNodeId);
- answ = $"{resp.Value}";
- // decremento errore...
- if (numErrors > 0)
- {
- numErrors--;
- }
- }
- catch (Exception exc)
- {
- // Log Error
- numErrors++;
- lgTrace($"ReadValue Error | reqNodeId: {reqNodeId} | numErrors: {numErrors}{Environment.NewLine}{exc}");
- }
+ ///Create a read value id with the nessessary attribute
+ ReadValueId nodeToRead = new ReadValueId();
+ nodeToRead.NodeId = nodeId;
+ nodeToRead.AttributeId = attributeDictionary[i + 1];
+ nodesToRead.Add(nodeToRead);
}
- catch (Exception ex)
+ //Read the read value id collection
+ m_session.Read(null, 0, TimestampsToReturn.Neither, nodesToRead, out dataValueCollection, out diag);
+ //Create a dictionary to store the attribute values
+ Dictionary attributeValues = new Dictionary();
+ //Go through the data value collection to store every data value according to its attribute identifier
+ int counter = 1;
+ foreach (DataValue dataValue in dataValueCollection)
{
- // Log Error
- lgError($"Read Nodes Error : {ex.Message}.");
- numErrors++;
+ attributeValues.Add(attributeDictionary[counter], dataValue);
+ counter += 1;
}
- // se supero maxErrors --> invio eccezione e resetto...
- if (numErrors >= maxErrors)
- {
- // reset + exception
- numErrors = 0;
- throw new Exception($"Errore lettura OPC-UA: {maxErrors} errori");
- }
- return answ;
+ return attributeValues;
}
///
@@ -484,6 +497,7 @@ namespace IOB_WIN_NEXT
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
+ numErrors++;
return answ;
}
@@ -500,6 +514,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
@@ -578,6 +600,101 @@ namespace IOB_WIN_NEXT
}
}
+ ///
+ /// Read a SINGLE of nodes value from Server
+ ///
+ ///
+ /// Value as String
+ public string ReadNodeString(NodeId reqNodeId)
+ {
+ string answ = "";
+ if (m_session == null || m_session.Connected == false)
+ {
+ lgError("Session not connected!");
+ return answ;
+ }
+
+ try
+ {
+ try
+ {
+ DataValue resp = m_session.ReadValue(reqNodeId);
+ answ = $"{resp.Value}";
+ // decremento errore...
+ if (numErrors > 0)
+ {
+ numErrors--;
+ }
+ }
+ catch (Exception exc)
+ {
+ // Log Error
+ numErrors++;
+ lgTrace($"ReadValue Error | reqNodeId: {reqNodeId} | numErrors: {numErrors}{Environment.NewLine}{exc}");
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log Error
+ lgError($"Read Nodes Error : {ex.Message}.");
+ numErrors++;
+ }
+ // se supero maxErrors --> invio eccezione e resetto...
+ if (numErrors >= maxErrors)
+ {
+ // reset + exception
+ numErrors = 0;
+ throw new Exception($"Errore lettura OPC-UA: {maxErrors} errori");
+ }
+ return answ;
+ }
+
+ /// Reads a struct or UDT by node Id
+ /// The node Id as strings
+ /// The read struct/UDT elements as a list of string[3]; string[0] = tag name, string[1] = value, string[2] = opc data type
+ /// Throws and forwards any exception with short error description.
+ public List ReadStructUdt(String nodeIdString)
+ {
+ //Define result list to return var name and var value
+ List resultStringList = new List();
+
+ //Read the node attributes
+ Dictionary nodeAttributes = ReadNodeAttributes(nodeIdString);
+ //Create a data value storing the value
+ DataValue data = nodeAttributes[Attributes.Value];
+
+ //Check if the entered node id is of type array and accordingly get the byte array of the data values
+ ExtensionObject dataValue = new ExtensionObject();
+ List byteArrays = new List();
+ if (data.Value is ExtensionObject[])
+ {
+ foreach (ExtensionObject temp in (ExtensionObject[])data.Value)
+ {
+ byteArrays.Add((byte[])temp.Body);
+ }
+ }
+ else if (data.Value is ExtensionObject)
+ {
+ dataValue = (ExtensionObject)data.Value;
+ byteArrays.Add((byte[])dataValue.Body);
+ }
+ else
+ {
+ throw new Exception("The entered node id is not of tpye struct/udt.");
+ }
+
+ //Get the type dictionary of the struct as a list of string[3]; string[0] = tag name, string[1] = data type, string [2] = array dimensions
+ List structureDictionary = new List();
+ DataTypeNode dataTypeNode = (DataTypeNode)ReadNode(nodeAttributes[Attributes.DataType].ToString());
+ GetTypeDictionary(dataTypeNode, m_session, structureDictionary);
+
+ //Get the deserialized byte string as a list of string[4]; string[0]=array index; string[1]=tag name; string[2]=tag value; string[3]=tag data type
+ resultStringList = ParseDataToTagsFromDictionary(structureDictionary, byteArrays);
+
+ //return result as List string[0]=array index; string[1]=tag name; string[2]=tag value; string[3]=tag data type
+ return resultStringList;
+ }
+
///
/// Create Subscription and MonitoredItems for DataChanges
///
@@ -678,6 +795,67 @@ namespace IOB_WIN_NEXT
{
// Log Error
lgError($"Write Nodes Error : {ex.Message}");
+ numErrors++;
+
+ // provare disconnect?!?
+ if (numErrors >= maxErrors)
+ {
+ numErrors = 0;
+ Disconnect();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Metodo scrittura alternativa in formato UDT dei dati...
+ ///
+ ///
+ public void WriteNodesUdt(string nodeIdString, Dictionary data2write)
+ {
+ if (!string.IsNullOrEmpty(nodeIdString) && data2write != null && data2write.Count > 0)
+ {
+ if (m_session == null || m_session.Connected == false)
+ {
+ lgError("Session not connected!");
+ return;
+ }
+
+ try
+ {
+ var listUdtRows = new List();
+
+ listUdtRows = new List();
+ // leggo elenco dati in primis
+ listUdtRows = ReadStructUdt(nodeIdString);
+ // va scartata ultima riga!!!!
+ listUdtRows.RemoveAt(listUdtRows.Count - 1);
+
+ // sostituzione dei dati: per ogni valore corrente cerco nei valori data2write
+ foreach (var item in listUdtRows)
+ {
+ // se trovo valore da sostituire
+ if (data2write.ContainsKey(item[1]))
+ {
+ item[2] = data2write[item[1]];
+ }
+ }
+
+ // scrivo
+ WriteStructUdt(nodeIdString, listUdtRows);
+ }
+ catch (Exception ex)
+ {
+ // Log Error
+ lgError($"Error WriteNodesUdt for {nodeIdString}: {Environment.NewLine}{ex.Message}");
+ numErrors++;
+
+ // provare disconnect?!?
+ if (numErrors >= maxErrors)
+ {
+ numErrors = 0;
+ Disconnect();
+ }
}
}
}
@@ -731,6 +909,132 @@ namespace IOB_WIN_NEXT
}
}
+ /// Writes data to a struct or UDT by node Id
+ /// The node Id as strings
+ /// The data to write as string[3]; string[0] = tag name, string[1] = value, string[2] = opc data type
+ /// Throws and forwards any exception with short error description.
+ public void WriteStructUdt(String nodeIdString, List dataToWrite)
+ {
+ //Create a NodeId from the NodeIdString
+ NodeId nodeId = new NodeId(nodeIdString);
+
+ //Creat a WriteValueColelction
+ WriteValueCollection valuesToWrite = new WriteValueCollection();
+
+ //Create a WriteValue
+ WriteValue writevalue = new WriteValue();
+
+ //Read the node attributes
+ Dictionary nodeAttributes = ReadNodeAttributes(nodeIdString);
+ //Create a data value storing the value
+ DataValue data = nodeAttributes[Attributes.Value];
+
+ //Check if the entered node id is of type array and accordingly get the byte array(s) of the data values
+ ExtensionObject extensionObject = new ExtensionObject();
+ List byteArrays = new List();
+ if (data.Value is ExtensionObject[])
+ {
+ foreach (ExtensionObject temp in (ExtensionObject[])data.Value)
+ {
+ byteArrays.Add((byte[])temp.Body);
+ }
+ }
+ else
+ {
+ extensionObject = (ExtensionObject)data.Value;
+ byteArrays.Add((byte[])extensionObject.Body);
+ }
+
+ //Get the type dictionary of the struct as a list of string[3]; string[0] = tag name, string[1] = data type, string [2] = array dimensions
+ List structureDictionary = new List();
+ DataTypeNode dataTypeNode = (DataTypeNode)ReadNode(nodeAttributes[Attributes.DataType].ToString());
+ GetTypeDictionary(dataTypeNode, m_session, structureDictionary);
+
+ //Delete empty list elements in the structure dictionary and in the list of the gui
+ for (int j = 0; j < structureDictionary.Count; j++)
+ {
+ if (structureDictionary[j][1] == "")//Check if the data type entry is empty as this is an indicator for a struct/udt in a struct/udt
+ {
+ structureDictionary.RemoveAt(j);
+ //dataToWrite.RemoveAt(j);
+ }
+ }
+
+ //Create a byte array
+ List bytesToWrite;
+
+ //Parse dataToWrite to the byte array
+ bytesToWrite = ParseDataToByteArray(dataToWrite, structureDictionary);
+
+ //Create a StatusCodeCollection
+ StatusCodeCollection results = new StatusCodeCollection();
+
+ //Create a DiagnosticInfoCollection
+ DiagnosticInfoCollection diag = new DiagnosticInfoCollection();
+
+ //Get the structure definition
+ StructureDefinition structureDefinition = (StructureDefinition)dataTypeNode.DataTypeDefinition.Body;
+
+ //Create an ExtensionObject from the Structure given to this function
+ ExtensionObject writeExtObj = new ExtensionObject();
+
+ //Copy the encoding node id to the extension object type id
+ writeExtObj.TypeId = structureDefinition.DefaultEncodingId;
+
+ //Copy data to extension object body
+ DataValue dataValue = null;
+
+ if (bytesToWrite.Count == 1)
+ {
+ //Copy data to extension object body
+ writeExtObj.Body = bytesToWrite[0];
+ //Turn the created ExtensionObject into a DataValue
+ dataValue = new DataValue(writeExtObj);
+ }
+ else
+ {
+ ExtensionObject[] writeExteObjArr = new ExtensionObject[bytesToWrite.Count];
+ for (int i = 0; i < bytesToWrite.Count; i++)
+ {
+ //Create an ExtensionObject from the Structure given to this function
+ writeExtObj = new ExtensionObject();
+ //Copy data to extension object body
+ writeExtObj.TypeId = structureDefinition.DefaultEncodingId;
+ writeExtObj.Body = bytesToWrite[i];
+ //Copy extension object to extension object array
+ writeExteObjArr[i] = writeExtObj;
+ }
+ //Turn the created ExtensionObject[] into a DataValue
+ dataValue = new DataValue(writeExteObjArr);
+ }
+ //Setup for the WriteValue
+ writevalue.NodeId = nodeId;
+ writevalue.Value = dataValue;
+ writevalue.AttributeId = Attributes.Value;
+ //Add the created value to the collection
+ valuesToWrite.Add(writevalue);
+
+ try
+ {
+ m_session.Write(null, valuesToWrite, out results, out diag);
+ }
+ catch (Exception e)
+ {
+ //Handle Exception here
+ throw e;
+ }
+
+ //Check result codes
+ foreach (StatusCode result in results)
+ {
+ if (result.ToString() != "Good")
+ {
+ Exception e = new Exception(result.ToString());
+ throw e;
+ }
+ }
+ }
+
///
/// Write a list of nodes to the Server
///
@@ -806,10 +1110,15 @@ namespace IOB_WIN_NEXT
#region Protected Fields
protected static bool isLogVerbose = false;
+
protected static Logger lg;
+
protected int currNullReceiv = 0;
+
protected int maxErrors = 20;
+
protected int maxNullAllowed = 100;
+
protected int numErrors = 0;
#endregion Protected Fields
@@ -861,14 +1170,494 @@ namespace IOB_WIN_NEXT
#region Private Fields
private readonly string currIob;
+
private readonly Action m_validateResponse;
+
private ApplicationConfiguration m_configuration;
+
private Session m_session;
#endregion Private Fields
#region Private Methods
+ ///
+ /// Browses for the node id of the parent data type of type base data type
+ ///
+ /// The data to write as string[4]; string[0] = index, string[1] = tag name, string[2] = value, string[3] = opc data type
+ /// The node id of the parent data type
+ /// Throws and forwards any exception with short error description.
+ private static NodeId GetParentDataType(NodeId nodeId, Session theSessionToBrowseIn)
+ {
+ ReferenceDescriptionCollection referenceDescriptionCollection;
+ byte[] continuationPoint;
+ theSessionToBrowseIn.Browse(null, null, nodeId, 1, BrowseDirection.Inverse, ReferenceTypeIds.HasSubtype, true, 0, out continuationPoint, out referenceDescriptionCollection);
+ NodeId nodeIdParentDataType = (NodeId)referenceDescriptionCollection[0].NodeId;
+
+ if (nodeIdParentDataType.NamespaceIndex != 0)
+ {
+ nodeIdParentDataType = GetParentDataType(nodeIdParentDataType, theSessionToBrowseIn);
+ }
+
+ return nodeIdParentDataType;
+ }
+
+ ///
+ /// Browses for the desired type dictonary to parse for containing data types
+ ///
+ /// The data type node
+ /// The current session to browse in
+ /// The list of string arrays containing the structure information tag name, data type id and array dimension
+ /// The dictionary as List of string arrays
+ /// Throws and forwards any exception with short error description.
+ private static List GetTypeDictionary(DataTypeNode dataTypeNode, Session theSessionToBrowseIn, List structureDictionary)
+ {
+ //Create a temp list of string containing the structure dictionary
+ List tempStructureDictionary = new List();
+ tempStructureDictionary = structureDictionary;
+ Session tempSession = theSessionToBrowseIn;
+ //Get Structure Definition
+ StructureFieldCollection structureFieldCollection = null;
+ try
+ {
+ StructureDefinition structureDefinition = (StructureDefinition)dataTypeNode.DataTypeDefinition.Body;
+ structureFieldCollection = (StructureFieldCollection)structureDefinition.Fields;
+ }
+ catch
+ {
+ structureDictionary[structureDictionary.Count - 1][1] = UAClient.GetParentDataType(dataTypeNode.NodeId, tempSession).ToString();
+ return structureDictionary;
+ }
+
+ //Check every structure field in the collection
+ foreach (StructureField structureField in structureFieldCollection)
+ {
+ if (structureField.DataType.NamespaceIndex != 0) //Check for structures in the structure
+ {
+ //Create a reference description collection
+ ReferenceDescriptionCollection referenceDescriptionCollection;
+ //Create a continuationPoint
+ byte[] continuationPoint;
+ //Browse for the references of the data type of the structure field
+ theSessionToBrowseIn.Browse(
+ null,
+ null,
+ structureField.DataType,
+ 0u,
+ BrowseDirection.Both,
+ ReferenceTypeIds.References,
+ true,
+ 0,
+ out continuationPoint,
+ out referenceDescriptionCollection);
+ if (((NodeId)referenceDescriptionCollection[0].NodeId).NamespaceIndex == 0)//Data type is sub type of a base data type, i.e. BYTE is a sub tpye of Byte
+ {
+ //Check if the structure field is an array and parse the dimension to string
+ string arrayDimensions = "";
+ if (structureField.ValueRank == ValueRanks.OneDimension)
+ {
+ arrayDimensions = structureField.ArrayDimensions[0].ToString();
+ }
+ //Add the structure field name, its data type node id and array dimension to the dictionary list
+ tempStructureDictionary.Add(new string[] { structureField.Name, ((NodeId)referenceDescriptionCollection[0].NodeId).ToString(), arrayDimensions });
+ }
+ else//The structure/udt has at least one additional structure/udt inside
+ {
+ //Read the data type node id of the structure/udt in the parent structure/udt
+ DataTypeNode tempDataTypeNode = (DataTypeNode)theSessionToBrowseIn.ReadNode(structureField.DataType.ToString());
+ //Enter an empty Line to seperate the inside structure from the parent
+ tempStructureDictionary.Add(new string[] { structureField.Name + " - " + "struct/udt of type: " + tempDataTypeNode.DisplayName.ToString(), "", "" });
+ //Copy the temp dictionary to the real dictionary
+ structureDictionary = tempStructureDictionary;
+ //Recursively get the structure of the structure/udt in the parent structure/udt
+ GetTypeDictionary(tempDataTypeNode, tempSession, structureDictionary);
+ }
+ }
+ else //The structure field is a base data type
+ {
+ //Check if the structure field is an array and parse the dimension to string
+ string arrayDimensions = "";
+ if (structureField.ValueRank == ValueRanks.OneDimension)
+ {
+ arrayDimensions = structureField.ArrayDimensions[0].ToString();
+ }
+ //Add the structure field name, its data type node id and array dimension to the dictionary list
+ tempStructureDictionary.Add(new string[] { structureField.Name, structureField.DataType.ToString(), arrayDimensions });
+ }
+ //Copy the temp dictionary to the real dictionary
+ structureDictionary = tempStructureDictionary;
+ }
+ //Add a row for marking the end of the struct/udt
+
+ structureDictionary.Add(new string[] { "/" + dataTypeNode.DisplayName.ToString(), "", "" });
+ return structureDictionary;
+ }
+
+ ///
+ /// Parses data to write to a list of byte arrays
+ ///
+ /// The data to write as string[4]; string[0] = index, string[1] = tag name, string[2] = value, string[3] = opc data type
+ /// The structure of the udt/struct
+ /// The list of the parsed byte arrays
+ /// Throws and forwards any exception with short error description.
+ private static List ParseDataToByteArray(List dataToWrite, List structureDictionary)
+ {
+ //Define result byte list to return a byte array
+ List resultByteList = new List();
+
+ //Dictionary to get the right byte length depending on the data type
+ Dictionary byteLength = new Dictionary()
+ {
+ {BuiltInType.Boolean, 1},
+ {BuiltInType.Int16, 2},
+ {BuiltInType.Int32, 4},
+ {BuiltInType.Int64, 8},
+ {BuiltInType.UInt16, 2},
+ {BuiltInType.UInt32, 4},
+ {BuiltInType.UInt64, 8},
+ {BuiltInType.Float, 4},
+ {BuiltInType.Double, 8},
+ {BuiltInType.Byte, 1},
+ {BuiltInType.String, 4}
+ };
+
+ //Counter for the structure dictionary browsing
+ int tempCounter = 0;
+
+ //Create byte array to store the data of one array
+ byte[] byteArray = new byte[0];
+
+ //Start decoding for opc data types
+ foreach (string[] element in dataToWrite)
+ {
+ //Reset the counter and the byteArray each time the counter reaches the size of the target Structure/UDT and the data to write include the target Structure/UDT several times
+ if (tempCounter == structureDictionary.Count)
+ {
+ //Add the byte array to the result byte array list
+ resultByteList.Add(byteArray);
+
+ //Reset the counter and the array
+ byteArray = new byte[0];
+ tempCounter = 0;
+ }
+
+ //Create byte array to store the bytes of one value depending on its data type, hence it is reseted to 0 for each element
+ byte[] dataByteArray = new byte[0];
+
+ //Get the data type id and the target type by using the structure dictionary
+ NodeId datatypeId = new NodeId(structureDictionary[tempCounter][1]);
+ BuiltInType targetType = TypeInfo.GetBuiltInType(datatypeId);
+
+ //Get the system type for getting the rigth parse method
+ Type systemType = TypeInfo.GetSystemType(datatypeId, null);
+
+ //Parse array dimension string of the structure dictionary in int32
+ Int32 arraySize = 0;
+ if (structureDictionary[tempCounter][2] != "")
+ {
+ arraySize = Int32.Parse(structureDictionary[tempCounter][2]);
+ }
+
+ //Serialize the byte string depending on the target data type and the array size - special if queries are needed for byte and string
+ if (targetType == BuiltInType.String && !(arraySize > 0))
+ {
+ //Resize the data byte array to its correct size depending on the byte length of target type by using the dictionary
+ dataByteArray = new byte[byteLength[targetType]];
+ //Get the bytes of the string length and copy them to the beginning of the data byte array
+ Array.Copy(BitConverter.GetBytes(element[2].Length), 0, dataByteArray, 0, byteLength[targetType]);
+ //Convert the string to byte and concat them to the data byte array by converting every char of the string
+ foreach (Char c in element[2])
+ {
+ byte[] tempArray = new byte[1];
+ tempArray[0] = Convert.ToByte(c);
+ dataByteArray = dataByteArray.Concat(tempArray).ToArray();
+ }
+ }
+ else if (targetType == BuiltInType.String && arraySize > 0)
+ {
+ //Create a temp string array with the array size
+ string[] tempStringArr = new string[(arraySize - 1)];
+ //Resize the data byte array to its correct size depending on the byte length of target type by using the dictionary
+ dataByteArray = new byte[byteLength[BuiltInType.UInt32]];
+ //Get the bytes of the string length and copy them to the beginning of the data byte array
+ Array.Copy(BitConverter.GetBytes(arraySize), 0, dataByteArray, 0, byteLength[targetType]);
+
+ //Get the single array elements by splitting the string
+ tempStringArr = element[2].Split(';');
+ //Go through every string element in the temp string array
+ for (int ii = 0; ii < arraySize; ii++)
+ {
+ //Copy the string length as byte to the data byte array
+ byte[] tempArray = new byte[byteLength[targetType]];
+ Array.Copy(BitConverter.GetBytes(tempStringArr[ii].Length), 0, tempArray, 0, byteLength[targetType]);
+ dataByteArray = dataByteArray.Concat(tempArray).ToArray();
+ //Convert the string to byte and concat them to the data byte array by converting every char of the string
+ foreach (Char c in tempStringArr[ii])
+ {
+ byte[] tempArraySingle = new byte[1];
+ tempArraySingle[0] = Convert.ToByte(c);
+ dataByteArray = dataByteArray.Concat(tempArraySingle).ToArray();
+ }
+ }
+ }
+ else if (targetType == BuiltInType.Byte && !(arraySize > 0))
+ {
+ //Convert the string of byte to byte
+ dataByteArray = new byte[byteLength[targetType]];
+ dataByteArray[0] = Convert.ToByte(element[2]);
+ }
+ else if (targetType == BuiltInType.Byte && arraySize > 0)
+ {
+ //Create a temp string variable to store the array elements which are divided by ;
+ String tempString = "";
+ //Resize the data byte array to its correct size depending on the byte length of target type by using the dictionary
+ dataByteArray = new byte[byteLength[BuiltInType.UInt32]];
+ //Get the bytes of the string length and copy them to the beginning of the data byte array
+ Array.Copy(BitConverter.GetBytes(arraySize), 0, dataByteArray, 0, byteLength[targetType]);
+
+ //Check every char in the string value of the element
+ foreach (Char c in element[2])
+ {
+ if (c != ';') //Concat every char to a string value in elements except the ;
+ {
+ tempString = String.Concat(tempString, c);
+ }
+ else
+ {
+ //Create a temp byte array with its correct size depending on the byte length of the target data type
+ byte[] tempArray = new byte[byteLength[targetType]];
+ tempArray[0] = Convert.ToByte(tempString);
+ //Copy the temp array to the data byte array
+ dataByteArray = dataByteArray.Concat(tempArray).ToArray();
+ //Reset the temp string
+ tempString = "";
+ }
+ }
+ }
+ else if (!(arraySize > 0))
+ {
+ //Create the byte array with its correct size depending on the byte length of the target data type
+ dataByteArray = new byte[byteLength[targetType]];
+ //Get the converter method depending on the system type
+ System.Reflection.MethodInfo methodInfo = typeof(Convert).GetMethod("To" + systemType.Name, new Type[] { typeof(string) });
+ //Call the converter method
+ dynamic convertDataType = methodInfo.Invoke(null, new Object[] { element[2] });
+ Array.Copy(BitConverter.GetBytes(convertDataType), 0, dataByteArray, 0, byteLength[targetType]);
+ }
+ else if (arraySize > 0)
+ {
+ //Create a temp string variable to store the array elements which are divided by ;
+ String tempString = "";
+ //Create the byte array with the size of a UInt32, which is the data type for the array dimension
+ dataByteArray = new byte[byteLength[BuiltInType.UInt32]];
+ //Get the bytes of the array dimension and copy them to the data byte array as they always are at the beginning of a byte array of an array
+ Array.Copy(BitConverter.GetBytes(arraySize), 0, dataByteArray, 0, byteLength[targetType]);
+
+ //Check every char in the string value of the element
+ foreach (Char c in element[2])
+ {
+ if (c != ';') //Concat every char to a string value in elements except the ;
+ {
+ tempString = String.Concat(tempString, c);
+ }
+ else
+ {
+ //Create a temp byte array with its correct size depending on the byte length of the target data type
+ byte[] tempArray = new byte[byteLength[targetType]];
+ //Get the converter method depending on the system type
+ System.Reflection.MethodInfo methodInfo = typeof(Convert).GetMethod("To" + systemType.Name, new Type[] { typeof(string) });
+ //Call the converter method
+ dynamic convertDataType = methodInfo.Invoke(null, new Object[] { element[2] });
+ Array.Copy(BitConverter.GetBytes(convertDataType), 0, tempArray, 0, byteLength[targetType]);
+ //Concat the temp array with the data array
+ dataByteArray = dataByteArray.Concat(tempArray).ToArray();
+ //Reset the temp string
+ tempString = "";
+ }
+ }
+ }
+ else
+ {
+ Exception e = new Exception("Data type is too complex to be parsed." + System.Environment.NewLine);
+ throw e;
+ }
+
+ //Add the data byte array to the byte array containing all data and count the counter
+ byteArray = byteArray.Concat(dataByteArray).ToArray();
+ tempCounter++;
+ }
+ resultByteList.Add(byteArray);
+ return resultByteList;
+ }
+
+ ///
+ /// Parses a byte array to objects containing tag names and tag data types
+ ///
+ /// List of object containing tag names and tag data types
+ /// A byte array to parse
+ /// A list of string[4]; string[0] = tag name, string[1] = tag name, string[2] = value, string[3] = opc data type
+ /// Throws and forwards any exception with short error description.
+ private static List ParseDataToTagsFromDictionary(List varList, List byteArrays)
+ {
+ //Define result list to return var name, var value and var data type
+ List resultStringList = new List();
+
+ //Dictionary for index counting
+ Dictionary byteLength = new Dictionary()
+ {
+ {BuiltInType.Boolean, 1},
+ {BuiltInType.Int16, 2},
+ {BuiltInType.Int32, 4},
+ {BuiltInType.Int64, 8},
+ {BuiltInType.UInt16, 2},
+ {BuiltInType.UInt32, 4},
+ {BuiltInType.UInt64, 8},
+ {BuiltInType.Float, 4},
+ {BuiltInType.Double, 8},
+ {BuiltInType.Byte, 1},
+ {BuiltInType.String, 4}
+ };
+
+ //Array index
+ int arrayIndex = 0;
+ foreach (byte[] byteResult in byteArrays)
+ {
+ //Byte decoding index
+ int index = 0;
+
+ //Start decoding for opc data types
+ foreach (string[] val in varList)
+ {
+ string[] dataReferenceStringArray = new string[4];
+
+ //Check if the type dictionary includes a struct/udt in a struct/udt and enter an empty row
+ if (val[1] == "")
+ {
+ dataReferenceStringArray[0] = "[" + arrayIndex.ToString() + "]";
+ dataReferenceStringArray[1] = val[0];
+ dataReferenceStringArray[2] = "";
+ dataReferenceStringArray[3] = "";
+ //Add the data Reference string array to the result string list
+ resultStringList.Add(dataReferenceStringArray);
+ continue;
+ }
+ //Add array index
+ dataReferenceStringArray[0] = "[" + arrayIndex.ToString() + "]";
+ //Copy tag name
+ dataReferenceStringArray[1] = val[0];
+ //Get the target data type via the data type id
+ NodeId datatypeId = new NodeId(val[1]);
+ BuiltInType targetType = TypeInfo.GetBuiltInType(datatypeId);
+ //Get the system type
+ Type systemType = TypeInfo.GetSystemType(datatypeId, null);
+ //Parse array dimensions string in int32
+ Int32 arrayDimensions = 0;
+ if (val[2] != "")
+ {
+ arrayDimensions = Int32.Parse(val[2]);
+ }
+
+ //Deserialize the byte array depending on the target data type and the array dimension
+ if (targetType == BuiltInType.String && !(arrayDimensions > 0))
+ {
+ //Get the string length
+ Int32 stringlength = BitConverter.ToInt32(byteResult, index);
+ index += byteLength[targetType];
+ if (stringlength > 0) //Decode the bytes to its string value
+ {
+ dataReferenceStringArray[2] = Encoding.UTF8.GetString(byteResult, index, stringlength);
+ index += stringlength;
+ }
+ else
+ {
+ dataReferenceStringArray[2] = "";
+ }
+ }
+ else if (targetType == BuiltInType.String && arrayDimensions > 0)
+ {
+ //Skip array information (UInt32) regarding its size
+ index += byteLength[BuiltInType.UInt32];
+ //Check every element of the array
+ for (int i = 0; i < arrayDimensions; i++)
+ {
+ //Get the string length
+ Int32 stringlength = BitConverter.ToInt32(byteResult, index);
+ index += byteLength[targetType];
+ if (stringlength > 0) //Decode the bytes to its string value
+ {
+ dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[2], Encoding.UTF8.GetString(byteResult, index, stringlength));
+ //Add a ; to the string value to seperate the value from the other array values
+ dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[2], ";");
+ index += stringlength;
+ }
+ else
+ {
+ dataReferenceStringArray[2] = dataReferenceStringArray[2] + ";";
+ }
+ }
+ }
+ else if (targetType == BuiltInType.Byte && !(arrayDimensions > 0))
+ {
+ //Copy the byte value as string to the data reference array
+ dataReferenceStringArray[2] = byteResult[index].ToString();
+ index += byteLength[targetType];
+ }
+ else if (targetType == BuiltInType.Byte && arrayDimensions > 0)
+ {
+ //Skip array information (UInt32) regarding its size
+ index += byteLength[BuiltInType.UInt32];
+ Int32[] tempArray = new Int32[arrayDimensions];
+ //Check every element of the array
+ for (int i = 0; i < arrayDimensions; i++)
+ {
+ tempArray[i] = byteResult[index];
+ index += byteLength[targetType];
+ }
+ //Add the values as one value seperated by ; to the data reference array
+ dataReferenceStringArray[2] = String.Join(";", tempArray);
+ dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[2], ";");
+ }
+ else if (!(arrayDimensions > 0))
+ {
+ //Get the converter method depending on the system type
+ System.Reflection.MethodInfo methodInfo = typeof(BitConverter).GetMethod("To" + systemType.Name);
+ //Call the method
+ dataReferenceStringArray[2] = methodInfo.Invoke(null, new Object[] { byteResult, index }).ToString();
+ index += byteLength[targetType];
+ }
+ else if (arrayDimensions > 0)
+ {
+ //Skip array information (UInt32) regarding its size
+ index += byteLength[BuiltInType.UInt32];
+ //Get the converter method depending on the system type
+ System.Reflection.MethodInfo methodInfo = typeof(BitConverter).GetMethod("To" + systemType.Name);
+ dynamic[] tempArray = new dynamic[arrayDimensions];
+ //Check every element of the array
+ for (int i = 0; i < arrayDimensions; i++)
+ {
+ tempArray[i] = methodInfo.Invoke(null, new Object[] { byteResult, index }).ToString();
+ index += byteLength[targetType];
+ }
+ //Add the values as one value seperated by ; to the data reference array
+ dataReferenceStringArray[2] = String.Join(";", tempArray);
+ dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[1], ";");
+ }
+ else
+ {
+ Exception e = new Exception("Data type is too complex to be parsed." + System.Environment.NewLine);
+ throw e;
+ }
+ //Copy the target type as string to the data reference array
+ dataReferenceStringArray[3] = targetType.ToString();
+ //Add the data Reference string array to the result string list
+ resultStringList.Add(dataReferenceStringArray);
+ }
+ //Count the array index
+ arrayIndex += 1;
+ }
+ return resultStringList;
+ }
+
///
/// Handles the certificate validation event. This event is triggered every time an
/// untrusted certificate is received from the server.