/* ======================================================================== * Copyright (c) 2005-2026 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/ * ======================================================================*/ using Opc.Ua.Aas.V3; using System; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Opc.Ua; using Opc.Ua.Aas; using Opc.Ua.Aas.Server.Materialization; using Opc.Ua.Aas.Server.Packaging; using Opc.Ua.Aas.Server.Registry; namespace AasSample { /// /// Creates the small AAS environment, registry documents, and DPP metadata used by the sample. /// public static class AasSampleData { /// /// The materialized AAS instance namespace exposed by the sample server. /// public const string InstanceNamespaceUri = "urn:opcfoundation.org:UA:AasSample:BatteryPassport"; /// /// The sample Asset Administration Shell identifier. /// public const string ShellId = "urn:opcfoundation:sample:aas:battery-pack:0001"; /// /// The nameplate submodel identifier. /// public const string NameplateSubmodelId = "urn:opcfoundation:sample:submodel:nameplate:0001"; /// /// The battery passport submodel identifier. /// public const string PassportSubmodelId = "urn:opcfoundation:sample:submodel:battery-passport:0001"; /// /// The published package identifier. /// public const string PackageId = "urn:opcfoundation:sample:aasx:battery-passport:0001"; /// /// Creates the complete sample data set. /// public static async ValueTask CreateAsync(CancellationToken cancellationToken) { AasDppIdentifierResult carbonFootprint = AasDppIdentifier.Construct("0173-1#02-AAO677#002"); AasDisclosureDecision passportDisclosure = AasDppDisclosurePolicy.Map( AasDppRegulatoryClass.AvailableToPublic); AasDisclosureDecision controlledDisclosure = AasDppDisclosurePolicy.Map( AasDppRegulatoryClass.LegitimateInterestAndCommission); AasSubmodel nameplate = CreateNameplateSubmodel(); AasSubmodel passport = CreatePassportSubmodel(carbonFootprint.Iri); AasConceptDescription concept = new() { Id = carbonFootprint.Iri, IdShort = Present("CarbonFootprintConcept") }; AasEnvironment environment = new() { AssetAdministrationShells = Present(new ArrayOf(new[] { new AasShell { Id = ShellId, IdShort = Present("BatteryPack0001"), AssetInformation = new AasAssetInformation { AssetKind = AASAssetKindDataType.Instance, GlobalAssetId = Present("urn:epc:id:sgtin:0614141.107346.0001"), SpecificAssetIds = Present(new ArrayOf(new[] { SpecificAssetId("serialNumber", "BP-2026-0001"), SpecificAssetId("manufacturerPartId", "BATT-MODULE-42") })) } } })), Submodels = Present(new ArrayOf(new[] { nameplate, passport })), ConceptDescriptions = Present(new ArrayOf(new[] { concept })) }; ByteString environmentDocument = await WriteEnvironmentAsync(environment, cancellationToken) .ConfigureAwait(false); ByteString nameplateDocument = await WriteEnvironmentAsync( new AasEnvironment { Submodels = Present(new ArrayOf(new[] { nameplate })) }, cancellationToken).ConfigureAwait(false); ByteString passportDocument = await WriteEnvironmentAsync( new AasEnvironment { Submodels = Present(new ArrayOf(new[] { passport })) }, cancellationToken).ConfigureAwait(false); ByteString conceptDocument = await WriteEnvironmentAsync( new AasEnvironment { ConceptDescriptions = Present(new ArrayOf(new[] { concept })) }, cancellationToken).ConfigureAwait(false); ByteString package = ByteString.From(Encoding.UTF8.GetBytes( "AASX placeholder package for the AAS V3 battery passport sample.")); string packageDigest = AasPackageIntegrity.ComputeDigest(package, AasPackageIntegrity.Sha256); return new AasSampleDataset( environment, environmentDocument, RegistryRequests( nameplateDocument, passportDocument, conceptDocument, package, passportDisclosure, controlledDisclosure, packageDigest), MaterializationDocuments(environmentDocument), carbonFootprint, passportDisclosure, controlledDisclosure, package, packageDigest); } private static ArrayOf RegistryRequests( ByteString nameplateDocument, ByteString passportDocument, ByteString conceptDocument, ByteString package, AasDisclosureDecision passportDisclosure, AasDisclosureDecision controlledDisclosure, string packageDigest) { return new AasUpsertResourceRequest[] { SubmodelRequest( NameplateSubmodelId, "Battery nameplate", nameplateDocument, AASDisclosureTierDataType.Public, string.Empty), SubmodelRequest( PassportSubmodelId, "EU battery passport", passportDocument, passportDisclosure.Tier, passportDisclosure.Authorization), new() { GroupSourceIdentity = "urn:opcfoundation:sample:concept-dictionary:battery-passport", ResourceSourceIdentity = "https://rdf.eclass.eu/resource/0173-1_02-AAO677_002", GroupKind = AasRegistryEntityKind.ConceptDictionary, ResourceKind = AasRegistryEntityKind.ConceptDescription, Name = "Carbon footprint concept", Content = conceptDocument }, new() { GroupSourceIdentity = "urn:opcfoundation:sample:package-store:battery-passports", ResourceSourceIdentity = PackageId, GroupKind = AasRegistryEntityKind.PackageStore, ResourceKind = AasRegistryEntityKind.Package, Name = "Battery passport AASX package", Content = package, Format = "aasx/3.0", ContentType = "application/asset-administration-shell-package+xml", Description = "Digest " + packageDigest, DisclosureTier = controlledDisclosure.Tier, Authorization = Authorization(controlledDisclosure.Authorization) } }.ToArrayOf(); } private static AasUpsertResourceRequest SubmodelRequest( string submodelId, string name, ByteString content, AASDisclosureTierDataType tier, string authorization) { return new AasUpsertResourceRequest { GroupSourceIdentity = ShellId, ResourceSourceIdentity = submodelId, GroupKind = AasRegistryEntityKind.Shell, ResourceKind = AasRegistryEntityKind.Submodel, Name = name, Content = content, DisclosureTier = tier, Authorization = Authorization(authorization), SpecificAssetIds = new AasRegistryAssetLink[] { new() { Name = "serialNumber", Value = "BP-2026-0001" }, new() { Name = "manufacturerPartId", Value = "BATT-MODULE-42" } }.ToArrayOf() }; } private static ArrayOf MaterializationDocuments(ByteString environmentDocument) { return new AasMaterializationDocument[] { new() { Xid = "battery-passport-environment", VersionId = "v1", SourceIdentity = ShellId, Kind = AasMaterializationDocumentKind.Environment, Content = environmentDocument, Format = "aas/3.0+json" } }.ToArrayOf(); } private static AasSubmodel CreateNameplateSubmodel() { return new AasSubmodel { Id = NameplateSubmodelId, IdShort = Present("Nameplate"), SubmodelElements = Present(new AasSubmodelElement[] { Property("ManufacturerName", AASDataTypeDefXsdDataType.String, "Contoso Battery Systems"), Property("SerialNumber", AASDataTypeDefXsdDataType.String, "BP-2026-0001") }.ToArrayOf()) }; } private static AasSubmodel CreatePassportSubmodel(string carbonFootprintSemanticId) { return new AasSubmodel { Id = PassportSubmodelId, IdShort = Present("BatteryPassport"), SubmodelElements = Present(new AasSubmodelElement[] { Property("PassportIdentifier", AASDataTypeDefXsdDataType.String, "DPP-BP-2026-0001"), Property( "CarbonFootprint", AASDataTypeDefXsdDataType.String, "87.4", Reference(carbonFootprintSemanticId)), Property("StateOfHealth", AASDataTypeDefXsdDataType.Double, 98.2d), Property("DisclosureClass", AASDataTypeDefXsdDataType.String, "available to the public"), new AasOperation { IdShort = Present("RecalculatePassport"), InputVariables = Present(new AasSubmodelElement[] { Property("Reason", AASDataTypeDefXsdDataType.String, "operator-request") }.ToArrayOf()), OutputVariables = Present(new AasSubmodelElement[] { Property("Result", AASDataTypeDefXsdDataType.String, "pending") }.ToArrayOf()) } }.ToArrayOf()) }; } private static AasProperty Property( string idShort, AASDataTypeDefXsdDataType valueType, Variant value, AASReferenceDataType? semanticId = null) { return new AasProperty { IdShort = Present(idShort), ValueType = valueType, Value = Present(value), SemanticId = semanticId is null ? default : Present(semanticId) }; } private static AASSpecificAssetIdDataType SpecificAssetId(string name, string value) { return new AASSpecificAssetIdDataType { Name = name, Value = value }; } private static AASReferenceDataType Reference(string value) { return new AASReferenceDataType { Type = AASReferenceTypesDataType.ExternalReference, Keys = new AASKeyDataType[] { new() { Type = AASKeyTypesDataType.GlobalReference, Value = value } }.ToArrayOf() }; } private static ArrayOf Authorization(string authorization) { return string.IsNullOrEmpty(authorization) ? ArrayOf.Empty : new AASAuthorizationOptionDataType[] { new() { Type = "DPP", ResourceUri = authorization } }.ToArrayOf(); } private static AasOptional Present(T value) where T : notnull { return AasOptional.Present(value); } private static async ValueTask WriteEnvironmentAsync( AasEnvironment environment, CancellationToken cancellationToken) { using var stream = new MemoryStream(); await new AasJsonWriter().WriteAsync(stream, environment, cancellationToken).ConfigureAwait(false); return ByteString.From(stream.ToArray()); } } /// /// Complete sample data produced before the server starts. /// public sealed class AasSampleDataset { /// /// Initializes the sample data set. /// public AasSampleDataset( AasEnvironment environment, ByteString environmentDocument, ArrayOf registryRequests, ArrayOf materializationDocuments, AasDppIdentifierResult carbonFootprintIdentifier, AasDisclosureDecision passportDisclosure, AasDisclosureDecision controlledDisclosure, ByteString package, string packageDigest) { Environment = environment; EnvironmentDocument = environmentDocument; RegistryRequests = registryRequests; MaterializationDocuments = materializationDocuments; CarbonFootprintIdentifier = carbonFootprintIdentifier; PassportDisclosure = passportDisclosure; ControlledDisclosure = controlledDisclosure; Package = package; PackageDigest = packageDigest; } /// /// Gets the AAS environment used for metamodel materialization. /// public AasEnvironment Environment { get; } /// /// Gets the serialized complete AAS environment. /// public ByteString EnvironmentDocument { get; } /// /// Gets the registry seed requests. /// public ArrayOf RegistryRequests { get; } /// /// Gets the coordinator document set. /// public ArrayOf MaterializationDocuments { get; } /// /// Gets the DPP semantic identifier construction result. /// public AasDppIdentifierResult CarbonFootprintIdentifier { get; } /// /// Gets the passport disclosure mapping. /// public AasDisclosureDecision PassportDisclosure { get; } /// /// Gets the controlled disclosure mapping. /// public AasDisclosureDecision ControlledDisclosure { get; } /// /// Gets the sample package bytes. /// public ByteString Package { get; } /// /// Gets the sample package digest. /// public string PackageDigest { get; } } }