Aggiunta preliminare progetto OPC-UA

This commit is contained in:
Samuele Locatelli
2025-01-09 08:54:12 +01:00
parent 4e589513f0
commit 7b47eafcdc
33 changed files with 11085 additions and 65 deletions
+90 -4
View File
@@ -1,12 +1,12 @@
using IOB_UT_NEXT;
using IOB_WIN_PING.Iob;
using IOB_WIN_OPC_UA.Iob;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IOB_WIN_PING
namespace IOB_WIN_OPC_UA
{
public class AdapterFormNext: IOB_WIN_FORM.AdapterForm
{
@@ -28,8 +28,94 @@ namespace IOB_WIN_PING
switch (tipoScelto)
{
case tipoAdapter.PingWatchdog:
iobObj = new IOB_WIN_FORM.Iob.PingWatchDog(this, IOBConf);
case tipoAdapter.OpcUa:
iobObj = new IobOpc.OpcUa(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaCMS:
case tipoAdapter.OpcUaSCM:
iobObj = new IobOpc.OpcUaCMS(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaEwon:
iobObj = new IobOpc.OpcUaEwon(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaEwonAdige:
iobObj = new IobOpc.OpcUaEwonAdige(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaEwonBLM:
iobObj = new IobOpc.OpcUaEwonBLM(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaEwonMonti:
iobObj = new IobOpc.OpcUaEwonMonti(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaEwonMecolpress:
iobObj = new IobOpc.OpcUaEwonMecolpress(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaKwp:
iobObj = new IobOpc.OpcUaKpw(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaKwpRama:
iobObj = new IobOpc.OpcUaKpwRama(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaImasAeromec:
iobObj = new IobOpc.OpcUaImas(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaMBH:
iobObj = new IobOpc.OpcUaMBH(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaMBHCimolai:
iobObj = new IobOpc.OpcUaMBHCimolai(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaOmron:
iobObj = new IobOpc.OpcUaOmron(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaOmronIcoel:
iobObj = new IobOpc.OpcUaOmronIcoel(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaSiemens:
iobObj = new IobOpc.OpcUaSiemens(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaSiemensOMP:
iobObj = new IobOpc.OpcUaSiemensOMP(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaSiemensRama:
iobObj = new IobOpc.OpcUaSiemensRama(this, IOBConf);
btnStart.Enabled = true;
break;
case tipoAdapter.OpcUaUlma:
iobObj = new IobOpc.OpcUaUlma(this, IOBConf);
btnStart.Enabled = true;
break;
+19 -3
View File
@@ -8,7 +8,7 @@
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup>
<appSettings>
<add key="appName" value="IOB-WIN-PING" />
<add key="appName" value="IOB-WIN-OPC-UA" />
<add key="enableTest" value="false" />
<add key="enableContapezzi" value="true" />
<add key="enableMode" value="true" />
@@ -131,11 +131,11 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.Pipelines" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
@@ -153,6 +153,22 @@
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Formats.Asn1" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.1" newVersion="8.0.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.1" newVersion="6.0.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.web>
+61 -9
View File
@@ -6,8 +6,8 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EEBE38BD-29EE-45DE-9AF4-8FA34B58D8BA}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>IOB_WIN_MTC</RootNamespace>
<AssemblyName>IOB-WIN-MTC</AssemblyName>
<RootNamespace>IOB_WIN_OPC_UA</RootNamespace>
<AssemblyName>IOB-WIN-OPC-UA</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
@@ -44,12 +44,18 @@
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
</Reference>
<Reference Include="MapoSDK, Version=6.14.2411.518, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MapoSDK.6.14.2411.518\lib\MapoSDK.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.HashCode, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.HashCode.6.0.0\lib\net462\Microsoft.Bcl.HashCode.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.6.0.0\lib\net461\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
@@ -59,43 +65,69 @@
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.3.4\lib\net46\NLog.dll</HintPath>
</Reference>
<Reference Include="Opc.Ua.Client, Version=1.5.374.0, Culture=neutral, PublicKeyToken=bfa7a73c5cf4b6e8, processorArchitecture=MSIL">
<HintPath>..\packages\OPCFoundation.NetStandard.Opc.Ua.Client.1.5.374.158\lib\netstandard2.0\Opc.Ua.Client.dll</HintPath>
</Reference>
<Reference Include="Opc.Ua.Configuration, Version=1.5.374.0, Culture=neutral, PublicKeyToken=bfa7a73c5cf4b6e8, processorArchitecture=MSIL">
<HintPath>..\packages\OPCFoundation.NetStandard.Opc.Ua.Configuration.1.5.374.158\lib\netstandard2.0\Opc.Ua.Configuration.dll</HintPath>
</Reference>
<Reference Include="Opc.Ua.Core, Version=1.5.374.0, Culture=neutral, PublicKeyToken=bfa7a73c5cf4b6e8, processorArchitecture=MSIL">
<HintPath>..\packages\OPCFoundation.NetStandard.Opc.Ua.Core.1.5.374.158\lib\netstandard2.0\Opc.Ua.Core.dll</HintPath>
</Reference>
<Reference Include="Opc.Ua.Security.Certificates, Version=1.5.374.0, Culture=neutral, PublicKeyToken=bfa7a73c5cf4b6e8, processorArchitecture=MSIL">
<HintPath>..\packages\OPCFoundation.NetStandard.Opc.Ua.Security.Certificates.1.5.374.158\lib\netstandard2.0\Opc.Ua.Security.Certificates.dll</HintPath>
</Reference>
<Reference Include="Pipelines.Sockets.Unofficial, Version=1.0.0.0, Culture=neutral, PublicKeyToken=42ea0a778e13fbe2, processorArchitecture=MSIL">
<HintPath>..\packages\Pipelines.Sockets.Unofficial.2.2.8\lib\net461\Pipelines.Sockets.Unofficial.dll</HintPath>
</Reference>
<Reference Include="S7.Net, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d5812d469e84c693, processorArchitecture=MSIL">
<HintPath>..\packages\S7netplus.0.1.9\lib\net45\S7.Net.dll</HintPath>
</Reference>
<Reference Include="StackExchange.Redis, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c219ff1ca8c2ce46, processorArchitecture=MSIL">
<HintPath>..\packages\StackExchange.Redis.2.8.24\lib\net461\StackExchange.Redis.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System">
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\System.dll</HintPath>
</Reference>
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.6.0.1\lib\net461\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Formats.Asn1, Version=8.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Formats.Asn1.8.0.1\lib\net462\System.Formats.Asn1.dll</HintPath>
</Reference>
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
</Reference>
<Reference Include="System.IO.Pipelines, Version=5.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Pipelines.5.0.1\lib\net461\System.IO.Pipelines.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Channels, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Channels.5.0.0\lib\net461\System.Threading.Channels.dll</HintPath>
<Reference Include="System.Threading.Channels, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Channels.6.0.0\lib\net461\System.Threading.Channels.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -113,12 +145,32 @@
<Compile Include="AdapterFormNext.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="IobOpc\OpcUa.cs" />
<Compile Include="IobOpc\OpcUaCMS.cs" />
<Compile Include="IobOpc\OpcUaEwon.cs" />
<Compile Include="IobOpc\OpcUaEwonAdige.cs" />
<Compile Include="IobOpc\OpcUaEwonBLM.cs" />
<Compile Include="IobOpc\OpcUaEwonMecolpress.cs" />
<Compile Include="IobOpc\OpcUaEwonMonti.cs" />
<Compile Include="IobOpc\OpcUaImas.cs" />
<Compile Include="IobOpc\OpcUaKpw.cs" />
<Compile Include="IobOpc\OpcUaKpwRama.cs" />
<Compile Include="IobOpc\OpcUaMBH.cs" />
<Compile Include="IobOpc\OpcUaMBHCimolai.cs" />
<Compile Include="IobOpc\OpcUaOmron.cs" />
<Compile Include="IobOpc\OpcUaOmronIcoel.cs" />
<Compile Include="IobOpc\OpcUaSiemens.cs" />
<Compile Include="IobOpc\OpcUaSiemensOMP.cs" />
<Compile Include="IobOpc\OpcUaSiemensRama.cs" />
<Compile Include="IobOpc\OpcUaUlma.cs" />
<Compile Include="Iob\GenericNext.cs" />
<Compile Include="MainFormNext.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="OpcUaDataItemExt.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UAClient.cs" />
<Content Include="Resources\STEAM-IOB-WIN.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
+1 -1
View File
@@ -1,6 +1,6 @@
using IOB_UT_NEXT;
namespace IOB_WIN_PING.Iob
namespace IOB_WIN_OPC_UA.Iob
{
public class GenericNext : IOB_WIN_FORM.Iob.Generic
{
File diff suppressed because it is too large Load Diff
+375
View File
@@ -0,0 +1,375 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaCMS : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per CMS/SCM (es. Eidos termoformatrice)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaCMS(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
// impostazioni specifiche OpcUA SCM
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if (utils.CRB("enableContapezzi"))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError($"Errore in decodifica valore contapezzi da {opcUaParams.keyPartCount} | valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down (NON GESTITO ?!?)
* B7: emergenza
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
// variabili RUN...
currRun = getDataItemValue(opcUaParams.keyRunMode);
// variabili RUN... se richiesto invio runMode
if (opcUaParams.runModeSend)
{
if (!string.IsNullOrEmpty(currRun))
{
// se ho valore --> invio
string sVal = "";
string descr = $"RunModeVal";
DateTime locTStamp = DateTime.Now;
sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {opcUaParams.keyRunMode} | Val: {currRun}";
accodaFLog(sVal, qEncodeFLog(descr, currRun));
// se richiesto provo a tradurre
if (opcUaParams.runModeTrad)
{
string RunModeDescr = itemTranslation("RunMode", currRun);
descr = $"RunMode";
accodaFLog(sVal, qEncodeFLog(descr, RunModeDescr));
}
}
}
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
//// se ho emergenza premuta --> emergenza!
//if (isWarmUpCoolDown)
//{
// B_input += (1 << 6);
//}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (adesso.Subtract(lastCurrent).TotalMinutes > 5)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
lg.Info($"plcWriteParams: {updatedPar.Count} params to write");
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, currMem.value);
serObj = JsonConvert.SerializeObject(item);
lgDebug($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}");
lgDebug($"---------------{Environment.NewLine}UPDATED PARAM:{Environment.NewLine}{serObj}{Environment.NewLine}---------------");
serObj = JsonConvert.SerializeObject(currMem);
lgDebug($"---------------{Environment.NewLine}MEMORY CONTENT:{Environment.NewLine}{serObj}{Environment.NewLine}---------------");
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;
memAddrWrite = currMem.memAddr;
break;
case plcDataType.Real:
double.TryParse(item.reqValue, out valReal);
commWriteVal.Value.Value = valReal;
break;
case plcDataType.String:
// verifico caso speciale: se è art/comm scrivo AFFIANCATE...
if (item.uid == "setArt" | item.uid == "setComm")
{
// accodo commessa + articolo con padding secondo lunghezza...
string codArt = "";
if (currProdData.ContainsKey("setArt"))
{
codArt = string.IsNullOrEmpty(currProdData["setArt"]) ? "" : currProdData["setArt"];
}
string codComm = "";
if (currProdData.ContainsKey("setComm"))
{
codComm = string.IsNullOrEmpty(currProdData["setComm"]) ? "" : currProdData["setComm"];
}
// padding...
codArt = codArt.PadRight(20, ' ');
codComm = codComm.PadRight(20, ' ');
commWriteVal.Value.Value = $"{codComm}{codArt}";
}
break;
default:
break;
}
lgInfo($"---------------{Environment.NewLine}OPC-UA data:{Environment.NewLine}NodeId: {commWriteVal.NodeId}{Environment.NewLine}Value: {commWriteVal.Value.Value}{Environment.NewLine}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(commWriteVal);
// 2023.09.19 resetto richiesta
item.reqValue = "";
}
else
{
lgError($"Errore: memAddrWrite vuoto!");
}
}
else
{
lgError($"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 (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+362
View File
@@ -0,0 +1,362 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaEwon : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per EWON (es Monti, Tenditalia)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaEwon(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if (utils.CRB("enableContapezzi"))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup - se NON c'è va negata
* B7: emergenza ARMATA (1=ok, 0 = premuta) - SE NON C'è NON VA negata
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho emergenza premuta --> emergenza!
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
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}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+142
View File
@@ -0,0 +1,142 @@
using IOB_UT_NEXT;
using MapoSDK;
using Opc.Ua;
using System.Collections.Generic;
using System.Linq;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaEwonAdige : OpcUaEwon
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per EWON (es Adige/BLM, Stel)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaEwonAdige(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init Ewon versione Adige/BLM");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Protected Methods
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
base.plcWriteParams(ref updatedPar);
// se DEVO gestire setup Mecolpress...
if (opcUaParams.SetupConf.SetupMode == IOB_UT_NEXT.MachineSetupMode.MECOLPRESS)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// faccio un check tra i valori in memoria che devono corrispondere e se NON
// corrispondono --> metto valore in scrittura...
int numDiff = 0;
foreach (var item in opcUaParams.SetupConf.checkParList)
{
// cerco la memoria corrispondente...
var memorieMes = dataItemMem.Where(x => x.Key.Contains(item.Key)).ToList();
var memorieMac = dataItemMem.Where(x => x.Key.Contains(item.Value)).ToList();
// se ho trovato qualcosa verifico se corrispondono...
if (memorieMes.Count > 0 && memorieMes.Count > 0)
{
// controllo primo record con primo record
if (memorieMes.FirstOrDefault().Value.value != memorieMac.FirstOrDefault().Value.value)
{
numDiff++;
}
}
}
// ciclo le condizioni di uscita
foreach (var item in opcUaParams.SetupConf.writeParAction)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.TargetParam);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
// se ho differenze --> scrivo richiesta attrezzaggio...
if (numDiff > 0)
{
commWriteVal.Value.Value = item.TargetValNotEqual;
}
else
{
commWriteVal.Value.Value = item.TargetValEqual;
}
// accodo x scrittura
nodes2Write.Add(commWriteVal);
lgInfo($"richiesta scrittura valore | nodeId: {commWriteVal.NodeId} | targetPar: {item.TargetParam} | val {commWriteVal.Value.Value}");
}
// x ora NON eseguo
if (opcUaParams.SetupConf.EnableAdvSetup)
{
// ora scrivo!
if (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
}
#endregion Protected Methods
/// <summary>
/// Processing degli allarmi (se presenti e gestiti)
/// </summary>
/// <returns></returns>
public override Dictionary<string, string> getAlarmData()
{
Dictionary<string, string> outVal = new Dictionary<string, string>();
// ora aggiungo (se ci fossero) gli allarmi...
if (alarmMaps != null && alarmMaps.Count > 0)
{
if (hasAlarms())
{
string bankVal = "";
foreach (var item in alarmMaps)
{
bankVal = "";
var currState = currAlarmsState(item);
// calcolo vettore stringa degli allarmi...
bankVal = getAlarmState(item, currState);
bankVal = string.IsNullOrEmpty(bankVal) ? "OK" : bankVal;
saveAlarmString(ref outVal, bankVal, item.description);
}
}
}
else
{
lgTrace("No alarm found in alarmMaps");
}
if (periodicLog || outVal.Count > 0)
{
lgDebug($"Esito getAlarmData: {outVal.Count} allarmi attivi/validi in outVal");
}
return outVal;
}
}
}
+102
View File
@@ -0,0 +1,102 @@
using IOB_UT_NEXT;
using MapoSDK;
using System.Collections.Generic;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaEwonBLM : OpcUaEwon
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per EWON (es Monti, Tenditalia)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaEwonBLM(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init Ewon versione BLM (Mecart)");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Protected Methods
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
base.plcWriteParams(ref updatedPar);
#if false
// se DEVO gestire setup Mecolpress...
if (opcUaParams.SetupConf.SetupMode == IOB_UT_NEXT.MachineSetupMode.MECOLPRESS)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// faccio un check tra i valori in memoria che devono corrispondere e se NON
// corrispondono --> metto valore in scrittura...
int numDiff = 0;
foreach (var item in opcUaParams.SetupConf.checkParList)
{
// cerco la memoria corrispondente...
var memorieMes = dataItemMem.Where(x => x.Key.Contains(item.Key)).ToList();
var memorieMac = dataItemMem.Where(x => x.Key.Contains(item.Value)).ToList();
// se ho trovato qualcosa verifico se corrispondono...
if (memorieMes.Count > 0 && memorieMes.Count > 0)
{
// controllo primo record con primo record
if (memorieMes.FirstOrDefault().Value.value != memorieMac.FirstOrDefault().Value.value)
{
numDiff++;
}
}
}
// ciclo le condizioni di uscita
foreach (var item in opcUaParams.SetupConf.writeParAction)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.TargetParam);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
// se ho differenze --> scrivo richiesta attrezzaggio...
if (numDiff > 0)
{
commWriteVal.Value.Value = item.TargetValNotEqual;
}
else
{
commWriteVal.Value.Value = item.TargetValEqual;
}
// accodo x scrittura
nodes2Write.Add(commWriteVal);
lgInfo($"richiesta scrittura valore | nodeId: {commWriteVal.NodeId} | targetPar: {item.TargetParam} | val {commWriteVal.Value.Value}");
}
// x ora NON eseguo
if (opcUaParams.SetupConf.EnableAdvSetup)
{
// ora scrivo!
if (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endif
}
#endregion Protected Methods
}
}
@@ -0,0 +1,104 @@
using IOB_UT_NEXT;
using MapoSDK;
using Opc.Ua;
using System.Collections.Generic;
using System.Linq;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaEwonMecolpress : OpcUaEwon
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per EWON (es Monti, Tenditalia)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaEwonMecolpress(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init Ewon versione Mecolpress (STEL)");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Protected Methods
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
base.plcWriteParams(ref updatedPar);
// se DEVO gestire setup Mecolpress...
if (opcUaParams.SetupConf.SetupMode == IOB_UT_NEXT.MachineSetupMode.MECOLPRESS)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// faccio un check tra i valori in memoria che devono corrispondere e se NON
// corrispondono --> metto valore in scrittura...
int numDiff = 0;
foreach (var item in opcUaParams.SetupConf.checkParList)
{
// cerco la memoria corrispondente...
var memorieMes = dataItemMem.Where(x => x.Key.Contains(item.Key)).ToList();
var memorieMac = dataItemMem.Where(x => x.Key.Contains(item.Value)).ToList();
// se ho trovato qualcosa verifico se corrispondono...
if (memorieMes.Count > 0 && memorieMes.Count > 0)
{
// controllo primo record con primo record
if (memorieMes.FirstOrDefault().Value.value != memorieMac.FirstOrDefault().Value.value)
{
numDiff++;
}
}
}
// ciclo le condizioni di uscita
foreach (var item in opcUaParams.SetupConf.writeParAction)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.TargetParam);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
// se ho differenze --> scrivo richiesta attrezzaggio...
if (numDiff > 0)
{
commWriteVal.Value.Value = item.TargetValNotEqual;
}
else
{
commWriteVal.Value.Value = item.TargetValEqual;
}
// accodo x scrittura
nodes2Write.Add(commWriteVal);
lgInfo($"richiesta scrittura valore | nodeId: {commWriteVal.NodeId} | targetPar: {item.TargetParam} | val {commWriteVal.Value.Value}");
}
// x ora NON eseguo
if (opcUaParams.SetupConf.EnableAdvSetup)
{
// ora scrivo!
if (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
}
#endregion Protected Methods
}
}
+225
View File
@@ -0,0 +1,225 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaEwonMonti : OpcUaEwon
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per EWON (es Monti, Tenditalia)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaEwonMonti(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init Ewon versione Monti (Tenditalia)");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
// impostazioni specifiche Ewon di Monti
forceResetInRun = true;
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if (utils.CRB("enableContapezzi"))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
#endregion Public Methods
#region Protected Methods
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
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;
memAddrWrite = currMem.memAddr;
break;
case plcDataType.Real:
double.TryParse(item.reqValue, out valReal);
commWriteVal.Value.Value = valReal;
break;
case plcDataType.String:
// verifico caso speciale: se è art/comm scrivo AFFIANCATE...
if (item.uid == "setArt" | item.uid == "setComm")
{
// accodo commessa + articolo con padding secondo lunghezza...
string codArt = "";
if (currProdData.ContainsKey("setArt"))
{
codArt = string.IsNullOrEmpty(currProdData["setArt"]) ? "" : currProdData["setArt"];
}
string codComm = "";
if (currProdData.ContainsKey("setComm"))
{
codComm = string.IsNullOrEmpty(currProdData["setComm"]) ? "" : currProdData["setComm"];
}
// padding...
codArt = codArt.PadRight(20, ' ');
codComm = codComm.PadRight(20, ' ');
commWriteVal.Value.Value = $"{codComm}{codArt}";
}
break;
default:
break;
}
lgInfo($"---------------{Environment.NewLine}OPC-UA data:{Environment.NewLine}NodeId: {commWriteVal.NodeId}{Environment.NewLine}Value: {commWriteVal.Value.Value}{Environment.NewLine}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+368
View File
@@ -0,0 +1,368 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaImas : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per IMAS OPC-UA (es Jetco)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaImas(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa IMAS");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if ((enablePzCountByApp || enablePzCountByIob) && !(disablePzCountByIob))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho emergenza premuta --> emergenza!
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitata gestione ODL esegue eventuale chiusura
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
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}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
lgInfo($"Richiesta scrittura {nodes2Write.Count} parametri");
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+397
View File
@@ -0,0 +1,397 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaKpw : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per adapter KepWare OPC-UA (es RAMA RED by Unitech x TFT)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaKpw(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa Kepware");
// 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);
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if ((enablePzCountByApp || enablePzCountByIob) && !(disablePzCountByIob))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho warmp up segnalo
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitata gestione ODL esegue eventuale chiusura
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
//// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
//if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
// controllo se non ho dati buoni da > lastCurrentMaxElapsed sec --> disconnetto
if (adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed)
{
lgInfo($"Timeout per mancata comunicazione da oltre {lastCurrentMaxElapsed} sec --> disconnessione adapter OpcUa!");
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
try
{
memAddrWrite = "";
int valInt = 0;
bool valBool = false;
float valFloat = 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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// controllo se il valore in memoria fosse vuoto --> popolo...
if (string.IsNullOrEmpty(currMem.value))
{
currMem.value = item.reqValue;
lgInfo($"Inizializzato currMem.value mancante | uid: {currMem.name} | value: {currMem.value}");
}
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
switch (currMem.tipoMem)
{
case plcDataType.Boolean:
bool.TryParse(item.reqValue, out valBool);
commWriteVal.Value.Value = valBool;
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:
// verifico da size se 32/64...
switch (currMem.size)
{
case 0:
case 2:
case 32:
float.TryParse(item.reqValue, out valFloat);
commWriteVal.Value.Value = valFloat;
break;
case 4:
case 64:
default:
double.TryParse(item.reqValue, out valReal);
commWriteVal.Value.Value = valReal;
break;
}
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}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
lgInfo($"Richiesta scrittura {nodes2Write.Count} parametri");
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+196
View File
@@ -0,0 +1,196 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using static IOB_UT_NEXT.CustomObj;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaKpwRama : OpcUaKpw
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per adapter KepWare OPC-UA (es RAMA RED by Unitech x TFT)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaKpwRama(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa Kepware x RAMA");
// 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);
}
sendKeyRichiesta = true;
}
/// <summary>
/// Effettua sync dati inviando RICETTA
/// </summary>
protected override void processDataSync()
{
lgInfo("--------------------------------------------");
lgInfo($"executeTasks --> syncDbData --> processDataSync");
lgInfo("--------------------------------------------");
// effettua sync recuperando dati ed effettuando scrittura su kepware
var taskGet = iobGetSendDossierKepware();
}
/// <summary>
/// Recupero dati da server x invio dati ricetta a KepWare (Ex RAMA x TFT)
/// </summary>
/// <returns></returns>
private bool iobGetSendDossierKepware()
{
bool answ;
lg.Info("iobGetSendDossier: Inizio scrittura dati a KepWare");
/*-----------------------------------------
* STEPS processo:
* - recupero ARTICOLI (solo quelli dei PODL attivi)
* - recupero PODL (tutti)
* - 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 dei memWrite x tradurre l'elenco fluxLog in nuovi valori
* - preparo il set di valori da scrivere con traduzione
* - invio scritture
* - aggiungo info articolo (+ ODL...)
* - aggiungo il valore di "scrittura effettuata"
*----------------------------------------- */
bool okArt = false;
bool okDoss = false;
bool okPodl = false;
List<AnagArticoli> listaArt = new List<AnagArticoli>();
List<PODLModel> listaPODL = new List<PODLModel>();
List<DossiersModel> listaDoss = new List<DossiersModel>();
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
var rawListArt = callUrl(urlGetCurrArt, false);
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
// fixme todo verificare se usare urlGetActPODL
var rawListPODL = callUrl(urlGetNextPODL, false);
if (!string.IsNullOrEmpty(rawListArt))
{
try
{
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
okArt = listaArt.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListDOSS))
{
try
{
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
okDoss = listaDoss.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
okPodl = listaPODL.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetNextPODL}) ha restituito valore vuoto");
}
answ = okArt && okPodl && okDoss;
if (answ)
{
// verifico i PODL attivi...
var listPodlAct = listaPODL.Where(x => x.Attivabile).OrderBy(x => x.InsertDate).ToList();
if (listPodlAct.Count > 0)
{
string codArt = listPodlAct.FirstOrDefault().CodArticolo;
// verifico di avere il dossier e l'articolo x questo PODL
var selArt = listaArt.Where(x => x.CodArticolo == codArt).FirstOrDefault();
var selDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
// prendo il primo e recupero...
if (selArt != null && selDoss != null)
{
// recupero il resultset dei valori FluxLog da caricare
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(selDoss.Valore);
List<FluxLog> listFluxLog = resultSet.ODL;
// preparo elenco parametri da inviare...
ListaCalcParams = new List<objItem>();
// per ogni valore ricevuto nel dossier --> calcolo parametro da inviare
foreach (var item in listFluxLog)
{
objItem currData = new objItem()
{
uid = item.CodFlux,
reqValue = item.Valore,
name = item.CodFlux
};
// aggiungo!
ListaCalcParams.Add(currData);
lgInfo($"Add param: {currData.uid} | reqVal: {currData.reqValue}");
}
// aggiungo ultimo parametro di newRecipe...
objItem newRecipeData = new objItem()
{
uid = "NewRecipe",
reqValue = "true",
name = "NewRecipe"
};
ListaCalcParams.Add(newRecipeData);
// effettua chiamata scrittura verso impianto...
plcWriteParams(ref ListaCalcParams);
}
}
}
return answ;
}
#endregion Public Constructors
}
}
+381
View File
@@ -0,0 +1,381 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaMBH : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per MBH OPC-UA (es OPC, Cimolai per Baglietto)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaMBH(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa MBH");
// 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);
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if ((enablePzCountByApp || enablePzCountByIob) && !(disablePzCountByIob))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho emergenza premuta --> emergenza!
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitata gestione ODL esegue eventuale chiusura
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
//// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
//if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
// controllo se non ho dati buoni da > lastCurrentMaxElapsed sec --> disconnetto
if (adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed)
{
lgInfo($"Timeout per mancata comunicazione da oltre {lastCurrentMaxElapsed} sec --> disconnessione adapter OpcUa!");
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
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}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
lgInfo($"Richiesta scrittura {nodes2Write.Count} parametri");
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+914
View File
@@ -0,0 +1,914 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using static IOB_UT_NEXT.CustomObj;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaMBHCimolai : OpcUaMBH
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per MBH (es Cimolai, Baglietto)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaMBHCimolai(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa MBH versione Cimolai (Baglietto)");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
lgInfo($"CHANGE_ODL_MODE: {CHANGE_ODL_MODE}");
}
if (!string.IsNullOrEmpty(getOptPar("VETO_SEND_SNAPSHOT")))
{
string strVETO_SEND_SNAPSHOT = getOptPar("VETO_SEND_SNAPSHOT");
int.TryParse(strVETO_SEND_SNAPSHOT, out VETO_SEND_SNAPSHOT);
lgInfo($"VETO_SEND_SNAPSHOT: {VETO_SEND_SNAPSHOT}");
}
// gestione restart OpcUa client...
if (!string.IsNullOrEmpty(getOptPar("ENABLE_CLI_RESTART")))
{
bool.TryParse(getOptPar("ENABLE_CLI_RESTART"), out enableCliRestart);
}
// init lastCurrentMaxElapsed
if (!string.IsNullOrEmpty(getOptPar("MAX_ELAPSED_TIME_SEC")))
{
int.TryParse(getOptPar("MAX_ELAPSED_TIME_SEC"), out lastCurrentMaxElapsed);
}
sendKeyRichiesta = true;
// controllo se abilitare Processing automatico ODL
if (!string.IsNullOrEmpty(getOptPar("AUTO_CHANGE_ODL")))
{
bool.TryParse(getOptPar("AUTO_CHANGE_ODL"), out doProcOdl);
lgInfo($"AUTO_CHANGE_ODL: {doProcOdl}");
}
// controllo se disabilitare check exe mode
if (!string.IsNullOrEmpty(getOptPar("EXEMODE_CHECK_BYPASS")))
{
bool.TryParse(getOptPar("EXEMODE_CHECK_BYPASS"), out EXEMODE_CHECK_BYPASS);
lgInfo($"EXEMODE_CHECK_BYPASS: {EXEMODE_CHECK_BYPASS}");
}
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 2:2
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x salvare esito scrittura
var writeResult = base.executeTasks(task2exe);
// aggiungo comportamento custom: se ho impostato nome ricetta (programma) --> imposto
// richiesta caricamento se ho richiesto reset o fine lavoro --> imposto azzeramento
// esco restituendo risultato scrittura iniziali
if (task2exe != null)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici x OMP
foreach (var item in task2exe)
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setProg:
// recupero dati da memMap...
if (memMap != null && memMap.mMapWrite != null)
{
if (memMap.mMapWrite.ContainsKey(item.Key))
{
dataConf currMem = memMap.mMapWrite[item.Key];
string addr = currMem.memAddr;
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = item.Value;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
}
// salvo in currProd..
upsertKey(item.Key, item.Value);
break;
case taskType.startSetup:
setFineLotto();
break;
case taskType.stopSetup:
setInizioProd();
break;
case taskType.syncDbData:
processDataSync();
break;
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
}
}
else
{
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
}
}
return writeResult;
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
// non fa nulal (non ha contapezzi)
}
public override bool resetContapezziPLC()
{
bool answ = false;
#if false
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actResetCounter)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
}
catch
{ }
#endif
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool EXEMODE_CHECK_BYPASS = false;
protected int lastAct = 0;
protected bool lastIsInCorso = false;
/// <summary>
/// Ultimo rum mode rilevato x decidere SE inviare variazione...
/// </summary>
protected int lastRunMode = -999;
protected int VETO_SEND_SNAPSHOT = 10;
protected DateTime vetoSnapshot = DateTime.Now;
#endregion Protected Fields
#region Protected Enums
/// <summary>
/// Definizione stati travel
/// </summary>
protected enum travelState
{
Idle = 0,
StartOdl,
UpLift,
Move,
DownLift,
WaitClose
}
#endregion Protected Enums
#region Protected Properties
/// <summary>
/// Attività corrente (INT) da keyRunMode valore di PLC/DB231/Attivita
/// </summary>
protected int currRunMode
{
get
{
int answ = 0;
if (!string.IsNullOrEmpty(opcUaParams.keyRunMode))
{
string currRun = getDataItemValue(opcUaParams.keyRunMode);
if (!string.IsNullOrEmpty(currRun))
{
int.TryParse(currRun, out answ);
}
}
return answ;
}
}
protected travelState currTravelState { get; set; } = travelState.Idle;
protected DateTime currTravelStateStart { get; set; } = DateTime.Today.AddYears(-1);
/// <summary>
/// Lavorazione in CORSO = da keyExeMode valore di PLC/DB231/InCorso
/// </summary>
protected bool isInCorso
{
get
{
bool answ = false;
if (!string.IsNullOrEmpty(opcUaParams.keyExeMode))
{
string currExe = getDataItemValue(opcUaParams.keyExeMode);
if (!string.IsNullOrEmpty(currExe))
{
bool.TryParse(currExe, out answ);
}
}
return answ;
}
}
/// <summary>
/// Indica se abbia stato MANUAL x CIMOLAI
/// </summary>
protected bool isManualCimolai
{
get
{
bool answ = false;
if (lastAct > 0 && lastAct < 10)
{
answ = (lastAct % 2 == 1);
}
return answ;
}
}
/// <summary>
/// Indica se abbia stato WORKING x CIMOLAI
/// </summary>
protected bool isWorkingCimolai
{
get
{
bool answ = false;
if (lastAct > 0 && lastAct < 10)
{
answ = (lastAct % 2 == 0);
}
return answ;
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
/* -----------------------------------------------------
* CIMOLAI CUSTOM
*------------------------------------------------------
* AUX = 2 --> emergenza armata
* AUX <> 2 --> emergenza premuta
* Aux = 2 + InCorso = 0 --> pronto
* Aux = 2 + LastAct in (2,4,6,8) --> LAVORA
*
*
* PLC/DB231/Attivita
* 0: Emergenza
* 1: Avvio registrazione ricetta
* 2: Inizio comando traslazione
* 3: Termine comando traslazione
* 4: Inizio comando di sterzatura
* 5: Termine comando di sterzatura
* 6: Inizio comando movimento carrelli
* 7: Termine comando movimento carrelli
* 8: Inizio comando sollevamento
* 9: Termine comando sollevamento
* 10: richiesta snapshot parametri
*
*------------------------------------------------------
* SEMPLIFICAZIONE POST CERTIFICAZIONE
*------------------------------------------------------
*
* Visto che la rete potrebbe saltare ad intermittenza, conviene gestire in modo semplificato il lavora
* torno a condizione work base: AUX = 2, marcia = in corso
*
*
---------------------------------------------------- */
// controllo emergenza... se zero --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
else
{
// resetto last act...
lastAct = 0;
}
// verifico se aggiornare stato LAST ACTION
if (lastAct != currRunMode && currRunMode != 0)
{
// registro solo azioni > 1 e < 10
if (currRunMode > 1 && currRunMode < 10)
{
// escludo le azioni 4 e 5 (che sono anche in concomitanza con 2-3)
if (currRunMode < 4 || currRunMode > 5)
{
lastAct = currRunMode;
}
}
}
// Gestione ODL automatica: se abilitata --> qui con start/stop da impianto...
if (doProcOdl)
{
lgTrace($"inizio process verifica presa in carico PODL | isInCorso: {isInCorso} | lastIsInCorso: {lastIsInCorso} | currProgName {currProgName}");
// se rilevo variazione exe (curr/last)
// --> registro richiesta attreazzaggio PODL da info commessa
if (lastIsInCorso != isInCorso)
{
// se 0 --> 1 --> registro
if (isInCorso)
{
lgInfo("--------------------------------------------");
// prendo senza stringa PODL
string sPODL = currProgName.Replace("PODL", "");
int idxPODL = 0;
int.TryParse(sPODL, out idxPODL);
// chiamo richiesta setup PODL...
lgInfo($"Inizio trySetupPODL per {idxPODL}");
trySetupPODL(idxPODL);
lgInfo("--------------------------------------------");
}
else
{
lgInfo("--------------------------------------------");
lgInfo("Inizio tryCloseODL");
// registro chiusura ODL..
tryCloseCurrODL();
lgInfo("--------------------------------------------");
}
// registro exe mode
lastIsInCorso = isInCorso;
}
}
// salvo running come = working...
isRunning = isWorkingCimolai;
// se ho setup
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorkingCimolai || isWorking || isInCorso)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else
{
if (!isReady || isManualCimolai || isManual)
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
}
// controllo se non ho dati buoni da > lastCurrentMaxElapsed sec --> disconnetto
if (adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed)
{
lgInfo($"Timeout per mancata comunicazione da oltre {lastCurrentMaxElapsed} sec --> disconnessione adapter OpcUa!");
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Recupera da server set di dati specifici x IOB : qui per compilare files CSV
/// </summary>
/// <returns></returns>
protected override bool iobGetDataFromServer()
{
bool answ = false;
bool okArt = false;
bool okDoss = false;
bool okPodl = false;
bool okLVFasi = false;
List<PODLModel> listaPODL = new List<PODLModel>();
List<DossiersModel> listaDoss = new List<DossiersModel>();
List<AnagArticoli> listaArt = new List<AnagArticoli>();
List<ListVal> anagLVFasi = new List<ListVal>();
Dictionary<string, string> dictAF = new Dictionary<string, string>();
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
var rawListArt = callUrl(urlGetCurrArt, false);
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
var rawListPODL = callUrl(urlGetCurrPODL, false);
var rawLVFasi = callUrl(urlGetListValFasiPodl, false);
if (!string.IsNullOrEmpty(rawListArt))
{
try
{
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
okArt = listaArt.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListDOSS))
{
try
{
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
okDoss = listaDoss.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
okPodl = listaPODL.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetCurrPODL}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawLVFasi))
{
try
{
anagLVFasi = JsonConvert.DeserializeObject<List<ListVal>>(rawLVFasi);
dictAF = anagLVFasi.ToDictionary(x => x.value, x => x.label);
okLVFasi = listaArt.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco ListVal ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco ListVal ({urlGetListValFasiPodl}) ha restituito valore vuoto");
}
answ = okPodl && okDoss && okArt && okLVFasi;
if (answ)
{
// predispongo dati PODL
ListaJobs = listaPODL
.Select(x => new JobRow() { Matricola = x.CodArticolo, Commessa = $"PODL{x.IdxPromessa:00000000}", Articolo = x.CodArticolo, Descrizione = x.CodArticolo, DataIns = $"{x.InsertDate:dd/MM/yyyy}", OraIns = $"{x.InsertDate:HH:mm}", Lavorazione = $"{getLV(dictAF, x.KeyRichiesta)} {x.Note}".Trim() })
.ToList();
// predispongo dati articoli
ListaArticoli = listaArt
.Select(a => new ArtRow() { Matricola = a.CodArticolo, Articolo = a.Disegno, Descrizione = a.DescArticolo, LimiteVel = 100 })
.Distinct()
.ToList();
// completo con dati DOSSIER
foreach (var item in ListaArticoli)
{
string codArt = item.Matricola;
var currDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
if (currDoss != null)
{
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(currDoss.Valore);
// traduco AD MENTULAM...
item.Peso_01 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso1");
item.Peso_02 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso2");
item.Peso_03 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso3");
item.Peso_04 = getFluxValInt(resultSet, "OPC_PLC/DB231/peso4");
item.PosizCarrello_01 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr1");
item.PosizCarrello_02 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr2");
item.PosizCarrello_03 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr3");
item.PosizCarrello_04 = getFluxValInt(resultSet, "OPC_PLC/DB231/PosCarr4");
}
}
}
return answ;
}
/// <summary>
/// Effettua sync dati
/// </summary>
protected override void processDataSync()
{
lgInfo("--------------------------------------------");
lgInfo($"executeTasks --> syncDbData --> processDataSync");
lgInfo("--------------------------------------------");
// effettua sync fixme todo: test scrittura file csv...
var taskGet = iobGetDataFromServer();
var taskWrite = iobWriteLocalCSV();
var taskSend = iobSendFTP("");
}
protected override void procRunMode(ref string currRun)
{
// variabili RUN... se richiesto invio runMode
if (opcUaParams.runModeSend)
{
currRun = $"{currRunMode}";
// effettuo processing SPECIFICO currRunMode x gestione snapshot...
if (currRunMode == 10)
{
if (DateTime.Now > vetoSnapshot)
{
lgInfo("--------------------------------------------");
callUrl(urlTakeSnapshot, false);
// blocco snapshot x VETO_SEND_SNAPSHOT sec...
vetoSnapshot = DateTime.Now.AddSeconds(VETO_SEND_SNAPSHOT);
lgInfo($"Effettuata richiesta salvataggio snapshot, impostato veto nuova chiamata a {vetoSnapshot}");
lgInfo("--------------------------------------------");
}
else
{
lgInfo($"NON effettuo salvataggio snapshot perché veto attivo fino a {vetoSnapshot}");
}
}
else
{
// aggiorno veto x snapshot
vetoSnapshot = DateTime.Now;
}
// invio del valore RUN MODE come FLog, SE variato...
if (!string.IsNullOrEmpty(currRun))
{
if (currReadErrors != lastRunMode)
{
// aggiorno last...
lastRunMode = currRunMode;
// se ho valore --> invio
string sVal = "";
string descr = $"RunModeVal";
DateTime locTStamp = DateTime.Now;
sVal = $"Change 04: {locTStamp.ToString()} | descr: {descr} | Id: {opcUaParams.keyExeMode} | Val: {currRun}";
// cerco se sia un dato/valore in veto
bool hasVetoLK = false;
bool hasVetoLKV = false;
hasVetoLK = opcUaParams.fluxLogKeyValVeto.ContainsKey(descr);
if (hasVetoLK)
{
// se c'è controllo il valore...
var valList = opcUaParams.fluxLogKeyValVeto[descr];
hasVetoLKV = valList.Contains(currRun);
}
if (hasVetoLKV)
{
lgTrace($"NON ACCODATO sample: veto trovato per {descr}/{currRun} in fluxLogKeyValVeto ", false);
}
else
{
accodaFLog(sVal, qEncodeFLog(descr, currRun));
// se richiesto provo a tradurre
if (opcUaParams.runModeTrad)
{
string RunModeDescr = itemTranslation("RunMode", currRun);
descr = $"RunMode";
accodaFLog(sVal, qEncodeFLog(descr, RunModeDescr));
}
}
}
}
}
//verifica preliminare durata minima stato
DateTime adesso = DateTime.Now;
bool canChange = adesso.Subtract(currTravelStateStart).TotalSeconds > opcUaParams.minSecStatusDuration;
// gestione dei macro-stati del travel... SOLO se in esecuzione da travel... BYPASS x ora!!!
if (isInCorso || EXEMODE_CHECK_BYPASS)
{
double pesoTot = 0;
// può cambiare stato se per ALMENO minSecStatus è rimasto nello status corrente...
if (canChange)
{
lgTrace($"start checkExe | TRAVEL STATE: {currTravelState}");
// STEP 1: verifico se sia iniziato caricamento...
if (currTravelState == travelState.Idle)
{
if (currIdxODL > 0)
{
currTravelState = travelState.StartOdl;
currTravelStateStart = adesso;
lgInfo("--------------------------------------------");
lgInfo("TRAVEL STATE: StartOdl");
lgInfo("--------------------------------------------");
}
}
// sono nell'esecuzione ODL, controllo il resto
else
{
// nb: vedere se usare isWorkingCimolai / isManualCimolai
// verifico il valore della variabile calcolata del PESO totale...
if (dataItemMem.ContainsKey("PesoTot"))
{
pesoTot = getDataItemValueDouble("PesoTot");
}
// ...oppuresommo direttamente
else
{
pesoTot += getDataItemValueDouble("PLC/DB231/peso1");
pesoTot += getDataItemValueDouble("PLC/DB231/peso2");
pesoTot += getDataItemValueDouble("PLC/DB231/peso3");
pesoTot += getDataItemValueDouble("PLC/DB231/peso4");
}
lgTrace($"TState: Test 03 | pesoTot: {pesoTot}");
// STEP 2: verifico se sia iniziato caricamento...
if (currTravelState == travelState.StartOdl)
{
if (pesoTot > 0)
{
currTravelState = travelState.UpLift;
currTravelStateStart = adesso;
lgInfo("--------------------------------------------");
lgInfo("TRAVEL STATE: UpLift");
lgInfo("--------------------------------------------");
}
}
// STEP 3: verifico se sia terminato caricamento...
else if (currTravelState == travelState.UpLift)
{
if (pesoTot == 0)
{
currTravelState = travelState.Move;
currTravelStateStart = adesso;
lgInfo("--------------------------------------------");
lgInfo("TRAVEL STATE: Move");
lgInfo("--------------------------------------------");
}
}
// STEP 4: verifico se sia terminato movimento...
else if (currTravelState == travelState.Move)
{
if (pesoTot > 0)
{
currTravelState = travelState.DownLift;
currTravelStateStart = adesso;
lgInfo("--------------------------------------------");
lgInfo("TRAVEL STATE: DownLift");
lgInfo("--------------------------------------------");
}
}
// STEP 5: verifico se sia terminato scaricamento...
else if (currTravelState == travelState.DownLift)
{
if (pesoTot == 0)
{
currTravelState = travelState.WaitClose;
currTravelStateStart = adesso;
lgInfo("--------------------------------------------");
lgInfo("TRAVEL STATE: WaitClose");
lgInfo("--------------------------------------------");
}
}
}
}
else
{
if (canChange)
{
lgTrace("TRAVEL STATE | Nessuna gestione status travel: inCorso = 0");
}
}
// ora verifico solo condizione finale x eventuale chiusura...
bool canSendAskClose = adesso.Subtract(currTravelStateStart).TotalSeconds > opcUaParams.minSecFinalWait;
if (canSendAskClose && currTravelState == travelState.WaitClose)
{
// STEP 6: verifico se sia tolto ODL e quindi ha già chiuso...
if (currIdxODL == 0)
{
currTravelState = travelState.Idle;
currTravelStateStart = adesso;
lgInfo("--------------------------------------------");
lgInfo("TRAVEL STATE: Idle");
lgInfo("--------------------------------------------");
}
// altrimenti manda richiesta chiusura
else
{
// chiama richiesta chiusura x utente
tryAskCloseCurrODL();
// resetta contatore x nuova richeista finale eventuale...
currTravelStateStart = adesso;
}
}
}
else
{
if (canChange)
{
// altrimenti forzo a "Idle" state
currTravelState = travelState.Idle;
currTravelStateStart = adesso;
// trace opzionale
lgTrace($"TRAVEL STATE: {currTravelState}");
}
}
}
#endregion Protected Methods
#region Private Fields
/// <summary>
/// abilitata gestione ODL da impianto
/// </summary>
private bool doProcOdl = false;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Azioni specifiche x indicare fine lotto di produzione
/// </summary>
/// <returns></returns>
private bool setFineLotto()
{
bool answ = false;
#if false
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actStopProd)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();\
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
}
catch
{ }
#endif
return answ;
}
/// <summary>
/// Azioni specifiche x iniziare produzione (impostazione ricetta)
/// </summary>
/// <returns></returns>
private bool setInizioProd()
{
bool answ = false;
#if false
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actSetRecipe)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
}
catch
{ }
#endif
return answ;
}
#endregion Private Methods
}
}
+371
View File
@@ -0,0 +1,371 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaOmron : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per Omron (es ICOEL)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaOmron(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if (utils.CRB("enableContapezzi"))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
#if false
// variabili RUN...
if (!string.IsNullOrEmpty(opcUaParams.keyRunMode))
{
currRun = getDataItemValue(opcUaParams.keyRunMode);
}
#endif
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho emergenza premuta --> emergenza!
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
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}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+661
View File
@@ -0,0 +1,661 @@
using IOB_UT_NEXT;
using MapoSDK;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public enum IcoelStatus
{
Allarme = 0,
Stop = 1,
Manuale = 2,
Automatico = 3
}
public class DatiMesIcoel
{
#region Public Constructors
/// <summary>
/// Inizializzazione classe con dati RAW da byte[]
/// </summary>
/// <param name="rawData">Flusso di dati RAW da tradurre...</param>
public DatiMesIcoel(byte[] rawData)
{
// solo se i dati sono esattamente 115...
if (rawData.Length >= 115)
{
Calibratrice_L1 = new MesItemStatus(rawData.Skip(0).Take(7).ToArray());
Calibratrice_L2 = new MesItemStatus(rawData.Skip(7).Take(7).ToArray());
Dewatering_L1 = new MesItemStatus(rawData.Skip(14).Take(7).ToArray());
Dewatering_L2 = new MesItemStatus(rawData.Skip(21).Take(7).ToArray());
Precalibro_L1 = new MesItemStatus(rawData.Skip(28).Take(7).ToArray());
Precalibro_L2 = new MesItemStatus(rawData.Skip(35).Take(7).ToArray());
Taglierina_L1 = new MesItemStatus(rawData.Skip(42).Take(7).ToArray());
Taglierina_L2 = new MesItemStatus(rawData.Skip(49).Take(7).ToArray());
NastroTaglierina_L1 = new MesItemStatus(rawData.Skip(56).Take(7).ToArray());
NastroTaglierina_L2 = new MesItemStatus(rawData.Skip(63).Take(7).ToArray());
Elevatore_L1 = new MesItemStatus(rawData.Skip(70).Take(7).ToArray());
Elevatore_L2 = new MesItemStatus(rawData.Skip(77).Take(7).ToArray());
ImmergitoreBins_L1 = new MesItemStatus(rawData.Skip(84).Take(7).ToArray());
ImmergitoreBins_L2 = new MesItemStatus(rawData.Skip(91).Take(7).ToArray());
ImmergitoreCasse_L1 = new MesItemStatus(rawData.Skip(98).Take(7).ToArray());
ImmergitoreCasse_L2 = new MesItemStatus(rawData.Skip(105).Take(7).ToArray());
Varie = new PlantStatus(rawData.Skip(112).Take(3).ToArray());
}
}
#endregion Public Constructors
#region Public Properties
public MesItemStatus Calibratrice_L1 { get; set; }
public MesItemStatus Calibratrice_L2 { get; set; }
public MesItemStatus Dewatering_L1 { get; set; }
public MesItemStatus Dewatering_L2 { get; set; }
public MesItemStatus Elevatore_L1 { get; set; }
public MesItemStatus Elevatore_L2 { get; set; }
public MesItemStatus ImmergitoreBins_L1 { get; set; }
public MesItemStatus ImmergitoreBins_L2 { get; set; }
public MesItemStatus ImmergitoreCasse_L1 { get; set; }
public MesItemStatus ImmergitoreCasse_L2 { get; set; }
public MesItemStatus NastroTaglierina_L1 { get; set; }
public MesItemStatus NastroTaglierina_L2 { get; set; }
public MesItemStatus Precalibro_L1 { get; set; }
public MesItemStatus Precalibro_L2 { get; set; }
public MesItemStatus Taglierina_L1 { get; set; }
public MesItemStatus Taglierina_L2 { get; set; }
public PlantStatus Varie { get; set; }
#endregion Public Properties
#region Public Methods
public override bool Equals(object obj)
{
// Object is not a GaugeModel instance
if (!(obj is DatiMesIcoel item))
return false;
if (Calibratrice_L1 != item.Calibratrice_L1)
return false;
if (Calibratrice_L2 != item.Calibratrice_L2)
return false;
if (Dewatering_L1 != item.Dewatering_L1)
return false;
if (Dewatering_L2 != item.Dewatering_L2)
return false;
if (Precalibro_L1 != item.Precalibro_L1)
return false;
if (Precalibro_L2 != item.Precalibro_L2)
return false;
if (Taglierina_L1 != item.Taglierina_L1)
return false;
if (Taglierina_L2 != item.Taglierina_L2)
return false;
if (NastroTaglierina_L1 != item.NastroTaglierina_L1)
return false;
if (NastroTaglierina_L2 != item.NastroTaglierina_L2)
return false;
if (Elevatore_L1 != item.Elevatore_L1)
return false;
if (Elevatore_L2 != item.Elevatore_L2)
return false;
if (ImmergitoreBins_L1 != item.ImmergitoreBins_L1)
return false;
if (ImmergitoreBins_L2 != item.ImmergitoreBins_L2)
return false;
if (ImmergitoreCasse_L1 != item.ImmergitoreCasse_L1)
return false;
if (ImmergitoreCasse_L2 != item.ImmergitoreCasse_L2)
return false;
if (Varie != item.Varie)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion Public Methods
}
public class MesItemStatus
{
#region Public Constructors
public MesItemStatus(byte[] rawData)
{
Stato = (IcoelStatus)BitConverter.ToUInt16(rawData, 0);
Velocita = BitConverter.ToUInt16(rawData, 2);
Termico = !rawData.Skip(4).Take(1).FirstOrDefault().Equals(0);
MagnetoTermico = !rawData.Skip(5).Take(1).FirstOrDefault().Equals(0);
AvariaInverter = !rawData.Skip(6).Take(1).FirstOrDefault().Equals(0);
}
#endregion Public Constructors
#region Public Properties
public bool AvariaInverter { get; set; } = false;
public bool MagnetoTermico { get; set; } = false;
public IcoelStatus Stato { get; set; } = 0;
public bool Termico { get; set; } = false;
public UInt16 Velocita { get; set; } = 0;
#endregion Public Properties
#region Public Methods
public override bool Equals(object obj)
{
// Object is not a GaugeModel instance
if (!(obj is MesItemStatus item))
return false;
if (Stato != item.Stato)
return false;
if (Velocita != item.Velocita)
return false;
if (Termico != item.Termico)
return false;
if (MagnetoTermico != item.MagnetoTermico)
return false;
if (AvariaInverter != item.AvariaInverter)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// Converte un singolo item in un array di byte per scrittura su PLC S7
/// </summary>
/// <returns></returns>
public byte[] serialize()
{
byte[] answ = new byte[7];
Buffer.BlockCopy(BitConverter.GetBytes((short)Stato), 0, answ, 0, 2);
Buffer.BlockCopy(BitConverter.GetBytes(Velocita), 0, answ, 2, 2);
Buffer.BlockCopy(BitConverter.GetBytes(Termico), 0, answ, 4, 1);
Buffer.BlockCopy(BitConverter.GetBytes(MagnetoTermico), 0, answ, 5, 1);
Buffer.BlockCopy(BitConverter.GetBytes(AvariaInverter), 0, answ, 6, 1);
return answ;
}
#endregion Public Methods
}
public class OpcUaOmronIcoel : OpcUaOmron
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per Omron (es ICOEL) https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaOmronIcoel(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
doByteRead = true;
lgInfo($"Avviato IobOpcUaOmronIcoel | encodeReadData: {doByteRead}");
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
#if false
if (utils.CRB("enableContapezzi"))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
#endif
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Internal Methods
/// <summary>
/// Verifica ed invia variazioni DAL FORMATO RAW data (byte[]) --&gt; esplode oggetti e li
/// testa 1:1
/// </summary>
/// <param name="MonIt"></param>
/// <param name="NotifyValue"></param>
/// <param name="forceSend"></param>
internal override bool checkAndSendRaw(Opc.Ua.Client.MonitoredItem MonIt, byte[] NotifyValue, bool forceSend)
{
bool changed = false;
if (MonIt != null)
{
if (NotifyValue != null && NotifyValue.Length > 0)
{
// verifico variazione "globale"
if (!lastData.Equals(currData))
{
// effettuo test/invio x ogni info
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L1", lastData.Calibratrice_L1, currData.Calibratrice_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L2", lastData.Calibratrice_L2, currData.Calibratrice_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Dewatering_L1", lastData.Dewatering_L1, currData.Dewatering_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Dewatering_L2", lastData.Dewatering_L2, currData.Dewatering_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Elevatore_L1", lastData.Elevatore_L1, currData.Elevatore_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Elevatore_L2", lastData.Elevatore_L2, currData.Elevatore_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L1", lastData.ImmergitoreBins_L1, currData.ImmergitoreBins_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L2", lastData.ImmergitoreBins_L2, currData.ImmergitoreBins_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L1", lastData.ImmergitoreCasse_L1, currData.ImmergitoreCasse_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L2", lastData.ImmergitoreCasse_L2, currData.ImmergitoreCasse_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L1", lastData.NastroTaglierina_L1, currData.NastroTaglierina_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L2", lastData.NastroTaglierina_L2, currData.NastroTaglierina_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Precalibro_L1", lastData.Precalibro_L1, currData.Precalibro_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Precalibro_L2", lastData.Precalibro_L2, currData.Precalibro_L2, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Taglierina_L1", lastData.Taglierina_L1, currData.Taglierina_L1, forceSend);
changed = changed || testSendDataBlock(MonIt.StartNodeId, "Taglierina_L2", lastData.Taglierina_L2, currData.Taglierina_L2, forceSend);
// salvo lastData...
lastData = currData;
}
}
else
{
lgError($"checkAndSend ERROR | MonIt: {MonIt.DisplayName} | NotifyValue Null!!!");
}
}
else
{
lgError("checkAndSend ERROR: MonIt null");
}
return changed;
}
internal override void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e)
{
base.UA_ref_eh_MonItChange(sender, e);
}
#endregion Internal Methods
#region Protected Properties
/// <summary>
/// Valore corrente dei dati ICOEL (traduzione JIT da byte[])
/// </summary>
protected DatiMesIcoel currData
{
get
{
byte[] rawByte = new byte[115];
// se ho dati raw decodifico...
if (byteRawData != null)
{
if (byteRawData.Length >= 115)
{
rawByte = byteRawData;
}
}
DatiMesIcoel answ = new DatiMesIcoel(rawByte);
return answ;
}
}
/// <summary>
/// Indica se abbia stato POWER ON (multicondizione)
/// </summary>
protected override bool hasPowerOn
{
get
{
// da rivedere
return true;
//return checkMultiCondition(opcUaParams.condPowerOn);
}
}
/// <summary>
/// Indica se abbia stato MANUAL (condizioni varie, es stopped)
/// </summary>
protected override bool isManual
{
get
{
return false;
//return checkMultiCondition(opcUaParams.condManual);
}
}
/// <summary>
/// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
/// </summary>
protected override bool isReady
{
get
{
return false;
//return checkMultiCondition(opcUaParams.condReady);
}
}
/// <summary>
/// Ultima versione validata delle info x confronto
/// </summary>
protected DatiMesIcoel lastData { get; set; } = new DatiMesIcoel(new byte[115]);
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
// decodifico da currData
if (!currData.Varie.InEmergenza)
{
B_input += (1 << 7);
}
if (currData.Varie.InMarcia)
{
B_input += (1 << 1);
}
if (currData.Varie.InStop)
{
B_input += (1 << 3);
}
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
}
/// <summary>
/// effettua verifica del datablock icoel inviando eventualmente i dati variati
/// </summary>
/// <param name="startNodeId"></param>
/// <param name="blockName"></param>
/// <param name="currBlock"></param>
/// <param name="newBlock"></param>
/// <param name="forceSend"></param>
/// <returns></returns>
protected bool testSendDataBlock(NodeId startNodeId, string blockName, MesItemStatus currBlock, MesItemStatus newBlock, bool forceSend)
{
bool changed = false;
// verifica globale blocchi old/new...
if (!currBlock.Equals(newBlock))
{
// creo un nuovo monitoredItem se non ci fosse x ogni variabile dell'oggetto...
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Stato", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Stato}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Velocita", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Velocita}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Termico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Termico}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_MagnetoTermico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.MagnetoTermico}", forceSend);
changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_AvariaInverter", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.AvariaInverter}", forceSend);
// spostare sotto in checkAndSendRaw ??? FIXME todo
if (changed)
{
lgTrace($"Invio variazione dataitem per {dataItemMem.Count} elementi");
sendDataItemListToServer(startNodeId);
}
else
{
lgTrace("Nessuna variazione DataItem da trasmettere");
}
}
return changed;
}
/// <summary>
/// Verifico se salvare e inviare proprietà specificata
/// </summary>
/// <param name="MonIt"></param>
protected bool testSendProperty(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
{
bool changed = false;
DateTime locTStamp = DateTime.Now;
string sVal = "";
string descr = "";
string uuid = calcID($"{MonIt.StartNodeId.Identifier}", $"{MonIt.DisplayName}");
descr = itemTranslation("OPC", uuid);
sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {NotifyValue}";
lgDebug($"TSP | {sVal}");
changed = checkSaveValue(MonIt, NotifyValue, false);
// cerco se non sia un dato filtrato in FLUXLOG...
bool isFiltered = opcUaParams.fluxLogVeto.Contains(uuid);
if (isFiltered)
{
lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false);
}
else
{
if (changed || forceSend)
{
accodaFLog(sVal, qEncodeFLog(descr, $"{NotifyValue}"));
}
else
{
lgTrace($"TSP | NON ACCODATO sample per {uuid} - verifica variazione ha dato esito negativo", false);
}
}
return changed;
}
#endregion Protected Methods
}
public class PlantStatus
{
#region Public Constructors
public PlantStatus(byte[] rawData)
{
InMarcia = !rawData.Skip(0).Take(1).FirstOrDefault().Equals(0);
InEmergenza = !rawData.Skip(1).Take(1).FirstOrDefault().Equals(0);
InStop = !rawData.Skip(2).Take(1).FirstOrDefault().Equals(0);
//var valore = BitConverter.ToUInt16(rawData, 0);
//byte b = rawData.Skip(0).Take(1).FirstOrDefault();
//InMarcia = (b & (1 << (1 - 1))) != 0;
//InEmergenza = (b & (1 << (2 - 1))) != 0;
//InStop = (b & (1 << (3 - 1))) != 0;
}
#endregion Public Constructors
#region Public Properties
public bool InEmergenza { get; set; } = false;
public bool InMarcia { get; set; } = false;
public bool InStop { get; set; } = false;
#endregion Public Properties
#region Public Methods
public override bool Equals(object obj)
{
// Object is not a GaugeModel instance
if (!(obj is PlantStatus item))
return false;
if (InMarcia != item.InMarcia)
return false;
if (InEmergenza != item.InEmergenza)
return false;
if (InStop != item.InStop)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion Public Methods
}
}
+599
View File
@@ -0,0 +1,599 @@
#if false
using EgwProxy.MultiCncLib.OPENcontrol;
#endif
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaSiemens : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per Siemens OPC-UA (es OPC x Cereria Finassi e Unitech x Rama)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaSiemens(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa Siemens");
// 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);
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// se ci fosse il task di gestione attrezzaggio --> aggiunge un SYNC DB...
Dictionary<string, string> task2add = new Dictionary<string, string>();
// cerco nei task
if (task2exe != null)
{
bool forceDbSync = false;
bool addResetPzCount = false;
// controllo se memMap != null...
if (memMap != null)
{
// cerco task specifici: se ho task da startSetup --> preparo un task di syncDb in coda...
foreach (var item in task2exe)
{
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setArt:
case taskType.setComm:
case taskType.setPzComm:
forceDbSync = true;
break;
case taskType.endProd:
case taskType.startSetup:
forceDbSync = true;
// se richiesto reset aggiungo!
if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE")
{
addResetPzCount = true;
}
lgInfo($"Chiamato {tName} | addResetPzCount: {addResetPzCount}");
break;
}
}
}
// se NON c'è già sync DB
if (!task2exe.ContainsKey("syncDbData"))
{
if (forceDbSync)
{
task2exe.Add("syncDbData", "syncDbData");
}
}
// se NON c'è già sync DB
if (addResetPzCount && !task2exe.ContainsKey("forceResetPzCount"))
{
task2exe.Add("forceResetPzCount", "0");
}
}
// uso metodo base ora (con eventuale aggiunta)
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
DateTime adesso = DateTime.Now;
// verifico abilitazione e che NON ci sia veto x processing contapezzi...
if (utils.CRB("enableContapezzi") && dtVetoReadPzCount < adesso)
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int iVal = -1;
bool fatto = false;
// se ho virgola --> double!
if (currPzCount.Contains(","))
{
double dVal = 0;
fatto = double.TryParse(currPzCount, out dVal);
iVal = (int)dVal;
}
else
{
fatto = int.TryParse(currPzCount, out iVal);
}
//bool fatto = Int32.TryParse(currPzCount, out iVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (iVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = iVal > -1 ? iVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
lgInfo("Chiamata resetContapezziPLC");
bool answ = false;
// ...SE abilitato da conf IOB
if (cIobConf.optPar.Count > 0 && getOptPar("ENABLE_PZ_RESET") == "TRUE")
{
// imposto un veto in lettura cmq x evitare problemi
dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
if (opcUaParams.actResetCounter != null && opcUaParams.actResetCounter.Count > 0)
{
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actResetCounter)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
lgInfo($"Accodo nodo per scrittura OPC-UA:{Environment.NewLine}{JsonConvert.SerializeObject(commWriteVal)}");
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
lgInfo($"Eseguito scrittura OPC-UA su nodo actResetCounter");
}
catch
{ }
}
else
{
// imposto a zero (SE gestito)
setcontapezziPLC(0);
}
}
else
{
lgError("Impossibile effettuare RESET contapezzi, mancanza parametro OPT:ENABLE_PZ_RESET");
}
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
if (memMap != null && memMap.mMapWrite.ContainsKey("forceSetPzCount"))
{
// provare x Donati EMCO e Siemens Rama Tenditalia
Dictionary<string, string> task2add = new Dictionary<string, string>();
task2add.Add("forceSetPzCount", $"{newPzCount}");
executeTasks(task2add);
}
else
{
lgError("Impossibile effettuare SET contapezzi, mancanza conf per mMapWrite.forceSetPzCount");
}
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// check lavora x contapezzi...
if (!isWorking && delayMinReadPzCount > 0)
{
// metto un veto al contapezzi...
dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
}
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho emergenza premuta --> emergenza!
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
if (dtVetoReadPzCount >= adesso)
{
// tolgo eventuale veto al contapezzi...
dtVetoReadPzCount = DateTime.Now.AddSeconds(-1);
}
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
else if (hasError)
{
B_input += (1 << 3);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
lgTrace("decodeToBaseBitmap --> tryDisconnect");
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// 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)
{
// valori da scrivere in memoria
Dictionary<string, WriteValue> nodes2Write = new Dictionary<string, WriteValue>();
Dictionary<string, Dictionary<string, string>> nodesUdt2Write = new Dictionary<string, Dictionary<string, string>>();
Dictionary<string, string> currUdsDict = new Dictionary<string, string>();
// 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;
// tolta x evitare reset a riavvio delle qta contapezzi
#if false
// faccio preliminarmente upsertKey... SE la chiave fa parte di quelle in scrittura...
upsertKey(memAddrWrite, currMem.value);
#endif
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<string, string>();
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<WriteValue> listWriteReq = nodes2Write.Select(x => x.Value).ToList();
// valori "diretti"
if (listWriteReq.Count > 0)
{
var toWrite = new Dictionary<NodeId, IEnumerable<string>>();
foreach (var item in listWriteReq)
{
toWrite.Add(item.NodeId, new List<string>() { $"{item.Value.Value}" });
}
// scrittura modalità siemens
UA_ref.WriteValues(toWrite);
}
// se valori Udt con modalità alternativa
if (nodesUdt2Write.Count > 0)
{
foreach (var item in nodesUdt2Write)
{
UA_ref.WriteNodesUdt(item.Key, item.Value);
}
}
}
}
}
}
#endregion Protected Methods
}
}
+214
View File
@@ -0,0 +1,214 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaSiemensOMP : OpcUaSiemens
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per EWON (es Monti, Tenditalia) https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaSiemensOMP(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa Siemens versione OMP (Cereria Finassi)");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x salvare esito scrittura
var writeResult = base.executeTasks(task2exe);
// aggiungo comportamento custom: se ho impostato nome ricetta (programma) --> imposto
// richiesta caricamento se ho richiesto reset o fine lavoro --> imposto azzeramento
// esco restituendo risultato scrittura iniziali
if (task2exe != null)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici x OMP
foreach (var item in task2exe)
{
taskOk = false;
taskVal = "";
// converto richiesta in enum...
taskType tName = taskType.nihil;
Enum.TryParse(item.Key, out tName);
// controllo sulla KEY...
switch (tName)
{
case taskType.setProg:
// recupero dati da memMap...
if (memMap != null && memMap.mMapWrite != null)
{
if (memMap.mMapWrite.ContainsKey(item.Key))
{
dataConf currMem = memMap.mMapWrite[item.Key];
string addr = currMem.memAddr;
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = item.Value;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
}
// salvo in currProd..
upsertKey(item.Key, item.Value);
break;
case taskType.startSetup:
setFineLotto();
break;
case taskType.stopSetup:
setInizioProd();
break;
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | {taskVal}");
break;
}
}
}
else
{
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
}
}
return writeResult;
}
public override bool resetContapezziPLC()
{
bool answ = false;
lgInfo("Chiamata resetContapezziPLC");
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actResetCounter)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
lgInfo($"Accodo nodo per scrittura OPC-UA:{Environment.NewLine}{JsonConvert.SerializeObject(commWriteVal)}");
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
lgInfo($"Eseguito scrittura OPC-UA su nodo actResetCounter");
}
catch
{ }
return answ;
}
#endregion Public Methods
#region Private Methods
/// <summary>
/// Azioni specifiche x indicare fine lotto di produzione
/// </summary>
/// <returns></returns>
private bool setFineLotto()
{
bool answ = false;
lgInfo("Chiamata setFineLotto");
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actStopProd)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
lgInfo($"Accodo nodo per scrittura OPC-UA:{Environment.NewLine}{JsonConvert.SerializeObject(commWriteVal)}");
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
lgInfo($"Eseguito scrittura OPC-UA su nodo actStopProd");
}
catch
{ }
return answ;
}
/// <summary>
/// Azioni specifiche x iniziare produzione (impostazione ricetta)
/// </summary>
/// <returns></returns>
private bool setInizioProd()
{
bool answ = false;
lgInfo("Chiamata setInizioProd");
try
{
List<WriteValue> nodes2Write = new List<WriteValue>();
foreach (var item in opcUaParams.actSetRecipe)
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(item.Key);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.Value;
nodes2Write.Add(commWriteVal);
lgInfo($"Accodo nodo per scrittura OPC-UA:{Environment.NewLine}{JsonConvert.SerializeObject(commWriteVal)}");
}
// vera scrittura
UA_ref.WriteNodes(nodes2Write);
answ = true;
lgInfo($"Eseguito scrittura OPC-UA su nodo actSetRecipe");
}
catch
{ }
return answ;
}
#endregion Private Methods
}
}
+298
View File
@@ -0,0 +1,298 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
using static IOB_UT_NEXT.CustomObj;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaSiemensRama : OpcUaSiemens
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per adapter Siemens integrato OPC-UA (es RAMA RED by Unitech x TFT)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaSiemensRama(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa Siemens x RAMA");
doByteRead = true;
/*-------------------------------------------
* Gestione custom
*
* Creare array vettori di BIN/bool x decodificare info specifiche
* configurare CABLATO in oggetti custom RAMA
* decodifica dataitem custom rama dagli oggetti binari
*
* QUANDO FUNZIONA verificare se e come rendere parametrica la gestione oggetti/byte configurabili
*
*-------------------------------------------*/
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Protected Methods
/// <summary>
/// Esegue step a valle dell'auto ODL:
/// - reset contapezzi
/// - richiesta setup (uguale al precedente)
/// </summary>
protected override void processAutoOdlExtraStep()
{
// inizio con reset ricetta caricata
ListaCalcParams = new List<objItem>();
// aggiungo reset caricamento
objItem resRecipeData = new objItem()
{
uid = "ResetAccepted",
reqValue = "false",
name = "ResetAccepted"
};
ListaCalcParams.Add(resRecipeData);
// effettua chiamata scrittura verso impianto x il solo reset......
plcWriteParams(ref ListaCalcParams);
// aspetta 1 sec procede
Task.Delay(1 * 1000);
// aggiungo ultimo parametro di resetCounter... come 0/1... boh!
objItem resetCnt = new objItem()
{
uid = "ResetCounter",
reqValue = "1",
name = "ResetCounter"
};
ListaCalcParams.Add(resetCnt);
// effettua chiamata scrittura verso impianto...
plcWriteParams(ref ListaCalcParams);
// aspetta 1 sec e indica ricetta richiesta
Task.Delay(1 * 1000);
ListaCalcParams = new List<objItem>();
// aggiungo ultimo parametro di newRecipe...
objItem newRecipeData = new objItem()
{
uid = "NewRecipe",
reqValue = "true",
name = "NewRecipe"
};
ListaCalcParams.Add(newRecipeData);
// effettua chiamata scrittura verso impianto...
plcWriteParams(ref ListaCalcParams);
// aggiungo 1 altro step di attesa alla lettura pezzi x sicurezza...
dtVetoReadPzCount = DateTime.Now.AddMinutes(delayMinReadPzCount);
}
/// <summary>
/// Effettua sync dati inviando RICETTA
/// </summary>
protected override void processDataSync()
{
lgInfo("--------------------------------------------");
lgInfo($"executeTasks --> syncDbData --> processDataSync");
lgInfo("--------------------------------------------");
// effettua sync recuperando dati ed effettuando scrittura su kepware
var taskGet = iobGetSendDossier();
}
#endregion Protected Methods
#region Private Methods
/// <summary>
/// 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 iobGetSendDossier()
{
bool answ;
lgInfo("iobGetSendDossier: Inizio scrittura dati a server OPC-UA");
/*-----------------------------------------
* STEPS processo:
* - recupero ARTICOLI (solo quelli dei PODL attivi)
* - recupero PODL (tutti)
* - 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 dei memWrite x tradurre l'elenco fluxLog in nuovi valori
* - preparo il set di valori da scrivere con traduzione
* - invio scritture
* - aggiungo info articolo (+ ODL...)
* - aggiungo i valori BIT di "reset contapezzi " + "nuova ricetta"
*----------------------------------------- */
bool okArt = false;
bool okDoss = false;
bool okPodl = false;
List<AnagArticoli> listaArt = new List<AnagArticoli>();
List<PODLModel> listaPODL = new List<PODLModel>();
List<DossiersModel> listaDoss = new List<DossiersModel>();
// recupera dati da server tramite chiamate REST a MP/IO/IOB...
var rawListArt = callUrl(urlGetCurrArt, false);
var rawListDOSS = callUrl(urlGetCurrDOSS, false);
var rawListPODL = callUrl(urlGetActPODL, false);
if (!string.IsNullOrEmpty(rawListArt))
{
try
{
listaArt = JsonConvert.DeserializeObject<List<AnagArticoli>>(rawListArt);
okArt = listaArt.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco ART ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco ART ({urlGetCurrArt}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListDOSS))
{
try
{
listaDoss = JsonConvert.DeserializeObject<List<DossiersModel>>(rawListDOSS);
okDoss = listaDoss.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco DOSSIER ({urlGetCurrDOSS}) ha restituito valore vuoto");
}
if (!string.IsNullOrEmpty(rawListPODL))
{
try
{
listaPODL = JsonConvert.DeserializeObject<List<PODLModel>>(rawListPODL);
okPodl = listaPODL.Count > 0;
}
catch (Exception exc)
{
lg.Error($"Errore: chiamata elenco DOSSIER ha restituito errore{Environment.NewLine}{exc}");
}
}
else
{
lg.Error($"Errore: chiamata elenco PODL ({urlGetActPODL}) ha restituito valore vuoto");
}
answ = okArt && okPodl && okDoss;
if (answ)
{
// verifico i PODL attivi...
var listPodlAct = listaPODL.Where(x => x.Attivabile).OrderBy(x => x.InsertDate).ToList();
if (listPodlAct.Count > 0)
{
string codArt = listPodlAct.FirstOrDefault().CodArticolo;
// verifico di avere il dossier e l'articolo x questo PODL
var selArt = listaArt.Where(x => x.CodArticolo == codArt).FirstOrDefault();
var selDoss = listaDoss.Where(x => x.CodArticolo == codArt).FirstOrDefault();
// prendo il primo e recupero...
if (selArt != null && selDoss != null)
{
// recupero il resultset dei valori FluxLog da caricare
DossierFluxLogDTO resultSet = JsonConvert.DeserializeObject<DossierFluxLogDTO>(selDoss.Valore);
List<FluxLog> listFluxLog = resultSet.ODL;
// inizio con reset ricetta caricata
ListaCalcParams = new List<objItem>();
// aggiungo reset caricamento
objItem resRecipeData = new objItem()
{
uid = "ResetAccepted",
reqValue = "false",
name = "ResetAccepted"
};
ListaCalcParams.Add(resRecipeData);
// effettua chiamata scrittura verso impianto x il solo reset......
plcWriteParams(ref ListaCalcParams);
// preparo elenco parametri da inviare...
// aspetta 1 sec procede
Task.Delay(1 * 1000);
ListaCalcParams = new List<objItem>();
// per ogni valore ricevuto nel dossier --> calcolo parametro da inviare
foreach (var item in listFluxLog)
{
objItem currData = new objItem()
{
uid = item.CodFlux,
reqValue = item.Valore,
name = item.CodFlux
};
// aggiungo!
ListaCalcParams.Add(currData);
lgInfo($"Add param: {currData.uid} | reqVal: {currData.reqValue}");
}
// aggiungo parametro ARTICOLO...
objItem setArt = new objItem()
{
uid = "setArt",
reqValue = getMemMapWriteVal("setArt"),
name = "setArt"
};
ListaCalcParams.Add(setArt);
// aggiungo parametro COMMESSA...
objItem setComm = new objItem()
{
uid = "setComm",
reqValue = getMemMapWriteVal("setComm"),
name = "setComm"
};
ListaCalcParams.Add(setComm);
// aggiungo ultimo parametro di resetCounter... come 0/1... boh!
objItem resetCnt = new objItem()
{
uid = "ResetCounter",
reqValue = "1",
name = "ResetCounter"
};
ListaCalcParams.Add(resetCnt);
// effettua chiamata scrittura verso impianto...
plcWriteParams(ref ListaCalcParams);
// aspetta 1 sec e indica ricetta richiesta
Task.Delay(1 * 1000);
ListaCalcParams = new List<objItem>();
// aggiungo ultimo parametro di newRecipe...
objItem newRecipeData = new objItem()
{
uid = "NewRecipe",
reqValue = "true",
name = "NewRecipe"
};
ListaCalcParams.Add(newRecipeData);
// effettua chiamata scrittura verso impianto...
plcWriteParams(ref ListaCalcParams);
}
}
}
return answ;
}
#endregion Private Methods
}
}
+368
View File
@@ -0,0 +1,368 @@
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
namespace IOB_WIN_OPC_UA.IobOpc
{
public class OpcUaUlma : OpcUa
{
#region Public Constructors
/// <summary>
/// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
/// gestione specifica per ULMA OPC-UA (es Giacovelli)
/// https://github.com/OPCFoundation/UA-.NETStandard
/// </summary>
/// <param name="caller"></param>
/// <param name="IOBConf"></param>
public OpcUaUlma(AdapterFormNext caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
lgInfo("Init OpcUa ULMA");
// inizializzo classe base...
if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE")))
{
CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE");
}
sendKeyRichiesta = true;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Processo i task richiesti e li elimino dalla coda 1:1
/// </summary>
/// <param name="task2exe"></param>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// uso metodo base x ora
return base.executeTasks(task2exe);
}
/// <summary>
/// Effettua vero processing contapezzi
/// </summary>
public override void processContapezzi()
{
if ((enablePzCountByApp || enablePzCountByIob) && !(disablePzCountByIob))
{
// check condizione validazione
if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0)
{
// cerco parametro contapezzi...
string currPzCount = getDataItemValue(opcUaParams.keyPartCount);
// se ho un contapezzi... processo...
if (!string.IsNullOrEmpty(currPzCount))
{
int newVal = -1;
bool fatto = Int32.TryParse(currPzCount, out newVal);
if (fatto)
{
// gestione decremento contapezzi: viene "messo via" solo SE c'è un
// effettivo decremento contapezzi...
if (newVal < contapezziPLC)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true");
}
// salvo nuovo valore contapezziPLC
contapezziPLC = newVal > -1 ? newVal : contapezziPLC;
}
else
{
lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}");
}
}
else
{
lgError("Errore in decodifica valore contapezzi, valore vuoto!");
}
}
if (CHANGE_ODL_MODE == "PZCOUNT_RESET")
{
// controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il
// valore x ODL e NON abilitato il trigger reset --> abilito trigger...
if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0)
{
pzCountResetted = true;
// incremento contatore richiesta
countKeyRichiesta = countKeyRichiesta + 1;
// log
lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true");
}
}
}
}
/// <summary>
/// Effettua reset del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool resetContapezziPLC()
{
bool answ = false;
return answ;
}
/// <summary>
/// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
/// </summary>
/// <returns></returns>
public override bool setcontapezziPLC(int newPzCount)
{
bool answ = false;
return answ;
}
#endregion Public Methods
#region Protected Fields
protected bool testDone = false;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua decodifica aree memoria alla bitmap usata x MAPO
/// </summary>
protected override void decodeToBaseBitmap()
{
DateTime adesso = DateTime.Now;
// init a zero...
B_input = 0;
/* -----------------------------------------------------
* STATE MACHINE 60 STD / SIMULA
*------------------------------------------------------
* bitmap MAPO
* B0: POWER_ON
* B1: RUN
* B2: pzCount
* B3: allarme
* B4: manuale
* B5: SlowTC (NON gestito qui)
* B6: warm-up / cool-down / setup
* B7: emergenza ARMATA (1=ok, 0 = premuta)
---------------------------------------------------- */
// se valido il check ping lo eseguo... altrimenti lo do x buono
bool checkPing = !opcUaParams.pingAsPowerOn;
string currRun = "N.A.";
if (!checkPing)
{
checkPing = (testPingMachine == IPStatus.Success);
}
// bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
bool powerOnOk = checkPing && hasPowerOn;
// procedo SOLO SE mi da ping OK...
if (checkPing)
{
B_input = powerOnOk ? 1 : 0;
procRunMode(ref currRun);
// salvo running come = working...
isRunning = isWorking;
// se ho emergenza premuta --> emergenza!
if (hasEStopArmed)
{
B_input += (1 << 7);
}
// se ho emergenza premuta --> emergenza!
if (isWarmUpCoolDown)
{
B_input += (1 << 6);
}
// se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
if (hasError)
{
B_input += (1 << 3);
}
if (isWorking)
{
// RUN = LAVORA!
B_input += (1 << 1);
}
else if (powerOnOk && (!isReady || isManual))
{
// se NON ready --> manual
B_input += (1 << 4);
}
}
// se abilitata gestione ODL esegue eventuale chiusura
// se abilitato watchdog...
if (opcUaParams.WatchDog.IsEnabled)
{
if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
{
lastWatchDogPLC = adesso;
WatchDog++;
WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;
lgTrace($"WatchDog val: {WatchDog}");
try
{
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = WatchDog;
List<WriteValue> nodes2Write = new List<WriteValue>();
nodes2Write.Add(commWriteVal);
UA_ref.WriteNodes(nodes2Write);
}
catch (Exception exc)
{
lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
}
}
}
else
{
lgTrace("WatchDog disabilitato");
}
// controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
{
tryDisconnect();
}
// solo se non ho veto check
int vFactor = 2;
if (vetoCheckStatus < adesso)
{
lgDebug($"Stato variabili checkPing: {testPingMachine}");
// imposto veto per vetoSeconds...
vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
}
// log opzionale!
if (verboseLog)
{
lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
}
}
/// <summary>
/// Effettua vera scrittura parametri
/// </summary>
/// <param name="updatedPar"></param>
protected override void plcWriteParams(ref List<objItem> updatedPar)
{
dataConf currMem = null;
int byteSize = 0;
string memAddrWrite = "";
string serObj = "";
if (updatedPar != null)
{
List<WriteValue> nodes2Write = new List<WriteValue>();
// controllo i parametri... ne gestisco 4...
foreach (var item in updatedPar)
{
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;
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId(currMem.memAddr);
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = item.reqValue;
// faccio preliminarmente upsertKey...
upsertKey(currMem.name, 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}---------------");
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}---------------");
if (!string.IsNullOrEmpty(memAddrWrite))
{
nodes2Write.Add(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 (nodes2Write.Count > 0)
{
lgInfo($"Richiesta scrittura {nodes2Write.Count} parametri");
UA_ref.WriteNodes(nodes2Write);
}
}
}
#endregion Protected Methods
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
using IOB_UT_NEXT;
using IOB_WIN_PING.Iob;
using IOB_WIN_OPC_UA.Iob;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IOB_WIN_PING
namespace IOB_WIN_OPC_UA
{
public class MainFormNext : IOB_WIN_FORM.MainForm
{
+74
View File
@@ -0,0 +1,74 @@
using System;
namespace IOB_WIN_OPC_UA
{
/// <summary>
/// Classe di estensione x oggetti DataItems OPC-UA con struttura e valori
/// </summary>
public class OpcUaDataItemExt : Opc.Ua.Client.MonitoredItem
{
#region Public Constructors
public OpcUaDataItemExt(Opc.Ua.Client.MonitoredItem MonIt)
{
if (MonIt != null)
{
AttributeId = MonIt.AttributeId;
CacheQueueSize = MonIt.CacheQueueSize;
Handle = MonIt.Handle;
DiscardOldest = MonIt.DiscardOldest;
DisplayName = MonIt.DisplayName;
Encoding = MonIt.Encoding;
Filter = MonIt.Filter;
IndexRange = MonIt.IndexRange;
MonitoringMode = MonIt.MonitoringMode;
NodeClass = MonIt.NodeClass;
QueueSize = MonIt.QueueSize;
RelativePath = MonIt.RelativePath;
SamplingInterval = MonIt.SamplingInterval;
StartNodeId = MonIt.StartNodeId;
}
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// NodeId univoco dell'oggetto x subscription (StartNodeId)
/// </summary>
public string nodeId { get; set; } = "";
/// <summary>
/// Valore Registrato in formato byte array
/// </summary>
public byte[] rawByte { get; set; } = new byte[1];
/// <summary>
/// Valore (in sec) del periodo di downsampling (0 --> NON usato)
/// </summary>
public int samplePeriod { get; set; } = 60;
/// <summary>
/// Valore soglia DeadBand (0 --> non usata)
/// </summary>
public double thresholdDeadBand { get; set; } = 0;
/// <summary>
/// UUID univoco dell'oggetto --> flusso (DisplayName)
/// </summary>
public string uid { get; set; } = "";
/// <summary>
/// Valore Registrato in formato stringa
/// </summary>
public string value { get; set; } = "";
/// <summary>
/// Timestamp data-ora evento registrato
/// </summary>
public DateTime valueTimestamp { get; set; } = DateTime.Now;
#endregion Public Properties
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
using System;
using System.Windows.Forms;
namespace IOB_WIN_PING
namespace IOB_WIN_OPC_UA
{
internal static class Program
{
+2 -2
View File
@@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("IOB-WIN-MTC")]
[assembly: AssemblyTitle("IOB-WIN-OPC-UA")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("IOB-WIN-MTC")]
[assembly: AssemblyProduct("IOB-WIN-OPC-UA")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
+19 -27
View File
@@ -8,10 +8,10 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace IOB_WIN_MTC.Properties
{
namespace IOB_WIN_OPC_UA.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@@ -19,51 +19,43 @@ namespace IOB_WIN_MTC.Properties
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IOB_WIN_MTC.Properties.Resources", typeof(Resources).Assembly);
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IOB_WIN_OPC_UA.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set
{
set {
resourceCulture = value;
}
}
+9 -13
View File
@@ -8,21 +8,17 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace IOB_WIN_MTC.Properties
{
namespace IOB_WIN_OPC_UA.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
public static Settings Default {
get {
return defaultInstance;
}
}
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
{
"metadata": [
{
"src": [
{
"files": [
"*.csproj"
],
"cwd": ".",
"exclude": [
"**/obj/**",
"**/bin/**",
"_site/**"
]
}
],
"dest": "obj/api"
}
],
"build": {
"content": [
{
"files": [
"api/**.yml"
],
"cwd": "obj"
},
{
"files": [
"api/*.md",
"articles/**.md",
"toc.yml",
"*.md"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"resource": [
{
"files": [
"images/**"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"overwrite": [
{
"files": [
"apidoc/**.md"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
"dest": "_site",
"template": [
"default"
]
}
}
+11
View File
@@ -0,0 +1,11 @@
# IOB-WIN-NEXT
Documentazione relativa all'applicativo IOB-WIN-NEXT
## Articles
Per maggiori dettagli, definizioni e demo funzionamento si rimanda alla sezione Articles
## Api
Per ogni dettaglio e riferimento alla libreria si rimanda alla sezione Api Documentation
+13 -3
View File
@@ -1,20 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net462" />
<package id="MapoSDK" version="6.14.2411.518" targetFramework="net462" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="6.0.0" targetFramework="net462" />
<package id="Microsoft.Bcl.HashCode" version="6.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="6.0.0" targetFramework="net462" />
<package id="Microsoft.VisualStudio.SlowCheetah" version="4.0.50" targetFramework="net462" developmentDependency="true" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" />
<package id="NLog" version="5.3.4" targetFramework="net462" />
<package id="OPCFoundation.NetStandard.Opc.Ua.Client" version="1.5.374.158" targetFramework="net462" />
<package id="OPCFoundation.NetStandard.Opc.Ua.Configuration" version="1.5.374.158" targetFramework="net462" />
<package id="OPCFoundation.NetStandard.Opc.Ua.Core" version="1.5.374.158" targetFramework="net462" />
<package id="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" version="1.5.374.158" targetFramework="net462" />
<package id="Pipelines.Sockets.Unofficial" version="2.2.8" targetFramework="net462" />
<package id="S7netplus" version="0.1.9" targetFramework="net462" />
<package id="StackExchange.Redis" version="2.8.24" targetFramework="net462" />
<package id="System.Buffers" version="4.5.1" targetFramework="net462" />
<package id="System.Diagnostics.DiagnosticSource" version="6.0.1" targetFramework="net462" />
<package id="System.Formats.Asn1" version="8.0.1" targetFramework="net462" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net462" />
<package id="System.IO.Pipelines" version="5.0.1" targetFramework="net462" />
<package id="System.Memory" version="4.5.4" targetFramework="net462" />
<package id="System.Memory" version="4.5.5" targetFramework="net462" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net462" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" targetFramework="net462" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net462" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net462" />
<package id="System.Threading.Channels" version="5.0.0" targetFramework="net462" />
<package id="System.Threading.Channels" version="6.0.0" targetFramework="net462" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net462" />
</packages>
+6
View File
@@ -0,0 +1,6 @@
- name: Articles
href: articles/
- name: API Documentation
href: obj/api/
homepage: api/index.md