Skip to content

Commit 0e5781e

Browse files
authored
Merge pull request #2998 from OPCFoundation/develop/main374
Merge Develop/main374 in release branch
2 parents ef645f6 + 73d316c commit 0e5781e

File tree

13 files changed

+165
-49
lines changed

13 files changed

+165
-49
lines changed

Libraries/Opc.Ua.Server/Diagnostics/CustomNodeManager.cs

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ protected CustomNodeManager2(
107107
// create the table of monitored nodes.
108108
// these are created by the node manager whenever a client subscribe to an attribute of the node.
109109
m_monitoredNodes = new NodeIdDictionary<MonitoredNode2>();
110+
111+
m_predefinedNodes = new NodeIdDictionary<NodeState>();
110112
}
111113
#endregion
112114

@@ -129,7 +131,7 @@ protected virtual void Dispose(bool disposing)
129131
{
130132
lock (Lock)
131133
{
132-
foreach (NodeState node in m_predefinedNodes?.Values)
134+
foreach (NodeState node in m_predefinedNodes.Values)
133135
{
134136
Utils.SilentDispose(node);
135137
}
@@ -321,7 +323,7 @@ protected virtual NodeHandle IsHandleInNamespace(object managerHandle)
321323
public NodeState Find(NodeId nodeId)
322324
{
323325
NodeState node = null;
324-
if (m_predefinedNodes?.TryGetValue(nodeId, out node) == true)
326+
if (m_predefinedNodes.TryGetValue(nodeId, out node) == true)
325327
{
326328
return node;
327329
}
@@ -349,11 +351,6 @@ public NodeId CreateNode(
349351

350352
lock (Lock)
351353
{
352-
if (m_predefinedNodes == null)
353-
{
354-
m_predefinedNodes = new NodeIdDictionary<NodeState>();
355-
}
356-
357354
instance.ReferenceTypeId = referenceTypeId;
358355

359356
NodeState parent = null;
@@ -390,7 +387,7 @@ public bool DeleteNode(
390387
List<LocalReference> referencesToRemove = new List<LocalReference>();
391388

392389
NodeState node = null;
393-
if (m_predefinedNodes?.TryGetValue(nodeId, out node) != true)
390+
if (m_predefinedNodes.TryGetValue(nodeId, out node) != true)
394391
{
395392
return false;
396393
}
@@ -477,11 +474,6 @@ public virtual void LoadPredefinedNodes(
477474
string resourcePath,
478475
IDictionary<NodeId, IList<IReference>> externalReferences)
479476
{
480-
if (m_predefinedNodes == null)
481-
{
482-
m_predefinedNodes = new NodeIdDictionary<NodeState>();
483-
}
484-
485477
// load the predefined nodes from an XML document.
486478
NodeStateCollection predefinedNodes = new NodeStateCollection();
487479
predefinedNodes.LoadFromResource(context, resourcePath, assembly, true);
@@ -544,11 +536,6 @@ protected virtual NodeState AddBehaviourToPredefinedNode(ISystemContext context,
544536
/// </summary>
545537
protected virtual void AddPredefinedNode(ISystemContext context, NodeState node)
546538
{
547-
if (m_predefinedNodes == null)
548-
{
549-
m_predefinedNodes = new NodeIdDictionary<NodeState>();
550-
}
551-
552539
// assign a default value to any variable in namespace 0
553540
if (node is BaseVariableState nodeStateVar)
554541
{
@@ -614,7 +601,7 @@ protected virtual void RemovePredefinedNode(
614601
NodeState node,
615602
List<LocalReference> referencesToRemove)
616603
{
617-
if (m_predefinedNodes?.TryRemove(node.NodeId, out _) != true)
604+
if (m_predefinedNodes.TryRemove(node.NodeId, out _) != true)
618605
{
619606
return;
620607
}
@@ -687,7 +674,7 @@ protected virtual void OnNodeRemoved(NodeState node)
687674
/// <param name="externalReferences">A list of references to add to external targets.</param>
688675
protected virtual void AddReverseReferences(IDictionary<NodeId, IList<IReference>> externalReferences)
689676
{
690-
foreach (NodeState source in m_predefinedNodes?.Values)
677+
foreach (NodeState source in m_predefinedNodes.Values)
691678
{
692679
IList<IReference> references = new List<IReference>();
693680
source.GetReferences(SystemContext, references);
@@ -855,15 +842,12 @@ public NodeState FindPredefinedNode(NodeId nodeId, Type expectedType)
855842
/// </summary>
856843
public virtual void DeleteAddressSpace()
857844
{
858-
if (m_predefinedNodes != null)
859-
{
860-
var nodes = m_predefinedNodes.Values.ToArray();
861-
m_predefinedNodes.Clear();
845+
var nodes = m_predefinedNodes.Values.ToArray();
846+
m_predefinedNodes.Clear();
862847

863-
foreach (NodeState node in nodes)
864-
{
865-
Utils.SilentDispose(node);
866-
}
848+
foreach (NodeState node in nodes)
849+
{
850+
Utils.SilentDispose(node);
867851
}
868852
}
869853

@@ -894,7 +878,7 @@ protected virtual NodeHandle GetManagerHandle(ServerSystemContext context, NodeI
894878
}
895879

896880
NodeState node = null;
897-
if (m_predefinedNodes?.TryGetValue(nodeId, out node) == true)
881+
if (m_predefinedNodes.TryGetValue(nodeId, out node) == true)
898882
{
899883
var handle = new NodeHandle {
900884
NodeId = nodeId,

Libraries/Opc.Ua.Server/NodeManager/SamplingGroup.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public void Startup()
106106
{
107107
m_shutdownEvent.Reset();
108108

109-
Task.Factory.StartNew(() => {
109+
m_samplingTask = Task.Factory.StartNew(() => {
110110
SampleMonitoredItems(m_samplingInterval);
111111
}, TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach);
112112
}
@@ -121,6 +121,9 @@ public void Shutdown()
121121
{
122122
m_shutdownEvent.Set();
123123
m_items.Clear();
124+
Utils.SilentDispose(m_samplingTask);
125+
m_samplingTask = null;
126+
Utils.SilentDispose(m_shutdownEvent);
124127
}
125128
}
126129

@@ -243,7 +246,7 @@ public bool ApplyChanges()
243246
m_itemsToRemove.Clear();
244247

245248
// start the group if it is not running.
246-
if (m_items.Count > 0)
249+
if (m_samplingTask == null && m_items.Count > 0)
247250
{
248251
Startup();
249252
}
@@ -487,6 +490,7 @@ private void DoSample(object state)
487490
private Dictionary<uint, ISampledDataChangeMonitoredItem> m_items;
488491
private ManualResetEvent m_shutdownEvent;
489492
private List<SamplingRateGroup> m_samplingRates;
493+
private Task m_samplingTask;
490494
#endregion
491495
}
492496
}

Stack/Opc.Ua.Core/Stack/State/FiniteStateMachineState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ protected virtual void ReportAuditProgramTransitionEvent(ISystemContext context,
645645
/// <param name="context">The context.</param>
646646
/// <param name="causeId">The cause id.</param>
647647
/// <returns></returns>
648-
public void CauseProcessingCompleted(ISystemContext context, uint causeId)
648+
public virtual void CauseProcessingCompleted(ISystemContext context, uint causeId)
649649
{
650650
// get the transition.
651651
uint transitionId = GetTransitionForCause(context, causeId);
@@ -679,7 +679,7 @@ public void CauseProcessingCompleted(ISystemContext context, uint causeId)
679679
/// <summary>
680680
/// Causes the specified transition to occur.
681681
/// </summary>
682-
public ServiceResult DoTransition(
682+
public virtual ServiceResult DoTransition(
683683
ISystemContext context,
684684
uint transitionId,
685685
uint causeId,

Stack/Opc.Ua.Core/Stack/Tcp/TcpListenerChannel.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public void IdleCleanup()
159159
lock (DataLock)
160160
{
161161
state = State;
162-
if (state == TcpChannelState.Open)
162+
if (state == TcpChannelState.Open || state == TcpChannelState.Connecting)
163163
{
164164
state = State = TcpChannelState.Closing;
165165
}
@@ -176,6 +176,21 @@ public void IdleCleanup()
176176
/// or received a keep alive.
177177
/// </summary>
178178
public int ElapsedSinceLastActiveTime => (HiResClock.TickCount - LastActiveTickCount);
179+
180+
/// <summary>
181+
/// Has the channel been used in a session
182+
/// </summary>
183+
public bool UsedBySession
184+
{
185+
get
186+
{
187+
return m_usedBySession;
188+
}
189+
protected set
190+
{
191+
m_usedBySession = value;
192+
}
193+
}
179194
#endregion
180195

181196
#region Socket Event Handlers
@@ -577,6 +592,7 @@ protected uint GetNewTokenId()
577592
private ReportAuditCloseSecureChannelEventHandler m_reportAuditCloseSecureChannelEvent;
578593
private ReportAuditCertificateEventHandler m_reportAuditCertificateEvent;
579594
private long m_lastTokenId;
595+
private bool m_usedBySession;
580596
#endregion
581597
}
582598

Stack/Opc.Ua.Core/Stack/Tcp/TcpServerChannel.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,14 @@ private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment<byte
699699
State = TcpChannelState.Open;
700700

701701
// send the response.
702-
SendOpenSecureChannelResponse(requestId, CurrentToken, request);
702+
if (requestType == SecurityTokenRequestType.Renew)
703+
{
704+
SendOpenSecureChannelResponse(requestId, RenewedToken, request);
705+
}
706+
else
707+
{
708+
SendOpenSecureChannelResponse(requestId, CurrentToken, request);
709+
}
703710

704711
// notify reverse
705712
CompleteReverseHello(null);
@@ -1100,6 +1107,11 @@ public void SendResponse(uint requestId, IServiceResponse response)
11001107
m_queuedResponses[requestId] = response;
11011108
return;
11021109
}
1110+
1111+
if (response is ActivateSessionResponse activateSessionResponse)
1112+
{
1113+
UsedBySession = StatusCode.IsGood(activateSessionResponse.ResponseHeader.ServiceResult);
1114+
}
11031115
}
11041116
}
11051117

Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,31 @@ private void OnAccept(object sender, SocketAsyncEventArgs e)
793793
{
794794
// TODO: .Count is flagged as hotpath, implement separate counter
795795
int channelCount = channels.Count;
796+
797+
// Remove oldest channel that does not have a session attached to it
798+
// before reaching m_maxChannelCount
799+
if (m_maxChannelCount > 0 && m_maxChannelCount == channelCount)
800+
{
801+
var snapshot = channels.ToArray();
802+
803+
// Identify channels without established sessions
804+
var nonSessionChannels = snapshot.Where(ch => !ch.Value.UsedBySession).ToArray();
805+
806+
if (nonSessionChannels.Any())
807+
{
808+
var oldestIdChannel = nonSessionChannels.Aggregate((max, current) =>
809+
current.Value.ElapsedSinceLastActiveTime > max.Value.ElapsedSinceLastActiveTime ? current : max);
810+
811+
Utils.LogInfo("TCPLISTENER: Channel Id {0} scheduled for IdleCleanup - Oldest without established session.",
812+
oldestIdChannel.Value.Id);
813+
oldestIdChannel.Value.IdleCleanup();
814+
Utils.LogInfo("TCPLISTENER: Channel Id {0} finished IdleCleanup - Oldest without established session.",
815+
oldestIdChannel.Value.Id);
816+
817+
channelCount--;
818+
}
819+
}
820+
796821
bool serveChannel = !(m_maxChannelCount > 0 && m_maxChannelCount < channelCount);
797822
if (!serveChannel)
798823
{

Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Symmetric.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ protected void ActivateToken(ChannelToken token)
7272
Utils.SilentDispose(m_previousToken);
7373
m_previousToken = m_currentToken;
7474
m_currentToken = token;
75-
Utils.SilentDispose(m_renewedToken);
7675
m_renewedToken = null;
7776

7877
OnTokenActivated?.Invoke(token, m_previousToken);

Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ public void UpdateLastActiveTime()
883883

884884
private TcpChannelStateEventHandler m_StateChanged;
885885

886-
private int m_lastActiveTickCount;
886+
private int m_lastActiveTickCount = HiResClock.TickCount;
887887
#endregion
888888
}
889889

Stack/Opc.Ua.Core/Types/BuiltIn/ExtensionObject.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,8 @@ private NodeId XmlEncodedTypeId
717717
// check for null Id.
718718
if (m_typeId.IsNull)
719719
{
720-
return NodeId.Null;
720+
// note: this NodeId is modified when the ExtensionObject is deserialized.
721+
return new NodeId();
721722
}
722723

723724
return ExpandedNodeId.ToNodeId(m_typeId, m_context.NamespaceUris);

Stack/Opc.Ua.Core/Types/BuiltIn/Matrix.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1+
/* Copyright (c) 1996-2022 The OPC Foundation. All rights reserved.
2+
The source code in this file is covered under a dual-license scenario:
3+
- RCL: for OPC Foundation Corporate Members in good-standing
4+
- GPL V2: everybody else
5+
RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/
6+
GNU General Public License as published by the Free Software Foundation;
7+
version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2
8+
This source code is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11+
*/
12+
113
using System;
214
using System.Diagnostics;
3-
using System.Globalization;
415
using System.Runtime.Serialization;
516
using System.Text;
617

@@ -30,7 +41,6 @@ public Matrix(Array value, BuiltInType builtInType)
3041

3142
m_elements = Utils.FlattenArray(value);
3243
m_typeInfo = new TypeInfo(builtInType, m_dimensions.Length);
33-
3444
}
3545

3646
/// <summary>
@@ -61,7 +71,6 @@ public Matrix(Array elements, BuiltInType builtInType, params int[] dimensions)
6171

6272
SanityCheckArrayElements(m_elements, builtInType);
6373
}
64-
6574
#endregion
6675

6776
#region Public Members
@@ -242,16 +251,14 @@ public virtual object Clone()
242251
/// <param name="builtInType">The builtInType used for the elements.</param>
243252
[Conditional("DEBUG")]
244253
private static void SanityCheckArrayElements(Array elements, BuiltInType builtInType)
245-
246-
247254
{
248255
#if DEBUG
249256
TypeInfo sanityCheck = TypeInfo.Construct(elements);
250257
Debug.Assert(sanityCheck.BuiltInType == builtInType || builtInType == BuiltInType.Enumeration ||
251-
(sanityCheck.BuiltInType == BuiltInType.ExtensionObject && builtInType == BuiltInType.Null) ||
252-
(sanityCheck.BuiltInType == BuiltInType.Int32 && builtInType == BuiltInType.Enumeration) ||
253-
(sanityCheck.BuiltInType == BuiltInType.ByteString && builtInType == BuiltInType.Byte) ||
254-
(builtInType == BuiltInType.Variant));
258+
(sanityCheck.BuiltInType == BuiltInType.ExtensionObject && builtInType == BuiltInType.Null) ||
259+
(sanityCheck.BuiltInType == BuiltInType.Int32 && builtInType == BuiltInType.Enumeration) ||
260+
(sanityCheck.BuiltInType == BuiltInType.ByteString && builtInType == BuiltInType.Byte) ||
261+
(builtInType == BuiltInType.Variant));
255262
#endif
256263
}
257264
#endregion

0 commit comments

Comments
 (0)