Compare commits

..

81 Commits

Author SHA1 Message Date
Neil Dorin
f3b4c0aa02 fix: resolves issue with incorrect partition state feedback in test mode 2024-06-28 13:56:42 -06:00
Neil Dorin
a3351812cd fix: fixes for room combination in manual/auto mode with actual crestron partition sensors 2024-06-28 13:20:19 -06:00
Neil Dorin
71815eff17 fix: updates mode for partition sensors when mode of room combiner changes 2024-06-28 12:57:06 -06:00
Neil Dorin
c7f4bf1fb2 fix: fixes backing values for eLevelControlType 2024-06-28 12:42:43 -06:00
Neil Dorin
bec3ab8e73 fix: Adds missing initializer for CameraLists 2024-06-28 11:27:22 -06:00
Neil Dorin
c499d2a2eb fix: corrects spelling mistake 2024-06-28 09:01:59 -06:00
Neil Dorin
d9721a362e feat: adds method to set input source type and corresponding enum to ICiscoCodecCameraConfig 2024-06-28 08:55:18 -06:00
Neil Dorin
5fb6f3e117 fix: Fixes namespace for CameraListItem 2024-06-25 22:20:13 -06:00
Neil Dorin
c2fb44a662 feat: adds CameraListKey to EssentialsAvRoomPropertiesConfig 2024-06-25 21:22:49 -06:00
Neil Dorin
0f9bddf4dd fix: updates EssentialsRoomBase to add CameraListKey 2024-06-25 17:58:44 -06:00
Neil Dorin
ddc2491664 feat: updates to IEssentialsRoom for CameraListKey and adds helper method to get camera list from config 2024-06-25 17:15:45 -06:00
Neil Dorin
9a6209f50a feat: Adds basic CamerLists config structure and CameraListItems class 2024-06-25 16:08:44 -06:00
Andrew Welker
49852d25e8 Merge pull request #1194 from PepperDash/feature-2.0.0/routing-extension-changes
Feature 2.0.0/routing extension changes
2024-06-24 16:01:37 -04:00
Andrew Welker
ba4ca20936 fix: add tieline type to output for console & CWS 2024-06-24 14:57:07 -05:00
Andrew Welker
c50726f813 feat: separate audio & video route descriptors
Also cleaned up logging some
2024-06-24 14:54:37 -05:00
Neil Dorin
0aafe8a62e fix: add message to indicate no handler registered when debugging stream data 2024-06-24 13:45:07 -06:00
Neil Dorin
71005940ac fix: catches last condition for destinationPort match 2024-06-21 15:00:17 -06:00
Neil Dorin
e5e79316a6 fix: fixes conditions for specific port and device matches 2024-06-21 14:53:03 -06:00
Neil Dorin
5aa1f85df5 feat: adds try catch to devjson execution thread and adds port names for sdi in/out 2024-06-20 11:57:32 -06:00
Andrew Welker
7bac65002d fix: correct issues with method calls 2024-06-19 15:01:25 -05:00
Andrew Welker
ed0141a536 fix: change ports and what's used where 2024-06-19 14:37:04 -05:00
Andrew Welker
25ebcdfb5d feat: update routing methods
Routing methods will now take a source port and destination port. This should solve the issue where a device could have multiple input ports defined in tielines and allow Essentials routing to find a path correctly.
2024-06-19 14:09:59 -05:00
Neil Dorin
3a56e47c48 fix: updates IHasInputs to remove second generic that is unnecessary. 2024-06-13 10:59:05 -06:00
Neil Dorin
171bd6b1ec fix: removed DefaultDisplay from TwoWayDisplayBase 2024-06-06 12:15:30 -06:00
Neil Dorin
e61fd7777a fix: fixed typenames for mockDisplay 2024-06-06 12:09:35 -06:00
Neil Dorin
027bdd5bf4 Merge 'development-2.0.0' into 'feature-2.0.0/fix-version-info' 2024-05-29 12:10:34 -06:00
Neil Dorin
b876b8123d feat: changes access modifiers to public on SystemMonitorController methods 2024-05-28 14:25:34 -06:00
Neil Dorin
6e05653c6c fix: fixes name for PDCore dll 2024-05-28 14:25:05 -06:00
Neil Dorin
16d32bc720 Merge pull request #1191 from PepperDash/feature-2.0.0/fix-touchpanel-config
Add JSON Props & Control Config
2024-05-28 14:00:19 -06:00
Andrew Welker
0bef5d4b77 build: update PD Core 2024-05-28 14:57:35 -05:00
Andrew Welker
a3c1572a77 build: update PD Core 2024-05-28 14:43:39 -05:00
Andrew Welker
cc06c7bfd8 fix: add nullable props where necessary 2024-05-28 14:43:31 -05:00
Andrew Welker
00a7b25026 build: update PD Core 2024-05-28 13:29:03 -05:00
Andrew Welker
edc916c9d3 fix: add more json props 2024-05-28 13:28:45 -05:00
Andrew Welker
c755ecb16c fix: add correct casing to base touchpanel config 2024-05-28 11:26:25 -05:00
Neil Dorin
a8c36ba243 Merge pull request #1190 from PepperDash/feature-2.0.0/routing-updates
Updates after testing
2024-05-24 15:28:59 -06:00
Andrew Welker
f630d3f410 fix: add missing interfaces 2024-05-24 16:13:35 -05:00
Andrew Welker
35e0662b27 fix: only update CurrentInputPort if it has changed 2024-05-24 16:13:21 -05:00
Andrew Welker
effefc939c fix: minor Web API enhancements
* changed path for DevJson to include the device key instead of requiring it in the body
* Made the `GetRequestBody` method an extension method for the `HttpCwsRequest` class
2024-05-24 16:12:50 -05:00
Andrew Welker
5afdc2effa fix: remove Crestron.SimplSharp.Reflection
Ran into some odd exceptions loading on a VC-4 instance, and changing to System.Reflection solved them.
2024-05-24 16:11:20 -05:00
Neil Dorin
448cc273ec feat: Adds PepperDashCoreAssembly 2024-05-23 14:42:31 -06:00
Neil Dorin
0a2aaa693f feat: Replaces Crestron.SimplSharp.Reflection with System.Reflextion and updates the way essentials plugin versions are stored and retrieved 2024-05-23 14:11:42 -06:00
Neil Dorin
1ebee58ad6 Merge pull request #1189 from PepperDash/feature-2.0.0/routing-updates
Add routing feedback for Essentials Routing
2024-05-23 12:31:49 -06:00
Neil Dorin
621d848418 feat: adds deviceKey property to LevelControlListItem to synthesize device key 2024-05-22 14:53:01 -06:00
Neil Dorin
2f9038a501 fix: adds initializer for dictionaries 2024-05-21 22:15:50 -06:00
Neil Dorin
983b18d25a fix: fixes type of AudioControlPointLists 2024-05-21 17:28:05 -06:00
Neil Dorin
048004d441 fix: updates IEssentialsRoom and EssentialsRoomBase for missed changes 2024-05-21 17:14:13 -06:00
Neil Dorin
e7e448f02c fix: Switches from LevelControlListKey to AudioControlPointListKey 2024-05-21 17:04:15 -06:00
Neil Dorin
2e61d8d709 fix: Changes LevelControlLists to AudioControlPointLists and modified IHasDspPresets 2024-05-21 16:49:13 -06:00
Neil Dorin
d8d2c5b340 build(force-patch): Updates PD.Core version to support LevelControlList property merge in config 2024-05-16 23:07:13 -06:00
Neil Dorin
7e736ae519 fix: initializes LevelControlLists in config 2024-05-16 22:44:57 -06:00
Neil Dorin
0067e11d3d fix: Adds new property to EssentialsRoomBase and implements consistent default key if no key set 2024-05-16 20:57:38 -06:00
Neil Dorin
7942c91f73 feat: Adds LevelControlListKey to IEssentialsRoom 2024-05-16 20:49:11 -06:00
Neil Dorin
e1638762a1 feat: Adds helper method for getting LevelControlList by key 2024-05-16 20:30:22 -06:00
Neil Dorin
3566400379 fix: Adds LevelControlListKey to EssentialsRoomPropertiesConfig 2024-05-16 20:24:45 -06:00
Neil Dorin
dde85f39a2 fix: combine enum values 2024-05-16 20:20:11 -06:00
Neil Dorin
735433f660 fix: adds missing flags decorator to enum 2024-05-16 17:26:35 -06:00
Neil Dorin
734149960b fix: Adds missing StringEnumConverter 2024-05-16 17:23:38 -06:00
Neil Dorin
cb16f2a505 feat: Adds LevelControlLists to BasicConfig and LevelControlListItem class 2024-05-16 17:17:59 -06:00
Neil Dorin
cb9eb5dafa Merge remote-tracking branch 'origin/feature-2.0.0/video-codec-interface-uiextensions' into feature-2.0.0/room-combiner-updates 2024-05-14 16:44:30 -06:00
Neil Dorin
eb955aa014 build: updates version of PD.Core --force-patch 2024-05-14 15:32:23 -06:00
Joshua_Gutenplan
2d9ffca78e Merge branch 'feature-2.0.0/room-combiner-updates' into feature-2.0.0/video-codec-interface-uiextensions 2024-05-10 21:36:53 -07:00
Neil Dorin
98f1a09c25 feat: Adds IHasCiscoNavigatorTouchpanel interface 2024-05-10 14:49:37 -06:00
Neil Dorin
a11ad421f0 fix: better implmentation of input select 2024-05-10 13:16:59 -06:00
Neil Dorin
8878ff7ddd chore: renames property to Keys 2024-05-09 16:09:16 -06:00
Neil Dorin
7e4b5f984f feat: Adds IHasAccessoryDevices 2024-05-09 16:07:52 -06:00
Neil Dorin
64ab315142 fix: various updates for room combining from testing 2024-05-09 15:16:35 -06:00
Neil Dorin
c47a93f4d0 Merge remote-tracking branch 'origin/feature-2.0.0/add-screenLift-controls' into feature-2.0.0/room-combiner-updates 2024-05-09 15:15:50 -06:00
Joshua_Gutenplan
5a55a701d6 fix: remove codec interfaces that are only needed in cisco epi now 2024-05-09 12:10:42 -07:00
Andrew Knous
01862ab9aa feat: moves mockdisplay factory from PepperDash.Essentials.Core to PepperDash.Essential.Devices.Common 2024-05-09 13:48:59 -04:00
Andrew Knous
8ec6fa785e feat: adds IProjectorScreenLiftControl and ScreenLiftController 2024-05-09 13:47:46 -04:00
Joshua_Gutenplan
e37c675da1 Merge remote-tracking branch 'origin/feature-2.0.0/room-combiner-updates' into feature-2.0.0/video-codec-interface-uiextensions 2024-05-06 12:55:16 -07:00
Neil Dorin
3ee8cb7ea3 feat: updates IHasInput to remove requirement for SetInputs method (unnecessary) 2024-05-03 13:34:22 -06:00
Neil Dorin
2b6f79b68f feat: updates to Room combiner for use with mobile control 2024-05-02 17:27:34 -06:00
Neil Dorin
65369606a4 feat: updates to room combiner interfaces 2024-05-02 15:00:17 -06:00
Joshua_Gutenplan
e9954b3081 fix: add GetRoomMessenger to IMobileControl 2024-05-02 12:07:00 -07:00
Joshua_Gutenplan
e1b50649fd fix: UiWebViewDisplayActionArgs add target 2024-04-30 21:41:53 -07:00
Joshua_Gutenplan
5e69ea1947 Merge remote-tracking branch 'origin/feature-2.0.0/tech-password-interface' into feature-2.0.0/video-codec-interface-uiextensions 2024-04-30 10:24:50 -07:00
Joshua_Gutenplan
fd1b92a6c0 Merge remote-tracking branch 'origin/feature-2.0.0/tech-password-interface' into feature-2.0.0/video-codec-interface-uiextensions 2024-04-25 19:13:46 -07:00
Joshua_Gutenplan
06d806687d feat: IMobileControlTouchpanelController 2024-04-25 15:29:44 -07:00
Joshua_Gutenplan
d8e2f8cd51 feat: IVideoCodecUiExtensions 2024-04-25 10:03:38 -07:00
75 changed files with 1520 additions and 1011 deletions

1
.gitignore vendored
View File

@@ -391,3 +391,4 @@ FodyWeavers.xsd
essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj
.DS_Store
/._PepperDash.Essentials.sln
.vscode/settings.json

View File

