diff --git a/.github/workflows/run_newman.yml b/.github/workflows/run_newman.yml
new file mode 100644
index 000000000..6d70002be
--- /dev/null
+++ b/.github/workflows/run_newman.yml
@@ -0,0 +1,43 @@
+name: newman_tests
+
+on:
+ push
+ #workflow_dispatch
+
+jobs:
+ api-test:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 3.1.x
+ - name: Setup Node
+ uses: actions/setup-node@v1
+ with:
+ node-version: 12.x
+ - name: Install Newman and HTML Reporter
+ run: |
+ npm install -g newman@5.3.1
+ npm install -g newman-reporter-htmlextra@1.22.7
+ - name: Restore dependencies
+ run: dotnet restore ./src/AasxServerBlazor/AasxServerBlazor.csproj
+ - name: Build
+ run: dotnet build ./src/AasxServerBlazor/AasxServerBlazor.csproj -c Release -o ./build
+ - name: Start Test Server And Execute Test Scripts
+ run: |
+ cd build
+ dotnet ./AasxServerBlazor.dll --aspnet --data-path ../test/rest_api/data &
+ sleep 10
+ cd ..
+ newman run ./test/rest_api/postman/SubmodelElementTest.json.postman_collection -e ./test/rest_api/postman/GitHubActionEnvironment.json.postman_environment --reporters cli,htmlextra --reporter-htmlextra-export report.html --insecure
+ kill -9 `lsof -i:5001 -t`
+ - name: Upload Report
+ uses: actions/upload-artifact@v2
+ if: always()
+ with:
+ name: report.html
+ path: ./report.html
diff --git a/README.md b/README.md
index bd0d347b6..335f9f574 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ https://github.com/admin-shell-io/aasx-server/workflows/Build-and-publish-docker
)
AASX Server serves Industrie 4.0 AASX packages accessible by REST, OPC UA and
-MQTT protocols.
+MQTT protocols..
The AASX Server is based on code of AASX Package Explorer (
https://github.com/admin-shell-io/aasx-package-explorer
diff --git a/src/AasxServer.sln b/src/AasxServer.sln
index e7b24bfdd..3b7aa454f 100644
--- a/src/AasxServer.sln
+++ b/src/AasxServer.sln
@@ -24,6 +24,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IO.Swagger.Lib", "IO.Swagge
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IO.Swagger.Registry", "IO.Swagger.Registry.Lib\IO.Swagger.Registry.csproj", "{F7B9E4DC-FDCD-4F6B-A5C5-DF416802B067}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IO.Swagger", "IO.Swagger\IO.Swagger.csproj", "{478697A0-89E4-4039-902E-0F13EA4EAD72}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IO.Swagger.Test", "IO.Swagger.Test\IO.Swagger.Test.csproj", "{1E721E5E-6E56-4E2C-A8ED-AB381B6C7E8A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/src/AasxServerBlazor/AasxServerBlazor.csproj b/src/AasxServerBlazor/AasxServerBlazor.csproj
index 08e60198b..dd45235d3 100644
--- a/src/AasxServerBlazor/AasxServerBlazor.csproj
+++ b/src/AasxServerBlazor/AasxServerBlazor.csproj
@@ -24,8 +24,6 @@
-
-
PreserveNewest
diff --git a/src/AasxServerBlazor/Program.cs b/src/AasxServerBlazor/Program.cs
index 54be5cbb6..54f435792 100644
--- a/src/AasxServerBlazor/Program.cs
+++ b/src/AasxServerBlazor/Program.cs
@@ -19,12 +19,19 @@ public static void Main(string[] args)
{
Console.WriteLine(Directory.GetCurrentDirectory());
+ //TODO DELETE this piece of code make wrong assumptions about the current directory
+ /*
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json").Build();
+ */
+
+ //TODO REFACTOR it is not ensured that this is the actual endpoint. Why is this information needed so early in the lifecycle?
+ /*
string[] url = config["Kestrel:Endpoints:Http:Url"].Split(':');
if (url[2] != null)
- AasxServer.Program.blazorPort = url[2];
+ AasxServer.Program.blazorHostPort = url[2];
+ */
CreateHostBuilder(args).Build().RunAsync();
@@ -38,7 +45,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
{
webBuilder
.UseStartup()
- .UseUrls("http://*:5000")
+ //TODO CHECK why is a manuel override used? server does not uses same port consistently (sometimes 5000, sometimes port specified in appconfig.json) across different platforms
+ //.UseUrls("http://*:5000")
/*
.UseKestrel(options =>
{
diff --git a/src/AasxServerBlazor/Properties/launchSettings.json b/src/AasxServerBlazor/Properties/launchSettings.json
index 41dd6f39a..98355ffbe 100644
--- a/src/AasxServerBlazor/Properties/launchSettings.json
+++ b/src/AasxServerBlazor/Properties/launchSettings.json
@@ -18,7 +18,17 @@
},
"AasxServerBlazor": {
"commandName": "Project",
- "commandLineArgs": "--rest --no-security --data-path \"C:\\Development\\PCF\" --edit --external-blazor http://localhost:5001",
+ "commandLineArgs": "--aspnet --no-security --data-path \"C:\\Development\\AASX\" --edit --external-blazor http://localhost:5001",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:5001",
+ "jsWebView2Debugging": true
+ },
+ "TestRestApi": {
+ "commandName": "Project",
+ "commandLineArgs": "--aspnet --no-security --data-path \"..\\..\\test\\rest_api\\data\" --edit --external-blazor http://localhost:5001",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
diff --git a/src/AasxServerStandardBib/Program.cs b/src/AasxServerStandardBib/Program.cs
index 57d995e4d..6adce7788 100644
--- a/src/AasxServerStandardBib/Program.cs
+++ b/src/AasxServerStandardBib/Program.cs
@@ -169,6 +169,7 @@ private class CommandLineArguments
public bool Rest { get; set; }
public bool Opc { get; set; }
public bool Mqtt { get; set; }
+ public bool AspNet { get; set; }//hack solution to start without grapevine
public bool DebugWait { get; set; }
public int? OpcClientRate { get; set; }
public string[] Connect { get; set; }
@@ -474,6 +475,11 @@ private static int Run(CommandLineArguments a)
envi++;
}
}
+ else
+ {
+ Console.WriteLine($"AASX Data Path does not exist. \n\tCurrent Dir\t{Directory.GetCurrentDirectory()}\n\tAASX Data Path\t{AasxHttpContextHelper.DataPath}");
+ throw new Exception("Invalid AASX Data Path.");
+ }
AasxHttpContextHelper.securityInit(); // read users and access rights form AASX Security
AasxHttpContextHelper.serverCertsInit(); // load certificates of auth servers
@@ -695,6 +701,10 @@ public static void Main(string[] args)
new[] {"--mqtt"},
"If set, starts a MQTT publisher"),
+ new Option(
+ new[] {"--aspnet"},
+ "If set, app is running using aspnet framework"),
+
new Option(
new[] {"--debug-wait"},
"If set, waits for Debugger to attach"),
@@ -704,6 +714,7 @@ public static void Main(string[] args)
"If set, starts an OPC client and refreshes on the given period " +
"(in milliseconds)"),
+ //TODO CHECK is the connect feature legacy code from Package Explorer?
new Option(
new[] {"--connect"},
"If set, connects to AAS connect server. " +
@@ -755,7 +766,7 @@ public static void Main(string[] args)
rootCommand.Handler = System.CommandLine.Invocation.CommandHandler.Create(
(CommandLineArguments a) =>
{
- if (!(a.Rest || a.Opc || a.Mqtt))
+ if (!(a.Rest || a.Opc || a.Mqtt || a.AspNet))
{
Console.Error.WriteLine($"Please specify --rest and/or --opc and/or --mqtt{nl}");
new HelpBuilder(new SystemConsole()).Write(rootCommand);
@@ -979,6 +990,7 @@ public static void connectPublish(string type, string json)
public static void connectThreadLoop()
{
+ //TODO CHECK is this leagy code?
bool newConnectData = false;
while (connectLoop)
diff --git a/src/IO.Swagger/.gitignore b/src/IO.Swagger/.gitignore
new file mode 100644
index 000000000..cd9b840e5
--- /dev/null
+++ b/src/IO.Swagger/.gitignore
@@ -0,0 +1,208 @@
+PID
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+bower_components/
+orleans.codegen.cs
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
diff --git a/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs b/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs
new file mode 100644
index 000000000..97b193c67
--- /dev/null
+++ b/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs
@@ -0,0 +1,61 @@
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace IO.Swagger.Attributes
+{
+ ///
+ /// Model state validation attribute
+ ///
+ public class ValidateModelStateAttribute : ActionFilterAttribute
+ {
+ ///
+ /// Called before the action method is invoked
+ ///
+ ///
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ // Per https://blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/
+ var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
+ if (descriptor != null)
+ {
+ foreach (var parameter in descriptor.MethodInfo.GetParameters())
+ {
+ object args = null;
+ if (context.ActionArguments.ContainsKey(parameter.Name))
+ {
+ args = context.ActionArguments[parameter.Name];
+ }
+
+ ValidateAttributes(parameter, args, context.ModelState);
+ }
+ }
+
+ if (!context.ModelState.IsValid)
+ {
+ context.Result = new BadRequestObjectResult(context.ModelState);
+ }
+ }
+
+ private void ValidateAttributes(ParameterInfo parameter, object args, ModelStateDictionary modelState)
+ {
+ foreach (var attributeData in parameter.CustomAttributes)
+ {
+ var attributeInstance = parameter.GetCustomAttribute(attributeData.AttributeType);
+
+ var validationAttribute = attributeInstance as ValidationAttribute;
+ if (validationAttribute != null)
+ {
+ var isValid = validationAttribute.IsValid(args);
+ if (!isValid)
+ {
+ modelState.AddModelError(parameter.Name, validationAttribute.FormatErrorMessage(parameter.Name));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/IO.Swagger/Controllers/AssetAdministrationShellRepositoryApi.cs b/src/IO.Swagger/Controllers/AssetAdministrationShellRepositoryApi.cs
new file mode 100644
index 000000000..d1cead5d8
--- /dev/null
+++ b/src/IO.Swagger/Controllers/AssetAdministrationShellRepositoryApi.cs
@@ -0,0 +1,1923 @@
+/*
+ * DotAAS Part 2 | HTTP/REST | Asset Administration Shell Repository
+ *
+ * An exemplary interface combination for the use case of an Asset Administration Shell Repository
+ *
+ * OpenAPI spec version: Final-Draft
+ *
+ * Generated by: https://github.com/swagger-api/swagger-codegen.git
+ */
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using AdminShellNS;
+using IO.Swagger.Attributes;
+using IO.Swagger.Helpers;
+using IO.Swagger.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.IdentityModel.Tokens;
+using Newtonsoft.Json;
+using Swashbuckle.AspNetCore.Annotations;
+
+namespace IO.Swagger.Controllers
+{
+ ///
+ ///
+ ///
+ //[Authorize]
+ [ApiController]
+ public class AssetAdministrationShellRepositoryApiController : ControllerBase
+ {
+ private static AASHelper aasHelper;
+ ///
+ /// Constructor
+ ///
+ public AssetAdministrationShellRepositoryApiController()
+ {
+ aasHelper = new AASHelper();
+ }
+
+ ///
+ /// Deletes an Asset Administration Shell
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Asset Administration Shell deleted successfully
+ [HttpDelete]
+ [Route("/shells/{aasIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("DeleteAssetAdministrationShellById")]
+ public virtual IActionResult DeleteAssetAdministrationShellById([FromRoute][Required] string aasIdentifier)
+ {
+ try
+ {
+ var deleted = aasHelper.DeleteAASAndAsset(Base64UrlEncoder.Decode(aasIdentifier));
+ if (deleted)
+ {
+ AasxServer.Program.signalNewData(2);
+ return NoContent();
+ }
+
+ return NotFound($"AAS not Found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Deletes a Concept Description
+ ///
+ /// The Concept Description’s unique id (BASE64-URL-encoded)
+ /// Concept Description deleted successfully
+ [HttpDelete]
+ [Route("/concept-descriptions/{cdIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("DeleteConceptDescriptionById")]
+ public virtual IActionResult DeleteConceptDescriptionById([FromRoute][Required] string cdIdentifier)
+ {
+ try
+ {
+ var conceptDescription = aasHelper.FindConceptDescription(Base64UrlEncoder.Decode(cdIdentifier), out int packageIndex);
+ if (conceptDescription == null)
+ {
+ return NotFound($"Concept Description not found.");
+ }
+
+ bool deleted = aasHelper.DeleteConceptDescription(conceptDescription, packageIndex);
+ if (deleted)
+ {
+ AasxServer.Program.signalNewData(1);//same tree
+ return NoContent();
+ }
+
+ return NotFound($"Could not delete the concept description. Please check the identifier again.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Deletes a submodel element at a specified path within the submodel elements hierarchy
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Submodel element deleted successfully
+ [HttpDelete]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("DeleteSubmodelElementByPath")]
+ public virtual IActionResult DeleteSubmodelElementByPath([FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath)
+ {
+ try
+ {
+ //Check if AAS exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out object parent);
+ if (submodelElement == null)
+ {
+ return NotFound($"Requested submodel element not found.");
+ }
+
+ var deleted = aasHelper.DeleteSubmodelElementByPath(submodelElement, parent);
+ if (deleted)
+ {
+ AasxServer.Program.signalNewData(1); //Same tree, structure changed
+ return NoContent();
+ }
+
+ return StatusCode(500, $"Could not delete Submodelelement. Please check the logs for more details.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Deletes a submodel element at a specified path within the submodel elements hierarchy
+ ///
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Submodel element deleted successfully
+ [HttpDelete]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("DeleteSubmodelElementByPathSubmodelRepo")]
+ public virtual IActionResult DeleteSubmodelElementByPathSubmodelRepo([FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath)
+ {
+ try
+ {
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out object parent);
+ if (submodelElement == null)
+ {
+ return NotFound($"Requested submodel element not found.");
+ }
+
+ var deleted = aasHelper.DeleteSubmodelElementByPath(submodelElement, parent);
+ if (deleted)
+ {
+ AasxServer.Program.signalNewData(1); // Same tree, structure may change
+ return NoContent();
+ }
+
+ return StatusCode(500, $"Could not delete Submodelelement. Please check the logs for more details.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Deletes the submodel reference from the Asset Administration Shell
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Submodel reference deleted successfully
+ [HttpDelete]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("DeleteSubmodelReferenceById")]
+ public virtual IActionResult DeleteSubmodelReferenceById([FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier)
+ {
+ try
+ {
+ bool deleted = aasHelper.DeleteSubmodelReferenceFromAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (deleted)
+ {
+ AasxServer.Program.signalNewData(1);//same tree structute may change
+ return NoContent();
+ }
+
+ return NotFound($"Could not delete the submodel reference from AAS");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns an appropriate serialization based on the specified format (see SerializationFormat)
+ ///
+ /// The Asset Administration Shells' unique ids (BASE64-URL-encoded)
+ /// The Submodels' unique ids (BASE64-URL-encoded)
+ /// Include Concept Descriptions?
+ /// Requested serialization based on SerializationFormat
+ [HttpGet]
+ [Route("/serialization")]
+ [ValidateModelState]
+ [SwaggerOperation("GenerateSerializationByIds")]
+ [SwaggerResponse(statusCode: 200, type: typeof(byte[]), description: "Requested serialization based on SerializationFormat")]
+ public virtual IActionResult GenerateSerializationByIds([FromQuery][Required()] List aasIds, [FromQuery][Required()] List submodelIds, [FromQuery][Required()] bool? includeConceptDescriptions)
+ {
+ try
+ {
+ AdminShellV20.AdministrationShellEnv outputEnv = new AdminShellV20.AdministrationShellEnv();
+
+ //Fetch AASs for the requested aasIds
+ var aaslist = aasHelper.FindAllAasByAASIds(aasIds);
+ outputEnv.AdministrationShells = aaslist;
+
+ //Fetch Submodels for the requested submodelIds
+ var submodellist = aasHelper.FindAllSubmodelsBySubmodelIds(submodelIds);
+ outputEnv.Submodels = submodellist;
+
+ //Fetch Concept Descriptions used in AAS/Submodels
+ if ((bool)includeConceptDescriptions)
+ {
+ var conceptDescriptionList = aasHelper.FindConceptDescriptionInAASs(aasIds);
+ aasHelper.FindAllConceptDescriptionsInSubmodels(submodelIds, conceptDescriptionList);
+ outputEnv.ConceptDescriptions.AddRange(conceptDescriptionList);
+ }
+
+ return new ObjectResult(outputEnv);
+
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns all Asset Administration Shells
+ ///
+ /// The key-value-pair of an Asset identifier
+ /// The Asset Administration Shell’s IdShort
+ /// Requested Asset Administration Shells
+ [HttpGet]
+ [Route("/shells")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAllAssetAdministrationShells")]
+ [SwaggerResponse(statusCode: 200, type: typeof(List), description: "Requested Asset Administration Shells")]
+ //public virtual IActionResult GetAllAssetAdministrationShells([FromQuery]List assetIds, [FromQuery]string idShort)
+ public virtual IActionResult GetAllAssetAdministrationShells([FromQuery] string assetIds, [FromQuery] string idShort)
+ {
+ try
+ {
+ var aasList = new List();
+
+ foreach (AdminShellPackageEnv env in AasxServer.Program.env)
+ {
+ if (env != null)
+ {
+ aasList.Add(env.AasEnv.AdministrationShells[0]);
+ }
+ }
+
+ //Filter w.r.t assetIds
+ if (!string.IsNullOrEmpty(assetIds))
+ {
+ assetIds = Base64UrlEncoder.Decode(assetIds);
+ var assetIdList = JsonConvert.DeserializeObject>(assetIds);
+ aasList = aasHelper.FindAllAasByAssetIds(assetIdList);
+ }
+
+ //Filter w.r.t idShort
+ if (!string.IsNullOrEmpty(idShort))
+ {
+ aasList = aasList.Where(x => (x.idShort != null) && x.idShort.Equals(idShort)).ToList();
+ }
+
+ return new ObjectResult(aasList);
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns all Concept Descriptions
+ ///
+ /// The Concept Description’s IdShort
+ /// IsCaseOf reference (BASE64-URL-encoded)
+ /// DataSpecification reference (BASE64-URL-encoded)
+ /// Requested Concept Descriptions
+ [HttpGet]
+ [Route("/concept-descriptions")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAllConceptDescriptions")]
+ [SwaggerResponse(statusCode: 200, type: typeof(List), description: "Requested Concept Descriptions")]
+ public virtual IActionResult GetAllConceptDescriptions([FromQuery] string idShort, [FromQuery] string isCaseOf, [FromQuery] string dataSpecificationRef)
+ {
+ try
+ {
+ var output = new List();
+ foreach (AdminShellPackageEnv env in AasxServer.Program.env)
+ {
+ if (env != null)
+ {
+ output.AddRange(env.AasEnv.ConceptDescriptions);
+ }
+ }
+
+ //Filter a list w.r.t. idShort
+ if (!string.IsNullOrEmpty(idShort))
+ {
+ output = output.Where(x => x.idShort.Equals(idShort)).ToList();
+ }
+
+ //Filter the list w.r.t. IsCaseOf
+ if (!string.IsNullOrEmpty(isCaseOf))
+ {
+ var isCaseOfObj = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(isCaseOf));
+ output = output.Where(x => (x.IsCaseOf != null) && aasHelper.CompareIsCaseOf(x.IsCaseOf, isCaseOfObj)).ToList();
+ }
+
+ //Filter the list w.r.t. dataSpecificationRef
+ if (!string.IsNullOrEmpty(dataSpecificationRef))
+ {
+ var dataSpecRefReq = JsonConvert.DeserializeObject(Base64UrlEncoder.Decode(dataSpecificationRef));
+ output = output.Where(x => (x.embeddedDataSpecification != null) && aasHelper.CompareDataSpecification(x.embeddedDataSpecification, dataSpecRefReq)).ToList();
+ }
+
+ return new ObjectResult(output);
+
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+
+ }
+
+ ///
+ /// Returns all submodel elements including their hierarchy
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// List of found submodel elements
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAllSubmodelElements")]
+ [SwaggerResponse(statusCode: 200, type: typeof(List), description: "List of found submodel elements")]
+ public virtual IActionResult GetAllSubmodelElements([FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+
+ try
+ {
+ // access AAS and Submodel
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ List submodelElements = new List();
+ foreach (var smeWrapper in submodel.submodelElements)
+ {
+ submodelElements.Add(smeWrapper.submodelElement);
+ }
+
+ var json = aasHelper.HandleOutputModifiers(submodelElements, level, content, extent);
+
+ return new ObjectResult(json);
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns all submodel elements including their hierarchy
+ ///
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// List of found submodel elements
+ [HttpGet]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAllSubmodelElementsSubmodelRepo")]
+ [SwaggerResponse(statusCode: 200, type: typeof(List), description: "List of found submodel elements")]
+ public virtual IActionResult GetAllSubmodelElementsSubmodelRepo([FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel != null)
+ {
+ List submodelElements = new List();
+ foreach (var smeWrapper in submodel.submodelElements)
+ {
+ submodelElements.Add(smeWrapper.submodelElement);
+ }
+ var json = aasHelper.HandleOutputModifiers(submodelElements, level, content, extent);
+
+ return new ObjectResult(json);
+ }
+ return NotFound($"Submodel not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns all submodel references
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Requested submodel references
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas/submodels")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAllSubmodelReferences")]
+ [SwaggerResponse(statusCode: 200, type: typeof(List), description: "Requested submodel references")]
+ public virtual IActionResult GetAllSubmodelReferences([FromRoute][Required] string aasIdentifier)
+ {
+ try
+ {
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS != null)
+ {
+ return new ObjectResult(aasReturn.AAS.submodelRefs);
+ }
+ return NotFound($"AAS not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+
+ }
+
+ ///
+ /// Returns all Submodels
+ ///
+ /// The value of the semantic id reference (BASE64-URL-encoded)
+ /// The Submodel’s idShort
+ /// Requested Submodels
+ [HttpGet]
+ [Route("/submodels")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAllSubmodels")]
+ [SwaggerResponse(statusCode: 200, type: typeof(List), description: "Requested Submodels")]
+ public virtual IActionResult GetAllSubmodels([FromQuery] string semanticId, [FromQuery] string idShort)
+ {
+ try
+ {
+ var output = new List();
+ foreach (AdminShellPackageEnv env in AasxServer.Program.env)
+ {
+ if (env != null)
+ {
+ output.AddRange(env.AasEnv.Submodels);
+ }
+ }
+
+ //Filter w.r.t. semanticId
+ if (!string.IsNullOrEmpty(semanticId))
+ {
+ var reqSemaniticId = JsonConvert.DeserializeObject(Base64UrlEncoder.Decode(semanticId));
+ output = output.Where(x => (x.semanticId != null) && x.semanticId.Matches(reqSemaniticId)).ToList();
+ }
+
+ //Filter w.r.t. idShort
+ if (!string.IsNullOrEmpty(idShort))
+ {
+ output = output.Where(x => x.idShort.Equals(idShort)).ToList();
+ }
+
+ return new ObjectResult(output);
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns the Asset Administration Shell
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Determines the request or response kind of the resource
+ /// Requested Asset Administration Shell
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAssetAdministrationShell")]
+ [SwaggerResponse(statusCode: 200, type: typeof(AssetAdministrationShell), description: "Requested Asset Administration Shell")]
+ public virtual IActionResult GetAssetAdministrationShell([FromRoute][Required] string aasIdentifier, [FromQuery] string content)
+ {
+ try
+ {
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS != null)
+ {
+ //If content is empty or Normal, return the object as it is
+ if (string.IsNullOrEmpty(content) || content.Equals("normal", StringComparison.OrdinalIgnoreCase))
+ {
+ return new ObjectResult(aasReturn.AAS);
+ }
+
+ return new ObjectResult(aasHelper.HandleOutputModifiers(aasReturn.AAS, content: content));
+ }
+
+ return NotFound();
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns a specific Asset Administration Shell
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Requested Asset Administration Shell
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAssetAdministrationShellById")]
+ [SwaggerResponse(statusCode: 200, type: typeof(AssetAdministrationShell), description: "Requested Asset Administration Shell")]
+ public virtual IActionResult GetAssetAdministrationShellById([FromRoute][Required] string aasIdentifier)
+ {
+ try
+ {
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS != null)
+ {
+ return new ObjectResult(aasReturn.AAS);
+ }
+
+ return NotFound($"Asset Administration Shell not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ throw;
+ }
+ }
+
+ ///
+ /// Returns the Asset Information
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Requested Asset Information
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas/asset-information")]
+ [ValidateModelState]
+ [SwaggerOperation("GetAssetInformation")]
+ [SwaggerResponse(statusCode: 200, type: typeof(AssetInformation), description: "Requested Asset Information")]
+ public virtual IActionResult GetAssetInformation([FromRoute][Required] string aasIdentifier)
+ {
+ //TODO:Change to AssetInformation in V3
+ //No AssetInformation in AAS_V2, hence returning Asset referenced by AAS
+ try
+ {
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS != null)
+ {
+ var asset = aasHelper.FindAssetwithReference(aasReturn.AAS.assetRef);
+ if (asset != null)
+ {
+ return new ObjectResult(asset);
+ }
+ }
+
+ return NotFound();
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+
+ }
+
+ ///
+ /// Returns a specific Concept Description
+ ///
+ /// The Concept Description’s unique id (BASE64-URL-encoded)
+ /// Requested Concept Description
+ [HttpGet]
+ [Route("/concept-descriptions/{cdIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("GetConceptDescriptionById")]
+ [SwaggerResponse(statusCode: 200, type: typeof(ConceptDescription), description: "Requested Concept Description")]
+ public virtual IActionResult GetConceptDescriptionById([FromRoute][Required] string cdIdentifier)
+ {
+ try
+ {
+ var conceptDescription = aasHelper.FindConceptDescription(Base64UrlEncoder.Decode(cdIdentifier), out _);
+ if (conceptDescription != null)
+ {
+ return new ObjectResult(conceptDescription);
+ }
+
+ return NotFound($"Concept Description not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ throw;
+ }
+ }
+
+ ///
+ /// Returns the Operation result of an asynchronous invoked Operation
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated), in this case an operation
+ /// The returned handle id of an operation’s asynchronous invocation used to request the current state of the operation’s execution (BASE64-URL-encoded)
+ ///
+ /// Operation result object
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}/operation-results/{handleId}")]
+ [ValidateModelState]
+ [SwaggerOperation("GetOperationAsyncResult")]
+ [SwaggerResponse(statusCode: 200, type: typeof(OperationResult), description: "Operation result object")]
+ public virtual IActionResult GetOperationAsyncResult([FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromRoute][Required] string handleId, [FromQuery] string content)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(handleId))
+ {
+ return BadRequest($"Invalid HandleId.");
+ }
+ //Check if aas exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ //Find the operation from the idShortpath
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (submodelElement == null)
+ {
+ return NotFound($"Operation {idShortPath} not found.");
+ }
+
+ var opResult = aasHelper.GetOperationAsyncResult(Base64UrlEncoder.Decode(handleId));
+ if (opResult != null)
+ {
+ return new ObjectResult(opResult);
+ }
+
+ return NotFound($"Operation with the handle id not found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns the Operation result of an asynchronous invoked Operation
+ ///
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated), in this case an operation
+ /// The returned handle id of an operation’s asynchronous invocation used to request the current state of the operation’s execution (BASE64-URL-encoded)
+ ///
+ /// Operation result object
+ [HttpGet]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}/operation-results/{handleId}")]
+ [ValidateModelState]
+ [SwaggerOperation("GetOperationAsyncResultSubmodelRepo")]
+ [SwaggerResponse(statusCode: 200, type: typeof(OperationResult), description: "Operation result object")]
+ public virtual IActionResult GetOperationAsyncResultSubmodelRepo([FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromRoute][Required] string handleId, [FromQuery] string content)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(handleId))
+ {
+ return BadRequest($"Invalid HandleId.");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ //Find the operation from the idShortpath
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (submodelElement == null)
+ {
+ return NotFound($"Operation {idShortPath} not found.");
+ }
+
+ var opResult = aasHelper.GetOperationAsyncResult(Base64UrlEncoder.Decode(handleId));
+ if (opResult != null)
+ {
+ return new ObjectResult(opResult);
+ }
+
+ return NotFound($"Operation with the handle id not found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns the Submodel
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Requested Submodel
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel")]
+ [ValidateModelState]
+ [SwaggerOperation("GetSubmodel")]
+ [SwaggerResponse(statusCode: 200, type: typeof(Submodel), description: "Requested Submodel")]
+ public virtual IActionResult GetSubmodel([FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Either AAS or Submodel not found");
+ }
+
+ var json = aasHelper.HandleOutputModifiers(submodel, level, content, extent);
+ if (json != null)
+ {
+ return new ObjectResult(json);
+ }
+
+ return NotFound($"Either AAS or Submodel not found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns a specific submodel element from the Submodel at a specified path
+ ///
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Requested submodel element
+ [HttpGet]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("GetSubmodelElementByPath")]
+ [SwaggerResponse(statusCode: 200, type: typeof(SubmodelElement), description: "Requested submodel element")]
+ public virtual IActionResult GetSubmodelElementByPath([FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if AAS exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found. Please check the identifier.");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found. Please check the identifier.");
+ }
+
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (submodelElement == null)
+ {
+ return NotFound($"Requested submodel element not found.");
+ }
+
+ var json = aasHelper.HandleOutputModifiers(submodelElement, level, content, extent);
+ if (json != null)
+ {
+ return new ObjectResult(json);
+ }
+
+ return NotFound($"Submodel Element not found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns a specific submodel element from the Submodel at a specified path
+ ///
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Requested submodel element
+ [HttpGet]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("GetSubmodelElementByPathSubmodelRepo")]
+ [SwaggerResponse(statusCode: 200, type: typeof(SubmodelElement), description: "Requested submodel element")]
+ public virtual IActionResult GetSubmodelElementByPathSubmodelRepo([FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (submodelElement == null)
+ {
+ return NotFound($"Requested submodel element not found.");
+ }
+
+ var json = aasHelper.HandleOutputModifiers(submodelElement, level, content, extent);
+ if (json != null)
+ {
+ return new ObjectResult(json);
+ }
+
+ return NotFound($"Submodel Element not found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Returns the Submodel
+ ///
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Requested Submodel
+ [HttpGet]
+ [Route("/submodels/{submodelIdentifier}/submodel")]
+ [ValidateModelState]
+ [SwaggerOperation("GetSubmodelSubmodelRepo")]
+ [SwaggerResponse(statusCode: 200, type: typeof(Submodel), description: "Requested Submodel")]
+ public virtual IActionResult GetSubmodelSubmodelRepo([FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"The Submodel not found");
+ }
+
+ var json = aasHelper.HandleOutputModifiers(submodel, level, content, extent);
+ if (json != null)
+ {
+ return new ObjectResult(json);
+ }
+
+ return NotFound($"Submodel not found");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Synchronously or asynchronously invokes an Operation at a specified path
+ ///
+ /// Operation request object
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated), in this case an operation
+ /// Determines whether an operation invocation is performed asynchronously or synchronously
+ /// Determines the request or response kind of the resource
+ /// Operation result object
+ [HttpPost]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}/invoke")]
+ [ValidateModelState]
+ [SwaggerOperation("InvokeOperation")]
+ [SwaggerResponse(statusCode: 200, type: typeof(OperationResult), description: "Operation result object")]
+ public virtual IActionResult InvokeOperation([FromBody] OperationRequest body, [FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] bool? _async, [FromQuery] string content)
+ {
+ try
+ {
+ //Check if aas exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ //Find the operation from the idShortpath
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (submodelElement == null)
+ {
+ return NotFound($"Operation {idShortPath} not found.");
+ }
+
+ if (submodelElement is AdminShellV20.Operation operation)
+ {
+ //Primary checks for inout and input variables
+ if (operation.inputVariable.Count != body.InputArguments.Count)
+ {
+ return BadRequest($"Number of input arguments in payload does not fit expected input arguments of Operation.");
+ }
+
+ if (operation.inoutputVariable.Count != body.InputArguments.Count)
+ {
+ return BadRequest($"Number of InOut arguments in payload does not fit expected InOut arguments of Operation.");
+ }
+
+ if ((_async != null) && (!(bool)_async))
+ {
+ return new ObjectResult(aasHelper.InvokeOperationSync(operation, body));
+ }
+
+ var opHandle = aasHelper.InvokeOperationAsync(operation, body);
+ var json = JsonConvert.SerializeObject(opHandle);
+ return new ObjectResult(json);
+ }
+ else
+ {
+ return NotFound($"Element in the IdShortPath is not an Operation.");
+ }
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Synchronously or asynchronously invokes an Operation at a specified path
+ ///
+ /// Operation request object
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated), in this case an operation
+ /// Determines whether an operation invocation is performed asynchronously or synchronously
+ /// Determines the request or response kind of the resource
+ /// Operation result object
+ [HttpPost]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}/invoke")]
+ [ValidateModelState]
+ [SwaggerOperation("InvokeOperationSubmodelRepo")]
+ [SwaggerResponse(statusCode: 200, type: typeof(OperationResult), description: "Operation result object")]
+ public virtual IActionResult InvokeOperationSubmodelRepo([FromBody] OperationRequest body, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] bool? _async, [FromQuery] string content)
+ {
+ try
+ {
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ //Find the operation from the idShortpath
+ var submodelElement = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (submodelElement == null)
+ {
+ return NotFound($"Operation {idShortPath} not found.");
+ }
+
+ if (submodelElement is AdminShellV20.Operation operation)
+ {
+ //Primary checks for inout and input variables
+ if (operation.inputVariable.Count != body.InputArguments.Count)
+ {
+ return BadRequest($"Number of input arguments in payload does not fit expected input arguments of Operation.");
+ }
+
+ if (operation.inoutputVariable.Count != body.InputArguments.Count)
+ {
+ return BadRequest($"Number of InOut arguments in payload does not fit expected InOut arguments of Operation.");
+ }
+
+ if ((_async != null) && (!(bool)_async))
+ {
+ return new ObjectResult(aasHelper.InvokeOperationSync(operation, body));
+ }
+
+ var opHandle = aasHelper.InvokeOperationAsync(operation, body);
+ var json = JsonConvert.SerializeObject(opHandle);
+ return new ObjectResult(json);
+ }
+ else
+ {
+ return NotFound($"Element in the IdShortPath is not an Operation.");
+ }
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Creates a new Asset Administration Shell
+ ///
+ /// Asset Administration Shell object
+ /// Asset Administration Shell created successfully
+ [HttpPost]
+ [Route("/shells")]
+ [ValidateModelState]
+ [SwaggerOperation("PostAssetAdministrationShell")]
+ [SwaggerResponse(statusCode: 201, type: typeof(AssetAdministrationShell), description: "Asset Administration Shell created successfully")]
+ //public virtual IActionResult PostAssetAdministrationShell([FromBody] AssetAdministrationShell body)
+ public virtual IActionResult PostAssetAdministrationShell([FromBody] AdminShellV20.AdministrationShell body)
+ {
+ try
+ {
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in AAS.");
+ }
+
+ //Check if already exists
+ var aasReturn = aasHelper.FindAas(body.identification.id);
+ if (aasReturn.AAS != null)
+ {
+ return Conflict($"Asset Administration Shell already exists.");
+ }
+
+ bool added = aasHelper.PostAAS(body);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(2);
+ return Created($"Asset Administration Shell created successfully.", body);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ throw;
+ }
+ }
+
+
+ ///
+ /// Creates a new Concept Description
+ ///
+ /// Concept Description object
+ /// Concept Description created successfully
+ [HttpPost]
+ [Route("/concept-descriptions")]
+ [ValidateModelState]
+ [SwaggerOperation("PostConceptDescription")]
+ [SwaggerResponse(statusCode: 201, type: typeof(ConceptDescription), description: "Concept Description created successfully")]
+ //public virtual IActionResult PostConceptDescription([FromBody]ConceptDescription body)
+ public virtual IActionResult PostConceptDescription([FromBody] AdminShellV20.ConceptDescription body)
+ {
+ try
+ {
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in Concept Description.");
+ }
+
+ //Check if already exists
+ var conceptDescription = aasHelper.FindConceptDescription(body.identification.id, out _);
+ if (conceptDescription != null)
+ {
+ return Conflict($"Concept Description already exists.");
+ }
+
+ bool added = aasHelper.PostConceptDescription(body);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1);
+ return Created($"Concept Description {body.idShort} created successfully.", body);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ throw;
+ }
+ }
+
+ ///
+ /// Creates a new Submodel
+ ///
+ /// Submodel object
+ /// Submodel created successfully
+ [HttpPost]
+ [Route("/submodels")]
+ [ValidateModelState]
+ [SwaggerOperation("PostSubmodel")]
+ [SwaggerResponse(statusCode: 201, type: typeof(Submodel), description: "Submodel created successfully")]
+ //public virtual IActionResult PostSubmodel([FromBody]Submodel body)
+ public virtual IActionResult PostSubmodel([FromBody] AdminShellV20.Submodel body)
+ {
+ try
+ {
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in Submodel.");
+ }
+
+ //Check if already exists
+ var submodel = aasHelper.FindSubmodel(body.identification.id, out _);
+ if (submodel != null)
+ {
+ return Conflict($"Submodel already exists.");
+ }
+
+ bool added = aasHelper.PostSubmodel(body);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //same tree, structure may change
+ return Created($"Submodel created successfully.", body);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Creates a new submodel element
+ ///
+ /// Requested submodel element
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel element created successfully
+ [HttpPost]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements")]
+ [ValidateModelState]
+ [SwaggerOperation("PostSubmodelElement")]
+ [SwaggerResponse(statusCode: 201, type: typeof(SubmodelElement), description: "Submodel element created successfully")]
+ //public virtual IActionResult PostSubmodelElement([FromBody]SubmodelElement body, [FromRoute][Required]string aasIdentifier, [FromRoute][Required]string submodelIdentifier, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PostSubmodelElement([FromBody] AdminShellV20.SubmodelElement body, [FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if AAS exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ //Check for idShort in SubmodelElement
+ if (string.IsNullOrEmpty(body.idShort))
+ {
+ return BadRequest($"No IdShort found in the submodel element");
+ }
+
+ //Check if submodel element already exists in the submodel
+ var submodelElement = submodel.FindSubmodelElementWrapper(body.idShort);
+ if (submodelElement != null)
+ {
+ return Conflict($"Submodel element {body.idShort} already exists in submodel");
+ }
+
+ bool added = aasHelper.PostSubmodelElement(submodel, body);
+ if (added)
+ {
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ AasxServer.Program.signalNewData(1); //Same tree, structure changed
+ return Created($"Submodel Element created successfully.", output);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Creates a new submodel element at a specified path within submodel elements hierarchy
+ ///
+ /// Requested submodel element
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel element created successfully
+ [HttpPost]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("PostSubmodelElementByPath")]
+ [SwaggerResponse(statusCode: 201, type: typeof(SubmodelElement), description: "Submodel element created successfully")]
+ //public virtual IActionResult PostSubmodelElementByPath([FromBody]SubmodelElement body, [FromRoute][Required]string aasIdentifier, [FromRoute][Required]string submodelIdentifier, [FromRoute][Required]string idShortPath, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PostSubmodelElementByPath([FromBody] AdminShellV20.SubmodelElement body, [FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if AAS exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ //IdShortPath is a path to the parent element
+ var parentSME = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (parentSME == null)
+ {
+ return NotFound($"Submodel element {idShortPath} not found in submodel.");
+ }
+
+ if (string.IsNullOrEmpty(body.idShort))
+ {
+ return BadRequest($"IdShort is not set in the submodel element.");
+ }
+
+ //Check if requested submodel element already exists in the parent SME
+ if (parentSME is AdminShellV20.SubmodelElementCollection parentSMEColl)
+ {
+ var existingSME = parentSMEColl.FindFirstIdShort(body.idShort);
+ if (existingSME != null)
+ {
+ return Conflict($"SubmodelElement already exists in {idShortPath}.");
+ }
+ }
+
+ bool added = aasHelper.PostSubmodelElementByPath(parentSME, body);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //same tree, structure may change
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel element created successfully", output);
+ }
+
+ //Re-do
+ return NotFound($"Requested submodel element not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ throw;
+ }
+ }
+
+ ///
+ /// Creates a new submodel element at a specified path within submodel elements hierarchy
+ ///
+ /// Requested submodel element
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel element created successfully
+ [HttpPost]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("PostSubmodelElementByPathSubmodelRepo")]
+ [SwaggerResponse(statusCode: 201, type: typeof(SubmodelElement), description: "Submodel element created successfully")]
+ //public virtual IActionResult PostSubmodelElementByPathSubmodelRepo([FromBody]SubmodelElement body, [FromRoute][Required]string submodelIdentifier, [FromRoute][Required]string idShortPath, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PostSubmodelElementByPathSubmodelRepo([FromBody] AdminShellV20.SubmodelElement body, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ //IdShortPath is a path to the parent element
+ var parentSME = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out _);
+ if (parentSME == null)
+ {
+ return NotFound($"Submodel element {idShortPath} not found in submodel.");
+ }
+
+ if (string.IsNullOrEmpty(body.idShort))
+ {
+ return BadRequest($"IdShort is not set in the submodel element.");
+ }
+
+ //Check if requested submodel element already exists in the parent SME
+ if (parentSME is AdminShellV20.SubmodelElementCollection parentSMEColl)
+ {
+ var existingSME = parentSMEColl.FindFirstIdShort(body.idShort);
+ if (existingSME != null)
+ {
+ return Conflict($"SubmodelElement already exists in {idShortPath}.");
+ }
+ }
+
+ bool added = aasHelper.PostSubmodelElementByPath(parentSME, body);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(2);
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel element created successfully", output);
+ }
+
+ //Re-do
+ return NotFound($"Requested submodel element not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Creates a new submodel element
+ ///
+ /// Requested submodel element
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel element created successfully
+ [HttpPost]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements")]
+ [ValidateModelState]
+ [SwaggerOperation("PostSubmodelElementSubmodelRepo")]
+ [SwaggerResponse(statusCode: 201, type: typeof(SubmodelElement), description: "Submodel element created successfully")]
+ //public virtual IActionResult PostSubmodelElementSubmodelRepo([FromBody]SubmodelElement body, [FromRoute][Required]string submodelIdentifier, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PostSubmodelElementSubmodelRepo([FromBody] AdminShellV20.SubmodelElement body, [FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ //Check for idShort in SubmodelElement
+ if (string.IsNullOrEmpty(body.idShort))
+ {
+ return BadRequest($"No IdShort found in the submodel element");
+ }
+
+ //Check if submodel element already exists in the submodel
+ var submodelElement = submodel.FindSubmodelElementWrapper(body.idShort);
+ if (submodelElement != null)
+ {
+ return Conflict($"Submodel element {body.idShort} already exists in submodel");
+ }
+
+ bool added = aasHelper.PostSubmodelElement(submodel, body);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(2);
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel Element {body.idShort} created successfully.", output);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Creates a submodel reference at the Asset Administration Shell
+ ///
+ /// Reference to the Submodel
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Submodel reference created successfully
+ [HttpPost]
+ [Route("/shells/{aasIdentifier}/aas/submodels")]
+ [ValidateModelState]
+ [SwaggerOperation("PostSubmodelReference")]
+ [SwaggerResponse(statusCode: 201, type: typeof(Reference), description: "Submodel reference created successfully")]
+ //public virtual IActionResult PostSubmodelReference([FromBody]Reference body, [FromRoute][Required]string aasIdentifier)
+ public virtual IActionResult PostSubmodelReference([FromBody] AdminShellV20.Reference body, [FromRoute][Required] string aasIdentifier)
+ {
+ try
+ {
+ if (body.Count == 0)
+ {
+ return BadRequest($"No references present in the request payload.");
+ }
+ else if (body.Count != 1)
+ {
+ return BadRequest($"More than one references present in the request payload.");
+ }
+
+ //Check if AAS exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found.");
+ }
+
+ //Check if Submodel with this reference exists
+ var submodel = aasHelper.FindSubmodelWithReference(body);
+ if (submodel == null)
+ {
+ return BadRequest($"No Submodel with this reference present in the server");
+ }
+
+ //Check if reference already exists with the AAS
+ if (aasReturn.AAS.HasSubmodelRef(new AdminShellV20.SubmodelRef(body)))
+ {
+ return Conflict($"The Submodel Reference already exists in AAS");
+ }
+
+ aasReturn.AAS.AddSubmodelRef(new AdminShellV20.SubmodelRef(body));
+ AasxServer.Program.signalNewData(1);
+ return Created($"Submodel reference created successfully.", body);
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+
+ }
+
+ ///
+ /// Updates the Asset Administration Shell
+ ///
+ /// Asset Administration Shell object
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Determines the request or response kind of the resource
+ /// Asset Administration Shell updated successfully
+ [HttpPut]
+ [Route("/shells/{aasIdentifier}/aas")]
+ [ValidateModelState]
+ [SwaggerOperation("PutAssetAdministrationShell")]
+ //public virtual IActionResult PutAssetAdministrationShell([FromBody]AssetAdministrationShell body, [FromRoute][Required]string aasIdentifier, [FromQuery]string content)
+ public virtual IActionResult PutAssetAdministrationShell([FromBody] AdminShellV20.AdministrationShell body, [FromRoute][Required] string aasIdentifier, [FromQuery] string content)
+ {
+ try
+ {
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in AAS.");
+ }
+
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found.");
+ }
+
+ bool added = aasHelper.PutAAS(body, aasReturn);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); // same tree, structure may change
+ object output = aasHelper.HandleOutputModifiers(body, content: content);
+ return Created($"AAS updated successfully.", output);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates an existing Asset Administration Shell
+ ///
+ /// Asset Administration Shell object
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Asset Administration Shell updated successfully
+ [HttpPut]
+ [Route("/shells/{aasIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("PutAssetAdministrationShellById")]
+ //public virtual IActionResult PutAssetAdministrationShellById([FromBody]AssetAdministrationShell body, [FromRoute][Required]string aasIdentifier)
+ public virtual IActionResult PutAssetAdministrationShellById([FromBody] AdminShellV20.AdministrationShell body, [FromRoute][Required] string aasIdentifier)
+ {
+ try
+ {
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found.");
+ }
+
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in AAS.");
+ }
+
+ bool added = aasHelper.PutAAS(body, aasReturn);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //Same tree, structure/values may change (e.g., submodelRefs may be added or deleted, hence 1)
+ return NoContent();
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates the Asset Information
+ ///
+ /// Asset Information object
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// Asset Information updated successfully
+ [HttpPut]
+ [Route("/shells/{aasIdentifier}/aas/asset-information")]
+ [ValidateModelState]
+ [SwaggerOperation("PutAssetInformation")]
+ //public virtual IActionResult PutAssetInformation([FromBody]AssetInformation body, [FromRoute][Required]string aasIdentifier)
+ public virtual IActionResult PutAssetInformation([FromBody] AdminShellV20.Asset body, [FromRoute][Required] string aasIdentifier)
+ {
+ try
+ {
+ //Check if identification exists
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in Asset.");
+ }
+
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found.");
+ }
+
+ bool added = aasHelper.AddAsset(body, aasReturn);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //Same tree, strcture may change
+ return NoContent();
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates an existing Concept Description
+ ///
+ /// Concept Description object
+ /// The Concept Description’s unique id (BASE64-URL-encoded)
+ /// Concept Description updated successfully
+ [HttpPut]
+ [Route("/concept-descriptions/{cdIdentifier}")]
+ [ValidateModelState]
+ [SwaggerOperation("PutConceptDescriptionById")]
+ //public virtual IActionResult PutConceptDescriptionById([FromBody]ConceptDescription body, [FromRoute][Required]string cdIdentifier)
+ public virtual IActionResult PutConceptDescriptionById([FromBody] AdminShellV20.ConceptDescription body, [FromRoute][Required] string cdIdentifier)
+ {
+ try
+ {
+ var existingCD = aasHelper.FindConceptDescription(Base64UrlEncoder.Decode(cdIdentifier), out int packageIndex);
+ if (existingCD == null)
+ {
+ return NotFound($"Concept Description not found.");
+ }
+
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in Concept Description.");
+ }
+
+ bool added = aasHelper.PutConceptDescription(body, existingCD, packageIndex);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(0); //Same tree, same structure, only values modified
+ return NoContent();
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates the Submodel
+ ///
+ /// Submodel object
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel updated successfully
+ [HttpPut]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel")]
+ [ValidateModelState]
+ [SwaggerOperation("PutSubmodel")]
+ //public virtual IActionResult PutSubmodel([FromBody]Submodel body, [FromRoute][Required]string aasIdentifier, [FromRoute][Required]string submodelIdentifier, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PutSubmodel([FromBody] AdminShellV20.Submodel body, [FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in Submodel.");
+ }
+
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found.");
+ }
+
+ //Check if submodel exists
+ var existingSubmodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out int packageIndex);
+ if (existingSubmodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ bool added = aasHelper.PutSubmodel(body, existingSubmodel, packageIndex);
+ if (added)
+ {
+ //Check if AAS has the submodelRef, if not create one.
+ var newsmRef = AdminShellV20.SubmodelRef.CreateNew("Submodel", true, body.identification.idType, body.identification.id);
+ if (!aasReturn.AAS.HasSubmodelRef(newsmRef))
+ {
+ aasReturn.AAS.submodelRefs.Add(newsmRef);
+ }
+ AasxServer.Program.signalNewData(1);
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel updated successfully.", output);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates an existing submodel element at a specified path within submodel elements hierarchy
+ ///
+ /// Requested submodel element
+ /// The Asset Administration Shell’s unique id (BASE64-URL-encoded)
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel element updated successfully
+ [HttpPut]
+ [Route("/shells/{aasIdentifier}/aas/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("PutSubmodelElementByPath")]
+ //public virtual IActionResult PutSubmodelElementByPath([FromBody]SubmodelElement body, [FromRoute][Required]string aasIdentifier, [FromRoute][Required]string submodelIdentifier, [FromRoute][Required]string idShortPath, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PutSubmodelElementByPath([FromBody] AdminShellV20.SubmodelElement body, [FromRoute][Required] string aasIdentifier, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if AAS exists
+ var aasReturn = aasHelper.FindAas(Base64UrlEncoder.Decode(aasIdentifier));
+ if (aasReturn.AAS == null)
+ {
+ return NotFound($"AAS not found");
+ }
+
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodelWithinAAS(Base64UrlEncoder.Decode(aasIdentifier), Base64UrlEncoder.Decode(submodelIdentifier));
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ if (string.IsNullOrEmpty(body.idShort))
+ {
+ return BadRequest($"IdShort is not set in the submodel element.");
+ }
+
+ if (!idShortPath.Contains('.'))
+ {
+ return BadRequest($"Please check the idShortPath again.");
+ }
+ //IdShortPath is a path to this requested submodel element
+ var existingSME = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out object parent);
+ if (existingSME == null)
+ {
+ return NotFound($"Submodel Element not found.");
+ }
+ //TODO: Remove
+ //string parentIdShortPath = idShortPath.Substring(0, idShortPath.IndexOf('.'));
+ //var parentSME = aasHelper.FindSubmodelElementByPath(submodel, parentIdShortPath, out _);
+ //if (parentSME == null)
+ //{
+ // return NotFound($"Parent Submodel element not found in submodel {submodelIdentifier}.");
+ //}
+
+ bool added = aasHelper.PutSubmodelElementByPath(parent, body, existingSME);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //TODO: change 0, however, value doesn't reflect automatically with 0.
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel updated successfully.", output);
+ }
+
+ //Re-do
+ return NotFound($"Requested submodel element not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates an existing submodel element at a specified path within submodel elements hierarchy
+ ///
+ /// Requested submodel element
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// IdShort path to the submodel element (dot-separated)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel element updated successfully
+ [HttpPut]
+ [Route("/submodels/{submodelIdentifier}/submodel/submodel-elements/{idShortPath}")]
+ [ValidateModelState]
+ [SwaggerOperation("PutSubmodelElementByPathSubmodelRepo")]
+ //public virtual IActionResult PutSubmodelElementByPathSubmodelRepo([FromBody]SubmodelElement body, [FromRoute][Required]string submodelIdentifier, [FromRoute][Required]string idShortPath, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PutSubmodelElementByPathSubmodelRepo([FromBody] AdminShellV20.SubmodelElement body, [FromRoute][Required] string submodelIdentifier, [FromRoute][Required] string idShortPath, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ //Check if submodel exists
+ var submodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out _);
+ if (submodel == null)
+ {
+ return NotFound($"Submodel not found");
+ }
+
+ if (string.IsNullOrEmpty(body.idShort))
+ {
+ return BadRequest($"IdShort is not set in the submodel element.");
+ }
+
+ //IdShortPath is a path to this requested submodel element
+ var existingSME = aasHelper.FindSubmodelElementByPath(submodel, idShortPath, out object parent);
+ if (existingSME == null)
+ {
+ return NotFound($"Submodel Element not found.");
+ }
+
+ //TODO: Remove
+ //string parentIdShortPath = idShortPath.Substring(0, idShortPath.IndexOf('.'));
+ //var parentSME = aasHelper.FindSubmodelElementByPath(submodel, parentIdShortPath, out _);
+ //if (parentSME == null)
+ //{
+ // return NotFound($"Parent Submodel element not found in submodel {submodelIdentifier}.");
+ //}
+
+ bool added = aasHelper.PutSubmodelElementByPath(parent, body, existingSME);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //TODO:0, however, values change is reflected automaticcally with 0.
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel element updated successfully.", output);
+ }
+
+ //Re-do
+ return NotFound($"Requested submodel element not found.");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+
+ ///
+ /// Updates the Submodel
+ ///
+ /// Submodel object
+ /// The Submodel’s unique id (BASE64-URL-encoded)
+ /// Determines the structural depth of the respective resource content
+ /// Determines the request or response kind of the resource
+ /// Determines to which extent the resource is being serialized
+ /// Submodel updated successfully
+ [HttpPut]
+ [Route("/submodels/{submodelIdentifier}/submodel")]
+ [ValidateModelState]
+ [SwaggerOperation("PutSubmodelSubmodelRepo")]
+ //public virtual IActionResult PutSubmodelSubmodelRepo([FromBody]Submodel body, [FromRoute][Required]string submodelIdentifier, [FromQuery]string level, [FromQuery]string content, [FromQuery]string extent)
+ public virtual IActionResult PutSubmodelSubmodelRepo([FromBody] AdminShellV20.Submodel body, [FromRoute][Required] string submodelIdentifier, [FromQuery] string level, [FromQuery] string content, [FromQuery] string extent)
+ {
+ try
+ {
+ if (body.identification == null || string.IsNullOrEmpty(body.identification.id))
+ {
+ return BadRequest($"No Identification found in Submodel.");
+ }
+
+ //Check if submodel exists
+ var existingSubmodel = aasHelper.FindSubmodel(Base64UrlEncoder.Decode(submodelIdentifier), out int packageIndex);
+ if (existingSubmodel == null)
+ {
+ return NotFound($"Submodel not found.");
+ }
+
+ bool added = aasHelper.PutSubmodel(body, existingSubmodel, packageIndex);
+ if (added)
+ {
+ AasxServer.Program.signalNewData(1); //same tree, but strcture may change, e.g. new SMEs may get added.
+ object output = aasHelper.HandleOutputModifiers(body, level, content, extent);
+ return Created($"Submodel updated successfully.", output);
+ }
+
+ return Ok($"Error: not added since datastructure completely filled already");
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+ }
+}
diff --git a/src/IO.Swagger/Dockerfile b/src/IO.Swagger/Dockerfile
new file mode 100644
index 000000000..a22c4e17f
--- /dev/null
+++ b/src/IO.Swagger/Dockerfile
@@ -0,0 +1,19 @@
+FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
+WORKDIR /app
+
+ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
+
+# copy csproj and restore as distinct layers
+COPY *.csproj ./
+RUN dotnet restore
+
+# copy everything else and build
+COPY . ./
+RUN dotnet publish -c Release -o out
+
+# build runtime image
+FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
+WORKDIR /app
+COPY --from=build-env /app/out .
+
+ENTRYPOINT ["dotnet", "IO.Swagger.dll"]
diff --git a/src/IO.Swagger/Extensions/JsonSerializerExtension.cs b/src/IO.Swagger/Extensions/JsonSerializerExtension.cs
new file mode 100644
index 000000000..4fe9f3897
--- /dev/null
+++ b/src/IO.Swagger/Extensions/JsonSerializerExtension.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace IO.Swagger.Extensions
+{
+ public static class JsonSerializerExtension
+ {
+ private static readonly ConditionalWeakTable