Skip to content

Commit

Permalink
Merge pull request #190 from json-scada/master
Browse files Browse the repository at this point in the history
 OPC_UA client driver: fixed memory leak and performance problems.
  • Loading branch information
riclolsen authored Dec 21, 2024
2 parents 8b842e5 + 6394529 commit a3cbb3a
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 144 deletions.
129 changes: 80 additions & 49 deletions src/OPC-UA-Client/AsduReceiveHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ namespace OPCUAClientDriver
{
partial class MainClass
{
public static JsonSerializerOptions jsonSerOpts = new JsonSerializerOptions
{
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
};
public enum ExitCode : int
{
Ok = 0,
Expand All @@ -42,7 +46,8 @@ public enum ExitCode : int
ErrorAddSubscription = 0x17,
ErrorRunning = 0x18,
ErrorNoKeepAlive = 0x30,
ErrorInvalidCommandLine = 0x100
ErrorInvalidCommandLine = 0x100,
ErrorClient = 0x200,
};
public class OPCUAClient
{
Expand Down Expand Up @@ -75,8 +80,9 @@ public void Run()
}
catch (Exception ex)
{
// Utils.Trace("ServiceResultException:" + ex.Message);
Log(conn_name + " - " + "Exception: " + ex.Message);
Log(conn_name + " - " + "Exception in ConsoleClient: " + ex.Message);
exitCode = ExitCode.ErrorClient;
failed = true;
return;
}

Expand Down Expand Up @@ -197,7 +203,7 @@ private async Task ConsoleClient()
{
Log(conn_name + " - " + "FATAL: error creating session!", LogLevelNoLog);
failed = true;
// Environment.Exit(1);
exitCode = ExitCode.ErrorCreateSession;
return;
}

Expand All @@ -212,8 +218,9 @@ private async Task ConsoleClient()
Log(conn_name + " - " + "Add a list of items (server current time and status) to the subscription.");
exitCode = ExitCode.ErrorMonitoredItem;
ListMon.ForEach(i => i.Notification += OnNotification);
//ListMon.ForEach(i => i.SamplingInterval = System.Convert.ToInt32(System.Convert.ToDouble(OPCUA_conn.autoCreateTagSamplingInterval) * 1000);
// ListMon.ForEach(i => Log(conn_name + " - " + i.DisplayName));
//OPCUA_conn.connection.session.Notification += OnSessionNotification;
ListMon.ForEach(i => i.SamplingInterval = System.Convert.ToInt32(System.Convert.ToDouble(OPCUA_conn.autoCreateTagSamplingInterval) * 1000));
ListMon.ForEach(i => i.QueueSize = System.Convert.ToUInt32(System.Convert.ToDouble(OPCUA_conn.autoCreateTagQueueSize)));
Log(conn_name + " - " + ListMon.Count + " Objects found");

Log(conn_name + " - " + "Create a subscription with publishing interval of " + System.Convert.ToDouble(OPCUA_conn.autoCreateTagPublishingInterval) + "seconds");
Expand All @@ -222,7 +229,10 @@ private async Task ConsoleClient()
new Subscription(session.DefaultSubscription)
{
PublishingInterval = System.Convert.ToInt32(System.Convert.ToDouble(OPCUA_conn.autoCreateTagPublishingInterval) * 1000),
PublishingEnabled = true
PublishingEnabled = true,
TimestampsToReturn = TimestampsToReturn.Both,
// MaxNotificationsPerPublish = 1,
SequentialPublishing = false,
};

await Task.Delay(50);
Expand Down Expand Up @@ -293,15 +303,12 @@ private async Task FindObjects(Opc.Ua.Client.ISession session, NodeId nodeid)
{
NodeIdsFromObjects.Add(nodeid.ToString());
await FindObjects(session, ExpandedNodeId.ToNodeId(rd.NodeId, session.NamespaceUris));
//Thread.Yield();
//Thread.Sleep(1);
//await Task.Delay(1);
}
}
}
catch (Exception ex)
{
Log(conn_name + " - " + ex.Message);
Log(conn_name + " - FindObjects - " + ex.Message);
}
}
private void Client_KeepAlive(ISession sender, KeepAliveEventArgs e)
Expand Down Expand Up @@ -336,10 +343,13 @@ private void Client_ReconnectComplete(object sender, EventArgs e)

private void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e)
{

//MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
//Console.WriteLine("Notification Received for Variable \"{0}\" and Value = {1} type {2}.", item.DisplayName, notification.Value, notification.TypeId);

foreach (var efl in item.DequeueEvents())
{
// Log(efl.ToString());
}
foreach (var value in item.DequeueValues())
{
if (value != null)
Expand All @@ -349,25 +359,26 @@ private void OnNotification(MonitoredItem item, MonitoredItemNotificationEventAr

try
{

if (value.WrappedValue.TypeInfo != null)
{
tp = value.WrappedValue.TypeInfo.BuiltInType.ToString();
isArray = value.Value.GetType().ToString().Contains("[]");
// Log(conn_name + " - " + item.ResolvedNodeId + "TYPE: " + tp, LogLevelDetailed);
}
else
{
Log(conn_name + " - " + item.ResolvedNodeId + " TYPE: ?????", LogLevelDetailed);
}

Log(conn_name + " - " + item.ResolvedNodeId + " " + item.DisplayName + " " + value.Value + " " + value.SourceTimestamp + " " + value.StatusCode, LogLevelDetailed);

if (value.Value != null)
{
CntNotificEvents++;

Double dblValue = 0.0;
string strValue = "";

if (value.WrappedValue.TypeInfo != null)
{
tp = value.WrappedValue.TypeInfo.BuiltInType.ToString();
isArray = value.Value.GetType().ToString().Contains("[]");
// Log(conn_name + " - " + item.ResolvedNodeId + "TYPE: " + tp, LogLevelDetailed);
}
else
{
Log(conn_name + " - " + item.ResolvedNodeId + " TYPE: ?????", LogLevelDetailed);
}

Log(conn_name + " - " + item.ResolvedNodeId + " " + item.DisplayName + " " + value.Value + " " + value.SourceTimestamp + " " + value.StatusCode, LogLevelDetailed);

try
{

Expand All @@ -394,11 +405,7 @@ private void OnNotification(MonitoredItem item, MonitoredItemNotificationEventAr
dblValue = 0;
try
{
var opt = new JsonSerializerOptions
{
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
};
strValue = JsonSerializer.Serialize(value.Value, opt);
strValue = JsonSerializer.Serialize(value.Value, jsonSerOpts);
}
catch
{
Expand Down Expand Up @@ -426,28 +433,23 @@ private void OnNotification(MonitoredItem item, MonitoredItemNotificationEventAr
dblValue = System.Convert.ToDouble(value.Value);
strValue = value.Value.ToString();
}
catch
catch
{
dblValue = 0;
strValue = value.Value.ToString();
}
}
}
}
catch
catch
{
dblValue = 0;
strValue = value.Value.ToString();
}

var options = new JsonSerializerOptions
{
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
};

OPC_Value iv =
new OPC_Value()
{
valueJson = JsonSerializer.Serialize(value, options),
valueJson = JsonSerializer.Serialize(value.Value, jsonSerOpts),
selfPublish = true,
address = item.ResolvedNodeId.ToString(),
asdu = tp,
Expand All @@ -464,7 +466,16 @@ private void OnNotification(MonitoredItem item, MonitoredItemNotificationEventAr
common_address = "",
display_name = item.DisplayName
};
OPCDataQueue.Enqueue(iv);
if (OPCDataQueue.Count < DataBufferLimit)
OPCDataQueue.Enqueue(iv);
else
CntLostDataUpdates++;
//if (item.ResolvedNodeId.ToString().ToLower().Contains("byteswritten"))
//{
// MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
// Console.WriteLine("Notification Received for Variable \"{0}\" and Value = {1} type {2}.", item.DisplayName, notification.Value, notification.TypeId);
// Console.WriteLine($"----------------------- {conn_name} {iv.address} {iv.value} {iv.sourceTimestamp}");
//}
}
}
catch (Exception excpt)
Expand All @@ -478,15 +489,7 @@ private void OnNotification(MonitoredItem item, MonitoredItemNotificationEventAr
{
Log(conn_name + " - " + item.ResolvedNodeId + " " + item.DisplayName + " NULL VALUE!", LogLevelDetailed);
}

Thread.Yield();
Thread.Sleep(1);
//if ((OPCDataQueue.Count % 50) == 0)
//{
// await Task.Delay(200);
//}
}

}

private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
Expand All @@ -504,6 +507,34 @@ private void CertificateValidator_CertificateValidation(CertificateValidator val
}
}
}

// not used yet (session events)
private void OnSessionNotification(ISession session, NotificationEventArgs e)
{
var notificationMsg = e.NotificationMessage;
Console.WriteLine(conn_name + " - Notification Received Value = {0} type {1}.", notificationMsg.NotificationData, notificationMsg.TypeId);

int count = 0;

for (int ii = 0; ii < e.NotificationMessage.NotificationData.Count; ii++)
{
DataChangeNotification notification = e.NotificationMessage.NotificationData[ii].Body as DataChangeNotification;

if (notification == null)
{
continue;
}

for (int jj = 0; jj < notification.MonitoredItems.Count; jj++)
{
CntNotificEvents++;
count++;
var value = notification.MonitoredItems[jj].Value;
}
}

// ReportMessage("OnDataChange. Time={0} ({3}), Count={1}/{2}", DateTime.UtcNow.ToString("mm:ss.fff"), count, m_totalItemUpdateCount, (m_lastMessageTime - m_firstMessageTime).TotalMilliseconds);
}
}
}
}
8 changes: 4 additions & 4 deletions src/OPC-UA-Client/Common_srv_cli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ public class
public string configFileName { get; set; }
[BsonDefaultValue(true)]
public bool autoCreateTags { get; set; }
[BsonDefaultValue(2.5)]
[BsonDefaultValue(5.0)]
public double autoCreateTagPublishingInterval { get; set; }
[BsonDefaultValue(0.0)]
[BsonDefaultValue(5.0)]
public double autoCreateTagSamplingInterval { get; set; }
[BsonDefaultValue(5.0)]
public double autoCreateTagQueueSize { get; set; }
Expand All @@ -118,12 +118,12 @@ public class protocolDriverInstancesClass
public String protocolDriver { get; set; } = "";
public Boolean enabled { get; set; } = true;
public Int32 logLevel { get; set; } = 1;
public String[] nodeNames { get; set; } = new string[0];
public String[] nodeNames { get; set; } = Array.Empty<string>();
public String activeNodeName { get; set; } = "";
public DateTime activeNodeKeepAliveTimeTag { get; set; } = DateTime.MinValue;
public Boolean keepProtocolRunningWhileInactive { get; set; } = false;
}
public struct OPC_Value
public class OPC_Value
{
public string valueJson;
public bool selfPublish;
Expand Down
Loading

0 comments on commit a3cbb3a

Please sign in to comment.