@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication;
@@ -168,7 +168,7 @@ namespace PepperDash.Essentials.Core.Bridges
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetCType()))
if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetType()))
{
Debug.LogMessage(LogEventLevel.Information, this,
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",

View File

@@ -101,6 +101,7 @@ namespace PepperDash.Essentials.Core
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
return;
}
var textHandler = TextReceived;
if (textHandler != null)
@@ -108,7 +109,10 @@ namespace PepperDash.Essentials.Core
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
return;
}
Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
}
public override bool Deactivate()

View File

@@ -23,12 +23,12 @@ namespace PepperDash.Essentials.Core
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (objectType == typeof(ComPort.ComPortSpec))
if (objectType == typeof(ComPort.ComPortSpec?))
{
var newSer = new JsonSerializer();
newSer.Converters.Add(new ComSpecPropsJsonConverter());
newSer.ObjectCreationHandling = ObjectCreationHandling.Replace;
return newSer.Deserialize<ComPort.ComPortSpec>(reader);
return newSer.Deserialize<ComPort.ComPortSpec?>(reader);
}
return null;
}
@@ -38,7 +38,7 @@ namespace PepperDash.Essentials.Core
/// </summary>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ComPort.ComPortSpec);
return objectType == typeof(ComPort.ComPortSpec?);
}
public override bool CanRead { get { return true; } }

View File

@@ -51,7 +51,7 @@ namespace PepperDash.Essentials.Core
switch (controlConfig.Method)
{
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams, controlConfig);
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
break;
case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
@@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core
var comPar = config.ComParams;
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
return dev.ComPorts[config.ControlPortNumber];
return dev.ComPorts[config.ControlPortNumber.Value];
Debug.LogMessage(LogEventLevel.Information, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
return null;
}
@@ -201,23 +201,26 @@ namespace PepperDash.Essentials.Core
/// <summary>
///
/// </summary>
public class EssentialsControlPropertiesConfig :
PepperDash.Core.ControlPropertiesConfig
public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig
{
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec ComParams { get; set; }
public ComPort.ComPortSpec? ComParams { get; set; }
public string CresnetId { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
public string CresnetId { get; set; }
/// <summary>
/// Attempts to provide uint conversion of string CresnetId
/// </summary>
[JsonIgnore]
public uint CresnetIdInt
{
get
{
try
try
{
return Convert.ToUInt32(CresnetId, 16);
}
@@ -228,11 +231,13 @@ namespace PepperDash.Essentials.Core
}
}
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
public string InfinetId { get; set; }
/// <summary>
/// Attepmts to provide uiont conversion of string InifinetId
/// </summary>
[JsonIgnore]
public uint InfinetIdInt
{
get

View File

@@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Net.Http;
using Crestron.SimplSharp.Net.Http;
using PepperDash.Core;
using System;
namespace PepperDash.Essentials.Core
{
[Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
[Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
public class GenericHttpClient : Device, IBasicCommunication
{
public HttpClient Client;

View File

@@ -0,0 +1,20 @@
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Config
{
public class AudioControlPointListItem
{
[JsonProperty("levelControls")]
public Dictionary<string, LevelControlListItem> LevelControls { get; set; } = new Dictionary<string, LevelControlListItem>();
[JsonProperty("presets")]
public Dictionary<string, PresetListItem> Presets { get; set; } = new Dictionary<string, PresetListItem>();
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core.Config
{
@@ -23,7 +24,13 @@ namespace PepperDash.Essentials.Core.Config
public Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; }
[JsonProperty("destinationLists")]
public Dictionary<string, Dictionary<string,DestinationListItem>> DestinationLists { get; set; }
public Dictionary<string, Dictionary<string, DestinationListItem>> DestinationLists { get; set; }
[JsonProperty("audioControlPointLists")]
public Dictionary<string, AudioControlPointListItem> AudioControlPointLists { get; set; }
[JsonProperty("cameraLists")]
public Dictionary<string, Dictionary<string, CameraListItem>> CameraLists { get; set; }
[JsonProperty("tieLines")]
public List<TieLineConfig> TieLines { get; set; }
@@ -37,6 +44,8 @@ namespace PepperDash.Essentials.Core.Config
Devices = new List<DeviceConfig>();
SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();
DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();
AudioControlPointLists = new Dictionary<string, AudioControlPointListItem>();
CameraLists = new Dictionary<string, Dictionary<string, CameraListItem>>();
TieLines = new List<TieLineConfig>();
JoinMaps = new Dictionary<string, JObject>();
}
@@ -55,8 +64,8 @@ namespace PepperDash.Essentials.Core.Config
/// <summary>
/// Retrieves a DestinationListItem based on the key
/// </summary>
/// <param name="key">key of the item to retrieve</param>
/// <returns>DestinationListItem if the key exists, null otherwise</returns>
/// <param name="key">key of the list to retrieve</param>
/// <returns>DestinationList if the key exists, null otherwise</returns>
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
@@ -65,7 +74,31 @@ namespace PepperDash.Essentials.Core.Config
}
return DestinationLists[key];
}
}
/// <summary>
/// Retrieves a AudioControlPointList based on the key
/// </summary>
/// <param name="key">key of the list to retrieve</param>
/// <returns>AudioControlPointList if the key exists, null otherwise</returns>
public AudioControlPointListItem GetAudioControlPointListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))
return null;
return AudioControlPointLists[key];
}
/// <summary>
/// Checks CameraLists for a given list and returns it if found. Otherwise, returns null
/// </summary>
public Dictionary<string, CameraListItem> GetCameraListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))
return null;
return CameraLists[key];
}
/// <summary>
/// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null

View File

@@ -1,18 +1,16 @@
using Crestron.SimplSharp;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Represents the info section of a Config file
/// </summary>
public class InfoConfig
/// <summary>
/// Represents the info section of a Config file
/// </summary>
public class InfoConfig
{
[JsonProperty("name")]
public string Name { get; set; }

View File

@@ -15,10 +15,22 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
/// <example>
/// See MockDisplay for example implemntation
/// </example>
public interface IHasInputs<TKey, TSelector>: IKeyName
[Obsolete("Use IHasInputs<T> instead. Will be removed for 2.0 release")]
public interface IHasInputs<T, TSelector>: IKeyName
{
ISelectableItems<TKey> Inputs { get; }
ISelectableItems<T> Inputs { get; }
}
void SetInput(TSelector selector);
/// <summary>
/// Describes a device that has selectable inputs
/// </summary>
/// <typeparam name="T">the type to use as the key for each input item. Most likely an enum or string</typeparam>\
/// <example>
/// See MockDisplay for example implemntation
/// </example>
public interface IHasInputs<T> : IKeyName
{
ISelectableItems<T> Inputs { get; }
}
}

View File

@@ -44,7 +44,10 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
void AddDeviceMessenger(IMobileControlMessenger messenger);
bool CheckForDeviceMessenger(string key);
}
IMobileControlRoomMessenger GetRoomMessenger(string key);
}
/// <summary>
/// Describes a mobile control messenger

View File

@@ -0,0 +1,27 @@
using Crestron.SimplSharpPro.DeviceSupport;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines a class that has warm up and cool down
/// </summary>
public interface IProjectorScreenLiftControl
{
void Raise();
void Lower();
BoolFeedback IsInUpPosition { get; }
bool InUpPosition { get; }
event EventHandler<EventArgs> PositionChanged;
string DisplayDeviceKey { get; }
eScreenLiftControlType Type { get; } // screen/lift
}
public enum eScreenLiftControlType
{
lift,
screen
}
}

View File

@@ -17,6 +17,6 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
Dictionary<TKey, ISelectableItem> Items { get; set; }
[JsonProperty("currentItem")]
string CurrentItem { get; set; }
TKey CurrentItem { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
public abstract class AudioControlListItemBase
{
[JsonProperty("parentDeviceKey")]
public string ParentDeviceKey { get; set; }
[JsonProperty("itemKey")]
public string ItemKey { get; set; }
/// <summary>
/// A name that will override the items's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Indicates if the item should be included in the user accessible list
/// </summary>
[JsonProperty("includeInUserList")]
public bool IncludeInUserList { get; set; }
/// <summary>
/// Used to specify the order of the items in the source list when displayed
/// </summary>
[JsonProperty("order")]
public int Order { get; set; }
}
}

View File

@@ -0,0 +1,76 @@
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class CameraListItem
{
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
/// <summary>
/// Returns the source Device for this, if it exists in DeviceManager
/// </summary>
[JsonIgnore]
public Device CameraDevice
{
get
{
if (_cameraDevice == null)
_cameraDevice = DeviceManager.GetDeviceForKey(DeviceKey) as Device;
return _cameraDevice;
}
}
Device _cameraDevice;
/// <summary>
/// Gets either the source's Name or this AlternateName property, if
/// defined. If source doesn't exist, returns "Missing source"
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (string.IsNullOrEmpty(Name))
{
if (CameraDevice == null)
return "---";
return CameraDevice.Name;
}
return Name;
}
}
/// <summary>
/// A name that will override the source's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Specifies and icon for the source list item
/// </summary>
[JsonProperty("icon")]
public string Icon { get; set; }
/// <summary>
/// Alternate icon
/// </summary>
[JsonProperty("altIcon", NullValueHandling = NullValueHandling.Ignore)]
public string AltIcon { get; set; }
/// <summary>
/// Indicates if the item should be included in the user facing list
/// </summary>
[JsonProperty("includeInUserList")]
public bool IncludeInUserList { get; set; }
/// <summary>
/// Used to specify the order of the items in the source list when displayed
/// </summary>
[JsonProperty("order")]
public int Order { get; set; }
}
}

View File

@@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Newtonsoft.Json;
using PepperDash.Core;
@@ -64,7 +64,7 @@ namespace PepperDash.Essentials.Core
action.Params = new object[0];
}
CType t = obj.GetType();
Type t = obj.GetType();
try
{
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
@@ -84,7 +84,18 @@ namespace PepperDash.Essentials.Core
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
.ToArray();
Task.Run(() => method.Invoke(obj, convertedParams));
Task.Run(() =>
{
try
{
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
method.Invoke(obj, convertedParams);
}
catch(Exception e)
{
Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
}
});
CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name,
action.DeviceKey);
@@ -123,7 +134,7 @@ namespace PepperDash.Essentials.Core
if (obj == null)
return "{ \"error\":\"No Device\"}";
CType t = obj.GetType();
Type t = obj.GetType();
// get the properties and set them into a new collection of NameType wrappers
var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
return JsonConvert.SerializeObject(props, Formatting.Indented);
@@ -141,7 +152,7 @@ namespace PepperDash.Essentials.Core
if(dev == null)
return "{ \"error\":\"No Device\"}";
object prop = dev.GetType().GetCType().GetProperty(propertyName).GetValue(dev, null);
object prop = dev.GetType().GetType().GetProperty(propertyName).GetValue(dev, null);
// var prop = t.GetProperty(propertyName);
if (prop != null)
@@ -167,7 +178,7 @@ namespace PepperDash.Essentials.Core
return "{ \"error\":\"No Device\"}";
// Package up method names using helper objects
CType t = obj.GetType();
Type t = obj.GetType();
var methods = t.GetMethods()
.Where(m => !m.IsSpecialName)
.Select(p => new MethodNameParams(p));
@@ -181,7 +192,7 @@ namespace PepperDash.Essentials.Core
return "{ \"error\":\"No Device\"}";
// Package up method names using helper objects
CType t = obj.GetType();
Type t = obj.GetType();
var methods = t.GetMethods()
.Where(m => !m.IsSpecialName)
.Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any())
@@ -228,7 +239,7 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
}
CType oType = obj.GetType();
Type oType = obj.GetType();
var prop = oType.GetProperty(objName);
if (prop == null)
{
@@ -258,7 +269,7 @@ namespace PepperDash.Essentials.Core
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
}
// if the index is bad, catch it here.
catch (Crestron.SimplSharp.Reflection.TargetInvocationException e)
catch (TargetInvocationException e)
{
if (e.InnerException is ArgumentOutOfRangeException)
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
@@ -289,7 +300,7 @@ namespace PepperDash.Essentials.Core
//if (obj == null)
// return "{\"error\":\"No object found\"}";
//CType t = obj.GetType();
//Type t = obj.GetType();
//// get the properties and set them into a new collection of NameType wrappers
@@ -367,7 +378,7 @@ namespace PepperDash.Essentials.Core
}
[AttributeUsage(AttributeTargets.All)]
public class ApiAttribute : CAttribute
public class ApiAttribute : Attribute
{
}

View File

