Le catalogue des types

Module d’exemple : hellotypes.

Les types d’élément se répartissent en deux familles.

Types forcément en lecture seule côté interface

ElementLight (indicateur d’état), ElementImg (image), ElementVideo (flux vidéo), ElementPrg (barre de progression ou spinner) — aucun d’entre eux n’a de sens en écriture directe depuis un client.

Image : la convention webroot

Un module écrit son fichier dans getWebroot() + "/" + getModuleName() + "/...", et donne à l’élément une URL relative à cet emplacement :

QDir dir;
dir.mkdir(getWebroot() + "/" + getModuleName());
atomicSaveJpeg(img, getWebroot() + "/" + getModuleName() + "/photo.jpeg");

OST::ImgData data;
data.mUrlJpeg = getModuleName() + "/photo.jpeg";
getEltImg("display", "photo")->setValue(data, true);
getWebroot() n’est pas encore défini dans le constructeur

Le contrôleur n’appelle setWebroot() sur un module qu’après le retour de son constructeur. Tout code qui écrit un fichier dépendant du webroot doit donc être différé à l’itération suivante de la boucle d’événements :

QTimer::singleShot(0, this, [this]() {
    // getWebroot() est maintenant garanti défini.
});

ElementVideo suit exactement la même convention d’URL (OST::VideoData::url), pour un flux déjà existant par ailleurs.

Barre de progression et spinner : tous deux pilotés par une valeur

ElementPrg a deux présentations (prgtype: "bar" ou "spinner"), mais les deux sont déterminées par une valeur 0-100 — un spinner n’est pas un indicateur d’activité indéterminé, c’est un anneau dont le remplissage suit setPrgValue(), exactement comme la barre :

getEltPrg("display", "progress")->setPrgValue(42.0, true);
getEltPrg("display", "spinner")->setPrgValue(42.0, true);   // oui, le spinner aussi
getEltPrg("display", "spinner")->setDynLabel("En cours...", true);

Types normalement éditables

ElementBool, ElementString, ElementInt, ElementFloat, ElementDate, ElementTime, ElementDateTime — tous peuvent être verrouillés en lecture seule au niveau de leur propriété (permission:0), indépendamment de tout réglage par élément (directedit, autoupdate) : le contrôle de permission est le premier vérifié, avant tout le reste.

Contraintes numériques et infobulle

Pour int/float, quatre clés JSON optionnelles supplémentaires :

"gain": { "type": "float", "directedit": true, "min": 0, "max": 100, "step": 1, "format": "%.0f", "hint": "Gain capteur" }
  • min/max : bornes acceptées.
  • step : incrément du contrôle côté client.
  • format : format d’affichage (printf-style).
  • hint : infobulle — disponible sur tous les types d’élément, pas seulement numériques.

Mélanger les types dans une seule propriété

Contrairement à INDI, où un vecteur ne contient qu’un seul type d’élément, une propriété OST peut librement mélanger des types différents :

"editabledata": {
  "permission": 2,
  "e": {
    "bool1":   { "type": "bool",   "directedit": true },
    "string1": { "type": "string", "directedit": true },
    "int1":    { "type": "int" },
    "float1":  { "type": "float" },
    "date1":   { "type": "date",   "directedit": true }
  }
}

directedit:true vs directedit:false

  • directedit:true : le client édite l’élément individuellement et l’envoie seul (SV) — appliqué immédiatement.
  • directedit:false : l’élément ne peut être modifié que via un envoi groupé de toute la propriété (SA) — tous les éléments éditables de la propriété doivent être inclus dans cet envoi, sinon rien n’est appliqué. Côté client type, ça correspond à deux présentations différentes : un contrôle d’édition immédiate pour chaque élément directedit:true, et un formulaire avec bouton “Enregistrer” regroupant tous les éléments directedit:false.

Cette distinction est indépendante du type : dans l’exemple ci-dessus, bool1/string1/date1 s’éditent immédiatement, int1/float1 seulement via le formulaire groupé.