Procedure

The Procedure class represents instruction trees to be executed within the oac-tree and a workspace of globally accessible variables (see Variable). It provides methods to handle variables and instructions, and to manage the execution flow. A procedure can include multiple top-level instructions, where at most one is defined to be the root instruction that will be executed on procedure execution.

Architecture

The Procedure class is responsible for managing a collection of instruction trees. It contains a vector of top-level instructions, each represented by a derived class of the Instruction class. The class also includes an internal Workspace that holds variables and their values during the execution of instructions.

Usage

Creating a Procedure

To create a new procedure, instantiate an object of the Procedure class. Optionally, you can specify the filename if the procedure is loaded from a file:

Procedure my_procedure("example.procedure");

The optional filename argument shall be provided when the procedure is constructed from a file and it contains special include instructions pointing to other files via relative path names.

Managing Instructions

You can add, retrieve, and remove instructions in the procedure. The procedure allows managing instructions at the top level.

// Add a new instruction to the top level
auto my_instruction = GlobalInstructionRegistry().Create("MyInstruction");
my_procedure.PushInstruction(std::move(my_instruction));

// Retrieve the root instruction
Instruction* root = my_procedure.RootInstruction();

// Remove an instruction from a specific position
std::unique_ptr<Instruction> removed_instruction = my_procedure.TakeInstruction(0);

Managing Variables

The procedure can hold and manage variables using the internal workspace. Users can add, retrieve, and list variables associated with the procedure’s workspace.

Variables are registered in the global variable registry using their type names. In the code snippet below, it is assumed that class MyVariable : public Variable was provided and registered in the global variable registry under the name “MyVariable”.

// Add a new variable to the procedure's workspace
auto my_variable = GlobalVariableRegistry().Create("MyVariable");
my_procedure.AddVariable("var1", std::move(my_variable));

// Retrieve the value of a variable
sup::dto::AnyValue value;
if (my_procedure.GetVariableValue("var1", value)) {
    // Successfully retrieved the value
}

// List all variable names in the procedure
std::vector<std::string> variable_names = my_procedure.VariableNames();

Execution Control

The procedure can be executed step-by-step using the ExecuteSingle method. This allows controlling the execution flow and validating the procedure at each step. The Setup method prepares the procedure for execution. The procedure can also be halted and reset.

// Setup the procedure before execution
my_procedure.Setup();

// Execute a single step of the procedure
UserInterface ui; // Assume UserInterface is properly implemented
my_procedure.ExecuteSingle(ui);

// Halt the procedure's execution
my_procedure.Halt();

// Tear down the procedure and wait for all asynchronous instructions to finish
my_procedure.Teardown(ui);

Procedure Status and Control

Users can retrieve the execution status of the root instruction to determine whether the procedure is still running, has completed, or encountered an error.

// Get the status of the root instruction
ExecutionStatus status = my_procedure.GetStatus();

Attributes

The procedure can have attributes associated with it. Attributes are key-value pairs that store additional information about the procedure.

// Add an attribute to the procedure
my_procedure.AddAttribute("version", "1.0");

// Retrieve the value of an attribute
std::string version = my_procedure.GetAttributeString("version");

Additional Notes

The Procedure class supports various additional features, such as setting up preamble information, registering types and plugins, and handling callbacks for variable updates. Users can refer to the specific class methods, presented in the following section, for more details on these advanced features.

Procedures can be serialized to XML files and restored back using helper functions from sequence_parser.h.

Class definition

Next is presented the definition of the Procedure class and its main methods.

class Procedure

Procedure contains a tree of instructions.

A Procedure object contains a full instruction tree and a workspace

Note

The client of the Procedure, i.e. the object responsible for its creation and destruction, needs to ensure that the Procedure is correctly set up before executing it (by calling the Setup method on it). Likewise, the client needs to call Reset on the Procedure before destroying the UserInterface class. Reset will block until all threads have terminated.

Public Functions

explicit Procedure(const std::string &filename = "")

Constructor.

Parameters:

filename – Filename of this procedure or empty if not created from file.

std::string GetFilename() const

Get the filename of this procedure (if loaded from file).

This filename is used for external includes with relative pathnames.

Returns:

Filename of this procedure.

Instruction *RootInstruction()

Get root instruction.

Returns:

Root instruction.

const Instruction *RootInstruction() const

Get root instruction (const version).

Returns:

Root instruction.

std::vector<const Instruction*> GetTopInstructions() const

Get top-level instructions.

Returns:

List of top-level instructions.

ProcedureContext GetContext() const

Get the procedure’s context, like its filename and the main ProcedureStore.

Returns:

ProcedureContext structure.

int GetInstructionCount() const

Get number of top-level instructions.

Returns:

Number of instructions

void PushInstruction(std::unique_ptr<Instruction> &&instruction)

Push Instruction at top level.

Parameters:

instructionInstruction to push.

Throws:

InvalidOperationException – when trying to pass an empty unique_ptr.

bool InsertInstruction(std::unique_ptr<Instruction> &&instruction, int index)

Inserts Instruction at the specified position.

Parameters:
  • instructionInstruction to be inserted

  • index – Position in the vector where instruction is inserted.

Returns:

true on successful insertion.

std::unique_ptr<Instruction> TakeInstruction(int index)

Removes Instruction from the specified position.

Parameters:

index – Position in the vector from where instruction is removed.

Returns:

Removed instruction

bool AddVariable(std::string name, std::unique_ptr<Variable> &&var)

Add variable.

Parameters:
Returns:

true on successful addition.

std::vector<std::string> VariableNames() const

List all variable names.

Returns:

Variable name list.

bool GetVariableValue(std::string name, sup::dto::AnyValue &value) const

Get variable value.

Parameters:
  • nameVariable name

  • value – AnyValue to where the variable will be retrieved.

Returns:

true on successful retrieval.

void Setup()

Setup the procedure.

void ExecuteSingle(UserInterface &ui)

Execute single step of procedure.

Parameters:

uiUserInterface to use for instruction input/output.

void Halt()

Halt the procedure’s execution.

Note

This is mainly used for interrupting all instructions that are running in a separate thread.

void Reset(UserInterface &ui)

Reset the procedure to its initial state after setup.

All workspaces are torn down and setup again; instructions receive a reset command. The procedure can be started afterwards in a fresh state.

Parameters:

uiUserInterface to use for instruction status updates.

ExecutionStatus GetStatus() const

Retrieve status of root sequence.

Returns:

Current execution status of the root instruction.

bool HasAttribute(const std::string &name) const

Indicate presence of attribute with given name.

Parameters:

name – Attribute name.

Returns:

true when present.

std::string GetAttributeString(const std::string &name) const

Get attribute with given name.

Parameters:

name – Attribute name.

Returns:

Attribute value.

bool AddAttribute(const std::string &name, const std::string &value)

Set attribute with given name and value.

Parameters:
  • name – Attribute name.

  • value – Attribute value.

Returns:

true when successful.

bool AddAttributes(const StringAttributeList &str_attributes)

Add all attributes from a given map.

Parameters:

str_attributes – List of attributes.

Returns:

true when successful.

template<typename T>
inline T GetAttributeValue(const std::string &attr_name) const

Get attribute value with given name and type.

Parameters:

attr_name – Attribute name.

Throws:

RuntimeException – when attribute with given name was not found or its value could not be converted to the requested type.

Returns:

Attribute value of requested type.

const Workspace &GetWorkspace() const

Returns pointer to internal workspace.

Returns:

workspace.

Workspace &GetWorkspace()

Returns pointer to internal workspace.

Returns:

workspace.

const ProcedurePreamble &GetPreamble() const

Returns procedure’s preamble.

Returns:

ProcedurePreamble object reference.

ProcedurePreamble &GetPreamble()

Returns procedure’s preamble.

Returns:

ProcedurePreamble object reference.

bool RegisterType(const sup::dto::AnyType &anytype)

Register an AnyType instance under its own name.

Parameters:

anytype – AnyType instance to register.

Returns:

true on successful registration.

const sup::dto::AnyTypeRegistry &GetTypeRegistry() const

Get the current type registry.

Returns:

Reference to type registry.

bool RegisterGenericCallback(const GenericCallback &cb, void *listener)

Add a generic callback for variable updates.

Note

Users are responsible for ensuring the callback outlives the underlying workspace or to unregister it using ScopeGuard object.

Parameters:
  • cb – Callback function object.

  • listener – Pointer to object that listens to updates.

Returns:

true if adding the callback was successful.

void SetupPreamble()

Register types and load plugins defined in the preamble.