NRF SDK 0.9

Lire et agir

Lire les horaires et le retard des trains

Séparer affectation, échéances simulées, retard prévu et offsets du plan de ligne.

Identifier ce que le train doit exécuter

Demandez TrainQuery(includeTimetables = true), qui demande aussi le service et les voies/gares nécessaires. L’affectation décrit horaire, service horaire et index d’ordre lorsqu’ils sont disponibles. La ligne est jointe depuis le service observé, qui décrit l’activité en cours ; ces informations répondent à des questions différentes.

DonnéeContrat
TimetableIdentité de l’horaire affecté. Son nom n’est pas résolu actuellement : name reste null.
TimetableShiftIdIdentité du service à l’intérieur d’un horaire. Conserver ensemble timetableId et value.
orderIndexIndex d’ordre observé, éventuellement absent ; ne pas le confondre avec un index de gare.
Gare de serviceLieu lié au service courant ; différent de la gare de position physique.

Convertir les échéances sans mélanger les horloges

Les échéances du service sont des instants simulés. En Native, train.service?.times regroupe les valeurs brutes et leurs GameInstant. En JVM, record.service fournit directement les valeurs et leurs Instant. Les accesseurs de date traitent l’origine simulée ; ne passez pas directement arrivalTimeUs à une conversion depuis 1970.

AccesseurCe qu’il représente
observedAtInstant simulé associé à l’observation du service.
arrivalÉchéance d’arrivée du service, si disponible.
departureÉchéance de départ du service, si disponible.
dispatchRetryÉchéance de nouvelle tentative de départ, distincte du départ prévu.
arrivalRemainingSecondsDurée signée jusqu’à l’arrivée ; peut être négative.
departureRemainingSecondsDurée avant départ bornée à zéro une fois l’échéance passée.
dispatchRetryRemainingSeconds (Native) / dispatchRemainingSeconds (JVM)Durée avant nouvelle tentative de départ, bornée à zéro.

GameInstant conserve les secondes UTC et la microseconde. Sa conversion dateTime produit un calendrier à la seconde ; gardez GameInstant pour la précision fine. Les durées ne sont pas des dates et l’horloge réelle capturedAtMillis ne doit pas servir à soustraire une échéance simulée.

Utiliser l’estimation de retard pour ce qu’elle mesure

predictedArrivalDelaySeconds est l’estimation signée fournie par le jeu : positive pour du retard, négative pour de l’avance. Elle provient de predictedArrivalDelayUs, une durée en microsecondes. Ce n’est pas une différence à recalculer entre l’heure réelle et arrival, ni une garantie sur l’arrivée future.

Lire le plan de ligne sans inventer un passage absolu

Native expose context.linePlan(trainId), un LinePlan nullable dans la capture du callback. Demandez d’abord le lot nécessaire et évitez de renouveler le réseau entre les lectures liées. En JVM, selectedTrain ajoute lineStops au lot spécialisé. Un plan absent peut être indisponible ou instable ; un point de passage hors gare peut avoir stationId = null.

arrivalOffsetSeconds et departureOffsetSeconds sont relatifs au plan de ligne. plannedDwellSeconds calcule leur différence lorsque les deux existent. Une course partielle, une boucle ou l’affectation du train empêche de transformer ces offsets seuls en date de prochain passage : affichez-les comme des offsets de plan.

Préparer une fiche horaire sans valeurs inventées

Native : horaire et plan dans le contexte de l’outil
package wiki.traintimetables

import nimby.*

data class TrainTiming(
    val id: TrainId,
    val timetable: Timetable?,
    val shift: TimetableShiftId?,
    val orderIndex: Int?,
    val observedAt: GameInstant?,
    val arrival: GameInstant?,
    val departure: GameInstant?,
    val dispatchRetry: GameInstant?,
    val predictedArrivalDelaySeconds: Double?,
    val plan: LinePlan?,
)

fun readTiming(context: ToolContext, id: TrainId): TrainTiming? {
    // Une seule requête riche avant de lire le plan dans cette capture.
    val snapshot = context.trains(TrainQuery(includeTimetables = true))
    val train = snapshot[id] ?: return null
    val times = train.service?.times
    val plan = context.linePlan(id)
    return TrainTiming(
        id, train.assignment?.timetable, train.assignment?.shift,
        train.assignment?.orderIndex, times?.observedAt, times?.arrival,
        times?.departure, times?.dispatchRetry,
        train.predictedArrivalDelaySeconds, plan,
    )
}

// Conserver les offsets ; ne pas fabriquer une date de prochain passage.
fun plannedDwells(plan: LinePlan): List<Pair<Int, Long?>> =
    plan.stops.map { it.index to it.plannedDwellSeconds }
JVM : plan du train sélectionné et jointures de la capture
package wiki.jvmtraintimetables

import fr.nimby.sdk.*
import java.time.Instant

data class PlannedStop(
    val index: Int,
    val station: Station?,
    val arrivalOffsetSeconds: Int?,
    val departureOffsetSeconds: Int?,
    val dwellSeconds: Long?,
)

data class TrainTiming(
    val id: TrainId,
    val timetable: Timetable?,
    val shift: TimetableShiftId?,
    val orderIndex: Int?,
    val observedAt: Instant?,
    val arrival: Instant?,
    val departure: Instant?,
    val dispatchRetry: Instant?,
    val predictedArrivalDelaySeconds: Double?,
    val stops: List<PlannedStop>?,
)

fun readTiming(game: Game, id: TrainId): TrainTiming? {
    // selectedTrain ajoute son plan ; la liste trains reste une capture par lot.
    val snapshot = game.trains.snapshot(
        selectedTrain = id,
        query = TrainQuery(includeTimetables = true),
    )
    val record = snapshot.train(id) ?: return null
    val service = record.service
    val stops = snapshot.lineStops?.map { stop ->
        PlannedStop(
            stop.index, stop.station?.let(snapshot::station),
            stop.arrivalOffsetSeconds, stop.departureOffsetSeconds,
            stop.plannedDwellSeconds,
        )
    }
    return TrainTiming(
        id, record.details?.timetable, record.details?.shift,
        record.details?.orderIndex, service?.observedAt, service?.arrival,
        service?.departure, service?.dispatchRetry,
        record.metadata?.predictedArrivalDelaySeconds, stops,
    )
}

readTiming conserve chaque absence, les identités composées et la nature des instants. Affichez séparément le retard prévu, les échéances du service et le plan. Les modèles PlannedStop et TrainTiming peuvent être testés avec des valeurs connues, absentes, anticipées ou déjà passées sans lancer le jeu.