Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OPC_UA client driver: fixed memory leak and performance problems. #190

Merged
merged 4 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading