/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ namespace Opc.Ua.SourceGeneration { /// /// Template strings /// internal static class ClientApiTemplates { /// /// Client API file template /// public static readonly TemplateString File = TemplateString.Parse( $$""" {{Tokens.CodeHeader}} using System; namespace {{Tokens.Prefix}} { {{Tokens.ServiceSets}} } """); /// /// Client API service set template (interface + class) /// public static readonly TemplateString ServiceSet = TemplateString.Parse( $$""" /// /// An interface used by by clients to access a UA server. /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")] public interface I{{Tokens.ServiceSet}}ClientMethods{{Tokens.BaseInterfaces}} { {{Tokens.ClientMethod}} } /// /// The client side interface for a UA server. /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")] [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] public partial class {{Tokens.ServiceSet}}Client : global::Opc.Ua.ClientBase, global::Opc.Ua.I{{Tokens.ServiceSet}}ClientMethods { /// /// Intializes the object with a channel and a message context. /// public {{Tokens.ServiceSet}}Client( global::Opc.Ua.ITransportChannel channel, global::Opc.Ua.ITelemetryContext telemetry) : base(channel, telemetry) { } {{Tokens.ClientApi}} } """); /// /// Client API service set template (interface only, no class) /// public static readonly TemplateString ServiceSetInterfaceOnly = TemplateString.Parse( $$""" /// /// An interface used by by clients to access a UA server. /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")] public interface I{{Tokens.ServiceSet}}ClientMethods{{Tokens.BaseInterfaces}} { {{Tokens.ClientMethod}} } """); /// /// Client API interface method template /// public static readonly TemplateString InterfaceMethods = TemplateString.Parse( $$""" /// /// Invokes the {{Tokens.Name}} service. /// {{Tokens.ClientMethodAsync}} """); /// /// Client API method implementations template /// public static readonly TemplateString Methods = TemplateString.Parse( $$""" /// /// Invokes the {{Tokens.Name}} service. /// /// /// The state machine is pooled, so the returned task must be awaited once, /// like any value task. A completed call keeps no reference to its response: /// a call that waited long enough to be promoted (a Publish waits for the /// publishing interval) would otherwise keep every later response alive /// until the next full collection. /// #if NET6_0_OR_GREATER [global::System.Runtime.CompilerServices.AsyncMethodBuilder( typeof(global::System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<>))] #endif {{Tokens.ClientMethodAsync}} { {{Tokens.Name}}Request request = new {{Tokens.Name}}Request(); {{Tokens.Name}}Response response = null; {{Tokens.RequestParameters}} UpdateRequestHeader(request, requestHeader == null, "{{Tokens.Name}}"); try { global::Opc.Ua.IServiceResponse? genericResponse = await TransportChannel.SendRequestAsync(request, ct).ConfigureAwait(false); if (genericResponse == null) { throw new global::Opc.Ua.ServiceResultException(StatusCodes.BadUnknownResponse); } ValidateResponse(genericResponse.ResponseHeader); response = ({{Tokens.Name}}Response)genericResponse; } finally { RequestCompleted(request, response, "{{Tokens.Name}}"); } return response; } /// /// Invokes the {{Tokens.Name}} service synchronously. /// [global::System.Obsolete("Sync methods are deprecated in this version. Use {{Tokens.Name}}Async instead.")] {{Tokens.ClientMethodSync}} { {{Tokens.Name}}Request request = new {{Tokens.Name}}Request(); {{Tokens.Name}}Response? response = null; {{Tokens.RequestParameters}} UpdateRequestHeader(request, requestHeader == null, "{{Tokens.Name}}"); try { global::Opc.Ua.IServiceResponse? genericResponse = TransportChannel .SendRequestAsync(request, global::System.Threading.CancellationToken.None) .AsTask() .GetAwaiter() .GetResult(); if (genericResponse == null) { throw new global::Opc.Ua.ServiceResultException(StatusCodes.BadUnknownResponse); } ValidateResponse(genericResponse.ResponseHeader); response = ({{Tokens.Name}}Response)genericResponse; {{Tokens.ResponseParameters}} } finally { RequestCompleted(request, response, "{{Tokens.Name}}"); } return response.ResponseHeader; } /// /// Begins an asynchronous invocation of the {{Tokens.Name}} service. /// [global::System.Obsolete("Begin/End methods are deprecated in this version. Use {{Tokens.Name}}Async instead.")] {{Tokens.ClientMethodBegin}} { {{Tokens.Name}}Request request = new {{Tokens.Name}}Request(); {{Tokens.RequestParameters}} UpdateRequestHeader(request, requestHeader == null, "{{Tokens.Name}}"); global::System.Threading.Tasks.Task task = TransportChannel .SendRequestAsync(request, global::System.Threading.CancellationToken.None) .AsTask(); global::System.Threading.Tasks.TaskCompletionSource tcs = new global::System.Threading.Tasks.TaskCompletionSource(asyncState); task.ContinueWith(t => { if (t.IsFaulted) { tcs.TrySetException(t.Exception!.InnerExceptions); } else if (t.IsCanceled) { tcs.TrySetCanceled(); } else { tcs.TrySetResult(t.Result); } callback?.Invoke(tcs.Task); }, global::System.Threading.Tasks.TaskScheduler.Default); return tcs.Task; } /// /// Finishes an asynchronous invocation of the {{Tokens.Name}} service. /// [global::System.Obsolete("Begin/End methods are deprecated in this version. Use {{Tokens.Name}}Async instead.")] {{Tokens.ClientMethodEnd}} { {{Tokens.Name}}Response response = null; try { global::Opc.Ua.IServiceResponse? genericResponse = ((global::System.Threading.Tasks.Task)result) .GetAwaiter() .GetResult(); if (genericResponse == null) { throw new global::Opc.Ua.ServiceResultException(StatusCodes.BadUnknownResponse); } ValidateResponse(genericResponse.ResponseHeader); response = ({{Tokens.Name}}Response)genericResponse; {{Tokens.ResponseParameters}} } finally { RequestCompleted(null, response, "{{Tokens.Name}}"); } return response.ResponseHeader; } """); } }