diff --git a/Essentials-Architecture.md b/Essentials-Architecture.md new file mode 100644 index 0000000..bfb163b --- /dev/null +++ b/Essentials-Architecture.md @@ -0,0 +1,106 @@ +### Summary +PepperDash Essentials is an open source Crestron framework that can be configured as a standalone program capable of running a wide variety of system designs and can also be utilized as a plug-in architecture to augment other Simpl# Pro and Simpl Windows programs. + +Essentials Framework is a collection of C# / Simpl# Pro libraries that can be utilized in several different manners. It is currently operating as a 100% configuration-driven system, and can be extended to add different workflows and behaviors, either through the addition of further device "types" or via the plug-in mechanism. The framework is a collection of "things" that are all related and interconnected, but in general do not have dependencies on each other. + +### 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. +--- PUT TABLE HERE --- + +### Architecture + +#### Device and DeviceManager + +A 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 is the collection of all Devices. The "array" of everything we control on a system. + +#### 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. + +Types of devices: + +* Rooms +* Sources +* Codecs, DSPs, displays, routing hardware +* IR Ports, Com ports, SSh Clients, ... +* Occupancy sensors and relay-driven devices +* Logical devices that manage multiple ports, like shade controllers +* 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. + +> The DeviceManager is nothing more than a modified collection of things, and technically those things don't have to be Devices, but must at least implement the IKeyed interface (simply so items can be looked up by key.) Items in the DeviceManager that are Devices are run through the additional steps of activation at startup. This collection of devices is all interrelated by string keys. + +In this flat design, we spin up devices, and then introduce them to their coworkers and bosses, and get them all operating together to form a running unit. For example: A room configuration will contain a "VideoCodecKey" property and a "DefaultDisplayKey" property. The DeviceManager provides the room with the codec or displays having the appropriate keys. What the room does with those is dependent on its coding. + +> In the default Essentials routing scheme, the DeviceManager is asked for the various devices defined in the desired route, and then queried for the devices that may exist in the tie lines defining the route. This is all done at runtime, on the fly, using only keys, and as soon as the routing operation is done, the whole process is released from memory. This is extremely-loose coupling between objects. + +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 + +--- DRAWING HERE!!! --- + +#### Essentials Configurable System Lifecycle + +--- ANOTHER DRAWING !!!! + +### Activation phases additional topics and examples (OTHER DOCS) + +Concepts (link) + +Room and touchpanel activation (link) + +#### 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. + +Often, code is written and tested first, and then handed to the developer of a configuration tool, only to discover that the object structure in the program is incompatible with the best ways to write the tool. This creates spaghetti code in config tools and tends to create tighter-coupling between objects than we desire to have. Later on a modified version of something is desired, and because the code was written in such a specific fashion, the code is hard to refactor into a new something, and the config tool becomes even more convoluted. The more-modern versions of configuration tools that are just starting to come out are very modular, and componentized. We want to ensure as much re-use of these modules as possible, with extensions and added features added on, rather than complete rewrites of similar things, and in our running systems, we want to ensure as much flexibility in design as possible, eliminating multiple classes and type with similar code. + +#### Configuration reader process + +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 +* devices (array) Contains, well, the devices we intend to build and load +* 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 +* 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. + +At startup, the configuration file is read, and a ReadyEvent is fired. Upon being ready, that configuration is loaded by the ConfigReader.LoadConfig() method. The template and system are merged into a single configuration object, and that object is then deserialized into configuration wrapper classes that define most of the structure of the program to be built. (Custom configuration objects were built to allow for better type handling rather than using JToken methods to parse out error-prone property names.) + +For example, a `DeviceConfig` object: + +``` +namespace PepperDash.Essentials.Core.Config +{ + public class DeviceConfig + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("uid")] + public int Uid { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("group")] + public string Group { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("properties")] + [JsonConverter(typeof(DevicePropertiesConverter))] + public JToken Properties { get; set; } + } +} +``` + +