Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arf/world-analysis-api-helpers/world-analysis-asp.net-server
1 result
Show changes
Commits on Source (11)
Showing
with 1927 additions and 57 deletions
## Ignore .openapi-generator Folder
##
.openapi-generator/
server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/.gitignore
# Visual Studio cache/options directory
.vs/
# Visual Studio solution
*.sln
# generated project-files
*.csproj
# build-jobs
build.*
# database data directory
server/data/
# database directory
server/programs/MongoDB/
!server/programs/MongoDB/kill_MongoDB.bat
!server/programs/MongoDB/startMongoDB.bat
!server/programs/MongoDB/readme.md
#generated readme
server/worldstorage/README.md
#!ETSI-ARF/Models/
#!ETSI-ARF/Controllers/
#!ETSI-ARF/Services/
# all generated directories
wwwroot/
!wwwroot/ws.html
Attributes/
Authentication/
Converters/
Filters/
Formatters/
OpenAPI/
Properties/
Models
Controllers/
# generated Program.cs
#Program.cs
# backup-files
*.bak
[submodule "openapi"]
path = openapi
url = https://forge.etsi.org/rep/arf/openapi.git
branch = master
\ No newline at end of file
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
#
# ETSI - ISG - ARF
#
**\ETSI.ARF.OpenAPI.WorldAnalysis.csproj
# Modules to initialize World Storage database, server....
**/Program.cs
**/Startup.cs
**/Dockerfile
**/appsettings.json
# Implementation of REST request and database functionalities
**/ETSI-ARF
# Design of some web pages
**/wwwroot/portal
**/wwwroot/index.html
\ No newline at end of file
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2022 ETSI
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# World Analysis ASP.NET Server
*This repository is part of the outcomes of the Specialist Task Force 620 focusing on the authoring of a World Representation as part of the ETSI ISG Augmented Reality Framework architecture (https://www.etsi.org/deliver/etsi_gs/ARF/001_099/003/01.01.01_60/gs_ARF003v010101p.pdf).*
*The set of the World Representation authoring components includes:*
*• The C++ and C# source code for servers and clients generated from OpenAPI available here (https://forge.etsi.org/rep/arf/arf005)*
*• A Unity plugin and a Unity editor for authoring and accessing a World Representation hosted on a World Storage server.*
## Getting started
*All these components are available under the ETSI Labs group “World Storage API Helpers”: https://labs.etsi.org/rep/arf/world-storage-api-helpers*
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
*If you wish to contribute to this project or any other projects in the context of the [ETSI ISG Augmented Reality Framework architecture](https://www.etsi.org/committee/1420-arf), please refer to the ["How to get involved in an ISG" section on the ETSI website](https://www.etsi.org/how-to-get-involved-in-an-isg)*
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
---
## Add your files
# Description - World Analysis Server for STF 669
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
This repo should be used to construct a complete ASP-Net REST server compliant to the ARF World Analysis API. It uses auto-generated ASP.NET server code. We propose to use the open source OpenAPI-Generator for this.
It includes description and code for a fully functional server with MongoDB integration.
## Repo Content
| | Files / Folders | Description |
|:-:|:--------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| 📂 | openapi | A git submodule (ForgeETSI ) pointing to the repo containing the API specification file |
| 📂 | server | The folder where the library code will be generated, the openapi generator is set to not overwrite some files used to generate and initialiue the ASP.Net server system |
| 📂 | server/worldstorage/src/ETSI.ARF.OpenAPI.WorldAnalysis | This is the location where the ASP.Net code will be generated |
| 📂 | server/worldstorage/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF | This folder contains codes implementating the REST end-paths for the different objects in API |
| 📂 | server/programs/MongoDB | This folder contains the MongoDB service. The World Storage database should be created or imported in a folder of your choice which path has to be editied in the .bat file |
| 📂 | server\worldanalysis\src\ETSI.ARF.OpenAPI.WorldAnalysis\appsettings.json | Parameter for accessing the MongoDB server from the ASP.NET services. This file contains the MongoDB server IP and optional port number, name of the World Storage database and their collections. |
## Requirements
What you need:
1. Installed npm: https://phoenixnap.com/kb/install-node-js-npm-on-windows
2. Installed openapi generator with npm: https://openapi-generator.tech/docs/installation/
3. Installed docker (recommanded): https://www.docker.com/get-started
# Code Generation
We provide the file `.openapi-generator-ignore` in `server`, which prevents openapi-generator to override some adapted files.
## Auto-generate server code
Use/define following setup for the config file `openapitools.json`:
```
{
"$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "5.3.0",
"generators":{
"v1.1": {
"generatorName": "aspnetcore",
"output": "./server/worldanalysis",
"inputSpec": "./openapi/API/worldanalysis/worldanalysisopenapi.yaml",
"additionalProperties": {
"aspnetCoreVersion": "5.0",
"packageName": "ETSI.ARF.OpenAPI.WorldAnalysis",
"operationModifier": "abstract",
"classModifier": "abstract"
}
}
}
}
}
```
Open a command shell and execute:
```
npx openapi-generator-cli generate
```
Open the solution `ETSI.ARF.OpenAPI.WorldAnalysis.sln` (folder `server/worldstorage`) in Visual Studio:
## In Visual Studio
Open `NuGet Package Manager` and add `MongoDB.Driver`.
### File adaptations
Change version number in all files if a new version is provided.
## 📂 Custom folders (new)
All custom files are now in the folder 'ETSI-ARF'. Nothing to do adapt manually after generation. But you have to provide the implementation of new endpoints because they are now directly declared as abstract (default).
The folder contains following subfolders:
`ControllersImpl`
`ModelsExt`
`Services`
### 📂 Folder 'ControllersImp'
The modules in this folder implement the endpoints. Compare files folder in "`ETSI-ARF/ControllersImpl`" with the corresponding files in "`Controllers`" and implement if necessary the new methods.
Methods should be the same with "`override`" instead of "`abstract`".
---
#### If some files are missing (and only then!)
Add
```
cd existing_repo
git remote add origin https://labs.etsi.org/rep/arf/world-analysis-api-helpers/world-analysis-asp.net-server.git
git branch -M main
git push -uf origin main
using MongoDB.Driver;
```
## Integrate with your tools
Add one or more private readonly services class variable like in the already existing files, e.g.:
```
private readonly xxxService _xxxService;
```
- [ ] [Set up project integrations](https://labs.etsi.org/rep/arf/world-analysis-api-helpers/world-analysis-asp.net-server/-/settings/integrations)
Add a constructor with this service class variable like in the already existing files.
```
public XxxApiControllerImpl(TrackableService trackableService)
{
_xxxService = xxxService;
// etc.
}
```
## Collaborate with your team
Implement endpoint code using the appropriate MongoDB methods of the corresponding classes from the folder `Services` (which you may be have to create).
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
### 📂 Folder `ModelsExt`
The modules inside this folder are for extensions of the generated API data structures. Add all the classes to be stored in the database (i.e. `XxxExt.cs`, ) inherited from `IModels`. (definition of the extra UUID):
```
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
```
## Test and Deploy
and at the value that is to become the MongoDB ID, add:
```
[BsonId]
[BsonRepresentation(BsonType.String)]
```
Use the built-in continuous integration in GitLab.
If some members won't be saved in the MongoDB then use this keyword:
```
[BsonIgnore]
```
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
### 📂 Folder `Services`
The folder `Services` is for handling the data with the MongoDB. It should contain one common class with the DatabaseSettings (`DatabaseSettings.cs`) and one with the database-access-methods (create, get, update, remove) for each API. If some are missing create them like the ones you find there. Be aware to add the reference to these in the file `startup.cs` in this case.
***
The naming in the DatabaseSettings is the same as defined in `appsettings.json`, which you have to extend when creating new classes in this folder. Change `appsettings.json` in the folder `docker` accordingly. Make sure that the ConnectionString for the database contains the correct IP address as specified in `docker-compose.yml`.
# Editing this README
## 📂 Extra folder `wwwroot`
Add in `openapi-original.json` in section `servers` the urls of the servers you want to use with swagger-ui
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
# MongoDB
If you don't have a MongoDB, follow the instructions in `readme.md` in `server/programs/MongoDB`
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
...and put MongoDB in folder `server/programs/MongoDB` (download MongoDB as zip-file from https://www.mongodb.com/try/download/community and unzip the file into this directory, so that the bin-directory is in this folder).
## Name
Choose a self-explaining name for your project.
To setup MongoDB in the IIS webserver, adjust the parameters in the file `appsettings.json`. Default values for the databes an collections are:
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
"DatabaseName": "WorldAnalysisAPI",
"CollectionNameWorldLinks": "XXXs",
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
Set the correct server IP/adress and port number (if there is one). Use the network setup as you define in the Docker-Compose yaml file, e.g.
```
ports:
- 27037:27017 // mapping, for accessing the MongoDB from outside
- 27038:27018
networks:
vpcbr:
ipv4_address: 172.24.30.101 // or whatever you want
```
Server settings:
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
"MongoSrv": "172.24.30.101", // same as defined in the yaml file
"MongoPort": "27017", // internal docker port
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## How to dump database
Execute the following command in docker:
```
mongodump --db **insert database_name** --out /data-dump/`date +"%Y-%m-%d"`
```
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## How to import database
Execute the following command in docker:
```
mongorestore --db **insert database_name** **insert path_to_bson_file**
```
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
# Use in Visual Studio
Make sure, that an instance of MongoDB is running.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
Start application with IIS Express.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
# Use API within a Docker
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Creating the IIS docker ##
Remove the substring `src/Org.OpenAPITools/` in Dockerfile (if not already done)
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
open a command shell and generate docker by executing:
```
build-iis-docker-ws.bat
```
## License
For open source projects, say how it is licensed.
## How to start (with Docker-Compose)
The easiest way is to use docker-compose:
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
Open a command shell and use docker-compose (if necessary adapt docker-compose.yml) by executing`:
```
docker-compose.bat
```
Open http://localhost:8080/openapi/index.html in a web-browser, if you want to check the functionalities using SwaggerUI
## How to stop
Open a command shell by executing in `server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage`:
```
docker-compose down
```
cd server\worldanalysis\src\ETSI.ARF.OpenAPI.WorldAnalysis
docker build -t etsi.arf.openapi.worldanalysis .
\ No newline at end of file
docker-compose up -d --force-recreate --remove-orphans
\ No newline at end of file
# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
name: etsi_arf_wa_server
version: '1.0'
networks:
minionetwork:
driver: bridge
vpcbr:
ipam:
config:
- subnet: 172.24.30.0/24
services:
mongod:
container_name: etsi_mongodb
image: mongo:latest
environment:
- PUID=1000
- PGID=1000
volumes:
# - mongodbdata:/data/mongodb:rw
- C:/Fraunhofer/Servers/ETSI/MongoDB/data:/data/db:rw
- C:/Fraunhofer/Servers/ETSI/MongoDB/data-dump:/data/db-dump:rw
# - D:/Fraunhofer/Servers/ETSI/MongoDB/mongod.yaml:/etc/mongod.conf:rw
ports:
- 27037:27017
- 27038:27018
networks:
vpcbr:
ipv4_address: 172.24.30.101
restart: unless-stopped
etsiapi:
container_name: etsi_arf_iis_wa_server
image: etsi.arf.openapi.worldanalysis:latest
volumes:
- C:/Fraunhofer/Servers/ETSI/appsettings.json:/app/appsettings.json:rw
- C:/Fraunhofer/Servers/ETSI/WWWRoot:/app/wwwroot:rw
ports:
- 8082:44301
networks:
vpcbr:
ipv4_address: 172.24.30.100
restart: unless-stopped
openapi @ 073fd721
Subproject commit 073fd7213fd9e6ebc2f8a47d628a650de30c8bc4
{
"$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.6.0",
"generators":{
"v1.1": {
"generatorName": "aspnetcore",
"output": "./server/worldanalysis",
"inputSpec": "./openapi/API/worldanalysis/worldanalysisopenapi.yaml",
"additionalProperties": {
"aspnetCoreVersion": "5.0",
"packageName": "ETSI.ARF.OpenAPI.WorldAnalysis",
"operationModifier": "abstract",
"classModifier": "abstract"
}
}
}
}
}
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
#
# ETSI - ISG - ARF
#
**\ETSI.ARF.OpenAPI.WorldAnalysis.csproj
# Modules to initialize World Storage database, server....
**/Program.cs
**/Startup.cs
**/Dockerfile
**/appsettings.json
# Implementation of REST request and database functionalities
**/ETSI-ARF
# Design of some web pages
**/wwwroot/portal
**/wwwroot/index.html
\ No newline at end of file
# ETSI.ARF.OpenAPI.WorldAnalysis - ASP.NET Core 5.0 Server
API ensuring interoperability between Scene Management and a World Analysis service
## Upgrade NuGet Packages
NuGet packages get frequently updated.
To upgrade this solution to the latest version of all NuGet packages, use the dotnet-outdated tool.
Install dotnet-outdated tool:
```
dotnet tool install --global dotnet-outdated-tool
```
Upgrade only to new minor versions of packages
```
dotnet outdated --upgrade --version-lock Major
```
Upgrade to all new versions of packages (more likely to include breaking API changes)
```
dotnet outdated --upgrade
```
## Run
Linux/OS X:
```
sh build.sh
```
Windows:
```
build.bat
```
## Run in Docker
```
cd src/ETSI.ARF.OpenAPI.WorldAnalysis
docker build -t etsi.arf.openapi.worldanalysis .
docker run -p 5000:8080 etsi.arf.openapi.worldanalysis
```
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# 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
# Note: 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
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# 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/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# 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
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
# Container we use for final publish
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# Build container
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
# Copy the code into the container
WORKDIR /src
COPY ["ETSI.ARF.OpenAPI.WorldAnalysis.csproj", "ETSI.ARF.OpenAPI.WorldAnalysis/"]
# NuGet restore
RUN dotnet restore "ETSI.ARF.OpenAPI.WorldAnalysis/ETSI.ARF.OpenAPI.WorldAnalysis.csproj"
COPY [".", "ETSI.ARF.OpenAPI.WorldAnalysis/"]
# Build the API
WORKDIR "ETSI.ARF.OpenAPI.WorldAnalysis"
RUN dotnet build "ETSI.ARF.OpenAPI.WorldAnalysis.csproj" -c Release -o /app/build
# Publish it
FROM build AS publish
RUN dotnet publish "ETSI.ARF.OpenAPI.WorldAnalysis.csproj" -c Release -o /app/publish
# Make the final image for publishing
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ETSI.ARF.OpenAPI.WorldAnalysis.dll"]
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: June 2024
//
/*
* World Analysis API
*
* API ensuring interoperability between Scene Management and a World Analysis service
*
* The version of the OpenAPI document: 2.0.1
*
* Generated by: https://openapi-generator.tech
*/
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
using ETSI.ARF.OpenAPI.WorldAnalysis.Attributes;
using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
{
/// <summary>
///
/// </summary>
[ApiController]
public class CapabilitiesApiControllerImpl : CapabilitiesApiController
{
/// <summary>
/// Get the supported capabilities of the World Analysis
/// </summary>
public override IActionResult GetCapabilities([FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// Get all capabilities from all anchors/trackables
// Info: Capabilities are collected after a module is connected via websockets
// Create list
List<Capability> capabilitiesList = new List<Capability>();
capabilitiesList.AddRange(WorldAnalysisConnections.Singleton.GetCapabilities());
// Create response object
GetCapabilities200Response response = new GetCapabilities200Response();
response.Capabilities = capabilitiesList;
return new ObjectResult(response);
//return StatusCode(405, "Not supported yet!");
}
/// <summary>
/// For a given trackable or anchor, get its support information
/// </summary>
public override IActionResult GetSupport([FromRoute (Name = "trackableOrAnchorUUID")][Required]Guid trackableOrAnchorUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// Create list
List<Capability> capabilitiesList = new List<Capability>();
capabilitiesList.AddRange(WorldAnalysisConnections.Singleton.GetCapabilitiesFromUuid(trackableOrAnchorUUID));
// Create response object
GetSupport200Response response = new GetSupport200Response();
response.Capabilities = capabilitiesList;
return new ObjectResult(response);
//return StatusCode(405, "Not supported yet!");
}
}
}
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: June 2024
//
/*
* ETSI ARF Core System API
*
* API ensuring interoperability between the various services, tools and apps in ARF
*
* The version of the OpenAPI document: 2.0.1
*
* Generated by: https://openapi-generator.tech
*/
using System;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
using ETSI.ARF.OpenAPI.WorldAnalysis.Attributes;
using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
//using ETSI.ARF.OpenAPI.WorldAnalysis.Services;
namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
{
#pragma warning disable CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
/// <summary>
///
/// </summary>
public class DefaultApiControllerImpl : DefaultApiController
{
public override IActionResult GetPing()
{
string answer = "Pong";
return new ObjectResult(answer);
}
public override IActionResult GetAdmin()
{
string answer = "ETSI ARF World Analysis Server is running.";
return new ObjectResult(answer);
}
public override IActionResult GetVersion()
{
string answer = "ETSI ARF RESTful API v" + Startup.apiVersion;
return new ObjectResult(answer);
}
[NonAction]
private string writeMsg(string title, string msg)
{
return $"<p>{title}: \t {msg}</p>";
}
[NonAction]
public /*override*/ IActionResult GetReport()
{
string projectName = "ETSI ARF - STF 669";
string machinename = System.Environment.MachineName;
string hostname = Dns.GetHostName();
var ips = Dns.GetHostAddresses(hostname);
string status = "Running";
string mongoStatus = "Running";
//string mongoCollectionUsers = "Number = " + UserService.Singleton?.NumOfDocuments();
//string mongoCollectionMachines = "Number = " + MachineService.Singleton?.NumOfDocuments();
//string mongoCollectionAssets = "Number = " + AssetService.Singleton?.NumOfDocuments();
//string mongoCollectionJobs = "Number = " + JobService.Singleton?.NumOfDocuments();
//List<User> users = UserService.Singleton?.Get();
//int activeU = 0;
//int nonActiveU = 0;
//foreach (var u in users)
// if (u.IsLogged) activeU++;
// else nonActiveU++;
//List<Job> jobs = JobService.Singleton?.Get();
//int jobNum = 0;
//foreach (var j in jobs)
// if (j.Progress != 100) jobNum++;
//
// Simple output
//
string html = "<!DOCTYPE html> <html>" +
"<head>" +
" <title>SmartSiteCloud</title>" +
"</head>" +
"<body>" +
" <h1>SmartSite Cloud</h1>" +
" <h2>Server based on IIS/ASP.NET</h1>" +
writeMsg("Local Date & Time:", DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString()) +
writeMsg("Machine Name", machinename) + writeMsg("Host Name", hostname) + writeMsg("IPv6", ips[0].ToString()) +
" <h3>Services</h3>" +
writeMsg("WebServer", status) +
writeMsg("MongoDB", mongoStatus) +
//writeMsg(" > Users", mongoCollectionUsers) +
//writeMsg(" > Machines", mongoCollectionMachines) +
//writeMsg(" > Assets", mongoCollectionAssets) +
//writeMsg(" > Jobs", mongoCollectionJobs) +
" <h2>Database</h1>" +
//" <h3>Users</h1>" +
// writeMsg(" - Active", activeU.ToString()) +
// writeMsg(" - Non active", nonActiveU.ToString()) +
//" <h3>Jobs</h1>" +
// writeMsg(" - Running jobs", jobNum.ToString()) +
"</body></html> ";
//
// Complex output (for RocketCake)
//
// always use https
//string domainName = (HttpContext.Request.IsHttps ? "https://" : "https://") + HttpContext.Request.Host.ToUriComponent() + "/portal"; // Ex: "https://localhost:44301/portal"
//string root = Path.Combine(Startup.wPath, "portal");
//string htmlFname = Path.Combine(root, "status.html");
//string[] template = System.IO.File.ReadAllLines(htmlFname);
//string html2 = "";
//string msg1 =
// writeMsg("Domain", domainName) +
// writeMsg("Project", projectName) +
// writeMsg("Local Date", DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString()) +
// writeMsg("Machine Name", machinename) + writeMsg("Host Name", hostname) + writeMsg("IPv6", ips[0].ToString());
//string msg2 =
// writeMsg("WebServer", status) +
// writeMsg("MongoDB", mongoStatus) +
// //writeMsg(" > Users", mongoCollectionUsers) +
// //writeMsg(" > Machines", mongoCollectionMachines) +
// //writeMsg(" > Assets", mongoCollectionAssets) +
// //writeMsg(" > Jobs", mongoCollectionJobs) +
// writeMsg("MinIO", "-");
//foreach (var line in template)
//{
// string res = line;
// res = res.Replace("src=\"", "src=\"" + domainName + "/");
// res = res.Replace("href=\"", "href=\"" + domainName + "/");
// res = res.Replace("$name1$", projectName);
// res = res.Replace("$msg1$", msg1);
// res = res.Replace("$msg2$", msg2);
// html2 += res;
//}
return Content(html, "text/html");
//return Content(html2, "text/html");
}
}
#pragma warning restore CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
}
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: June 2024
//
/*
* World Analysis API
*
* API ensuring interoperability between Scene Management and a World Analysis service
*
* The version of the OpenAPI document: 2.0.1
*
* Generated by: https://openapi-generator.tech
*/
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
using ETSI.ARF.OpenAPI.WorldAnalysis.Attributes;
using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
{
/// <summary>
///
/// </summary>
[ApiController]
public class PoseApiControlleImpl : PoseApiController
{
/// <summary>
/// Specify the a minimum frame rate for pose estimation for Trackable types
/// </summary>
public override IActionResult ConfigureFramerate([FromBody] PoseConfiguration poseConfiguration, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// Notify the modules that the client need a new framerate
bool result = WorldAnalysisConnections.Singleton.ConfigureFramerate(poseConfiguration);
return result ? StatusCode(200, "Ok.") : StatusCode(405, "Not supported.");
}
/// <summary>
/// Request the last pose of a single Anchor or Trackable
/// </summary>
public override IActionResult GetPose([FromRoute(Name = "trackableOrAnchorUUID")][Required] Guid trackableOrAnchorUUID, [FromQuery(Name = "mode")][Required()] ModeWorldAnalysis mode, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// Request from the modules a new pose of a single UUID
Pose result;
result = WorldAnalysisConnections.Singleton.GetPose(trackableOrAnchorUUID, mode);
return new ObjectResult(result);
}
/// <summary>
/// Request the last pose of a batch of Anchor or Trackable
/// </summary>
public override IActionResult GetPoses([FromQuery(Name = "uuid")][Required()] List<UuidAndMode> uuid, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// Request from the modules new poses from all UUIDs
GetPoses200Response result = new GetPoses200Response();
foreach (var item in uuid)
{
result.Poses.Add(WorldAnalysisConnections.Singleton.GetPose(item.Uuid, item.Mode));
}
return new ObjectResult(result);
}
#pragma warning disable CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
//
// Management of subscriptions
//
/// <summary>
/// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable)
/// </summary>
private Dictionary<Guid, SubscriptionInfo> m_subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>();
public struct SubscriptionInfo
{
public Guid uuidSub; // id of subscription (id is defined by the WA server)
public SubscribeToPoseRequest subscription;
public string webSocket;
public Pose pose;
public DateTime timeValidity;
//public PoseCallback callback;
}
#pragma warning restore CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
/// <summary>
/// Get information about a subscription
/// </summary>
public override IActionResult GetSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// todo: search for a subscription and send it back
foreach (var item in m_subscriptionsPoses)
{
if (item.Key == subscriptionUUID)
{
SubscriptionSingle response = new SubscriptionSingle();
response.Uuid = subscriptionUUID;
response.Target = item.Value.subscription.Target;
response.Mode = item.Value.pose.Mode;
response.Validity = item.Value.timeValidity.Millisecond;
response.WebhookUrl = item.Value.subscription.WebhookUrl;
response.WebsocketUrl = item.Value.webSocket;
return new ObjectResult(response);
}
}
return StatusCode(404, "Not found.");
}
/// <summary>
/// Subscribe to collect the pose of an AR device, an Anchor or a Trackable
/// </summary>
public override IActionResult SubscribeToPose([FromBody] SubscribeToPoseRequest subscribeToPoseRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
// Simulation ON for STF669 pose calcualtion
string _token = token;
Guid parentUUID = Guid.Empty;
if (token.Contains(","))
{
_token = token.Split(',')[0];
parentUUID = new Guid(token.Split(',')[1]);
}
if (!Startup.IsAccessGranted(_token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
if (subscribeToPoseRequest.Targets != null && subscribeToPoseRequest.Targets.Count > 0)
{
return StatusCode(404, "Multiple subscriptions are not implemented (targets).");
}
else if (subscribeToPoseRequest.Modes != null && subscribeToPoseRequest.Modes.Count > 0)
{
return StatusCode(404, "Multiple subscriptions are not implemented (modes).");
}
int validity = subscribeToPoseRequest.Validity; // todo: is to handle here or by the client?
SubscribeToPose200Response response = new SubscribeToPose200Response();
response.Validity = validity;
response.Uuid = Guid.NewGuid();
response.Target = subscribeToPoseRequest.Target;
response.Mode = subscribeToPoseRequest.Mode;
response.WebhookUrl = subscribeToPoseRequest.WebhookUrl;
response.WebsocketUrl = "";
// Send the websocket connection URL
if (string.IsNullOrEmpty(response.WebhookUrl))
{
// Notice: starting websocket server is done autom. by the client, when calling "URL:xxx/ws"
// Registering the client is done in the analysis module, so the websocket can send to it pose updates
response.WebsocketUrl = "wss://" + Request.Host.ToString() + "/ws";
}
// We add the subscription
SubscriptionInfo info = new SubscriptionInfo();
info.uuidSub = response.Uuid;
info.webSocket = response.WebsocketUrl;
info.timeValidity = DateTime.Now.AddMilliseconds(validity / 1000.0f);
info.pose = new Pose();
info.pose.Mode = response.Mode;
info.subscription = new SubscribeToPoseRequest();
info.subscription.Target = response.Target;
info.subscription.Mode = response.Mode;
info.subscription.Validity = response.Validity;
info.subscription.WebhookUrl = response.WebhookUrl;
m_subscriptionsPoses.Add(info.uuidSub, info);
// todo: inform the module(s) that the client will track an anchor/trackable and need the pose
// todo: has the module to call GetRelocalizationInformation() then?!?
WorldAnalysisConnections.Singleton.SubscribeToPose(info, parentUUID);
return new ObjectResult(response);
}
/// <summary>
/// Remove a subscription to a given pose
/// </summary>
public override IActionResult UnsubscribeFromPose([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// Remove the subscription from the list?
if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) m_subscriptionsPoses.Remove(subscriptionUUID);
else
{
//return StatusCode(404, new Error() { Message = "Unsubscribe UUID not found!" });
}
// Inform the module(s) that the subscription ended
WorldAnalysisConnections.Singleton.UnsubscribeFromPose(subscriptionUUID);
return StatusCode(200, new Success() { Message = "Unsubscription successfull." });
}
/// <summary>
/// Update a subscription
/// </summary>
public override IActionResult UpdateSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromBody] UpdateSubscriptionRequest updateSubscriptionRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
{
if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
// todo: compare sessionID
// todo: inform the module(s) that the subscription changed
return StatusCode(405, "Not supported yet!");
}
}
}
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: June 2024
//
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Net.WebSockets;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
#pragma warning disable CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
{
//
// ETSI-ARF World Analysis WebSocket implementation
// see also: https://learn.microsoft.com/de-de/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0
//
public class WebSocketController : ControllerBase
{
private WebSocket websocket;
static public int WebSocketControllerInstanceCount = 0;
private string currentName = "";
private bool registered = false;
private bool firstTime = true;
private int timeCnt = 3;
[HttpGet("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
if (webSocket.State == WebSocketState.Connecting)
{
// Response an OK message?
}
else if (webSocket.State == WebSocketState.Open)
{
// Go to the loop...
await WebSocketServer_Loop(HttpContext, webSocket);
}
}
else
{
await HttpContext.Response.WriteAsync("ETSI-ARF World Analysis: Not a valid WebSocket request.");
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
//
// Send a line of text
//
static private async Task SendText(WebSocket ws, string msg)
{
var message = msg;
var bytes = Encoding.UTF8.GetBytes(message);
var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length);
await ws.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
}
public void SendText(string msg)
{
var message = msg;
var bytes = Encoding.UTF8.GetBytes(message);
var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length);
websocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
}
//
// Send a demo pose all seconds
//
float rotInc = 0;
private async Task SendDemoPose(WebSocket ws)
{
while (true)
{
if (ws.State == WebSocketState.Open)
{
timeCnt--;
if (timeCnt < 0)
{
await SendText(ws, "PoseStop");
break;
}
else
{
PoseValue val = new PoseValue();
val.Unit = UnitSystem.MEnum;
val.Position = new List<float>() { 1, 2, 3 };
val.Rotation = new List<float>() { rotInc, 0, 0, 0 };
rotInc += 0.01f;
Pose pose = new Pose();
pose.Uuid = new Guid();
pose.EstimationState = Pose.EstimationStateEnum.OKEnum;
//pose.SubscriptionUrl = "";
pose.Value = val;
pose.Timestamp = (int)DateTime.Now.ToFileTime();
pose.Confidence = 1;
pose.Mode = ModeWorldAnalysis.DEVICETOTRACKABLESEnum;
string json = pose.ToJson();
await SendText(ws, "NewPose=" + json);
}
}
else if (ws.State == WebSocketState.Closed || ws.State == WebSocketState.Aborted)
{
if (WorldAnalysisConnections.Singleton.modules.Contains(currentModule)) WorldAnalysisConnections.Singleton.modules.Remove(currentModule);
if (WorldAnalysisConnections.Singleton.clients.Contains(currentClient)) WorldAnalysisConnections.Singleton.clients.Remove(currentClient);
currentClient = null;
currentModule = null;
break;
}
Thread.Sleep(250);
}
}
private Module currentModule;
private Client currentClient;
private async void OnReceiveText(string msg)
{
#region Register the client/module
if (firstTime)
{
if (msg.StartsWith("RegisterModule="))
{
registered = true;
firstTime = false;
currentName = msg.Split('=')[1];
// If module exist already reuse it
Module module = null;
foreach (var item in WorldAnalysisConnections.Singleton.modules) if (item.name == currentName) module = item;
if (module != null)
{
currentModule = module;
currentModule.name = currentName;
currentModule.websockets = this;
currentModule.capabilities.Clear();
}
if (currentModule == null && currentClient == null)
{
currentModule = new Module();
currentModule.name = currentName;
currentModule.websockets = this;
WorldAnalysisConnections.Singleton.modules.Add(currentModule);
}
SendText($"ARF World Analysis Server: #{ WebSocketControllerInstanceCount } You are now registered as module: { currentName }");
}
else if (msg.StartsWith("RegisterClient="))
{
registered = true;
firstTime = false;
currentName = msg.Split('=')[1];
if (currentModule == null && currentClient == null)
{
currentClient = new Client();
currentClient.name = currentName;
currentClient.websockets = this;
WorldAnalysisConnections.Singleton.clients.Add(currentClient);
SendText($"ARF World Analysis Server: #{ WebSocketControllerInstanceCount } You are now registered as client: { currentName }");
}
}
else
{
registered = false;
SendText("ARF World Analysis Server: Cannot register " + msg);
}
return;
}
#endregion
if (registered)
{
//
// Some admin stuffs
//
if (msg == "Idle")
{
SendText("Idle");
}
else if (msg == "Busy")
{
SendText("Busy");
}
else if (msg.StartsWith("UnregisterModule="))
{
string name = msg.Split('=')[1];
// Unregister a client (e.g. Unity client)
Module module = null;
foreach (var item in WorldAnalysisConnections.Singleton.modules) if (item.name == name) module = item;
if (module != null)
{
WorldAnalysisConnections.Singleton.modules.Remove(module);
SendText("UnregisterModuleOK");
}
currentName = "";
firstTime = true;
registered = false;
}
else if (msg.StartsWith("UnregisterClient="))
{
string name = msg.Split('=')[1];
// Unregister a client (e.g. Unity client)
Client client = null;
foreach (var item in WorldAnalysisConnections.Singleton.clients) if (item.name == name) client = item;
if (client != null)
{
WorldAnalysisConnections.Singleton.clients.Remove(client);
SendText("UnregisterClientOK");
}
currentName = "";
firstTime = true;
registered = false;
}
//
// OpenAPI
//
else if (msg == "PoseIsNowSubscribed")
{
SendText("RequestNextPose");
}
//
// Messages from a module (Analysis)
//
else if (msg.StartsWith("Capabilities=")) // Receive capab. from a module
{
// Module is sending their capabilities
string[] str_cap = msg.Split('=');
string moduleName = str_cap[1];
Capability _c = JsonConvert.DeserializeObject<Capability>(str_cap[2]);
// Has the module already send their capabilities?
Module module = null;
foreach (var item in WorldAnalysisConnections.Singleton.modules) if (item.name == moduleName) module = item;
if (module != null) module.capabilities.Add(_c);
}
else if (msg.StartsWith("NewPose="))
{
string[] str_pose = msg.Split('=');
// Send the pose to the client(s)
Pose pose = JsonConvert.DeserializeObject<Pose>(str_pose[1]);
//WorldAnalysisConnections.Singleton.SendPoseToClients(pose);
WorldAnalysisConnections.Singleton.SendPoseToClients(str_pose[1]);
// todo: if there are some subscription ask modules for new pose
SendText("RequestNextPose");
}
else if (msg.StartsWith("Error="))
{
string errorMsg = msg.Split('=')[1];
foreach (var item in WorldAnalysisConnections.Singleton.clients)
{
await SendText(item.websockets.websocket, errorMsg);
}
}
//
// Messages from a client (Unity)
//
else if (msg.StartsWith("PoseStart")) // send some fake poses to Unity clients
{
await SendDemoPose(websocket);
}
else if (msg.StartsWith("CurrentUserPose="))
{
// A client (Unity) is sending the current user posistion
// Send new user pose to all modules
foreach (var item in WorldAnalysisConnections.Singleton.modules)
{
await SendText(item.websockets.websocket, msg);
}
//// Test: send user pose back to clients
//string[] str_pose = msg.Split('=');
//Pose pose = JsonConvert.DeserializeObject<Pose>(str_pose[1]);
//pose.Value.Position[2] += 1f;
//WorldAnalysisConnections.Singleton.SendPoseToClients(pose);
}
//
// Messages from modules and clients
//
else
{
// Send a response
SendText("ARF World Analysis Server: I got this unknown message: " + msg);
}
}
}
private async Task WebSocketServer_Loop(HttpContext context, WebSocket ws)
{
websocket = ws;
WebSocketControllerInstanceCount++;
var buffer = new byte[1024 * 4];
// Read/get the first data block
WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
string getMsg = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
OnReceiveText(getMsg);
}
// Entering the loop
while (!result.CloseStatus.HasValue)
{
// Read/get the next block
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
string getMsg = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
OnReceiveText(getMsg);
}
}
await ws.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
WebSocketControllerInstanceCount--;
}
//
// Echo incoming messages
//
private async Task Echo(WebSocketReceiveResult context, WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
// Read/get the first data block
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
// Send/echo the message back to the client
await SendText(webSocket, "ARF World Analysis Server: I got this (raw) message: ");
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
}
}
}
#pragma warning restore CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element