@@ -166,7 +166,7 @@ namespace PepperDash.Essentials.Core
// var dev = GetDeviceForKey(devKey);
// if(dev != null)
// {
// var type = dev.GetType().GetCType();
// var type = dev.GetType().GetType();
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
// var sb = new StringBuilder();
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;

View File

@@ -1,17 +0,0 @@
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
public interface IHasDspPresets
{
List<IDspPreset> Presets { get; }
void RecallPreset(IDspPreset preset);
}
public interface IDspPreset
{
string Name { get; }
}
}

View File

@@ -0,0 +1,12 @@
using PepperDash.Core;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
public interface IDspPresets
{
Dictionary<string, IKeyName> Presets { get; }
void RecallPreset(string key);
}
}

View File

@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System;
using System.Linq;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using Serilog.Events;
@@ -23,7 +21,7 @@ namespace PepperDash.Essentials.Core
{
public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)
{
CType t = source.GetType();
Type t = source.GetType();
// get the properties and set them into a new collection of NameType wrappers
var props = t.GetProperties().Select(p => new PropertyNameType(p, t));

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core
{
public class LevelControlListItem : AudioControlListItemBase
{
[JsonIgnore]
public IBasicVolumeWithFeedback LevelControl
{
get
{
if (_levelControl == null)
_levelControl = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IBasicVolumeWithFeedback;
return _levelControl;
}
}
IBasicVolumeWithFeedback _levelControl;
/// <summary>
/// Gets the name from the device if it implements IKeyName or else returns the Name property
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (!string.IsNullOrEmpty(Name)) return Name;
else
{
if (LevelControl is IKeyName namedLevelControl)
{
if (namedLevelControl == null)
return "---";
return namedLevelControl.Name;
}
else return "---";
}
}
}
/// <summary>
/// The key of the device in the DeviceManager for control
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey => DeviceManager.AllDevices.
Where(d => d.Key.Contains(ParentDeviceKey) && d.Key.Contains(ItemKey)).FirstOrDefault()?.Key ?? $"{ParentDeviceKey}--{ItemKey}";
/// <summary>
/// Indicates if the item is a level, mute , or both
/// </summary>
[JsonProperty("type")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public eLevelControlType Type { get; set; }
}
[Flags]
public enum eLevelControlType
{
Level = 1,
Mute = 2,
LevelAndMute = Level | Mute,
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class PresetListItem : AudioControlListItemBase
{
[JsonIgnore]
public IKeyName Preset
{
get
{
if (_preset == null)
{
var parent = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IDspPresets;
if (parent == null || !parent.Presets.ContainsKey(ItemKey))
return null;
_preset = parent.Presets[ItemKey];
}
return _preset;
}
}
private IKeyName _preset;
/// <summary>
/// Gets the name from the device if it implements IKeyName or else returns the Name property
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (!string.IsNullOrEmpty(Name)) return Name;
else return Preset.Name;
}
}
}
}

View File

@@ -193,6 +193,7 @@ namespace PepperDash.Essentials.Core
defaultDisplay,
leftDisplay,
rightDisplay,
centerDisplay,
programAudio,
codecContent
}

View File

@@ -266,16 +266,16 @@ namespace PepperDash.Essentials.Core
abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }
public static MockDisplay DefaultDisplay
{
get
{
if (_DefaultDisplay == null)
_DefaultDisplay = new MockDisplay("default", "Default Display");
return _DefaultDisplay;
}
}
static MockDisplay _DefaultDisplay;
// public static MockDisplay DefaultDisplay
// {
// get
// {
// if (_DefaultDisplay == null)
// _DefaultDisplay = new MockDisplay("default", "Default Display");
// return _DefaultDisplay;
// }
//}
//static MockDisplay _DefaultDisplay;
public TwoWayDisplayBase(string key, string name)
: base(key, name)

View File

@@ -1,237 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using Crestron.SimplSharpPro.DM.Endpoints;
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced
{
public RoutingInputPort HdmiIn1 { get; private set; }
public RoutingInputPort HdmiIn2 { get; private set; }
public RoutingInputPort HdmiIn3 { get; private set; }
public RoutingInputPort ComponentIn1 { get; private set; }
public RoutingInputPort VgaIn1 { get; private set; }
bool _PowerIsOn;
bool _IsWarmingUp;
bool _IsCoolingDown;
protected override Func<bool> PowerIsOnFeedbackFunc
{
get
{
return () =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** Display Power is {0}", _PowerIsOn ? "on" : "off");
return _PowerIsOn;
};
} }
protected override Func<bool> IsCoolingDownFeedbackFunc
{
get
{
return () =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** {0}", _IsCoolingDown ? "Display is cooling down" : "Display has finished cooling down");
return _IsCoolingDown;
};
}
}
protected override Func<bool> IsWarmingUpFeedbackFunc
{
get
{
return () =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** {0}", _IsWarmingUp ? "Display is warming up" : "Display has finished warming up");
return _IsWarmingUp;
};
}
}
protected override Func<string> CurrentInputFeedbackFunc { get { return () => "Not Implemented"; } }
int VolumeHeldRepeatInterval = 200;
ushort VolumeInterval = 655;
ushort _FakeVolumeLevel = 31768;
bool _IsMuted;
public MockDisplay(string key, string name)
: base(key, name)
{
HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
ComponentIn1 = new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Video,
eRoutingPortConnectionType.Component, null, this);
VgaIn1 = new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.Video,
eRoutingPortConnectionType.Composite, null, this);
InputPorts.AddRange(new[] { HdmiIn1, HdmiIn2, HdmiIn3, ComponentIn1, VgaIn1 });
VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; });
MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted);
WarmupTime = 10000;
CooldownTime = 10000;
}
public override void PowerOn()
{
if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{
_IsWarmingUp = true;
IsWarmingUpFeedback.InvokeFireUpdate();
// Fake power-up cycle
WarmupTimer = new CTimer(o =>
{
_IsWarmingUp = false;
_PowerIsOn = true;
IsWarmingUpFeedback.InvokeFireUpdate();
PowerIsOnFeedback.InvokeFireUpdate();
}, WarmupTime);
}
}
public override void PowerOff()
{
// If a display has unreliable-power off feedback, just override this and
// remove this check.
if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{
_IsCoolingDown = true;
IsCoolingDownFeedback.InvokeFireUpdate();
// Fake cool-down cycle
CooldownTimer = new CTimer(o =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "Cooldown timer ending");
_IsCoolingDown = false;
IsCoolingDownFeedback.InvokeFireUpdate();
_PowerIsOn = false;
PowerIsOnFeedback.InvokeFireUpdate();
}, CooldownTime);
}
}
public override void PowerToggle()
{
if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)
PowerOff();
else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)
PowerOn();
}
public override void ExecuteSwitch(object selector)
{
Debug.LogMessage(LogEventLevel.Verbose, this, "ExecuteSwitch: {0}", selector);
if (!_PowerIsOn)
{
PowerOn();
}
}
#region IBasicVolumeWithFeedback Members
public IntFeedback VolumeLevelFeedback { get; private set; }
public void SetVolume(ushort level)
{
_FakeVolumeLevel = level;
VolumeLevelFeedback.InvokeFireUpdate();
}
public void MuteOn()
{
_IsMuted = true;
MuteFeedback.InvokeFireUpdate();
}
public void MuteOff()
{
_IsMuted = false;
MuteFeedback.InvokeFireUpdate();
}
public BoolFeedback MuteFeedback { get; private set; }
#endregion
#region IBasicVolumeControls Members
public void VolumeUp(bool pressRelease)
{
//while (pressRelease)
//{
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Down {0}", pressRelease);
if (pressRelease)
{
var newLevel = _FakeVolumeLevel + VolumeInterval;
SetVolume((ushort)newLevel);
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
}
//}
}
public void VolumeDown(bool pressRelease)
{
//while (pressRelease)
//{
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Up {0}", pressRelease);
if (pressRelease)
{
var newLevel = _FakeVolumeLevel - VolumeInterval;
SetVolume((ushort)newLevel);
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
}
//}
}
public void MuteToggle()
{
_IsMuted = !_IsMuted;
MuteFeedback.InvokeFireUpdate();
}
#endregion
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);
}
}
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>
{
public MockDisplayFactory()
{
TypeNames = new List<string>() { "mockdisplay" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device");
return new MockDisplay(dc.Key, dc.Name);
}
}
}

View File

@@ -1,7 +1,7 @@
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
@@ -14,13 +14,13 @@ namespace PepperDash.Essentials.Core
{
public class DeviceFactoryWrapper
{
public CType CType { get; set; }
public Type Type { get; set; }
public string Description { get; set; }
public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; }
public DeviceFactoryWrapper()
{
CType = null;
Type = null;
Description = "Not Available";
}
}
@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
{
try
{
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
var factory = (IDeviceFactory)Activator.CreateInstance(type);
factory.LoadTypeFactories();
}
catch (Exception e)
@@ -69,7 +69,7 @@ namespace PepperDash.Essentials.Core
DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
}
public static void AddFactoryForType(string typeName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
{
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
@@ -79,7 +79,7 @@ namespace PepperDash.Essentials.Core
return;
}
var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
DeviceFactory.FactoryMethods.Add(typeName, wrapper);
}
@@ -149,18 +149,7 @@ namespace PepperDash.Essentials.Core
}
catch (Exception ex)
{
Debug.LogMessage(LogEventLevel.Error, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.StackTrace);
if (ex.InnerException == null)
{
return null;
}
Debug.LogMessage(LogEventLevel.Error, "Inner exception while creating device {0}: {1}", dc.Key,
ex.InnerException.Message);
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.InnerException.StackTrace);
Debug.LogMessage(ex, "Exception occurred while creating device {0}: {1}", null, dc.Key, ex.Message);
return null;
}
}
@@ -180,17 +169,17 @@ namespace PepperDash.Essentials.Core
foreach (var type in types.OrderBy(t => t.Key))
{
var description = type.Value.Description;
var cType = "Not Specified by Plugin";
var Type = "Not Specified by Plugin";
if (type.Value.CType != null)
if (type.Value.Type != null)
{
cType = type.Value.CType.FullName;
Type = type.Value.Type.FullName;
}
CrestronConsole.ConsoleCommandResponse(
@"Type: '{0}'
CType: '{1}'
Description: {2}{3}", type.Key, cType, description, CrestronEnvironment.NewLine);
Type: '{1}'
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
}
}

View File

@@ -1,14 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a class that is capable of loading device types

View File

