Multiple Fixes to markdown syntax

Trevor Payne
2020-05-05 17:31:49 -05:00
parent 988ceb6b23
commit 88cb3e798d
16 changed files with 325 additions and 303 deletions

@@ -1,12 +1,12 @@
## Essentials architecture # Essentials architecture
### Device and DeviceManager ## Device and DeviceManager
A `Device` (`PepperDash.Core.Device`) is a logical construct. It may represent a piece of hardware, a port, a socket, a collection of other devices/ports/constructs that define an operation, or any unit of logic that should be created at startup and exist independent of other devices. A `Device` (`PepperDash.Core.Device`) is a logical construct. It may represent a piece of hardware, a port, a socket, a collection of other devices/ports/constructs that define an operation, or any unit of logic that should be created at startup and exist independent of other devices.
`DeviceManager` (`PepperDash.Essentials.Core.DeviceManager`) is the collection of all Devices. The collection of everything we control, and other business logic in a system. See the list below for what is typical in the device manager. `DeviceManager` (`PepperDash.Essentials.Core.DeviceManager`) is the collection of all Devices. The collection of everything we control, and other business logic in a system. See the list below for what is typical in the device manager.
### Flat system design ## Flat system design
In Essentials, most everything we do is focused in one layer: The Devices layer. This layer interacts with the physical Crestron and other hardware and logical constructs underneath, and is designed so that we rarely act directly on the often-inconsistent hardware layer. The `DeviceManager` is responsible for containing all of the devices in this layer. In Essentials, most everything we do is focused in one layer: The Devices layer. This layer interacts with the physical Crestron and other hardware and logical constructs underneath, and is designed so that we rarely act directly on the often-inconsistent hardware layer. The `DeviceManager` is responsible for containing all of the devices in this layer.
@@ -30,7 +30,7 @@ In this flat design, we spin up devices, and then introduce them to their "cowor
This flat structure ensures that every device in a system exists in one place and may be shared and reused with relative ease. There is no hierarchy. This flat structure ensures that every device in a system exists in one place and may be shared and reused with relative ease. There is no hierarchy.
### Architecture drawing ## Architecture drawing
![Architecture overview](https://pepperdash.github.io/Essentials/arch-overview.png) ![Architecture overview](https://pepperdash.github.io/Essentials/arch-overview.png)

@@ -1,26 +1,27 @@
## Essentials architecture: DeviceManager activation # Essentials architecture: DeviceManager activation
### What is all this? ## 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 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. 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; addition to `DeviceManager`; pre-activation; activation; post-activation. We will describe what is optimal behavior for each of the steps below: In order to facilitate this loose coupling, Essentials devices go through five phases during the startup process: Construction; addition to `DeviceManager`; pre-activation; activation; post-activation. We will describe what is optimal behavior for each of the steps below:
#### Classes Referenced ### Classes Referenced
* `PepperDash.Core.Device` * `PepperDash.Core.Device`
* `PepperDash.Essentials.Core.EssentialsDevice` * `PepperDash.Essentials.Core.EssentialsDevice`
* `PepperDash.Essentials.Core.DeviceManager` * `PepperDash.Essentials.Core.DeviceManager`
* `PepperDash.Essentials.Core.Privacy.MicrophonePrivacyController` * `PepperDash.Essentials.Core.Privacy.MicrophonePrivacyController`
### 1. Construction and addition to the DeviceManager ## 1. Construction and addition to the DeviceManager
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. 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 ## 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 ## 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 their constructors. 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.
@@ -30,26 +31,27 @@ If the `CustomActivate()` method is long, consider breaking it up into many smal
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! 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!
``` ```cs
public override bool CustomActivate() public override bool CustomActivate()
{ {
Debug.Console(0, this, "Final activation. Setting up actions and feedbacks"); Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
SetupFunctions(); SetupFunctions();
SetupFeedbacks(); SetupFeedbacks();
EISC.SigChange += EISC_SigChange; EISC.SigChange += EISC_SigChange;
... ...
} }
``` ```
### 4. Post-activation ## 4. Post-activation
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 virtual-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 virtual-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** ### **Example**
```
```cs
public override bool CustomActivate() public override bool CustomActivate()
{ {
foreach (var i in Config.Inputs) foreach (var i in Config.Inputs)
@@ -58,30 +60,30 @@ public override bool CustomActivate()
if(input != null) if(input != null)
AddInput(input); AddInput(input);
} }
var greenLed = DeviceManager.GetDeviceForKey(Config.GreenLedRelay.DeviceKey) as GenericRelayDevice; var greenLed = DeviceManager.GetDeviceForKey(Config.GreenLedRelay.DeviceKey) as GenericRelayDevice;
if (greenLed != null) if (greenLed != null)
GreenLedRelay = greenLed; GreenLedRelay = greenLed;
else else
Debug.Console(0, this, "Unable to add Green LED device"); Debug.Console(0, this, "Unable to add Green LED device");
var redLed = DeviceManager.GetDeviceForKey(Config.RedLedRelay.DeviceKey) as GenericRelayDevice; var redLed = DeviceManager.GetDeviceForKey(Config.RedLedRelay.DeviceKey) as GenericRelayDevice;
if (redLed != null) if (redLed != null)
RedLedRelay = redLed; RedLedRelay = redLed;
else else
Debug.Console(0, this, "Unable to add Red LED device"); Debug.Console(0, this, "Unable to add Red LED device");
AddPostActivationAction(() => { AddPostActivationAction(() => {
CheckPrivacyMode(); CheckPrivacyMode();
PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange; PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange;
PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange; PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange;
}); });
initialized = true; initialized = true;
return base.CustomActivate(); return base.CustomActivate();
} }
void CheckPrivacyMode() void CheckPrivacyMode()
{ {
if (PrivacyDevice != null) if (PrivacyDevice != null)
@@ -95,20 +97,20 @@ void CheckPrivacyMode()
} }
``` ```
### Activation exceptions ## Activation exceptions
Each of the three activation phases operates in a try/catch block for each device. This way if one device has a fatal failure during activation, the failure will be logged and the system can continue to activate. This allows the developer to chase down multiple issues per load while testing, or to fix configuration omissions/errors as a group rather than one-at-a-time. A program can theoretically be fully-initialized and have many or all devices fail. We generally do not want to depend on exception handling to log device failures. Construction and activation code should have plenty of null checks, parameter validity checks, and debugging output to prevent exceptions from occurring. `String.IsEmptyOrNull(myString)` and `if(myObject == null)` are your friends. Invite them often. Each of the three activation phases operates in a try/catch block for each device. This way if one device has a fatal failure during activation, the failure will be logged and the system can continue to activate. This allows the developer to chase down multiple issues per load while testing, or to fix configuration omissions/errors as a group rather than one-at-a-time. A program can theoretically be fully-initialized and have many or all devices fail. We generally do not want to depend on exception handling to log device failures. Construction and activation code should have plenty of null checks, parameter validity checks, and debugging output to prevent exceptions from occurring. `String.IsEmptyOrNull(myString)` and `if(myObject == null)` are your friends. Invite them often.
### Interdependence ## Interdependence
In any real-world system, devices and business logic need to talk to each other, otherwise, what's the point of all this coding? When creating your classes and configuration, it is best practice to _try _not to "plug" one device into another during construction or activation. For example your touchpanel controller class has a `Display1` property that holds the display-1 object. Rather, it may be better to refer to the device as it is stored in the `DeviceManager` when it's needed using the static `DeviceManager.GetDeviceForKey(key)` method to get a reference to the device, which can be cast using various interfaces/class types, and then interacted with. This prevents objects from being referenced in places where the developer may later forget to dereference them, causing memory leak. This will become more important as Essentials becomes more able to be reconfigured at runtime. In any real-world system, devices and business logic need to talk to each other, otherwise, what's the point of all this coding? When creating your classes and configuration, it is best practice to _try_ not to "plug" one device into another during construction or activation. For example your touchpanel controller class has a `Display1` property that holds the display-1 object. Rather, it may be better to refer to the device as it is stored in the `DeviceManager` when it's needed using the static `DeviceManager.GetDeviceForKey(key)` method to get a reference to the device, which can be cast using various interfaces/class types, and then interacted with. This prevents objects from being referenced in places where the developer may later forget to dereference them, causing memory leak. This will become more important as Essentials becomes more able to be reconfigured at runtime.
As an example, [connecting-based routing](https://github.com/PepperDash/Essentials/wiki/Connection-based-routing#essentials-connection-based-routing) uses these methods. When a route is requested, the collection of tielines and devices is searched for the devices and paths necessary to complete a route, but there are no devices or tie lines that are object-referenced in running code. It can all be torn down and reconfigured without any memory-management dereferencing, setting things to null. As an example, [connecting-based routing](https://github.com/PepperDash/Essentials/wiki/Connection-based-routing#essentials-connection-based-routing) uses these methods. When a route is requested, the collection of tielines and devices is searched for the devices and paths necessary to complete a route, but there are no devices or tie lines that are object-referenced in running code. It can all be torn down and reconfigured without any memory-management dereferencing, setting things to null.
### The goal ## The goal
Robust C#-based system code should not depend on "order" or "time" to get running. We do not need to manage the order of our startup in this environment. Our Room class may come alive before our DSP and or Codec, and the Room is responsible for handling things when those devices become available. The UI layer is responsible for blocking the UI or providing status when the Room's requirements are coming alive, or if something has gone away. We use events or `Feedbacks` to notify dependents that other devices/classes are ready or not, but we do not prevent continued construction/activation of the system when many of these events don't happen, or don't happen in a timely fashion. This removes the need for startup management, which is often prolonged and consumes _tons_ of developer/installer time. A fully-loaded Essentials system may go through activation in several seconds, with all devices concurrently getting themselves going, where legacy code may take 10 minutes. Robust C#-based system code should not depend on "order" or "time" to get running. We do not need to manage the order of our startup in this environment. Our Room class may come alive before our DSP and or Codec, and the Room is responsible for handling things when those devices become available. The UI layer is responsible for blocking the UI or providing status when the Room's requirements are coming alive, or if something has gone away. We use events or `Feedbacks` to notify dependents that other devices/classes are ready or not, but we do not prevent continued construction/activation of the system when many of these events don't happen, or don't happen in a timely fashion. This removes the need for startup management, which is often prolonged and consumes _tons_ of developer/installer time. A fully-loaded Essentials system may go through activation in several seconds, with all devices concurrently getting themselves going, where legacy code may take 10 minutes.
When designing new Device-based classes, be it rooms, devices, port controllers, bridges, make them as independent as possible. They could exist alone in a program with no required partner objects, and just quietly exist without failing. We want the system to be fast and flexible, and keeping the interdependence between objects at a minimum improves this flexibility into the future. When designing new Device-based classes, be it rooms, devices, port controllers, bridges, make them as independent as possible. They could exist alone in a program with no required partner objects, and just quietly exist without failing. We want the system to be fast and flexible, and keeping the interdependence between objects at a minimum improves this flexibility into the future.
Next: [More architecture](Arch-topics) Next: [More architecture](Arch-topics)

@@ -1,4 +1,4 @@
## Essentials Configurable System Lifecycle # Essentials Configurable System Lifecycle
The diagram below describes how Essentials gets a program up and running. The diagram below describes how Essentials gets a program up and running.

@@ -1,12 +1,12 @@
## Essentials architecture # Essentials architecture
### Summary ## Summary
PepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs. PepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs.
Essentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other. Essentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other.
### Framework Libraries ## Framework Libraries
The table below is a guide to understand the basic organization of code concepts within the various libraries that make up the architecture. The table below is a guide to understand the basic organization of code concepts within the various libraries that make up the architecture.
@@ -16,4 +16,4 @@ The diagram below shows the reference dependencies that exist between the differ
![Architecture drawing](https://pepperdash.github.io/Essentials/arch-high-level.png) ![Architecture drawing](https://pepperdash.github.io/Essentials/arch-high-level.png)
Next: [Architecture](Arch-1) Next: [Architecture](Arch-1)

@@ -1,14 +1,14 @@
## Configuration topics # Configuration topics
Configuration is central to Essentials. On this page we will cover configuration-related topics, including the important concept of configure-first and some details about the config file process. Configuration is central to Essentials. On this page we will cover configuration-related topics, including the important concept of configure-first and some details about the config file process.
#### Classes Referenced ## Classes Referenced
* `PepperDash.Essentials.Core.Config.DeviceConfig` * `PepperDash.Essentials.Core.Config.DeviceConfig`
### Configure-first development ## Configure-first development
### Framework Libraries ## Framework Libraries
The table below is meant to serve as a guide to understand the basic organization of code concepts within the various libraries that make up the architecture. The table below is meant to serve as a guide to understand the basic organization of code concepts within the various libraries that make up the architecture.
@@ -34,13 +34,13 @@ In Essentials, most everything we do is focused in one layer: The Devices layer.
Types of devices: Types of devices:
- Rooms * Rooms
- Sources * Sources
- Codecs, DSPs, displays, routing hardware * Codecs, DSPs, displays, routing hardware
- IR Ports, Com ports, SSh Clients, ... * IR Ports, Com ports, SSh Clients, ...
- Occupancy sensors and relay-driven devices * Occupancy sensors and relay-driven devices
- Logical devices that manage multiple devices and other business, like shade or lighting scene controllers * Logical devices that manage multiple devices and other business, like shade or lighting scene controllers
- Fusion connectors to rooms * Fusion connectors to rooms
A Device doesn't always represent a physical piece of hardware, but rather a logical construct that "does something" and is used by one or more other devices in the running program. For example, we create a room device, and its corresponding Fusion device, and that room has a Cisco codec device, with an attached SSh client device. All of these lie in a flat collection in the `DeviceManager`. A Device doesn't always represent a physical piece of hardware, but rather a logical construct that "does something" and is used by one or more other devices in the running program. For example, we create a room device, and its corresponding Fusion device, and that room has a Cisco codec device, with an attached SSh client device. All of these lie in a flat collection in the `DeviceManager`.
@@ -66,7 +66,7 @@ Concepts (link)
Room and touchpanel activation (link) Room and touchpanel activation (link)
#### Configure-first development #### Configure first development
One of the primary concepts that has been adopted and must be adhered to when writing for Essentials framework is the concept of "configure first." The simple version is: Write what you need to do in the related configuration file (and configuration tool) first, then write the code that runs from that configuration. This ensures that the running code can actually be configured in the "flat" structure of devices and rooms that Essentials uses. One of the primary concepts that has been adopted and must be adhered to when writing for Essentials framework is the concept of "configure first." The simple version is: Write what you need to do in the related configuration file (and configuration tool) first, then write the code that runs from that configuration. This ensures that the running code can actually be configured in the "flat" structure of devices and rooms that Essentials uses.
@@ -76,11 +76,11 @@ Often, code is written and tested first without consideration for configurabilit
At the heart of the Essentials framework is the configuration system. While not technically necessary for a system written with the Essentials framework, it is the preferred and, currently, the only way to build an Essentials system. The configuration file is JSON, and well-defined (but not well documented, yet). It is comprised of blocks: At the heart of the Essentials framework is the configuration system. While not technically necessary for a system written with the Essentials framework, it is the preferred and, currently, the only way to build an Essentials system. The configuration file is JSON, and well-defined (but not well documented, yet). It is comprised of blocks:
- info (object) Contains metadata about the config file * info (object) Contains metadata about the config file
- devices (array) Contains, well, the devices we intend to build and load * devices (array) Contains, well, the devices we intend to build and load
- rooms (array, typically only one) Contains the rooms we need * rooms (array, typically only one) Contains the rooms we need
- sourceLists (object) Used by one or more rooms to represent list(s) of sources for those rooms * sourceLists (object) Used by one or more rooms to represent list(s) of sources for those rooms
- tieLines (array) Used by the routing system to discover routing between sources and displays * tieLines (array) Used by the routing system to discover routing between sources and displays
In addition, a downloaded Portal config file will most likely be in a template/system form, meaning that the file contains two main objects, representing the template configuration and its system-level overrides. Other metadata, such as Portal UUIDs or URLs may be present. In addition, a downloaded Portal config file will most likely be in a template/system form, meaning that the file contains two main objects, representing the template configuration and its system-level overrides. Other metadata, such as Portal UUIDs or URLs may be present.
@@ -88,7 +88,7 @@ At startup, the configuration file is read, and a ReadyEvent is fired. Upon bein
For example, a `DeviceConfig` object: For example, a `DeviceConfig` object:
```csharp ```cs
namespace PepperDash.Essentials.Core.Config namespace PepperDash.Essentials.Core.Config
{ {
public class DeviceConfig public class DeviceConfig

@@ -1,8 +1,8 @@
### Unifying communication methods # Unifying communication methods
In networked A/V systems, devices can use many different methods of communication: COM ports, TCP/IP sockets, Telnet, SSh. Generally, the data protocol and commands that are sent and received using any of these methods are the same, and it is not necessary for a device to know the details of the communication method it is using. A Samsung MDC protocol display in room 1 using RS232 speaks the same language as another Samsung MDC does in the next room using TCP/IP. For these, and most cases where the device doesn't need to know its communication method, we introduce the `IBasicCommunication` interface. In networked A/V systems, devices can use many different methods of communication: COM ports, TCP/IP sockets, Telnet, SSh. Generally, the data protocol and commands that are sent and received using any of these methods are the same, and it is not necessary for a device to know the details of the communication method it is using. A Samsung MDC protocol display in room 1 using RS232 speaks the same language as another Samsung MDC does in the next room using TCP/IP. For these, and most cases where the device doesn't need to know its communication method, we introduce the `IBasicCommunication` interface.
#### Classes Referenced ## Classes Referenced
* `PepperDash.Core.IBasicCommunication` * `PepperDash.Core.IBasicCommunication`
* `PepperDash.Core.ISocketStatus` * `PepperDash.Core.ISocketStatus`
@@ -12,11 +12,11 @@ In networked A/V systems, devices can use many different methods of communicatio
* `PepperDash.Essentials.Core.ComPortController` * `PepperDash.Essentials.Core.ComPortController`
* `PepperDash.Essentilas.Core.StatusMonitorBase` * `PepperDash.Essentilas.Core.StatusMonitorBase`
#### IBasicCommunication and ISocketStatus ## IBasicCommunication and ISocketStatus
All common communication controllers will implement the `IBasicCommunication` interface, which is an extension of `ICommunicationReceiver`. This defines receive events, connection state properties, and send methods. Devices that need to use COM port, TCP, SSh or other similar communication will require an `IBasicCommunication` type object to be injected at construction time. All common communication controllers will implement the `IBasicCommunication` interface, which is an extension of `ICommunicationReceiver`. This defines receive events, connection state properties, and send methods. Devices that need to use COM port, TCP, SSh or other similar communication will require an `IBasicCommunication` type object to be injected at construction time.
```csharp ```cs
/// <summary> /// <summary>
/// An incoming communication stream /// An incoming communication stream
/// </summary> /// </summary>
@@ -50,11 +50,11 @@ public interface ISocketStatus : IBasicCommunication
} }
``` ```
#### Developing devices with communication ### Developing devices with communication
Essentials uses dependency injection concepts in its start up phase. Simply, most devices use the same methods of communication, and are often communication-agnostic. During the build-from-configuration phase, the communication method device is instantiated, and then injected into the device that will use it. Since the communication device is of `IBasicCommunication`, the device controller receiving it knows that it can do things like listen for events, send text, or be notified when sockets change. Essentials uses dependency injection concepts in its start up phase. Simply, most devices use the same methods of communication, and are often communication-agnostic. During the build-from-configuration phase, the communication method device is instantiated, and then injected into the device that will use it. Since the communication device is of `IBasicCommunication`, the device controller receiving it knows that it can do things like listen for events, send text, or be notified when sockets change.
#### Device Factory, Codec example ### Device Factory, Codec example
![Communication Device factory](https://pepperdash.github.io/Essentials/comm-device-factory.png) ![Communication Device factory](https://pepperdash.github.io/Essentials/comm-device-factory.png)
@@ -66,7 +66,7 @@ The DeviceManager will contain two new devices after this: The Cisco codec, and
`PepperDash.Core.GenericTcpIpClient`, `GenericSshClient` and some other socket controllers implement `ISocketStatus`, which is an extension of `IBasicCommunication`. This interface reveals connection status properties and events. `PepperDash.Core.GenericTcpIpClient`, `GenericSshClient` and some other socket controllers implement `ISocketStatus`, which is an extension of `IBasicCommunication`. This interface reveals connection status properties and events.
```csharp ```cs
public interface ISocketStatus : IBasicCommunication public interface ISocketStatus : IBasicCommunication
{ {
event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange; event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;

@@ -8,7 +8,7 @@ At a high level, the idea is to define a template of all of the common configura
## Top Level Object Structure (Double Config) ## Top Level Object Structure (Double Config)
``` ```cs
{ {
// This object is deserialized to type PepperDash.Essentials.Core.Config.EssentialsConfig // This object is deserialized to type PepperDash.Essentials.Core.Config.EssentialsConfig
@@ -26,27 +26,26 @@ At a high level, the idea is to define a template of all of the common configura
} }
``` ```
## Object Structure for `template` and `system` (`PepperDash.Essentials.Core.EssentialsConfig`) ## Object Structure for `template` and `system` (`PepperDash.Essentials.Core.EssentialsConfig`)
``` ``` js
{ {
"info": { "info": {
// This object is deserialized to type PepperDash.Essentials.Core.Config.InfoConfig // This object is deserialized to type PepperDash.Essentials.Core.Config.InfoConfig
// Contains information about the system/configuration // Contains information about the system/configuration
}, },
"devices": [ "devices": [
// This object is deserialized to type List<PepperDash.Essentials.Core.Config.DeviceConfig> // This object is deserialized to type List<PepperDash.Essentials.Core.Config.DeviceConfig>
// An array of devices // An array of devices
], ],
"rooms": [ "rooms": [
// This object is deserialized to type List<PepperDash.Essentials.Core.Config.DeviceConfig> // This object is deserialized to type List<PepperDash.Essentials.Core.Config.DeviceConfig>
// An array of rooms. These are not automatically deserialized // An array of rooms. These are not automatically deserialized
], ],
"tielines":[ "tielines":[
// An array of tie lines that describe the connections between routing ports on devices // An array of tie lines that describe the connections between routing ports on devices
], ],
"sourceLists":{ "sourceLists":{
// This object is deserialized to type Dictionary<string, Dictionary<string, PepperDash.Essentials.Core.SourceListItem>> // This object is deserialized to type Dictionary<string, Dictionary<string, PepperDash.Essentials.Core.SourceListItem>>
// An object that contains a collection // An object that contains a collection
}, },
@@ -58,80 +57,87 @@ At a high level, the idea is to define a template of all of the common configura
``` ```
## The Template and System Concept (Merging Configurations) ## The Template and System Concept (Merging Configurations)
In order to understand how and why we use a double configuration concept, it's important to understand the relationship between a Template and a System in Portal.  A System represents a physical installed group of hardware(either currently or in the future), acting together usually as part of a single control system program.  A system MUST inherit from a Template.  A Template represents the common elements of one or more systems. In order to understand how and why we use a double configuration concept, it's important to understand the relationship between a Template and a System in Portal.  A System represents a physical installed group of hardware(either currently or in the future), acting together usually as part of a single control system program.  A system MUST inherit from a Template.  A Template represents the common elements of one or more systems.
The idea being that configuration values that are common to all systems can be stored in the configuration for the template.  Then, any configuration values that are unique to a particular system cane be stored in the configuration of the System.  By "merging" the System configuration values over top of the Template configuration values, the resulting data contains all of the values that should be shared by each system that inherits from a common template, as well as the unique values for each individual system. The idea being that configuration values that are common to all systems can be stored in the configuration for the template.  Then, any configuration values that are unique to a particular system cane be stored in the configuration of the System.  By "merging" the System configuration values over top of the Template configuration values, the resulting data contains all of the values that should be shared by each system that inherits from a common template, as well as the unique values for each individual system.
Below is an example of a double configuration containing both template and system properties. Below is an example of a double configuration containing both template and system properties.
``` ```JSON
{ {
"template": { "template": {
"info": { "info": {
"name": "Template Name", "name": "Template Name",
"description": "A 12 person conference room" "description": "A 12 person conference room"
}, },
"devices": [ "devices": [
], ],
"rooms": [ "rooms": [
] ]
}, },
"system": { "system": {
"info": { "info": {
"name": "System Name", "name": "System Name",
"myNewSystemProperty": "Some Value" "myNewSystemProperty": "Some Value"
}, },
"devices": [ "devices": [
], ],
"rooms": [ "rooms": [
] ]
} }
} }
``` ```
Below is an example of the result of merging the above double configuration example into a single configuration. Below is an example of the result of merging the above double configuration example into a single configuration.
```
{
"info": {
"name": "System Name", // Since this property existed in both the template and system, the system value replaces the template value after the merge
"description": "A 12 person conference room", // This property existed only in the template and is unchanged after the merge
"myNewSystemProperty": "Some Value" // This property existed only in the system and is unchanged after the merge
},
"devices": [
], ```JSON
"rooms": [ {
"info": {
] "name": "System Name", // Since this property existed in both the template and system, the system value replaces the template value after the merge
"description": "A 12 person conference room", // This property existed only in the template and is unchanged after the merge
"myNewSystemProperty": "Some Value" // This property existed only in the system and is unchanged after the merge
},
"devices": [
],
"rooms": [
]
} }
``` ```
--- ---
## Device Object Structure ## Device Object Structure
The devices array is meant to hold a series of device objects.  The basic device object structure is defined below. The devices array is meant to hold a series of device objects.  The basic device object structure is defined below.
```
```JSON
{ {
"key": "someUniqueString", // *required* a unique string "key": "someUniqueString", // *required* a unique string
"name": "A friendly Name", // *required* a friendly name meant for display to users "name": "A friendly Name", // *required* a friendly name meant for display to users
"type": "exampleType", // *required* the type identifier for this object. "type": "exampleType", // *required* the type identifier for this object.
"group": "exampleGroup", // *required* the group identifier for this object. This really equates to a category for the device, "group": "exampleGroup", // *required* the group identifier for this object. This really equates to a category for the device,
// such as "lighting" or "displays" and may be deprecated in future in favor of "category" // such as "lighting" or "displays" and may be deprecated in future in favor of "category"
"uid":0, // *required* a unique numeric identifier for each device "uid":0, // *required* a unique numeric identifier for each device
"properties": { // *required* an object where the configurable properties of the device are contained "properties": { // *required* an object where the configurable properties of the device are contained
"control": { // an object to contain all of the properties to connect to and control the device "control": { // an object to contain all of the properties to connect to and control the device
"method": "ssh", // the control method used by this device "method": "ssh", // the control method used by this device
"tcpSshProperties": { // contains the necessary properties for the specified method "tcpSshProperties": { // contains the necessary properties for the specified method
"address": "1.2.3.4", "address": "1.2.3.4",
"port": 22, "port": 22,
"username": "admin", "username": "admin",
"password": "uncrackablepassword" "password": "uncrackablepassword"
} }
}, },
"someCustomProperty": "I Love Tacos!" "someCustomProperty": "I Love Tacos!"
} }
// Do NOT add any custom data at the top level of the device object. All custom data must be in the properties object. // Do NOT add any custom data at the top level of the device object. All custom data must be in the properties object.
} }
``` ```
@@ -141,33 +147,35 @@ Some additional details about specific properties that are important to note:
* "uid": This value also needs to be unique for reasons related to configuration tools and template/system merging * "uid": This value also needs to be unique for reasons related to configuration tools and template/system merging
* "type": Think of this as a way to identify what specific module you might associate with this device.  In Essentials, this value is used to determine what class will be instantiated for the device (ex. "necmpsx" or "samsungMdc" for two types of displays) * "type": Think of this as a way to identify what specific module you might associate with this device.  In Essentials, this value is used to determine what class will be instantiated for the device (ex. "necmpsx" or "samsungMdc" for two types of displays)
* "properties":  This object is used to store both specific and miscellaneous data about the device. * "properties":  This object is used to store both specific and miscellaneous data about the device.
* Specific data, like that shown above in the "control" object has a pre-defined structure. * Specific data, like that shown above in the "control" object has a pre-defined structure.
* Other data must be stored as objects or new properties inside the "properties" object such as "someCustomProperty" in the example above. * Other data must be stored as objects or new properties inside the "properties" object such as "someCustomProperty" in the example above.
* Do NOT add any additional properties at the top level of the device object.  All custom data must be in the "properties" object. * Do NOT add any additional properties at the top level of the device object.  All custom data must be in the "properties" object.
## The Device Properties.Control Object ## The Device Properties.Control Object
The control object inside properties has some reserved properties that are used by configuration tools and Essentials that require some caution. The control object inside properties has some reserved properties that are used by configuration tools and Essentials that require some caution.
```
```JSON
{ {
"properties": { // *required* an object where the configurable properties of the device are contained "properties": { // *required* an object where the configurable properties of the device are contained
"control": { // an object to contain all of the properties to connect to and control the device "control": { // an object to contain all of the properties to connect to and control the device
// Example of the reserved properties for a socket based port (ssh, tcpIp, udp) // Example of the reserved properties for a socket based port (ssh, tcpIp, udp)
"method": "ssh", // the control method used by this device "method": "ssh", // the control method used by this device
"tcpSshProperties": { // contains the necessary properties for the specified method "tcpSshProperties": { // contains the necessary properties for the specified method
"address": "1.2.3.4", // IP Address or hostname "address": "1.2.3.4", // IP Address or hostname
"port": 22, "port": 22,
"username": "admin", "username": "admin",
"password": "uncrackablepassword", "password": "uncrackablepassword",
"autoReconnect": true, // If true, the client will attempt to re-connect if the connection is broken externally "autoReconnect": true, // If true, the client will attempt to re-connect if the connection is broken externally
"AutoReconnectIntervalMs": 2000 // The time between re-connection attempts "AutoReconnectIntervalMs": 2000 // The time between re-connection attempts
}, },
// Example of the reserved properties for a Com port // Example of the reserved properties for a Com port
"method": "com", "method": "com",
"controlPortNumber": 1, // The number of the com port on the device specified by controlPortDevKey "controlPortNumber": 1, // The number of the com port on the device specified by controlPortDevKey
"controlPortDevKey": "processor", // The key of the device where the com port is located "controlPortDevKey": "processor", // The key of the device where the com port is located
"comParams": { // This object contains all of the com spec properties for the com port "comParams": { // This object contains all of the com spec properties for the com port
"hardwareHandshake": "None", "hardwareHandshake": "None",
"parity": "None", "parity": "None",
"protocol": "RS232", "protocol": "RS232",
"baudRate": 9600, "baudRate": 9600,
@@ -175,100 +183,105 @@ The control object inside properties has some reserved properties that are used
"softwareHandshake": "None", "softwareHandshake": "None",
"stopBits": 1 "stopBits": 1
} }
} }
} }
} }
``` ```
--- ---
## Device Merging ## Device Merging
The following examples illustrate how the device key and uid properties affect how devices are merged together in a double configuration scenario.  In order for a template device and a system device to merge, they must have the same key and uid values The following examples illustrate how the device key and uid properties affect how devices are merged together in a double configuration scenario.  In order for a template device and a system device to merge, they must have the same key and uid values
``` ```JSON
{ {
"template": { "template": {
"info": { "info": {
"name": "Template Name", "name": "Template Name",
"description": "A 12 person conference room" "description": "A 12 person conference room"
}, },
"devices": [ "devices": [
{ // This is the template device { // This is the template device
"key": "display-1", "key": "display-1",
"name": "Display", "name": "Display",
"type": "samsungMdc", "type": "samsungMdc",
"group": "displays", "group": "displays",
"uid":0, "uid":0,
"properties": { "properties": {
"control": { "control": {
"method": "ssh", "method": "ssh",
"tcpSshProperties": { "tcpSshProperties": {
"address": "", // Note that at the template level we won't know the actual IP address so this value is left empty "address": "", // Note that at the template level we won't know the actual IP address so this value is left empty
"port": 22, "port": 22,
"username": "admin", "username": "admin",
"password": "uncrackablepassword" "password": "uncrackablepassword"
} }
} }
} }
} }
], ],
"rooms": [ "rooms": [
] ]
}, },
"system": { "system": {
"info": { "info": {
"name": "System Name", "name": "System Name",
"myNewSystemProperty": "Some Value" "myNewSystemProperty": "Some Value"
}, },
"devices": [ "devices": [
{ // This is the system device { // This is the system device
"key": "display-1", "key": "display-1",
"uid":0, "uid":0,
"properties": { "properties": {
"control": { "control": {
"tcpSshProperties": { "tcpSshProperties": {
"address": "10.10.10.10" // Note that the actual IP address is specified at the system level "address": "10.10.10.10" // Note that the actual IP address is specified at the system level
} }
} }
} }
} }
], ],
"rooms": [ "rooms": [
] ]
} }
} }
``` ```
Below is an example of the result of merging the above double configuration example into a single configuration.   Below is an example of the result of merging the above double configuration example into a single configuration.  
```
```JSON
{ {
"info": { "info": {
"name": "System Name", "name": "System Name",
"description": "A 12 person conference room", "description": "A 12 person conference room",
"myNewSystemProperty": "Some Value" "myNewSystemProperty": "Some Value"
}, },
"devices": [ "devices": [
{ {
"key": "display-1", "key": "display-1",
"name": "Display", "name": "Display",
"type": "samsungMdc", "type": "samsungMdc",
"group": "displays", "group": "displays",
"uid":0, "uid":0,
"properties": { "properties": {
"control": { "control": {
"method": "ssh", "method": "ssh",
"tcpSshProperties": { "tcpSshProperties": {
"address": "10.10.10.10", // Note that the merged device object inherits all of the template "address": "10.10.10.10", // Note that the merged device object inherits all of the template
// properties and overwrites the template address property with the system value // properties and overwrites the template address property with the system value
"port": 22, "port": 22,
"username": "admin", "username": "admin",
"password": "uncrackablepassword" "password": "uncrackablepassword"
} }
} }
} }
} }
], ],
"rooms": [ "rooms": [
] ]
} }
``` ```

