Skip to content

Commit

Permalink
Merge pull request #2206 from OPCFoundation/master371_temp
Browse files Browse the repository at this point in the history
Merge release updates to main
  • Loading branch information
mregen committed Jun 27, 2023
2 parents 280db53 + 49c8532 commit f25cc4d
Show file tree
Hide file tree
Showing 30 changed files with 18,330 additions and 14,024 deletions.
22 changes: 21 additions & 1 deletion Applications/ConsoleReferenceClient/ClientSamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -878,9 +878,11 @@ void FetchReferenceIdTypes(ISession session)
KeepAliveCount = keepAliveCount,
SequentialPublishing = true,
RepublishAfterTransfer = true,
DisableMonitoredItemCache = true,
MaxNotificationsPerPublish = 1000,
MinLifetimeInterval = (uint)session.SessionTimeout,
FastDataChangeCallback = FastDataChangeNotification,
FastKeepAliveCallback = FastKeepAliveNotification,
};
session.AddSubscription(subscription);

Expand Down Expand Up @@ -959,14 +961,32 @@ void FetchReferenceIdTypes(ISession session)
#endregion

#region Private Methods
/// <summary>
/// The fast keep alive notification callback.
/// </summary>
private void FastKeepAliveNotification(Subscription subscription, NotificationData notification)
{
try
{
m_output.WriteLine("Keep Alive : Id={0} PublishTime={1} SequenceNumber={2}.",
subscription.Id, notification.PublishTime, notification.SequenceNumber);
}
catch (Exception ex)
{
m_output.WriteLine("FastKeepAliveNotification error: {0}", ex.Message);
}
}

/// <summary>
/// The fast data change notification callback.
/// </summary>
private void FastDataChangeNotification(Subscription subscription, DataChangeNotification notification, IList<string> stringTable)
{
try
{
m_output.WriteLine("Notification: Id={0} Items={1}.", subscription.Id, notification.MonitoredItems.Count);
m_output.WriteLine("Notification: Id={0} PublishTime={1} SequenceNumber={2} Items={3}.",
subscription.Id, notification.PublishTime,
notification.SequenceNumber, notification.MonitoredItems.Count);
}
catch (Exception ex)
{
Expand Down
35 changes: 28 additions & 7 deletions Applications/ConsoleReferenceClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;

namespace Quickstarts.ConsoleReferenceClient
Expand Down Expand Up @@ -77,14 +78,17 @@ public static async Task Main(string[] args)
bool jsonvalues = false;
bool verbose = false;
bool subscribe = false;
bool noSecurity = false;
string password = null;
int timeout = Timeout.Infinite;
string logFile = null;
string reverseConnectUrlString = null;

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 },
{ "nsec|nosecurity", "select endpoint with security NONE, least secure if unavailable", s => noSecurity = s != null },
{ "un|username=", "the name of the user identity for the connection", (string u) => username = u },
{ "up|userpassword=", "the password of the user identity for the connection", (string u) => userpassword = u },
{ "c|console", "log to console", c => logConsole = c != null },
Expand All @@ -99,8 +103,11 @@ public static async Task Main(string[] args)
{ "j|json", "Output all Values as JSON", j => { if (j != null) jsonvalues = true; } },
{ "v|verbose", "Verbose output", v => { if (v != null) verbose = true; } },
{ "s|subscribe", "Subscribe", s => { if (s != null) subscribe = true; } },
{ "rc|reverseconnect=", "Connect using the reverse connect endpoint. (e.g. rc=opc.tcp://localhost:65300)", (string url) => reverseConnectUrlString = url},
};

ReverseConnectManager reverseConnectManager = null;

try
{
// parse command line and set options
Expand Down Expand Up @@ -159,8 +166,18 @@ public static async Task Main(string[] args)
throw new ErrorExitException("Application instance certificate invalid!", ExitCode.ErrorCertificate);
}

