Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/build-push-acr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Build and Push to ACR

on:
workflow_dispatch:
push:
branches:
- "main"
- "rest-api-stc-integration"

permissions:
id-token: write
contents: read

env:
IMAGE_NAME: ship-traffic-generator

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Read package version and create image tags
id: meta
run: |
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
IMAGE_TAG=$(python -c "import tomllib, pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text(encoding='utf-8'))['project']['version'])")
else
IMAGE_TAG=$(echo "${GITHUB_REF_NAME}" | tr '[:upper:]' '[:lower:]' | sed -E 's|[^a-z0-9._-]+|-|g')
fi
echo "image_tag=${IMAGE_TAG}" >> $GITHUB_OUTPUT
echo "image=${{ secrets.ACR_LOGIN_SERVER }}/${IMAGE_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT

- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: ACR login
run: az acr login --name ${{ secrets.ACR_NAME }}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.image }}
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ celerybeat-schedule
.venv
venv/
ENV/
.env

# Spyder project settings
.spyderproject
Expand All @@ -107,8 +108,11 @@ ENV/

# VS Code Settings
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local certs
cert.pem
key.pem
48 changes: 0 additions & 48 deletions .vscode/settings.json

This file was deleted.

21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PYTHONPATH=/app/src \
FLASK_APP=trafficgen.app \
FLASK_DEBUG=0

WORKDIR /app

COPY pyproject.toml README.md LICENSE ./
COPY src ./src
COPY data ./data

RUN pip install --upgrade pip \
&& pip install .

EXPOSE 5000

CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"]
127 changes: 127 additions & 0 deletions data/example_situations_config_json/example_situation_01_ts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"trafficSituations": {
"title": "HO, CR-GW, OT-GW",
"description": "Head-on, crossing give-way and overtaking give-way situations with three target ships.",
"ownShip": {
"id": 1,
"initial": {
"position": {
"lat": 58.763449,
"lon": 10.490654
},
"sog": 10.0,
"cog": 0.0,
"heading": 0.0,
"navStatus": "Under way using engine"
}
},
"encounters": [
{
"desiredEncounterType": "head-on",
"beta": -3.0,
"relativeSpeed": 1.2,
"vectorTime": 20.0
},
{
"desiredEncounterType": "crossing-give-way",
"beta": 55.0,
"relativeSpeed": 1.4,
"vectorTime": 17.0
},
{
"desiredEncounterType": "overtaking-give-way",
"beta": 30.0,
"relativeSpeed": 0.6,
"vectorTime": 15.0
}
]
},
"ownShipStatic": {
"id": 1,
"dimensions": {
"length": 122,
"width": 20,
"height": 8
},
"sogMax": 17,
"mmsi": 257847600,
"name": "BASTO VI",
"shipType": "Passenger"
},
"targetShipsStatic": [
{
"id": 2,
"dimensions": {
"length": 80,
"width": 15,
"height": 5
},
"sogMax": 12,
"mmsi": 257847601,
"name": "TARGET SHIP 1",
"shipType": "Cargo"
},
{
"id": 3,
"dimensions": {
"length": 60,
"width": 10,
"height": 4
},
"sogMax": 10,
"mmsi": 257847602,
"name": "TARGET SHIP 2",
"shipType": "Tanker"
},
{
"id": 4,
"dimensions": {
"length": 40,
"width": 8,
"height": 3
},
"sogMax": 8,
"mmsi": 257847603,
"name": "TARGET SHIP 3",
"shipType": "Fishing"
}
],
"encounterSettings": {
"classification": {
"theta13Criteria": 67.5,
"theta14Criteria": 5.0,
"theta15Criteria": 5.0,
"theta15": [
112.5,
247.5
]
},
"relativeSpeed": {
"overtakingStandOn": [
1.5,
2
],
"overtakingGiveWay": [
0.25,
0.75
],
"headOn": [
0.5,
1.5
],
"crossingGiveWay": [
0.5,
1.5
],
"crossingStandOn": [
0.5,
1.5
]
},
"situationLength": 30.0,
"maxMeetingDistance": 0.0,
"commonVector": 5.0,
"situationDevelopTime": 120.0,
"disableLandCheck": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
"encounters": [
{
"desiredEncounterType": "overtaking-give-way",
"vectorTime": [15.0,20.0]
"vectorTime": [
15.0,
20.0
]
},
{
"desiredEncounterType": "crossing-give-way",
"vectorTime": 25.0
}
]
}
}
1 change: 1 addition & 0 deletions docs/source/_autosummary/trafficgen.read_files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
read_generated_situation_files
read_own_ship_static_file
read_situation_files
read_situation_from_file
read_target_ship_static_files

7 changes: 0 additions & 7 deletions docs/source/api.rst

This file was deleted.

48 changes: 48 additions & 0 deletions docs/source/app.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.. _app_api:

Application API
===============

This page documents the Flask REST API implemented in ``src/trafficgen/app.py``.

Available endpoints
-------------------

* ``GET /api/health``
* ``GET /api/version``
* ``GET /api/settings/default``
* ``GET /api/baseline/{situation_id}``
* ``POST /api/generate``

Localhost TLS (HTTPS)
---------------------

The API enforces HTTPS by default (see ``TRAFFICGEN_ENFORCE_HTTPS`` in ``src/trafficgen/app.py``).
For local testing, start Flask with a local certificate.

Generate certificate and key:

.. code-block:: sh

openssl req -x509 -newkey rsa:2048 -sha256 -nodes \
-keyout key.pem -out cert.pem -days 365 \
-subj "/CN=localhost"

Run the API with TLS:

.. code-block:: sh

uv run flask --app src/trafficgen/app.py run --host 0.0.0.0 --port 5000 --cert cert.pem --key key.pem


If you use a self-signed certificate that is not trusted by your client,
you may need to add your certificate to trust store (recommended) or use
client-specific insecure flags for local testing only.

OpenAPI specification
---------------------

The OpenAPI 3.0 specification for the current app is available below:

.. literalinclude:: openapi.yaml
:language: yaml
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Ship Traffic Generator Documentation
input_files
output_files
api
app
CHANGELOG
STYLEGUIDE
LICENSE
Expand Down
Loading
Loading