@@ -1,5 +1,5 @@
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
@@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Core
{
try
{
var factory = (IProcessorExtensionDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(extension);
var factory = (IProcessorExtensionDeviceFactory)Activator.CreateInstance(extension);
factory.LoadFactories();
}
catch( Exception e )
@@ -55,7 +55,7 @@ namespace PepperDash.Essentials.Core
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });
}
public static void AddFactoryForType(string extensionName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
public static void AddFactoryForType(string extensionName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
{
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
@@ -65,7 +65,7 @@ namespace PepperDash.Essentials.Core
return;
}
var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
}

View File

@@ -137,6 +137,7 @@ namespace PepperDash.Essentials.Core
public static void SetFilePathPrefix(string prefix)
{
FilePathPrefix = prefix;
Debug.LogMessage(LogEventLevel.Information, "File Path Prefix set to '{0}'", FilePathPrefix);
}
static string _AssemblyVersion;

View File

@@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp;
@@ -109,7 +109,7 @@ namespace PepperDash.Essentials.Core
protected void AddJoins(Type type)
{
var fields =
type.GetCType()
type.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true));
@@ -501,7 +501,7 @@ namespace PepperDash.Essentials.Core
public string GetNameAttribute(MemberInfo memberInfo)
{
var name = string.Empty;
var attribute = (JoinNameAttribute)CAttribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
var attribute = (JoinNameAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
if (attribute == null) return name;
@@ -514,7 +514,7 @@ namespace PepperDash.Essentials.Core
[AttributeUsage(AttributeTargets.All)]
public class JoinNameAttribute : CAttribute
public class JoinNameAttribute : Attribute
{
private string _Name;

View File

@@ -110,7 +110,7 @@ namespace PepperDash.Essentials.Core.Monitoring
_uptimePollTimer = null;
}
private void PollUptime(object obj)
public void PollUptime(object obj)
{
var consoleResponse = string.Empty;
@@ -142,19 +142,22 @@ namespace PepperDash.Essentials.Core.Monitoring
_uptime = uptimeRaw.Substring(forIndex + 4);
}
private static void ProcessorReboot()
public static void ProcessorReboot()
{
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;
Debug.LogMessage(LogEventLevel.Information, "Rebooting...");
var response = string.Empty;
var response = string.Empty;
CrestronConsole.SendControlSystemCommand("reboot", ref response);
}
private static void ProgramReset(uint index)
public static void ProgramReset(uint index)
{
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;
Debug.LogMessage(LogEventLevel.Information, "Resetting Program {0}...", index);
if (index <= 0 || index > 10) return;
if (index <= 0 || index > 10) return;
var cmd = string.Format("progreset -p:{0}", index);

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
@@ -17,9 +18,31 @@ namespace PepperDash.Essentials.Core
{
private IPartitionStateProvider _partitionSensor;
private bool isInAutoMode;
public bool IsInAutoMode { get; private set; }
private bool partitionPresent;
private bool _partitionPresent;
public bool PartitionPresent
{
get
{
return _partitionPresent;
}
set
{
if (_partitionPresent == value)
{
return;
}
_partitionPresent = value;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.FireUpdate();
}
}
}
public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys)
{
@@ -52,9 +75,16 @@ namespace PepperDash.Essentials.Core
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
if (isInAutoMode)
if (IsInAutoMode)
{
PartitionPresentFeedback.FireUpdate();
if(e.BoolValue)
{
PartitionPresent = true;
}
else
{
PartitionPresent = false;
}
}
}
@@ -64,7 +94,9 @@ namespace PepperDash.Essentials.Core
public void SetAutoMode()
{
isInAutoMode = true;
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Auto Mode", this);
IsInAutoMode = true;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
@@ -76,20 +108,23 @@ namespace PepperDash.Essentials.Core
if (_partitionSensor != null)
{
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
_partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
}
}
public void SetManualMode()
{
isInAutoMode = false;
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Manual Mode", this);
IsInAutoMode = false;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.SetValueFunc(() => partitionPresent);
PartitionPresentFeedback.SetValueFunc(() => _partitionPresent);
}
else
{
PartitionPresentFeedback = new BoolFeedback(() => partitionPresent);
PartitionPresentFeedback = new BoolFeedback(() => _partitionPresent);
}
if (_partitionSensor != null)
@@ -101,27 +136,28 @@ namespace PepperDash.Essentials.Core
public void SetPartitionStatePresent()
{
if (!isInAutoMode)
if (!IsInAutoMode)
{
partitionPresent = true;
PartitionPresentFeedback.FireUpdate();
PartitionPresent = true;
}
}
public void SetPartitionStateNotPresent()
{
if (!isInAutoMode)
if (!IsInAutoMode)
{
partitionPresent = false;
PartitionPresentFeedback.FireUpdate();
PartitionPresent = false;
}
}
public void ToggglePartitionState()
{
if (!isInAutoMode)
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Toggling Partition State for {Key}", this);
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"IsInAutoMode: {IsInAutoMode}", this);
if (!IsInAutoMode)
{
partitionPresent = !partitionPresent;
PartitionPresent = !PartitionPresent;
PartitionPresentFeedback.FireUpdate();
}
}

View File

@@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using System.Collections.Generic;
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
@@ -13,7 +9,11 @@ namespace PepperDash.Essentials.Core
/// </summary>
public interface IPartitionStateProvider : IKeyName
{
[JsonIgnore]
BoolFeedback PartitionPresentFeedback { get; }
[JsonProperty("partitionPresent")]
bool PartitionPresent { get; }
}
/// <summary>
@@ -21,8 +21,12 @@ namespace PepperDash.Essentials.Core
/// </summary>
public interface IPartitionController : IPartitionStateProvider
{
[JsonProperty("adjacentRoomKeys")]
List<string> AdjacentRoomKeys { get; }
[JsonProperty("isInAutoMode")]
bool IsInAutoMode { get; }
void SetPartitionStatePresent();
void SetPartitionStateNotPresent();

View File

@@ -12,6 +12,8 @@
<Title>PepperDash Essentials Core</Title>
<PackageId>PepperDash.Essentials.Core</PackageId>
<InformationalVersion>$(Version)</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>2.0.0-local</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
@@ -22,13 +24,9 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Routing\GenericExtensions.cs" />
<Compile Remove="Routing\IRoutingSinkWithFeedback.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-beta-418" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
<ItemGroup>
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />

View File

@@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using Serilog.Events;
using Newtonsoft.Json;
namespace PepperDash.Essentials
{
@@ -27,24 +27,31 @@ namespace PepperDash.Essentials
/// </summary>
static List<LoadedAssembly> LoadedPluginFolderAssemblies;
public static LoadedAssembly EssentialsAssembly { get; private set; }
public static LoadedAssembly PepperDashCoreAssembly { get; private set; }
public static List<LoadedAssembly> EssentialsPluginAssemblies { get; private set; }
/// <summary>
/// The directory to look in for .cplz plugin packages
/// </summary>
static string _pluginDirectory = Global.FilePathPrefix + "plugins";
static string _pluginDirectory => Global.FilePathPrefix + "plugins";
/// <summary>
/// The directory where plugins will be moved to and loaded from
/// </summary>
static string _loadedPluginsDirectoryPath = _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
static string _loadedPluginsDirectoryPath => _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
// The temp directory where .cplz archives will be unzipped to
static string _tempDirectory = _pluginDirectory + Global.DirectorySeparator + "temp";
static string _tempDirectory => _pluginDirectory + Global.DirectorySeparator + "temp";
static PluginLoader()
{
LoadedAssemblies = new List<LoadedAssembly>();
LoadedPluginFolderAssemblies = new List<LoadedAssembly>();
EssentialsPluginAssemblies = new List<LoadedAssembly>();
}
/// <summary>
@@ -69,6 +76,7 @@ namespace PepperDash.Essentials
case ("PepperDashEssentials.dll"):
{
version = Global.AssemblyVersion;
EssentialsAssembly = new LoadedAssembly(fi.Name, version, assembly);
break;
}
case ("PepperDash_Essentials_Core.dll"):
@@ -81,9 +89,12 @@ namespace PepperDash.Essentials
version = Global.AssemblyVersion;
break;
}
case ("PepperDash_Core.dll"):
case ("PepperDashCore.dll"):
{
version = PepperDash.Core.Debug.PepperDashCoreVersion;
Debug.LogMessage(LogEventLevel.Verbose, "Found PepperDash_Core.dll");
version = Debug.PepperDashCoreVersion;
Debug.LogMessage(LogEventLevel.Verbose, "PepperDash_Core Version: {0}", version);
PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);
break;
}
}
@@ -119,24 +130,31 @@ namespace PepperDash.Essentials
/// <param name="fileName"></param>
static LoadedAssembly LoadAssembly(string filePath)
{
//Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath);
var assembly = Assembly.LoadFrom(filePath);
if (assembly != null)
try
{
var assyVersion = GetAssemblyVersion(assembly);
//Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath);
var assembly = Assembly.LoadFrom(filePath);
if (assembly != null)
{
var assyVersion = GetAssemblyVersion(assembly);
var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
LoadedAssemblies.Add(loadedAssembly);
Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
return loadedAssembly;
}
else
var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
LoadedAssemblies.Add(loadedAssembly);
Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
return loadedAssembly;
}
else
{
Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
}
return null;
} catch(Exception ex)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
Debug.LogMessage(ex, "Error loading assembly from {path}", null, filePath);
return null;
}
return null;
}
/// <summary>
@@ -144,7 +162,7 @@ namespace PepperDash.Essentials
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
static string GetAssemblyVersion(Assembly assembly)
public static string GetAssemblyVersion(Assembly assembly)
{
var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
if (ver != null && ver.Length > 0)
@@ -190,12 +208,19 @@ namespace PepperDash.Essentials
/// <param name="command"></param>
public static void ReportAssemblyVersions(string command)
{
CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:" + CrestronEnvironment.NewLine);
foreach (var assembly in LoadedAssemblies)
CrestronConsole.ConsoleCommandResponse("Essentials Version: {0}" + CrestronEnvironment.NewLine, Global.AssemblyVersion);
CrestronConsole.ConsoleCommandResponse("PepperDash Core Version: {0}" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version);
CrestronConsole.ConsoleCommandResponse("Essentials Plugin Versions:" + CrestronEnvironment.NewLine);
foreach (var assembly in EssentialsPluginAssemblies)
{
CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
}
//CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:" + CrestronEnvironment.NewLine);
//foreach (var assembly in LoadedAssemblies)
//{
// CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
//}
}
/// <summary>
/// Moves any .dll assemblies not already loaded from the plugins folder to loadedPlugins folder
@@ -354,7 +379,7 @@ namespace PepperDash.Essentials
try
{
var assy = loadedAssembly.Assembly;
CType[] types = {};
Type[] types = {};
try
{
types = assy.GetTypes();
@@ -375,7 +400,7 @@ namespace PepperDash.Essentials
if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)
{
var plugin =
(IPluginDeviceFactory) Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
(IPluginDeviceFactory)Activator.CreateInstance(type);
LoadCustomPlugin(plugin, loadedAssembly);
}
}
@@ -432,6 +457,9 @@ namespace PepperDash.Essentials
Debug.LogMessage(LogEventLevel.Information, "Loading plugin: {0}", loadedAssembly.Name);
plugin.LoadTypeFactories();
if(!EssentialsPluginAssemblies.Contains(loadedAssembly))
EssentialsPluginAssemblies.Add(loadedAssembly);
}
/// <summary>
@@ -439,7 +467,7 @@ namespace PepperDash.Essentials
/// </summary>
/// <param name="type"></param>
/// <param name="loadPlugin"></param>
static void LoadCustomLegacyPlugin(CType type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly)
static void LoadCustomLegacyPlugin(Type type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly)
{
Debug.LogMessage(LogEventLevel.Verbose, "LoadPlugin method found in {0}", type.Name);
@@ -486,6 +514,8 @@ namespace PepperDash.Essentials
/// </summary>
public static void LoadPlugins()
{
Debug.LogMessage(LogEventLevel.Information, "Attempting to Load Plugins from {_pluginDirectory}", _pluginDirectory);
if (Directory.Exists(_pluginDirectory))
{
Debug.LogMessage(LogEventLevel.Information, "Plugins directory found, checking for plugins");
@@ -514,8 +544,11 @@ namespace PepperDash.Essentials
/// </summary>
public class LoadedAssembly
{
[JsonProperty("name")]
public string Name { get; private set; }
[JsonProperty("version")]
public string Version { get; private set; }
[JsonIgnore]
public Assembly Assembly { get; private set; }
public LoadedAssembly(string name, string version, Assembly assembly)

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Serilog.Events;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core
{
@@ -17,7 +17,33 @@ namespace PepperDash.Essentials.Core
private List<IEssentialsRoom> _rooms;
private bool isInAutoMode;
public List<IKeyName> Rooms
{
get
{
return _rooms.Cast<IKeyName>().ToList();
}
}
private bool _isInAutoMode;
public bool IsInAutoMode
{
get
{
return _isInAutoMode;
}
set
{
if(value == _isInAutoMode)
{
return;
}
_isInAutoMode = value;
IsInAutoModeFeedback.FireUpdate();
}
}
private CTimer _scenarioChangeDebounceTimer;
@@ -36,14 +62,14 @@ namespace PepperDash.Essentials.Core
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
}
IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode);
// default to auto mode
isInAutoMode = true;
IsInAutoMode = true;
if (_propertiesConfig.defaultToManualMode)
{
isInAutoMode = false;
IsInAutoMode = false;
}
IsInAutoModeFeedback.FireUpdate();
@@ -56,7 +82,7 @@ namespace PepperDash.Essentials.Core
SetRooms();
if (isInAutoMode)
if (IsInAutoMode)
{
DetermineRoomCombinationScenario();
}
@@ -111,7 +137,11 @@ namespace PepperDash.Essentials.Core
void StartDebounceTimer()
{
var time = _scenarioChangeDebounceTimeSeconds * 1000;
// default to 500ms for manual mode
var time = 500;
// if in auto mode, debounce the scenario change
if(IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
if (_scenarioChangeDebounceTimer == null)
{
@@ -185,7 +215,7 @@ namespace PepperDash.Essentials.Core
{
_currentScenario.Activate();
Debug.LogMessage(LogEventLevel.Debug, this, "Current Scenario: {0}", _currentScenario.Name);
Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this);
}
var handler = RoomCombinationScenarioChanged;
@@ -201,20 +231,36 @@ namespace PepperDash.Essentials.Core
public void SetAutoMode()
{
isInAutoMode = true;
IsInAutoModeFeedback.FireUpdate();
IsInAutoMode = true;
foreach (var partition in Partitions)
{
partition.SetAutoMode();
}
DetermineRoomCombinationScenario();
}
public void SetManualMode()
{
isInAutoMode = false;
IsInAutoModeFeedback.FireUpdate();
IsInAutoMode = false;
foreach (var partition in Partitions)
{
partition.SetManualMode();
}
}
public void ToggleMode()
{
isInAutoMode = !isInAutoMode;
IsInAutoModeFeedback.FireUpdate();
if(IsInAutoMode)
{
SetManualMode();
}
else
{
SetAutoMode();
}
}
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
@@ -223,7 +269,7 @@ namespace PepperDash.Essentials.Core
public void TogglePartitionState(string partitionKey)
{
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey));
if (partition != null)
{
@@ -233,7 +279,7 @@ namespace PepperDash.Essentials.Core
public void SetRoomCombinationScenario(string scenarioKey)
{
if (isInAutoMode)
if (IsInAutoMode)
{
Debug.LogMessage(LogEventLevel.Information, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
return;

View File

@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
@@ -21,13 +18,21 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// The current room combination scenario
/// </summary>
[JsonProperty("currentScenario")]
IRoomCombinationScenario CurrentScenario { get; }
/// <summary>
/// When true, indicates the current mode is auto mode
/// </summary>
[JsonIgnore]
BoolFeedback IsInAutoModeFeedback {get;}
[JsonProperty("isInAutoMode")]
bool IsInAutoMode { get; }
[JsonProperty("rooms")]
List<IKeyName> Rooms { get; }
/// <summary>
/// Sets auto mode
/// </summary>
@@ -46,11 +51,13 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// The available room combinatino scenarios
/// </summary>
[JsonProperty("roomCombinationScenarios")]
List<IRoomCombinationScenario> RoomCombinationScenarios { get; }
/// <summary>
/// The partition
/// </summary>
[JsonProperty("partitions")]
List<IPartitionController> Partitions { get; }
/// <summary>
@@ -71,8 +78,12 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// When true, indicates that this room combination scenario is active
/// </summary>
[JsonIgnore]
BoolFeedback IsActiveFeedback { get; }
[JsonProperty("isActive")]
bool IsActive { get; }
/// <summary>
/// Activates this room combination scenario
/// </summary>
@@ -86,11 +97,13 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// The state of the partitions that would activate this scenario
/// </summary>
[JsonProperty("partitionStates")]
List<PartitionState> PartitionStates { get; }
/// <summary>
/// The mapping of UIs by key to rooms by key
/// </summary>
[JsonProperty("uiMap")]
Dictionary<string, string> UiMap { get; set; }
}

View File

@@ -16,20 +16,41 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// Represents a room combination scenario
/// </summary>
public class RoomCombinationScenario: IRoomCombinationScenario
public class RoomCombinationScenario: IRoomCombinationScenario, IKeyName
{
private RoomCombinationScenarioConfig _config;
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("partitionStates")]
public List<PartitionState> PartitionStates { get; private set; }
[JsonProperty("uiMap")]
public Dictionary<string, string> UiMap { get; set; }
private bool _isActive;
[JsonProperty("isActive")]
public bool IsActive
{
get { return _isActive; }
set
{
if(value == _isActive)
{
return;
}
_isActive = value;
IsActiveFeedback.FireUpdate();
}
}
[JsonIgnore]
public BoolFeedback IsActiveFeedback { get; private set; }
private List<DeviceActionWrapper> activationActions;
@@ -67,8 +88,7 @@ namespace PepperDash.Essentials.Core
}
}
_isActive = true;
IsActiveFeedback.FireUpdate();
IsActive = true;
}
public void Deactivate()
@@ -83,8 +103,7 @@ namespace PepperDash.Essentials.Core
}
}
_isActive = false;
IsActiveFeedback.FireUpdate();
IsActive = false;
}
}

