/* ========================================================================
* 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; }
}
}