Skip to content

Commit db5152b

Browse files
authored
Merge pull request #2199 from OPCFoundation/master371
Merge updates in release branch
2 parents c9fe45a + 1dfda2b commit db5152b

File tree

28 files changed

+18329
-14001
lines changed

28 files changed

+18329
-14001
lines changed

Applications/ConsoleReferenceClient/ClientSamples.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,9 +878,11 @@ public async Task SubscribeAllValuesAsync(
878878
KeepAliveCount = keepAliveCount,
879879
SequentialPublishing = true,
880880
RepublishAfterTransfer = true,
881+
DisableMonitoredItemCache = true,
881882
MaxNotificationsPerPublish = 1000,
882883
MinLifetimeInterval = (uint)session.SessionTimeout,
883884
FastDataChangeCallback = FastDataChangeNotification,
885+
FastKeepAliveCallback = FastKeepAliveNotification,
884886
};
885887
session.AddSubscription(subscription);
886888

@@ -959,14 +961,32 @@ public static string FormatValueAsJson(
959961
#endregion
960962

961963
#region Private Methods
964+
/// <summary>
965+
/// The fast keep alive notification callback.
966+
/// </summary>
967+
private void FastKeepAliveNotification(Subscription subscription, NotificationData notification)
968+
{
969+
try
970+
{
971+
m_output.WriteLine("Keep Alive : Id={0} PublishTime={1} SequenceNumber={2}.",
972+
subscription.Id, notification.PublishTime, notification.SequenceNumber);
973+
}
974+
catch (Exception ex)
975+
{
976+
m_output.WriteLine("FastKeepAliveNotification error: {0}", ex.Message);
977+
}
978+
}
979+
962980
/// <summary>
963981
/// The fast data change notification callback.
964982
/// </summary>
965983
private void FastDataChangeNotification(Subscription subscription, DataChangeNotification notification, IList<string> stringTable)
966984
{
967985
try
968986
{
969-
m_output.WriteLine("Notification: Id={0} Items={1}.", subscription.Id, notification.MonitoredItems.Count);
987+
m_output.WriteLine("Notification: Id={0} PublishTime={1} SequenceNumber={2} Items={3}.",
988+
subscription.Id, notification.PublishTime,
989+
notification.SequenceNumber, notification.MonitoredItems.Count);
970990
}
971991
catch (Exception ex)
972992
{

Applications/ConsoleReferenceClient/Program.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
using System.Threading.Tasks;
3737
using Microsoft.Extensions.Logging;
3838
using Opc.Ua;
39+
using Opc.Ua.Client;
3940
using Opc.Ua.Configuration;
4041

4142
namespace Quickstarts.ConsoleReferenceClient
@@ -77,14 +78,17 @@ public static async Task Main(string[] args)
7778
bool jsonvalues = false;
7879
bool verbose = false;
7980
bool subscribe = false;
81+
bool noSecurity = false;
8082
string password = null;
8183
int timeout = Timeout.Infinite;
8284
string logFile = null;
85+
string reverseConnectUrlString = null;
8386

8487
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
8588
usage,
8689
{ "h|help", "show this message and exit", h => showHelp = h != null },
8790
{ "a|autoaccept", "auto accept certificates (for testing only)", a => autoAccept = a != null },
91+
{ "nsec|nosecurity", "select endpoint with security NONE, least secure if unavailable", s => noSecurity = s != null },
8892
{ "un|username=", "the name of the user identity for the connection", (string u) => username = u },
8993
{ "up|userpassword=", "the password of the user identity for the connection", (string u) => userpassword = u },
9094
{ "c|console", "log to console", c => logConsole = c != null },
@@ -99,8 +103,11 @@ public static async Task Main(string[] args)
99103
{ "j|json", "Output all Values as JSON", j => { if (j != null) jsonvalues = true; } },
100104
{ "v|verbose", "Verbose output", v => { if (v != null) verbose = true; } },
101105
{ "s|subscribe", "Subscribe", s => { if (s != null) subscribe = true; } },
106+
{ "rc|reverseconnect=", "Connect using the reverse connect endpoint. (e.g. rc=opc.tcp://localhost:65300)", (string url) => reverseConnectUrlString = url},
102107
};
103108

109+
ReverseConnectManager reverseConnectManager = null;
110+
104111
try
105112
{
106113
// parse command line and set options
@@ -158,8 +165,18 @@ public static async Task Main(string[] args)
158165
throw new ErrorExitException("Application instance certificate invalid!", ExitCode.ErrorCertificate);
159166
}
160167

168+
if (reverseConnectUrlString != null)
169+
{
170+
// start the reverse connection manager
171+
output.WriteLine("Create reverse connection endpoint at {0}.", reverseConnectUrlString);
172+
reverseConnectManager = new ReverseConnectManager();
173+
reverseConnectManager.AddEndpoint(new Uri(reverseConnectUrlString));
174+
reverseConnectManager.StartService(config);
175+
}
176+
161177
// wait for timeout or Ctrl-C
162-
var quitEvent = ConsoleUtils.CtrlCHandler();
178+
var quitCTS = new CancellationTokenSource();
179+
var quitEvent = ConsoleUtils.CtrlCHandler(quitCTS);
163180

164181
// connect to a server until application stops
165182
bool quit = false;
@@ -177,10 +194,9 @@ public static async Task Main(string[] args)
177194
}
178195

179196
// create the UA Client object and connect to configured server.
180-
using (UAClient uaClient = new UAClient(
181-
application.ApplicationConfiguration, output, ClientBase.ValidateResponse) {
197+
using (UAClient uaClient = new UAClient(application.ApplicationConfiguration, reverseConnectManager, output, ClientBase.ValidateResponse) {
182198
AutoAccept = autoAccept,
183-
SessionLifeTime = 60000,
199+
SessionLifeTime = 60_000,
184200
})
185201
{
186202
// set user identity
@@ -189,7 +205,7 @@ public static async Task Main(string[] args)
189205
uaClient.UserIdentity = new UserIdentity(username, userpassword ?? string.Empty);
190206
}
191207

192-
bool connected = await uaClient.ConnectAsync(serverUrl.ToString(), false).ConfigureAwait(false);
208+
bool connected = await uaClient.ConnectAsync(serverUrl.ToString(), !noSecurity, quitCTS.Token).ConfigureAwait(false);
193209
if (connected)
194210
{
195211
output.WriteLine("Connected! Ctrl-C to quit.");
@@ -257,7 +273,7 @@ public static async Task Main(string[] args)
257273
}
258274

259275
await samples.SubscribeAllValuesAsync(uaClient,
260-
variableIds: new NodeCollection(variables.Take(100)),
276+
variableIds: new NodeCollection(variables),
261277
samplingInterval: 1000,
262278
publishingInterval: 5000,
263279
queueSize: 10,
@@ -307,6 +323,11 @@ await samples.SubscribeAllValuesAsync(uaClient,
307323
{
308324
output.WriteLine(ex.Message);
309325
}
326+
finally
327+
{
328+
Utils.SilentDispose(reverseConnectManager);
329+
output.Close();
330+
}
310331
}
311332
}
312333
}

Applications/ConsoleReferenceClient/UAClient.cs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using System;
3131
using System.Collections;
3232
using System.IO;
33+
using System.Threading;
3334
using System.Threading.Tasks;
3435
using Opc.Ua;
3536
using Opc.Ua.Client;
@@ -51,6 +52,19 @@ public UAClient(ApplicationConfiguration configuration, TextWriter writer, Actio
5152
m_output = writer;
5253
m_configuration = configuration;
5354
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
55+
m_reverseConnectManager = null;
56+
}
57+
58+
/// <summary>
59+
/// Initializes a new instance of the UAClient class for reverse connections.
60+
/// </summary>
61+
public UAClient(ApplicationConfiguration configuration, ReverseConnectManager reverseConnectManager, TextWriter writer, Action<IList, IList> validateResponse)
62+
{
63+
m_validateResponse = validateResponse;
64+
m_output = writer;
65+
m_configuration = configuration;
66+
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
67+
m_reverseConnectManager = reverseConnectManager;
5468
}
5569
#endregion
5670

@@ -112,7 +126,7 @@ public void Dispose()
112126
/// <summary>
113127
/// Creates a session with the UA server
114128
/// </summary>
115-
public async Task<bool> ConnectAsync(string serverUrl, bool useSecurity = true)
129+
public async Task<bool> ConnectAsync(string serverUrl, bool useSecurity = true, CancellationToken ct = default)
116130
{
117131
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
118132

@@ -124,19 +138,47 @@ public async Task<bool> ConnectAsync(string serverUrl, bool useSecurity = true)
124138
}
125139
else
126140
{
127-
m_output.WriteLine("Connecting to... {0}", serverUrl);
141+
ITransportWaitingConnection connection = null;
142+
EndpointDescription endpointDescription = null;
143+
if (m_reverseConnectManager != null)
144+
{
145+
m_output.WriteLine("Waiting for reverse connection to.... {0}", serverUrl);
146+
do
147+
{
148+
using (var cts = new CancellationTokenSource(30_000))
149+
using (var linkedCTS = CancellationTokenSource.CreateLinkedTokenSource(ct, cts.Token))
150+
{
151+
connection = await m_reverseConnectManager.WaitForConnection(new Uri(serverUrl), null, linkedCTS.Token);
152+
if (connection == null)
153+
{
154+
throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out.");
155+
}
156+
if (endpointDescription == null)
157+
{
158+
m_output.WriteLine("Discover reverse connection endpoints....");
159+
endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, connection, useSecurity);
160+
connection = null;
161+
}
162+
}
163+
} while (connection == null);
164+
}
165+
else
166+
{
167+
m_output.WriteLine("Connecting to... {0}", serverUrl);
168+
endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, serverUrl, useSecurity);
169+
}
128170

129171
// Get the endpoint by connecting to server's discovery endpoint.
130172
// Try to find the first endopint with security.
131-
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, serverUrl, useSecurity);
132173
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
133174
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
134175

