feat: docs Sphinx pipeline + lien dashboard header

This commit is contained in:
Flag
2026-04-23 21:17:49 +00:00
parent 1bbb6c8e6d
commit 91b25f0aae
90 changed files with 9557 additions and 5 deletions

4
docs/_build/html/.buildinfo vendored Normal file
View File

@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: fa99f5b27a6838059f5aa6cbfb8873ec
tags: 645f666f9bcd5a90fca523b33c5a78b7

BIN
docs/_build/html/.doctrees/data.doctree vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
docs/_build/html/.doctrees/index.doctree vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/_build/html/.doctrees/usage.doctree vendored Normal file

Binary file not shown.

162
docs/_build/html/_sources/data.rst.txt vendored Normal file
View File

@@ -0,0 +1,162 @@
Données — Stockage et budget disque
=====================================
Où sont stockées les données
------------------------------
.. list-table::
:header-rows: 1
:widths: 30 20 50
* - Type de donnée
- Emplacement
- Remarques
* - MP4 bruts GoPro
- z620 ``/mnt/portablessd``
- Ne quittent jamais z620. Jamais copiés sur workers.
* - Frames JPEG
- Worker ``/cosma-qc-frames/job_{id}/frame_*.jpg``
- Conservés pour reprise sur crash. Supprimables après validation du stitch.
* - PLY par job
- Worker ``/cosma-qc-frames/job_{id}/reconstruction.ply``
- Entrée du stitch per_auv.
* - PLY stitch par AUV
- Worker ``/cosma-qc-frames/stitch_{N}.ply``
- Fusion des segments d'un AUV.
* - PLY stitch global
- Worker ``/cosma-qc-frames/stitch_global.ply``
- Nuage de points final toute mission.
* - GLB (export web)
- Worker ``/cosma-qc-frames/job_{id}/reconstruction.glb``
- Généré à la demande. 5M points, ~76 MB.
Budget disque observé
----------------------
.. list-table::
:header-rows: 1
:widths: 40 30 30
* - Type
- Taille typique
- Base de calcul
* - Frames JPEG par job
- ~11 GB
- job 45 min à 2 fps, 1920x1080
* - PLY par job (reconstruction)
- 2 5 GB
- dépend de la densité de la scène
* - GLB par job (export web)
- ~76 MB
- 5M points (job_21 observé)
* - PLY stitch AUV
- variable
- somme des PLY segments
* - PLY global
- variable
- somme de tous les AUV
Pour un AUV avec 4 jobs de 45 min chacun :
- Frames : 4 x 11 GB = **~44 GB** (supprimables après validation)
- PLY jobs : 4 x 3.5 GB = **~14 GB**
- PLY stitch AUV : **~6-10 GB**
Politique de nettoyage
------------------------
Frames JPEG
^^^^^^^^^^^
Les frames sont conservées uniquement pour permettre la reprise sur crash.
Une fois le stitch per_auv validé visuellement, **les frames peuvent être supprimées**.
.. code-block:: bash
# Supprimer les frames d'un job (conserver le PLY !)
rm -rf /cosma-qc-frames/job_ID/frame_*.jpg
rm -f /cosma-qc-frames/job_ID/.video_*.done
# Vérifier que le PLY est intact avant suppression
ls -lh /cosma-qc-frames/job_ID/reconstruction.ply
PLY intermédiaires
^^^^^^^^^^^^^^^^^^
Les PLY par job peuvent être supprimés après que le stitch per_auv est validé
et sauvegardé hors-ligne.
.. code-block:: bash
# Conserver uniquement le stitch, supprimer les PLY jobs
rm /cosma-qc-frames/job_ID/reconstruction.ply
Vérification espace disque
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: bash
# Espace total workers
ssh floppyrj45@192.168.0.84 "df -h /cosma-qc-frames"
ssh floppyrj45@192.168.0.87 "df -h /cosma-qc-frames"
# Taille par job
du -sh /cosma-qc-frames/job_*/
# Top consommateurs
du -sh /cosma-qc-frames/* | sort -rh | head -20
Export GLB
-----------
Le GLB est une version allégée du nuage de points pour visualisation web.
Génération via l'API dashboard :
.. code-block:: bash
curl -X POST http://192.168.0.82:3849/jobs/ID/export_glb
Génération manuelle sur le worker :
.. code-block:: python
import trimesh, numpy as np
pc = trimesh.load('/cosma-qc-frames/job_ID/reconstruction.ply')
idx = np.random.choice(len(pc.vertices), 5_000_000, replace=False)
sub = trimesh.PointCloud(pc.vertices[idx], colors=pc.colors[idx])
sub.export('/cosma-qc-frames/job_ID/reconstruction.glb')
Téléchargement :
.. code-block:: bash
# Lancer le serveur HTTP sur le worker
ssh floppyrj45@192.168.0.84 \
"python3 -m http.server 8300 --directory /cosma-qc-frames"
# Télécharger depuis PC
wget http://192.168.0.84:8300/job_ID/reconstruction.glb
Reprise sur crash — marqueurs .done
-------------------------------------
Chaque MP4 extrait avec succès génère un fichier marqueur :
.. code-block:: text
/cosma-qc-frames/job_ID/.video_0.done
/cosma-qc-frames/job_ID/.video_1.done
...
En cas de crash, la reprise saute automatiquement les vidéos déjà traitées.
Pour forcer une ré-extraction complète :
.. code-block:: bash
rm /cosma-qc-frames/job_ID/.video_*.done

13
docs/_build/html/_sources/index.rst.txt vendored Normal file
View File

@@ -0,0 +1,13 @@
cosma-qc — Documentation
=========================
Pipeline de contrôle qualité vidéo pour drones sous-marins AUV COSMA.
.. toctree::
:maxdepth: 2
:caption: Contenu
pipeline
infrastructure
data
usage

View File

@@ -0,0 +1,171 @@
Infrastructure
==============
Réseau LAN — 192.168.0.0/24
-----------------------------
.. code-block:: text
┌─────────────────────────────────────────────────────────┐
│ LAN 192.168.0.0/24 │
│ │
│ .82 CORE Dispatcher (systemd) + FastAPI :3849 │
│ Gitea + Grafana + InfluxDB + Caddy │
│ │
│ .84 ml-stack GPU worker RTX 3090 24GB │
│ .87 gpu GPU worker RTX 3060 12GB │
│ │
│ .168 z620 Proxmox host HP Z620 │
│ SSD → /mnt/portablessd (MP4 bruts) │
└─────────────────────────────────────────────────────────┘
Nœud core (.82)
----------------
**Rôle :** orchestrateur central du pipeline.
Services actifs :
- **Dispatcher** — service systemd cosma-qc-dispatcher.
Boucle principale qui dispatch les jobs aux workers GPU.
- **Dashboard FastAPI** — conteneur Docker exposé sur le port **3849**.
Interface web de monitoring des jobs.
- **Gitea** — dépôt source floppyrj45/cosma-qc.
- **Grafana / InfluxDB** — monitoring infrastructure.
Commandes utiles :
.. code-block:: bash
# Statut dispatcher
sudo systemctl status cosma-qc-dispatcher
# Logs dispatcher temps réel
sudo journalctl -u cosma-qc-dispatcher -f
# Dashboard
http://192.168.0.82:3849
Nœuds GPU workers (.84 et .87)
--------------------------------
.. list-table::
:header-rows: 1
:widths: 15 25 20 40
* - IP
- Nom
- GPU
- VRAM
* - .84
- ml-stack
- RTX 3090
- 24 GB
* - .87
- gpu
- RTX 3060
- 12 GB
**Rôle :** exécution de ffmpeg (extraction frames) et lingbot-map (reconstruction 3D).
Répertoire de travail sur chaque worker :
.. code-block:: text
/cosma-qc-frames/
├── job_1/
│ ├── frame_000001.jpg … frame_NNNNNN.jpg
│ ├── .video_0.done
│ ├── reconstruction.ply
│ └── reconstruction.glb (généré à la demande)
├── job_2/
│ └── …
└── stitch_1.ply
Nœud z620 (.168)
-----------------
**Rôle :** stockage des MP4 bruts GoPro.
- Proxmox host HP Z620.
- SSD monté sur /mnt/portablessd.
- Les MP4 **ne quittent jamais** z620 — ffmpeg s'y exécute via SSH.
Accès SSH depuis core :
.. code-block:: bash
ssh floppyrj45@192.168.0.168
Service systemd dispatcher
---------------------------
Fichier de service : /etc/systemd/system/cosma-qc-dispatcher.service
.. code-block:: ini
[Unit]
Description=COSMA QC Dispatcher
After=network.target
[Service]
User=floppyrj45
WorkingDirectory=/home/floppyrj45/docker/cosma-qc
ExecStart=/usr/bin/python3 app/dispatcher.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Commandes de gestion :
.. code-block:: bash
sudo systemctl start cosma-qc-dispatcher
sudo systemctl stop cosma-qc-dispatcher
sudo systemctl restart cosma-qc-dispatcher
sudo systemctl enable cosma-qc-dispatcher # démarrage auto
Conteneur Docker dashboard
---------------------------
Le dashboard FastAPI tourne dans un conteneur Docker.
.. code-block:: bash
cd /home/floppyrj45/docker/cosma-qc
docker compose up -d # démarrer
docker compose down # arrêter
docker compose logs -f # logs
Accès : http://192.168.0.82:3849
Ports réseau récapitulatifs
----------------------------
.. list-table::
:header-rows: 1
:widths: 15 15 70
* - Host
- Port
- Service
* - .82
- 3849
- Dashboard FastAPI cosma-qc
* - .84 / .87
- 8100+N
- Viser viewer (reconstruction job N)
* - .84 / .87
- 8300
- HTTP server GLB export

View File

@@ -0,0 +1,185 @@
Pipeline cosma-qc
=================
Vue d'ensemble
--------------
Le pipeline cosma-qc traite les vidéos GoPro brutes acquises par les drones sous-marins
AUV COSMA pour produire des nuages de points 3D denses (PLY) et des exports web (GLB).
Flux de données global
----------------------
.. code-block:: text
z620 (/mnt/portablessd)
├── GX*.MP4 (brut)
▼ [1. Ingest]
SQLite DB (jobs)
▼ [2. Extraction ffmpeg]
GPU worker /cosma-qc-frames/job_{id}/frame_%06d.jpg
▼ [3. Reconstruction lingbot-map]
GPU worker /cosma-qc-frames/job_{id}/reconstruction.ply
▼ [4. Stitch per_auv]
GPU worker /cosma-qc-frames/stitch_{N}.ply (par AUV)
▼ [5. Stitch cross_auv]
GPU worker /cosma-qc-frames/stitch_global.ply
▼ [6. Export GLB (à la demande)]
GPU worker /cosma-qc-frames/job_{id}/reconstruction.glb
Étape 1 — Ingest
-----------------
**Script :** scripts/ingest.py
**Rôle :** Scanner le SSD de z620, regrouper les MP4 GoPro par AUV/GoPro/segment
temporel et écrire les jobs dans la base SQLite.
Logique de regroupement :
- Les fichiers sont groupés par numéro de caméra GoPro (GXXX).
- Un nouveau segment est créé si l'écart entre deux fichiers dépasse **5 minutes**.
- Chaque segment → un job en base avec statut pending.
Structure d'un job en base :
.. code-block:: sql
CREATE TABLE jobs (
id INTEGER PRIMARY KEY,
auv TEXT,
gopro TEXT,
segment INTEGER,
files TEXT, -- JSON list of z620:/path/GX*.MP4
status TEXT, -- pending / running / done / failed
worker TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
Les chemins vidéo sont stockés sous la forme z620:/chemin/absolu/GX*.MP4
pour indiquer que la source est sur z620, pas sur le worker GPU.
Étape 2 — Extraction des frames
---------------------------------
**Fonction :** do_extract dans le dispatcher (core .82)
**Outil :** ffmpeg
Paramètres d'extraction :
.. code-block:: bash
ffmpeg -i input.mp4 -vf fps=2,scale=1920:1080 -q:v 2 {frames_dir}/job_{id}/frame_%06d.jpg
**Particularité z620 :** lorsque la source est z620:/..., ffmpeg s'exécute
**directement sur z620** via SSH — seuls les JPEG voyagent sur le réseau.
Les MP4 bruts ne quittent jamais z620.
Filtre hors-eau (heuristique de luminosité) :
- Les frames dont la luminosité moyenne est au-dessus d'un seuil sont supprimées
(ciel, surface, hors-eau).
- Ce filtre réduit le bruit dans la reconstruction.
Reprise sur crash :
- Un fichier marqueur .video_N.done est créé après chaque MP4 traité.
- En cas de redémarrage, les vidéos déjà traitées sont ignorées.
Étape 3 — Reconstruction 3D
-----------------------------
**Fonction :** do_reconstruct
**Outil :** lingbot-map (demo.py) sur le worker GPU
**Entrée :** répertoire de frames {worker_frames_dir}/job_{id}/
**Sortie :** {worker_frames_dir}/job_{id}/reconstruction.ply
(nuage de points dense, jusqu'à **150 millions de points**)
Un viewer Viser est automatiquement démarré sur le port 8100 + job_id
pendant la reconstruction pour visualisation en temps réel.
.. code-block:: bash
# Visualiser pendant reconstruction
# naviguer vers http://{worker_ip}:{8100+job_id}
Étape 4 — Stitch par AUV
--------------------------
**Fonction :** do_stitch_per_auv
**Déclenchement :** automatique quand **tous les jobs d'un AUV** sont en statut done.
**Outil :** cosma-stitch.py
**Opération :** alignement et fusion des PLY de tous les segments d'un même AUV.
**Sortie :** {worker_frames_dir}/stitch_{N}.ply
Ce stitch s'exécute sur le worker qui détient les PLY (pas de copie inter-workers).
Étape 5 — Stitch cross-AUV
----------------------------
**Fonction :** do_stitch_cross_auv
**Déclenchement :** automatique quand **tous les stitches per_auv** sont validés.
**Opération :** fusion de tous les PLY par AUV en un nuage de points global final.
**Sortie :** {worker_frames_dir}/stitch_global.ply
Étape 6 — Export GLB (à la demande)
-------------------------------------
**Outil :** trimesh (conversion PLY → GLB)
**Sous-échantillonnage :** 5 millions de points (adapté web/navigateur)
**Sortie :** {worker_frames_dir}/job_{id}/reconstruction.glb
Un serveur HTTP minimal est lancé sur le worker (port **8300**) pour le téléchargement :
.. code-block:: bash
# Sur le worker
python3 -m http.server 8300 --directory {worker_frames_dir}
# Télécharger depuis un PC
wget http://{worker_ip}:8300/job_{id}/reconstruction.glb
Statuts de jobs
---------------
.. list-table::
:header-rows: 1
:widths: 20 80
* - Statut
- Description
* - pending
- Job créé, en attente de dispatch
* - running
- En cours de traitement (extraction ou reconstruction)
* - done
- Reconstruction terminée, PLY disponible
* - failed
- Erreur — voir logs du dispatcher

181
docs/_build/html/_sources/usage.rst.txt vendored Normal file
View File

@@ -0,0 +1,181 @@
Utilisation
===========
Ingérer une nouvelle acquisition
----------------------------------
1. Connecter le SSD GoPro à z620 (ou s'assurer que les MP4 sont dans ``/mnt/portablessd``).
2. Lancer l'ingest depuis core :
.. code-block:: bash
ssh floppyrj45@192.168.0.82
cd /home/floppyrj45/docker/cosma-qc
python3 scripts/ingest.py --path /mnt/portablessd/AUV009/
3. Vérifier les jobs créés :
.. code-block:: bash
python3 -c "
import sqlite3
conn = sqlite3.connect('cosma-qc.db')
for row in conn.execute('SELECT id, auv, gopro, segment, status FROM jobs ORDER BY id'):
print(row)
conn.close()
"
4. Le dispatcher prend automatiquement en charge les jobs en statut ``pending``.
Surveiller les jobs
--------------------
Dashboard web
^^^^^^^^^^^^^
Accéder au dashboard : http://192.168.0.82:3849
Il affiche en temps réel :
- Statut de chaque job (pending / running / done / failed)
- Worker assigné
- Progression des frames
- Liens vers les PLY et GLB
Logs dispatcher
^^^^^^^^^^^^^^^
.. code-block:: bash
# Logs temps réel
sudo journalctl -u cosma-qc-dispatcher -f
# Logs des 100 dernières lignes
sudo journalctl -u cosma-qc-dispatcher -n 100
# Filtrer erreurs
sudo journalctl -u cosma-qc-dispatcher | grep -i error
Base de données
^^^^^^^^^^^^^^^
.. code-block:: bash
# État global des jobs
sqlite3 /home/floppyrj45/docker/cosma-qc/cosma-qc.db \
"SELECT id, auv, status, worker, updated_at FROM jobs ORDER BY id;"
# Jobs en cours
sqlite3 cosma-qc.db \
"SELECT id, auv, worker FROM jobs WHERE status='running';"
# Jobs échoués
sqlite3 cosma-qc.db \
"SELECT id, auv, status FROM jobs WHERE status='failed';"
Visualiser un nuage de points PLY
-----------------------------------
Sur le worker avec viewer Viser (lancé automatiquement pendant reconstruction) :
.. code-block:: bash
# Naviguer vers (remplacer ID par le numéro de job)
http://192.168.0.84:8100 # pour job 0
http://192.168.0.84:8101 # pour job 1
# etc.
Avec CloudCompare depuis un PC (si PLY téléchargé) :
.. code-block:: bash
# Copier le PLY vers PC
scp floppyrj45@192.168.0.84:/cosma-qc-frames/job_ID/reconstruction.ply ./
# Ouvrir dans CloudCompare
Télécharger un GLB
-------------------
1. Générer le GLB via l'API :
.. code-block:: bash
curl -X POST http://192.168.0.82:3849/jobs/ID/export_glb
2. Attendre la fin de la génération (peut prendre quelques minutes selon la taille du PLY).
3. Lancer le serveur HTTP sur le worker concerné :
.. code-block:: bash
ssh floppyrj45@192.168.0.84 \
"nohup python3 -m http.server 8300 --directory /cosma-qc-frames > /tmp/http8300.log 2>&1 &"
4. Télécharger :
.. code-block:: bash
wget http://192.168.0.84:8300/job_ID/reconstruction.glb
5. Arrêter le serveur HTTP après téléchargement :
.. code-block:: bash
ssh floppyrj45@192.168.0.84 "pkill -f 'http.server 8300'"
Relancer un job échoué
-----------------------
.. code-block:: bash
# Remettre un job en pending
sqlite3 /home/floppyrj45/docker/cosma-qc/cosma-qc.db \
"UPDATE jobs SET status='pending', worker=NULL WHERE id=ID;"
# Supprimer les marqueurs .done pour forcer ré-extraction complète
ssh floppyrj45@192.168.0.84 \
"rm -f /cosma-qc-frames/job_ID/.video_*.done"
# Redémarrer le dispatcher si nécessaire
sudo systemctl restart cosma-qc-dispatcher
Le dispatcher reprend le job au prochain cycle.
Redémarrer le pipeline complet
--------------------------------
.. code-block:: bash
# Arrêter
sudo systemctl stop cosma-qc-dispatcher
docker compose -f /home/floppyrj45/docker/cosma-qc/docker-compose.yml down
# Démarrer
docker compose -f /home/floppyrj45/docker/cosma-qc/docker-compose.yml up -d
sudo systemctl start cosma-qc-dispatcher
Vérifications rapides post-mission
------------------------------------
.. code-block:: bash
# 1. Tous les jobs done ?
sqlite3 cosma-qc.db "SELECT COUNT(*) FROM jobs WHERE status != 'done';"
# Doit retourner 0
# 2. Tous les PLY présents ?
for id in $(sqlite3 cosma-qc.db "SELECT id FROM jobs WHERE status='done'"); do
worker=$(sqlite3 cosma-qc.db "SELECT worker FROM jobs WHERE id=$id")
ssh floppyrj45@$worker "ls -lh /cosma-qc-frames/job_${id}/reconstruction.ply"
done
# 3. Espace disque OK ?
ssh floppyrj45@192.168.0.84 "df -h /cosma-qc-frames"
ssh floppyrj45@192.168.0.87 "df -h /cosma-qc-frames"

View File

@@ -0,0 +1,123 @@
/* Compatability shim for jQuery and underscores.js.
*
* Copyright Sphinx contributors
* Released under the two clause BSD licence
*/
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}