View File

@@ -198,6 +198,12 @@ namespace PepperDash.Essentials.Room.Config
public string SourceListKey { get; set; }
[JsonProperty("destinationListKey")]
public string DestinationListKey { get; set; }
[JsonProperty("audioControlPointListKey")]
public string AudioControlPointListKey { get; set; }
[JsonProperty("cameraListKey")]
public string CameraListKey { get; set; }
[JsonProperty("defaultSourceItem")]
public string DefaultSourceItem { get; set; }
/// <summary>

View File

@@ -59,28 +59,102 @@ namespace PepperDash.Essentials.Core
/// </summary>
public IMobileControlRoomMessenger MobileControlRoomBridge { get; private set; }
protected const string _defaultListKey = "default";
/// <summary>
/// The config name of the source list
/// </summary>
///
protected string _SourceListKey;
private string _sourceListKey;
public string SourceListKey {
get
{
return _SourceListKey;
}
private set
{
if (value != _SourceListKey)
if(string.IsNullOrEmpty(_sourceListKey))
{
_SourceListKey = value;
return _defaultListKey;
}
else
{
return _sourceListKey;
}
}
protected set
{
if (value != _sourceListKey)
{
_sourceListKey = value;
}
}
}
public string DestinationListKey { get; private set; }
private string _destinationListKey;
public string DestinationListKey
{
get
{
if (string.IsNullOrEmpty(_destinationListKey))
{
return _defaultListKey;
}
else
{
return _destinationListKey;
}
}
protected set
{
if (value != _destinationListKey)
{
_destinationListKey = value;
}
}
}
protected const string _defaultSourceListKey = "default";
private string _audioControlPointListKey;
public string AudioControlPointListKey
{
get
{
if (string.IsNullOrEmpty(_audioControlPointListKey))
{
return _defaultListKey;
}
else
{
return _destinationListKey;
}
}
protected set
{
if (value != _audioControlPointListKey)
{
_audioControlPointListKey = value;
}
}
}
private string _cameraListKey;
public string CameraListKey
{
get
{
if (string.IsNullOrEmpty(_cameraListKey))
{
return _defaultListKey;
}
else
{
return _cameraListKey;
}
}
protected set
{
if (value != _cameraListKey)
{
_cameraListKey = value;
}
}
}
/// <summary>
/// Timer used for informing the UIs of a shutdown
@@ -141,6 +215,7 @@ namespace PepperDash.Essentials.Core
if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
ShutdownType = eShutdownType.None;
};
ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
ShutdownPromptSeconds = 60;
@@ -191,7 +266,7 @@ namespace PepperDash.Essentials.Core
}
else
{
sourceListKey = _defaultSourceListKey;
sourceListKey = _defaultListKey;
}
}

View File

@@ -29,6 +29,10 @@ namespace PepperDash.Essentials.Core
string DestinationListKey { get; }
string AudioControlPointListKey { get; }
string CameraListKey { get; }
SecondsCountdownTimer ShutdownPromptTimer { get; }
int ShutdownPromptSeconds { get; }
int ShutdownVacancySeconds { get; }

View File

@@ -159,4 +159,13 @@ namespace PepperDash.Essentials.Core
Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; }
}
public interface IHasAccessoryDevices : IKeyName
{
List<string> AccessoryDeviceKeys { get; }
}
public interface IHasCiscoNavigatorTouchpanel
{
string CiscoNavigatorTouchpanelKey { get; }
}
}

View File

@@ -1,5 +1,6 @@
using PepperDash.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -12,20 +13,37 @@ namespace PepperDash.Essentials.Core
/// on those destinations.
/// </summary>
public static class Extensions
{
{
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
/// <summary>
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
/// in RouteDescriptorCollection.DefaultCollection
/// </summary>
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
{
var routeRequest = new RouteRequest {
Destination = destination,
Source = source,
SignalType = signalType
};
/// <summary>
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
/// in RouteDescriptorCollection.DefaultCollection
/// </summary>
public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, string destinationPortKey = "", string sourcePortKey = "")
{
var inputPort = string.IsNullOrEmpty(destinationPortKey) ? null : destination.InputPorts.FirstOrDefault(p => p.Key == destinationPortKey);
var outputPort = string.IsNullOrEmpty(sourcePortKey) ? null : source.OutputPorts.FirstOrDefault(p => p.Key == sourcePortKey);
ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);
}
private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)
{
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (source == null) throw new ArgumentNullException(nameof(source));
if (destinationPort == null) Debug.LogMessage(LogEventLevel.Verbose, "Destination port is null");
if (sourcePort == null) Debug.LogMessage(LogEventLevel.Verbose, "Source port is null");
var routeRequest = new RouteRequest
{
Destination = destination,
DestinationPort = destinationPort,
Source = source,
SourcePort = sourcePort,
SignalType = signalType
};
var coolingDevice = destination as IWarmingCooling;
@@ -65,33 +83,39 @@ namespace PepperDash.Essentials.Core
destination.ReleaseRoute();
RunRouteRequest(routeRequest);
}
RunRouteRequest(routeRequest);
}
private static void RunRouteRequest(RouteRequest request)
{
if (request.Source == null)
return;
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
if (newRoute == null)
var (audioOrSingleRoute, videoRoute) = request.Destination.GetRouteToSource(request.Source, request.SignalType, request.DestinationPort, request.SourcePort);
if (audioOrSingleRoute == null && videoRoute == null)
return;
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(audioOrSingleRoute);
if (videoRoute != null)
{
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(videoRoute);
}
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
newRoute.ExecuteRoutes();
audioOrSingleRoute.ExecuteRoutes();
videoRoute?.ExecuteRoutes();
}
/// <summary>
/// Will release the existing route on the destination, if it is found in
/// RouteDescriptorCollection.DefaultCollection
/// </summary>
/// <param name="destination"></param>
public static void ReleaseRoute(this IRoutingSink destination)
{
/// <summary>
/// Will release the existing route on the destination, if it is found in
/// RouteDescriptorCollection.DefaultCollection
/// </summary>
/// <param name="destination"></param>
public static void ReleaseRoute(this IRoutingInputs destination)
{
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
{
@@ -102,138 +126,191 @@ namespace PepperDash.Essentials.Core
RouteRequests.Remove(destination.Key);
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
if (current != null)
{
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes();
}
}
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
if (current != null)
{
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes();
}
}
/// <summary>
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
/// of an audio/video route are discovered a route descriptor is returned. If no route is
/// discovered, then null is returned
/// </summary>
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
{
var routeDescriptor = new RouteDescriptor(source, destination, signalType);
/// <summary>
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
/// of an audio/video route are discovered a route descriptor is returned. If no route is
/// discovered, then null is returned
/// </summary>
public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
{
// if it's a single signal type, find the route
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
{
var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, signalType);
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key, signalType);
// if it's a single signal type, find the route
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
{
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {0}", null, source.Key);
if (!destination.GetRouteToSource(source, null, null, signalType, 0, singleTypeRouteDescriptor, destinationPort, sourcePort))
singleTypeRouteDescriptor = null;
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescriptor))
routeDescriptor = null;
foreach (var route in singleTypeRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Route for device: {route}", destination, route.ToString());
}
return routeDescriptor;
}
// otherwise, audioVideo needs to be handled as two steps.
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build audio and video routes from {0}", destination, source.Key);
return (singleTypeRouteDescriptor, null);
}
// otherwise, audioVideo needs to be handled as two steps.
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescriptor);
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
var audioRouteDescriptor = new RouteDescriptor(source, destination, eRoutingSignalType.Audio);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescriptor);
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, audioRouteDescriptor, destinationPort, sourcePort);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
if (!audioSuccess && !videoSuccess)
routeDescriptor = null;
return routeDescriptor;
}
var videoRouteDescriptor = new RouteDescriptor(source, destination, eRoutingSignalType.Video);
/// <summary>
/// The recursive part of this. Will stop on each device, search its inputs for the
/// desired source and if not found, invoke this function for the each input port
/// hoping to find the source.
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
/// <param name="cycle">Just an informational counter</param>
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
/// <returns>true if source is hit</returns>
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
{
cycle++;
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, videoRouteDescriptor, destinationPort, sourcePort);
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", null, cycle, source.Key, destination.Key);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
RoutingInputPort goodInputPort = null;
foreach (var route in audioRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Audio route for device: {route}", destination, route.ToString());
}
var destinationTieLines = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
foreach (var route in videoRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Video route for device: {route}", destination, route.ToString());
}
// find a direct tie
var directTie = destinationTieLines.FirstOrDefault(
t => t.DestinationPort.ParentDevice == destination
&& t.SourcePort.ParentDevice == source);
if (directTie != null) // Found a tie directly to the source
{
goodInputPort = directTie.DestinationPort;
}
else // no direct-connect. Walk back devices.
{
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {0}. Walking down tie lines", destination, source.Key);
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var attachedMidpoints = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
if (!audioSuccess && !videoSuccess)
return (null, null);
return (audioRouteDescriptor, videoRouteDescriptor);
}
/// <summary>
/// The recursive part of this. Will stop on each device, search its inputs for the
/// desired source and if not found, invoke this function for the each input port
/// hoping to find the source.
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
/// <param name="destinationPort">The RoutingOutputPort whose link is being checked for a route</param>
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
/// <param name="cycle">Just an informational counter</param>
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
/// <returns>true if source is hit</returns>
private static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
{
cycle++;
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {cycle} {sourceKey}:{sourcePortKey}--> {destinationKey}:{destinationPortKey} {type}", null, cycle, source.Key, sourcePort?.Key ?? "auto", destination.Key, destinationPort?.Key ?? "auto", signalType.ToString());
RoutingInputPort goodInputPort = null;
IEnumerable<TieLine> destinationTieLines;
TieLine directTie = null;
if (destinationPort == null)
{
destinationTieLines = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice.Key == destination.Key && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
}
else
{
destinationTieLines = TieLineCollection.Default.Where(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.DestinationPort.Key == destinationPort.Key && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
}
// find the TieLine without a port
if (destinationPort == null && sourcePort == null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.SourcePort.ParentDevice.Key == source.Key);
}
// find a tieLine to a specific destination port without a specific source port
else if (destinationPort != null && sourcePort == null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key);
}
// find a tieline to a specific source port without a specific destination port
else if (destinationPort == null & sourcePort != null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.SourcePort.ParentDevice.Key == source.Key && t.SourcePort.Key == sourcePort.Key);
}
// find a tieline to a specific source port and destination port
else if (destinationPort != null && sourcePort != null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key && t.SourcePort.Key == sourcePort.Key);
}
if (directTie != null) // Found a tie directly to the source
{
goodInputPort = directTie.DestinationPort;
}
else // no direct-connect. Walk back devices.
{
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {sourceKey}. Walking down tie lines", destination, source.Key);
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var midpointTieLines = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
foreach (var inputTieToTry in attachedMidpoints)
{
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {0}", destination, upstreamRoutingDevice.Key);
foreach (var tieLine in midpointTieLines)
{
var midpointDevice = tieLine.SourcePort.ParentDevice as IRoutingInputsOutputs;
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(midpointDevice))
{
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {0} on {1}, this was already checked", destination, upstreamRoutingDevice.Key, destination.Key);
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {midpointDeviceKey} on {destinationKey}, this was already checked", destination, midpointDevice.Key, destination.Key);
continue;
}
var midpointOutputPort = tieLine.SourcePort;
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {midpointDeviceKey}", destination, midpointDevice.Key);
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable);
var upstreamRoutingSuccess = midpointDevice.GetRouteToSource(source, midpointOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable, null, sourcePort);
if (upstreamRoutingSuccess)
{
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
goodInputPort = inputTieToTry.DestinationPort;
Debug.LogMessage(LogEventLevel.Verbose, "Route found on {midpointDeviceKey}", destination, midpointDevice.Key);
Debug.LogMessage(LogEventLevel.Verbose, "TieLine: SourcePort: {SourcePort} DestinationPort: {DestinationPort}", destination, tieLine.SourcePort, tieLine.DestinationPort);
goodInputPort = tieLine.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
}
}
}
if (goodInputPort == null)
{
if (goodInputPort == null)
{
Debug.LogMessage(LogEventLevel.Verbose, "No route found to {0}", destination, source.Key);
return false;
}
}
// we have a route on corresponding inputPort. *** Do the route ***
if (outputPortToUse == null)
if (destination is IRoutingSink)
{
// it's a sink device
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
@@ -244,8 +321,8 @@ namespace PepperDash.Essentials.Core
}
else // device is merely IRoutingInputOutputs
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
return true;
}
}
}
}

