secure commit

This commit is contained in:
fy59 2026-08-28 06:31:44 +02:00
parent ba21f5eb83
commit 40d9d27ba6
20 changed files with 1290 additions and 103 deletions

View file

@ -34,8 +34,12 @@ persistante, enrichissable et versionnable.
- **Image View** : vues triées et filtrées pour la TUI
- **Task** : moteur de tâches avec pause/reprise, annulation et séquences
- **Task Checkpoint v1** : snapshot durable, fichier atomique et reprise sûre
- **Project Database v19** : fondations v18 préservées, avec Capture / Asset
Provenance additive
- **Project Database v20** : fondations v19 préservées, avec persistance
additive des tâches de campagne d'acquisition
- **Exécution durable de campagne d'acquisition** : tâche générique à requête
typée immuable, confirmations `CALLER_EXPLICIT`, curseur et correspondance
tâche/groupe→Capture persistants ; un groupe S3-E par séquence, reprise par
la registry, la Queue et le Resource Governor existants
- **Sparse SfM Gates C/D/E** : géométrie calibrée, noyau incrémental et Bundle
Adjustment final par composante, tous PASS / FROZEN
- **Sparse SfM Gate F** : orchestration durable, runtime gouverné et publication
@ -62,15 +66,14 @@ persistante, enrichissable et versionnable.
### Prochaine tranche
- **Exécution durable de campagne d'acquisition** : confirmations et progression
persistantes, matérialisation S3-E incrémentale, reprise par `capture_id` retenu
et admission par les Task/Scheduler/Governor existants. Voir la
- **Intégration multi-campagne réelle A6000 + S21 Engine Bay** : exécuter les
campagnes persistantes à travers le pipeline scientifique existant. Voir la
[roadmap canonique](docs/roadmap/roadmap.md).
### Plus tard / différé
- publication durable dense/mesh et scratch SSD externe optionnel gouverné ;
- workflow TUI de confirmation/progression et sources mixtes multi-ScanSet ;
- workflow TUI de confirmation/progression ;
- vidéo/keyframes et **Capture Guidance / Live Coverage** : analyse et viewer de
couverture, suggestions de prises de vue puis assistance live, après
reconstruction mature ;

View file

@ -1,6 +1,6 @@
# Base de données projet Lardon3D
## Capture / Asset Provenance v1 — Project DB v19
## Capture / Asset Provenance v1 — Project DB v19 (historique FROZEN)
**PASS / FROZEN.** La migration transactionnelle v18→v19
ajoute seulement une fondation de catalogue : `captures`, `capture_images`,
@ -109,10 +109,57 @@ confirmation explicite `CALLER_EXPLICIT` avant tout regroupement. Le fixture
d'intégration couvre l'adaptateur, sa matérialisation et son retry ; aucune
mutation de campagne complète n'a été effectuée.
Restent hors S3 : identité persistante de campagne ou de Task, exécution par
scheduler/Governor, traitement de campagne complet, TUI, scrub d'assets et
réconciliation des orphelins, ainsi que vidéo et reconstruction aval lorsqu'ils
sont applicables.
Restent hors S3 : TUI, scrub d'assets et réconciliation des orphelins, ainsi
que vidéo et reconstruction aval lorsqu'ils sont applicables. L'identité
opérationnelle persistante de campagne, son exécution par Task/Scheduler/Governor
et sa reprise sont définies par Project DB v20 ci-dessous ; ils ne modifient pas
le contrat S3.
## Exécution durable de campagne d'acquisition — Project DB v20
**PASS / FROZEN.** La migration transactionnelle additive
v19→v20 ajoute `acquisition_campaign_tasks` et
`acquisition_campaign_captures`, sans modifier les tables ni les identités
Capture/Asset de v19. Une base v19 est donc ouverte par la chaîne séquentielle
existante ; un échec de migration laisse la v19 complète et utilisable.
`acquisition_campaign_tasks` est lié une-à-une à la tâche générique par
`task_id`, qui est l'identité opérationnelle de la campagne. La création et les
checkpoints enregistrent dans une même transaction le snapshot Task générique,
sa référence de checkpoint et le record typé de campagne. Ce record conserve le
`scanset_id`, le curseur `next_group_id`, le nombre de groupes et une requête
immuable : un upsert ne peut faire progresser le curseur que si ScanSet, nombre
de groupes et octets de requête sont identiques.
La requête v1 est un codec borné, déterministe, à magic/version explicites et
champs entiers de largeur fixe little-endian. Elle sérialise les sources, leurs
métadonnées déjà validées, les confirmations et les options d'ingestion ; elle
est refusée si ses bornes, longueurs, énumérations, indices, plan ou octets de
fin ne sont pas valides. Les confirmations sont ainsi persistées explicitement
et ne confèrent que la basis `CALLER_EXPLICIT`, jamais une inférence `STRONG`.
Les groupes de plan gardent leurs IDs un-based. Pour chaque groupe achevé,
`acquisition_campaign_captures` conserve la relation unique
`(task_id, group_id) → capture_id` et interdit qu'un même Capture corresponde à
deux groupes de la même tâche. Après le retour de S3-E, une transaction unique
retient cette relation et avance `next_group_id`, avant la progression générique
et le checkpoint. La reprise peut alors transmettre le `capture_id` retenu à
S3-E, sans réhoming ni inférence depuis un chemin, SHA, basename, timestamp,
métadonnée ou `image_id`.
Chaque séquence matérialise un seul groupe. Pause et annulation restent
coopératives aux frontières de groupe ; après un groupe non terminal,
`sequence_break` rend la réservation et force la réadmission par la Queue et le
Resource Governor existants. À l'ouverture, la registry existante reconstruit
la tâche depuis le Task ID et la requête durable, puis le mécanisme de reprise
existant la soumet à la Queue. Cette tranche n'introduit aucun runtime, queue,
governor, scheduler ou stockage parallèle et reste générique pour les sources
mono-source comme multi-source.
La fenêtre résiduelle acceptée est strictement l'arrêt après le retour de S3-E
et avant la transaction de rétention du Capture. Aucun `capture_id` n'est alors
inféré et aucune garantie exactly-once ne lui est attribuée ; les autres
frontières s'appuient sur la relation retenue et le curseur transactionnel.
### S3-D — Acquisition Pairing Evidence v1
@ -201,8 +248,10 @@ persistée exactement ; seule une composante de graphe non reconstruite est
omise. Les comptes et métriques globaux décrivent exclusivement la géométrie
effectivement persistée. Aucun placeholder ni table diagnostique n'est ajouté.
> Version courante : **v19**. La migration transactionnelle v18→v19 ajoute la
> fondation Capture/Asset Provenance décrite ci-dessus sans modifier les tables
> Version courante : **v20**. La migration transactionnelle additive v19→v20
> ajoute les records de campagne durable décrits ci-dessus sans modifier les
> tables ni identités Capture/Asset de v19. La migration transactionnelle
> v18→v19 ajoute la fondation Capture/Asset Provenance sans modifier les tables
> scientifiques historiques. La migration transactionnelle v17→v18 ajoute le
> discriminateur générique nullable et les deux tables H minimales décrites
> ci-dessus. La migration v16→v17 ajoute le
@ -946,10 +995,17 @@ ouvert.
## Statut
**PASS / FROZEN** — SQLite système, schéma v19 et migrations séquentielles
v1→v2→v3→v4→v5→v6→v7→v8→v9→v10→v11→v12→v13→v14→v15→v16→v17→v18→v19, identité projet, transactions
**PASS / FROZEN** — SQLite système, schéma v20 et migrations séquentielles
v1→v2→v3→v4→v5→v6→v7→v8→v9→v10→v11→v12→v13→v14→v15→v16→v17→v18→v19→v20, identité projet, transactions
tâche+checkpoint, pagination de reprise et artefacts génériques.
**PASS / FROZEN** — Project DB v20 : migration additive
v19→v20, record typé de campagne lié au Task ID, requête immuable bornée,
confirmations `CALLER_EXPLICIT`, curseur et relation unique
task/groupe→Capture. La reprise reste celle de la registry, de la Queue et du
Resource Governor existants ; aucune identité de Capture n'est inférée dans la
fenêtre résiduelle pré-rétention.
**IMPLEMENTED** — ouverture/fermeture avec le projet, identité INI/DB cohérente,
publication de checkpoints par le projet et inventaire de reprise validé.

View file

@ -122,6 +122,15 @@ complet. Le scope GVR est persistant et immuable ; la reprise rejoue depuis le
début avant publication et l'exact reuse Gate C absorbe un crash post-publication.
Pause et annulation sont observées avant/after l'unité Gate B non préemptible.
**PASS / FROZEN** — `acquisition_campaign.run` v1 est une
tâche durable reconstruite par la registry existante à partir de son Task ID et
de sa requête typée immuable Project DB v20. Elle matérialise un seul groupe
S3-E par séquence, vérifie pause/annulation aux frontières de groupe, retient
transactionnellement Capture et curseur avant progression/checkpoint, puis
appelle `sequence_break` avant le groupe suivant. Sa reprise passe par la Queue
et le Resource Governor existants ; aucune boucle d'exécution parallèle n'est
introduite.
Le chemin de production de l'import ne possède plus de thread ni de drapeau
d'annulation privés. Son wrapper TUI ne fait qu'enqueue/cancel/observer la
tâche générique. Chaque callback traite un lot borné, checkpoint hors mutex de

View file

