Reading and acting
Read one train without scanning the map
Build a targeted speed indicator and understand the timing limits of its measurement.
Start with the question to display
An already connected JVM application can read one train’s driving data with game.trains.read(TrainId). This suits a speed indicator, position display or comparison with material capabilities. Obtain the identity from an observation of the selected game; a train name does not uniquely identify an object.
| Need | Call | Result |
|---|---|---|
| One train’s driving measurement | game.trains.read(id) | DrivingObservation? |
| Services, tags or material for several trains | game.trains.snapshot(query = ...) | Observation |
| Topology, occupation and other map tables | game.snapshot(selectedTrain = ...) | Observation |
package wiki.targetedtrain
import fr.nimby.sdk.Game
import fr.nimby.sdk.TrainId
data class SpeedSample(
val train: TrainId,
val generation: Long,
val capturedAtMillis: Long,
val speedKmh: Double?,
val currentMaximumKmh: Double?,
)
fun readSpeed(game: Game, id: TrainId): SpeedSample? {
val observation = game.trains.read(id) ?: return null
val measured = observation.speedMps.takeUnless { observation.speedDefaulted }
return SpeedSample(
id, observation.sessionGeneration, observation.capturedAtMillis,
measured?.times(3.6), observation.currentDynamics?.maxSpeedMps?.times(3.6),
)
}
readSpeed returns null when the train is unavailable. Within a returned card, speedKmh may still be unknown. currentMaximumKmh is the current material capability; it is neither measured speed nor permission to pass the next signal. Multiplying by 3.6 here converts metres per second to kilometres per hour.
Display a dated observation, not a permanent certainty
- null indicates an absent or unstable observation. Do not turn it into a stopped or deleted train.
- speedDefaulted indicates that a fallback speed was used. Preserve the unknown instead of displaying a definite zero.
- elapsedBeginMillis and elapsedEndMillis bound the read in simulation time. The result is not an atomic photograph of a single tick.
- capturedAtMillis dates the read using the computer clock. It measures neither train delay nor game time.
- sessionGeneration belongs to the connection. Clear derived values on every reconnect and generation change; comparing the number across connections is insufficient.
Refresh at the frequency useful to your screen, with at most one read in flight per stream. Reuse the copied value across its widgets. Concurrent calls on one Game do not guarantee parallel reads; calculate and format copied data outside the reading path.