476
docs/_build/html/_static/base-stemmer.js vendored Normal file
View File

@@ -0,0 +1,476 @@
// @ts-check
/**@constructor*/
BaseStemmer = function() {
/** @protected */
this.current = '';
this.cursor = 0;
this.limit = 0;
this.limit_backward = 0;
this.bra = 0;
this.ket = 0;
/**
* @param {string} value
*/
this.setCurrent = function(value) {
this.current = value;
this.cursor = 0;
this.limit = this.current.length;
this.limit_backward = 0;
this.bra = this.cursor;
this.ket = this.limit;
};
/**
* @return {string}
*/
this.getCurrent = function() {
return this.current;
};
/**
* @param {BaseStemmer} other
*/
this.copy_from = function(other) {
/** @protected */
this.current = other.current;
this.cursor = other.cursor;
this.limit = other.limit;
this.limit_backward = other.limit_backward;
this.bra = other.bra;
this.ket = other.ket;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.in_grouping = function(s, min, max) {
/** @protected */
if (this.cursor >= this.limit) return false;
var ch = this.current.charCodeAt(this.cursor);
if (ch > max || ch < min) return false;
ch -= min;
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
this.cursor++;
return true;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.go_in_grouping = function(s, min, max) {
/** @protected */
while (this.cursor < this.limit) {
var ch = this.current.charCodeAt(this.cursor);
if (ch > max || ch < min)
return true;
ch -= min;
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0)
return true;
this.cursor++;
}
return false;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.in_grouping_b = function(s, min, max) {
/** @protected */
if (this.cursor <= this.limit_backward) return false;
var ch = this.current.charCodeAt(this.cursor - 1);
if (ch > max || ch < min) return false;
ch -= min;
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
this.cursor--;
return true;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.go_in_grouping_b = function(s, min, max) {
/** @protected */
while (this.cursor > this.limit_backward) {
var ch = this.current.charCodeAt(this.cursor - 1);
if (ch > max || ch < min) return true;
ch -= min;
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return true;
this.cursor--;
}
return false;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.out_grouping = function(s, min, max) {
/** @protected */
if (this.cursor >= this.limit) return false;
var ch = this.current.charCodeAt(this.cursor);
if (ch > max || ch < min) {
this.cursor++;
return true;
}
ch -= min;
if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) {
this.cursor++;
return true;
}
return false;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.go_out_grouping = function(s, min, max) {
/** @protected */
while (this.cursor < this.limit) {
var ch = this.current.charCodeAt(this.cursor);
if (ch <= max && ch >= min) {
ch -= min;
if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) != 0) {
return true;
}
}
this.cursor++;
}
return false;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.out_grouping_b = function(s, min, max) {
/** @protected */
if (this.cursor <= this.limit_backward) return false;
var ch = this.current.charCodeAt(this.cursor - 1);
if (ch > max || ch < min) {
this.cursor--;
return true;
}
ch -= min;
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) {
this.cursor--;
return true;
}
return false;
};
/**
* @param {number[]} s
* @param {number} min
* @param {number} max
* @return {boolean}
*/
this.go_out_grouping_b = function(s, min, max) {
/** @protected */
while (this.cursor > this.limit_backward) {
var ch = this.current.charCodeAt(this.cursor - 1);
if (ch <= max && ch >= min) {
ch -= min;
if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) != 0) {
return true;
}
}
this.cursor--;
}
return false;
};
/**
* @param {string} s
* @return {boolean}
*/
this.eq_s = function(s)
{
/** @protected */
if (this.limit - this.cursor < s.length) return false;
if (this.current.slice(this.cursor, this.cursor + s.length) != s)
{
return false;
}
this.cursor += s.length;
return true;
};
/**
* @param {string} s
* @return {boolean}
*/
this.eq_s_b = function(s)
{
/** @protected */
if (this.cursor - this.limit_backward < s.length) return false;
if (this.current.slice(this.cursor - s.length, this.cursor) != s)
{
return false;
}
this.cursor -= s.length;
return true;
};
/**
* @param {Among[]} v
* @return {number}
*/
this.find_among = function(v)
{
/** @protected */
var i = 0;
var j = v.length;
var c = this.cursor;
var l = this.limit;
var common_i = 0;
var common_j = 0;
var first_key_inspected = false;
while (true)
{
var k = i + ((j - i) >>> 1);
var diff = 0;
var common = common_i < common_j ? common_i : common_j; // smaller
// w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional)
var w = v[k];
var i2;
for (i2 = common; i2 < w[0].length; i2++)
{
if (c + common == l)
{
diff = -1;
break;
}
diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2);
if (diff != 0) break;
common++;
}
if (diff < 0)
{
j = k;
common_j = common;
}
else
{
i = k;
common_i = common;
}
if (j - i <= 1)
{
if (i > 0) break; // v->s has been inspected
if (j == i) break; // only one item in v
// - but now we need to go round once more to get
// v->s inspected. This looks messy, but is actually
// the optimal approach.
if (first_key_inspected) break;
first_key_inspected = true;
}
}
do {
var w = v[i];
if (common_i >= w[0].length)
{
this.cursor = c + w[0].length;
if (w.length < 4) return w[2];
var res = w[3](this);
this.cursor = c + w[0].length;
if (res) return w[2];
}
i = w[1];
} while (i >= 0);
return 0;
};
// find_among_b is for backwards processing. Same comments apply
/**
* @param {Among[]} v
* @return {number}
*/
this.find_among_b = function(v)
{
/** @protected */
var i = 0;
var j = v.length
var c = this.cursor;
var lb = this.limit_backward;
var common_i = 0;
var common_j = 0;
var first_key_inspected = false;
while (true)
{
var k = i + ((j - i) >> 1);
var diff = 0;
var common = common_i < common_j ? common_i : common_j;
var w = v[k];
var i2;
for (i2 = w[0].length - 1 - common; i2 >= 0; i2--)
{
if (c - common == lb)
{
diff = -1;
break;
}
diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2);
if (diff != 0) break;
common++;
}
if (diff < 0)
{
j = k;
common_j = common;
}
else
{
i = k;
common_i = common;
}
if (j - i <= 1)
{
if (i > 0) break;
if (j == i) break;
if (first_key_inspected) break;
first_key_inspected = true;
}
}
do {
var w = v[i];
if (common_i >= w[0].length)
{
this.cursor = c - w[0].length;
if (w.length < 4) return w[2];
var res = w[3](this);
this.cursor = c - w[0].length;
if (res) return w[2];
}
i = w[1];
} while (i >= 0);
return 0;
};
/* to replace chars between c_bra and c_ket in this.current by the
* chars in s.
*/
/**
* @param {number} c_bra
* @param {number} c_ket
* @param {string} s
* @return {number}
*/
this.replace_s = function(c_bra, c_ket, s)
{
/** @protected */
var adjustment = s.length - (c_ket - c_bra);
this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket);
this.limit += adjustment;
if (this.cursor >= c_ket) this.cursor += adjustment;
else if (this.cursor > c_bra) this.cursor = c_bra;
return adjustment;
};
/**
* @return {boolean}
*/
this.slice_check = function()
{
/** @protected */
if (this.bra < 0 ||
this.bra > this.ket ||
this.ket > this.limit ||
this.limit > this.current.length)
{
return false;
}
return true;
};
/**
* @param {number} c_bra
* @return {boolean}
*/
this.slice_from = function(s)
{
/** @protected */
var result = false;
if (this.slice_check())
{
this.replace_s(this.bra, this.ket, s);
result = true;
}
return result;
};
/**
* @return {boolean}
*/
this.slice_del = function()
{
/** @protected */
return this.slice_from("");
};
/**
* @param {number} c_bra
* @param {number} c_ket
* @param {string} s
*/
this.insert = function(c_bra, c_ket, s)
{
/** @protected */
var adjustment = this.replace_s(c_bra, c_ket, s);
if (c_bra <= this.bra) this.bra += adjustment;
if (c_bra <= this.ket) this.ket += adjustment;
};
/**
* @return {string}
*/
this.slice_to = function()
{
/** @protected */
var result = '';
if (this.slice_check())
{
result = this.current.slice(this.bra, this.ket);
}
return result;
};
/**
* @return {string}
*/
this.assign_to = function()
{
/** @protected */
return this.current.slice(0, this.limit);
};
};

