Lesson 02 Livebook: Signal Window
The wake cycle is the same. The boundary around it is not.
Mix.install([
{:helios_fleet, path: "../02_signal_window"}
])
Application.ensure_all_started(:helios_fleet)
Helpers
defmodule Lesson02Helpers do
alias HeliosFleet.Runtime
alias Jido.AgentServer
def unique_agent_id(prefix) do
"#{prefix}-#{System.unique_integer([:positive])}"
end
def safe_stop(agent_id) do
case Runtime.whereis(agent_id) do
nil -> :ok
_pid -> Runtime.stop_agent(agent_id)
end
end
def wait_until(fun, attempts \\ 20)
def wait_until(fun, attempts) when attempts > 0 do
case fun.() do
nil ->
Process.sleep(50)
wait_until(fun, attempts - 1)
value ->
value
end
end
def wait_until(_fun, 0) do
raise "condition was not met before the timeout"
end
def runtime_snapshot(pid) do
{:ok, state} = AgentServer.state(pid)
HeliosFleet.mission_snapshot(state.agent)
end
end
Run A Short Contact Window
alias HeliosFleet
alias Jido.AgentServer
alias Jido.Signal
probe_id = Lesson02Helpers.unique_agent_id("khepri")
{:ok, pid} = HeliosFleet.start_probe(probe_id)
{:ok, _probe} =
AgentServer.call(
pid,
Signal.new!(
"fleet.probe.subsystem_checked",
%{
subsystem: "power_bus",
status: "nominal",
detail: "reactor stable at hotel load"
},
source: "/relay/sol-echo"
)
)
{:ok, _probe} =
AgentServer.call(
pid,
Signal.new!(
"fleet.probe.subsystem_checked",
%{
subsystem: "thermal_loop",
status: "nominal",
detail: "radiator petals holding shadow-side equilibrium",
thermal_state: "shadow_stable"
},
source: "/relay/sol-echo"
)
)
{:ok, _probe} =
AgentServer.call(
pid,
Signal.new!(
"fleet.probe.subsystem_checked",
%{
subsystem: "attitude_control",
status: "nominal",
detail: "reaction wheels trimmed against drift"
},
source: "/relay/sol-echo"
)
)
{:ok, _probe} =
AgentServer.call(
pid,
Signal.new!(
"fleet.probe.subsystem_checked",
%{
subsystem: "sensor_mast",
status: "nominal",
detail: "lidar aperture clean after dust shake"
},
source: "/relay/sol-echo"
)
)
{:ok, _probe} =
AgentServer.call(
pid,
Signal.new!(
"fleet.probe.target_assigned",
%{
name: "ice fragment 6R",
safe_standoff_m: 1_500,
scan_profile: "passive_then_lidar"
},
source: "/orpheus/mission"
)
)
:ok =
AgentServer.cast(
pid,
Signal.new!("fleet.probe.survey_burn_requested", %{}, source: "/orpheus/mission")
)
runtime_state =
Lesson02Helpers.wait_until(fn ->
snapshot = Lesson02Helpers.runtime_snapshot(pid)
if snapshot.mission_phase == "survey_ready" do
snapshot
end
end)
Lesson02Helpers.safe_stop(probe_id)
runtime_state