if (reverseConnectUrlString != null)
{
// start the reverse connection manager
output.WriteLine("Create reverse connection endpoint at {0}.", reverseConnectUrlString);
reverseConnectManager = new ReverseConnectManager();
reverseConnectManager.AddEndpoint(new Uri(reverseConnectUrlString));
reverseConnectManager.StartService(config);
}

// wait for timeout or Ctrl-C
var quitEvent = ConsoleUtils.CtrlCHandler();
var quitCTS = new CancellationTokenSource();
var quitEvent = ConsoleUtils.CtrlCHandler(quitCTS);

// connect to a server until application stops
bool quit = false;
Expand All @@ -178,11 +195,10 @@ public static async Task Main(string[] args)
}

// create the UA Client object and connect to configured server.
using (UAClient uaClient = new UAClient(
application.ApplicationConfiguration, output, ClientBase.ValidateResponse)
{

using (UAClient uaClient = new UAClient(application.ApplicationConfiguration, reverseConnectManager, output, ClientBase.ValidateResponse) {
AutoAccept = autoAccept,
SessionLifeTime = 60000,
SessionLifeTime = 60_000,
})
{
// set user identity
Expand All @@ -191,7 +207,7 @@ public static async Task Main(string[] args)
uaClient.UserIdentity = new UserIdentity(username, userpassword ?? string.Empty);
}

bool connected = await uaClient.ConnectAsync(serverUrl.ToString(), false).ConfigureAwait(false);
bool connected = await uaClient.ConnectAsync(serverUrl.ToString(), !noSecurity, quitCTS.Token).ConfigureAwait(false);
if (connected)
{
output.WriteLine("Connected! Ctrl-C to quit.");
Expand Down Expand Up @@ -261,7 +277,7 @@ public static async Task Main(string[] args)
}

await samples.SubscribeAllValuesAsync(uaClient,
variableIds: new NodeCollection(variables.Take(100)),
variableIds: new NodeCollection(variables),
samplingInterval: 1000,
publishingInterval: 5000,
queueSize: 10,
Expand Down Expand Up @@ -311,6 +327,11 @@ public static async Task Main(string[] args)
{
output.WriteLine(ex.Message);
}
finally
{
Utils.SilentDispose(reverseConnectManager);
output.Close();
}
}
}
}
59 changes: 50 additions & 9 deletions Applications/ConsoleReferenceClient/UAClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using System;
using System.Collections;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Client;
Expand All @@ -51,6 +52,19 @@ public UAClient(ApplicationConfiguration configuration, TextWriter writer, Actio
m_output = writer;
m_configuration = configuration;
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
m_reverseConnectManager = null;
}

/// <summary>
/// Initializes a new instance of the UAClient class for reverse connections.
/// </summary>
public UAClient(ApplicationConfiguration configuration, ReverseConnectManager reverseConnectManager, TextWriter writer, Action<IList, IList> validateResponse)
{
m_validateResponse = validateResponse;
m_output = writer;
m_configuration = configuration;
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
m_reverseConnectManager = reverseConnectManager;
}
#endregion

Expand Down Expand Up @@ -117,7 +131,7 @@ public void Dispose()
/// <summary>
/// Creates a session with the UA server
/// </summary>
public async Task<bool> ConnectAsync(string serverUrl, bool useSecurity = true)
public async Task<bool> ConnectAsync(string serverUrl, bool useSecurity = true, CancellationToken ct = default)
{
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));

Expand All @@ -129,19 +143,47 @@ public async Task<bool> ConnectAsync(string serverUrl, bool useSecurity = true)
}
else
{
m_output.WriteLine("Connecting to... {0}", serverUrl);
ITransportWaitingConnection connection = null;
EndpointDescription endpointDescription = null;
if (m_reverseConnectManager != null)
{
m_output.WriteLine("Waiting for reverse connection to.... {0}", serverUrl);
do
{
using (var cts = new CancellationTokenSource(30_000))
using (var linkedCTS = CancellationTokenSource.CreateLinkedTokenSource(ct, cts.Token))
{
connection = await m_reverseConnectManager.WaitForConnection(new Uri(serverUrl), null, linkedCTS.Token).ConfigureAwait(false);
if (connection == null)
{
throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out.");
}
if (endpointDescription == null)
{
m_output.WriteLine("Discover reverse connection endpoints....");
endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, connection, useSecurity);
connection = null;
}
}
} while (connection == null);
}
else
{
m_output.WriteLine("Connecting to... {0}", serverUrl);
endpointDescription = CoreClientUtils.SelectEndpoint(m_configuration, serverUrl, useSecurity);
}