View File

@@ -1,261 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
/// on those destinations.
/// </summary>
public static class GenericExtensions
{
//private static readonly Dictionary<string, RouteRequest<TInputSelector, TOutputSelector>> RouteRequests = new();
/// <summary>
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
/// in RouteDescriptorCollection.DefaultCollection
/// </summary>
public static void ReleaseAndMakeRoute<TInputSelector, TOutputSelector>(this IRoutingSink<TInputSelector> destination, IRoutingOutputs<TOutputSelector> source, eRoutingSignalType signalType)
{
var routeRequest = new RouteRequest<TInputSelector, TOutputSelector> {
Destination = destination,
Source = source,
SignalType = signalType
};
var coolingDevice = destination as IWarmingCooling;
var existingRouteRequest = destination.GetRouteRequest<TOutputSelector>();
//We already have a route request for this device, and it's a cooling device and is cooling
if (existingRouteRequest != null && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
destination.UpdateRouteRequest(routeRequest);
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
return;
}
//New Request
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
destination.UpdateRouteRequest(routeRequest);
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down. Storing route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
return;
}
if (existingRouteRequest != null && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
{
destination.UpdateRouteRequest<TOutputSelector>(null);
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", null, destination.Key, routeRequest.Source.Key);
}
destination.ReleaseRoute();
RunRouteRequest(routeRequest);
}
public static void RunRouteRequest<TInputSelector, TOutputSelector>(RouteRequest<TInputSelector, TOutputSelector> request)
{
if (request.Source == null)
return;
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
if (newRoute == null)
return;
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
newRoute.ExecuteRoutes();
}
/// <summary>
/// Will release the existing route on the destination, if it is found in
/// RouteDescriptorCollection.DefaultCollection
/// </summary>
/// <param name="destination"></param>
public static void ReleaseRoute<TInputSelector, TOutputSelector>(this IRoutingSink<TInputSelector> destination)
{
var existingRouteRequest = destination.GetRouteRequest<TOutputSelector>();
if (existingRouteRequest != null && destination is IWarmingCooling)
{
var coolingDevice = destination as IWarmingCooling;
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
}
destination.UpdateRouteRequest<TOutputSelector>(null);
var current = RouteDescriptorCollection<TInputSelector, TOutputSelector>.DefaultCollection.RemoveRouteDescriptor(destination);
if (current != null)
{
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes();
}
}
/// <summary>
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
/// of an audio/video route are discovered a route descriptor is returned. If no route is
/// discovered, then null is returned
/// </summary>
public static RouteDescriptor<TInputSelector, TOutputSelector> GetRouteToSource<TInputSelector, TOutputSelector>(this IRoutingSink<TInputSelector> destination, IRoutingOutputs<TOutputSelector> source, eRoutingSignalType signalType)
{
var routeDescriptor = new RouteDescriptor<TInputSelector, TOutputSelector>(source, destination, signalType);
// if it's a single signal type, find the route
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
{
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {0}", null, source.Key);
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescriptor))
routeDescriptor = null;
return routeDescriptor;
}
// otherwise, audioVideo needs to be handled as two steps.
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build audio and video routes from {0}", destination, source.Key);
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescriptor);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescriptor);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
if (!audioSuccess && !videoSuccess)
routeDescriptor = null;
return routeDescriptor;
}
/// <summary>
/// The recursive part of this. Will stop on each device, search its inputs for the
/// desired source and if not found, invoke this function for the each input port
/// hoping to find the source.
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
/// <param name="cycle">Just an informational counter</param>
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
/// <returns>true if source is hit</returns>
static bool GetRouteToSource<TInputSelector, TOutputSelector>(this IRoutingInputs<TInputSelector> destination, IRoutingOutputs<TOutputSelector> source,
RoutingOutputPort<TOutputSelector> outputPortToUse, List<IRoutingInputsOutputs<TInputSelector, TOutputSelector>> alreadyCheckedDevices,
eRoutingSignalType signalType, int cycle, RouteDescriptor<TInputSelector, TOutputSelector> routeTable)
{
cycle++;
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", null, cycle, source.Key, destination.Key);
RoutingInputPort<TInputSelector> goodInputPort = null;
var destDevInputTies = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
// find a direct tie
var directTie = destDevInputTies.FirstOrDefault(
t => t.DestinationPort.ParentDevice == destination
&& t.SourcePort.ParentDevice == source);
if (directTie != null) // Found a tie directly to the source
{
goodInputPort = directTie.DestinationPort;
}
else // no direct-connect. Walk back devices.
{
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {0}. Walking down tie lines", destination, source.Key);
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs<TInputSelector, TOutputSelector>>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs<TInputSelector, TOutputSelector>);
foreach (var inputTieToTry in attachedMidpoints)
{
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs<TInputSelector, TOutputSelector>;
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {0}", destination, upstreamRoutingDevice.Key);
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
{
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {0} on {1}, this was already checked", destination, upstreamRoutingDevice.Key, destination.Key);
continue;
}
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable);
if (upstreamRoutingSuccess)
{
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
goodInputPort = inputTieToTry.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
}
if (goodInputPort == null)
{
Debug.LogMessage(LogEventLevel.Verbose, "No route found to {0}", destination, source.Key);
return false;
}
// we have a route on corresponding inputPort. *** Do the route ***
if (outputPortToUse == null)
{
// it's a sink device
routeTable.Routes.Add(new RouteSwitchDescriptor<TInputSelector, TOutputSelector>(goodInputPort));
}
else if (destination is IRouting)
{
routeTable.Routes.Add(new RouteSwitchDescriptor<TInputSelector, TOutputSelector>(outputPortToUse, goodInputPort));
}
else // device is merely IRoutingInputOutputs
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
return true;
}
}
}

View File

@@ -3,7 +3,7 @@
/// <summary>
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
/// </summary>
public interface IRoutingSource
{
}
public interface IRoutingSource : IRoutingOutputs
{
}
}

View File

@@ -2,19 +2,22 @@
{
public class RouteRequest
{
public IRoutingSink Destination {get; set;}
public IRoutingOutputs Source {get; set;}
public eRoutingSignalType SignalType {get; set;}
public RoutingInputPort DestinationPort { get; set; }
public RoutingOutputPort SourcePort { get; set; }
public IRoutingInputs Destination { get; set; }
public IRoutingOutputs Source { get; set; }
public eRoutingSignalType SignalType { get; set; }
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
var coolingDevice = sender as IWarmingCooling;
if(args.BoolValue == false)
if (args.BoolValue == false)
{
Destination.ReleaseAndMakeRoute(Source, SignalType);
if(sender == null) return;
if (sender == null) return;
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
}

View File

@@ -239,5 +239,13 @@ namespace PepperDash.Essentials.Core
/// HdBaseTOut
/// </summary>
public const string HdBaseTOut = "hdBaseTOut";
/// <summary>
/// SdiIn
/// </summary>
public const string SdiIn = "sdiIn";
/// <summary>
/// SdiOut
/// </summary>
public const string SdiOut = "sdiOut";
}
}

View File

