NRF SDK 0.9

Lire et agir

Observer les trains : requêtes, identités et états

Construire des fiches de trains avec les seuls groupes utiles et conserver le sens des données inconnues.

Choisir le point d’entrée

Vous allez obtenir un lot de trains, joindre leurs informations puis préparer des valeurs d’affichage. Un outil Native doit déjà recevoir un ToolContext actif ; une application JVM doit déjà posséder un Game ouvert. Les résultats sont des copies conservables, mais le ToolContext appartient uniquement au callback qui l’a fourni.

BesoinNativeJVM
Lot de données de trainscontext.trains(query)game.trains.snapshot(query = query)
Joindre un train identifiésnapshot[trainId]snapshot.train(trainId)
Plan de ligne d’un traincontext.linePlan(trainId)game.trains.snapshot(selectedTrain = trainId, query = query)
Mesure de conduite cibléeObservations du callback de conduitegame.trains.read(trainId)

Composer une requête selon l’écran

TrainQuery choisit les données supplémentaires. Déclarez ensemble tous les besoins du calcul puis réutilisez le lot. Les valeurs par défaut incluent le service et les lieux ; désactivez-les pour un écran qui ne les utilise pas. En Native, une requête déjà couverte dans le callback réutilise sa copie ; une demande plus riche peut déclencher une nouvelle lecture.

OptionDéfautCe qu’elle demande
includeServicetrueService et état opérationnel associés au train.
includeLocationstrueVoies et gares pour les jointures de localisation ; ne demande pas la table des quais.
includeTimetablesfalseAffectation et échéances ; implique le service et les voies/gares même si includeLocations vaut false.
includeLinesfalseCatalogue des lignes, identités et relations parentales disponibles.
includeTagsfalseTags et déclarations des lignes ; implique le catalogue des lignes.
includeCharacteristicsfalseCaractéristiques des profils configuré et actuel.
includeCompositionfalseComposition ordonnée et catalogue des modèles de véhicules.
includePassengersfalseNombre observé d’occupants ; indépendant de la capacité.

La capture spécialisée ne demande pas la carte complète, les signaux, les quais, les occupations, les réservations ou le chemin réservé. Des tables de réseau vides dans ce profil ne prouvent donc pas que le monde est vide. Pour les quais en JVM, demandez game.snapshot() et utilisez Observation.platform. Demandez le réseau uniquement si votre fonctionnalité en a besoin.

Faire les jointures sans perdre les inconnues

TrainId, LineId, StationId, TrackId, TimetableId, TagId et VehicleModelId expriment la nature d’une identité. Leur value est opaque : ne la découpez pas et ne la remplacez pas par un nom. Utilisez les recherches indexées du lot : get et line en Native, train, line et station en JVM. Les véhicules portent directement leur model ; inutile de le rechercher pour chaque fiche.

SituationTraitement attendu
Groupe non demandéNe pas afficher « aucun ». Demander ce groupe si l’écran en a besoin.
Objet ou propriété nullable absentConserver « inconnu » ou « indisponible » ; la requête ne suffit pas à garantir sa présence.
Liste nullable obtenue et videAucun élément dans cette liste observée ; ce constat ne s’étend pas aux tables non demandées.
Identité présente, jointure absenteConserver l’identité et afficher un libellé de remplacement sans fabriquer l’objet joint.

En Native, Train rassemble les parties disponibles : une demande de localisation peut fournir un TrainService partiel avec présence et lieux, sans état de service. Les demandes du callback se cumulent ; une option false ne retire pas un groupe déjà lu. En JVM, Observation.train(id) retourne un TrainRecord regroupant train, service, details et metadata ; le service y reste absent si son groupe n’a pas été demandé. La présence d’un train ne garantit donc pas toutes ses parties enrichies.

Distinguer mouvement, état et alerte

La vitesse mesurée décrit le mouvement. TrainState décrit un état opérationnel et TrainAlert un signalement ; aucun de ces champs ne constitue une permission de franchir un signal. Interprétez chaque enum dans son domaine, sans attribuer un ordre de gravité à sa position dans entries.

ValeurSens
nullDonnée indisponible ou non demandée à cet emplacement.
UnknownLe jeu rapporte explicitement son état inconnu.
OtherUne valeur observée ne correspond pas aux catégories nommées du SDK.
TrainAlert.NoneAucune alerte rapportée ; ce n’est pas une autorisation de mouvement.

Construire des fiches prêtes à afficher

Native : appeler readTrainCards dans le callback de l’outil
package wiki.trainqueries

import nimby.*

data class TrainCard(
    val id: TrainId,
    val name: String,
    val speedKmh: Double?,
    val state: TrainState?,
    val alert: TrainAlert?,
    val positionStation: Station?,
    val serviceStation: Station?,
)

// Appeler depuis un callback d'outil ; ne pas conserver son ToolContext.
fun readTrainCards(context: ToolContext): List<TrainCard> {
    val snapshot = context.trains(TrainQuery())
    return snapshot.trains.map { train ->
        TrainCard(
            train.trainId, train.name, train.speedKmh,
            train.service?.state, train.service?.alert,
            train.position?.station, train.service?.locationStation,
        )
    }
}

// null reste inconnu ; une vitesse nulle connue vaut bien 0.0.
fun measuredSpeedKmh(card: TrainCard): Double? = card.speedKmh
JVM : réutiliser le Game de l’application
package wiki.jvmtrainqueries

import fr.nimby.sdk.*
import java.nio.file.Path

data class TrainCard(
    val id: TrainId,
    val name: String,
    val speedKmh: Double?,
    val state: TrainState?,
    val alert: TrainAlert?,
    val positionStation: Station?,
    val serviceStation: Station?,
)

fun readTrainCards(game: Game): List<TrainCard> {
    val snapshot = game.trains.snapshot(query = TrainQuery())
    return snapshot.trains.mapNotNull { train ->
        val record = snapshot.train(train.trainId) ?: return@mapNotNull null
        TrainCard(
            train.trainId, train.name, train.speedKmh,
            record.service?.state, record.service?.alertState,
            record.positionStation, record.locationStation,
        )
    }
}

// Exemple ponctuel ; une application conserve sa connexion entre deux lectures.
fun readOnce(sdkLibrary: Path, gamePid: Int): List<TrainCard> =
    Nimby.connect(sdk = sdkLibrary, processId = gamePid).use(::readTrainCards)

Les fonctions renvoient des TrainCard composées uniquement de valeurs copiées. La gare de position et la gare du service sont deux informations distinctes : un train peut rouler entre gares tout en ayant une destination de service. L’interface peut afficher un tiret pour une valeur inconnue sans modifier le résultat SDK.

Conserver une copie sans la croire actuelle

Une copie reste lisible après le callback ou la lecture, mais elle ne se met pas à jour. En Native, worldId et generation décrivent son contexte ; capturedAtMillis date la capture et ageMillis est son âge au moment de la copie, pas un compteur qui progresse. Effacez les calculs associés lors d’un changement de monde ou de génération.

En JVM, conservez les jointures à l’intérieur d’un même Observation et invalidez votre cache lors d’un changement de partie ou de connexion. processId et gameHash donnent un contexte de processus et de version ; ils ne sont pas une identité durable de sauvegarde. Une opération ultérieure doit vérifier ses propres préconditions actuelles.