Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.davinci-app.com/llms.txt

Use this file to discover all available pages before exploring further.

Description

Actions describe the behavior of a system using a subset textual syntax from SysML v2. Actions can be composed using nested levels to refine behavior in detail, with sequences defined using standard Python control flow and specialized action functions.

Syntax for Action Modeling

Actions in Davinci use a Python-based syntax that aligns with SysML v2 action modeling principles. This approach provides:
  • Executable Behavior: Actions are defined as executable Python code that can be simulated and tested
  • Hierarchical Composition: Actions can call nested sub-actions to build complex behaviors from simple components
  • Standard Control Flow: Use familiar Python constructs (if/else, for, while) for action sequencing

Key Concepts

Actions as Functions: Each action is a function that can be called by parent actions or executed directly. Attribute Binding: Attributes referenced in action sequences are global variables, enabling data flow between actions and physics simulations. Flow Modeling: The flow() function explicitly models message passing and data routing between system components, critical for architectural definition.

View Types

ViewDescription
ActivityVisually represent action flow and logic.
SequenceVisualize interactions over time.
PropertiesEdit the object’s properties, attributes, and metadata.
TableDisplay the object and its children in a tabular format.
TreeView the hierarchical structure of the object.
RelationshipsExplore the network of connections to other objects.

Properties Fields

Name
string
Name of the object.
Short Name
string
Short name of the object.
Documentation
string
Description of the object.
Sequence
code
Code that defines the action’s behavior and flow. The sequence uses standard Python syntax with specialized functions and reference notation for modeling behaviors.
Attributes
object
A list of all Attribute objects owned by this action.Attribute Equations, Units and Type can be edited here, as well as recalculated and deleted entirely.Read more about Attributes
Constraints
object
A list of all Constraint objects owned by this action.Constraint Equations and Analysis Type can be editied here, as well as recalculated and deleted entirely.Read more about Constraints
Relationships
connection
A list of all Relationships this object has with other model objects.Read more about Relationships

DSL Syntax

Davinci action sequences use a Python-like DSL. The runtime resolves model references before execution, but this documentation uses the friendlier @'name' notation for readability in examples.
In the underlying DSL implementation, model references are transformed into executable calls and bindings during preprocessing. The examples below keep the docs-friendly @'name' form, even though the runtime internally uses a lower-level resolved representation.

Model References

Actions, attributes, ports, and other modeled elements can be referenced using @'name':
@'Initialize System'
@'Configure Parameters'
if @'status_code' == 200:
    @'output_value' = @'input_value' * 2
Attributes are always single scalar values, not arrays or lists. Use multiple scalar attributes for vectors such as @'pos_x', @'pos_y', and @'pos_z'.

Attribute Runtime Behavior

Attributes act like execution-time scalar variables while an action is running. When the sequence starts, referenced attributes are bound into the runtime so the action can read their current values:
if @'tank_pressure' > @'max_pressure':
    @'Open Relief Valve'()
Assignments update the runtime value of that attribute for the remainder of the execution and for any downstream logic that reads it during that run:
@'attempt_count' = @'attempt_count' + 1

if @'attempt_count' >= @'max_attempts':
    end(status="failed")
This is important:
  • Reading @'name' pulls the current execution-time value for that attribute.
  • Writing @'name' = ... updates the runtime value used by later steps in the same execution.
  • These updates do not rewrite the modeled attribute definition itself.
  • In other words, action code works with an execution context, not by editing the persistent model.
Use action inputs when a value should be explicitly passed in by the caller. Use attributes when the value is part of the modeled system state that the action should read or update during execution.

Action Inputs

Actions can be defined in one of two ways:

No-input actions

If an action does not need inputs, write the sequence directly. No action(...) declaration is needed.
@'Run Pre Checks'()
@'Load Configuration'()
@'Start Services'()
end()
Use this form when the action relies only on local logic, global model state, or previously bound attributes.

Actions with inputs

If an action needs caller-provided values, declare them at the top with action(...). The declared parameters become normal Python variables inside the action body.
action(speed, mode="nominal"):
    if mode == "nominal":
        @'Run Nominal Sequence'(value=speed)
    else:
        @'Run Degraded Sequence'()
    end()
action(config, threshold):
    result = @'Analyze Configuration'(config=config)
    if result.status == "ok" and result.score > threshold:
        end(status="passed")
    else:
        end(status="failed")
Rules for action definitions with inputs:
  • action(...) must be the first non-comment line in the action sequence.
  • If action(...) is present, the full executable body must be indented under it.
  • Parameters are plain Python identifiers, not model references.
  • Parameters without defaults are required at the call site.
  • Parameters with defaults are optional at the call site.

Calling Actions

Actions are called like functions. Parentheses are optional for no-argument calls and may be used with positional or keyword inputs.
# No-argument calls
@'Run Pre Checks'
@'Run Pre Checks'()

# Positional inputs
@'Configure System'(@'current_speed', "nominal")

# Keyword inputs
@'Configure System'(mode="nominal", speed=@'current_speed')

# Mixed inputs
@'Configure System'(@'current_speed', mode="nominal")

Returning Outputs with end()

Actions return outputs by passing keyword arguments to end(...). end() with no arguments is still valid when no outputs are needed.
# No outputs
end()

# One output
end(result=@'computed_value')

# Multiple outputs
end(velocity=@'computed_velocity', heading=@'computed_heading', status="ok")

Capturing Outputs

When an action returns outputs, capture the result in a Python variable and access named outputs with dot notation.
outputs = @'Analyze Plan'(input=@'sensor_data')
@'analysis_result' = outputs.result
@'analysis_status' = outputs.status
Inline access is also valid when only one field is needed:
@'approval_status' = @'Review Test Plan'(plan=@'test_plan').status

Control Flow

Standard Python control structures remain available:
if @'system_status' == "OK":
    @'Normal Startup'()
else:
    @'Recovery Mode'()
    end(status="recovery")
for i in range(int(@'retry_count')):
    @'Attempt Connection'()
    if @'connection_status' == "success":
        break

Runtime Functions

fork([...])

fork() executes multiple branches in parallel. A branch may be a single action call or a named group(...) block.
fork([@'Data Collection'(), @'System Monitoring'(), @'Log Events'()])

trigger(...)

trigger() starts an action or state asynchronously and immediately continues the current flow.
trigger(@'Health Monitor'())
trigger(@'Send Alert'(severity="critical", message=@'alert_message'))
Use trigger() for fire-and-forget behavior. Do not use it when the current action needs the result before continuing.

end(...)

end() exits the current action flow and may optionally return outputs.
if @'critical_error':
    @'Log Error'()
    end(status="failed")

stop()

stop() immediately halts the current action hierarchy.
if @'emergency_condition':
    @'Emergency Shutdown'()
    stop()

checkpoint("name") and retry("name")

Use checkpoints to label a recovery point and retry() to resume execution from that named location.
checkpoint("initialization")

@'Run Pre Checks'()
@'attempt_result' = @'System Init'(config=@'boot_config').status

if @'attempt_result' == "failed":
    if @'attempt_count' < @'max_attempts':
        @'attempt_count' = @'attempt_count' + 1
        retry("initialization")
    else:
        @'Enter Safe Mode'()
        end(status="safe_mode")
Guidelines:
  • Checkpoint names must be unique within the action.
  • retry() may only target a checkpoint defined in the same action.
  • Guard retry() with a counter or condition to avoid infinite retry loops.

group("name"): Blocks

Use group(...) to define a named sub-flow for a parallel branch. Groups must be declared before the fork() call that uses them.
group("branch_power"):
    @'Power Self Check'()
    if @'battery_level' < @'min_battery':
        @'Switch To Backup Power'()
    @'Confirm Power State'()

group("branch_comms"):
    for i in range(int(@'retry_limit')):
        @'Establish Link'()
        if @'link_established':
            break
    if not @'link_established':
        end(status="link_failed")

fork([branch_power, branch_comms, @'Start Logging'()])

Message Flow Modeling

Use flow() to explicitly model data and message routing between ports or entities:
flow(@'command_port', @'actuator_port', "command", [@'Control Signal'])

if @'mode' == "autonomous":
    flow(@'sensor_port', @'ai_processor_port', "sensor_data", [@'Image Data', @'Telemetry'])
    @'AI Decision Making'()
    flow(@'ai_processor_port', @'command_port', "decision", [@'Command'])
else:
    flow(@'sensor_port', @'operator_display_port', "sensor_data", [@'Image Data'])
    flow(@'operator_input_port', @'command_port', "manual_command", [@'Command'])

Code Object References

Code objects may be called and imported inside action sequences:
@'calculated_torque' = @'PD Control Algorithm'.compute_torque(@'current_attitude', @'target_attitude')

import @'Utilities Code'
result = @'Utilities Code'.helper_function(@'input_data')

Complete Example

action(config, max_attempts=3):
    checkpoint("boot_start")

    trigger(@'Health Monitor'())

    init_outputs = @'System Initialization'(config=config, timeout=30)
    @'init_status' = init_outputs.status

    if @'init_status' == "ready":
        group("branch_power"):
            @'Power Self Check'()
            if @'battery_level' < @'min_battery':
                @'Switch To Backup Power'()
            @'Confirm Power State'()

        group("branch_comms"):
            plan = @'Build Comms Test Plan'()
            approval = @'Review Comms Plan'(plan.result)
            @'comms_approval' = approval.status
            if @'comms_approval' == "approved":
                @'Establish Link'()
            else:
                end(status="comms_rejected")

        fork([branch_power, branch_comms, @'Start Logging'()])
        @'Begin Primary Operations'()

    elif @'init_status' == "degraded":
        if @'attempt_count' < max_attempts:
            @'attempt_count' = @'attempt_count' + 1
            retry("boot_start")
        else:
            @'Enter Safe Mode'()
            end(status="safe_mode")

    else:
        stop()

    end(status="nominal")

Best Practices

Action Composition

  • Single Responsibility: Give each action a clear purpose.
  • Hierarchical Design: Decompose complex behavior into smaller reusable actions.
  • Consistent Outputs: Prefer consistent output keys across branches when returning data from end(...).

Sequence Definition

  • Use Inputs Deliberately: Prefer action(...) parameters for local action inputs instead of relying only on global attributes.
  • Guard Recovery Paths: Protect retry() with explicit retry limits or exit conditions.
  • Keep Groups Focused: Use group(...) only to express meaningful parallel branches.
  • End Intentionally: Use end() when you need to terminate early or return outputs; otherwise allow the sequence to reach its natural end.

Technical Implementation

Parse-Time Resolution

When an action sequence is executed, the system performs several preprocessing steps before runtime:
  1. Reference Resolution: Model references such as @'Initialize System' are resolved into executable calls and bindings.
  2. Action Hoisting: action(...) declarations are transformed into callable action entry points with injected parameters.
  3. Group Hoisting: group("name"): blocks are rewritten into callable branch functions for use by fork().
  4. Runtime Primitive Wiring: trigger(), checkpoint(), and retry() are connected to runtime execution semantics.
  5. Code Import Resolution: Code-object references are transformed into module imports and callable functions as needed.