// 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, useSecurity);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

// Create the session
var session = await Opc.Ua.Client.Session.Create(
m_configuration,
connection,
endpoint,
true,
connection == null,
false,
m_configuration.ApplicationName,
SessionLifeTime,
Expand Down Expand Up @@ -240,7 +282,7 @@ private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
return;
}

var state = m_reconnectHandler.BeginReconnect(m_session, ReconnectPeriod, Client_ReconnectComplete);
var state = m_reconnectHandler.BeginReconnect(m_session, m_reverseConnectManager, ReconnectPeriod, Client_ReconnectComplete);
if (state == SessionReconnectHandler.ReconnectState.Triggered)
{
Utils.LogInfo("KeepAlive status {0}, reconnect status {1}, reconnect period {2}ms.", e.Status, state, ReconnectPeriod);
Expand Down Expand Up @@ -281,9 +323,7 @@ private void Client_ReconnectComplete(object sender, EventArgs e)
{
m_output.WriteLine("--- RECONNECTED TO NEW SESSION --- {0}", m_reconnectHandler.Session.SessionId);
var session = m_session;
session.KeepAlive -= Session_KeepAlive;
m_session = m_reconnectHandler.Session as Session;
m_session.KeepAlive += Session_KeepAlive;
m_session = m_reconnectHandler.Session;
Utils.SilentDispose(session);
}
else
Expand Down Expand Up @@ -335,9 +375,10 @@ protected virtual void CertificateValidation(CertificateValidator sender, Certif

#region Private Fields
private object m_lock = new object();
private ReverseConnectManager m_reverseConnectManager;
private ApplicationConfiguration m_configuration;
private SessionReconnectHandler m_reconnectHandler;
private Session m_session;
private ISession m_session;
private readonly TextWriter m_output;
private readonly Action<IList, IList> m_validateResponse;
#endregion
Expand Down
3 changes: 2 additions & 1 deletion Applications/ConsoleReferenceServer/ConsoleUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,13 @@ public static void LogTest()
/// Create an event which is set if a user
/// enters the Ctrl-C key combination.
/// </summary>
public static ManualResetEvent CtrlCHandler()
public static ManualResetEvent CtrlCHandler(CancellationTokenSource cts = default)
{
var quitEvent = new ManualResetEvent(false);
try
{
Console.CancelKeyPress += (_, eArgs) => {
cts.Cancel();
quitEvent.Set();
eArgs.Cancel = true;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ private void DoSimulation(object state)
if (m_success > 0)
{
m_missed++;
Utils.LogInfo("Alarms: Missed Loop {1} Success {2}", m_missed, m_success);
Utils.LogInfo("Alarms: Missed Loop {0} Success {1}", m_missed, m_success);
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions Docs/ReverseConnect.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ The Reverse Connect option consists of the following elements:
* Updated client library which support to
- Configure a client endpoint to accept *ReverseHello* messages using a *ReverseConnectManager*.
- 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.
* The updated C# [Reference Server](../Applications/ConsoleReferenceServer) with reverse connect support.
* 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.
* The C# [Console Reference Server](../Applications/ConsoleReferenceServer) with reverse connect support in the configuration xml.
* The C# Core [Console Reference Client](../Applications/ConsoleReferenceClient) that can initiate a Reverse connection with command line options.
* A modified C# [Aggregation Server](https://github.com/OPCFoundation/UA-.NETStandard-Samples/tree/master/Workshop/Aggregation) that supports incoming and outgoing reverse connections.

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

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



0 comments on commit f25cc4d

Please sign in to comment.