Updated Construction and Activation phases concepts (markdown)

hvolmer
2020-02-11 18:28:12 -07:00
parent 0a36dd3e68
commit 48b448c356

@@ -1,27 +1,28 @@
### What is all this?
The Essentials system architecture is a loose collection of "things", generally real or logical Devices, that all need to relate to each other. In the interest of keeping Essentials extensible and flexible, we use an no-order collection of objects that should only have references to each other in the least-binding way possible. Meaning: Devices should be designed to be able to function without related objects present, and when they are present they should only retain loose reference to those other objects for memory management and later deconstruction as Essentials grows into a real-time configurable environment. (More to come on this subject.)
The Essentials system architecture is a loose collection of "things" - generally real or logical Devices - that all need to relate to each other. In the interest of keeping Essentials extensible and flexible, we use an non-ordered collection of objects that should only have references to each other in the least-binding way possible. Meaning: Devices should be designed to be able to function without related objects present, and when they are present they should only retain loose reference to those other objects for memory management and later deconstruction as Essentials grows into a real-time configurable environment.
In order to facilitate this loose coupling, Essentials devices go through five phases during the startup process: Construction and DeviceManager addition; pre-activation; activation; post-activation. We will describe what is optimal behavior for each of the steps below:
### 1. Construction & DeviceManager addition
### 1. Construction and addition to the `DeviceManager`
In general, construction is only to get the framework of the device in place. All devices are constructed in this stage. Rooms and fusion bridges included. Simple devices like IR driver devices, and devices with no controls can be completely spun up in this phase. All devices are added to the DeviceManager after they are constructed, but may not be fully functional.
In general, a device's constructor should only be used to get the "framework" of the device in place. All devices are constructed in this stage. Rooms and fusion bridges included. Simple devices like IR driver devices, and devices with no controls can be completely spun up in this phase. All devices are added to the `DeviceManager` after they are constructed, but may not be fully functional.
### 2. Pre-activation
This stage is rarely used. It exists to allow an opportunity for any necessary logic to take place before the main activation phase.
This stage is rarely used. It exists to allow an opportunity for any necessary logic to take place before the main activation phase.
### 3. Activation
This stage is the main phase of startup, and where most devices will get up and running, if they need additional startup behavior defined. The developer will code an optional overridden `CustomActivate()` method on the device class. This is where hardware ports may be set up; signals and feedbacks linked; UI drivers fired up; rooms linked to their displays and codec... With the exception of early-designed devices, most new Essentials classes do all of their startup here, rather than in constructor.
This stage is the main phase of startup, and where most devices will get up and running, if they need additional startup behavior defined. The developer will code an optional overridden `CustomActivate()` method on the device class. This is where hardware ports may be set up; signals and feedbacks linked; UI drivers fired up; rooms linked to their displays and codec... With the exception of early-designed devices, most new Essentials classes do all of their startup here, rather than in their constructors.
Remember that in your `CustomActivate()` method, you cannot assume that a device you depend on is alive and running yet. It may be activating later. You can depend on that device existing, and link yourself to it, but it may not be functional yet. In general, there should be no conditions in any Essentials code that depend on device startup sequence and ordering. All rooms, devices, classes should be able to function without linked devices being alive, and respond appropriately when they do come to life. Any post-activation steps can be done in step four below - and should be avoided in general.
Remember that in your `CustomActivate()` method, you cannot assume that a device you depend on is alive and running yet. It may be activating later. You _can_ depend on that device's existence, and link yourself to it, but it may not be functional yet. In general, there should be no conditions in any Essentials code that depend on device startup sequence and ordering. All rooms, devices, classes should be able to function without linked devices being alive, and respond appropriately when they do come to life. Any post-activation steps can be done in step four below - and should be avoided in general.
If the `CustomActivate` method is long, consider breaking it up into many smaller methods. This will enhance exception handling and debugging when things go wrong, with more-detailed stack traces, and makes for easier-to-read code.
```
Note: It is best-practice in Essentials to not write arbitrarily-timed startup sequences to ensure that a "system" or room is functional. Rather, we encourage the developer to use various properties and conditions on devices to aggregate together "room is ready" statuses that can trigger further action. This ensures that all devices can be up and alive, allowing them to be debugged within a program that may otherwise be misbehaving - as well as not making users and expensive developers wait for code to start up!
```
public override bool CustomActivate()
{
Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
@@ -35,9 +36,9 @@ public override bool CustomActivate()
### 4. Post-activation
This phase is used primarily to handle any logic in a device that might be dependent on another device where we need to ensure we've waited for the dependent device to be "activated" first. For example, if we look at the MicrophonePrivacyController class, this is a "virtual" device whose purpose is to control the mute state of microphones from one or more contact closure inputs as well as provide feedback via different colored LEDs as to the current mute state. This device doesn't actually represent any sort of physical hardware device, but rather relies on associating itself with other devices that represent digital inputs and relays as well as whatever device is responsible for preforming the actual muting of the microphones.
This phase is used primarily to handle any logic in a device that might be dependent on another device, and we need to ensure that we have waited for the dependent device to be activated first. For example, if we look at the `MicrophonePrivacyController` class, this is a "virtual" device whose purpose is to control the mute state of microphones from one or more contact closure inputs as well as provide feedback via different colored LEDs as to the current mute state. This device doesn't actually represent any sort of physical hardware device, but rather relies on associating itself with other devices that represent digital inputs and relays as well as whatever device is responsible for preforming the actual muting of the microphones.
We can see in the example below that during the CustomActivate() phase, we define a post-activation action via a lambda in AddPostActivationAction() that will execute during the post-activation phase. The purpose here is to check the state of the microphone mute and set the state of the relays that control the LEDs accordingly. We need to do this as a post-activation action because we need to make sure that the devices PrivacyDevice, RedLedRelay and GreenLedRelay are fully activated before we can attempt to interact with them.
We can see in the example below that during the `CustomActivate()` phase, we define a post-activation action via a lambda in `AddPostActivationAction()` that will execute during the post-activation phase. The purpose here is to check the state of the microphone mute and set the state of the relays that control the LEDs accordingly. We need to do this as a post-activation action because we need to make sure that the devices PrivacyDevice, RedLedRelay and GreenLedRelay are fully activated before we can attempt to interact with them.
**Example**
```