@@ -1,16 +1,16 @@
## Essentials connection-based routing # Essentials connection-based routing
### TL;DR ## TL;DR
Routing is defined by a connection graph or a wiring diagram. Routeable devices are sources, midpoints, or destinations. Devices are connected by tie lines. Tie lines represent the cables connecting devices, and are of type audio, video or both. Routes are made by telling a destination to get an audio/video/combined route from a source. Routing is defined by a connection graph or a wiring diagram. Routeable devices are sources, midpoints, or destinations. Devices are connected by tie lines. Tie lines represent the cables connecting devices, and are of type audio, video or both. Routes are made by telling a destination to get an audio/video/combined route from a source.
### Summary ## Summary
Essentials routing is described by defining a graph of connections between devices in a system, typically in configuration. The audio, video and combination connections are like a wiring diagram. This graph is a collection of devices and tie lines, each tie line connecting a source device, source output port, destination device and destination input port. Tie lines are logically represented as a collection. Essentials routing is described by defining a graph of connections between devices in a system, typically in configuration. The audio, video and combination connections are like a wiring diagram. This graph is a collection of devices and tie lines, each tie line connecting a source device, source output port, destination device and destination input port. Tie lines are logically represented as a collection.
When routes are to be executed, Essentials will use this connection graph to decide on routes from source to destination. A method call is made on a destination, which says “destination, find a way for source xyz to get to you.” An algorithm analyzes the tie lines, instantly walking backwards from the destination, down every connection until it finds a complete path from the source. If a connected path is found, the algorithm then walks forward through all midpoints to the destination, executing switches as required until the full route is complete. The developer or configurer only needs to say “destination, get source xyz” and Essentials figures out how, regardless of what devices lie in between. When routes are to be executed, Essentials will use this connection graph to decide on routes from source to destination. A method call is made on a destination, which says “destination, find a way for source xyz to get to you.” An algorithm analyzes the tie lines, instantly walking backwards from the destination, down every connection until it finds a complete path from the source. If a connected path is found, the algorithm then walks forward through all midpoints to the destination, executing switches as required until the full route is complete. The developer or configurer only needs to say “destination, get source xyz” and Essentials figures out how, regardless of what devices lie in between.
#### Classes Referenced ### Classes Referenced
* `PepperDash.Essentials.Core.Routing.IRoutingSource` * `PepperDash.Essentials.Core.Routing.IRoutingSource`
* `PepperDash.Essentials.Core.Routing.IRoutingOutputs` * `PepperDash.Essentials.Core.Routing.IRoutingOutputs`
@@ -19,8 +19,7 @@ When routes are to be executed, Essentials will use this connection graph to dec
* `PepperDash.Essentials.Core.Routing.IRoutingSinkNoSwitching` * `PepperDash.Essentials.Core.Routing.IRoutingSinkNoSwitching`
* `PepperDash.Essentials.Core.Routing.IRoutingSinkWithSwitching` * `PepperDash.Essentials.Core.Routing.IRoutingSinkWithSwitching`
## Example system, a simple presentation system
### Example system, a simple presentation system
The diagram below shows the connections in a simple presentation system, with a few variations in connection paths. Example routes will be described following the diagram. The diagram below shows the connections in a simple presentation system, with a few variations in connection paths. Example routes will be described following the diagram.