@ -74,7 +74,7 @@ Le stem commun ne prouve jamais une acquisition. Les 953 propositions doivent
être confirmées et deviennent `CALLER_EXPLICIT`, jamais `STRONG`. La campagne
complète n'a pas encore été matérialisée.
### Limite de reprise DB v19
### Limite de reprise DB v19 (historique)
S3-E reprend une matérialisation connue par `resume_capture_id`. Une campagne
peut reconstruire son plan et conserver côté appelant le `capture_id` de chaque
@ -85,31 +85,41 @@ future identité durable de requête/campagne pourra fermer cette fenêtre ; ell
ne sera jamais déduite d'un chemin, SHA, basename, timestamp, métadonnée ou
`image_id`.
## NEXT — exécution durable de campagne d'acquisition
## Exécution durable de campagne d'acquisition — PASS / FROZEN
La prochaine tranche architecturale unique est l'exécution opérationnelle du
plan S3 gelé en réutilisant Task, Scheduler et Governor existants :
Project DB v20 apporte une migration additive v19→v20. Une tâche de campagne
enregistre atomiquement son snapshot générique, sa référence de checkpoint et
sa requête typée immuable. Le Task ID est l'identité opérationnelle de la
campagne ; la requête ne déduit aucune identité depuis les sources. Son codec
v1 est borné, à champs de largeur fixe et little-endian ; il conserve les
sources, les confirmations explicites et les options d'ingestion.
1. identité durable de requête et payload Task borné ;
2. persistance/rechargement des confirmations, du curseur et des `capture_id` ;
3. matérialisation incrémentale d'un groupe S3-E par unité sûre ;
4. checkpoint après rétention durable, pause/reprise/annulation aux frontières ;
5. admission et budgets par l'unique Resource Governor ;
6. progression et échecs sans runtime, queue ou persistence parallèle.
La requête persiste les confirmations comme `CALLER_EXPLICIT`, jamais comme
inférence `STRONG`. Le plan conserve des IDs de groupe un-based et Project DB
persiste la correspondance `(task_id, group_id) → capture_id`, ainsi que le
curseur. Chaque séquence admet et matérialise exactement un groupe via S3-E.
Après le retour de S3-E, une transaction conserve le Capture et avance le
curseur avant la progression générique et le checkpoint. Pause et annulation
sont coopératives aux frontières de groupe ; entre deux groupes,
`sequence_break` rend la réservation et impose une nouvelle admission.
Cette tranche décidera explicitement si une évolution additive après DB v19 est
nécessaire. Elle ne peut ni changer les identités S3 ni fusionner des Captures.
À la réouverture, la registry existante reconstruit la tâche à partir de son
Task ID et de sa requête, puis la Queue et le Governor existants la réadmettent.
Aucun runtime, queue, governor ou mécanisme de persistance parallèle n'est
introduit. La fenêtre résiduelle acceptée demeure l'arrêt après le retour de
S3-E et avant la transaction de rétention : elle ne déclenche aucune inférence
de Capture et ne fournit pas de garantie exactly-once pour ce groupe.
## NEXT REAL-DATA MILESTONE — A6000 ENGINE BAY END-TO-END
## NEXT REAL-DATA MILESTONE — A6000 + S21 ENGINE BAY MULTI-CAMPAIGN INTEGRATION
1. confirmer explicitement les 953 propositions ARW/JPEG ;
2. matérialiser progressivement environ 953 Captures ;
3. produire les représentations scientifiques par lots gouvernés ;
4. exécuter features, candidats, matching, vérification, tracks et Sparse SfM ;
5. évaluer la qualité de la reconstruction sparse ;
6. exécuter MVS/dense avec budgets et scratch contrôlés ;
7. publier durablement nuage dense et mesh ;
8. comparer le résultat aux tentatives photogrammétriques antérieures.
1. confirmer explicitement et exécuter les campagnes A6000 et S21 ;
2. matérialiser progressivement leurs Captures et représentations scientifiques
par lots gouvernés ;
3. exécuter features, candidats, matching, vérification, tracks et Sparse SfM ;
4. évaluer la qualité de la reconstruction sparse multi-campagne ;
5. exécuter MVS/dense avec budgets et scratch contrôlés ;
6. publier durablement nuage dense et mesh ;
7. comparer le résultat aux tentatives photogrammétriques antérieures.
Ce milestone est une intégration réelle, pas une nouvelle série de micro-gates
S3. Les antécédents d'OOM/SIGSEGV OpenMVS, pression swap, grands intermédiaires
@ -204,7 +214,8 @@ Le but à long terme n'est pas seulement de reconstruire ce qui a été
photographié, mais de guider activement l'opérateur vers les photographies qui
manquent encore pour obtenir une reconstruction fiable. Cette capacité est
postérieure à une reconstruction suffisamment mature ; elle ne fait pas partie
de S3 et ne remplace pas la prochaine tranche d'exécution durable de campagne.
de S3 et ne remplace pas le prochain milestone d'intégration multi-campagne
A6000 + S21.
```text
photographies existantes
@ -421,10 +432,8 @@ L'ordre demeure sans ambiguïté :
```text
CURRENT NEXT
exécution durable de campagne (Task / Queue / Scheduler / Governor,
confirmations durables, progression et reprise)
→ INTÉGRATION RÉELLE
campagnes A6000 + S21 → pipeline scientifique → dense/mesh → publication
intégration réelle multi-campagne A6000 + S21
→ pipeline scientifique → dense/mesh → publication
→ LATER
Coverage Analysis → Coverage Viewer → suggestions de points de vue
→ localisation live → intégration HDMI/USB → Capture Guidance / Live Coverage
@ -448,8 +457,8 @@ scientifique.
- ALIKED tant que provenance modèle, export ONNX et oracle upstream ne sont pas
reproductibles.
Ces idées ne doivent pas devancer l'exécution durable de campagne ni créer un
second runtime, Governor ou système de persistance.
Ces idées ne doivent pas devancer l'intégration réelle multi-campagne ni créer
un second runtime, Governor ou système de persistance.
## Principes de séquencement

View file