@@ -1,98 +1,91 @@
using System;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class TieLine
{
public RoutingOutputPort SourcePort { get; private set; }
public RoutingInputPort DestinationPort { get; private set; }
//public int InUseCount { get { return DestinationUsingThis.Count; } }
public class TieLine
{
public RoutingOutputPort SourcePort { get; private set; }
public RoutingInputPort DestinationPort { get; private set; }
//public int InUseCount { get { return DestinationUsingThis.Count; } }
/// <summary>
/// Gets the type of this tie line. Will either be the type of the desination port
/// or the type of OverrideType when it is set.
/// </summary>
public eRoutingSignalType Type
{
get
{
if (OverrideType.HasValue) return OverrideType.Value;
return DestinationPort.Type;
}
}
/// <summary>
/// Gets the type of this tie line. Will either be the type of the desination port
/// or the type of OverrideType when it is set.
/// </summary>
public eRoutingSignalType Type
{
get
{
if (OverrideType.HasValue) return OverrideType.Value;
return DestinationPort.Type;
}
}
/// <summary>
/// Use this to override the Type property for the destination port. For example,
/// when the tie line is type AudioVideo, and the signal flow should be limited to
/// Audio-only or Video only, changing this type will alter the signal paths
/// available to the routing algorithm without affecting the actual Type
/// of the destination port.
/// </summary>
public eRoutingSignalType? OverrideType { get; set; }
/// <summary>
/// Use this to override the Type property for the destination port. For example,
/// when the tie line is type AudioVideo, and the signal flow should be limited to
/// Audio-only or Video only, changing this type will alter the signal paths
/// available to the routing algorithm without affecting the actual Type
/// of the destination port.
/// </summary>
public eRoutingSignalType? OverrideType { get; set; }
//List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>();
//List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>();
/// <summary>
/// For tie lines that represent internal links, like from cards to the matrix in a DM.
/// This property is true if SourcePort and DestinationPort IsInternal
/// property are both true
/// </summary>
public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } }
public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } }
public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } }
public string TypeMismatchNote { get; set; }
/// <summary>
/// For tie lines that represent internal links, like from cards to the matrix in a DM.
/// This property is true if SourcePort and DestinationPort IsInternal
/// property are both true
/// </summary>
public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } }
public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } }
public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } }
public string TypeMismatchNote { get; set; }
/// <summary>
///
/// </summary>
/// <param name="sourcePort"></param>
/// <param name="destinationPort"></param>
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort)
{
if (sourcePort == null || destinationPort == null)
throw new ArgumentNullException("source or destination port");
SourcePort = sourcePort;
DestinationPort = destinationPort;
}
/// <summary>
///
/// </summary>
/// <param name="sourcePort"></param>
/// <param name="destinationPort"></param>
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort)
{
if (sourcePort == null || destinationPort == null)
throw new ArgumentNullException("source or destination port");
SourcePort = sourcePort;
DestinationPort = destinationPort;
}
/// <summary>
/// Creates a tie line with an overriding Type. See help for OverrideType property for info
/// </summary>
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) :
this(sourcePort, destinationPort)
{
OverrideType = overrideType;
}
/// <summary>
/// Creates a tie line with an overriding Type. See help for OverrideType property for info
/// </summary>
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) :
this(sourcePort, destinationPort)
{
OverrideType = overrideType;
}
/// <summary>
/// Will link up video status from supporting inputs to connected outputs
/// </summary>
public void Activate()
{
// Now does nothing
}
/// <summary>
/// Will link up video status from supporting inputs to connected outputs
/// </summary>
public void Activate()
{
// Now does nothing
}
public void Deactivate()
{
// Now does nothing
}
public void Deactivate()
{
// Now does nothing
}
public override string ToString()
{
return string.Format("Tie line: {0}:{1} --> {2}:{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
DestinationPort.ParentDevice.Key, DestinationPort.Key);
}
}
public override string ToString()
{
return string.Format("Tie line: {0}:{1} --> {2}:{3} {4}", SourcePort.ParentDevice.Key, SourcePort.Key,
DestinationPort.ParentDevice.Key, DestinationPort.Key, Type.ToString());
}
}
//********************************************************************************
@@ -109,6 +102,6 @@ namespace PepperDash.Essentials.Core
}
[JsonIgnore]
static TieLineCollection _Default;
}
private static TieLineCollection _Default;
}
}

View File

