Skip to main content

CAS System

Introduction

CAS System is an SDK API that aims to provide a generic framework from which you can build a CAS (crew alerting system) for your specific avionics system. The API provides classes that handle much of the state logic involved in deciding which CAS alerts should be displayed. By design, the API provides limited support for actually rendering CAS displays since those details are largely avionics/airplane-specific.

System Description

CAS System is based on the concept of alerts and messages. An alert represents a condition or state which, when true or active, should be communicated to the pilot through the CAS. A message is a textual element that is displayed to the pilot by the CAS to communicate the presence of one or more alerts. As alerts are activated and deactivated, the CAS will display and hide their associated messages automatically.

An alert has three basic properties:

  • ID
  • Message Text
  • Suffixes

The ID of an alert is used to identify and reference it. Each alert must have a unique ID. The ID of an alert is never exposed or displayed to the user. On the other hand, the message text of an alert determines the text that is displayed by CAS when the alert is active. Two different alerts are allowed to have the same message text. An alert's suffixes are a list of possible text that is appended to the end of the message text when displaying the alert. Examples of commonly used suffixes include L, R, A, B, etc. An alert may have zero suffixes, in which case its message text will be all that is displayed.

Setting Up the System

The CasSystem class handles the bulk of the CAS state logic. For the system to function, you must create at least one instance of CasSystem on any of your airplane's JS/HTML instruments. Most CAS state information is not shared between instruments; therefore you must create one instance CasSystem on each instrument that needs access to data provided by CasSystem. Do not create more than one instance of CasSystem on any given instrument - doing so will lead to undesired behavior.

When instantiating CasSystem, exactly one instance across all instruments must be designated as the primary instance:

import { CasSystem, EventBus } from '@microsoft/msfs-sdk';

const bus = new EventBus();
new CasSystem(bus, true /* designates this instance as the primary */);

All other instances must be designated as non-primary:

new CasSystem(bus, false);
// ---- OR ----
new CasSystem(bus);

Once CasSystem is instantiated, it is ready to use.

Registering Alerts

Alerts must be registered with CasSystem before they can be used. Registering an alert is done using the CasRegistrationManager class:

import { CasRegistrationManager, EventBus } from '@microsoft/msfs-sdk';

const bus = new EventBus();
const manager = new CasRegistrationManager(bus);

// Registers an alert with ID 'parking-brake' which will display 'PARKING BRAKE' with no suffixes.
manager.register({
uuid: 'parking-brake',
message: 'PARKING BRAKE'
});

// Registers an alert with ID 'generator-off' which will display 'GEN OFF' with the 'L' and 'R' suffixes.
manager.register({
uuid: 'generator-off',
message: 'GEN OFF',
suffixes: ['L', 'R']
});

The register() method takes in a single object that satisfies the CasAlertDefinition type. The two properties required by CasAlertDefinition are uuid and message, which define the ID and message text of the registered alert, respectively. The property suffix is optional and is an array of strings that define the alert's suffixes. The order of suffixes in the array determine the order in which they will be displayed at the end of a message.

tip

You don't have to wait for all CasSystem instances to be created before registering alerts using CasRegistrationManager. The manager will ensure that any instances of CasSystem created after an alert was registered still receive the registration information for that alert.

Activating Alerts

Three pieces of information are needed to activate an alert: the ID of the alert, the suffix to activate, and the priority level at which to activate the alert.

When an alert is activated, a single message is displayed by the CAS with the alert's message text. For alerts with suffixes, each suffix is activated independently. No matter how many suffixes are active for a given alert, only one message is ever displayed; it is left up to individual CAS displays to determine how to present a message with multiple active suffixes. Finally, each alert (and suffix, if applicable) can be activated at four different priority levels (in order of decreasing priority): warning, caution, advisory, and safeop. Like with suffixes, each priority level is activated independently. If an alert is activated at multiple priority levels, the highest priority level takes precedence and only that priority level is displayed by the CAS.

CasSystem will automatically sort alert messages based on order of activation and priority level. Messages with higher priority always appear before messages with lower priority. Within the same priority level, messages are sorted such that those with the most recently activated alerts appear first. Activating an alert with suffix and priority level that were already activated does not count as a more recent activation.

The event bus is used to activate alerts:

import { AnnunciationType, CasEvents } from '@microsoft/msfs-sdk';

// Activates the alert with ID 'parking-brake' with no suffix and at advisory priority.
bus.getPublisher<CasEvents>().pub('cas_activate_alert', {
key: { uuid: 'parking-brake' },
priority: AnnunciationType.Advisory
}, true, false);

// Activates the alert with ID 'generator-off' with the 'L' suffix and at caution priority.
bus.getPublisher<CasEvents>().pub('cas_activate_alert', {
key: { uuid: 'generator-off', suffix: 'L' },
priority: AnnunciationType.Caution
}, true, false);

The CasEvents interface defines event bus topics related to CAS. The cas_activate_alert topic is used to activate alerts, and requires event data in the form of an object that has the key and priority properties. The key property defines the ID and optionally suffix of the alert to activate. The priority property defines the priority level at which to activate the alert as a member of the AnnunciationType enum.

caution

When publishing the cas_activate_alert topic, you must specify that the topic be synced to other instruments and not cached (the third and fourth parameters of pub(), respectively). Failure to specify these options will result in incorrect behavior.

Deactivating Alerts

Deactivating an alert will cause it to not be displayed as a message. Like with activation, deactivating alerts is done independently for each suffix and each priority level. The event bus is used to deactivate alerts using syntax similar to that used to activate alerts:

import { AnnunciationType, CasEvents } from '@microsoft/msfs-sdk';