906
docs/_build/html/_static/basic.css vendored Normal file
View File

@@ -0,0 +1,906 @@
/*
* Sphinx stylesheet -- basic theme.
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin-top: 10px;
}
ul.search li {
padding: 5px 0;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
a:visited {
color: #551A8B;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

View File

@@ -0,0 +1 @@
.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

150
docs/_build/html/_static/doctools.js vendored Normal file
View File

@@ -0,0 +1,150 @@
/*
* Base JavaScript utilities for all Sphinx HTML documentation.
*/
"use strict";
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`,
);
Documentation.LOCALE = catalog.locale;
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget)),
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS
&& !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName))
return;
// bail with special keys
if (event.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);

View File

@@ -0,0 +1,13 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '1.0',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};

File diff suppressed because it is too large Load Diff

BIN
docs/_build/html/_static/file.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

2
docs/_build/html/_static/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});

1
docs/_build/html/_static/js/theme.js vendored Normal file

File diff suppressed because one or more lines are too long

228
docs/_build/html/_static/js/versions.js vendored Normal file
View File

@@ -0,0 +1,228 @@
const themeFlyoutDisplay = "hidden";
const themeVersionSelector = true;
const themeLanguageSelector = true;
if (themeFlyoutDisplay === "attached") {
function renderLanguages(config) {
if (!config.projects.translations.length) {
return "";
}
// Insert the current language to the options on the selector
let languages = config.projects.translations.concat(config.projects.current);
languages = languages.sort((a, b) => a.language.name.localeCompare(b.language.name));
const languagesHTML = `
<dl>
<dt>Languages</dt>
${languages
.map(
(translation) => `
<dd ${translation.slug == config.projects.current.slug ? 'class="rtd-current-item"' : ""}>
<a href="${translation.urls.documentation}">${translation.language.code}</a>
</dd>
`,
)
.join("\n")}
</dl>
`;
return languagesHTML;
}
function renderVersions(config) {
if (!config.versions.active.length) {
return "";
}
const versionsHTML = `
<dl>
<dt>Versions</dt>
${config.versions.active
.map(
(version) => `
<dd ${version.slug === config.versions.current.slug ? 'class="rtd-current-item"' : ""}>
<a href="${version.urls.documentation}">${version.slug}</a>
</dd>
`,
)
.join("\n")}
</dl>
`;
return versionsHTML;
}
function renderDownloads(config) {
if (!Object.keys(config.versions.current.downloads).length) {
return "";
}
const downloadsNameDisplay = {
pdf: "PDF",
epub: "Epub",
htmlzip: "HTML",
};
const downloadsHTML = `
<dl>
<dt>Downloads</dt>
${Object.entries(config.versions.current.downloads)
.map(
([name, url]) => `
<dd>
<a href="${url}">${downloadsNameDisplay[name]}</a>
</dd>
`,
)
.join("\n")}
</dl>
`;
return downloadsHTML;
}
document.addEventListener("readthedocs-addons-data-ready", function (event) {
const config = event.detail.data();
const flyout = `
<div class="rst-versions" data-toggle="rst-versions" role="note">
<span class="rst-current-version" data-toggle="rst-current-version">
<span class="fa fa-book"> Read the Docs</span>
v: ${config.versions.current.slug}
<span class="fa fa-caret-down"></span>
</span>
<div class="rst-other-versions">
<div class="injected">
${renderLanguages(config)}
${renderVersions(config)}
${renderDownloads(config)}
<dl>
<dt>On Read the Docs</dt>
<dd>
<a href="${config.projects.current.urls.home}">Project Home</a>
</dd>
<dd>
<a href="${config.projects.current.urls.builds}">Builds</a>
</dd>
<dd>
<a href="${config.projects.current.urls.downloads}">Downloads</a>
</dd>
</dl>
<dl>
<dt>Search</dt>
<dd>
<form id="flyout-search-form">
<input
class="wy-form"
type="text"
name="q"
aria-label="Search docs"
placeholder="Search docs"
/>
</form>
</dd>
</dl>
<hr />
<small>
<span>Hosted by <a href="https://about.readthedocs.org/?utm_source=&utm_content=flyout">Read the Docs</a></span>
</small>
</div>
</div>
`;
// Inject the generated flyout into the body HTML element.
document.body.insertAdjacentHTML("beforeend", flyout);
// Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout.
document
.querySelector("#flyout-search-form")
.addEventListener("focusin", () => {
const event = new CustomEvent("readthedocs-search-show");
document.dispatchEvent(event);
});
})
}
if (themeLanguageSelector || themeVersionSelector) {
function onSelectorSwitch(event) {
const option = event.target.selectedIndex;
const item = event.target.options[option];
window.location.href = item.dataset.url;
}
document.addEventListener("readthedocs-addons-data-ready", function (event) {
const config = event.detail.data();
const versionSwitch = document.querySelector(
"div.switch-menus > div.version-switch",
);
if (themeVersionSelector) {
let versions = config.versions.active;
if (config.versions.current.hidden || config.versions.current.type === "external") {
versions.unshift(config.versions.current);
}
const versionSelect = `
<select>
${versions
.map(
(version) => `
<option
value="${version.slug}"
${config.versions.current.slug === version.slug ? 'selected="selected"' : ""}
data-url="${version.urls.documentation}">
${version.slug}
</option>`,
)
.join("\n")}
</select>
`;
versionSwitch.innerHTML = versionSelect;
versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch);
}
const languageSwitch = document.querySelector(
"div.switch-menus > div.language-switch",
);
if (themeLanguageSelector) {
if (config.projects.translations.length) {
// Add the current language to the options on the selector
let languages = config.projects.translations.concat(
config.projects.current,
);
languages = languages.sort((a, b) =>
a.language.name.localeCompare(b.language.name),
);
const languageSelect = `
<select>
${languages
.map(
(language) => `
<option
value="${language.language.code}"
${config.projects.current.slug === language.slug ? 'selected="selected"' : ""}
data-url="${language.urls.documentation}">
${language.language.name}
</option>`,
)
.join("\n")}
</select>
`;
languageSwitch.innerHTML = languageSelect;
languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch);
}
else {
languageSwitch.remove();
}
}
});
}
document.addEventListener("readthedocs-addons-data-ready", function (event) {
// Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav.
document
.querySelector("[role='search'] input")
.addEventListener("focusin", () => {
const event = new CustomEvent("readthedocs-search-show");
document.dispatchEvent(event);
});
});

File diff suppressed because one or more lines are too long

BIN
docs/_build/html/_static/minus.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

BIN
docs/_build/html/_static/plus.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

75
docs/_build/html/_static/pygments.css vendored Normal file
View File

@@ -0,0 +1,75 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #F00 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666 } /* Operator */
.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #9C6500 } /* Comment.Preproc */
.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #E40000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #008400 } /* Generic.Inserted */
.highlight .go { color: #717171 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #04D } /* Generic.Traceback */
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #B00040 } /* Keyword.Type */
.highlight .m { color: #666 } /* Literal.Number */
.highlight .s { color: #BA2121 } /* Literal.String */
.highlight .na { color: #687822 } /* Name.Attribute */
.highlight .nb { color: #008000 } /* Name.Builtin */
.highlight .nc { color: #00F; font-weight: bold } /* Name.Class */
.highlight .no { color: #800 } /* Name.Constant */
.highlight .nd { color: #A2F } /* Name.Decorator */
.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #00F } /* Name.Function */
.highlight .nl { color: #767600 } /* Name.Label */
.highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #19177C } /* Name.Variable */
.highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #666 } /* Literal.Number.Bin */
.highlight .mf { color: #666 } /* Literal.Number.Float */
.highlight .mh { color: #666 } /* Literal.Number.Hex */
.highlight .mi { color: #666 } /* Literal.Number.Integer */
.highlight .mo { color: #666 } /* Literal.Number.Oct */
.highlight .sa { color: #BA2121 } /* Literal.String.Affix */
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #A45A77 } /* Literal.String.Regex */
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #00F } /* Name.Function.Magic */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .vm { color: #19177C } /* Name.Variable.Magic */
.highlight .il { color: #666 } /* Literal.Number.Integer.Long */

693
docs/_build/html/_static/searchtools.js vendored Normal file
View File

@@ -0,0 +1,693 @@
/*
* Sphinx JavaScript utilities for the full-text search.
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename, kind] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
// Global search result kind enum, used by themes to style search results.
// prettier-ignore
class SearchResultKind {
static get index() { return "index"; }
static get object() { return "object"; }
static get text() { return "text"; }
static get title() { return "title"; }
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _escapeHTML = (text) => {
return text
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&apos;");
};
const _displayItem = (item, searchTerms, highlightTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const contentRoot = document.documentElement.dataset.content_root;
const [docName, title, anchor, descr, score, _filename, kind] = item;
let listItem = document.createElement("li");
// Add a class representing the item's type:
// can be used by a theme's CSS selector for styling
// See SearchResultKind for the class names.
listItem.classList.add(`kind-${kind}`);
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = contentRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = contentRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = _escapeHTML(title);
if (descr) {
listItem.appendChild(document.createElement("span")).innerHTML =
` (${_escapeHTML(descr)})`;
// highlight search terms in the description
if (SPHINX_HIGHLIGHT_ENABLED)
// SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js
highlightTerms.forEach((term) =>
_highlightText(listItem, term, "highlighted"),
);
} else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, anchor),
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED)
// SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js
highlightTerms.forEach((term) =>
_highlightText(listItem, term, "highlighted"),
);
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.",
);
else
Search.status.innerText = Documentation.ngettext(
"Search finished, found one page matching the search query.",
"Search finished, found ${resultCount} pages matching the search query.",
resultCount,
).replace("${resultCount}", resultCount);
};
const _displayNextItem = (
results,
resultCount,
searchTerms,
highlightTerms,
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms, highlightTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
5,
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
// Helper function used by query() to order search results.
// Each input is an array of [docname, title, anchor, descr, score, filename, kind].
// Order the results by score (in opposite order of appearance, since the
// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
const _orderResultsByScoreThenName = (a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) =>
query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter((term) => term); // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString, anchor) => {
const htmlElement = new DOMParser().parseFromString(
htmlString,
"text/html",
);
for (const removalQuery of [".headerlink", "script", "style"]) {
htmlElement.querySelectorAll(removalQuery).forEach((el) => {
el.remove();
});
}
if (anchor) {
const anchorContent = htmlElement.querySelector(
`[role="main"] ${anchor}`,
);
if (anchorContent) return anchorContent.textContent;
console.warn(
`Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`,
);
}
// if anchor not specified or not found, fall back to main content
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template.",
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.setAttribute("role", "list");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
_parseQuery: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords set is from language_data.js
if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
if (SPHINX_HIGHLIGHT_ENABLED) {
// SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js
localStorage.setItem(
"sphinx_highlight_terms",
[...highlightTerms].join(" "),
);
}
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
},
/**
* execute search (requires search index to be loaded)
*/
_performSearch: (
query,
searchTerms,
excludedTerms,
highlightTerms,
objectTerms,
) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// Collect multiple result groups to be sorted separately and then ordered.
// Each is an array of [docname, title, anchor, descr, score, filename, kind].
const normalResults = [];
const nonMainIndexResults = [];
_removeChildren(document.getElementById("search-progress"));
const queryLower = query.toLowerCase().trim();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (
title.toLowerCase().trim().includes(queryLower)
&& queryLower.length >= title.length / 2
) {
for (const [file, id] of foundTitles) {
const score = Math.round(
(Scorer.title * queryLower.length) / title.length,
);
const boost = titles[file] === title ? 1 : 0; // add a boost for document titles
normalResults.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score + boost,
filenames[file],
SearchResultKind.title,
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && queryLower.length >= entry.length / 2) {
for (const [file, id, isMain] of foundEntries) {
const score = Math.round((100 * queryLower.length) / entry.length);
const result = [
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
SearchResultKind.index,
];
if (isMain) {
normalResults.push(result);
} else {
nonMainIndexResults.push(result);
}
}
}
}
// lookup as object
objectTerms.forEach((term) =>
normalResults.push(...Search.performObjectSearch(term, objectTerms)),
);
// lookup as search terms in fulltext
normalResults.push(
...Search.performTermsSearch(searchTerms, excludedTerms),
);
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
normalResults.forEach((item) => (item[4] = Scorer.score(item)));
nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
}
// Sort each group of results by score and then alphabetically by name.
normalResults.sort(_orderResultsByScoreThenName);
nonMainIndexResults.sort(_orderResultsByScoreThenName);
// Combine the result groups in (reverse) order.
// Non-main index entries are typically arbitrary cross-references,
// so display them after other results.
let results = [...nonMainIndexResults, ...normalResults];
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result
.slice(0, 4)
.concat([result[5]])
.map((v) => String(v))
.join(",");
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
return results.reverse();
},
query: (query) => {
const [
searchQuery,
searchTerms,
excludedTerms,
highlightTerms,
objectTerms,
] = Search._parseQuery(query);
const results = Search._performSearch(
searchQuery,
searchTerms,
excludedTerms,
highlightTerms,
objectTerms,
);
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, searchTerms, highlightTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4];
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
SearchResultKind.object,
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) => objectSearchCallback(prefix, array)),
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
// find documents, if any, containing the query word in their text/title term indices
// use Object.hasOwnProperty to avoid mismatching against prototype properties
const arr = [
{
files: terms.hasOwnProperty(word) ? terms[word] : undefined,
score: Scorer.term,
},
{
files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined,
score: Scorer.title,
},
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
if (!terms.hasOwnProperty(word)) {
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
}
if (!titleTerms.hasOwnProperty(word)) {
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
});
}
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, new Map());
const fileScores = scoreMap.get(file);
fileScores.set(word, record.score);
});
});
// create the mapping
files.forEach((file) => {
if (!fileMap.has(file)) fileMap.set(file, [word]);
else if (fileMap.get(file).indexOf(word) === -1)
fileMap.get(file).push(word);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2,
).length;
if (
wordList.length !== searchTerms.size
&& wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file
|| titleTerms[term] === file
|| (terms[term] || []).includes(file)
|| (titleTerms[term] || []).includes(file),
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w)));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
SearchResultKind.text,
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords, anchor) => {
const text = Search.htmlToText(htmlText, anchor);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent =
top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
_ready(Search.init);

View File

@@ -0,0 +1,159 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";
const SPHINX_HIGHLIGHT_ENABLED = true;
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0
&& !parent.classList.contains(className)
&& !parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
const rest = document.createTextNode(val.substr(pos + text.length));
parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling));
node.nodeValue = val.substr(0, pos);
/* There may be more occurrences of search term in this node. So call this
* function recursively on the remaining fragment.
*/
_highlight(rest, addItems, text, className);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect",
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target),
);
};
/**
* Small JavaScript module for the documentation.
*/
const SphinxHighlight = {
/**
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms");
// Update history only if '?highlight' is present; otherwise it
// clears text fragments (not set in window.location by the browser)
if (url.searchParams.has("highlight")) {
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
}
// get individual terms from highlight string
const terms = highlight
.toLowerCase()
.split(/\s+/)
.filter((x) => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">'
+ '<a href="javascript:SphinxHighlight.hideSearchWords()">'
+ _("Hide Search Matches")
+ "</a></p>",
),
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
localStorage.removeItem("sphinx_highlight_terms");
},
initEscapeListener: () => {
// only install a listener if it is really needed
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName))
return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)
return;
if (
DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
&& event.key === "Escape"
) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};
_ready(() => {
/* Do not call highlightSearchWords() when we are on the search page.
* It will highlight words from the *previous* search query.
*/
if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
SphinxHighlight.initEscapeListener();
});

288
docs/_build/html/data.html vendored Normal file
View File

@@ -0,0 +1,288 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Données — Stockage et budget disque &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Utilisation" href="usage.html" />
<link rel="prev" title="Infrastructure" href="infrastructure.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a></li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Données — Stockage et budget disque</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#ou-sont-stockees-les-donnees">Où sont stockées les données</a></li>
<li class="toctree-l2"><a class="reference internal" href="#budget-disque-observe">Budget disque observé</a></li>
<li class="toctree-l2"><a class="reference internal" href="#politique-de-nettoyage">Politique de nettoyage</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#frames-jpeg">Frames JPEG</a></li>
<li class="toctree-l3"><a class="reference internal" href="#ply-intermediaires">PLY intermédiaires</a></li>
<li class="toctree-l3"><a class="reference internal" href="#verification-espace-disque">Vérification espace disque</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#export-glb">Export GLB</a></li>
<li class="toctree-l2"><a class="reference internal" href="#reprise-sur-crash-marqueurs-done">Reprise sur crash — marqueurs .done</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Données — Stockage et budget disque</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/data.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="donnees-stockage-et-budget-disque">
<h1>Données — Stockage et budget disque<a class="headerlink" href="#donnees-stockage-et-budget-disque" title="Link to this heading"></a></h1>
<section id="ou-sont-stockees-les-donnees">
<h2>Où sont stockées les données<a class="headerlink" href="#ou-sont-stockees-les-donnees" title="Link to this heading"></a></h2>
<table class="docutils align-default">
<colgroup>
<col style="width: 30.0%" />
<col style="width: 20.0%" />
<col style="width: 50.0%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type de donnée</p></th>
<th class="head"><p>Emplacement</p></th>
<th class="head"><p>Remarques</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>MP4 bruts GoPro</p></td>
<td><p>z620 <code class="docutils literal notranslate"><span class="pre">/mnt/portablessd</span></code></p></td>
<td><p>Ne quittent jamais z620. Jamais copiés sur workers.</p></td>
</tr>
<tr class="row-odd"><td><p>Frames JPEG</p></td>
<td><p>Worker <code class="docutils literal notranslate"><span class="pre">/cosma-qc-frames/job_{id}/frame_*.jpg</span></code></p></td>
<td><p>Conservés pour reprise sur crash. Supprimables après validation du stitch.</p></td>
</tr>
<tr class="row-even"><td><p>PLY par job</p></td>
<td><p>Worker <code class="docutils literal notranslate"><span class="pre">/cosma-qc-frames/job_{id}/reconstruction.ply</span></code></p></td>
<td><p>Entrée du stitch per_auv.</p></td>
</tr>
<tr class="row-odd"><td><p>PLY stitch par AUV</p></td>
<td><p>Worker <code class="docutils literal notranslate"><span class="pre">/cosma-qc-frames/stitch_{N}.ply</span></code></p></td>
<td><p>Fusion des segments dun AUV.</p></td>
</tr>
<tr class="row-even"><td><p>PLY stitch global</p></td>
<td><p>Worker <code class="docutils literal notranslate"><span class="pre">/cosma-qc-frames/stitch_global.ply</span></code></p></td>
<td><p>Nuage de points final toute mission.</p></td>
</tr>
<tr class="row-odd"><td><p>GLB (export web)</p></td>
<td><p>Worker <code class="docutils literal notranslate"><span class="pre">/cosma-qc-frames/job_{id}/reconstruction.glb</span></code></p></td>
<td><p>Généré à la demande. 5M points, ~76 MB.</p></td>
</tr>
</tbody>
</table>
</section>
<section id="budget-disque-observe">
<h2>Budget disque observé<a class="headerlink" href="#budget-disque-observe" title="Link to this heading"></a></h2>
<table class="docutils align-default">
<colgroup>
<col style="width: 40.0%" />
<col style="width: 30.0%" />
<col style="width: 30.0%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Taille typique</p></th>
<th class="head"><p>Base de calcul</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>Frames JPEG par job</p></td>
<td><p>~11 GB</p></td>
<td><p>job 45 min à 2 fps, 1920x1080</p></td>
</tr>
<tr class="row-odd"><td><p>PLY par job (reconstruction)</p></td>
<td><p>2 5 GB</p></td>
<td><p>dépend de la densité de la scène</p></td>
</tr>
<tr class="row-even"><td><p>GLB par job (export web)</p></td>
<td><p>~76 MB</p></td>
<td><p>5M points (job_21 observé)</p></td>
</tr>
<tr class="row-odd"><td><p>PLY stitch AUV</p></td>
<td><p>variable</p></td>
<td><p>somme des PLY segments</p></td>
</tr>
<tr class="row-even"><td><p>PLY global</p></td>
<td><p>variable</p></td>
<td><p>somme de tous les AUV</p></td>
</tr>
</tbody>
</table>
<p>Pour un AUV avec 4 jobs de 45 min chacun :</p>
<ul class="simple">
<li><p>Frames : 4 x 11 GB = <strong>~44 GB</strong> (supprimables après validation)</p></li>
<li><p>PLY jobs : 4 x 3.5 GB = <strong>~14 GB</strong></p></li>
<li><p>PLY stitch AUV : <strong>~6-10 GB</strong></p></li>
</ul>
</section>
<section id="politique-de-nettoyage">
<h2>Politique de nettoyage<a class="headerlink" href="#politique-de-nettoyage" title="Link to this heading"></a></h2>
<section id="frames-jpeg">
<h3>Frames JPEG<a class="headerlink" href="#frames-jpeg" title="Link to this heading"></a></h3>
<p>Les frames sont conservées uniquement pour permettre la reprise sur crash.
Une fois le stitch per_auv validé visuellement, <strong>les frames peuvent être supprimées</strong>.</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Supprimer les frames d&#39;un job (conserver le PLY !)</span>
rm<span class="w"> </span>-rf<span class="w"> </span>/cosma-qc-frames/job_ID/frame_*.jpg
rm<span class="w"> </span>-f<span class="w"> </span>/cosma-qc-frames/job_ID/.video_*.done
<span class="c1"># Vérifier que le PLY est intact avant suppression</span>
ls<span class="w"> </span>-lh<span class="w"> </span>/cosma-qc-frames/job_ID/reconstruction.ply
</pre></div>
</div>
</section>
<section id="ply-intermediaires">
<h3>PLY intermédiaires<a class="headerlink" href="#ply-intermediaires" title="Link to this heading"></a></h3>
<p>Les PLY par job peuvent être supprimés après que le stitch per_auv est validé
et sauvegardé hors-ligne.</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Conserver uniquement le stitch, supprimer les PLY jobs</span>
rm<span class="w"> </span>/cosma-qc-frames/job_ID/reconstruction.ply
</pre></div>
</div>
</section>
<section id="verification-espace-disque">
<h3>Vérification espace disque<a class="headerlink" href="#verification-espace-disque" title="Link to this heading"></a></h3>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Espace total workers</span>
ssh<span class="w"> </span>floppyrj45@192.168.0.84<span class="w"> </span><span class="s2">&quot;df -h /cosma-qc-frames&quot;</span>
ssh<span class="w"> </span>floppyrj45@192.168.0.87<span class="w"> </span><span class="s2">&quot;df -h /cosma-qc-frames&quot;</span>
<span class="c1"># Taille par job</span>
du<span class="w"> </span>-sh<span class="w"> </span>/cosma-qc-frames/job_*/
<span class="c1"># Top consommateurs</span>
du<span class="w"> </span>-sh<span class="w"> </span>/cosma-qc-frames/*<span class="w"> </span><span class="p">|</span><span class="w"> </span>sort<span class="w"> </span>-rh<span class="w"> </span><span class="p">|</span><span class="w"> </span>head<span class="w"> </span>-20
</pre></div>
</div>
</section>
</section>
<section id="export-glb">
<h2>Export GLB<a class="headerlink" href="#export-glb" title="Link to this heading"></a></h2>
<p>Le GLB est une version allégée du nuage de points pour visualisation web.</p>
<p>Génération via lAPI dashboard :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>http://192.168.0.82:3849/jobs/ID/export_glb
</pre></div>
</div>
<p>Génération manuelle sur le worker :</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">trimesh</span><span class="o">,</span><span class="w"> </span><span class="nn">numpy</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">np</span>
<span class="n">pc</span> <span class="o">=</span> <span class="n">trimesh</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s1">&#39;/cosma-qc-frames/job_ID/reconstruction.ply&#39;</span><span class="p">)</span>
<span class="n">idx</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pc</span><span class="o">.</span><span class="n">vertices</span><span class="p">),</span> <span class="mi">5_000_000</span><span class="p">,</span> <span class="n">replace</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">sub</span> <span class="o">=</span> <span class="n">trimesh</span><span class="o">.</span><span class="n">PointCloud</span><span class="p">(</span><span class="n">pc</span><span class="o">.</span><span class="n">vertices</span><span class="p">[</span><span class="n">idx</span><span class="p">],</span> <span class="n">colors</span><span class="o">=</span><span class="n">pc</span><span class="o">.</span><span class="n">colors</span><span class="p">[</span><span class="n">idx</span><span class="p">])</span>
<span class="n">sub</span><span class="o">.</span><span class="n">export</span><span class="p">(</span><span class="s1">&#39;/cosma-qc-frames/job_ID/reconstruction.glb&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>Téléchargement :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Lancer le serveur HTTP sur le worker</span>
ssh<span class="w"> </span>floppyrj45@192.168.0.84<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;python3 -m http.server 8300 --directory /cosma-qc-frames&quot;</span>
<span class="c1"># Télécharger depuis PC</span>
wget<span class="w"> </span>http://192.168.0.84:8300/job_ID/reconstruction.glb
</pre></div>
</div>
</section>
<section id="reprise-sur-crash-marqueurs-done">
<h2>Reprise sur crash — marqueurs .done<a class="headerlink" href="#reprise-sur-crash-marqueurs-done" title="Link to this heading"></a></h2>
<p>Chaque MP4 extrait avec succès génère un fichier marqueur :</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>/cosma-qc-frames/job_ID/.video_0.done
/cosma-qc-frames/job_ID/.video_1.done
...
</pre></div>
</div>
<p>En cas de crash, la reprise saute automatiquement les vidéos déjà traitées.</p>
<p>Pour forcer une ré-extraction complète :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>rm<span class="w"> </span>/cosma-qc-frames/job_ID/.video_*.done
</pre></div>
</div>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="infrastructure.html" class="btn btn-neutral float-left" title="Infrastructure" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="usage.html" class="btn btn-neutral float-right" title="Utilisation" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

108
docs/_build/html/genindex.html vendored Normal file
View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="#" />
<link rel="search" title="Search" href="search.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a></li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a></li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a></li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Index</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
</div>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

156
docs/_build/html/index.html vendored Normal file
View File

@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>cosma-qc — Documentation &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Pipeline cosma-qc" href="pipeline.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="#" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a></li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a></li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a></li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="#">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="#" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">cosma-qc — Documentation</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/index.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="cosma-qc-documentation">
<h1>cosma-qc — Documentation<a class="headerlink" href="#cosma-qc-documentation" title="Link to this heading"></a></h1>
<p>Pipeline de contrôle qualité vidéo pour drones sous-marins AUV COSMA.</p>
<div class="toctree-wrapper compound">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a><ul>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#vue-d-ensemble">Vue densemble</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#flux-de-donnees-global">Flux de données global</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#etape-1-ingest">Étape 1 — Ingest</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#etape-2-extraction-des-frames">Étape 2 — Extraction des frames</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#etape-3-reconstruction-3d">Étape 3 — Reconstruction 3D</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#etape-4-stitch-par-auv">Étape 4 — Stitch par AUV</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#etape-5-stitch-cross-auv">Étape 5 — Stitch cross-AUV</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#etape-6-export-glb-a-la-demande">Étape 6 — Export GLB (à la demande)</a></li>
<li class="toctree-l2"><a class="reference internal" href="pipeline.html#statuts-de-jobs">Statuts de jobs</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a><ul>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#reseau-lan-192-168-0-0-24">Réseau LAN — 192.168.0.0/24</a></li>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#noeud-core-82">Nœud core (.82)</a></li>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#noeuds-gpu-workers-84-et-87">Nœuds GPU workers (.84 et .87)</a></li>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#noeud-z620-168">Nœud z620 (.168)</a></li>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#service-systemd-dispatcher">Service systemd dispatcher</a></li>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#conteneur-docker-dashboard">Conteneur Docker dashboard</a></li>
<li class="toctree-l2"><a class="reference internal" href="infrastructure.html#ports-reseau-recapitulatifs">Ports réseau récapitulatifs</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a><ul>
<li class="toctree-l2"><a class="reference internal" href="data.html#ou-sont-stockees-les-donnees">Où sont stockées les données</a></li>
<li class="toctree-l2"><a class="reference internal" href="data.html#budget-disque-observe">Budget disque observé</a></li>
<li class="toctree-l2"><a class="reference internal" href="data.html#politique-de-nettoyage">Politique de nettoyage</a></li>
<li class="toctree-l2"><a class="reference internal" href="data.html#export-glb">Export GLB</a></li>
<li class="toctree-l2"><a class="reference internal" href="data.html#reprise-sur-crash-marqueurs-done">Reprise sur crash — marqueurs .done</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a><ul>
<li class="toctree-l2"><a class="reference internal" href="usage.html#ingerer-une-nouvelle-acquisition">Ingérer une nouvelle acquisition</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#surveiller-les-jobs">Surveiller les jobs</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#visualiser-un-nuage-de-points-ply">Visualiser un nuage de points PLY</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#telecharger-un-glb">Télécharger un GLB</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#relancer-un-job-echoue">Relancer un job échoué</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#redemarrer-le-pipeline-complet">Redémarrer le pipeline complet</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#verifications-rapides-post-mission">Vérifications rapides post-mission</a></li>
</ul>
</li>
</ul>
</div>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="pipeline.html" class="btn btn-neutral float-right" title="Pipeline cosma-qc" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

285
docs/_build/html/infrastructure.html vendored Normal file
View File

@@ -0,0 +1,285 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infrastructure &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Données — Stockage et budget disque" href="data.html" />
<link rel="prev" title="Pipeline cosma-qc" href="pipeline.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Infrastructure</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#reseau-lan-192-168-0-0-24">Réseau LAN — 192.168.0.0/24</a></li>
<li class="toctree-l2"><a class="reference internal" href="#noeud-core-82">Nœud core (.82)</a></li>
<li class="toctree-l2"><a class="reference internal" href="#noeuds-gpu-workers-84-et-87">Nœuds GPU workers (.84 et .87)</a></li>
<li class="toctree-l2"><a class="reference internal" href="#noeud-z620-168">Nœud z620 (.168)</a></li>
<li class="toctree-l2"><a class="reference internal" href="#service-systemd-dispatcher">Service systemd dispatcher</a></li>
<li class="toctree-l2"><a class="reference internal" href="#conteneur-docker-dashboard">Conteneur Docker dashboard</a></li>
<li class="toctree-l2"><a class="reference internal" href="#ports-reseau-recapitulatifs">Ports réseau récapitulatifs</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a></li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Infrastructure</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/infrastructure.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="infrastructure">
<h1>Infrastructure<a class="headerlink" href="#infrastructure" title="Link to this heading"></a></h1>
<section id="reseau-lan-192-168-0-0-24">
<h2>Réseau LAN — 192.168.0.0/24<a class="headerlink" href="#reseau-lan-192-168-0-0-24" title="Link to this heading"></a></h2>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>┌─────────────────────────────────────────────────────────┐
│ LAN 192.168.0.0/24 │
│ │
│ .82 CORE Dispatcher (systemd) + FastAPI :3849 │
│ Gitea + Grafana + InfluxDB + Caddy │
│ │
│ .84 ml-stack GPU worker RTX 3090 24GB │
│ .87 gpu GPU worker RTX 3060 12GB │
│ │
│ .168 z620 Proxmox host HP Z620 │
│ SSD → /mnt/portablessd (MP4 bruts) │
└─────────────────────────────────────────────────────────┘
</pre></div>
</div>
</section>
<section id="noeud-core-82">
<h2>Nœud core (.82)<a class="headerlink" href="#noeud-core-82" title="Link to this heading"></a></h2>
<p><strong>Rôle :</strong> orchestrateur central du pipeline.</p>
<p>Services actifs :</p>
<ul class="simple">
<li><p><strong>Dispatcher</strong> — service systemd cosma-qc-dispatcher.
Boucle principale qui dispatch les jobs aux workers GPU.</p></li>
<li><p><strong>Dashboard FastAPI</strong> — conteneur Docker exposé sur le port <strong>3849</strong>.
Interface web de monitoring des jobs.</p></li>
<li><p><strong>Gitea</strong> — dépôt source floppyrj45/cosma-qc.</p></li>
<li><p><strong>Grafana / InfluxDB</strong> — monitoring infrastructure.</p></li>
</ul>
<p>Commandes utiles :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Statut dispatcher</span>
sudo<span class="w"> </span>systemctl<span class="w"> </span>status<span class="w"> </span>cosma-qc-dispatcher
<span class="c1"># Logs dispatcher temps réel</span>
sudo<span class="w"> </span>journalctl<span class="w"> </span>-u<span class="w"> </span>cosma-qc-dispatcher<span class="w"> </span>-f
<span class="c1"># Dashboard</span>
http://192.168.0.82:3849
</pre></div>
</div>
</section>
<section id="noeuds-gpu-workers-84-et-87">
<h2>Nœuds GPU workers (.84 et .87)<a class="headerlink" href="#noeuds-gpu-workers-84-et-87" title="Link to this heading"></a></h2>
<table class="docutils align-default">
<colgroup>
<col style="width: 15.0%" />
<col style="width: 25.0%" />
<col style="width: 20.0%" />
<col style="width: 40.0%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>IP</p></th>
<th class="head"><p>Nom</p></th>
<th class="head"><p>GPU</p></th>
<th class="head"><p>VRAM</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>.84</p></td>
<td><p>ml-stack</p></td>
<td><p>RTX 3090</p></td>
<td><p>24 GB</p></td>
</tr>
<tr class="row-odd"><td><p>.87</p></td>
<td><p>gpu</p></td>
<td><p>RTX 3060</p></td>
<td><p>12 GB</p></td>
</tr>
</tbody>
</table>
<p><strong>Rôle :</strong> exécution de ffmpeg (extraction frames) et lingbot-map (reconstruction 3D).</p>
<p>Répertoire de travail sur chaque worker :</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>/cosma-qc-frames/
├── job_1/
│ ├── frame_000001.jpg … frame_NNNNNN.jpg
│ ├── .video_0.done
│ ├── reconstruction.ply
│ └── reconstruction.glb (généré à la demande)
├── job_2/
│ └── …
└── stitch_1.ply
</pre></div>
</div>
</section>
<section id="noeud-z620-168">
<h2>Nœud z620 (.168)<a class="headerlink" href="#noeud-z620-168" title="Link to this heading"></a></h2>
<p><strong>Rôle :</strong> stockage des MP4 bruts GoPro.</p>
<ul class="simple">
<li><p>Proxmox host HP Z620.</p></li>
<li><p>SSD monté sur /mnt/portablessd.</p></li>
<li><p>Les MP4 <strong>ne quittent jamais</strong> z620 — ffmpeg sy exécute via SSH.</p></li>
</ul>
<p>Accès SSH depuis core :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>ssh<span class="w"> </span>floppyrj45@192.168.0.168
</pre></div>
</div>
</section>
<section id="service-systemd-dispatcher">
<h2>Service systemd dispatcher<a class="headerlink" href="#service-systemd-dispatcher" title="Link to this heading"></a></h2>
<p>Fichier de service : /etc/systemd/system/cosma-qc-dispatcher.service</p>
<div class="highlight-ini notranslate"><div class="highlight"><pre><span></span><span class="k">[Unit]</span>
<span class="na">Description</span><span class="o">=</span><span class="s">COSMA QC Dispatcher</span>
<span class="na">After</span><span class="o">=</span><span class="s">network.target</span>
<span class="k">[Service]</span>
<span class="na">User</span><span class="o">=</span><span class="s">floppyrj45</span>
<span class="na">WorkingDirectory</span><span class="o">=</span><span class="s">/home/floppyrj45/docker/cosma-qc</span>
<span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/python3 app/dispatcher.py</span>
<span class="na">Restart</span><span class="o">=</span><span class="s">on-failure</span>
<span class="na">RestartSec</span><span class="o">=</span><span class="s">10</span>
<span class="k">[Install]</span>
<span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span>
</pre></div>
</div>
<p>Commandes de gestion :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>sudo<span class="w"> </span>systemctl<span class="w"> </span>start<span class="w"> </span>cosma-qc-dispatcher
sudo<span class="w"> </span>systemctl<span class="w"> </span>stop<span class="w"> </span>cosma-qc-dispatcher
sudo<span class="w"> </span>systemctl<span class="w"> </span>restart<span class="w"> </span>cosma-qc-dispatcher
sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>cosma-qc-dispatcher<span class="w"> </span><span class="c1"># démarrage auto</span>
</pre></div>
</div>
</section>
<section id="conteneur-docker-dashboard">
<h2>Conteneur Docker dashboard<a class="headerlink" href="#conteneur-docker-dashboard" title="Link to this heading"></a></h2>
<p>Le dashboard FastAPI tourne dans un conteneur Docker.</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="nb">cd</span><span class="w"> </span>/home/floppyrj45/docker/cosma-qc
docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span><span class="c1"># démarrer</span>
docker<span class="w"> </span>compose<span class="w"> </span>down<span class="w"> </span><span class="c1"># arrêter</span>
docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span><span class="c1"># logs</span>
</pre></div>
</div>
<p>Accès : <a class="reference external" href="http://192.168.0.82:3849">http://192.168.0.82:3849</a></p>
</section>
<section id="ports-reseau-recapitulatifs">
<h2>Ports réseau récapitulatifs<a class="headerlink" href="#ports-reseau-recapitulatifs" title="Link to this heading"></a></h2>
<table class="docutils align-default">
<colgroup>
<col style="width: 15.0%" />
<col style="width: 15.0%" />
<col style="width: 70.0%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Host</p></th>
<th class="head"><p>Port</p></th>
<th class="head"><p>Service</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>.82</p></td>
<td><p>3849</p></td>
<td><p>Dashboard FastAPI cosma-qc</p></td>
</tr>
<tr class="row-odd"><td><p>.84 / .87</p></td>
<td><p>8100+N</p></td>
<td><p>Viser viewer (reconstruction job N)</p></td>
</tr>
<tr class="row-even"><td><p>.84 / .87</p></td>
<td><p>8300</p></td>
<td><p>HTTP server GLB export</p></td>
</tr>
</tbody>
</table>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="pipeline.html" class="btn btn-neutral float-left" title="Pipeline cosma-qc" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="data.html" class="btn btn-neutral float-right" title="Données — Stockage et budget disque" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

BIN
docs/_build/html/objects.inv vendored Normal file

Binary file not shown.

276
docs/_build/html/pipeline.html vendored Normal file
View File

@@ -0,0 +1,276 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pipeline cosma-qc &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Infrastructure" href="infrastructure.html" />
<link rel="prev" title="cosma-qc — Documentation" href="index.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul class="current">
<li class="toctree-l1 current"><a class="current reference internal" href="#">Pipeline cosma-qc</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#vue-d-ensemble">Vue densemble</a></li>
<li class="toctree-l2"><a class="reference internal" href="#flux-de-donnees-global">Flux de données global</a></li>
<li class="toctree-l2"><a class="reference internal" href="#etape-1-ingest">Étape 1 — Ingest</a></li>
<li class="toctree-l2"><a class="reference internal" href="#etape-2-extraction-des-frames">Étape 2 — Extraction des frames</a></li>
<li class="toctree-l2"><a class="reference internal" href="#etape-3-reconstruction-3d">Étape 3 — Reconstruction 3D</a></li>
<li class="toctree-l2"><a class="reference internal" href="#etape-4-stitch-par-auv">Étape 4 — Stitch par AUV</a></li>
<li class="toctree-l2"><a class="reference internal" href="#etape-5-stitch-cross-auv">Étape 5 — Stitch cross-AUV</a></li>
<li class="toctree-l2"><a class="reference internal" href="#etape-6-export-glb-a-la-demande">Étape 6 — Export GLB (à la demande)</a></li>
<li class="toctree-l2"><a class="reference internal" href="#statuts-de-jobs">Statuts de jobs</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a></li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a></li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Pipeline cosma-qc</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/pipeline.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="pipeline-cosma-qc">
<h1>Pipeline cosma-qc<a class="headerlink" href="#pipeline-cosma-qc" title="Link to this heading"></a></h1>
<section id="vue-d-ensemble">
<h2>Vue densemble<a class="headerlink" href="#vue-d-ensemble" title="Link to this heading"></a></h2>
<p>Le pipeline cosma-qc traite les vidéos GoPro brutes acquises par les drones sous-marins
AUV COSMA pour produire des nuages de points 3D denses (PLY) et des exports web (GLB).</p>
</section>
<section id="flux-de-donnees-global">
<h2>Flux de données global<a class="headerlink" href="#flux-de-donnees-global" title="Link to this heading"></a></h2>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>z620 (/mnt/portablessd)
├── GX*.MP4 (brut)
▼ [1. Ingest]
SQLite DB (jobs)
▼ [2. Extraction ffmpeg]
GPU worker /cosma-qc-frames/job_{id}/frame_%06d.jpg
▼ [3. Reconstruction lingbot-map]
GPU worker /cosma-qc-frames/job_{id}/reconstruction.ply
▼ [4. Stitch per_auv]
GPU worker /cosma-qc-frames/stitch_{N}.ply (par AUV)
▼ [5. Stitch cross_auv]
GPU worker /cosma-qc-frames/stitch_global.ply
▼ [6. Export GLB (à la demande)]
GPU worker /cosma-qc-frames/job_{id}/reconstruction.glb
</pre></div>
</div>
</section>
<section id="etape-1-ingest">
<h2>Étape 1 — Ingest<a class="headerlink" href="#etape-1-ingest" title="Link to this heading"></a></h2>
<p><strong>Script :</strong> scripts/ingest.py</p>
<p><strong>Rôle :</strong> Scanner le SSD de z620, regrouper les MP4 GoPro par AUV/GoPro/segment
temporel et écrire les jobs dans la base SQLite.</p>
<p>Logique de regroupement :</p>
<ul class="simple">
<li><p>Les fichiers sont groupés par numéro de caméra GoPro (GXXX).</p></li>
<li><p>Un nouveau segment est créé si lécart entre deux fichiers dépasse <strong>5 minutes</strong>.</p></li>
<li><p>Chaque segment → un job en base avec statut pending.</p></li>
</ul>
<p>Structure dun job en base :</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">jobs</span><span class="w"> </span><span class="p">(</span>
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="nb">INTEGER</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span>
<span class="w"> </span><span class="n">auv</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span>
<span class="w"> </span><span class="n">gopro</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span>
<span class="w"> </span><span class="n">segment</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span>
<span class="w"> </span><span class="n">files</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="c1">-- JSON list of z620:/path/GX*.MP4</span>
<span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="c1">-- pending / running / done / failed</span>
<span class="w"> </span><span class="n">worker</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span>
<span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="k">TIMESTAMP</span><span class="p">,</span>
<span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="k">TIMESTAMP</span>
<span class="p">);</span>
</pre></div>
</div>
<p>Les chemins vidéo sont stockés sous la forme z620:/chemin/absolu/GX*.MP4
pour indiquer que la source est sur z620, pas sur le worker GPU.</p>
</section>
<section id="etape-2-extraction-des-frames">
<h2>Étape 2 — Extraction des frames<a class="headerlink" href="#etape-2-extraction-des-frames" title="Link to this heading"></a></h2>
<p><strong>Fonction :</strong> do_extract dans le dispatcher (core .82)</p>
<p><strong>Outil :</strong> ffmpeg</p>
<p>Paramètres dextraction :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>ffmpeg<span class="w"> </span>-i<span class="w"> </span>input.mp4<span class="w"> </span>-vf<span class="w"> </span><span class="nv">fps</span><span class="o">=</span><span class="m">2</span>,scale<span class="o">=</span><span class="m">1920</span>:1080<span class="w"> </span>-q:v<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">{</span>frames_dir<span class="o">}</span>/job_<span class="o">{</span>id<span class="o">}</span>/frame_%06d.jpg
</pre></div>
</div>
<p><strong>Particularité z620 :</strong> lorsque la source est z620:/…, ffmpeg sexécute
<strong>directement sur z620</strong> via SSH — seuls les JPEG voyagent sur le réseau.
Les MP4 bruts ne quittent jamais z620.</p>
<p>Filtre hors-eau (heuristique de luminosité) :</p>
<ul class="simple">
<li><p>Les frames dont la luminosité moyenne est au-dessus dun seuil sont supprimées
(ciel, surface, hors-eau).</p></li>
<li><p>Ce filtre réduit le bruit dans la reconstruction.</p></li>
</ul>
<p>Reprise sur crash :</p>
<ul class="simple">
<li><p>Un fichier marqueur .video_N.done est créé après chaque MP4 traité.</p></li>
<li><p>En cas de redémarrage, les vidéos déjà traitées sont ignorées.</p></li>
</ul>
</section>
<section id="etape-3-reconstruction-3d">
<h2>Étape 3 — Reconstruction 3D<a class="headerlink" href="#etape-3-reconstruction-3d" title="Link to this heading"></a></h2>
<p><strong>Fonction :</strong> do_reconstruct</p>
<p><strong>Outil :</strong> lingbot-map (demo.py) sur le worker GPU</p>
<p><strong>Entrée :</strong> répertoire de frames {worker_frames_dir}/job_{id}/</p>
<p><strong>Sortie :</strong> {worker_frames_dir}/job_{id}/reconstruction.ply
(nuage de points dense, jusquà <strong>150 millions de points</strong>)</p>
<p>Un viewer Viser est automatiquement démarré sur le port 8100 + job_id
pendant la reconstruction pour visualisation en temps réel.</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Visualiser pendant reconstruction</span>
<span class="c1"># naviguer vers http://{worker_ip}:{8100+job_id}</span>
</pre></div>
</div>
</section>
<section id="etape-4-stitch-par-auv">
<h2>Étape 4 — Stitch par AUV<a class="headerlink" href="#etape-4-stitch-par-auv" title="Link to this heading"></a></h2>
<p><strong>Fonction :</strong> do_stitch_per_auv</p>
<p><strong>Déclenchement :</strong> automatique quand <strong>tous les jobs dun AUV</strong> sont en statut done.</p>
<p><strong>Outil :</strong> cosma-stitch.py</p>
<p><strong>Opération :</strong> alignement et fusion des PLY de tous les segments dun même AUV.</p>
<p><strong>Sortie :</strong> {worker_frames_dir}/stitch_{N}.ply</p>
<p>Ce stitch sexécute sur le worker qui détient les PLY (pas de copie inter-workers).</p>
</section>
<section id="etape-5-stitch-cross-auv">
<h2>Étape 5 — Stitch cross-AUV<a class="headerlink" href="#etape-5-stitch-cross-auv" title="Link to this heading"></a></h2>
<p><strong>Fonction :</strong> do_stitch_cross_auv</p>
<p><strong>Déclenchement :</strong> automatique quand <strong>tous les stitches per_auv</strong> sont validés.</p>
<p><strong>Opération :</strong> fusion de tous les PLY par AUV en un nuage de points global final.</p>
<p><strong>Sortie :</strong> {worker_frames_dir}/stitch_global.ply</p>
</section>
<section id="etape-6-export-glb-a-la-demande">
<h2>Étape 6 — Export GLB (à la demande)<a class="headerlink" href="#etape-6-export-glb-a-la-demande" title="Link to this heading"></a></h2>
<p><strong>Outil :</strong> trimesh (conversion PLY → GLB)</p>
<p><strong>Sous-échantillonnage :</strong> 5 millions de points (adapté web/navigateur)</p>
<p><strong>Sortie :</strong> {worker_frames_dir}/job_{id}/reconstruction.glb</p>
<p>Un serveur HTTP minimal est lancé sur le worker (port <strong>8300</strong>) pour le téléchargement :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Sur le worker</span>
python3<span class="w"> </span>-m<span class="w"> </span>http.server<span class="w"> </span><span class="m">8300</span><span class="w"> </span>--directory<span class="w"> </span><span class="o">{</span>worker_frames_dir<span class="o">}</span>
<span class="c1"># Télécharger depuis un PC</span>
wget<span class="w"> </span>http://<span class="o">{</span>worker_ip<span class="o">}</span>:8300/job_<span class="o">{</span>id<span class="o">}</span>/reconstruction.glb
</pre></div>
</div>
</section>
<section id="statuts-de-jobs">
<h2>Statuts de jobs<a class="headerlink" href="#statuts-de-jobs" title="Link to this heading"></a></h2>
<table class="docutils align-default">
<colgroup>
<col style="width: 20.0%" />
<col style="width: 80.0%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Statut</p></th>
<th class="head"><p>Description</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>pending</p></td>
<td><p>Job créé, en attente de dispatch</p></td>
</tr>
<tr class="row-odd"><td><p>running</p></td>
<td><p>En cours de traitement (extraction ou reconstruction)</p></td>
</tr>
<tr class="row-even"><td><p>done</p></td>
<td><p>Reconstruction terminée, PLY disponible</p></td>
</tr>
<tr class="row-odd"><td><p>failed</p></td>
<td><p>Erreur — voir logs du dispatcher</p></td>
</tr>
</tbody>
</table>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="index.html" class="btn btn-neutral float-left" title="cosma-qc — Documentation" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="infrastructure.html" class="btn btn-neutral float-right" title="Infrastructure" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>

123
docs/_build/html/search.html vendored Normal file
View File

@@ -0,0 +1,123 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<script src="_static/searchtools.js"></script>
<script src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="#" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="#" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a></li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a></li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a></li>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Utilisation</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Search</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<noscript>
<div id="fallback" class="admonition warning">
<p class="last">
Please activate JavaScript to enable the search functionality.
</p>
</div>
</noscript>
<div id="search-results">
</div>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
<script>
jQuery(function() { Search.loadIndex("searchindex.js"); });
</script>
<script id="searchindexloader"></script>
</body>
</html>

1
docs/_build/html/searchindex.js vendored Normal file

File diff suppressed because one or more lines are too long

285
docs/_build/html/usage.html vendored Normal file
View File

@@ -0,0 +1,285 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Utilisation &mdash; cosma-qc 1.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=9edc463e" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=f2a433a1"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="prev" title="Données — Stockage et budget disque" href="data.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
cosma-qc
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Contenu</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="pipeline.html">Pipeline cosma-qc</a></li>
<li class="toctree-l1"><a class="reference internal" href="infrastructure.html">Infrastructure</a></li>
<li class="toctree-l1"><a class="reference internal" href="data.html">Données — Stockage et budget disque</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Utilisation</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#ingerer-une-nouvelle-acquisition">Ingérer une nouvelle acquisition</a></li>
<li class="toctree-l2"><a class="reference internal" href="#surveiller-les-jobs">Surveiller les jobs</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#dashboard-web">Dashboard web</a></li>
<li class="toctree-l3"><a class="reference internal" href="#logs-dispatcher">Logs dispatcher</a></li>
<li class="toctree-l3"><a class="reference internal" href="#base-de-donnees">Base de données</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#visualiser-un-nuage-de-points-ply">Visualiser un nuage de points PLY</a></li>
<li class="toctree-l2"><a class="reference internal" href="#telecharger-un-glb">Télécharger un GLB</a></li>
<li class="toctree-l2"><a class="reference internal" href="#relancer-un-job-echoue">Relancer un job échoué</a></li>
<li class="toctree-l2"><a class="reference internal" href="#redemarrer-le-pipeline-complet">Redémarrer le pipeline complet</a></li>
<li class="toctree-l2"><a class="reference internal" href="#verifications-rapides-post-mission">Vérifications rapides post-mission</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">cosma-qc</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Utilisation</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/usage.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="utilisation">
<h1>Utilisation<a class="headerlink" href="#utilisation" title="Link to this heading"></a></h1>
<section id="ingerer-une-nouvelle-acquisition">
<h2>Ingérer une nouvelle acquisition<a class="headerlink" href="#ingerer-une-nouvelle-acquisition" title="Link to this heading"></a></h2>
<ol class="arabic simple">
<li><p>Connecter le SSD GoPro à z620 (ou sassurer que les MP4 sont dans <code class="docutils literal notranslate"><span class="pre">/mnt/portablessd</span></code>).</p></li>
<li><p>Lancer lingest depuis core :</p></li>
</ol>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>ssh<span class="w"> </span>floppyrj45@192.168.0.82
<span class="nb">cd</span><span class="w"> </span>/home/floppyrj45/docker/cosma-qc
python3<span class="w"> </span>scripts/ingest.py<span class="w"> </span>--path<span class="w"> </span>/mnt/portablessd/AUV009/
</pre></div>
</div>
<ol class="arabic simple" start="3">
<li><p>Vérifier les jobs créés :</p></li>
</ol>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>python3<span class="w"> </span>-c<span class="w"> </span><span class="s2">&quot;</span>
<span class="s2">import sqlite3</span>
<span class="s2">conn = sqlite3.connect(&#39;cosma-qc.db&#39;)</span>
<span class="s2">for row in conn.execute(&#39;SELECT id, auv, gopro, segment, status FROM jobs ORDER BY id&#39;):</span>
<span class="s2"> print(row)</span>
<span class="s2">conn.close()</span>
<span class="s2">&quot;</span>
</pre></div>
</div>
<ol class="arabic simple" start="4">
<li><p>Le dispatcher prend automatiquement en charge les jobs en statut <code class="docutils literal notranslate"><span class="pre">pending</span></code>.</p></li>
</ol>
</section>
<section id="surveiller-les-jobs">
<h2>Surveiller les jobs<a class="headerlink" href="#surveiller-les-jobs" title="Link to this heading"></a></h2>
<section id="dashboard-web">
<h3>Dashboard web<a class="headerlink" href="#dashboard-web" title="Link to this heading"></a></h3>
<p>Accéder au dashboard : <a class="reference external" href="http://192.168.0.82:3849">http://192.168.0.82:3849</a></p>
<p>Il affiche en temps réel :</p>
<ul class="simple">
<li><p>Statut de chaque job (pending / running / done / failed)</p></li>
<li><p>Worker assigné</p></li>
<li><p>Progression des frames</p></li>
<li><p>Liens vers les PLY et GLB</p></li>
</ul>
</section>
<section id="logs-dispatcher">
<h3>Logs dispatcher<a class="headerlink" href="#logs-dispatcher" title="Link to this heading"></a></h3>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Logs temps réel</span>
sudo<span class="w"> </span>journalctl<span class="w"> </span>-u<span class="w"> </span>cosma-qc-dispatcher<span class="w"> </span>-f
<span class="c1"># Logs des 100 dernières lignes</span>
sudo<span class="w"> </span>journalctl<span class="w"> </span>-u<span class="w"> </span>cosma-qc-dispatcher<span class="w"> </span>-n<span class="w"> </span><span class="m">100</span>
<span class="c1"># Filtrer erreurs</span>
sudo<span class="w"> </span>journalctl<span class="w"> </span>-u<span class="w"> </span>cosma-qc-dispatcher<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>-i<span class="w"> </span>error
</pre></div>
</div>
</section>
<section id="base-de-donnees">
<h3>Base de données<a class="headerlink" href="#base-de-donnees" title="Link to this heading"></a></h3>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># État global des jobs</span>
sqlite3<span class="w"> </span>/home/floppyrj45/docker/cosma-qc/cosma-qc.db<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;SELECT id, auv, status, worker, updated_at FROM jobs ORDER BY id;&quot;</span>
<span class="c1"># Jobs en cours</span>
sqlite3<span class="w"> </span>cosma-qc.db<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;SELECT id, auv, worker FROM jobs WHERE status=&#39;running&#39;;&quot;</span>
<span class="c1"># Jobs échoués</span>
sqlite3<span class="w"> </span>cosma-qc.db<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;SELECT id, auv, status FROM jobs WHERE status=&#39;failed&#39;;&quot;</span>
</pre></div>
</div>
</section>
</section>
<section id="visualiser-un-nuage-de-points-ply">
<h2>Visualiser un nuage de points PLY<a class="headerlink" href="#visualiser-un-nuage-de-points-ply" title="Link to this heading"></a></h2>
<p>Sur le worker avec viewer Viser (lancé automatiquement pendant reconstruction) :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Naviguer vers (remplacer ID par le numéro de job)</span>
http://192.168.0.84:8100<span class="w"> </span><span class="c1"># pour job 0</span>
http://192.168.0.84:8101<span class="w"> </span><span class="c1"># pour job 1</span>
<span class="c1"># etc.</span>
</pre></div>
</div>
<p>Avec CloudCompare depuis un PC (si PLY téléchargé) :</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Copier le PLY vers PC</span>
scp<span class="w"> </span>floppyrj45@192.168.0.84:/cosma-qc-frames/job_ID/reconstruction.ply<span class="w"> </span>./
<span class="c1"># Ouvrir dans CloudCompare</span>
</pre></div>
</div>
</section>
<section id="telecharger-un-glb">
<h2>Télécharger un GLB<a class="headerlink" href="#telecharger-un-glb" title="Link to this heading"></a></h2>
<ol class="arabic simple">
<li><p>Générer le GLB via lAPI :</p></li>
</ol>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>http://192.168.0.82:3849/jobs/ID/export_glb
</pre></div>
</div>
<ol class="arabic simple" start="2">
<li><p>Attendre la fin de la génération (peut prendre quelques minutes selon la taille du PLY).</p></li>
<li><p>Lancer le serveur HTTP sur le worker concerné :</p></li>
</ol>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>ssh<span class="w"> </span>floppyrj45@192.168.0.84<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;nohup python3 -m http.server 8300 --directory /cosma-qc-frames &gt; /tmp/http8300.log 2&gt;&amp;1 &amp;&quot;</span>
</pre></div>
</div>
<ol class="arabic simple" start="4">
<li><p>Télécharger :</p></li>
</ol>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>wget<span class="w"> </span>http://192.168.0.84:8300/job_ID/reconstruction.glb
</pre></div>
</div>
<ol class="arabic simple" start="5">
<li><p>Arrêter le serveur HTTP après téléchargement :</p></li>
</ol>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>ssh<span class="w"> </span>floppyrj45@192.168.0.84<span class="w"> </span><span class="s2">&quot;pkill -f &#39;http.server 8300&#39;&quot;</span>
</pre></div>
</div>
</section>
<section id="relancer-un-job-echoue">
<h2>Relancer un job échoué<a class="headerlink" href="#relancer-un-job-echoue" title="Link to this heading"></a></h2>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Remettre un job en pending</span>
sqlite3<span class="w"> </span>/home/floppyrj45/docker/cosma-qc/cosma-qc.db<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;UPDATE jobs SET status=&#39;pending&#39;, worker=NULL WHERE id=ID;&quot;</span>
<span class="c1"># Supprimer les marqueurs .done pour forcer ré-extraction complète</span>
ssh<span class="w"> </span>floppyrj45@192.168.0.84<span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="s2">&quot;rm -f /cosma-qc-frames/job_ID/.video_*.done&quot;</span>
<span class="c1"># Redémarrer le dispatcher si nécessaire</span>
sudo<span class="w"> </span>systemctl<span class="w"> </span>restart<span class="w"> </span>cosma-qc-dispatcher
</pre></div>
</div>
<p>Le dispatcher reprend le job au prochain cycle.</p>
</section>
<section id="redemarrer-le-pipeline-complet">
<h2>Redémarrer le pipeline complet<a class="headerlink" href="#redemarrer-le-pipeline-complet" title="Link to this heading"></a></h2>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># Arrêter</span>
sudo<span class="w"> </span>systemctl<span class="w"> </span>stop<span class="w"> </span>cosma-qc-dispatcher
docker<span class="w"> </span>compose<span class="w"> </span>-f<span class="w"> </span>/home/floppyrj45/docker/cosma-qc/docker-compose.yml<span class="w"> </span>down
<span class="c1"># Démarrer</span>
docker<span class="w"> </span>compose<span class="w"> </span>-f<span class="w"> </span>/home/floppyrj45/docker/cosma-qc/docker-compose.yml<span class="w"> </span>up<span class="w"> </span>-d
sudo<span class="w"> </span>systemctl<span class="w"> </span>start<span class="w"> </span>cosma-qc-dispatcher
</pre></div>
</div>
</section>
<section id="verifications-rapides-post-mission">
<h2>Vérifications rapides post-mission<a class="headerlink" href="#verifications-rapides-post-mission" title="Link to this heading"></a></h2>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># 1. Tous les jobs done ?</span>
sqlite3<span class="w"> </span>cosma-qc.db<span class="w"> </span><span class="s2">&quot;SELECT COUNT(*) FROM jobs WHERE status != &#39;done&#39;;&quot;</span>
<span class="c1"># Doit retourner 0</span>
<span class="c1"># 2. Tous les PLY présents ?</span>
<span class="k">for</span><span class="w"> </span>id<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="k">$(</span>sqlite3<span class="w"> </span>cosma-qc.db<span class="w"> </span><span class="s2">&quot;SELECT id FROM jobs WHERE status=&#39;done&#39;&quot;</span><span class="k">)</span><span class="p">;</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="nv">worker</span><span class="o">=</span><span class="k">$(</span>sqlite3<span class="w"> </span>cosma-qc.db<span class="w"> </span><span class="s2">&quot;SELECT worker FROM jobs WHERE id=</span><span class="nv">$id</span><span class="s2">&quot;</span><span class="k">)</span>
<span class="w"> </span>ssh<span class="w"> </span>floppyrj45@<span class="nv">$worker</span><span class="w"> </span><span class="s2">&quot;ls -lh /cosma-qc-frames/job_</span><span class="si">${</span><span class="nv">id</span><span class="si">}</span><span class="s2">/reconstruction.ply&quot;</span>
<span class="k">done</span>
<span class="c1"># 3. Espace disque OK ?</span>
ssh<span class="w"> </span>floppyrj45@192.168.0.84<span class="w"> </span><span class="s2">&quot;df -h /cosma-qc-frames&quot;</span>
ssh<span class="w"> </span>floppyrj45@192.168.0.87<span class="w"> </span><span class="s2">&quot;df -h /cosma-qc-frames&quot;</span>
</pre></div>
</div>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="data.html" class="btn btn-neutral float-left" title="Données — Stockage et budget disque" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright .</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>