@@ -52,8 +52,8 @@ namespace PepperDash.Essentials.Core
var timeSpan = FinishTime - DateTime.Now;
Debug.LogMessage(LogEventLevel.Verbose, this,
"timeSpan.Minutes == {0}, timeSpan.Seconds == {1}, timeSpan.TotalSeconds == {2}",
Debug.LogMessage(LogEventLevel.Verbose,
"timeSpan.Minutes == {0}, timeSpan.Seconds == {1}, timeSpan.TotalSeconds == {2}", this,
timeSpan.Minutes, timeSpan.Seconds, timeSpan.TotalSeconds);
if (Math.Floor(timeSpan.TotalSeconds) < 60 && Math.Floor(timeSpan.TotalSeconds) >= 0) //ignore milliseconds
@@ -103,6 +103,7 @@ namespace PepperDash.Essentials.Core
public void Reset()
{
_isRunning = false;
IsRunningFeedback.FireUpdate();
Start();
}
@@ -133,7 +134,11 @@ namespace PepperDash.Essentials.Core
void StopHelper()
{
if (_secondTimer != null)
{
_secondTimer.Stop();
_secondTimer = null;
}
_isRunning = false;
IsRunningFeedback.FireUpdate();
}

View File

@@ -1,38 +1,74 @@
namespace PepperDash.Essentials.Core
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core
{
public class CrestronTouchpanelPropertiesConfig
{
[JsonProperty("control")]
public EssentialsControlPropertiesConfig ControlProperties { get; set; }
[JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
public string IpId { get; set; }
[JsonProperty("defaultRoomKey", NullValueHandling = NullValueHandling.Ignore)]
public string DefaultRoomKey { get; set; }
[JsonProperty("roomListKey", NullValueHandling = NullValueHandling.Ignore)]
public string RoomListKey { get; set; }
[JsonProperty("sgdFile", NullValueHandling = NullValueHandling.Ignore)]
public string SgdFile { get; set; }
[JsonProperty("projectName", NullValueHandling = NullValueHandling.Ignore)]
public string ProjectName { get; set; }
public bool ShowVolumeGauge { get; set; }
public bool UsesSplashPage { get; set; }
public bool ShowDate { get; set; }
public bool ShowTime { get; set; }
[JsonProperty("showVolumeGauge", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowVolumeGauge { get; set; }
[JsonProperty("usesSplashPage", NullValueHandling = NullValueHandling.Ignore)]
public bool? UsesSplashPage { get; set; }
[JsonProperty("showDate", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowDate { get; set; }
[JsonProperty("showTime", NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowTime { get; set; }
[JsonProperty("setup", NullValueHandling = NullValueHandling.Ignore)]
public UiSetupPropertiesConfig Setup { get; set; }
[JsonProperty("headerStyle", NullValueHandling = NullValueHandling.Ignore)]
public string HeaderStyle { get; set; }
public bool IncludeInFusionRoomHealth { get; set; }
public uint ScreenSaverTimeoutMin { get; set; }
public uint ScreenSaverMovePositionIntervalMs { get; set; }
[JsonProperty("includeInFusionRoomHealth", NullValueHandling = NullValueHandling.Ignore)]
public bool? IncludeInFusionRoomHealth { get; set; }
[JsonProperty("screenSaverTimeoutMin", NullValueHandling = NullValueHandling.Ignore)]
public uint? ScreenSaverTimeoutMin { get; set; }
[JsonProperty("screenSaverMovePositionIntervalMs", NullValueHandling = NullValueHandling.Ignore)]
public uint? ScreenSaverMovePositionIntervalMs { get; set; }
/// <summary>
/// The count of sources that will trigger the "additional" arrows to show on the SRL.
/// Defaults to 5
/// </summary>
public int SourcesOverflowCount { get; set; }
[JsonProperty("sourcesOverflowCount", NullValueHandling = NullValueHandling.Ignore)]
public int? SourcesOverflowCount { get; set; }
public CrestronTouchpanelPropertiesConfig()
public CrestronTouchpanelPropertiesConfig() : this(false) { }
public CrestronTouchpanelPropertiesConfig(bool setDefaultValues = false)
{
if(!setDefaultValues) { return; }
SourcesOverflowCount = 5;
HeaderStyle = CrestronTouchpanelPropertiesConfig.Habanero;
HeaderStyle = Habanero;
// Default values
ScreenSaverTimeoutMin = 5;
ScreenSaverMovePositionIntervalMs = 15000;
}
}
/// <summary>
/// "habanero"
@@ -49,6 +85,7 @@
/// </summary>
public class UiSetupPropertiesConfig
{
[JsonProperty("isVisible", NullValueHandling = NullValueHandling.Ignore)]
public bool IsVisible { get; set; }
}
}

View File

@@ -89,7 +89,7 @@ namespace PepperDash.Essentials.Core.Web
Name = "DevList",
RouteHandler = new DevListRequestHandler()
},
new HttpCwsRoute("deviceCommands")
new HttpCwsRoute("deviceCommands/{deviceKey}")
{
Name = "DevJson",
RouteHandler = new DevJsonRequestHandler()

View File

@@ -7,11 +7,11 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core.Web
{
public class EssentialsWebApiHelpers
public static class EssentialsWebApiHelpers
{
public static string GetRequestBody(HttpCwsRequest request)
public static string GetRequestBody(this HttpCwsRequest request)
{
var bytes = new Byte[request.ContentLength];
var bytes = new byte[request.ContentLength];
request.InputStream.Read(bytes, 0, request.ContentLength);
@@ -22,8 +22,8 @@ namespace PepperDash.Essentials.Core.Web
{
return new
{
Name = assembly.Name,
Version = assembly.Version
assembly.Name,
assembly.Version
};
}
@@ -31,7 +31,7 @@ namespace PepperDash.Essentials.Core.Web
{
return new
{
Key = device.Key,
device.Key,
Name = (device is IKeyName)
? (device as IKeyName).Name
: "---"
@@ -80,7 +80,7 @@ namespace PepperDash.Essentials.Core.Web
{
Type = device.Key,
Description = device.Value.Description,
CType = device.Value.CType == null ? "---": device.Value.CType.ToString()
CType = device.Value.Type == null ? "---": device.Value.Type.ToString()
};
}
}

View File

@@ -52,7 +52,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
var data = context.Request.GetRequestBody();
if (string.IsNullOrEmpty(data))
{
context.Response.StatusCode = 400;

View File

@@ -26,6 +26,26 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
/// <param name="context"></param>
protected override void HandlePost(HttpCwsContext context)
{
var routeData = context.Request.RouteData;
if(routeData == null)
{
context.Response.StatusCode = 400;
context.Response.StatusDescription = "Bad Request";
context.Response.End();
return;
}
if(!routeData.Values.TryGetValue("deviceKey", out var deviceKey))
{
context.Response.StatusCode = 400;
context.Response.StatusDescription = "Bad Request";
context.Response.End();
return;
}
if (context.Request.ContentLength < 0)
{
context.Response.StatusCode = 400;
@@ -35,7 +55,8 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
var data = context.Request.GetRequestBody();
if (string.IsNullOrEmpty(data))
{
context.Response.StatusCode = 400;
@@ -46,9 +67,14 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
}
try
{
var daw = JsonConvert.DeserializeObject<DeviceActionWrapper>(data);
DeviceJsonApi.DoDeviceActionWithJson(data);
{
var daw = new DeviceActionWrapper { DeviceKey = (string) deviceKey};
JsonConvert.PopulateObject(data, daw);
Debug.LogMessage(LogEventLevel.Verbose, "Device Action Wrapper: {@wrapper}", null, daw);
DeviceJsonApi.DoDeviceAction(daw);
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";

View File

@@ -34,7 +34,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
allDevices.Sort((a, b) => System.String.Compare(a.Key, b.Key, System.StringComparison.Ordinal));
allDevices.Sort((a, b) => string.Compare(a.Key, b.Key, System.StringComparison.Ordinal));
var deviceList = allDevices.Select(d => EssentialsWebApiHelpers.MapToDeviceListObject(d)).ToList();

View File

@@ -52,7 +52,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
var data = context.Request.GetRequestBody();
if (string.IsNullOrEmpty(data))
{
context.Response.StatusCode = 400;

View File

@@ -1,25 +1,24 @@
using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
using PepperDash.Core.Web.RequestHandlers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Web.RequestHandlers
{
public class GetTieLinesRequestHandler:WebApiBaseRequestHandler
public class GetTieLinesRequestHandler : WebApiBaseRequestHandler
{
public GetTieLinesRequestHandler() : base(true) { }
protected override void HandleGet(HttpCwsContext context)
{
var tieLineString = JsonConvert.SerializeObject(TieLineCollection.Default.Select((tl) => new {
var tieLineString = JsonConvert.SerializeObject(TieLineCollection.Default.Select((tl) => new
{
sourceKey = tl.SourcePort.ParentDevice.Key,
sourcePort = tl.SourcePort.Key,
destinationKey = tl.DestinationPort.ParentDevice.Key,
destinationPort = tl.DestinationPort.Key
destinationPort = tl.DestinationPort.Key,
type = tl.Type.ToString(),
}));
context.Response.StatusCode = 200;

View File

@@ -89,7 +89,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
var data = context.Request.GetRequestBody();
if (data == null)
{
context.Response.StatusCode = 500;

View File

@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials.Core;

View File

@@ -12,7 +12,7 @@ using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Devices.Common.Codec;
using System.Text.RegularExpressions;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Newtonsoft.Json;
using Serilog.Events;

View File

@@ -0,0 +1,31 @@
using Crestron.SimplSharpPro;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Describes a cisco codec device that can allow configuration of cameras
/// </summary>
public interface ICiscoCodecCameraConfig
{
void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber);
void SetCameraName(uint videoConnectorId, string name);
void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType);
}
public enum eCiscoCodecInputSourceType
{
PC,
camera,
document_camera,
mediaplayer,
other,
whiteboard
}
}

View File

@@ -2,7 +2,7 @@
using System;
using System.Linq;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using Serilog.Events;
@@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Devices.Common
{
try
{
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
var factory = (IDeviceFactory)Activator.CreateInstance(type);
factory.LoadTypeFactories();
}
catch (Exception e)

View File

@@ -29,6 +29,8 @@ namespace PepperDash.Essentials.Devices.Common.Displays
protected set
{
if (_currentInputPort == value) return;
_currentInputPort = value;
InputChanged?.Invoke(this, _currentInputPort);

View File

@@ -12,7 +12,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Displays
{
public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced, IHasInputs<string, string>
public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced, IHasInputs<string, string>, IRoutingSinkWithSwitchingWithInputPort, IHasPowerControlWithFeedback
{
public ISelectableItems<string> Inputs { get; private set; }
@@ -79,7 +79,7 @@ namespace PepperDash.Essentials.Devices.Common.Displays
eRoutingPortConnectionType.Hdmi, "HDMI2", this);
var hdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI3", this);
var hdmiIn4 = new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.AudioVideo,
var hdmiIn4 = new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI4", this);
var dpIn = new RoutingInputPort(RoutingPortNames.DisplayPortIn, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.DisplayPort, "DP", this);
@@ -120,7 +120,7 @@ namespace PepperDash.Essentials.Devices.Common.Displays
// Fake cool-down cycle
CooldownTimer = new CTimer(o =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "Cooldown timer ending");
Debug.LogMessage(LogEventLevel.Verbose, "Cooldown timer ending", this);
_IsCoolingDown = false;
IsCoolingDownFeedback.InvokeFireUpdate();
_PowerIsOn = false;
@@ -143,10 +143,10 @@ namespace PepperDash.Essentials.Devices.Common.Displays
{
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteSwitch: {0}", this, selector);
if (!_PowerIsOn)
{
PowerOn();
}
if (!_PowerIsOn)
{
PowerOn();
}
if (!Inputs.Items.TryGetValue(selector.ToString(), out var input))
return;
@@ -200,7 +200,6 @@ namespace PepperDash.Essentials.Devices.Common.Displays
}
#region IBasicVolumeWithFeedback Members
public IntFeedback VolumeLevelFeedback { get; private set; }
@@ -270,21 +269,19 @@ namespace PepperDash.Essentials.Devices.Common.Displays
{
LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);
}
}
public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>
{
public MockDisplayFactory()
{
TypeNames = new List<string>() { "mockdisplay2" };
}
public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>
{
public MockDisplayFactory()
{
TypeNames = new List<string>() { "mockdisplay" , "mockdisplay2" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device");
return new MockDisplay(dc.Key, dc.Name);
}
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device");
return new MockDisplay(dc.Key, dc.Name);
}
}
}

View File

@@ -54,8 +54,7 @@ namespace PepperDash.Essentials.Devices.Common.Displays
public class MockDisplayInput : ISelectableItem
{
private IHasInputs<string, string> _parent;
private MockDisplay _parent;
private bool _isSelected;
@@ -91,7 +90,12 @@ namespace PepperDash.Essentials.Devices.Common.Displays
public void Select()
{
_parent.SetInput(Key);
if (!_parent.PowerIsOnFeedback.BoolValue) _parent.PowerOn();
foreach(var input in _parent.Inputs.Items)
{
input.Value.IsSelected = input.Key == this.Key;
}
}
}
}

View File

@@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.CrestronIO;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Devices.Common;
using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Shades
{
/// <summary>
/// Controls a single shade using three relays
/// </summary>
public class ScreenLiftController : EssentialsDevice, IProjectorScreenLiftControl
{
readonly ScreenLiftControllerConfigProperties Config;
readonly ScreenLiftRelaysConfig RaiseRelayConfig;
readonly ScreenLiftRelaysConfig LowerRelayConfig;
readonly ScreenLiftRelaysConfig LatchedRelayConfig;
Displays.DisplayBase DisplayDevice;
ISwitchedOutput RaiseRelay;
ISwitchedOutput LowerRelay;
ISwitchedOutput LatchedRelay;
public bool InUpPosition
{
get { return _isInUpPosition; }
set
{
if (value == _isInUpPosition) return;
_isInUpPosition = value;
IsInUpPosition.FireUpdate();
PositionChanged?.Invoke(this, new EventArgs());
}
}
private bool _isInUpPosition { get; set; }
public eScreenLiftControlType Type { get; private set; }
public eScreenLiftControlMode Mode { get; private set; }
public string DisplayDeviceKey { get; private set; }
public BoolFeedback IsInUpPosition { get; private set; }
public event EventHandler<EventArgs> PositionChanged;
public ScreenLiftController(string key, string name, ScreenLiftControllerConfigProperties config)
: base(key, name)
{
Config = config;
DisplayDeviceKey = Config.DisplayDeviceKey;
Mode = Config.Mode;
Type = Config.Type;
IsInUpPosition = new BoolFeedback(() => _isInUpPosition);
switch (Mode)
{
case eScreenLiftControlMode.momentary:
{
RaiseRelayConfig = Config.Relays["raise"];
LowerRelayConfig = Config.Relays["lower"];
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelayConfig = Config.Relays["latched"];
break;
}
}
}
private void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
if (!DisplayDevice.IsCoolingDownFeedback.BoolValue && Type == eScreenLiftControlType.lift)
{
Raise();
return;
}
if (DisplayDevice.IsCoolingDownFeedback.BoolValue && Type == eScreenLiftControlType.screen)
{
Raise();
return;
}
}
private void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
if (DisplayDevice.IsWarmingUpFeedback.BoolValue)
{
Lower();
}
}
public override bool CustomActivate()
{
//Create ISwitchedOutput objects based on props
switch (Mode)
{
case eScreenLiftControlMode.momentary:
{
Debug.LogMessage(LogEventLevel.Debug, this, $"Getting relays for {Mode}");
RaiseRelay = GetSwitchedOutputFromDevice(RaiseRelayConfig.DeviceKey);
LowerRelay = GetSwitchedOutputFromDevice(LowerRelayConfig.DeviceKey);
break;
}
case eScreenLiftControlMode.latched:
{
Debug.LogMessage(LogEventLevel.Debug, this, $"Getting relays for {Mode}");
LatchedRelay = GetSwitchedOutputFromDevice(LatchedRelayConfig.DeviceKey);
break;
}
}
Debug.LogMessage(LogEventLevel.Debug, this, $"Getting display with key {DisplayDeviceKey}");
DisplayDevice = GetDisplayBaseFromDevice(DisplayDeviceKey);
if (DisplayDevice != null)
{
Debug.LogMessage(LogEventLevel.Debug, this, $"Subscribing to {DisplayDeviceKey} feedbacks");
DisplayDevice.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;
DisplayDevice.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange;
}
return base.CustomActivate();
}
public void Raise()
{
if (RaiseRelay == null && LatchedRelay == null) return;
Debug.LogMessage(LogEventLevel.Debug, this, $"Raising {Type}");
switch (Mode)
{
case eScreenLiftControlMode.momentary:
{
PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.Off();
break;
}
}
InUpPosition = true;
}
public void Lower()
{
if (LowerRelay == null && LatchedRelay == null) return;
Debug.LogMessage(LogEventLevel.Debug, this, $"Lowering {Type}");
switch (Mode)
{
case eScreenLiftControlMode.momentary:
{
PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);
break;
}
case eScreenLiftControlMode.latched:
{
LatchedRelay.On();
break;
}
}
InUpPosition = false;
}
void PulseOutput(ISwitchedOutput output, int pulseTime)
{
output.On();
CTimer pulseTimer = new CTimer(new CTimerCallbackFunction((o) => output.Off()), pulseTime);
}
/// <summary>
/// Attempts to get the port on teh specified device from config
/// </summary>
/// <param name="relayConfig"></param>
/// <returns></returns>
ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey)
{
var portDevice = DeviceManager.GetDeviceForKey(relayKey);
if (portDevice != null)
{
return (portDevice as ISwitchedOutput);
}
else
{
Debug.LogMessage(LogEventLevel.Debug, this, "Error: Unable to get relay device with key '{0}'", relayKey);
return null;
}
}
Displays.DisplayBase GetDisplayBaseFromDevice(string displayKey)
{
var displayDevice = DeviceManager.GetDeviceForKey(displayKey);
if (displayDevice != null)
{
return displayDevice as Displays.DisplayBase;
}
else
{
Debug.LogMessage(LogEventLevel.Debug, this, "Error: Unable to get display device with key '{0}'", displayKey);
return null;
}
}
}
public class ScreenLiftControllerConfigProperties
{
[JsonProperty("displayDeviceKey")]
public string DisplayDeviceKey { get; set; }
[JsonProperty("type")]
[JsonConverter(typeof(StringEnumConverter))]
public eScreenLiftControlType Type { get; set; }
[JsonProperty("mode")]
[JsonConverter(typeof(StringEnumConverter))]
public eScreenLiftControlMode Mode { get; set; }
[JsonProperty("relays")]
public Dictionary<string,ScreenLiftRelaysConfig> Relays { get; set; }
}
public class ScreenLiftRelaysConfig
{
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
[JsonProperty("pulseTimeInMs")]
public int PulseTimeInMs { get; set; }
}
public class ScreenLiftControllerFactory : EssentialsDeviceFactory<RelayControlledShade>
{
public ScreenLiftControllerFactory()
{
TypeNames = new List<string>() { "screenliftcontroller" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Comm Device");
var props = Newtonsoft.Json.JsonConvert.DeserializeObject<ScreenLiftControllerConfigProperties>(dc.Properties.ToString());
return new ScreenLiftController(dc.Key, dc.Name, props);
}
}
public enum eScreenLiftControlMode
{
momentary,
latched
}
}

View File

@@ -13,7 +13,7 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common
{
public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingSource, IRoutingOutputs, IUsageTracking
public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingSource, IUsageTracking
{
public uint DisplayUiType { get { return DisplayUiConstants.TypeNoControls; } }

View File

@@ -12,7 +12,9 @@
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>PepperDash Essentials Devices Common</Title>
<PackageId>PepperDash.Essentials.Devices.Common</PackageId>
<Version>2.0.0-local</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
@@ -28,6 +30,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-beta-418" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
</Project>

View File

@@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
@@ -42,7 +42,7 @@ namespace PepperDash.Essentials.Devices.Common
public void PrintExpectedIrCommands()
{
var cmds = typeof (AppleTvIrCommands).GetCType().GetFields(BindingFlags.Public | BindingFlags.Static);
var cmds = typeof (AppleTvIrCommands).GetType().GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var value in cmds.Select(cmd => cmd.GetValue(null)).OfType<string>())
{

View File

@@ -1214,7 +1214,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
var entryIndex = counterIndex;
Debug.LogMessage(LogEventLevel.Verbose, this, "Entry{2:0000} Name: {0}, Folder ID: {1}, Type: {3}, ParentFolderId: {4}",
entry.Name, entry.FolderId, entryIndex, entry.GetType().GetCType().FullName, entry.ParentFolderId);
entry.Name, entry.FolderId, entryIndex, entry.GetType().GetType().FullName, entry.ParentFolderId);
if (entry is DirectoryFolder)
{

View File

@@ -1,7 +1,7 @@
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.CrestronThread;
using Crestron.SimplSharpPro.Diagnostics;
@@ -177,11 +177,7 @@ namespace PepperDash.Essentials
directoryPrefix = Directory.GetApplicationRootDirectory();
var fullVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Global.SetAssemblyVersion(fullVersion);
//Global.SetAssemblyVersion(fullVersionAtt.InformationalVersion);
Global.SetAssemblyVersion(PluginLoader.GetAssemblyVersion(Assembly.GetExecutingAssembly()));
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows CE OS
{

View File

@@ -61,7 +61,7 @@
"supportedSystemTypes": [
"hudType",
"presType",
"vtcType",
"vtType",
"custom"
],
"type": "rmc3",

View File

@@ -6,7 +6,7 @@ using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Crestron.SimplSharp.Reflection;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -35,7 +35,7 @@ namespace PepperDash.Essentials
{
try
{
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
var factory = (IDeviceFactory)Activator.CreateInstance(type);
factory.LoadTypeFactories();
}
catch (Exception e)

View File

@@ -11,7 +11,9 @@
<OutputPath>bin\$(Configuration)\</OutputPath>
<Title>PepperDash Essentials</Title>
<PackageId>PepperDashEssentials</PackageId>
<Version>2.0.0-local</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
@@ -47,7 +49,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-beta-418" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />