UserInterface

The UserInterface class represents the interface for all input/output during the execution of instructions within a software system. It provides methods to request user input, display messages, and log information. Implementations of this interface can be created to suit different user interaction scenarios, e.g. command line interfaces or GUIs.

Architecture

The UserInterface class is based on the non-virtual interface (NVI) idiom to support the distinction between thread-safe and non-thread-safe methods. It employs locks to provide thread safety for certain public methods that can be called during execution, ensuring that multiple threads can safely access these methods concurrently. Other methods should be called only from a single thread to prevent data corruption or race conditions.

Usage

Creating a UserInterface

To use the UserInterface, you need to create an implementation of this class that provides concrete implementations for its virtual methods. For example, you can create a class MyUserInterface that inherits from UserInterface and overrides its virtual methods.

class MyUserInterface : public UserInterface {
    // Implement the virtual methods of UserInterface here
};

Alternatively, one can derive from DefaultUserInterface, which provides default empty implementations of all pure virtual methods in UserInterface. This allows one to implement only the methods that are relevant for the use case at hand.

Updating Instruction Status

The UpdateInstructionStatus method is called when the execution status of an instruction changes. Implement this method to handle any updates related to instruction execution.

void UpdateInstructionStatus(const Instruction* instruction) override {
    // Implement the logic for handling instruction status updates here
}

Handling Variable Updates

When a workspace variable receives a value update, the VariableUpdated method is called. Implement this method to handle updates related to workspace variables.

void VariableUpdated(const std::string& name, const sup::dto::AnyValue& value,
                             bool connected) override {
    // Implement the logic for handling variable updates here
}

Breakpoint Notifications

When a breakpoint is hit, the BreakpointInstructionUpdated method is called with the instruction that triggered the breakpoint. The method is also called to clear this status by passing a nullptr. Implement this method to handle breakpoint notifications.

void BreakpointInstructionUpdated(const Instruction* instruction) override {
    // Implement the logic for handling breakpoint notifications here
}

Displaying Values

Use the PutValue method to interact with the user for value output. Implement this method to handle such interactions.

bool PutValue(const sup::dto::AnyValue& value, const std::string& description) override {
    // Implement logic to display the value to the user
    return true; // Return true on successful retrieval of the value
}

Requesting User Input

The RequestUserInput method is used to request the user to provide input. It returns a future of type IUserInputFuture that needs to be checked for readiness before retrieving its value. This allows instructions to properly exit when they are halted, instead of blocking on user input methods.

Implementations will typically use a member AsyncInputWrapper object to implement this method.

Displaying Messages and Logging

Use the Message and Log methods to display messages and log information. Implement these methods to handle message display and logging.

void Message(const std::string& message) override {
    // Implement logic to display the message to the user
}

void Log(int severity, const std::string& message) override {
    // Implement logic to log the message with the specified severity level
}

Remember to properly implement each of the virtual methods in your derived MyUserInterface class according to your specific user interaction requirements.

Additional Notes

The UserInterface class provides a non-virtual interface for most of its methods, meaning that it wraps the virtual methods with non-virtual ones. The virtual methods must be implemented in your derived class, while the non-virtual methods can be called directly in your application code to interact with the user interface.

The UserInterface class also includes helper methods related to user choice metadata, which can be used to provide additional information to the user interface for displaying user choice options.

Class definition

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

class UserInterface

Interface representing user interactions.

Implementers of concrete user interfaces should take care of thread safety, since many of the member functions can be called from different threads.

Subclassed by sup::oac_tree::CLInterface, sup::oac_tree::DaemonInterface, sup::oac_tree::DefaultUserInterface, sup::oac_tree::JobInterface

Public Functions

virtual void UpdateInstructionStatus(const Instruction *instruction, ExecutionStatus status) = 0

Method called when instruction’s execution status changes.

Parameters:
  • instructionInstruction that has new execution status.

  • status – New execution status of the instruction.

virtual void VariableUpdated(const std::string &name, const sup::dto::AnyValue &value, bool connected) = 0

Method called when a workspace Variable has received a value update.

Parameters:
  • name – Name of the Variable.

  • value – New value of the Variable.

  • connected – Boolean indicating if variable is connected.

virtual void BreakpointInstructionUpdated(const Instruction *instruction) = 0

Method to notify a change in the current breakpoint instruction.

This method will report a nullptr when no running normally, i.e. when not halted at a breakpoint.

Parameters:

instruction – Pointer to the current breakpoint instruction, or nullptr.

virtual bool PutValue(const sup::dto::AnyValue &value, const std::string &description) = 0

Method to put the value.

Parameters:
  • value – Value to put into UI.

  • description – Optional description of the value.

Returns:

true on successful retrieval of a value, false otherwise.

virtual std::unique_ptr<IUserInputFuture> RequestUserInput(const UserInputRequest &request) = 0

Asynchronous, i.e. non-blocking, interface to retrieve user input.

Note

Upon destruction of the returned future, the request is cancelled, but it will not join any threads that were created during creation. This avoids blocking on these objects’ destruction.

Parameters:

request – UserInputRequest object, describing what kind of input is expected.

Returns:

A handle to a future that will cancel the request upon destruction.

virtual void Message(const std::string &message) = 0

Method called to display a message.

Parameters:

message – Message to be displayed.

virtual void Log(int severity, const std::string &message) = 0

Method called to log a message.