@@ -1,4 +1,4 @@
## Methods of Debugging # Methods of Debugging
1. You can use Visual Studio step debugging 1. You can use Visual Studio step debugging
- Pros: - Pros:
@@ -35,7 +35,7 @@ In code, the most useful method is `Debug.Console()` which has several overloads
All statements printed to console are prefixed by a timestamp which can be greatly helpful in debugging order of operations. All statements printed to console are prefixed by a timestamp which can be greatly helpful in debugging order of operations.
```csharp ```cs
// The most basic use, sets the level (0) and the message to print. // The most basic use, sets the level (0) and the message to print.
Debug.Console(0, "Hello World"); Debug.Console(0, "Hello World");
// prints: [timestamp]App 1:Hello World // prints: [timestamp]App 1:Hello World
@@ -67,7 +67,7 @@ Gets or sets the current debug level where 0 is the lowest setting and 2 is the
Example: Example:
``` ```sh
RMC3>appdebug:1 // Gets current level RMC3>appdebug:1 // Gets current level
RMC3>AppDebug level = 0 RMC3>AppDebug level = 0
@@ -83,7 +83,7 @@ Prints in the form [deviceKey] deviceName
Example: Example:
``` ```sh
// Get the list of devices for program 1 // Get the list of devices for program 1
RMC3>devlist:1 RMC3>devlist:1
@@ -124,7 +124,7 @@ Gets the list of public properties on the device with the corresponding `deviceK
Example: Example:
``` ```sh
// Get the properties on the device with Key 'cec-1-cec' // Get the properties on the device with Key 'cec-1-cec'
// This device happens to be a CEC port on a DM-TX-201-C's HDMI input // This device happens to be a CEC port on a DM-TX-201-C's HDMI input
RMC3>devprops:1 cec-1-cec RMC3>devprops:1 cec-1-cec
@@ -169,7 +169,7 @@ Gets the list of public methods available on the device
Example: Example:
``` ```sh
// Get the methods on the device with Key 'cec-1-cec' // Get the methods on the device with Key 'cec-1-cec'
RMC3>devmethods:1 cec-1-cec RMC3>devmethods:1 cec-1-cec
[ [
@@ -214,7 +214,7 @@ This command is most useful for testing without access to hardware as it allows
Example: Example:
``` ```sh
// This command will call the SendText(string text) method on the // This command will call the SendText(string text) method on the
// device with the Key 'cec-1-cec' and pass in "hello world" as the // device with the Key 'cec-1-cec' and pass in "hello world" as the
// argument parameter. On this particular device, it would cause // argument parameter. On this particular device, it would cause

@@ -1,8 +1,8 @@
## Feedback classes # Feedback classes
The various Feedback classes are like "signals". They can enable various events, and are designed to be used where we need small data events to be sent without requiring custom handlers. The various Feedback classes are like "signals". They can enable various events, and are designed to be used where we need small data events to be sent without requiring custom handlers.
### Why Feedbacks? ## Why Feedbacks?
We have been writing "code" in an environment, Simpl, for years and have taken for granted the power that signals in that environment give us. With the release of the ability to develop in C#, we have been handed a massive set of tools to potentially make our lives better, but because of the age and limited scope of the .NET 3.5 Compact Framework, many of the things that have been very easy to do in the past have become challenging or bulky to write. Crestron classes have things called "Sigs", which are a less-functional version of the signal that we used in Simpl, but we have no ability to use our own Sigs around our own classes. This forces us to break out of the constraints and mindset of Simpl programming, but simultaneously keeps us partially bound to the "old way" of doing things. We have been writing "code" in an environment, Simpl, for years and have taken for granted the power that signals in that environment give us. With the release of the ability to develop in C#, we have been handed a massive set of tools to potentially make our lives better, but because of the age and limited scope of the .NET 3.5 Compact Framework, many of the things that have been very easy to do in the past have become challenging or bulky to write. Crestron classes have things called "Sigs", which are a less-functional version of the signal that we used in Simpl, but we have no ability to use our own Sigs around our own classes. This forces us to break out of the constraints and mindset of Simpl programming, but simultaneously keeps us partially bound to the "old way" of doing things.
@@ -10,7 +10,7 @@ Signals as we have known them since Simpl came around are great. They allow a ce
Enter the Feedback. We want to define simple events that can be attached to various things - TP Sigs, EISC, event handlers - and maintain their own state. This simplifies the interface to various device classes, and allows us to define functional, simple classes with well-defined means of connecting them together. Enter the Feedback. We want to define simple events that can be attached to various things - TP Sigs, EISC, event handlers - and maintain their own state. This simplifies the interface to various device classes, and allows us to define functional, simple classes with well-defined means of connecting them together.
#### Feedbacks are similar to signals ### Feedbacks are similar to signals
Feedbacks can: Feedbacks can:
@@ -27,7 +27,7 @@ A Feedback is defined on a class using a C# construct called a `Func`. A `Func`
The following `IntFeedback` returns the value of the `_VolumeLevel` field in this display class: The following `IntFeedback` returns the value of the `_VolumeLevel` field in this display class:
``` ```cs
public class MyDisplay public class MyDisplay
{ {
public IntFeedback VolumeLevelFeedback { get; private set; } public IntFeedback VolumeLevelFeedback { get; private set; }
@@ -43,7 +43,7 @@ public class MyDisplay
This BoolFeedback, adapted from the DmTx201Controller class, defines the `Func` first, and then creates the BoolFeedback using that `Func`. The value returned is true if the input is the digital-HDMI connection, and the TX hardware's VideoAttributes.HdcpActiveFeedback is true as well. This BoolFeedback, adapted from the DmTx201Controller class, defines the `Func` first, and then creates the BoolFeedback using that `Func`. The value returned is true if the input is the digital-HDMI connection, and the TX hardware's VideoAttributes.HdcpActiveFeedback is true as well.
``` ```cs
public class MyTx public class MyTx
{ {
public BoolFeedback HdcpActiveFeedback { get; private set; } public BoolFeedback HdcpActiveFeedback { get; private set; }
@@ -65,7 +65,7 @@ public class MyTx
In your classes, when you need to update the objects listening to a Feedback, you will call MyFeedback.FireUpdate() inside your class. This will trigger the evaluation of the Func value, update any linked Sigs, and fire the OutputChange event. In your classes, when you need to update the objects listening to a Feedback, you will call MyFeedback.FireUpdate() inside your class. This will trigger the evaluation of the Func value, update any linked Sigs, and fire the OutputChange event.
``` ```cs
int _VolumeLevel; int _VolumeLevel;
void ComDataChanged(string data) // volume=77 void ComDataChanged(string data) // volume=77
@@ -81,7 +81,7 @@ void ComDataChanged(string data) // volume=77
Feedbacks of the various types have BoolValue, IntValue, UShortValue, and StringValue properties that return the current value of the Feedback. Feedbacks of the various types have BoolValue, IntValue, UShortValue, and StringValue properties that return the current value of the Feedback.
``` ```cs
if (MyTxDevice.HdcpActiveFeedback.BoolValue) if (MyTxDevice.HdcpActiveFeedback.BoolValue)
{ {
... do something that needs to happen when HDCP is active ... ... do something that needs to happen when HDCP is active ...
@@ -89,7 +89,7 @@ if (MyTxDevice.HdcpActiveFeedback.BoolValue)
Feedbacks all share an OutputChange event, that fires an event with an empty EventArgs object. The event handler can go get the appropriate \*Value property when the event fires. The example below is a bit contrived, but explains the idea. Feedbacks all share an OutputChange event, that fires an event with an empty EventArgs object. The event handler can go get the appropriate \*Value property when the event fires. The example below is a bit contrived, but explains the idea.
``` ```cs
... ...
MyDisplayDevice.VolumeLevelFeedback.OutputChange += MyDisplayVolumeHandler; MyDisplayDevice.VolumeLevelFeedback.OutputChange += MyDisplayVolumeHandler;
@@ -105,7 +105,7 @@ Feedbacks also have a LinkInputSig(\*InputSig sig) method that can directly trig
As well as updating upon change, the Feedback will set the Sig's value to the Feedback value upon calling the LinkInputSig method. This eliminates the need to walk through an object, property-by-property, and update several Sig values - as well as setting up to watch those values for changes. It is all handled in one step. As well as updating upon change, the Feedback will set the Sig's value to the Feedback value upon calling the LinkInputSig method. This eliminates the need to walk through an object, property-by-property, and update several Sig values - as well as setting up to watch those values for changes. It is all handled in one step.
``` ```cs
public class MyClass public class MyClass
{ {
Tsw760 MyTp; Tsw760 MyTp;

@@ -23,6 +23,4 @@ To help understand Essentials Framework, we recommend starting with the current
1. Run the command `devjson:1 {"deviceKey":"display-1","methodName":"PowerOn", "params": []}`. This will call the method PowerOn() on the device with key "display-1". 1. Run the command `devjson:1 {"deviceKey":"display-1","methodName":"PowerOn", "params": []}`. This will call the method PowerOn() on the device with key "display-1".
1. Run the provided example XPanel SmartGraphics project and connect to your processor at the appropriate IPID. 1. Run the provided example XPanel SmartGraphics project and connect to your processor at the appropriate IPID.
Next: [Standalone use](Standalone-Use) Next: [Standalone use](Standalone-Use)

@@ -1,3 +1,5 @@
# Glossary of Terms
**Assembly** **Assembly**
An assembly is a file that is automatically generated by the compiler upon successful compilation of every . NET application. It can be either a Dynamic Link Library or an executable file. It is generated only once for an application and upon each subsequent compilation the assembly gets updated. An assembly is a file that is automatically generated by the compiler upon successful compilation of every . NET application. It can be either a Dynamic Link Library or an executable file. It is generated only once for an application and upon each subsequent compilation the assembly gets updated.
@@ -20,4 +22,4 @@ SIMPL# Pro libraries that reference the Essentials Framework and are loaded at r
An important interface defined in PepperDash.Core that requires a string property named Key, whose value must be unique. An important interface defined in PepperDash.Core that requires a string property named Key, whose value must be unique.
**PepperDash.Core** **PepperDash.Core**
A SIMPL# utility library referenced by Essentials Framework. A SIMPL# utility library referenced by Essentials Framework.

11
Home.md

@@ -1,12 +1,11 @@
# Welcome to PepperDash Essentials! # Welcome to PepperDash Essentials!
PepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs. PepperDash Essentials is an open-source framework for control systems, built on Crestron's Simpl# Pro framework. It can be configured as a standalone program capable of running a wide variety of system designs and can also be used to augment other Crestron programs.
Essentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other. Essentials is a collection of C# libraries that can be used in many ways. It is a 100% configuration-driven framework that can be extended to add different workflows and behaviors, either through the addition of new device-types and classes, or via a plug-in mechanism. The framework is a collection of things that are all related and interconnected, but in general do not have strong dependencies on each other.
--- ---
## Get started ## Get started
* [Download essentials build or clone repo](Get-started#) * [Download essentials build or clone repo](Get-started#)
@@ -54,11 +53,11 @@ The `master` branch always contain the latest stable version. The `development`
1. Fork this repository ([More Info](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks)) 1. Fork this repository ([More Info](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks))
2. Create a branch using standard GitFlow branch prefixes (feature/hotfix) followed by a descriptive name. 2. Create a branch using standard GitFlow branch prefixes (feature/hotfix) followed by a descriptive name.
- Example: `feature/add-awesomeness` or `hotfix/really-big-oops` * Example: `feature/add-awesomeness` or `hotfix/really-big-oops`
- When working on a new feature or bugfix, branch from the `development` branch. When working on a hotfix, branch from `master`. * When working on a new feature or bugfix, branch from the `development` branch. When working on a hotfix, branch from `master`.
3. Make commits as necessary (often is better). And use concise, descriptive language, leveraging issue notation and/or [Closing Keywords](https://help.github.com/articles/closing-issues-using-keywords) to ensure any issues addressed by your work are referenced accordingly. 3. Make commits as necessary (often is better). And use concise, descriptive language, leveraging issue notation and/or [Closing Keywords](https://help.github.com/articles/closing-issues-using-keywords) to ensure any issues addressed by your work are referenced accordingly.
4. When the scope of the work for your branch is complete, make sure to rebase your branch in case further progress has been made since the repo was forked 4. When the scope of the work for your branch is complete, make sure to rebase your branch in case further progress has been made since the repo was forked
5. Create a Pull Request to pull your branch into the appropriate branch in the main repository. 5. Create a Pull Request to pull your branch into the appropriate branch in the main repository.
6. Your Pull Request will be reviewed by our team and evaluated for inclusion into the main repository. 6. Your Pull Request will be reviewed by our team and evaluated for inclusion into the main repository.
Next: [Get started](Get-started) Next: [Get started](Get-started)

@@ -1,4 +1,4 @@
## What are Essentials Plugins? # What are Essentials Plugins?
**Note : this entry is out of date - please see [Plugins - Updated](Plugins-Updated)** **Note : this entry is out of date - please see [Plugins - Updated](Plugins-Updated)**

@@ -5,20 +5,21 @@ Essentials was originally designed as a standalone SIMPL# Pro control system app
By defining devices and a room in a JSON configuration file, Essentials can control an entire AV control system for a room. A file can be manually created in an IDE such as Visual Studio Code, or it can be generated by a friendly web-based configuration tool on [PepperDash Portal](http://pepperdash.com/products/), or some other configuration tool application, both requiring no knowledge of JSON. These tools step a user through building the necessary devices and setting to achieve a full working room. By defining devices and a room in a JSON configuration file, Essentials can control an entire AV control system for a room. A file can be manually created in an IDE such as Visual Studio Code, or it can be generated by a friendly web-based configuration tool on [PepperDash Portal](http://pepperdash.com/products/), or some other configuration tool application, both requiring no knowledge of JSON. These tools step a user through building the necessary devices and setting to achieve a full working room.
## Included standalone room types ## Included standalone room types
- `EssentialsHuddleSpaceRoom` - Presentation-only Huddle Room
- Single display device
- Use the display's speakers or another device for audio
- Any number of presentation sources
- Fusion Room and Static Asset integration with device usage tracking and schedule awareness
- Occupancy Sensor integration with vacancy shutdown
- Audio/video routing via Crestron DM hardware
- `EssentialsHuddleVtc1Room` - Single-display ATC/VTC capable Huddle Room * `EssentialsHuddleSpaceRoom` - Presentation-only Huddle Room
- All of the above, plus: * Single display device
- Audio calling via a DSP/Audio Codec or Video Codec * Use the display's speakers or another device for audio
- Video calling via a Video Codec * Any number of presentation sources
- Microphone Mute button and LED color control * Fusion Room and Static Asset integration with device usage tracking and schedule awareness
- Schedule awareness via Video Codec * Occupancy Sensor integration with vacancy shutdown
- One button meeting join for Video Calling (with supported Video Codec) * Audio/video routing via Crestron DM hardware
Next: [Simpl Windows bridging](SIMPL-Bridging-Updated) * `EssentialsHuddleVtc1Room` - Single-display ATC/VTC capable Huddle Room
* All of the above, plus:
* Audio calling via a DSP/Audio Codec or Video Codec
* Video calling via a Video Codec
* Microphone Mute button and LED color control
* Schedule awareness via Video Codec
* One button meeting join for Video Calling (with supported Video Codec)
Next: [Simpl Windows bridging](SIMPL-Bridging-Updated)

@@ -1,53 +1,61 @@
# Essentials Framework Devices by Type # Essentials Framework Devices by Type
### Cameras ## Cameras
- VISCA protocol
- Cisco (via codec)
- Zoom (via Zoom Room)
### Disc Player * VISCA protocol
- Any IR disc player that implements standard RAD commands * Cisco (via codec)
* Zoom (via Zoom Room)
### Displays ## Disc Player
- Any IR display that implements standard RAD commands
- Samsung MDC protocol (commercial)
- NEC Professional series
- NEC PA series projector
- Avocor VTF
- Panasonic TH series
### Lighting/Shading * Any IR disc player that implements standard RAD commands
- Lutron Quantum
- Somfy Shades (relay control)
### Power Controllers ## Displays
- Digital Logger
### Set Top Boxes * Any IR display that implements standard RAD commands
- Any IR set top box that implements standard RAD commands * Samsung MDC protocol (commercial)
* NEC Professional series
* NEC PA series projector
* Avocor VTF
* Panasonic TH series
### Streaming Players ## Lighting/Shading
- AppleTV (IR)
- Roku (IR) * Lutron Quantum
* Somfy Shades (relay control)
## Power Controllers
* Digital Logger
## Set Top Boxes
* Any IR set top box that implements standard RAD commands
## Streaming Players
* AppleTV (IR)
* Roku (IR)
## Video Codecs ## Video Codecs
- Cisco CE series (C/SX/RoomKit)
- Zoom Room * Cisco CE series (C/SX/RoomKit)
* Zoom Room
## Crestron Devices ## Crestron Devices
- AM-200/300 Airmedia
- All DM Chassis (8x8 - 128x128)
- All DM input/output cards
- All DMPS Processors
- All DM Transmitter models (with COM/IR/Relay/CEC port access)
- All DM Receiver models (with COM/IR/Relay/CEC port access)
- DGE-100
- DM-DGE-200-C
- DIN-8SW8
- CEN-IO-DIGIN-104
- CEN-RFGWEX/GWEXER
- GLS-ODT/OIR-C-CN Occupancy Sensors
- TSW-XXX series touchpanels
- XPanel for SmartGraphics
- Fusion Room and Assets
* AM-200/300 Airmedia
* All DM Chassis (8x8 * 128x128)
* All DM input/output cards
* All DMPS Processors
* All DM Transmitter models (with COM/IR/Relay/CEC port access)
* All DM Receiver models (with COM/IR/Relay/CEC port access)
* DGE-100
* DM-DGE-200-C
* DIN-8SW8
* CEN-IO-DIGIN-104
* CEN-RFGWEX/GWEXER
* GLS-ODT/OIR-C-CN Occupancy Sensors
* TSW-XXX series touchpanels
* XPanel for SmartGraphics
* Fusion Room and Assets