Ancora update x adapter OPC UA

This commit is contained in:
Samuele Locatelli
2022-06-14 19:31:53 +02:00
parent 2fe6f46a85
commit e878bcd4db
42 changed files with 3857 additions and 1060 deletions
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32519.379
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.MONO.ADAPTER.OPC", "MP.MONO.ADAPTER.OPC\MP.MONO.ADAPTER.OPC.csproj", "{458FD824-7804-4CF9-8C43-463C1F5659DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.MONO.ADAPTER.OLD", "MP.MONO.ADAPTER.OLD\MP.MONO.ADAPTER.OLD.csproj", "{84F0ABFE-ADD6-426D-BD9B-E4E9086EE169}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{458FD824-7804-4CF9-8C43-463C1F5659DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{458FD824-7804-4CF9-8C43-463C1F5659DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{458FD824-7804-4CF9-8C43-463C1F5659DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{458FD824-7804-4CF9-8C43-463C1F5659DA}.Release|Any CPU.Build.0 = Release|Any CPU
{84F0ABFE-ADD6-426D-BD9B-E4E9086EE169}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84F0ABFE-ADD6-426D-BD9B-E4E9086EE169}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84F0ABFE-ADD6-426D-BD9B-E4E9086EE169}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84F0ABFE-ADD6-426D-BD9B-E4E9086EE169}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3811E8B4-1DD3-4841-9840-1B1A08782CD3}
EndGlobalSection
EndGlobal
@@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>1.12206.1410</Version>
<Version>1.12206.1416</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>MAPO-MONO</i>
<h4>Version: 1.12206.1410</h4>
<h4>Version: 1.12206.1416</h4>
<br /> Release Note:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
1.12206.1410
1.12206.1416
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.12206.1410</version>
<version>1.12206.1416</version>
<url>http://nexus.steamware.net/repository/SWS/MP.MONO.ADAPTER/stable/LAST/MP.Mon.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/MP.MONO.ADAPTER/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+142 -156
View File
@@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using NLog;
using Opc.Ua;
using Opc.Ua.Client;
using NLog;
using System.Collections;
namespace MP.MONO.ADAPTER
{
@@ -15,20 +10,6 @@ namespace MP.MONO.ADAPTER
/// </summary>
public class opcUaMonitItemChange : EventArgs
{
#region Private Fields
/// <summary>
/// Monitored Item da notificare
/// </summary>
private readonly MonitoredItem _monitoredItem;
/// <summary>
/// Valore notifica
/// </summary>
private readonly MonitoredItemNotification _notification;
#endregion Private Fields
#region Public Constructors
/// <summary>
@@ -62,34 +43,27 @@ namespace MP.MONO.ADAPTER
}
#endregion Public Properties
#region Private Fields
/// <summary>
/// Monitored Item da notificare
/// </summary>
private readonly MonitoredItem _monitoredItem;
/// <summary>
/// Valore notifica
/// </summary>
private readonly MonitoredItemNotification _notification;
#endregion Private Fields
}
/// <summary>
/// OPC UA Client with examples of basic functionality.
/// </summary>
public class UAClient
{
#region Private Fields
private readonly string currIob;
private readonly Action<IList, IList> m_validateResponse;
private ApplicationConfiguration m_configuration;
private Session m_session;
#endregion Private Fields
#region Protected Fields
protected static Logger lg;
protected static bool isLogVerbose = false;
/// <summary>
/// The user identity to use when creating the session.
/// </summary>
public IUserIdentity CurrUserIdentity { get; set; } = new UserIdentity();
#endregion Protected Fields
#region Public Constructors
/// <summary>
@@ -126,6 +100,11 @@ namespace MP.MONO.ADAPTER
#region Public Properties
/// <summary>
/// The user identity to use when creating the session.
/// </summary>
public IUserIdentity CurrUserIdentity { get; set; } = new UserIdentity();
/// <summary>
/// Gets or sets the server URL.
/// </summary>
@@ -138,105 +117,6 @@ namespace MP.MONO.ADAPTER
#endregion Public Properties
#region Private Methods
/// <summary>
/// Handles the certificate validation event.
/// This event is triggered every time an untrusted certificate is received from the server.
/// </summary>
private void CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
{
bool certificateAccepted = true;
// ****
// Implement a custom logic to decide if the certificate should be
// accepted or not and set certificateAccepted flag accordingly.
// The certificate can be retrieved from the e.Certificate field
// ***
ServiceResult error = e.Error;
while (error != null)
{
lgError($"{error.StatusCode} | {error.Code} | {error.LocalizedText}");
error = error.InnerResult;
}
if (certificateAccepted)
{
lgInfo($"Untrusted Certificate accepted. SubjectName = {e.Certificate.SubjectName}");
}
e.AcceptAll = certificateAccepted;
}
/// <summary>
/// Handle DataChange notifications from Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
// sollevo evento notifica vaziazione MonitoredItem
if (eh_MonItChange != null)
{
eh_MonItChange(this, new opcUaMonitItemChange(monitoredItem, notification));
}
lgTrace($"Notification Received | Variable: {monitoredItem.DisplayName} | Value: {notification.Value}");
}
catch (Exception ex)
{
lgError($"OnMonitoredItemNotification error: {ex.Message}");
}
}
#endregion Private Methods
#region Protected Methods
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgError(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Error(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgInfo(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Info(message);
}
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgDebug(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Info(message);
}
/// <summary>
/// Effettua logging TRACE corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgTrace(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Trace(message);
}
#endregion Protected Methods
#region Public Methods
/// <summary>
@@ -384,14 +264,11 @@ namespace MP.MONO.ADAPTER
try
{
// Define the UA Method to call
// Parent node - Objects\CTT\Methods
// Method node - Objects\CTT\Methods\Add
// Define the UA Method to call Parent node - Objects\CTT\Methods Method node - Objects\CTT\Methods\Add
NodeId objectId = new NodeId("ns=2;s=Methods");
NodeId methodId = new NodeId("ns=2;s=Methods_Add");
// Define the method parameters
// Input argument requires a Float and an UInt32 value
// Define the method parameters Input argument requires a Float and an UInt32 value
object[] inputArguments = new object[] { (float)10.5, (uint)10 };
IList<object> outputArguments = null;
@@ -428,8 +305,8 @@ namespace MP.MONO.ADAPTER
{
lgInfo("Connecting...");
// Get the endpoint by connecting to server's discovery endpoint.
// Try to find the first endopint without security.
// Get the endpoint by connecting to server's discovery endpoint. Try to find
// the first endopint without security.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(ServerUrl, false);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
@@ -572,7 +449,6 @@ namespace MP.MONO.ADAPTER
return answ;
}
/// <summary>
/// Read a SINGLE of nodes value (RAW) from Server
/// </summary>
@@ -589,7 +465,6 @@ namespace MP.MONO.ADAPTER
try
{
#region Read the Value attribute of a node by calling the Session.ReadValue method
try
@@ -613,8 +488,6 @@ namespace MP.MONO.ADAPTER
return answ;
}
/// <summary>
/// Read a list of nodes from Server
/// </summary>
@@ -812,8 +685,7 @@ namespace MP.MONO.ADAPTER
return monItList;
}
/// Write a list of nodes to the Server
/// </summary>
/// Write a list of nodes to the Server </summary>
public void WriteNodes(List<WriteValue> node2Write)
{
if (m_session == null || m_session.Connected == false)
@@ -1001,5 +873,119 @@ namespace MP.MONO.ADAPTER
}
#endregion Public Methods
#region Protected Fields
protected static bool isLogVerbose = false;
protected static Logger lg;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgDebug(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Info(message);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgError(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Error(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgInfo(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Info(message);
}
/// <summary>
/// Effettua logging TRACE corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgTrace(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Trace(message);
}
#endregion Protected Methods
#region Private Fields
private readonly string currIob;
private readonly Action<IList, IList> m_validateResponse;
private ApplicationConfiguration m_configuration;
private Session m_session;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Handles the certificate validation event. This event is triggered every time an
/// untrusted certificate is received from the server.
/// </summary>
private void CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
{
bool certificateAccepted = true;
// **** Implement a custom logic to decide if the certificate should be accepted or not
// and set certificateAccepted flag accordingly. The certificate can be retrieved from
// the e.Certificate field ***
ServiceResult error = e.Error;
while (error != null)
{
lgError($"{error.StatusCode} | {error.Code} | {error.LocalizedText}");
error = error.InnerResult;
}
if (certificateAccepted)
{
lgInfo($"Untrusted Certificate accepted. SubjectName = {e.Certificate.SubjectName}");
}
e.AcceptAll = certificateAccepted;
}
/// <summary>
/// Handle DataChange notifications from Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
// sollevo evento notifica vaziazione MonitoredItem
if (eh_MonItChange != null)
{
eh_MonItChange(this, new opcUaMonitItemChange(monitoredItem, notification));
}
lgTrace($"Notification Received | Variable: {monitoredItem.DisplayName} | Value: {notification.Value}");
}
catch (Exception ex)
{
lgError($"OnMonitoredItemNotification error: {ex.Message}");
}
}
#endregion Private Methods
}
}
}
+3 -3
View File
@@ -1,9 +1,9 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32510.428
# Visual Studio Version 17
VisualStudioVersion = 17.2.32519.379
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MP.MONO.ADAPTER.OPC", "MP.MONO.ADAPTER.OPC\MP.MONO.ADAPTER.OPC.csproj", "{458FD824-7804-4CF9-8C43-463C1F5659DA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.MONO.ADAPTER.OPC", "MP.MONO.ADAPTER.OPC\MP.MONO.ADAPTER.OPC.csproj", "{458FD824-7804-4CF9-8C43-463C1F5659DA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+204
View File
@@ -0,0 +1,204 @@
{
"BrowseFullVal": "ns=1;s=OSAI-PLC",
"BrowseNSIndex": 1,
"BrowseValue": 0,
"keyPartCount": "1:507_2_WORKPIECE QTY",
"keyPartReq": "",
"keyPartId": "",
"keyProgName": "1:502_1_RECIPE NAME",
"keyRunMode": "",
"pingAsPowerOn": true,
//"Identity": {
// "UserName": "CUSTOMER",
// "Passwd": "BLM"
//},
"condWork": [
{
"keyName": "1:1_1_MACHINE STATUS",
"targetValue": "ON"
}
],
"condWorkOpc": {
"checkMode": "AND",
"checkList": [
{
"keyName": "MachineStatus(0)",
"targetValue": "4"
}
]
},
"condPowerOn": {
"checkMode": "OR",
"negateValue": true,
"checkList": [
{
"keyName": "1:1_1_MACHINE STATUS",
"targetValue": "ON"
},
{
"keyName": "1:500_1_STATUS",
"targetValue": "CYCLE"
},
{
"keyName": "1:501_1_MODE",
"targetValue": "AUTO"
}
]
},
"condReady": {
"checkMode": "OR",
"checkList": [
//{
// "keyName": "MachineStatus(0)",
// "targetValue": "3"
//},
//{
// "keyName": "MachineStatus(0)",
// "targetValue": "5"
//}
]
},
"condManual": {
"checkMode": "AND",
"checkList": [
//{
// "keyName": "MachineStatus(0)",
// "targetValue": "8"
//}
]
},
"condEStop": {
"checkMode": "AND",
"checkList": [
//{
// "keyName": "EmergencyState",
// "targetValue": "0"
//}
]
},
"condError": {
"checkMode": "AND",
"checkList": [
//{
// "keyName": "MachineStatus(0)",
// "targetValue": "6"
//}
]
},
"condCountEnabled": {
"checkMode": "AND",
"checkList": []
},
"condWarmUpCoolDown": {
"checkMode": "OR",
"negateValue": true,
"checkList": []
},
"condWarning": {
"checkMode": "AND",
"checkList": [
//{
// "keyName": "MachineStatus(0)",
// "targetValue": "9"
//}
]
},
"condSetup": {
"checkMode": "AND",
"checkList": [
//{
// "keyName": "MachineStatus(0)",
// "targetValue": "7"
//}
]
},
"fluxLogVeto": [],
"itemTranslation": {
"avail": "Machine Available",
"rstat": "Execution Mode",
"mode": "Controller Mode",
"PartName(0)": "Program Name",
"PartDone(0)": "Pezzi Prodotti",
"PartToDo(0)": "Qta Richiesta",
"MachineStatus(0)": "Stato Macchina principale",
"MachineStatus(1)": "Stato Macchina secondario",
"EmergencyState": "Emergenza",
"fdovrd": "PATH FEED OVERRIDE",
"rovrd": "PATH RAPID OVERRIDE"
},
"subscribedItems": [
"ns=1;s=1_1_MACHINE STATUS",
"ns=1;s=1_2_MACHINE COUNTER OFF",
"ns=1;s=2_1_AXES STATUS",
"ns=1;s=2_2_AXES COUNTER OFF",
"ns=1;s=3_1_ALARM NC",
"ns=1;s=3_2_ALARM PLC 1",
"ns=1;s=3_3_ALARM PLC 2",
"ns=1;s=3_4_ALARM PLC 3",
"ns=1;s=3_5_ALARM PLC 4",
"ns=1;s=3_6_ALARM PLC 5",
"ns=1;s=3_7_ALARM PLC 6",
"ns=1;s=3_8_ALARM PLC 7",
"ns=1;s=3_9_ALARM PLC 8",
"ns=1;s=3_10_ALARM PLC 9",
"ns=1;s=3_11_ALARM PLC 10",
"ns=1;s=4_1_TOOL 1 CURRENT LIFE PC",
"ns=1;s=4_2_TOOL 2 CURRENT LIFE PC",
"ns=1;s=4_3_TOOL 3 CURRENT LIFE PC",
"ns=1;s=4_4_TOOL 4 CURRENT LIFE PC",
"ns=1;s=4_5_TOOL 11 CURRENT LIFE PC",
"ns=1;s=4_6_TOOL 12 CURRENT LIFE PC",
"ns=1;s=4_7_TOOL 13 CURRENT LIFE PC",
"ns=1;s=4_8_TOOL 14 CURRENT LIFE PC",
"ns=1;s=4_9_TOOL 21 CURRENT LIFE PC",
"ns=1;s=4_10_TOOL 22 CURRENT LIFE PC",
"ns=1;s=4_11_TOOL 23 CURRENT LIFE PC",
"ns=1;s=4_12_TOOL 24 CURRENT LIFE PC",
"ns=1;s=4_13_TOOL 31 CURRENT LIFE PC",
"ns=1;s=4_14_TOOL 32 CURRENT LIFE PC",
"ns=1;s=4_15_TOOL 33 CURRENT LIFE PC",
"ns=1;s=4_16_TOOL 34 CURRENT LIFE PC",
"ns=1;s=500_1_STATUS",
"ns=1;s=500_2_COUNTER CYCLE",
"ns=1;s=500_3_COUNTER HOLDA",
"ns=1;s=500_4_COUNTER ERROR",
"ns=1;s=500_5_COUNTER PAUSE",
"ns=1;s=501_1_MODE",
"ns=1;s=502_1_RECIPE NAME",
"ns=1;s=502_2_PROGRESS BAR",
"ns=1;s=25_AXIS 1 LOAD PC",
"ns=1;s=26_AXIS 2 LOAD PC",
"ns=1;s=27_AXIS 3 LOAD PC",
"ns=1;s=28_AXIS 4 LOAD PC",
"ns=1;s=29_AXIS 5 LOAD PC",
"ns=1;s=30_AXIS 6 LOAD PC",
"ns=1;s=31_AXIS 7 LOAD PC",
"ns=1;s=32_AXIS 8 LOAD PC",
"ns=1;s=33_AXIS 9 LOAD PC",
"ns=1;s=34_AXIS 10 LOAD PC",
"ns=1;s=35_AXIS 11 LOAD PC",
"ns=1;s=36_AXIS 12 LOAD PC",
"ns=1;s=37_AXIS 13 LOAD PC",
"ns=1;s=38_AXIS 14 LOAD PC",
"ns=1;s=39_AXIS 15 LOAD PC",
"ns=1;s=45_P2 AXIS V SW OVT UPPER",
"ns=1;s=46_P2 AXIS V SW OVT LOWER",
"ns=1;s=47_P2 AXIS V ACTUAL POSITION",
"ns=1;s=48_P3 AXIS W SW OVT UPPER",
"ns=1;s=49_P3 AXIS W SW OVT LOWER",
"ns=1;s=50_P3 AXIS W ACTUAL POSITION",
"ns=1;s=504_PROC 1 P.P. ORDER",
"ns=1;s=505_PROC 1 P.P. PROCESSING",
"ns=1;s=506_PROC 1 P.P. PROCESSING PHASE",
"ns=1;s=510_PROC 1 FEED OVERRIDE",
"ns=1;s=511_PROC 1 FEED PROGRAMMED",
"ns=1;s=512_PROC 1 FEED ACTUAL",
"ns=1;s=513_PROC 1 SPEED OVERRIDE",
"ns=1;s=514_PROC 1 SPEED PROGRAMMED",
"ns=1;s=515_PROC 1 SPEED ACTUAL",
"ns=1;s=516_PROC 1 SPINDLE 1 LOAD PC",
"ns=1;s=517_PROC 1 SPINDLE 2 LOAD PC",
"ns=1;s=518_PROC 1 SPINDLE 3 LOAD PC",
"ns=1;s=519_PROC 1 SPINDLE 4 LOAD PC"
]
}
+722
View File
@@ -0,0 +1,722 @@
using Microsoft.Extensions.Configuration;
using MP.MONO.Core;
using MP.MONO.Core.CONF;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using static MP.MONO.Core.CONF.OpcUaParamConf;
using static MP.MONO.Core.Enums;
namespace MP.MONO.ADAPTER.OPC
{
public class IobGeneric
{
#region Public Fields
/// <summary>
/// valore booleano di check se sia stato AVVIATO l'adapter (Running)
/// </summary>
internal bool adpRunning = false;
/// <summary>
/// Data/ora ultimo avvio adapter
/// </summary>
internal DateTime dtAvvioAdp = DateTime.Now;
/// <summary>
/// Data/ora ultimo spegnimento adapter
/// </summary>
internal DateTime dtStopAdp = DateTime.Now;
/// <summary>
/// dataOra ultimo log periodico...
/// </summary>
internal DateTime lastPeriodicLog;
/// <summary>
/// dataOra ultimo PING inviato verso il PLC...
/// </summary>
internal DateTime lastPING = DateTime.Now.AddHours(-1);
/// <summary>
/// indica se serva refresh parametri e quindi PLC...
/// </summary>
internal bool needRefresh = true;
/// <summary>
/// Struttura memoria PLC x lettura/scrittura da JSON file
/// </summary>
public plcMemMap memMap;
#endregion Public Fields
#region Public Constructors
/// <summary>
/// Avvia generico IOB
/// </summary>
/// <param name="confPath"></param>
/// <param name="config"></param>
public IobGeneric(string confPath, IConfigurationRoot? config)
{
lg = LogManager.GetCurrentClassLogger();
connectionOk = false;
configPath = confPath;
confMan = config;
if (confMan != null)
{
var selection = confMan.GetSection("Endpoint");
if (selection.Exists())
{
// recupero dati endpoint
cIobConf = selection.Get<EndpointData>();
}
}
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Salva verifica stato connessione OK
/// </summary>
/// <returns></returns>
internal virtual bool connectionOk
{
get
{
return _connOk;
}
set
{
_connOk = value;
}
}
/// <summary>
/// Fornisce il valore di flusso e valore in formato valido x messa in coda nel formato
/// dtEve#flusso#valore#cont <paramref name="flusso">Flusso dati</paramref><paramref
/// name="valore">Valore da salvare</paramref>
/// </summary>
public string qEncodeFLog(string flusso, string valore)
{
string answ = "";
// fixme todo !!! fare
// tenere? cambiare? modificare? si dovrebbe cambiare con invio su channel REDIS dei dati ricevuti, eventualmente divisi x canale secondo setup
#if false
try
{
answ = $"{DateTime.Now:yyyyMMddHHmmssfff}#{flusso}#{valore}#{counterULog}";
}
catch
{ }
#endif
return answ;
}
/// <summary>
/// Accumula in coda i valori Flux Log e logga...
/// </summary>
/// <param name="val">VALORE RAW (x display)</param>
/// <param name="encodedVal">VALORE già processato con qEncodeFLog(...)</param>
public void accodaFLog(string val, string encodedVal)
{
// fixme todo !!! fare
// tenere? cambiare? modificare? si dovrebbe cambiare con invio su channel REDIS dei dati ricevuti, eventualmente divisi x canale secondo setup
#if false
// mostro dati variati letti...
displayOtherData(val);
// --> accodo (valore già formattato)!
QueueFLog.Enqueue(encodedVal);
// se abilitato controllo coda FLog (superiore a 0...)
if (maxQueueFLog > 0)
{
// se ho una coda superiore a max ammesso
if (QueueFLog.Count > maxQueueFLog)
{
// elimino valori iniziali fino a tornare al max ammesso...
while (QueueFLog.Count > maxQueueFLog)
{
string currVal = "";
QueueFLog.TryDequeue(out currVal);
lgInfo($"Eliminazione da coda FLog per superamento maxLengh: {currVal}");
}
}
}
// loggo!
lgTrace(string.Format("[QUEUE-FLOG] {0}", encodedVal));
counterFLog++;
if (counterFLog > 9999)
{
counterFLog = 0;
}
#endif
}
/// <summary>
/// indica se ping disabilitato da optPar
/// </summary>
public bool pingDisabled
{
get => getCRB("NO_PING");
}
/// <summary>
/// Verifica SE si debba fare log verboso (verboso + ogni tot letture IN)
/// </summary>
public bool verboseLog
{
get
{
bool answ = false;
int logEvery = getCRI("logEvery");
if (logEvery < 1)
{
logEvery = 10;
}
answ = getCRB("verbose") && (nReadIN % logEvery == 0);
return answ;
}
}
#endregion Public Properties
#region Public Methods
/// <summary>
/// Esecuzione dei task richiesti e pulizia coda richieste eseguite
/// </summary>
/// <param name="task2exe"></param>
public virtual Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
// Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti...
Dictionary<string, string> taskDone = new Dictionary<string, string>();
if (task2exe != null)
{
// controllo se memMap != null...
if (memMap != null)
{
bool taskOk = false;
string taskVal = "";
// cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
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.setArt:
case taskType.setComm:
case taskType.setProg:
case taskType.setPzComm:
// recupero dati da memMap...
if (memMap != null && memMap.mMapWrite != null)
{
if (memMap.mMapWrite.ContainsKey(item.Key))
{
dataConf currMem = memMap.mMapWrite[item.Key];
string addr = currMem.memAddr;
taskVal = $"SET task: {item.Key} --> {item.Value} | mem: {currMem.memAddr} - {currMem.size} byte";
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
memMap.mMapWrite[item.Key].value = item.Value;
}
else
{
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {item.Value}";
}
}
else
{
taskVal = $"NO MemMap found, SET task: {item.Key} --> {item.Value}";
}
// salvo in currProd..
saveProdData(new KeyValuePair<string, string>(item.Key, item.Value));
break;
case taskType.forceResetPzCount:
// reset contapezzi inizio setup
taskOk = resetcontapezziPLC();
taskVal = taskOk ? "RESET PZ COUNT OK" : "PZ RESET DISABLED | NO EXEC";
lgInfo($"Chiamata forceResetPzCount: taskOk: {taskOk} | taskVal: {taskVal}");
break;
case taskType.startSetup:
// reset contapezzi inizio setup
taskOk = resetcontapezziPLC();
taskVal = taskOk ? "RESET: SETUP START" : "PZ RESET DISABLED | NO EXEC";
lgInfo($"Chiamata startSetup: taskOk: {taskOk} | taskVal: {taskVal}");
break;
case taskType.stopSetup:
// reset contapezzi fine setup SE ESPLICITAMENTE IMPOSTATO
if (getCRB("ENABLE_PZ_RESET_stopSetup"))
{
taskOk = resetcontapezziPLC();
}
taskVal = taskOk ? "RESET: SETUP END" : "PZ RESET DISABLED | NO EXEC";
lgInfo($"Chiamata stopSetup: taskOk: {taskOk} | taskVal: {taskVal}");
break;
case taskType.setParameter:
// richiedo da URL i parametri WRITE da popolare
lgInfo("Chiamata setParameter --> processMemWriteRequests");
taskVal = processMemWriteRequests();
// se restituiscce "" faccio altra prova...
if (string.IsNullOrEmpty(taskVal))
{
// i parametri me li aspetto come stringa composta paramName|paramvalue
if (item.Value.Contains("|"))
{
string[] paramsJob = item.Value.Split('|');
taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
}
else
{
taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
}
}
break;
default:
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
break;
}
// aggiungo task!
taskDone.Add(item.Key, taskVal);
}
}
else
{
lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
}
}
return taskDone;
}
/// <summary>
/// Metodo generico di reset contapezzi...
/// </summary>
/// <returns></returns>
public virtual bool resetcontapezziPLC()
{
return false;
}
/// <summary>
/// Salva valori indicati in prod data
/// </summary>
/// <param name="item">Item KVP di cui salvare i dati in currProdData come chiave/valore</param>
/// <returns></returns>
public void saveProdData(KeyValuePair<string, string> item)
{
// imposto i valori...
if (currProdData.ContainsKey(item.Key))
{
currProdData[item.Key] = item.Value;
}
else
{
currProdData.Add(item.Key, item.Value);
}
}
/// <summary>
/// Metodo base connessione...
/// </summary>
public virtual void tryConnect()
{
dtAvvioAdp = DateTime.Now;
}
#endregion Public Methods
#region Internal Properties
/// <summary>
/// COnfiguraizone Endpoint corrente
/// </summary>
internal EndpointData cIobConf { get; set; } = new EndpointData();
internal string configPath { get; set; } = "";
internal IConfigurationRoot? confMan { get; set; } = null!;
#endregion Internal Properties
#region Internal Methods
internal bool getCRB(string keyName)
{
bool answ = false;
answ = confMan.GetValue<bool>(keyName);
return answ;
}
internal int getCRI(string keyName)
{
int answ = -1;
answ = confMan.GetValue<int>(keyName);
return answ;
}
internal string getCRS(string keyName)
{
string answ = "";
answ = confMan.GetValue<string>(keyName);
return answ;
}
#endregion Internal Methods
#region Protected Fields
/// <summary>
/// wrapper di log
/// </summary>
protected static Logger lg;
protected bool _connOk = false;
/// <summary>
/// Dizionario valori impostati x produzione
/// </summary>
protected Dictionary<string, string> currProdData = new Dictionary<string, string>();
/// <summary>
/// Dizionario ultimi valori (double) delle TSVC
/// </summary>
protected Dictionary<string, double> LastTSVC = new Dictionary<string, double>();
/// <summary>
/// Dizionario di VC da trattare come TimeSeries (con conf decodificata + processing successivo...)
/// </summary>
protected Dictionary<string, VCData> TSVC_Data = new Dictionary<string, VCData>();
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Numero letture IN da avvio
/// </summary>
protected int nReadIN { get; set; }
/// <summary>
/// test ping all'indirizzo PLC/CNC impostato nei parametri
/// </summary>
/// <returns></returns>
protected IPStatus testPingMachine
{
get
{
IPStatus answ = IPStatus.Unknown;
// se disabilitato salto...
if (pingDisabled)
{
answ = IPStatus.Success;
}
else
{
IPAddress address;
PingReply reply;
using (Ping pingSender = new Ping())
{
address = IPAddress.Loopback;
int pingMsTimeout = cIobConf.PingMsTimeout;
IPAddress.TryParse(cIobConf.IpAddress, out address);
try
{
// se != null --> uso address...
if (address != null)
{
reply = pingSender.Send(address, pingMsTimeout);
}
else
{
reply = pingSender.Send(cIobConf.IpAddress, pingMsTimeout);
}
}
catch
{
reply = pingSender.Send(IPAddress.Loopback, pingMsTimeout);
}
answ = reply.Status;
}
}
return answ;
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Stringa raw dei parametri da scrivere...
/// </summary>
/// <returns></returns>
protected string getParams2write()
{
string answ = "";
// recuperare da una apposita area REDIS...
#if false
string url2call = $"{urlGetParams2Write}";
if (verboseLog)
{
lgInfo("chiamata URL " + url2call);
}
answ = utils.callUrlNow(url2call);
// se vuoto faccio seconda prova...
if (string.IsNullOrEmpty(answ))
{
answ = utils.callUrlNow(url2call);
}
#endif
return answ;
}
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgDebug(string message)
{
lg.Debug(message);
}
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgDebug(string message, params object[] args)
{
lg.Debug(message, args);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgError(string message)
{
lg.Error(message);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgError(string message, params object[] args)
{
lg.Error(message, args);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgError(Exception exception, string message, params object[] args)
{
lg.Error(exception, message, args);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgFatal(string message)
{
lg.Fatal(message);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgFatal(string message, params object[] args)
{
lg.Fatal(message, args);
}
/// <summary>
/// Effettua logging FATAL corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgFatal(Exception exception, string message, params object[] args)
{
lg.Fatal(exception, message, args);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgInfo(string message)
{
lg.Info(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgInfo(string message, params object[] args)
{
lg.Info(message, args);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgTrace(string message)
{
lg.Trace(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
protected void lgTrace(string message, params object[] args)
{
lg.Trace(message, args);
}
/// <summary>
/// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
/// </summary>
/// <param name="updatedPar"></param>
protected virtual void plcWriteParams(ref List<objItem> updatedPar)
{
// non faccio nulla di base...
}
/// <summary>
/// Processa le richieste di scrittura memoria
/// </summary>
/// <returns></returns>
protected string processMemWriteRequests()
{
string answ = "";
// li salvo nei parametri in memoria locale (ogni adapter DOVREBBE salvare POI sul VERO PLC)
List<objItem> writeList = new List<objItem>();
List<objItem> updatedPar = new List<objItem>();
// recupero elenco delle cose da fare
string resp = getParams2write();
if (!string.IsNullOrEmpty(resp))
{
try
{
writeList = JsonConvert.DeserializeObject<List<objItem>>(resp);
// se ho da fare chiamo esecuzione..
if (writeList.Count > 0)
{
foreach (var item in writeList)
{
// scrivo in memoria
if (memMap.mMapWrite.ContainsKey(item.uid))
{
memMap.mMapWrite[item.uid].value = item.reqValue;
// accodo in stringa taskVal...
answ += $" | Parameter {item.uid} --> {item.reqValue}";
// sistemo valori
item.value = item.reqValue;
lgInfo($"Richiesta update parametro {item.uid} | actVal = {item.value} | reqVal = {item.reqValue}");
item.reqValue = "";
// salvo in lista da ritrasmettere
updatedPar.Add(item);
}
else
{
answ += $" | Error: parameter {item.uid} not found";
}
}
// richiamo scrittura parametri su PLC
plcWriteParams(ref updatedPar);
// invio su cloud parametri!
string rawData = JsonConvert.SerializeObject(updatedPar);
lgInfo("Notifica a server scrittura parametri");
#if false
utils.callUrl($"{urlUpdateWriteParams}", rawData);
#endif
}
}
catch (Exception exc)
{
lgError($"Eccezione in processMemWriteRequests:{Environment.NewLine}{exc}");
}
}
else
{
lgError("Non è stata ricevuta risposta x task da eseguire");
}
return answ;
}
/// <summary>
/// setup parametri da file di conf
/// </summary>
protected void setupMemMap()
{
lgInfo($"setupMemMap | trovati {memMap.mMapRead.Count} parametri Read (TSVC)");
lgInfo($"setupMemMap | trovati {memMap.mMapWrite.Count} parametri Write");
if (getCRB("verbose"))
{
string rawMemConf = JsonConvert.SerializeObject(memMap, Formatting.Indented);
lgDebug($"setupMemMap | configurazione memoria R/W:{Environment.NewLine}{rawMemConf}");
}
// se ho variabili read --> genero dati TSVC...
if (memMap.mMapRead.Count > 0)
{
TSVC_Data.Clear();
LastTSVC.Clear();
VCData currConf;
int periodo = 0;
VC_func funz = VC_func.POINT;
// accodo nella conf...
foreach (var item in memMap.mMapRead)
{
funz = item.Value.func;
periodo = item.Value.period;
currConf = new VCData()
{
Funzione = funz,
Period = periodo,
DTStart = DateTime.Now.AddHours(-1),
dataArray = new List<double>()
};
TSVC_Data.Add(item.Key, currConf);
}
// documento...
foreach (var item in TSVC_Data)
{
lgTrace($"TSVC: {item.Key} | periodo: {item.Value.Period} | funz: {item.Value.Funzione}");
// salvo i valori PREC...
LastTSVC.Add(item.Key, 0);
}
}
}
#endregion Protected Methods
}
}
File diff suppressed because it is too large Load Diff
+23 -2
View File
@@ -7,13 +7,23 @@
<PackageId>MP.MONO.ADAPTER.OPC</PackageId>
<Company>EgalWare</Company>
<Description>OPC UA Console Client</Description>
<Copyright>Copyright © 2020+ EgalWare</Copyright>
<Copyright>Copyright 2020+ EgalWare</Copyright>
<RootNamespace>MP.MONO.ADAPTER.OPC</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Remove="UAClient.old.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.4.368.58" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.4.368.58" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.4.368.58" />
@@ -24,9 +34,20 @@
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="StackExchange.Redis" Version="2.5.61" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MP.MONO.Core\MP.MONO.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Quickstarts.ReferenceClient.Config.xml">
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="CONF\MpMonoAdapterClient.Config.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="CONF\Multiax.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
+78
View File
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MP.MONO.ADAPTER.OPC
{
/// <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 (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>
/// Valore Registrato in formato byte array
/// </summary>
public byte[] rawByte { get; set; } = new byte[1];
/// <summary>
/// Timestamp data-ora evento registrato
/// </summary>
public DateTime valueTimestamp { get; set; } = DateTime.Now;
#endregion Public Properties
}
}
+282 -35
View File
@@ -1,32 +1,3 @@
/* ========================================================================
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Globalization;
using System.IO;
@@ -35,6 +6,15 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Configuration;
using Microsoft.Extensions.Configuration;
using MP.MONO.ADAPTER;
using MP.MONO.Core;
using MP.MONO.Core.CONF;
using MP.MONO.Core.DTO;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System.Collections.Generic;
namespace MP.MONO.ADAPTER.OPC
{
@@ -43,22 +23,85 @@ namespace MP.MONO.ADAPTER.OPC
/// </summary>
public static class Program
{
private static string redisConf = "";
private static string confPath = "";
private static string lineSep = "---------------------------------------------";
private static Logger Log = LogManager.GetCurrentClassLogger();
private static bool verboseLog = false;
private static bool logWriting = false;
/// <summary>
/// Redis channel manager
/// </summary>
private static ISubscriber sub { get; set; }
/// <summary>
/// Redis DB
/// </summary>
private static IDatabase? redisDb { get; set; }
private static Dictionary<string, int> LogSimulator = new Dictionary<string, int>();
private static Dictionary<string, DateTime> LastSend = new Dictionary<string, DateTime>();
private static DateTime lastLog = DateTime.Now.AddMinutes(-1);
/// <summary>
/// Main entry point.
/// </summary>
public static async Task Main(string[] args)
{
TextWriter output = Console.Out;
output.WriteLine("OPC UA Console Reference Client");
output.WriteLine("Egalware | MP.MONO.ADAPTER | OPC UA Console Client");
output.WriteLine("OPC UA library: {0} @ {1} -- {2}",
Utils.GetAssemblyBuildNumber(),
Utils.GetAssemblyTimestamp().ToString("G", CultureInfo.InvariantCulture),
Utils.GetAssemblySoftwareVersion());
// init parte config, vedere https://blog.hildenco.com/2020/05/configuration-in-net-core-console.html
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var builder = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json", true, true)
.AddJsonFile($"appsettings.{env}.json", true, true)
.AddEnvironmentVariables();
var config = builder.Build();
// imposto variabili di base
redisConf = config.GetConnectionString("Redis");
confPath = Path.Combine(Directory.GetCurrentDirectory(), "conf");
Random rand = new Random();
List<MachineStatus>? statusList = new List<MachineStatus>();
List<MachineMode>? modeList = new List<MachineMode>();
// fix numero minimo dei thread pool x evitare collasso chiamate redis
ThreadPool.SetMinThreads(10, 10);
logInfo(lineSep, true, true);
logInfo($"Starting Machine ADAPTER", true, true);
logInfo($"Redis server param: {redisConf.Substring(0, 20)}...", false, true);
logInfo(lineSep, true, true);
logInfo("", true, true);
// Setup REDIS
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisConf);
sub = redis.GetSubscriber();
redisDb = redis.GetDatabase();
// salvo configurazioni in redis
setupConf();
// avvio il vero e proprio programma di comunicaizone OPC-UA
var currIob = new IobOpcUa(confPath, config);
logInfo($"Started IOB at path {confPath}", true, true);
#if false
// The application name and config file names
var applicationName = "ConsoleReferenceClient";
var configSectionName = "Quickstarts.ReferenceClient";
var applicationName = "MP.MONO.ADAPTER.OPC";
var configSectionName = "MpMonoAdapterOpc";
var usage = $"Usage: dotnet {applicationName}.dll [OPTIONS]";
// command line options
@@ -102,7 +145,8 @@ namespace MP.MONO.ADAPTER.OPC
// Define the UA Client application
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(output);
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(password);
ApplicationInstance application = new ApplicationInstance {
ApplicationInstance application = new ApplicationInstance
{
ApplicationName = applicationName,
ApplicationType = ApplicationType.Client,
ConfigSectionName = configSectionName,
@@ -113,7 +157,7 @@ namespace MP.MONO.ADAPTER.OPC
var config = await application.LoadApplicationConfiguration(silent: false);
// setup the logging
ConsoleUtils.ConfigureLogging(config, applicationName, logConsole, LogLevel.Information);
ConsoleUtils.ConfigureLogging(config, applicationName, logConsole, Microsoft.Extensions.Logging.LogLevel.Information);
// delete old certificate
if (renewCertificate)
@@ -147,7 +191,8 @@ namespace MP.MONO.ADAPTER.OPC
}
// create the UA Client object and connect to configured server.
UAClient uaClient = new UAClient(application.ApplicationConfiguration, output, ClientBase.ValidateResponse) {
UAClient uaClient = new UAClient(application.ApplicationConfiguration, output, ClientBase.ValidateResponse)
{
AutoAccept = autoAccept
};
@@ -180,6 +225,208 @@ namespace MP.MONO.ADAPTER.OPC
catch (Exception ex)
{
output.WriteLine(ex.Message);
}
#endif
logInfo("Running - press CTRL-C to stop ADAPTER", false, true);
logInfo("", false, true);
// Ciclo infinito x attesa chiusura con CTRL-C
do
{
// se non fosse connesso... riprovo la connessione...
if (!currIob.connectionOk)
{
currIob.tryConnect();
}
// attesa...
Thread.Sleep(100);
} while (true);
}
/// <summary>
/// verifica esistenza file oppure lo crea...
/// </summary>
private static void checkFilePresent(string filePath)
{
// verific presenza file log...
if (!File.Exists(filePath))
{
File.WriteAllText(filePath, $"{filePath} created!");
}
}
/// <summary>
/// Setup e salvataggio redis delle conf (es modi/stati)
/// </summary>
private static void setupConf()
{
#if false
// leggo e salvo conf stati
string fullPath = Path.Combine(confPath, "StatusList.json");
if (File.Exists(fullPath))
{
var rawData = File.ReadAllText(fullPath);
if (!string.IsNullOrEmpty(rawData))
{
List<MachineStatus>? statusList = JsonConvert.DeserializeObject<List<MachineStatus>>(rawData);
// salvo in redis!
redisDb.StringSetAsync(Constants.STATUS_CONF_KEY, JsonConvert.SerializeObject(statusList));
}
}
// leggo e salvo conf modi
fullPath = Path.Combine(confPath, "ModeList.json");
if (File.Exists(fullPath))
{
var rawData = File.ReadAllText(fullPath);
if (!string.IsNullOrEmpty(rawData))
{
var localObj = JsonConvert.DeserializeObject<List<MachineMode>>(rawData);
// salvo in redis!
redisDb.StringSetAsync(Constants.MODE_CONF_KEY, JsonConvert.SerializeObject(localObj));
}
}
// leggo e salvo conf allarmi
fullPath = Path.Combine(confPath, "AlarmList.json");
if (File.Exists(fullPath))
{
var rawData = File.ReadAllText(fullPath);
if (!string.IsNullOrEmpty(rawData))
{
var localObj = JsonConvert.DeserializeObject<List<BaseAlarmConf>>(rawData);
if (localObj != null)
{
// sistemo allarmi
foreach (var item in localObj)
{
item.setupData();
// loggo
logInfo($"Decodifica aree alarmMap: {item.description} | {item.memAddr} x {item.size} byte | {item.messages.Count} messaggi allarme", true, true);
}
}
// salvo in redis!
redisDb.StringSetAsync(Constants.ALARMS_CONF_KEY, JsonConvert.SerializeObject(localObj));
}
}
// leggo e salvo conf parametri
fullPath = Path.Combine(confPath, "ParamList.json");
if (File.Exists(fullPath))
{
var rawData = File.ReadAllText(fullPath);
if (!string.IsNullOrEmpty(rawData))
{
var localObj = JsonConvert.DeserializeObject<List<DisplayDataDTO>>(rawData);
// salvo in redis!
redisDb.StringSetAsync(Constants.PARAMS_CONF_KEY, JsonConvert.SerializeObject(localObj));
}
}
#endif
ConfigManager configManager = new ConfigManager(redisConf, confPath);
_ = configManager.getAlarmsConf();
_ = configManager.getMachineModeConf();
_ = configManager.getMachineStatusConf();
_ = configManager.getParamsConf();
}
/// <summary>
/// Effettua log INFO su file e se richiesto su console
/// </summary>
private static void logInfo(string msg, bool log2file = true, bool log2console = false)
{
if (log2console)
{
Console.WriteLine(msg);
}
if (log2file)
{
Log.Info(msg);
}
}
/// <summary>
/// Effettua log ERROR su file e se richiesto su console
/// </summary>
private static void logError(string msg, bool log2file = true, bool log2console = false)
{
if (log2console)
{
Console.WriteLine(msg);
}
if (log2file)
{
Log.Error(msg);
}
}
private static void saveAndSendMessage(string memKey, string value, string notifyChannel, string message)
{
// effettuo la scrittura nell'area di memoria indicata SE passato intervallo minimo
bool doSend = true;
if (LastSend.ContainsKey(memKey))
{
if (DateTime.Now.Subtract(LastSend[memKey]).TotalSeconds < 60)
{
doSend = false;
}
}
else
{
LastSend.Add(memKey, DateTime.Now);
}
if (doSend)
{
redisDb.StringSetAsync(memKey, value);
LastSend[memKey] = DateTime.Now;
logInfo($"Redis Cache Key: {memKey}");
}
//redisDb.SetAdd(memKey, value);
// invio notifica tramite il canale richiesto
sub.Publish(notifyChannel, message);
if (verboseLog)
{
logInfo($"[{notifyChannel}] key: {memKey} | val: {value} | message: {message}");
}
else
{
try
{
if (!logWriting)
{
if (LogSimulator.ContainsKey(notifyChannel))
{
LogSimulator[notifyChannel]++;
}
else
{
LogSimulator.Add(notifyChannel, 1);
}
logWriting = true;
// vedo se loggare...
DateTime adesso = DateTime.Now;
if (adesso.Subtract(lastLog).TotalSeconds > 15)
{
lastLog = adesso;
logInfo(lineSep);
// lavoro su copia...
var LogSimulatorCopy = new Dictionary<string, int>(LogSimulator);
foreach (var item in LogSimulatorCopy)
{
logInfo($"Redis mQueue {item.Key,-20}{item.Value,12}");
}
logInfo(lineSep);
}
logWriting = false;
}
}
catch (Exception ex)
{
logError($"ERROR{Environment.NewLine}{ex}");
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
@@ -11,7 +11,7 @@
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
@@ -27,22 +27,22 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Client;
namespace MP.MONO.ADAPTER.OPC
{
/// <summary>
/// OPC UA Client with examples of basic functionality.
/// </summary>
class UAClient
internal class UAClient
{
#region Constructors
/// <summary>
/// Initializes a new instance of the UAClient class.
/// </summary>
@@ -53,21 +53,98 @@ namespace MP.MONO.ADAPTER.OPC
m_configuration = configuration;
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
}
#endregion
#region Public Properties
/// <summary>
/// Auto accept untrusted certificates.
/// </summary>
public bool AutoAccept { get; set; } = false;
/// <summary>
/// Gets the client session.
/// </summary>
public Session Session => m_session;
/// <summary>
/// Auto accept untrusted certificates.
/// Browse Server nodes
/// </summary>
public bool AutoAccept { get; set; } = false;
#endregion
public void Browse()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
NodeId nodeToBrowse = ObjectIds.Server;
// Call Browse service
m_output.WriteLine("Browsing {0} node...", nodeToBrowse);
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
m_output.WriteLine("Browse returned {0} results:", browseResults.Count);
foreach (ReferenceDescription result in browseResults)
{
m_output.WriteLine(" DisplayName = {0}, NodeClass = {1}", result.DisplayName.Text, result.NodeClass);
}
}
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Browse Error : {ex.Message}.");
}
}
/// <summary>
/// Call UA method
/// </summary>
public void CallMethod()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Define the UA Method to call Parent node - Objects\CTT\Methods Method node - Objects\CTT\Methods\Add
NodeId objectId = new NodeId("ns=2;s=Methods");
NodeId methodId = new NodeId("ns=2;s=Methods_Add");
// Define the method parameters Input argument requires a Float and an UInt32 value
object[] inputArguments = new object[] { (float)10.5, (uint)10 };
IList<object> outputArguments = null;
// Invoke Call service
m_output.WriteLine("Calling UAMethod for node {0} ...", methodId);
outputArguments = m_session.Call(objectId, methodId, inputArguments);
// Display results
m_output.WriteLine("Method call returned {0} output argument(s):", outputArguments.Count);
foreach (var outputArgument in outputArguments)
{
m_output.WriteLine(" OutputValue = {0}", outputArgument.ToString());
}
}
catch (Exception ex)
{
m_output.WriteLine("Method call error: {0}", ex.Message);
}
}
#region Public Methods
/// <summary>
/// Creates a session with the UA server
/// </summary>
@@ -85,8 +162,8 @@ namespace MP.MONO.ADAPTER.OPC
{
m_output.WriteLine("Connecting to... {0}", serverUrl);
// Get the endpoint by connecting to server's discovery endpoint.
// Try to find the first endopint with security.
// Get the endpoint by connecting to server's discovery endpoint. Try to find
// the first endopint with security.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, serverUrl, true);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
@@ -166,7 +243,6 @@ namespace MP.MONO.ADAPTER.OPC
try
{
#region Read a node by calling the Read Service
// build a list of nodes to be read
ReadValueIdCollection nodesToRead = new ReadValueIdCollection()
@@ -199,15 +275,13 @@ namespace MP.MONO.ADAPTER.OPC
{
m_output.WriteLine("Read Value = {0} , StatusCode = {1}", result.Value, result.StatusCode);
}
#endregion
#region Read the Value attribute of a node by calling the Session.ReadValue method
// Read Server NamespaceArray
m_output.WriteLine("Reading Value of NamespaceArray node...");
DataValue namespaceArray = m_session.ReadValue(Variables.Server_NamespaceArray);
// Display the result
m_output.WriteLine($"NamespaceArray Value = {namespaceArray}");
#endregion
}
catch (Exception ex)
{
@@ -216,159 +290,6 @@ namespace MP.MONO.ADAPTER.OPC
}
}
/// <summary>
/// Write a list of nodes to the Server
/// </summary>
public void WriteNodes()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Write the configured nodes
WriteValueCollection nodesToWrite = new WriteValueCollection();
// Int32 Node - Objects\CTT\Scalar\Scalar_Static\Int32
WriteValue intWriteVal = new WriteValue();
intWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Int32");
intWriteVal.AttributeId = Attributes.Value;
intWriteVal.Value = new DataValue();
intWriteVal.Value.Value = (int)100;
nodesToWrite.Add(intWriteVal);
// Float Node - Objects\CTT\Scalar\Scalar_Static\Float
WriteValue floatWriteVal = new WriteValue();
floatWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Float");
floatWriteVal.AttributeId = Attributes.Value;
floatWriteVal.Value = new DataValue();
floatWriteVal.Value.Value = (float)100.5;
nodesToWrite.Add(floatWriteVal);
// String Node - Objects\CTT\Scalar\Scalar_Static\String
WriteValue stringWriteVal = new WriteValue();
stringWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_String");
stringWriteVal.AttributeId = Attributes.Value;
stringWriteVal.Value = new DataValue();
stringWriteVal.Value.Value = "String Test";
nodesToWrite.Add(stringWriteVal);
// Write the node attributes
StatusCodeCollection results = null;
DiagnosticInfoCollection diagnosticInfos;
m_output.WriteLine("Writing nodes...");
// Call Write Service
m_session.Write(null,
nodesToWrite,
out results,
out diagnosticInfos);
// Validate the response
m_validateResponse(results, nodesToWrite);
// Display the results.
m_output.WriteLine("Write Results :");
foreach (StatusCode writeResult in results)
{
m_output.WriteLine(" {0}", writeResult);
}
}
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Write Nodes Error : {ex.Message}.");
}
}
/// <summary>
/// Browse Server nodes
/// </summary>
public void Browse()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
NodeId nodeToBrowse = ObjectIds.Server;
// Call Browse service
m_output.WriteLine("Browsing {0} node...", nodeToBrowse);
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
m_output.WriteLine("Browse returned {0} results:", browseResults.Count);
foreach (ReferenceDescription result in browseResults)
{
m_output.WriteLine(" DisplayName = {0}, NodeClass = {1}", result.DisplayName.Text, result.NodeClass);
}
}
catch (Exception ex)
{
// Log Error
m_output.WriteLine($"Browse Error : {ex.Message}.");
}
}
/// <summary>
/// Call UA method
/// </summary>
public void CallMethod()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Define the UA Method to call
// Parent node - Objects\CTT\Methods
// Method node - Objects\CTT\Methods\Add
NodeId objectId = new NodeId("ns=2;s=Methods");
NodeId methodId = new NodeId("ns=2;s=Methods_Add");
// Define the method parameters
// Input argument requires a Float and an UInt32 value
object[] inputArguments = new object[] { (float)10.5, (uint)10 };
IList<object> outputArguments = null;
// Invoke Call service
m_output.WriteLine("Calling UAMethod for node {0} ...", methodId);
outputArguments = m_session.Call(objectId, methodId, inputArguments);
// Display results
m_output.WriteLine("Method call returned {0} output argument(s):", outputArguments.Count);
foreach (var outputArgument in outputArguments)
{
m_output.WriteLine(" OutputValue = {0}", outputArgument.ToString());
}
}
catch (Exception ex)
{
m_output.WriteLine("Method call error: {0}", ex.Message);
}
}
/// <summary>
/// Create Subscription and MonitoredItems for DataChanges
/// </summary>
@@ -438,39 +359,95 @@ namespace MP.MONO.ADAPTER.OPC
m_output.WriteLine("Subscribe error: {0}", ex.Message);
}
}
#endregion
#region Private Methods
/// <summary>
/// Handle DataChange notifications from Server
/// Write a list of nodes to the Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
public void WriteNodes()
{
if (m_session == null || m_session.Connected == false)
{
m_output.WriteLine("Session not connected!");
return;
}
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
m_output.WriteLine("Notification Received for Variable \"{0}\" and Value = {1}.", monitoredItem.DisplayName, notification.Value);
// Write the configured nodes
WriteValueCollection nodesToWrite = new WriteValueCollection();
// Int32 Node - Objects\CTT\Scalar\Scalar_Static\Int32
WriteValue intWriteVal = new WriteValue();
intWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Int32");
intWriteVal.AttributeId = Attributes.Value;
intWriteVal.Value = new DataValue();
intWriteVal.Value.Value = (int)100;
nodesToWrite.Add(intWriteVal);
// Float Node - Objects\CTT\Scalar\Scalar_Static\Float
WriteValue floatWriteVal = new WriteValue();
floatWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_Float");
floatWriteVal.AttributeId = Attributes.Value;
floatWriteVal.Value = new DataValue();
floatWriteVal.Value.Value = (float)100.5;
nodesToWrite.Add(floatWriteVal);
// String Node - Objects\CTT\Scalar\Scalar_Static\String
WriteValue stringWriteVal = new WriteValue();
stringWriteVal.NodeId = new NodeId("ns=2;s=Scalar_Static_String");
stringWriteVal.AttributeId = Attributes.Value;
stringWriteVal.Value = new DataValue();
stringWriteVal.Value.Value = "String Test";
nodesToWrite.Add(stringWriteVal);
// Write the node attributes
StatusCodeCollection results = null;
DiagnosticInfoCollection diagnosticInfos;
m_output.WriteLine("Writing nodes...");
// Call Write Service
m_session.Write(null,
nodesToWrite,
out results,
out diagnosticInfos);
// Validate the response
m_validateResponse(results, nodesToWrite);
// Display the results.
m_output.WriteLine("Write Results :");
foreach (StatusCode writeResult in results)
{
m_output.WriteLine(" {0}", writeResult);
}
}
catch (Exception ex)
{
m_output.WriteLine("OnMonitoredItemNotification error: {0}", ex.Message);
// Log Error
m_output.WriteLine($"Write Nodes Error : {ex.Message}.");
}
}
private readonly TextWriter m_output;
private readonly Action<IList, IList> m_validateResponse;
private ApplicationConfiguration m_configuration;
private Session m_session;
/// <summary>
/// Handles the certificate validation event.
/// This event is triggered every time an untrusted certificate is received from the server.
/// Handles the certificate validation event. This event is triggered every time an
/// untrusted certificate is received from the server.
/// </summary>
private void CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
{
bool certificateAccepted = false;
// ****
// Implement a custom logic to decide if the certificate should be
// accepted or not and set certificateAccepted flag accordingly.
// The certificate can be retrieved from the e.Certificate field
// ***
// **** Implement a custom logic to decide if the certificate should be accepted or not
// and set certificateAccepted flag accordingly. The certificate can be retrieved from
// the e.Certificate field ***
ServiceResult error = e.Error;
m_output.WriteLine(error);
@@ -489,13 +466,23 @@ namespace MP.MONO.ADAPTER.OPC
m_output.WriteLine("Untrusted Certificate rejected. Subject = {0}", e.Certificate.Subject);
}
}
#endregion
#region Private Fields
private ApplicationConfiguration m_configuration;
private Session m_session;
private readonly TextWriter m_output;
private readonly Action<IList, IList> m_validateResponse;
#endregion
/// <summary>
/// Handle DataChange notifications from Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
m_output.WriteLine("Notification Received for Variable \"{0}\" and Value = {1}.", monitoredItem.DisplayName, notification.Value);
}
catch (Exception ex)
{
m_output.WriteLine("OnMonitoredItemNotification error: {0}", ex.Message);
}
}
}
}
}
+49
View File
@@ -0,0 +1,49 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Redis": "nkcredis.steamware.net:6379,DefaultDatabase=7,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false,password=nkc.password",
"AuthConnection": "Server=localhost;port=3306;database=GWMS;user=GWMS;pwd=GWMS_secret_pwd;sslmode=None;",
"DefaultConnection": "Server=localhost;port=3306;database=MAPO.MONO;user=GWMS;pwd=GWMS_secret_pwd;sslmode=None;",
"AdminConnection": "Server=localhost;port=3306;database=MAPO.MONO;user=root;pwd=Egalware_24068!;sslmode=None;",
"MP.MONO.Data": "Server=localhost;port=3306;database=MAPO.MONO;user=GWMS;pwd=GWMS_secret_pwd;sslmode=None;"
},
"DbConfig": {
"Server": "10.74.82.230",
"nKey": "MONO",
"sKey": "Calcium0xide-CaO"
},
"MachineId": 1,
"maxRecord": 15,
"ExternalProviders": {
"MailKit": {
"SMTP": {
"Address": "smtp.gmail.com",
"Port": "465",
"Account": "steamwarebot@gmail.com",
"Password": "drmfsls16",
"SenderEmail": "steamwarebot@gmail.com",
"SenderName": "Steamware Email BOT"
}
}
},
"Endpoint": {
"IpAddress": "192.168.0.2",
"Port": "4840",
"PingMsTimeout": 1500
},
"ENABLE_DATA_FILTER": true,
"ENABLE_CLI_RESTART": true,
"logEvery": 10,
"OPC_PARAM_CONF": "MULTIAX.json",
"pingTestSec": 5,
"verbose": false,
"disconMaxSec": 30,
"verboseLogTOut": 60,
"waitRecMSec": 60000
}
@@ -1,45 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(AppTargetFrameWorks)</TargetFrameworks>
<AssemblyName>MP.MONO.ADAPTER.OPC</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>MP.MONO.ADAPTER.OPC</PackageId>
<Company>EgalWare</Company>
<Description>OPC UA Console Client</Description>
<Copyright>Copyright © 2020+ EgalWare</Copyright>
<RootNamespace>MP.MONO.ADAPTER.OPC</RootNamespace>
</PropertyGroup>
<ItemGroup Condition=" '$(NoHttps)' != 'true' ">
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ConsoleReferenceServer\ConsoleUtils.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Client\Opc.Ua.Client.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="MP.MONO.ADAPTER.OPC.Config.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -1,89 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
>
<ApplicationName>Quickstart Console Reference Client</ApplicationName>
<ApplicationUri>urn:localhost:UA:Quickstarts:ReferenceClient</ApplicationUri>
<ProductUri>uri:opcfoundation.org:Quickstarts:ReferenceClient</ProductUri>
<ApplicationType>Client_1</ApplicationType>
<SecurityConfiguration>
<!-- Where the application instance certificate is stored (MachineDefault) -->
<ApplicationCertificate>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/own</StorePath>
<SubjectName>CN=Console Reference Client, C=US, S=Arizona, O=OPC Foundation, DC=localhost</SubjectName>
</ApplicationCertificate>
<!-- Where the issuer certificate are stored (certificate authorities) -->
<TrustedIssuerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/issuer</StorePath>
</TrustedIssuerCertificates>
<!-- Where the trust list is stored -->
<TrustedPeerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/trusted</StorePath>
</TrustedPeerCertificates>
<!-- The directory used to store invalid certficates for later review by the administrator. -->
<RejectedCertificateStore>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/rejected</StorePath>
</RejectedCertificateStore>
<!-- WARNING: The following setting (to automatically accept untrusted certificates) should be used
for easy debugging purposes ONLY and turned off for production deployments! -->
<AutoAcceptUntrustedCertificates>false</AutoAcceptUntrustedCertificates>
</SecurityConfiguration>
<TransportConfigurations></TransportConfigurations>
<TransportQuotas>
<OperationTimeout>600000</OperationTimeout>
<MaxStringLength>1048576</MaxStringLength>
<MaxByteStringLength>1048576</MaxByteStringLength>
<MaxArrayLength>65535</MaxArrayLength>
<MaxMessageSize>4194304</MaxMessageSize>
<MaxBufferSize>65535</MaxBufferSize>
<ChannelLifetime>300000</ChannelLifetime>
<SecurityTokenLifetime>3600000</SecurityTokenLifetime>
</TransportQuotas>
<ClientConfiguration>
<DefaultSessionTimeout>60000</DefaultSessionTimeout>
<WellKnownDiscoveryUrls>
<ua:String>opc.tcp://{0}:4840</ua:String>
<ua:String>http://{0}:52601/UADiscovery</ua:String>
<ua:String>http://{0}/UADiscovery/Default.svc</ua:String>
</WellKnownDiscoveryUrls>
<DiscoveryServers></DiscoveryServers>
<MinSubscriptionLifetime>10000</MinSubscriptionLifetime>
</ClientConfiguration>
<Extensions>
</Extensions>
<TraceConfiguration>
<OutputFilePath>%LocalApplicationData%/OPC Foundation/Logs/Quickstarts.ReferenceClient.log.txt</OutputFilePath>
<DeleteOnLoad>true</DeleteOnLoad>
<!-- Show Only Errors -->
<!-- <TraceMasks>1</TraceMasks> -->
<!-- Show Only Security and Errors -->
<!-- <TraceMasks>513</TraceMasks> -->
<!-- Show Only Security, Errors and Trace -->
<!-- <TraceMasks>515</TraceMasks> -->
<!-- Show Only Security, COM Calls, Errors and Trace -->
<!-- <TraceMasks>771</TraceMasks> -->
<!-- Show Only Security, Service Calls, Errors and Trace -->
<!-- <TraceMasks>523</TraceMasks> -->
<!-- Show Only Security, ServiceResultExceptions, Errors and Trace -->
<!-- <TraceMasks>519</TraceMasks> -->
</TraceConfiguration>
</ApplicationConfiguration>
@@ -1,45 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(AppTargetFrameWorks)</TargetFrameworks>
<AssemblyName>MP.MONO.ADAPTER.OPC</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>MP.MONO.ADAPTER.OPC</PackageId>
<Company>EgalWare</Company>
<Description>OPC UA Console Client</Description>
<Copyright>Copyright © 2020+ EgalWare</Copyright>
<RootNamespace>MP.MONO.ADAPTER.OPC</RootNamespace>
</PropertyGroup>
<ItemGroup Condition=" '$(NoHttps)' != 'true' ">
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ConsoleReferenceServer\ConsoleUtils.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Client\Opc.Ua.Client.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="MP.MONO.ADAPTER.OPC.Config.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
-186
View File
@@ -1,186 +0,0 @@
/* ========================================================================
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Configuration;
namespace MP.MONO.ADAPTER.OPC
{
/// <summary>
/// The program.
/// </summary>
public static class Program
{
/// <summary>
/// Main entry point.
/// </summary>
public static async Task Main(string[] args)
{
TextWriter output = Console.Out;
output.WriteLine("OPC UA Console Reference Client");
output.WriteLine("OPC UA library: {0} @ {1} -- {2}",
Utils.GetAssemblyBuildNumber(),
Utils.GetAssemblyTimestamp().ToString("G", CultureInfo.InvariantCulture),
Utils.GetAssemblySoftwareVersion());
// The application name and config file names
var applicationName = "ConsoleReferenceClient";
var configSectionName = "Quickstarts.ReferenceClient";
var usage = $"Usage: dotnet {applicationName}.dll [OPTIONS]";
// command line options
bool showHelp = false;
bool autoAccept = false;
bool logConsole = false;
bool appLog = false;
bool renewCertificate = false;
string password = null;
int timeout = Timeout.Infinite;
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
usage,
{ "h|help", "show this message and exit", h => showHelp = h != null },
{ "a|autoaccept", "auto accept certificates (for testing only)", a => autoAccept = a != null },
{ "c|console", "log to console", c => logConsole = c != null },
{ "l|log", "log app output", c => appLog = c != null },
{ "p|password=", "optional password for private key", (string p) => password = p },
{ "r|renew", "renew application certificate", r => renewCertificate = r != null },
{ "t|timeout=", "timeout in seconds to exit application", (int t) => timeout = t * 1000 },
};
try
{
// parse command line and set options
var extraArg = ConsoleUtils.ProcessCommandLine(output, args, options, ref showHelp, false);
// connect Url?
Uri serverUrl = new Uri("opc.tcp://localhost:62541/Quickstarts/ReferenceServer");
if (!string.IsNullOrEmpty(extraArg))
{
serverUrl = new Uri(extraArg);
}
// log console output to logger
if (logConsole && appLog)
{
output = new LogWriter();
}
// Define the UA Client application
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(output);
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(password);
ApplicationInstance application = new ApplicationInstance {
ApplicationName = applicationName,
ApplicationType = ApplicationType.Client,
ConfigSectionName = configSectionName,
CertificatePasswordProvider = PasswordProvider
};
// load the application configuration.
var config = await application.LoadApplicationConfiguration(silent: false);
// setup the logging
ConsoleUtils.ConfigureLogging(config, applicationName, logConsole, LogLevel.Information);
// delete old certificate
if (renewCertificate)
{
await application.DeleteApplicationInstanceCertificate().ConfigureAwait(false);
}
// check the application certificate.
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0).ConfigureAwait(false);
if (!haveAppCertificate)
{
throw new ErrorExitException("Application instance certificate invalid!", ExitCode.ErrorCertificate);
}
// wait for timeout or Ctrl-C
var quitEvent = ConsoleUtils.CtrlCHandler();
// connect to a server until application stopped
bool quit = false;
DateTime start = DateTime.UtcNow;
int waitTime = int.MaxValue;
do
{
if (timeout > 0)
{
waitTime = timeout - (int)DateTime.UtcNow.Subtract(start).TotalMilliseconds;
if (waitTime <= 0)
{
break;
}
}
// create the UA Client object and connect to configured server.
UAClient uaClient = new UAClient(application.ApplicationConfiguration, output, ClientBase.ValidateResponse) {
AutoAccept = autoAccept
};
bool connected = await uaClient.ConnectAsync(serverUrl.ToString());
if (connected)
{
// Run tests for available methods.
uaClient.ReadNodes();
uaClient.WriteNodes();
uaClient.Browse();
uaClient.CallMethod();
uaClient.SubscribeToDataChanges();
// Wait for some DataChange notifications from MonitoredItems
quit = quitEvent.WaitOne(Math.Min(30_000, waitTime));
uaClient.Disconnect();
}
else
{
output.WriteLine("Could not connect to server! Retry in 10 seconds or Ctrl-C to quit.");
quit = quitEvent.WaitOne(Math.Min(10_000, waitTime));
}
} while (!quit);
output.WriteLine("\nClient stopped.");
}
catch (Exception ex)
{
output.WriteLine(ex.Message);
}
}
}
}
-3
View File
@@ -1,3 +0,0 @@
REM collect a trace using the EventSource provider OPC-UA-Core
dotnet tool install --global dotnet-trace
dotnet-trace collect --name consolereferenceclient --providers OPC-UA-Core,OPC-UA-Client
-44
View File
@@ -1,44 +0,0 @@
<Project>
<!-- Uncomment to suppress warning that .NET Core 2.1 is used with .NET 6 library. -->
<!--
<PropertyGroup>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
</PropertyGroup>
-->
<Choose>
<!-- Note: .NET Core 2.x is end of life, removed netcoreapp2.1 from any target. -->
<!-- Visual Studio 2022, supports .NET Framework 4.6.2, .NET Core 3.1 and .NET 6 -->
<When Condition="'$(VisualStudioVersion)' == '17.0'">
<PropertyGroup>
<AppTargetFrameworks>net6.0;netcoreapp3.1;net462</AppTargetFrameworks>
<AppTargetFramework>net6.0</AppTargetFramework>
<TestsTargetFrameworks>net462;netcoreapp3.1;net6.0</TestsTargetFrameworks>
<LibTargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0</LibTargetFrameworks>
<LibxTargetFrameworks>net462;netstandard2.1;net6.0</LibxTargetFrameworks>
<HttpsTargetFrameworks>net462;netstandard2.0;netcoreapp3.1;net6.0</HttpsTargetFrameworks>
</PropertyGroup>
</When>
<!-- Visual Studio 2019, supports .NET Framework 4.6.2 and .NET Core 3.1 -->
<When Condition="'$(VisualStudioVersion)' == '16.0'">
<PropertyGroup>
<AppTargetFrameworks>netcoreapp3.1;net462</AppTargetFrameworks>
<AppTargetFramework>netcoreapp3.1</AppTargetFramework>
<TestsTargetFrameworks>net462;netcoreapp3.1</TestsTargetFrameworks>
<LibTargetFrameworks>net462;netstandard2.0;netstandard2.1</LibTargetFrameworks>
<LibxTargetFrameworks>net462;netstandard2.1</LibxTargetFrameworks>
<HttpsTargetFrameworks>net462;netstandard2.0;netcoreapp3.1</HttpsTargetFrameworks>
</PropertyGroup>
</When>
<!-- Visual Studio 2017 and earlier, support only .NET Framework 4.6.2 because .NET Core 2.x is end of life. -->
<Otherwise>
<PropertyGroup>
<AppTargetFrameworks>net462</AppTargetFrameworks>
<AppTargetFramework>net462</AppTargetFramework>
<TestsTargetFrameworks>net462</TestsTargetFrameworks>
<LibTargetFrameworks>net462</LibTargetFrameworks>
<LibxTargetFrameworks>net462</LibxTargetFrameworks>
<HttpsTargetFrameworks>net462</HttpsTargetFrameworks>
</PropertyGroup>
</Otherwise>
</Choose>
</Project>
+14
View File
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.MONO.DECODER", "MP.MONO.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.MONO.ANALYZER", "MP.MONO.ANALYZER\MP.MONO.ANALYZER.csproj", "{4C9BEAED-1A33-41A7-B9D6-7C173A43FDB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.MONO.ADAPTER.OPC", "MP.MONO.ADAPTER.OPC\MP.MONO.ADAPTER.OPC.csproj", "{FA236252-9AF8-426A-8A0A-D3C234441563}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -97,6 +99,18 @@ Global
{4C9BEAED-1A33-41A7-B9D6-7C173A43FDB8}.Release|x64.Build.0 = Release|x64
{4C9BEAED-1A33-41A7-B9D6-7C173A43FDB8}.Release|x86.ActiveCfg = Release|x86
{4C9BEAED-1A33-41A7-B9D6-7C173A43FDB8}.Release|x86.Build.0 = Release|x86
{FA236252-9AF8-426A-8A0A-D3C234441563}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Debug|x64.ActiveCfg = Debug|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Debug|x64.Build.0 = Debug|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Debug|x86.ActiveCfg = Debug|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Debug|x86.Build.0 = Debug|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Release|Any CPU.Build.0 = Release|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Release|x64.ActiveCfg = Release|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Release|x64.Build.0 = Release|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Release|x86.ActiveCfg = Release|Any CPU
{FA236252-9AF8-426A-8A0A-D3C234441563}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+1 -1
View File
@@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x86;x64</Platforms>
<Version>1.12206.1319</Version>
<Version>1.12206.1419</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>MAPO-MONO</i>
<h4>Version: 1.12206.1319</h4>
<h4>Version: 1.12206.1419</h4>
<br /> Release Note:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
1.12206.1319
1.12206.1419
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.12206.1319</version>
<version>1.12206.1419</version>
<url>http://nexus.steamware.net/repository/SWS/MP.MONO.ANALYZER/stable/LAST/MP.Mon.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/MP.MONO.ANALYZER/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+51
View File
@@ -0,0 +1,51 @@
using static MP.MONO.Core.Enums;
namespace MP.MONO.Core.CONF
{
/// <summary>
/// Descrittore oggetto DataItem generico
/// </summary>
public class MachDataItem
{
#region Public Constructors
public MachDataItem()
{ }
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Categoria oggetto
/// </summary>
public DataItemCategory Category { get; set; }
/// <summary>
/// Nome / descrizione
/// </summary>
public string Name { get; set; }
/// <summary>
/// Tipologia specifica
/// </summary>
public string SubType { get; set; }
/// <summary>
/// Tipologia principale
/// </summary>
public string Type { get; set; }
/// <summary>
/// Unità di misura
/// </summary>
public string Units { get; set; }
/// <summary>
/// ID generico
/// </summary>
public string uuid { get; set; }
#endregion Public Properties
}
}
+10
View File
@@ -20,6 +20,16 @@
OR
}
/// <summary>
/// Categoria dataItem
/// </summary>
public enum DataItemCategory
{
CONDITION = 0,
EVENT = 1,
SAMPLE = 2
}
/// <summary>
/// ENUM degli stati (es path OPC-UA)
/// </summary>
+1 -1
View File
@@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x86;x64</Platforms>
<Version>1.12206.1319</Version>
<Version>1.12206.1419</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>MAPO-MONO</i>
<h4>Version: 1.12206.1319</h4>
<h4>Version: 1.12206.1419</h4>
<br /> Release Note:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
1.12206.1319
1.12206.1419
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.12206.1319</version>
<version>1.12206.1419</version>
<url>http://nexus.steamware.net/repository/SWS/MP.MONO.DECODER/stable/LAST/MP.Mon.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/MP.MONO.DECODER/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+1 -1
View File
@@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x86;x64</Platforms>
<Version>1.12206.1319</Version>
<Version>1.12206.1419</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>MAPO-MONO</i>
<h4>Version: 1.12206.1319</h4>
<h4>Version: 1.12206.1419</h4>
<br /> Release Note:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
1.12206.1319
1.12206.1419
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.12206.1319</version>
<version>1.12206.1419</version>
<url>http://nexus.steamware.net/repository/SWS/MP.MONO.SIM/stable/LAST/MP.Mon.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/MP.MONO.SIM/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+1 -1
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>AnyCPU;x86;x64</Platforms>
<Version>1.12206.1411</Version>
<Version>1.12206.1419</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>MAPO-MONO</i>
<h4>Version: 1.12206.1411</h4>
<h4>Version: 1.12206.1419</h4>
<br /> Release Note:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
1.12206.1411
1.12206.1419
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.12206.1411</version>
<version>1.12206.1419</version>
<url>http://nexus.steamware.net/repository/SWS/MP.MONO.UI/stable/LAST/MP.Mon.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/MP.MONO.UI/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>