// Deactivates the alert with ID 'generator-off' with the 'L' suffix and at caution priority.
bus.getPublisher<CasEvents>().pub('cas_deactivate_alert', {
key: { uuid: 'generator-off', suffix: 'L' },
priority: AnnunciationType.Caution
}, true, false);

Using CasAlertTransporter

While it is always possible to manually activate and deactivate alerts using the event bus as shown above, most of the time you will end up wanting to bind the activation state of an alert to some set of conditions that can be reduced to a boolean state. Since this pattern is so common, we have provided a convenience class that allows you to do that while minimizing repetitive boilerplate: CasAlertTransporter.

CasAlertTransporter allows you to bind the activation state of an alert to a boolean value, taken either from a Subscribable or a custom update loop. It also allows you to directly set the activation state with its set() method.

Let's say that we want to activate the parking-brake CAS alert at advisory priority whenever the parking brake is engaged. We can do that using the following code:

import { AnnunciationType, CasAlertTransporter, SimVarValueType } from '@microsoft/msfs-sdk';

CasAlertTransporter.create(bus, 'parking-brake', AnnunciationType.Advisory)
.bindUpdate(() => SimVar.GetSimVarValue('BRAKE PARKING POSITION', SimVarValueType.Bool) !== 0);

The above code will evaluate the BRAKE PARKING POSITION simvar in an update loop and use the result to activate/deactivate the parking-brake alert accordingly.

info

Update loops spawned by CasAlertTransporter update every JS instrument frame. The performance overhead for each update loop is relatively minor. However, we still recommend that you reserve the use of update loops for when the bound state cannot be evaluated in an event-driven manner.

Here's another example where CasAlertTransporter is used to control activation of a low-fuel alert with suffixes - this time using event-driven logic:

CasAlertTransporter.create(bus, 'fuel-low', AnnunciationType.Caution, 'L')
.bind(ConsumerSubject.create(bus.getSubscriber<EngineEvents>().on('fuel_left'), 0), fuel => fuel < 10);

CasAlertTransporter.create(bus, 'fuel-low', AnnunciationType.Caution, 'R')
.bind(ConsumerSubject.create(bus.getSubscriber<EngineEvents>().on('fuel_right'), 0), fuel => fuel < 10);

For more detailed information on CasAlertTransporter, please refer to the API documentation.

Acknowledging Alerts

When an alert is activated at the warning or caution priority levels, it will cause the master warning or caution state to become active, respectively. Once active, the master warning and caution states remain that way until alerts are acknowledged. Acknowledging alerts can be done at each of the warning or caution priority levels by triggering the MASTER_WARNING_ACKNOWLEDGE and MASTER_CAUTION_ACKNOWLEDGE key events. The master warning and caution states can be read from the local vars L:Generic_Master_Warning_Active and L:Generic_Master_Caution_Active (0 = inactive, 1 = active).

Alert Debounce

For certain alerts, you may wish to delay displaying a CAS message for an alert until the alert has been active for a certain amount of time. CAS System provides support for this so that you don't have to manage your own timers. Debounce delays can be specified for an alert during registration:

// Adds a 2000-millisecond debounce delay to the 'generator-off' alert.
manager.register({
uuid: 'generator-off',
message: 'GEN OFF',
suffixes: ['L', 'R'],
debounceTime: 2000
});

When activating a debounced alert, the delay is tracked separately for each suffix and priority level. Deactivating an alert will reset the debounce timer for the deactivated suffix/priority level.

Inhibit States

A common scenario when working with CAS is needing to inhibit some alerts from being displayed at certain times, even if the conditions required to activate those alerts are met. This can be done through the use of inhibit states. Each alert can optionally declare a set of inhibit states such that when any of those inhibit states are active, the alert will not be displayed as a message, even if activated by cas_activate_alert. Inhibit states are activated and deactivated using the cas_activate_inhibit_state and cas_deactivate_inhibit_state event bus topics, respectively.

Here is an example of how to declare an inhibit state for an alert and how to activate and deactivate an inhibit state:

// Registers a FUEL TEMP MISCOMPARE alert that is inhibited by the 'in-air' state.
manager.register({
uuid: 'fuel-temp-miscompare',
message: 'FUEL TEMP MISCOMPARE',
inhibitedBy: ['in-air']
});

// Activates/deactivates the 'in-air' inhibit state based on whether the aircraft is on the ground.
bus.getSubscriber<AdcEvents>().on('on_ground').whenChanged().handle(isOnGround => {
bus.getPublisher<CasEvents>().pub(isOnGround ? 'cas_deactivate_inhibit_state' : 'cas_activate_inhibit_state', 'in-air', true, false);
});

If an inhibit state becomes active, any messages for already-active alerts affected by the inhibit state will be removed from the CAS. Once all inhibit states affecting an alert become deactivated and the alert is still active, a message for the alert will immediately appear on the CAS.

Consuming CAS State

You can retrieve information on the state of CAS in several different ways. If you want a list of all displayed messages, then you can get that directly from CasSystem via its casActiveMessageSubject property. This property exposes a SubscribableArray of messages in the order in which they should be displayed. It is particularly useful when rendering a CAS display component.

If you need information on whether a particular CAS alert is displayed as a message, deriving it from a list of all displayed messages can be cumbersome. Instead, you can subscribe to the cas_alert_displayed and cas_alert_hidden event bus topics, defined by the CasStateEvents interface. There is also a cas_alert_acknowledged topic for when an alert that is displayed as a message has been acknowledged.

Finally, you can subscribe to the cas_master_warning_active and cas_master_caution_active topics (also defined by CasStateEvents) to consume the master warning and master caution states.