Skip to content

Development Overview & Concepts

TheBigO edited this page Dec 12, 2020 · 66 revisions

Introduction

The architecture of Simulator Controller has been designed with extensibility in mind. Since every racing rig is different and there are so many different applications out there for sim racers, the core of Simulator Controller is build around a very flexible and generic concept. Plugins may be used to provide additional functionality ranging from simple code additions up to very complex, object-oriented extensions of the Simulator Controller itself.

Plugin Integration

When the Simulator Controller starts up, a single file in the Sources/Controller/Plugins folder will be included using the AutoHotkey #Include directive: Plugins.ahk. This file may be modified to include all the plugin script required for your specific configuration.

Although a plugin script may execute any kind of code written in the AutoHotkey language, real plugins must extend the ControllerPlugin (*) class and will provide additional functionality for your controller box. The following sections will introduce all the concepts and classes needed to implement your own plugins step by step.

Overview

The Simulator Controller framework has been build around the similar named Singleton Class SimulatorController. This class implements the complete control flow between the hardware controller elements like buttons, dials and switches and the functionalities provided by a plugin. Since the number of hardware control elements is limited, functionalities may be grouped in so called modes, which may be activated or deactivated as a group. Each mode belongs to a given plugin and only on mode may be active at a given point in time. From the user point of view, a mode defines a set of controls as a switchable layer for the hardware controller. In addition, plugins may bind functionality to controller functions independent of a specific mode, and these functions may be available all the time. An example will make it more clear: A toggle switch to enable or disable rig motion feedback might be always available and therefore is provided by the plugin itself, but detailed control over specific effect intensities might only be necessary, while finetuning the feedback levels, which may be grouped by a "Feedback Settings" mode.

A specific hardware control element is represented in code by an instance of the class SimulatorControllerFunction (), respectivly one of its subclasses. For a controller function to be useful, it must be connected or bound to a ControllerAction (), which implements the functionality that should be triggered by the hardware controller. These connections are of dynamic nature, which means that the functional mapping for the hardware controller can be changed anytime. This is first and foremost used when switching between modes, but it can also be used to create context sensitive function mappings.

Plugins

Modes

Controller Functions

Instances of ControllerFunction represent the active elements of a hardware controller - buttons, dials, switches and so on. A function must be mapped to an Action () to be useful. This mapping is handled by Plugins () and Modes (), since both can take ownership of a function and define the corresponding Action. Whereas modes may connect a function to an action only as long they are the currently active mode of the Controller () (i.e. the currently active layer of a button box), plugins can define actions and bind them to functions, so that they are available all the time. Functions might be enabled or disabled according to the current state of their mode or plugin and they can give visual feedback, if a visual button box representation has been defined. For example, if you increase the force feedback of your steering wheel with a dial knob, the current feedback strength might be dislayed below the dial. Normally this is handled by the action, when the fireAction (*) method is called.

Controller functions are identified by their descriptor, which consists of the type name followed by a dot and a running number. For example, the third button on a hardware controller might have "Buttton.3" as its descriptor. All available functions must have been defined by the setup tool (), before they can used. With the setup tool, you also define the Hotkeys (), that will trigger the function from the hardware. To retrieve a function object in code, use the findFunction method of SimulatorController. As sais, functions may be enabled or disabled according to the current context, and associated label on the visual controller representation may be changed anytime using the setText method.

Several subclasses of ControllerFunction define specialized behaviour, for example 2WayToggleFunction can trigger to different action methods, since they have an On and an Off state. See the class reference for details on all subclasses of ControllerFunction.

Note: To make things more complicated, a seperate controller function inheritance tree exists for all functions handled by SimulatorController (), since AutoHotkey does not support multiple inheritence. The complete protocol of ControllerFunction is implemented by these classes () as well and they use instances of the original classes by a delegation pattern. Since AutoHotey is a weakly typed prototype-based language, this fact is completely invisible to the programmer, but in the end good to know and to understand.

Controller Actions

ControllerAction () objects are very simple. They define a label, which might be displayed by the button box visual and the define the method fireAction () which will be triggered by the function. Although actions might be created and registered to their mode or plugin anytime, normally they are created by the mode or plugin during initialization, for example based on configuration data.

Button Box

Example

Clone this wiki locally