/* ======================================================================== * 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 { /// /// Templates for the . Emits one /// partial record {Type}Record : {Parent}Record per OPC UA /// ObjectType that derives from BaseEventType. Each /// record declares only the fields introduced by its type; /// inherited fields come from the parent record class. The /// generated chain mirrors the OPC UA event-type hierarchy. /// /// /// In addition to the record class, each emitted record contains /// a nested public static class Decoder with a positional /// StandardFields browse-path table and a /// Decode(IReadOnlyList<Variant>) method that /// populates every property (own + inherited) from positional /// variant reads. A per-file /// Register{ModelPrefix}Decoders(this EventRecordDecoderRegistry) /// extension method registers every emitted decoder against a /// caller-supplied Opc.Ua.EventRecordDecoderRegistry. /// internal static class EventRecordTemplates { /// /// Single output file template. /// public static readonly TemplateString File = TemplateString.Parse( $$""" {{Tokens.CodeHeader}} #nullable enable annotations #nullable disable warnings namespace {{Tokens.Namespace}} { {{Tokens.ListOfTypes}} {{Tokens.ListOfActivatorRegistrations}} } """); /// /// One record class per ObjectType. Generated as a /// partial record so consumers can add convenience /// properties without touching the generated source. /// public static readonly TemplateString RecordClass = TemplateString.Parse( $$""" /// /// Decoded event record for OPC UA event type /// {{Tokens.SymbolicName}}. Add convenience members in /// a hand-written partial declaration of this same record. /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")] public partial record {{Tokens.ClassName}} : {{Tokens.BaseClassName}} { {{Tokens.ListOfProperties}} /// /// Source-generated positional decoder for /// . Reads variant fields /// at indices fixed by and /// populates every own + inherited init-only property. /// public {{Tokens.AccessModifier}}static class Decoder { /// /// Browse paths in positional order. The composed /// registry layout (and any filter built from it) /// uses the same positional convention; the /// runtime remaps from the composed positions /// before calling . /// public static readonly global::Opc.Ua.QualifiedName[][] StandardFields = [ {{Tokens.ListOfFields}} ]; /// /// Decodes into a /// . /// /// Positional variant array /// matching . /// null when /// is null or empty; /// a fully populated record otherwise. public static {{Tokens.ClassName}}? Decode( global::System.Collections.Generic.IReadOnlyList fields) { if (fields == null || fields.Count == 0) { return null; } return new {{Tokens.ClassName}} { {{Tokens.ListOfDecodedFields}} }; } } /// /// Source-generated event-filter factory for /// . Produces an /// whose /// where clause restricts events to /// {{Tokens.SymbolicName}} and whose select /// clauses come from a supplied /// /// (or its Default). /// public {{Tokens.AccessModifier}}static class EventFilters { /// /// Builds the event filter. /// /// Decoder registry whose /// composed StandardFields form the select /// clauses. When null, /// /// is used. public static global::Opc.Ua.EventFilter Build( global::Opc.Ua.EventRecordDecoderRegistry? registry = null) => global::Opc.Ua.EventFilterFactory.Create( global::Opc.Ua.ObjectTypeIds.{{Tokens.SymbolicName}}, registry); } } """); /// /// One init-only property per directly-declared field on the type. /// public static readonly TemplateString FieldProperty = TemplateString.Parse( $$""" /// {{Tokens.Description}} public {{Tokens.DataType}} {{Tokens.PropertyName}} { get; init; } """); /// /// One QualifiedName[] entry per StandardFields row. /// For TwoStateVariable children the path has two segments /// (the variable + its Id property). /// public static readonly TemplateString StandardFieldEntry = TemplateString.Parse( $$""" {{Tokens.ChildPath}}, """); /// /// One property assignment per StandardFields position. /// public static readonly TemplateString DecodedField = TemplateString.Parse( $$""" {{Tokens.PropertyName}} = global::Opc.Ua.EventRecordFieldReaders.{{Tokens.ClientMethod}}(fields, {{Tokens.FieldIndex}}), """); /// /// Per-file static class that registers every emitted decoder /// onto a caller-supplied Opc.Ua.EventRecordDecoderRegistry. /// public static readonly TemplateString RegistrationExtension = TemplateString.Parse( $$""" /// /// Registration extensions for the source-generated event /// record decoders in this file. The /// {{Tokens.ClientMethod}} extension method is idempotent /// via EventRecordDecoderRegistry.TryRegister so /// duplicate calls are safe. /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")] public static class {{Tokens.ClassName}} { /// /// Registers every event-record decoder in this model /// with . Returns the /// registry for chaining. /// public static global::Opc.Ua.EventRecordDecoderRegistry {{Tokens.ClientMethod}}( this global::Opc.Ua.EventRecordDecoderRegistry registry) { {{Tokens.ListOfActivatorRegistrations}} return registry; } } """); /// /// One TryRegister call per emitted record decoder. /// public static readonly TemplateString DecoderRegistration = TemplateString.Parse( $$""" registry.TryRegister( global::Opc.Ua.ObjectTypeIds.{{Tokens.SymbolicName}}, {{Tokens.ClassName}}.Decoder.StandardFields, {{Tokens.ClassName}}.Decoder.Decode); """); } }