A small deps.edn Clojure project for joining an HLA 1516e federation from a REPL, subscribing to interactions, and inspecting received traffic interactively.
The HLA 1516e API is declared as the Maven dependency nl.tno/rti1516e.
You may still need an RTI implementation available at runtime, depending on how
your local RTI is installed and how RtiFactoryFactory discovers it. If you want
to use the vendored Portico jar, include the :portico alias. The user can add a :pitch
alias to their ~/.clojure/deps.edn to include the Pitch pRTI jar if they have it installed:
The vendored Portico jar is built from the Yet Analytics Portico fork:
make vendor/portico-local.jarTo force a rebuild:
make vendor-porticoBy default this builds yetanalytics/portico at yet_patch_object_subs. You can
build from a local checkout or override the source ref/repository:
make vendor-portico PORTICO_SRC=/Users/milt/projects/portico
make vendor-portico PORTICO_REF=my-branch
make vendor-portico PORTICO_REPO_URL=https://github.com/openlvc/portico.git PORTICO_REF=v3.0.0{:aliases
{:pitch
{:extra-paths ["/path/to/prti1516e/lib/prti1516e.jar"
"/path/to/prti1516e/lib/prticore.jar"]
:jvm-opts ["-Djava.library.path=/path/to/prti1516e/lib"]}}}
clojure -M:replTo run with the vendored Portico implementation:
clojure -M:portico:replPortico also needs a Portico RTI process running. Start it in a separate terminal before connecting from the REPL:
clojure -M:portico:portico-rtiThe project-local RTI.rid configures that RTI on loopback TCP and configures
federates to connect to the same local endpoint.
To run with the Pitch pRTI implementation:
clojure -M:pitch:replFrom the REPL:
(require '[hla-federepl.core :as hla])
(def system
(hla/start {:local-settings-designator ""
:fom-path "/absolute/path/to/fom.xml"
:federation-name "Federation"
:federate-name "federepl"
:interactions ["HLAinteractionRoot.SomeInteraction"]}))
(:config system)
(hla/status system)
(hla/events system)
(hla/latest system)
(hla/event-values system)
(hla/latest-values system)
(hla/subscribe! system "HLAinteractionRoot.AnotherInteraction")
(hla/stop! system)start does not create or destroy the federation by default, which is usually
the safer behavior when attaching to an already-running simulation.
To create the federation if needed:
(hla/start {... :create-federation? true})To destroy then recreate when possible:
(hla/start {... :recreate-federation? true})To request federation destruction on shutdown:
(hla/stop! system {:destroy-federation? true})Start the RTI process first:
clojure -M:portico:portico-rtiThen run the simulation federate in another terminal:
clojure -M:portico:run-sim --create-federation trueUseful options include:
clojure -M:portico:run-sim \
--federation-name HlaFedereplSimulation \
--federate-name hla-federepl-sim \
--world-size 10 \
--initial-carrots 10 \
--initial-rabbits 5 \
--initial-wolves 3 \
--step-interval-ms 1000 \
--max-rabbit-hunger 5 \
--max-wolf-hunger 5 \
--state-log-interval-steps 1 \
--max-steps 20The runner publishes World, Carrot, Rabbit, and Wolf object instances,
sends lifecycle interactions such as EntityCreated, EntityMoved,
EntityAte, EntityRemoved, and StepCompleted, and subscribes to
InitializeWorld. Pass --wait-for-initialize true to avoid local
initialization until an InitializeWorld interaction is received. The runner
prints a compact emoji view of the world every simulation step by default; set
--state-log-interval-steps to a larger cadence or 0 to disable it. By
default, the runner suppresses Portico's portico.lrc logger so Portico LRC
logging does not drown out the state visualization. The runner lets the RTI
assign object instance names for Portico compatibility; pass
--deterministic-object-names true if your RTI supports name reservation and
you want names derived from simulation UUIDs.
start parses the configured FOM and keeps the metadata in the running system.
You can then declare and send interactions from ordinary Clojure maps:
(hla/publish-interaction-class! system "HLAinteractionRoot.InitializeWorld")
(hla/send-interaction!
system
"HLAinteractionRoot.InitializeWorld"
{:world-size 10
:initial-carrots 20
:initial-rabbits 5
:initial-wolves 2})Parameter keys are matched leniently against FOM names. For example,
:InitialCarrots, :initial-carrots, and "InitialCarrots" all refer to the
same parameter.
For repeated use, build a publisher from a FOM and interaction name:
(def sim-fom (hla/load-fom "dev-resources/HlaFedereplFOM.xml"))
(def send-entity-moved
(hla/publisher
sim-fom
"HLAinteractionRoot.EntityMoved"))
(send-entity-moved
system
{:entity-id "rabbit-1"
:entity-type :rabbit
:from-position {:x 1 :y 2}
:to-position {:x 2 :y 2}
:location-history [{:x 1 :y 2} {:x 2 :y 2}]})The encoder currently supports HLA primitive encodings, simple data aliases, enumerations, dynamic arrays, one-dimensional fixed arrays with numeric cardinality, and fixed records. Variant records are parsed as unsupported and will produce raw values when encountered in received data.
Received events keep the raw parameter bytes under :parameters and add decoded
Clojure data under :values when the interaction and parameter datatypes are
defined in the loaded FOM:
(pprint (hla/latest system))
(pprint (hla/latest-values system))If a parameter cannot be decoded, its decoded value is an opaque wrapper instead of failing the whole interaction decode:
(hla/raw-value? (:vendor-payload (hla/latest-values system)))
(vec (hla/raw-bytes (:vendor-payload (hla/latest-values system))))That wrapper can be passed back to send-interaction! as a parameter value when
you want to forward an unsupported custom value without interpreting it.
To run tests against Portico RTI:
make testCopyright © 2026 Yet Analytics, Inc.
Distributed under the Apache License version 2.0.