@ -0,0 +1,63 @@
#ifndef LARDON3D_ACQUISITION_CAMPAIGN_TASK_H
#define LARDON3D_ACQUISITION_CAMPAIGN_TASK_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <lardon3d/acquisition_campaign.h>
#include <lardon3d/task_kind_registry.h>
#ifdef __cplusplus
extern "C" {
#endif
#define LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND "acquisition_campaign.run"
enum {
LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND_VERSION = 1,
LARDON3D_ACQUISITION_CAMPAIGN_REQUEST_VERSION = 1,
};
/* Derived solely from the frozen source, path, confirmation and group bounds.
*/
#define LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES \
((size_t)LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES * \
(LARDON3D_ACQUISITION_CAMPAIGN_PATH_CAPACITY + 700u) + \
(size_t)LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES * \
(4u + 4u * LARDON3D_ACQUISITION_INGEST_MAX_SOURCES) + \
128u)
typedef struct {
const Lardon3DAcquisitionCampaignSource *sources;
size_t source_count;
const Lardon3DAcquisitionCampaignConfirmation *confirmations;
size_t confirmation_count;
Lardon3DAcquisitionIngestOptions ingest_options;
} Lardon3DAcquisitionCampaignTaskRequest;
Lardon3DTask *lardon3d_project_create_acquisition_campaign_task(
Lardon3DAppState *state, uint64_t scanset_id,
const Lardon3DAcquisitionCampaignTaskRequest *request, uint64_t *task_id);
bool lardon3d_project_enqueue_acquisition_campaign(
Lardon3DAppState *state, uint64_t scanset_id,
const Lardon3DAcquisitionCampaignTaskRequest *request, uint64_t *task_id);
bool lardon3d_acquisition_campaign_task_reconstruct(
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
Lardon3DTaskKindBinding *binding);
/* Exposed for deterministic codec validation without executing ingestion. */
bool lardon3d_acquisition_campaign_request_encode(
const Lardon3DAcquisitionCampaignTaskRequest *request,
unsigned char *output, size_t capacity, size_t *size);
bool lardon3d_acquisition_campaign_request_decode(
const unsigned char *input, size_t size,
Lardon3DAcquisitionCampaignSource *sources, size_t source_capacity,
Lardon3DAcquisitionCampaignConfirmation *confirmations,
size_t confirmation_capacity,
Lardon3DAcquisitionCampaignTaskRequest *request);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -58,6 +58,9 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_task(Lardon3DApp
Lardon3DProjectTaskCheckpointResult
lardon3d_project_checkpoint_image_import_task(Lardon3DAppState *state, const Lardon3DTask *task,
const char *source_path, uint64_t scanset_id);
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_acquisition_campaign_task(
Lardon3DAppState *state, const Lardon3DTask *task,
const Lardon3DProjectDbAcquisitionCampaignTask *parameters);
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_feature_extract_task(
Lardon3DAppState *state, const Lardon3DTask *task,
const Lardon3DProjectDbFeatureExtractTask *parameters);

View file

@ -10,7 +10,7 @@
#include <lardon3d/sparse_sfm_incremental.h>
enum {
LARDON3D_PROJECT_DB_SCHEMA_VERSION = 19,
LARDON3D_PROJECT_DB_SCHEMA_VERSION = 20,
LARDON3D_PROJECT_DB_ID_CAPACITY = 65,
LARDON3D_PROJECT_DB_KIND_CAPACITY = 65,
LARDON3D_PROJECT_DB_PATH_CAPACITY = 4096,
@ -100,6 +100,20 @@ typedef struct {
uint64_t scanset_id;
} Lardon3DProjectDbImageImport;
typedef struct {
uint64_t task_id;
uint64_t scanset_id;
uint32_t next_group_id;
uint32_t group_count;
const unsigned char *request;
size_t request_size;
} Lardon3DProjectDbAcquisitionCampaignTask;
typedef struct {
uint32_t group_id;
uint64_t capture_id;
} Lardon3DProjectDbAcquisitionCampaignCapture;
typedef struct {
uint64_t scanset_id;
char name[LARDON3D_PROJECT_DB_SCANSET_NAME_CAPACITY];
@ -570,6 +584,20 @@ Lardon3DProjectDbResult lardon3d_project_db_list_candidate_pairs(
Lardon3DProjectDbResult
lardon3d_project_db_load_image_import(Lardon3DProjectDb *database, uint64_t task_id,
Lardon3DProjectDbImageImport *parameters);
Lardon3DProjectDbResult lardon3d_project_db_record_acquisition_campaign_task(
Lardon3DProjectDb *database, const Lardon3DTaskDurableSnapshot *snapshot,
const char *task_kind, uint32_t task_kind_version,
const Lardon3DProjectDbCheckpoint *checkpoint,
const Lardon3DProjectDbAcquisitionCampaignTask *parameters, int64_t updated_at);
Lardon3DProjectDbResult lardon3d_project_db_load_acquisition_campaign_task(
Lardon3DProjectDb *database, uint64_t task_id, unsigned char *request,
size_t request_capacity, Lardon3DProjectDbAcquisitionCampaignTask *parameters);
Lardon3DProjectDbResult lardon3d_project_db_retain_acquisition_campaign_capture(
Lardon3DProjectDb *database, uint64_t task_id, uint32_t group_id,
uint64_t capture_id, uint32_t next_group_id);
Lardon3DProjectDbResult lardon3d_project_db_load_acquisition_campaign_capture(
Lardon3DProjectDb *database, uint64_t task_id, uint32_t group_id,
Lardon3DProjectDbAcquisitionCampaignCapture *capture);
Lardon3DProjectDbResult lardon3d_project_db_allocate_task_id(Lardon3DProjectDb *database,
uint64_t *task_id);
Lardon3DProjectDbResult lardon3d_project_db_load_task(Lardon3DProjectDb *database, uint64_t task_id,

View file

@ -152,6 +152,7 @@ executable(
'src/acquisition_pairing.cpp',
'src/acquisition_ingest.cpp',
'src/acquisition_campaign.cpp',
'src/acquisition_campaign_task.cpp',
'src/feature_extractor_opencv.cpp',
'src/feature_store.c',
'src/feature_extractor_opencv.cpp',
@ -195,6 +196,7 @@ executable(
'-DLARDON3D_TRACK_BUILDER_TASK_AVAILABLE',
'-DLARDON3D_SPARSE_SFM_TASK_AVAILABLE',
'-DLARDON3D_INCREMENTAL_RECONSTRUCTION_TASK_AVAILABLE',
'-DLARDON3D_ACQUISITION_CAMPAIGN_TASK_AVAILABLE',
],
dependencies: [
ncursesw,
@ -377,6 +379,24 @@ acquisition_campaign_test = executable(
test('acquisition-campaign', acquisition_campaign_test, timeout: 30)
acquisition_campaign_task_test = executable(
'test-acquisition-campaign-task',
sources: [
'tests/test_acquisition_campaign_task.cpp',
'src/acquisition_campaign_task.cpp', 'src/acquisition_campaign.cpp',
'src/acquisition_ingest.cpp', 'src/acquisition_pairing.cpp',
'src/raw_development.cpp', 'src/app_state.c', 'src/image_catalog_persistent.c',
'src/image_catalog.c', 'src/image_view.c', 'src/task_kind_registry.c',
'src/project.c', 'src/project_db.c', 'src/project_db_sparse_sfm.c',
'src/task.c', 'src/task_checkpoint.c', 'src/task_queue.c',
'src/resource_governor.c', 'src/resource_snapshot.c',
],
include_directories: include_directories('include'),
cpp_args: ['-DLARDON3D_ACQUISITION_CAMPAIGN_TASK_TESTING'],
dependencies: [threads, sqlite3, openssl, opencv, libraw, libexif, libpng, libdeflate],
)
test('acquisition-campaign-task', acquisition_campaign_task_test, timeout: 30)
feature_store_test = executable(
'test-feature-store',
sources: [

View file

@ -0,0 +1,569 @@
extern "C" {
#include <lardon3d/acquisition_campaign_task.h>
#include <lardon3d/project.h>
#include <lardon3d/project_db.h>
#include <lardon3d/task_queue.h>
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <new>
#include <vector>
namespace {
constexpr unsigned char magic[8] = {'L', '3', 'D', 'A', 'C', 'T', '1', '\0'};
constexpr size_t max_request_size =
LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES;
struct Writer {
unsigned char *p;
size_t left;
size_t used;
bool bytes(const void *v, size_t n) {
if (n > left)
return false;
if (p)
std::memcpy(p + used, v, n);
used += n;
left -= n;
return true;
}
bool u16(uint16_t v) {
unsigned char b[2] = {static_cast<unsigned char>(v),
static_cast<unsigned char>(v >> 8)};
return bytes(b, 2);
}
bool u32(uint32_t v) {
unsigned char b[4];
for (int i = 0; i < 4; ++i)
b[i] = static_cast<unsigned char>(v >> (8 * i));
return bytes(b, 4);
}
bool u64(uint64_t v) {
unsigned char b[8];
for (int i = 0; i < 8; ++i)
b[i] = static_cast<unsigned char>(v >> (8 * i));
return bytes(b, 8);
}
bool text(const char *s, size_t cap) {
if (!s)
return false;
const void *end = std::memchr(s, '\0', cap);
if (!end)
return false;
size_t n = static_cast<const char *>(end) - s;
return n <= UINT16_MAX && u16(static_cast<uint16_t>(n)) && bytes(s, n);
}
};
struct Reader {
const unsigned char *p;
size_t left;
bool bytes(void *v, size_t n) {
if (n > left)
return false;
std::memcpy(v, p, n);
p += n;
left -= n;
return true;
}
bool u16(uint16_t &v) {
unsigned char b[2];
if (!bytes(b, 2))
return false;
v = uint16_t(b[0]) | uint16_t(uint16_t(b[1]) << 8);
return true;
}
bool u32(uint32_t &v) {
unsigned char b[4];
if (!bytes(b, 4))
return false;
v = 0;
for (int i = 3; i >= 0; --i)
v = (v << 8) | b[i];
return true;
}
bool u64(uint64_t &v) {
unsigned char b[8];
if (!bytes(b, 8))
return false;
v = 0;
for (int i = 7; i >= 0; --i)
v = (v << 8) | b[i];
return true;
}
bool text(char *s, size_t cap) {
uint16_t n;
if (!u16(n) || size_t(n) >= cap || size_t(n) > left)
return false;
std::memcpy(s, p, n);
s[n] = '\0';
p += n;
left -= n;
return true;
}
};
bool encode(const Lardon3DAcquisitionCampaignTaskRequest *r, Writer &w) {
if (!r || !r->sources || r->source_count == 0 ||
r->source_count > LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES ||
(r->confirmation_count && !r->confirmations) ||
r->confirmation_count > r->source_count ||
r->ingest_options.representation <
LARDON3D_ACQUISITION_SELECT_JPEG_SOURCE ||
r->ingest_options.representation >
LARDON3D_ACQUISITION_SELECT_DEVELOP_RAW ||
r->ingest_options.select_representation > 1u ||
r->ingest_options.imported_at < 0 ||
r->ingest_options.max_source_bytes == 0u)
return false;
std::unique_ptr<Lardon3DAcquisitionCampaignPlan> validated(
new (std::nothrow) Lardon3DAcquisitionCampaignPlan{});
if (!validated ||
lardon3d_acquisition_campaign_plan(
r->sources, r->source_count, r->confirmations, r->confirmation_count,
validated.get()) != LARDON3D_ACQUISITION_CAMPAIGN_OK ||
validated->group_count == 0)
return false;
if (!w.bytes(magic, sizeof(magic)) ||
!w.u32(LARDON3D_ACQUISITION_CAMPAIGN_REQUEST_VERSION) ||
!w.u32(static_cast<uint32_t>(r->source_count)) ||
!w.u32(static_cast<uint32_t>(r->confirmation_count)) ||
!w.u32(static_cast<uint32_t>(r->ingest_options.representation)) ||
!w.u32(r->ingest_options.select_representation ? 1u : 0u) ||
!w.u64(static_cast<uint64_t>(r->ingest_options.imported_at)) ||
!w.u64(r->ingest_options.max_source_bytes))
return false;
for (size_t i = 0; i < r->source_count; ++i) {
const auto &s = r->sources[i];
const auto &m = s.metadata;
if (!w.text(s.path, sizeof(s.path)) ||
!w.u32(static_cast<uint32_t>(s.source_kind)) ||
!w.u32(static_cast<uint32_t>(s.metadata_result)) ||
!w.u32(m.policy_version) ||
!w.u32(static_cast<uint32_t>(m.source_kind)) ||
!w.u32(m.present_fields) || !w.text(m.make, sizeof(m.make)) ||
!w.text(m.model, sizeof(m.model)) ||
!w.text(m.body_serial, sizeof(m.body_serial)) ||
!w.text(m.datetime_original, sizeof(m.datetime_original)) ||
!w.text(m.subsec_original, sizeof(m.subsec_original)) ||
!w.text(m.offset_original, sizeof(m.offset_original)) ||
!w.text(m.image_unique_id, sizeof(m.image_unique_id)) ||
!w.u32(m.width) || !w.u32(m.height) || !w.u16(m.orientation))
return false;
}
for (size_t i = 0; i < r->confirmation_count; ++i) {
const auto &c = r->confirmations[i];
if (c.source_count == 0 ||
c.source_count > LARDON3D_ACQUISITION_INGEST_MAX_SOURCES ||
!w.u32(static_cast<uint32_t>(c.source_count)))
return false;
for (size_t j = 0; j < c.source_count; ++j)
if (c.source_indices[j] >= r->source_count ||
!w.u32(static_cast<uint32_t>(c.source_indices[j])))
return false;
}
return true;
}
struct Context {
char project_path[LARDON3D_APP_STATE_PATH_CAPACITY]{};
Lardon3DProjectDb *db{};
Lardon3DResourceGovernor *governor{};
uint64_t scanset{};
std::vector<Lardon3DAcquisitionCampaignSource> sources;
std::vector<Lardon3DAcquisitionCampaignConfirmation> confirmations;
std::vector<unsigned char> encoded;
Lardon3DAcquisitionCampaignPlan plan{};
Lardon3DAcquisitionIngestOptions options{};
};
void destroy(void *p) { delete static_cast<Context *>(p); }
void runtime(Context *c, Lardon3DAppState &s) {
lardon3d_app_state_init(&s);
s.project_loaded = true;
s.project_db = c->db;
s.resource_governor = c->governor;
std::snprintf(s.project_path, sizeof(s.project_path), "%s", c->project_path);
}
bool checkpoint(Context *c, Lardon3DTask *t, uint32_t cursor) {
Lardon3DAppState s;
runtime(c, s);
Lardon3DProjectDbAcquisitionCampaignTask p{
lardon3d_task_id(t),
c->scanset,
cursor,
static_cast<uint32_t>(c->plan.group_count),
c->encoded.data(),
c->encoded.size()};
return lardon3d_project_checkpoint_acquisition_campaign_task(&s, t, &p) ==
LARDON3D_PROJECT_TASK_CHECKPOINT_OK;
}
bool run_impl(Lardon3DTask *t, void *p) {
auto *c = static_cast<Context *>(p);
Lardon3DProjectDbAcquisitionCampaignTask persisted{};
std::vector<unsigned char> blob(max_request_size);
if (lardon3d_project_db_load_acquisition_campaign_task(
c->db, lardon3d_task_id(t), blob.data(), blob.size(), &persisted) !=
LARDON3D_PROJECT_DB_OK)
return lardon3d_task_fail(t, "Campagne durable introuvable.");
for (uint32_t cursor = persisted.next_group_id; cursor < c->plan.group_count;
++cursor) {
const uint32_t group_id = cursor + 1u;
if (!lardon3d_task_checkpoint(t))
return false;
Lardon3DProjectDbAcquisitionCampaignCapture retained{};
uint64_t resume = 0;
auto lr = lardon3d_project_db_load_acquisition_campaign_capture(
c->db, lardon3d_task_id(t), group_id, &retained);
if (lr == LARDON3D_PROJECT_DB_OK)
resume = retained.capture_id;
else if (lr != LARDON3D_PROJECT_DB_NOT_FOUND)
return lardon3d_task_fail(t, "Lecture de rétention impossible.");
auto options = c->options;
options.grouping = LARDON3D_ACQUISITION_GROUP_CALLER_EXPLICIT;
options.resume_capture_id = resume;
options.producer_task_id = lardon3d_task_id(t);
Lardon3DAcquisitionIngestOutput out{};
Lardon3DAcquisitionIngestResult ir{};
Lardon3DAppState s;
runtime(c, s);
auto cr = lardon3d_acquisition_campaign_materialize_group(
&s, c->scanset, c->sources.data(), c->sources.size(), &c->plan,
group_id, &options, &out, &ir);
if (cr != LARDON3D_ACQUISITION_CAMPAIGN_OK ||
ir != LARDON3D_ACQUISITION_INGEST_OK || out.group_count != 1)
return lardon3d_task_fail(t, "Échec de matérialisation de campagne.");
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_TESTING
const char *before_retention =
std::getenv("LARDON3D_TEST_CAMPAIGN_FAIL_BEFORE_RETENTION");
if (before_retention && std::strcmp(before_retention, "1") == 0)
return lardon3d_task_fail(t, "Échec injecté avant rétention.");
#endif
if (lardon3d_project_db_retain_acquisition_campaign_capture(
c->db, lardon3d_task_id(t), group_id, out.groups[0].capture_id,
group_id) != LARDON3D_PROJECT_DB_OK)
return lardon3d_task_fail(t, "Rétention de Capture impossible.");
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_TESTING
const char *after_retention =
std::getenv("LARDON3D_TEST_CAMPAIGN_FAIL_AFTER_RETENTION");
if (after_retention && std::strcmp(after_retention, "1") == 0)
return lardon3d_task_fail(t, "Échec injecté après rétention.");
#endif
unsigned progress = static_cast<unsigned>(
(uint64_t(group_id) * 100u / c->plan.group_count));
if (!lardon3d_task_set_progress(t, progress,
"Groupe de campagne matérialisé.") ||
!checkpoint(c, t, group_id))
return lardon3d_task_fail(t, "Checkpoint de campagne impossible.");
if (group_id < c->plan.group_count) {
Lardon3DTaskExecutionContract contract{};
Lardon3DResourceReservation *reservation = nullptr;
if (!lardon3d_task_sequence_break(t, c->governor, &reservation,
&contract))
return false;
}
}
return true;
}
bool run(Lardon3DTask *t, void *p) noexcept {
try {
return run_impl(t, p);
} catch (const std::bad_alloc &) {
return lardon3d_task_fail(t, "Mémoire insuffisante pour la campagne.");
} catch (...) {
return lardon3d_task_fail(t, "Erreur interne de campagne.");
}
}
void finished(const Lardon3DTask *t, void *p) {
try {
auto *c = static_cast<Context *>(p);
Lardon3DAppState s;
runtime(c, s);
(void)lardon3d_project_checkpoint_task(&s, t);
} catch (...) {
}
}
Context *make_context(const char *path, Lardon3DProjectDb *db,
Lardon3DResourceGovernor *g, uint64_t scanset,
const Lardon3DAcquisitionCampaignTaskRequest &r,
const unsigned char *encoded, size_t n) {
if (!path || !path[0])
return nullptr;
std::unique_ptr<Context> c(new (std::nothrow) Context);
if (!c)
return nullptr;
const int path_size =
std::snprintf(c->project_path, sizeof(c->project_path), "%s", path);
if (path_size < 0 || static_cast<size_t>(path_size) >= sizeof(c->project_path))
return nullptr;
c->db = db;
c->governor = g;
c->scanset = scanset;
c->sources.assign(r.sources, r.sources + r.source_count);
c->confirmations.assign(r.confirmations,
r.confirmations + r.confirmation_count);
c->options = r.ingest_options;
c->encoded.assign(encoded, encoded + n);
if (lardon3d_acquisition_campaign_plan(c->sources.data(), c->sources.size(),
c->confirmations.data(),
c->confirmations.size(), &c->plan) !=
LARDON3D_ACQUISITION_CAMPAIGN_OK ||
c->plan.group_count == 0)
return nullptr;
return c.release();
}
} // namespace
bool request_encode_impl(
const Lardon3DAcquisitionCampaignTaskRequest *r, unsigned char *out,
size_t cap, size_t *size) {
if (size)
*size = 0;
if (!size)
return false;
Writer measure{nullptr, max_request_size, 0};
if (!encode(r, measure) || measure.used > max_request_size) {
return false;
}
*size = measure.used;
if (!out)
return cap == 0;
if (cap < measure.used)
return false;
Writer writer{out, cap, 0};
return encode(r, writer) && writer.used == measure.used;
}
bool request_decode_impl(
const unsigned char *input, size_t size,
Lardon3DAcquisitionCampaignSource *sources, size_t sc,
Lardon3DAcquisitionCampaignConfirmation *confirmations, size_t cc,
Lardon3DAcquisitionCampaignTaskRequest *r) {
if (!input || !r || size > max_request_size)
return false;
std::memset(r, 0, sizeof(*r));
Reader q{input, size};
unsigned char m[8];
uint32_t version, n, c, rep, select;
uint64_t imported, maxbytes;
if (!q.bytes(m, 8) || std::memcmp(m, magic, 8) || !q.u32(version) ||
version != 1 || !q.u32(n) || n == 0 || n > sc ||
n > LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES || !q.u32(c) || c > cc ||
c > n || !sources || (c > 0 && !confirmations) || !q.u32(rep) ||
rep < 1 || rep > 2 || !q.u32(select) || select > 1 ||
!q.u64(imported) || imported > INT64_MAX || !q.u64(maxbytes) ||
maxbytes == 0u)
return false;
std::memset(sources, 0, n * sizeof(*sources));
std::memset(confirmations, 0, c * sizeof(*confirmations));
for (uint32_t i = 0; i < n; ++i) {
auto &s = sources[i];
auto &md = s.metadata;
uint32_t sk, mr, msk;
if (!q.text(s.path, sizeof(s.path)) || !q.u32(sk) || sk > 2 || !q.u32(mr) ||
mr > LARDON3D_ACQUISITION_INTERNAL_ERROR || !q.u32(md.policy_version) ||
!q.u32(msk) || msk > 2 || !q.u32(md.present_fields) ||
!q.text(md.make, sizeof(md.make)) ||
!q.text(md.model, sizeof(md.model)) ||
!q.text(md.body_serial, sizeof(md.body_serial)) ||
!q.text(md.datetime_original, sizeof(md.datetime_original)) ||
!q.text(md.subsec_original, sizeof(md.subsec_original)) ||
!q.text(md.offset_original, sizeof(md.offset_original)) ||
!q.text(md.image_unique_id, sizeof(md.image_unique_id)) ||
!q.u32(md.width) || !q.u32(md.height) || !q.u16(md.orientation))
return false;
s.source_kind = static_cast<Lardon3DAcquisitionSourceKind>(sk);
s.metadata_result = static_cast<Lardon3DAcquisitionResult>(mr);
md.source_kind = static_cast<Lardon3DAcquisitionSourceKind>(msk);
}
for (uint32_t i = 0; i < c; ++i) {
uint32_t count;
if (!q.u32(count) || count == 0 ||
count > LARDON3D_ACQUISITION_INGEST_MAX_SOURCES)
return false;
confirmations[i].source_count = count;
for (uint32_t j = 0; j < count; ++j) {
uint32_t index;
if (!q.u32(index) || index >= n)
return false;
confirmations[i].source_indices[j] = index;
}
}
if (q.left)
return false;
r->sources = sources;
r->source_count = n;
r->confirmations = confirmations;
r->confirmation_count = c;
r->ingest_options.representation =
static_cast<Lardon3DAcquisitionRepresentation>(rep);
r->ingest_options.select_representation = static_cast<uint8_t>(select);
r->ingest_options.imported_at = static_cast<int64_t>(imported);
r->ingest_options.max_source_bytes = maxbytes;
std::unique_ptr<Lardon3DAcquisitionCampaignPlan> validated(
new (std::nothrow) Lardon3DAcquisitionCampaignPlan{});
return validated &&
lardon3d_acquisition_campaign_plan(sources, n, confirmations, c,
validated.get()) ==
LARDON3D_ACQUISITION_CAMPAIGN_OK &&
validated->group_count > 0;
}
bool task_reconstruct_impl(
const Lardon3DTaskDurableSnapshot *s, void *userdata,
Lardon3DTaskKindBinding *b) {
auto *rt = static_cast<Lardon3DTaskReconstructionContext *>(userdata);
if (!s || !rt || !b)
return false;
std::vector<unsigned char> blob(max_request_size);
Lardon3DProjectDbAcquisitionCampaignTask p{};
if (lardon3d_project_db_load_acquisition_campaign_task(
rt->project_db, s->id, blob.data(), blob.size(), &p) !=
LARDON3D_PROJECT_DB_OK)
return false;
std::vector<Lardon3DAcquisitionCampaignSource> sources(
LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES);
std::vector<Lardon3DAcquisitionCampaignConfirmation> confirmations(
LARDON3D_ACQUISITION_CAMPAIGN_MAX_SOURCES);
Lardon3DAcquisitionCampaignTaskRequest r{};
if (!lardon3d_acquisition_campaign_request_decode(
blob.data(), p.request_size, sources.data(), sources.size(),
confirmations.data(), confirmations.size(), &r))
return false;
auto *c =
make_context(rt->project_path, rt->project_db, rt->resource_governor,
p.scanset_id, r, blob.data(), p.request_size);
if (!c)
return false;
*b = {run, c, destroy, finished, c};
return true;
}
Lardon3DTask *create_task_impl(
Lardon3DAppState *s, uint64_t scanset,
const Lardon3DAcquisitionCampaignTaskRequest *r, uint64_t *id) {
if (id)
*id = 0;
if (!s || !s->project_loaded || !s->project_db || !s->resource_governor ||
!r || !id)
return nullptr;
Lardon3DProjectDbScanSet ss{};
if (lardon3d_project_db_load_scanset(s->project_db, scanset, &ss) !=
LARDON3D_PROJECT_DB_OK)
return nullptr;
size_t n = 0;
if (!lardon3d_acquisition_campaign_request_encode(r, nullptr, 0, &n))
return nullptr;
std::vector<unsigned char> blob(n);
if (!lardon3d_acquisition_campaign_request_encode(r, blob.data(), blob.size(),
&n))
return nullptr;
auto *c = make_context(s->project_path, s->project_db, s->resource_governor,
scanset, *r, blob.data(), n);
if (!c)
return nullptr;
uint64_t task_id;
if (lardon3d_project_db_allocate_task_id(s->project_db, &task_id) !=
LARDON3D_PROJECT_DB_OK) {
delete c;
return nullptr;
}
Lardon3DResourceEstimate e{
256 * 1024, 0, 64 * 1024, 0, 1,
1, 1, 0, 1, LARDON3D_RESOURCE_TASK_IMPORT};
auto *t = lardon3d_task_create_typed("Campagne d'acquisition", &e,
LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND,
1, run, c, destroy);
if (!t || !lardon3d_task_assign_id(t, task_id) ||
!lardon3d_task_set_finished_callback(t, finished, c) ||
!checkpoint(c, t, 0)) {
lardon3d_task_destroy(t);
return nullptr;
}
*id = task_id;
return t;
}
extern "C" bool lardon3d_acquisition_campaign_request_encode(
const Lardon3DAcquisitionCampaignTaskRequest *r, unsigned char *out,
size_t cap, size_t *size) {
try {
return request_encode_impl(r, out, cap, size);
} catch (const std::bad_alloc &) {
if (size)
*size = 0;
return false;
} catch (...) {
if (size)
*size = 0;
return false;
}
}
extern "C" bool lardon3d_acquisition_campaign_request_decode(
const unsigned char *input, size_t size,
Lardon3DAcquisitionCampaignSource *sources, size_t sc,
Lardon3DAcquisitionCampaignConfirmation *confirmations, size_t cc,
Lardon3DAcquisitionCampaignTaskRequest *r) {
try {
return request_decode_impl(input, size, sources, sc, confirmations, cc, r);
} catch (const std::bad_alloc &) {
return false;
} catch (...) {
return false;
}
}
extern "C" bool lardon3d_acquisition_campaign_task_reconstruct(
const Lardon3DTaskDurableSnapshot *s, void *userdata,
Lardon3DTaskKindBinding *b) {
try {
return task_reconstruct_impl(s, userdata, b);
} catch (const std::bad_alloc &) {
return false;
} catch (...) {
return false;
}
}
extern "C" Lardon3DTask *lardon3d_project_create_acquisition_campaign_task(
Lardon3DAppState *s, uint64_t scanset,
const Lardon3DAcquisitionCampaignTaskRequest *r, uint64_t *id) {
if (id)
*id = 0;
try {
return create_task_impl(s, scanset, r, id);
} catch (const std::bad_alloc &) {
return nullptr;
} catch (...) {
return nullptr;
}
}
extern "C" bool lardon3d_project_enqueue_acquisition_campaign(
Lardon3DAppState *s, uint64_t scanset,
const Lardon3DAcquisitionCampaignTaskRequest *r, uint64_t *id) {
try {
if (!s || !s->task_queue)
return false;
auto *t =
lardon3d_project_create_acquisition_campaign_task(s, scanset, r, id);
if (!t)
return false;
if (!lardon3d_task_queue_add(s->task_queue, t, nullptr)) {
lardon3d_task_destroy(t);
return false;
}
return true;
} catch (const std::bad_alloc &) {
return false;
} catch (...) {
return false;
}
}

View file

@ -674,7 +674,8 @@ checkpoint_task_internal(Lardon3DAppState *state, const Lardon3DTask *task,
const Lardon3DProjectDbVisualIndexUpdateTask *visual_parameters,
const Lardon3DProjectDbCandidatePairGenerateTask *candidate_parameters,
const Lardon3DProjectDbMatcherTask *matcher_parameters,
const Lardon3DProjectDbGeometricVerifierTask *geometric_parameters) {
const Lardon3DProjectDbGeometricVerifierTask *geometric_parameters,
const Lardon3DProjectDbAcquisitionCampaignTask *campaign_parameters) {
if (!state || !state->project_loaded || !state->project_db) {
return LARDON3D_PROJECT_TASK_CHECKPOINT_NO_PROJECT;
}
@ -741,6 +742,10 @@ checkpoint_task_internal(Lardon3DAppState *state, const Lardon3DTask *task,
? lardon3d_project_db_record_geometric_verifier_task(
state->project_db, &snapshot, task_kind, task_kind_version, &checkpoint,
geometric_parameters, now.tv_sec)
: campaign_parameters
? lardon3d_project_db_record_acquisition_campaign_task(
state->project_db, &snapshot, task_kind, task_kind_version, &checkpoint,
campaign_parameters, now.tv_sec)
: lardon3d_project_db_record_task(state->project_db, &snapshot, task_kind,
task_kind_version, &checkpoint, now.tv_sec);
if (recorded == LARDON3D_PROJECT_DB_BUSY) {
@ -756,7 +761,7 @@ checkpoint_task_internal(Lardon3DAppState *state, const Lardon3DTask *task,
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_task(Lardon3DAppState *state,
const Lardon3DTask *task) {
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
}
Lardon3DProjectTaskCheckpointResult
@ -766,7 +771,15 @@ lardon3d_project_checkpoint_image_import_task(Lardon3DAppState *state, const Lar
return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
}
return checkpoint_task_internal(
state, task, source_path, scanset_id, NULL, NULL, NULL, NULL, NULL, NULL);
state, task, source_path, scanset_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_acquisition_campaign_task(
Lardon3DAppState *state, const Lardon3DTask *task,
const Lardon3DProjectDbAcquisitionCampaignTask *parameters) {
if (!parameters) return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL,
parameters);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_feature_extract_task(
@ -775,14 +788,16 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_feature_extract_
if (!parameters) {
return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
}
return checkpoint_task_internal(state, task, NULL, 0, parameters, NULL, NULL, NULL, NULL, NULL);
return checkpoint_task_internal(state, task, NULL, 0, parameters, NULL, NULL, NULL, NULL, NULL,
NULL);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_sift_extract_task(
Lardon3DAppState *state, const Lardon3DTask *task,
const Lardon3DProjectDbSiftExtractTask *parameters) {
if (!parameters) return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
return checkpoint_task_internal(state, task, NULL, 0, NULL, parameters, NULL, NULL, NULL, NULL);
return checkpoint_task_internal(state, task, NULL, 0, NULL, parameters, NULL, NULL, NULL, NULL,
NULL);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_visual_index_update_task(
@ -791,7 +806,8 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_visual_index_upd
if (!parameters) {
return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
}
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, parameters, NULL, NULL, NULL);
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, parameters, NULL, NULL, NULL,
NULL);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_candidate_pair_generate_task(
@ -800,7 +816,8 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_candidate_pair_g
if (!parameters) {
return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
}
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, parameters, NULL, NULL);
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, parameters, NULL, NULL,
NULL);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_matcher_task(
@ -809,7 +826,8 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_matcher_task(
if (!parameters) {
return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
}
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, NULL, parameters, NULL);
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, NULL, parameters, NULL,
NULL);
}
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_geometric_verifier_task(
@ -819,7 +837,7 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_geometric_verifi
return LARDON3D_PROJECT_TASK_CHECKPOINT_INVALID_TASK;
}
return checkpoint_task_internal(state, task, NULL, 0, NULL, NULL, NULL, NULL, NULL,
parameters);
parameters, NULL);
}
static bool coherent_recovery(const Lardon3DProjectDbTask *database_task,

View file

@ -607,6 +607,19 @@ static const char schema_capture_provenance_v19[] =
"created_at INTEGER NOT NULL CHECK(created_at>=0),"
"CHECK(parent_asset_id!=child_asset_id));";
static const char schema_acquisition_campaign_v20[] =
"CREATE TABLE IF NOT EXISTS acquisition_campaign_tasks("
"task_id INTEGER PRIMARY KEY REFERENCES tasks(task_id) ON DELETE CASCADE,"
"scanset_id INTEGER NOT NULL REFERENCES scansets(scanset_id),"
"next_group_id INTEGER NOT NULL CHECK(next_group_id>=0 AND next_group_id<=4294967295),"
"group_count INTEGER NOT NULL CHECK(group_count>0 AND group_count<=4096),"
"request BLOB NOT NULL CHECK(length(request)>0));"
"CREATE TABLE IF NOT EXISTS acquisition_campaign_captures("
"task_id INTEGER NOT NULL REFERENCES acquisition_campaign_tasks(task_id) ON DELETE CASCADE,"
"group_id INTEGER NOT NULL CHECK(group_id>=0 AND group_id<=4294967295),"
"capture_id INTEGER NOT NULL REFERENCES captures(capture_id),"
"PRIMARY KEY(task_id,group_id),UNIQUE(task_id,capture_id));";
static void copy_error(char destination[LARDON3D_PROJECT_DB_ERROR_CAPACITY], const char *text) {
if (destination) {
(void)snprintf(destination, LARDON3D_PROJECT_DB_ERROR_CAPACITY, "%s", text ? text : "");
@ -1093,6 +1106,22 @@ static Lardon3DProjectDbResult migrate(Lardon3DProjectDb *database, unsigned int
"finish schema v19 migration");
}
}
if (result == LARDON3D_PROJECT_DB_OK && from_version < 20) {
result = execute(database, schema_acquisition_campaign_v20,
"migrate acquisition campaign task v19 to v20");
#ifdef LARDON3D_PROJECT_DB_TESTING
if (result == LARDON3D_PROJECT_DB_OK &&
getenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V20")) {
result = execute(database, "INSERT INTO missing_v20_test_table VALUES(1)",
"forced migration v20 failure");
}
#endif
if (result == LARDON3D_PROJECT_DB_OK) {
result = execute(database,
"UPDATE metadata SET value=20 WHERE key='schema_version' AND value=19",
"finish schema v20 migration");
}
}
if (result == LARDON3D_PROJECT_DB_OK) {
result = execute(database, "COMMIT", "commit migration");
}
@ -1228,7 +1257,9 @@ Lardon3DProjectDbResult lardon3d_project_db_open(const char *path, Lardon3DProje
"capture_images",
"capture_assets",
"capture_selections",
"asset_derivations"};
"asset_derivations",
"acquisition_campaign_tasks",
"acquisition_campaign_captures"};
for (size_t index = 0; index < sizeof(required) / sizeof(required[0]) &&
result == LARDON3D_PROJECT_DB_OK;
++index) {
@ -1452,6 +1483,7 @@ record_task_internal(Lardon3DProjectDb *database, const Lardon3DTaskDurableSnaps
const Lardon3DProjectDbMatcherTask *matcher,
const Lardon3DProjectDbGeometricVerifierTask *geometric_verifier,
const Lardon3DProjectDbSparseSfmTask *sparse_sfm,
const Lardon3DProjectDbAcquisitionCampaignTask *campaign,
int64_t updated_at) {
bool typed = task_kind != NULL;
if (!database || !valid_durable_task(snapshot, updated_at) ||
@ -1518,6 +1550,11 @@ record_task_internal(Lardon3DProjectDb *database, const Lardon3DTaskDurableSnaps
!valid_task_id(sparse_sfm->track_set_id) ||
!valid_task_id(sparse_sfm->calibration_scope_id) || sparse_sfm->sfm_kind != 1 ||
sparse_sfm->sfm_version != 1 || !valid_sparse_sfm_parameters(&sparse_sfm->parameters))) ||
(campaign && (!valid_task_id(campaign->task_id) || campaign->task_id != snapshot->id ||
!valid_task_id(campaign->scanset_id) || campaign->group_count == 0 ||
campaign->group_count > 4096 ||
campaign->next_group_id > campaign->group_count || !campaign->request ||
campaign->request_size == 0 || campaign->request_size > INT_MAX)) ||
(checkpoint && !valid_checkpoint(checkpoint))) {
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
@ -1582,6 +1619,26 @@ record_task_internal(Lardon3DProjectDb *database, const Lardon3DTaskDurableSnaps
}
#endif
}
if (result == LARDON3D_PROJECT_DB_OK && campaign) {
result = prepare(database,
"INSERT INTO acquisition_campaign_tasks(task_id,scanset_id,next_group_id,group_count,"
"request) VALUES(?1,?2,?3,?4,?5) ON CONFLICT(task_id) DO UPDATE SET "
"next_group_id=excluded.next_group_id WHERE scanset_id=excluded.scanset_id AND "
"group_count=excluded.group_count AND request=excluded.request", &statement);
if (result == LARDON3D_PROJECT_DB_OK) {
sqlite3_bind_int64(statement, 1, (sqlite3_int64)campaign->task_id);
sqlite3_bind_int64(statement, 2, (sqlite3_int64)campaign->scanset_id);
sqlite3_bind_int64(statement, 3, campaign->next_group_id);
sqlite3_bind_int64(statement, 4, campaign->group_count);
sqlite3_bind_blob(statement, 5, campaign->request, (int)campaign->request_size,
SQLITE_TRANSIENT);
result = step_done(database, statement, "upsert campaign task");
if (result == LARDON3D_PROJECT_DB_OK && sqlite3_changes(database->connection) != 1) {
copy_error(database->error, "Payload de campagne immuable.");
result = LARDON3D_PROJECT_DB_CONSTRAINT;
}
}
}
if (result == LARDON3D_PROJECT_DB_OK) {
result =
prepare(database,
@ -1916,7 +1973,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_task(
Lardon3DProjectDb *database, const Lardon3DTaskDurableSnapshot *snapshot, const char *task_kind,
uint32_t task_kind_version, const Lardon3DProjectDbCheckpoint *checkpoint, int64_t updated_at) {
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint, NULL, 0,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, updated_at);
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_record_image_import_task(
@ -1928,7 +1985,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_image_import_task(
}
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint,
source_path, scanset_id, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, updated_at);
NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_record_feature_extract_task(
@ -1939,7 +1996,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_feature_extract_task(
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint, NULL, 0,
parameters, NULL, NULL, NULL, NULL, NULL, NULL, updated_at);
parameters, NULL, NULL, NULL, NULL, NULL, NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_record_sift_extract_task(
@ -1949,7 +2006,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_sift_extract_task(
const Lardon3DProjectDbSiftExtractTask *parameters, int64_t updated_at) {
if (!parameters) return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint, NULL, 0,
NULL, parameters, NULL, NULL, NULL, NULL, NULL, updated_at);
NULL, parameters, NULL, NULL, NULL, NULL, NULL, NULL, updated_at);
}
static bool read_task(sqlite3_stmt *statement, Lardon3DProjectDbTask *task) {
@ -2179,6 +2236,158 @@ lardon3d_project_db_load_image_import(Lardon3DProjectDb *database, uint64_t task
return result;
}
Lardon3DProjectDbResult lardon3d_project_db_record_acquisition_campaign_task(
Lardon3DProjectDb *database, const Lardon3DTaskDurableSnapshot *snapshot,
const char *task_kind, uint32_t task_kind_version,
const Lardon3DProjectDbCheckpoint *checkpoint,
const Lardon3DProjectDbAcquisitionCampaignTask *parameters, int64_t updated_at) {
if (!database || !snapshot || !parameters || parameters->task_id != snapshot->id ||
!valid_task_id(parameters->scanset_id) || parameters->group_count == 0 ||
parameters->group_count > 4096 || parameters->next_group_id > parameters->group_count ||
!parameters->request || parameters->request_size == 0 || parameters->request_size > INT_MAX) {
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
return record_task_internal(database, snapshot, task_kind, task_kind_version, checkpoint,
NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, parameters,
updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_load_acquisition_campaign_task(
Lardon3DProjectDb *database, uint64_t task_id, unsigned char *request,
size_t request_capacity, Lardon3DProjectDbAcquisitionCampaignTask *parameters) {
if (!database || !valid_task_id(task_id) || !request || request_capacity == 0 || !parameters)
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
memset(parameters, 0, sizeof(*parameters));
(void)pthread_mutex_lock(&database->mutex);
sqlite3_stmt *statement = NULL;
Lardon3DProjectDbResult result = prepare(database,
"SELECT scanset_id,next_group_id,group_count,request FROM acquisition_campaign_tasks "
"WHERE task_id=?1", &statement);
if (result == LARDON3D_PROJECT_DB_OK) {
sqlite3_bind_int64(statement, 1, (sqlite3_int64)task_id);
int code = sqlite3_step(statement);
if (code == SQLITE_DONE) result = LARDON3D_PROJECT_DB_NOT_FOUND;
else if (code != SQLITE_ROW) result = sqlite_result(database, code, "load campaign task");
else {
int bytes = sqlite3_column_bytes(statement, 3);
const void *blob = sqlite3_column_blob(statement, 3);
if (bytes <= 0 || (size_t)bytes > request_capacity || !blob) {
result = LARDON3D_PROJECT_DB_CONSTRAINT;
} else {
parameters->task_id = task_id;
parameters->scanset_id = (uint64_t)sqlite3_column_int64(statement, 0);
parameters->next_group_id = (uint32_t)sqlite3_column_int64(statement, 1);
parameters->group_count = (uint32_t)sqlite3_column_int64(statement, 2);
memcpy(request, blob, (size_t)bytes);
parameters->request = request;
parameters->request_size = (size_t)bytes;
}
}
}
sqlite3_finalize(statement);
(void)pthread_mutex_unlock(&database->mutex);
return result;
}
Lardon3DProjectDbResult lardon3d_project_db_retain_acquisition_campaign_capture(
Lardon3DProjectDb *database, uint64_t task_id, uint32_t group_id,
uint64_t capture_id, uint32_t next_group_id) {
if (!database || !valid_task_id(task_id) || !valid_task_id(capture_id) ||
group_id == 0 || next_group_id != group_id) return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
(void)pthread_mutex_lock(&database->mutex);
Lardon3DProjectDbResult result = execute(database, "BEGIN IMMEDIATE", "begin capture retention");
sqlite3_stmt *statement = NULL;
uint32_t cursor = 0;
uint32_t group_count = 0;
uint64_t retained_capture_id = 0;
if (result == LARDON3D_PROJECT_DB_OK) {
result = prepare(database,
"SELECT t.next_group_id,t.group_count,COALESCE(m.capture_id,0),"
"EXISTS(SELECT 1 FROM captures c WHERE c.capture_id=?3 AND "
"c.scanset_id=t.scanset_id) FROM acquisition_campaign_tasks t LEFT JOIN "
"acquisition_campaign_captures m ON m.task_id=t.task_id AND m.group_id=?2 "
"WHERE t.task_id=?1", &statement);
}
if (result == LARDON3D_PROJECT_DB_OK) {
sqlite3_bind_int64(statement, 1, (sqlite3_int64)task_id);
sqlite3_bind_int64(statement, 2, group_id);
sqlite3_bind_int64(statement, 3, (sqlite3_int64)capture_id);
int code = sqlite3_step(statement);
if (code == SQLITE_DONE) result = LARDON3D_PROJECT_DB_NOT_FOUND;
else if (code != SQLITE_ROW) result = sqlite_result(database, code, "validate capture retention");
else {
cursor = (uint32_t)sqlite3_column_int64(statement, 0);
group_count = (uint32_t)sqlite3_column_int64(statement, 1);
retained_capture_id = (uint64_t)sqlite3_column_int64(statement, 2);
bool matching_scanset = sqlite3_column_int(statement, 3) == 1;
bool new_mapping = cursor + 1u == group_id && retained_capture_id == 0;
bool exact_retry = cursor == next_group_id && retained_capture_id == capture_id;
if (group_id > group_count || !matching_scanset || (!new_mapping && !exact_retry))
result = LARDON3D_PROJECT_DB_CONSTRAINT;
}
}
sqlite3_finalize(statement);
statement = NULL;
if (result == LARDON3D_PROJECT_DB_OK && retained_capture_id == 0)
result = prepare(database,
"INSERT INTO acquisition_campaign_captures(task_id,group_id,capture_id) VALUES(?1,?2,?3)",
&statement);
if (result == LARDON3D_PROJECT_DB_OK && retained_capture_id == 0) {
sqlite3_bind_int64(statement, 1, (sqlite3_int64)task_id);
sqlite3_bind_int64(statement, 2, group_id);
sqlite3_bind_int64(statement, 3, (sqlite3_int64)capture_id);
result = step_done(database, statement, "retain campaign capture");
if (result == LARDON3D_PROJECT_DB_OK && sqlite3_changes(database->connection) != 1)
result = LARDON3D_PROJECT_DB_CONSTRAINT;
}
if (result == LARDON3D_PROJECT_DB_OK && cursor + 1u == group_id) {
result = prepare(database,
"UPDATE acquisition_campaign_tasks SET next_group_id=?2 WHERE task_id=?1 AND "
"next_group_id=?3 AND group_count>=?2", &statement);
if (result == LARDON3D_PROJECT_DB_OK) {
sqlite3_bind_int64(statement, 1, (sqlite3_int64)task_id);
sqlite3_bind_int64(statement, 2, next_group_id);
sqlite3_bind_int64(statement, 3, (sqlite3_int64)group_id - 1);
result = step_done(database, statement, "advance campaign cursor");
if (result == LARDON3D_PROJECT_DB_OK && sqlite3_changes(database->connection) != 1)
result = LARDON3D_PROJECT_DB_CONSTRAINT;
}
}
if (result == LARDON3D_PROJECT_DB_OK)
result = execute(database, "COMMIT", "commit capture retention");
if (result != LARDON3D_PROJECT_DB_OK)
(void)execute(database, "ROLLBACK", "rollback capture retention");
(void)pthread_mutex_unlock(&database->mutex);
return result;
}
Lardon3DProjectDbResult lardon3d_project_db_load_acquisition_campaign_capture(
Lardon3DProjectDb *database, uint64_t task_id, uint32_t group_id,
Lardon3DProjectDbAcquisitionCampaignCapture *capture) {
if (!database || !valid_task_id(task_id) || !capture)
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
memset(capture, 0, sizeof(*capture));
(void)pthread_mutex_lock(&database->mutex);
sqlite3_stmt *statement = NULL;
Lardon3DProjectDbResult result = prepare(database,
"SELECT capture_id FROM acquisition_campaign_captures WHERE task_id=?1 AND group_id=?2",
&statement);
if (result == LARDON3D_PROJECT_DB_OK) {
sqlite3_bind_int64(statement, 1, (sqlite3_int64)task_id);
sqlite3_bind_int64(statement, 2, group_id);
int code = sqlite3_step(statement);
if (code == SQLITE_DONE) result = LARDON3D_PROJECT_DB_NOT_FOUND;
else if (code != SQLITE_ROW) result = sqlite_result(database, code, "load retained capture");
else {
capture->group_id = group_id;
capture->capture_id = (uint64_t)sqlite3_column_int64(statement, 0);
}
}
sqlite3_finalize(statement);
(void)pthread_mutex_unlock(&database->mutex);
return result;
}
static bool valid_catalog_id(uint64_t value) { return value > 0 && value <= INT64_MAX; }
static bool valid_original_name(const char *name) {
@ -4764,7 +4973,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_visual_index_update_task(
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
return record_task_internal(db, snapshot, kind, version, checkpoint, NULL, 0, NULL,
NULL, parameters, NULL, NULL, NULL, NULL, updated_at);
NULL, parameters, NULL, NULL, NULL, NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_load_visual_index_update_task(
@ -4811,7 +5020,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_candidate_pair_generate_task(
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
return record_task_internal(db, snapshot, kind, version, checkpoint, NULL, 0, NULL,
NULL, NULL, parameters, NULL, NULL, NULL, updated_at);
NULL, NULL, parameters, NULL, NULL, NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_load_candidate_pair_generate_task(
@ -4867,7 +5076,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_matcher_task(
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
return record_task_internal(db, snapshot, kind, version, checkpoint, NULL, 0, NULL,
NULL, NULL, NULL, parameters, NULL, NULL, updated_at);
NULL, NULL, NULL, parameters, NULL, NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_load_matcher_task(
@ -4925,7 +5134,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_geometric_verifier_task(
return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
}
return record_task_internal(db, snapshot, kind, version, checkpoint, NULL, 0, NULL,
NULL, NULL, NULL, NULL, parameters, NULL, updated_at);
NULL, NULL, NULL, NULL, parameters, NULL, NULL, updated_at);
}
Lardon3DProjectDbResult lardon3d_project_db_load_geometric_verifier_task(
@ -4998,7 +5207,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_track_builder_task(
}
Lardon3DProjectDbResult result = record_task_internal(
db, snapshot, kind, version, checkpoint, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
updated_at);
NULL, updated_at);
if (result != LARDON3D_PROJECT_DB_OK) return result;
(void)pthread_mutex_lock(&db->mutex);
result = execute(db, "BEGIN IMMEDIATE", "begin track builder task");
@ -5109,7 +5318,7 @@ Lardon3DProjectDbResult lardon3d_project_db_record_sparse_sfm_task(
const Lardon3DProjectDbSparseSfmTask *parameters, int64_t updated_at) {
if (!parameters) return LARDON3D_PROJECT_DB_INVALID_ARGUMENT;
return record_task_internal(database, snapshot, kind, version, checkpoint, NULL, 0, NULL, NULL,
NULL, NULL, NULL, NULL, parameters, updated_at);
NULL, NULL, NULL, NULL, parameters, NULL, updated_at);
}
static bool read_u64_blob(sqlite3_stmt *statement, int column, uint64_t *value) {

View file

@ -1,4 +1,7 @@
#include <lardon3d/candidate_pair_task.h>
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_AVAILABLE
#include <lardon3d/acquisition_campaign_task.h>
#endif
#include <lardon3d/feature_task.h>
#include <lardon3d/geometric_verifier_task.h>
#include <lardon3d/import_task.h>
@ -18,6 +21,13 @@
const Lardon3DTaskKindRegistry *lardon3d_task_kind_registry_production(void) {
static const Lardon3DTaskKindDescriptor descriptors[] = {
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_AVAILABLE
{
.kind = LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND,
.kind_version = LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND_VERSION,
.reconstruct = lardon3d_acquisition_campaign_task_reconstruct,
},
#endif
{
.kind = LARDON3D_IMAGE_IMPORT_TASK_KIND,
.kind_version = LARDON3D_IMAGE_IMPORT_TASK_KIND_VERSION,

View file

@ -0,0 +1,187 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <vector>
extern "C" {
#include <lardon3d/acquisition_campaign_task.h>
#include <lardon3d/project_db.h>
}
#define CHECK(x) \
do { \
if (!(x)) { \
std::fprintf(stderr, "failure line %d: %s\n", __LINE__, #x); \
return 1; \
} \
} while (0)
extern "C" const Lardon3DTaskKindRegistry *
lardon3d_task_kind_registry_production(void) {
return nullptr;
}
int main() {
Lardon3DAcquisitionCampaignSource sources[2]{};
std::snprintf(sources[0].path, sizeof(sources[0].path), "/input/a.arw");
std::snprintf(sources[1].path, sizeof(sources[1].path), "/input/a.jpg");
sources[0].source_kind = sources[0].metadata.source_kind =
LARDON3D_ACQUISITION_SOURCE_RAW;
sources[1].source_kind = sources[1].metadata.source_kind =
LARDON3D_ACQUISITION_SOURCE_JPEG;
for (auto &source : sources) {
source.metadata_result = LARDON3D_ACQUISITION_OK;
source.metadata.policy_version =
LARDON3D_ACQUISITION_PAIRING_POLICY_VERSION;
}
Lardon3DAcquisitionCampaignConfirmation confirmation{};
confirmation.source_count = 2;
confirmation.source_indices[0] = 0;
confirmation.source_indices[1] = 1;
Lardon3DAcquisitionCampaignTaskRequest input{};
input.sources = sources;
input.source_count = 2;
input.confirmations = &confirmation;
input.confirmation_count = 1;
input.ingest_options.representation = LARDON3D_ACQUISITION_SELECT_DEVELOP_RAW;
input.ingest_options.select_representation = 1;
input.ingest_options.imported_at = 123;
input.ingest_options.max_source_bytes = 456;
size_t size = 0;
CHECK(
lardon3d_acquisition_campaign_request_encode(&input, nullptr, 0, &size));
CHECK(size > 32);
std::vector<unsigned char> encoded(size);
CHECK(lardon3d_acquisition_campaign_request_encode(&input, encoded.data(),
encoded.size(), &size));
Lardon3DAcquisitionCampaignSource decoded_sources[2]{};
Lardon3DAcquisitionCampaignConfirmation decoded_confirmations[2]{};
Lardon3DAcquisitionCampaignTaskRequest decoded{};
CHECK(lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size(), decoded_sources, 2, decoded_confirmations,
2, &decoded));
CHECK(decoded.source_count == 2 && decoded.confirmation_count == 1);
CHECK(decoded_confirmations[0].source_count == 2);
CHECK(decoded.ingest_options.representation ==
LARDON3D_ACQUISITION_SELECT_DEVELOP_RAW);
CHECK(decoded.ingest_options.select_representation == 1);
CHECK(decoded.ingest_options.imported_at == 123 &&
decoded.ingest_options.max_source_bytes == 456);
CHECK(std::strcmp(decoded_sources[0].path, "/input/a.arw") == 0);
CHECK(!lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size(), nullptr, 2, decoded_confirmations, 2,
&decoded));
CHECK(!lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size(), decoded_sources, 2, nullptr, 2,
&decoded));
constexpr size_t imported_at_offset = 8u + 5u * sizeof(uint32_t);
for (size_t i = 0; i < sizeof(uint64_t); ++i)
encoded[imported_at_offset + i] = 0xffu;
CHECK(!lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size(), decoded_sources, 2, decoded_confirmations,
2, &decoded));
for (size_t i = 0; i < sizeof(uint64_t); ++i)
encoded[imported_at_offset + i] =
static_cast<unsigned char>(uint64_t(123) >> (8u * i));
input.ingest_options.imported_at = -1;
CHECK(!lardon3d_acquisition_campaign_request_encode(&input, nullptr, 0,
&size));
input.ingest_options.imported_at = 123;
input.ingest_options.max_source_bytes = 0;
CHECK(!lardon3d_acquisition_campaign_request_encode(&input, nullptr, 0,
&size));
input.ingest_options.max_source_bytes = 456;
encoded[8] = 2;
CHECK(!lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size(), decoded_sources, 2, decoded_confirmations,
2, &decoded));
encoded[8] = 1;
CHECK(!lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size() - 1, decoded_sources, 2,
decoded_confirmations, 2, &decoded));
CHECK(!lardon3d_acquisition_campaign_request_decode(
encoded.data(), encoded.size(), decoded_sources, 1, decoded_confirmations,
2, &decoded));
char database_path[] = "/tmp/lardon3d-campaign-task-XXXXXX";
int descriptor = mkstemp(database_path);
CHECK(descriptor >= 0 && close(descriptor) == 0 &&
unlink(database_path) == 0);
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY]{};
Lardon3DProjectDb *database = nullptr;
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DProjectDbScanSet target{}, other{};
CHECK(lardon3d_project_db_create_scanset(database, "target", &target) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_create_scanset(database, "other", &other) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DTaskDurableSnapshot snapshot{};
snapshot.id = 1;
std::snprintf(snapshot.name, sizeof(snapshot.name), "campaign");
snapshot.saved_state = snapshot.recovery_state = TASK_PENDING;
Lardon3DProjectDbAcquisitionCampaignTask durable{
1, target.scanset_id, 0, 2, encoded.data(), encoded.size()};
CHECK(lardon3d_project_db_record_acquisition_campaign_task(
database, &snapshot, LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND,
LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND_VERSION, nullptr, &durable,
1) == LARDON3D_PROJECT_DB_OK);
std::vector<unsigned char> changed = encoded;
changed.back() ^= 1u;
snapshot.progress = 50;
durable.request = changed.data();
CHECK(lardon3d_project_db_record_acquisition_campaign_task(
database, &snapshot, LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND,
LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND_VERSION, nullptr, &durable,
2) == LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DProjectDbTask generic{};
CHECK(lardon3d_project_db_load_task(database, 1, &generic) ==
LARDON3D_PROJECT_DB_OK &&
generic.progress == 0);
snapshot.progress = 0;
durable.request = encoded.data();
Lardon3DProjectDbCapture first{}, wrong{}, second{};
CHECK(lardon3d_project_db_create_capture(database, target.scanset_id, 1,
&first) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_create_capture(database, other.scanset_id, 1,
&wrong) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_create_capture(database, target.scanset_id, 1,
&second) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 1, 1, wrong.capture_id, 1) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 1, 2, second.capture_id, 2) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 1, 1, first.capture_id, 1) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 1, 1, first.capture_id, 1) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 1, 1, second.capture_id, 1) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
std::vector<unsigned char> loaded(encoded.size());
Lardon3DProjectDbAcquisitionCampaignTask loaded_task{};
CHECK(lardon3d_project_db_load_acquisition_campaign_task(
database, 1, loaded.data(), loaded.size(), &loaded_task) ==
LARDON3D_PROJECT_DB_OK);
CHECK(loaded_task.next_group_id == 1 && loaded_task.group_count == 2 &&
loaded_task.request_size == encoded.size() &&
std::memcmp(loaded.data(), encoded.data(), encoded.size()) == 0);
std::vector<char> overlong_project_path(LARDON3D_APP_STATE_PATH_CAPACITY + 1,
'x');
overlong_project_path.back() = '\0';
Lardon3DTaskReconstructionContext reconstruction{
overlong_project_path.data(), database, nullptr, nullptr};
Lardon3DTaskKindBinding binding{};
CHECK(!lardon3d_acquisition_campaign_task_reconstruct(
&snapshot, &reconstruction, &binding));
lardon3d_project_db_close(database);
CHECK(unlink(database_path) == 0);
return 0;
}

View file

@ -202,7 +202,7 @@ static bool test_model_api(const char *path) {
Lardon3DProjectDb *database = NULL;
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
Parents parents;
CHECK(create_parents(database, &parents));
@ -486,7 +486,7 @@ static bool test_migration(const char *v11_path, const char *failed_path) {
0));
CHECK(lardon3d_project_db_open(v11_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
Parents parents;
CHECK(create_parents(database, &parents));
unsigned char fingerprint[32] = {0x91};
@ -501,7 +501,7 @@ static bool test_migration(const char *v11_path, const char *failed_path) {
migrated_result.geometric_verification_result_id;
lardon3d_project_db_close(database);
CHECK(query_integer(
v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(
query_integer(v11_path,
"SELECT count(*) FROM sqlite_master WHERE type='index' AND "
@ -530,7 +530,7 @@ static bool test_migration(const char *v11_path, const char *failed_path) {
0));
CHECK(lardon3d_project_db_open(failed_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
return true;
}

View file

@ -369,7 +369,7 @@ static bool run_task_test(void) {
0));
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
CHECK(remove_tree(root));
return true;

View file

@ -124,7 +124,7 @@ static bool run_test(void) {
Lardon3DProjectDb *database = NULL;
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(database && lardon3d_project_db_schema_version(database) == 19);
CHECK(database && lardon3d_project_db_schema_version(database) == 20);
Lardon3DProjectDbScanSet scanset;
CHECK(lardon3d_project_db_create_scanset(database, "Match-test", &scanset) ==
@ -501,7 +501,7 @@ static bool run_test(void) {
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
/* Verify persistence: load previously created results */
CHECK(lardon3d_project_db_load_match_result(database, first_id, &loaded) ==
@ -544,11 +544,11 @@ static bool run_test(void) {
v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 9));
CHECK(lardon3d_project_db_open(v9_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(
v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(
query_integer(v9_path,
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
@ -576,7 +576,7 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(failed_v10_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;

View file

@ -373,7 +373,7 @@ static bool run_test(void) {
"name='matcher_tasks'",
0));
CHECK(reopen_runtime(&fixture));
CHECK(lardon3d_project_db_schema_version(fixture.state.project_db) == 19);
CHECK(lardon3d_project_db_schema_version(fixture.state.project_db) == 20);
CHECK(
query_integer(database_path,
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "

View file

@ -116,7 +116,7 @@ static bool create_future_database(const char *path) {
sqlite3_exec(
connection,
"CREATE TABLE metadata(key TEXT PRIMARY KEY,value INTEGER NOT NULL);"
"INSERT INTO metadata VALUES('schema_version',20);",
"INSERT INTO metadata VALUES('schema_version',21);",
NULL, NULL, NULL) == SQLITE_OK;
return sqlite3_close(connection) == SQLITE_OK && ok;
}
@ -940,7 +940,7 @@ static bool run_test(void) {
Lardon3DProjectDb *database = NULL;
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(database && lardon3d_project_db_schema_version(database) == 19);
CHECK(database && lardon3d_project_db_schema_version(database) == 20);
bool legacy_pending = true;
CHECK(lardon3d_project_db_legacy_catalog_pending(database, &legacy_pending) ==
LARDON3D_PROJECT_DB_OK &&
@ -1499,7 +1499,7 @@ static bool run_test(void) {
database = NULL;
CHECK(query_integer(database_path,
"SELECT value FROM metadata WHERE key='schema_version'",
19));
20));
CHECK(query_integer(database_path,
"SELECT count(*) FROM tasks WHERE task_id=1", 1));
@ -1521,12 +1521,13 @@ static bool run_test(void) {
"created_at) VALUES(1,1,1,1,1,zeroblob(32),1,2,1,0,0,1);"
"DROP TABLE incremental_reconstruction_tasks;"
"DROP TABLE incremental_reconstructions;"
"DROP TABLE acquisition_campaign_captures;DROP TABLE acquisition_campaign_tasks;"
"DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;"
"DROP TABLE capture_images;DROP TABLE captures;"
"UPDATE metadata SET value=17 WHERE key='schema_version';"));
CHECK(lardon3d_project_db_open(v17_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(v17_path,
@ -1550,6 +1551,7 @@ static bool run_test(void) {
database = NULL;
CHECK(execute_test_sql(
failed_v19_path,
"DROP TABLE acquisition_campaign_captures;DROP TABLE acquisition_campaign_tasks;"
"DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;"
"DROP TABLE capture_images;DROP TABLE captures;"
"UPDATE metadata SET value=18 WHERE key='schema_version';"));
@ -1564,13 +1566,14 @@ static bool run_test(void) {
"name='captures'", 0));
CHECK(lardon3d_project_db_open(failed_v19_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(execute_test_sql(
failed_v18_path,
"DROP TABLE incremental_reconstruction_tasks;"
"DROP TABLE incremental_reconstructions;"
"DROP TABLE acquisition_campaign_captures;DROP TABLE acquisition_campaign_tasks;"
"DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;"
"DROP TABLE capture_images;DROP TABLE captures;"
"UPDATE metadata SET value=17 WHERE key='schema_version';"));
@ -1615,7 +1618,7 @@ static bool run_test(void) {
CHECK(create_v1_database(legacy_path));
CHECK(lardon3d_project_db_open(legacy_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
CHECK(lardon3d_project_db_get_project(database, &loaded_project) ==
LARDON3D_PROJECT_DB_OK &&
strcmp(loaded_project.stable_id, "legacy-project") == 0);
@ -1630,7 +1633,7 @@ static bool run_test(void) {
database = NULL;
CHECK(query_integer(legacy_path,
"SELECT value FROM metadata WHERE key='schema_version'",
19));
20));
CHECK(create_v1_database(failed_migration_path));
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V2", "1", 1) == 0);
@ -1661,7 +1664,7 @@ static bool run_test(void) {
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(
v2_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v2_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(create_v2_database(failed_v3_migration_path));
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V3", "1", 1) == 0);
@ -1695,7 +1698,7 @@ static bool run_test(void) {
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(
v3_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v3_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(create_v3_database(failed_v4_path));
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V4", "1", 1) == 0);
@ -1717,7 +1720,7 @@ static bool run_test(void) {
fprintf(stderr, "Migration v4 (%d): %s\n", (int)v4_result, error);
}
CHECK(v4_result == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
CHECK(lardon3d_project_db_load_task(database, 9, &task) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_load_artifact(database, "legacy-artifact",
@ -1772,7 +1775,7 @@ static bool run_test(void) {
error);
}
CHECK(retry_v7 == LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
@ -1782,7 +1785,7 @@ static bool run_test(void) {
5));
CHECK(lardon3d_project_db_open(direct_v5_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
@ -1791,11 +1794,11 @@ static bool run_test(void) {
v8_path, "SELECT value FROM metadata WHERE key='schema_version'", 7));
CHECK(lardon3d_project_db_open(v8_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(
v8_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v8_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(create_v7_database(failed_v8_path));
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V8", "1", 1) == 0);
@ -1812,7 +1815,7 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(failed_v8_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
@ -1831,11 +1834,11 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(v10_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(
v10_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v10_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(
query_integer(v10_path,
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
@ -1862,7 +1865,7 @@ static bool run_test(void) {
1));
CHECK(lardon3d_project_db_open(failed_v11_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
@ -1876,11 +1879,11 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(v13_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(query_integer(
v13_path, "SELECT value FROM metadata WHERE key='schema_version'", 19));
v13_path, "SELECT value FROM metadata WHERE key='schema_version'", 20));
CHECK(
query_integer(v13_path,
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
@ -1903,7 +1906,7 @@ static bool run_test(void) {
true_v14_path);
}
CHECK(true_v14_result == LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(
@ -2018,7 +2021,7 @@ static bool run_test(void) {
if (true_v15_result != LARDON3D_PROJECT_DB_OK)
fprintf(stderr, "true v15 upgrade: %d %s\n", true_v15_result, error);
CHECK(true_v15_result == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
Lardon3DProjectDbProject migrated_project;
CHECK(lardon3d_project_db_get_project(database, &migrated_project) ==
LARDON3D_PROJECT_DB_OK &&
@ -2064,7 +2067,7 @@ static bool run_test(void) {
database = NULL;
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
CHECK(schema_compare(database_path, true_v15_path, true));
@ -2099,7 +2102,7 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(true_v15_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
@ -2118,7 +2121,7 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(failed_v14_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;
@ -2137,7 +2140,7 @@ static bool run_test(void) {
0));
CHECK(lardon3d_project_db_open(failed_v15_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) == 19);
lardon3d_project_db_schema_version(database) == 20);
lardon3d_project_db_close(database);
database = NULL;

View file

@ -123,7 +123,7 @@ static bool run_test(void) {
Lardon3DProjectDb *db = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &db, error) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(db) == 19);
CHECK(lardon3d_project_db_schema_version(db) == 20);
Lardon3DProjectDbScanSet scanset;
CHECK(lardon3d_project_db_create_scanset(db, "Sparse model", &scanset) ==

View file

@ -66,7 +66,7 @@ int main() {
Lardon3DProjectDb *database = nullptr;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY]{};
CHECK(lardon3d_project_db_open(path.c_str(), &database, error) == LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) == 19);
CHECK(lardon3d_project_db_schema_version(database) == 20);
sqlite3 *raw = nullptr;
CHECK(sqlite3_open(path.c_str(), &raw) == SQLITE_OK);