User hooks for Foretify flow control
The following methods are available as hooks that allow you to perform different actions on different flows.
init()
Perform various actions when an object is created
Method of any object
extend <object>:
def init() is also:
<procedural-code>
<object>- (Required) Is a struct, actor or scenario.
<procedural-code>- (Required) Is any valid procedural code. See Native methods for more information.
Each object (struct, actor or scenario) has a predefined init() method. init() is called each time a new object is allocated, both when creating new objects in procedural code and when generation allocates new objects. Regarding init() in generation, init() is called before post_plan(). The order is as follows: init() is called, then generation sets all generatable fields for the specific object, then post_plan() is called.
You can extend this method using is_also to initialize non-generatable fields to a computed value, for example. Overwriting this method is not allowed.
In the following example, the call to logger.log_info() prints the value of my_vars.my_speed, which has been initialized to 100kph.
import "$FTX_BASIC/exe_platforms/sumo_ssp/config/sumo_config.osc"
extend test_config:
set map = "$FTX_PACKAGES/maps/M77_FTX_highway_straight_long_road.xodr"
struct my_vars:
var my_speed: speed
def init() is also: my_speed = 100kph
extend top.main:
my_vars: my_vars
do serial:
call logger.log_info("my_speed is $(my_vars.my_speed)")
sut.car.drive() with: speed([30..60]kph)
with:
duration([3..7]s)
on_created()
Provides the values for an actor received from the simulator
Method of any agent
extend any_agent:
def on_created(sim_id: string, desc: av_actor_description) is empty
<sim_id>: <string>- (Required) Specifies the unique ID created by the simulator for the new actor.
<desc>: <av_actor_description>- (Required) Specifies the values for the new actor receieved from the simulator. See definition below.
The struct av_actor_description is defined as:
struct av_actor_description:
var actor_type: string
var additional_properties: list of property
var length: length
var width: length
var height: length
<actor_type>: <string>- Actor type.
<additional_properties>: <list of property>- List of optional additional properties.
<length>: <length>- Actor's length.
<width>: <length>- Actor's width.
<height>: <length>- Actor's height.
on_created() is a callback function that is called after an actor is created in the simulator. on_created() provides the actual values for the actor received from the simulator.
on_execution_start()
Is called for each actor on simulation start, prior to the simulation's first step
Method of any agent
extend any_agent:
def on_execution_start() is empty
on_execution_start() is called during test execution before the first simulation step.
on_post_plan()
Perform various actions after planning is finished and before execution starts
Method of any agent
extend any_agent:
def on_post_plan() is empty
on_post_plan() is called after scenario generation is finished, and it lets you either:
- Perform some checks and cause a generation retry.
- Run some code for preparing data based on generated values. This should be done with care, since there may be a generation retry after a call to on_post_plan().
post_plan()
Perform various actions after an object is generated
Method of any object
extend <object>:
def post_plan() is also:
<procedural-code>
<object>- (Required) Is a struct, actor or scenario.
<procedural-code>- (Required) Is any valid procedural code. See Native methods for more information.
Each object (struct, actor or scenario) has a predefined post_plan() method. This method is called by Foretify for each object once the generation of the object is completed, after:
- All generative primitive fields of the object have a value.
- The post-plan() method of all struct fields of the object has been called.
You can extend this method using is_also to initialize non-generatable fields to a computed value, for example.
In the following example, the initial value for the variable z is calculated on the generated values of x and y.
struct foo:
x, y: int
var z: int
def post_plan() is also:
z = x + y
post_sim_update()
Called for each actor after calling update_state()
Method of any agent
extend any_agent:
def post_sim_update() is empty
See Update methods.
pre_sim_update()
Called for each actor before calling update_state()
Method of any agent
extend any_agent:
def pre_sim_update() is empty
See Update methods.
update_state()
Called when new data for an actor is received from the simulator
Method of any agent
extend any_agent:
def update_state(sim_state: av_actor_state) is empty
<sim_state>: <av_actor_state>- (Required) Specifies the state received from the simulator. See definition below.
The struct sim_state is defined as follows:
struct av_actor_state:
var position: global_position
var speed: global_speed
var angular_speed: global_angular_speed
var acceleration: global_acceleration
var collision_info: collision_info
var controls: actor_controls
var additional_properties: list of property
<position>: <global_position>- Position of the actor in global coordinates.
<speed>: <global_speed>- Speed of the actor in global coordinates.
<angular_speed>: <global_angular_speed>- Angular speed of the actor in global coordinates.
<acceleration>: <global_acceleration>- Acceleration of the actor in global coordinates.
<collision_info>: <collision_info>- Collision information (includes a single field:
var other_actor: physical_object). <controls>: <actor_controls>- Actor controls. See definition below.
<additional_properties>: <list of property>- Optional additional information.
The struct actor_controls is defined as follows:
struct actor_controls:
var gear: string
var hand_brake: bool
var hazard_lights: bool
var indicators: av_indicator_state
# Holds the brake pedal position in percent [0,1]
var brake_pedal_pos: float
# Holds the throttle pedal position in percent [0,1]
var throttle_pedal_pos: float
# Holds the steering wheel angle
var steering_wheel_angle: angle
var siren: bool
var emergency_lights: bool
<gear>: <string>- Actor's gear.
<hand_brake>: <bool>- Is hand brake active.
<hazard_lights>: <bool>- Are hazard lights active.
<indicators>: <av_indicator_state>- Vehicle turning light indicator state. See definition below.
<brake_pedal_pos>: <float>- Brake position as percentage.
<throttle_pedal_pos>: <float>- Throttle position as percentage.
<steering_wheel_angle>: <angle>- Steering wheel angle.
<siren>: <bool>- Is siren turned on.
<emergency_lights>: <bool>- Are emergency lights turned on.
av_indicator_state is defined as:
av_indicator_state: [indicator_state_unknown, indicator_state_off, indicator_state_left, indicator_state_right]
See Update methods.
validate()
Add post-plan validations
Method of any normal scenario
extend any_normal_scenario:
def validate() is empty
validate() is used to validate generated values. In the case of an invalid plan, calling top.replan_error causes a plan retry.
validate() is useful for detecting post-generation limitations, which are hard to model for generation. For example, validate() is used to check that no object is planned to be created in an excluded area (since modeling excluded areas for generation is not a trivial task).
Update methods
The update methods, pre_sim_update(), update_state() and post_sim_update(), are called during state updates from the simulator. The flow is as follows:
-
Call pre_sim_update() for all of the simulation actors.
-
Get the updated state from the simulation.
-
For each actor received from the simulator, call update_state() with the state received from the simulator.
-
After handling the state of all of the actors, call post_sim_update() for all of the actors.
update_state() should not assume any order, so it should mainly handle the state of the current actor.
post_sim_update() can assume that the state of all of the actors is already updated.