135176
// Create the session
136177
var session = await Opc.Ua.Client.Session.Create(
137178
m_configuration,
179+
connection,
138180
endpoint,
139-
true,
181+
connection == null,
140182
false,
141183
m_configuration.ApplicationName,
142184
SessionLifeTime,
@@ -235,7 +277,7 @@ private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
235277
return;
236278
}
237279

238-
var state = m_reconnectHandler.BeginReconnect(m_session, ReconnectPeriod, Client_ReconnectComplete);
280+
var state = m_reconnectHandler.BeginReconnect(m_session, m_reverseConnectManager, ReconnectPeriod, Client_ReconnectComplete);
239281
if (state == SessionReconnectHandler.ReconnectState.Triggered)
240282
{
241283
Utils.LogInfo("KeepAlive status {0}, reconnect status {1}, reconnect period {2}ms.", e.Status, state, ReconnectPeriod);
@@ -276,9 +318,7 @@ private void Client_ReconnectComplete(object sender, EventArgs e)
276318
{
277319
m_output.WriteLine("--- RECONNECTED TO NEW SESSION --- {0}", m_reconnectHandler.Session.SessionId);
278320
var session = m_session;
279-
session.KeepAlive -= Session_KeepAlive;
280-
m_session = m_reconnectHandler.Session as Session;
281-
m_session.KeepAlive += Session_KeepAlive;
321+
m_session = m_reconnectHandler.Session;
282322
Utils.SilentDispose(session);
283323
}
284324
else
@@ -330,9 +370,10 @@ protected virtual void CertificateValidation(CertificateValidator sender, Certif
330370

331371
#region Private Fields
332372
private object m_lock = new object();
373+
private ReverseConnectManager m_reverseConnectManager;
333374
private ApplicationConfiguration m_configuration;
334375
private SessionReconnectHandler m_reconnectHandler;
335-
private Session m_session;
376+
private ISession m_session;
336377
private readonly TextWriter m_output;
337378
private readonly Action<IList, IList> m_validateResponse;
338379
#endregion

Applications/ConsoleReferenceServer/ConsoleUtils.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,12 +395,13 @@ public static void LogTest()
395395
/// Create an event which is set if a user
396396
/// enters the Ctrl-C key combination.
397397
/// </summary>
398-
public static ManualResetEvent CtrlCHandler()
398+
public static ManualResetEvent CtrlCHandler(CancellationTokenSource cts = default)
399399
{
400400
var quitEvent = new ManualResetEvent(false);
401401
try
402402
{
403403
Console.CancelKeyPress += (_, eArgs) => {
404+
cts.Cancel();
404405
quitEvent.Set();
405406
eArgs.Cancel = true;
406407
};

Applications/Quickstarts.Servers/Alarms/AlarmNodeManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ private void DoSimulation(object state)
343343
if (m_success > 0)
344344
{
345345
m_missed++;
346-
Utils.LogInfo("Alarms: Missed Loop {1} Success {2}", m_missed, m_success);
346+
Utils.LogInfo("Alarms: Missed Loop {0} Success {1}", m_missed, m_success);
347347
}
348348
}
349349
}

Docs/ReverseConnect.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ The Reverse Connect option consists of the following elements:
1111
* Updated client library which support to
1212
- Configure a client endpoint to accept *ReverseHello* messages using a *ReverseConnectManager*.
1313
- A client API extension to allow applications to register for reverse connections either by callback or by waiting for the *ReverseHello* message for a specific server endpoint and application Uri combination. An optional filter for server Uris or endpoint Urls can be applied to allow multiple clients to use the same endpoint.
14-
* The updated C# [Reference Server](../Applications/ConsoleReferenceServer) with reverse connect support.
15-
* The C# Core [Client](https://github.com/OPCFoundation/UA-.NETStandard-Samples/tree/master/Samples/NetCoreComplexClient) and [Server](https://github.com/OPCFoundation/UA-.NETStandard-Samples/tree/master/Samples/NetCoreConsoleServer) samples that can initiate a Reverse connection with command line options.
14+
* The C# [Console Reference Server](../Applications/ConsoleReferenceServer) with reverse connect support in the configuration xml.
15+
* The C# Core [Console Reference Client](../Applications/ConsoleReferenceClient) that can initiate a Reverse connection with command line options.
1616
* A modified C# [Aggregation Server](https://github.com/OPCFoundation/UA-.NETStandard-Samples/tree/master/Workshop/Aggregation) that supports incoming and outgoing reverse connections.
1717

1818
## Reverse Connect Handshake ##
@@ -83,4 +83,4 @@ The Client configuration extension to allow incoming connections for one or more
8383

8484
- Only a limited number of samples is available yet, the Reference Server, the Aggregation Server and the Console server and client.
8585

86-
86+

0 commit comments

Comments
 (0)