Compare commits

...

57 Commits

Author SHA1 Message Date
Andrew Welker
332faaa9cc fix: joins in join maps get added correctly to a bridge
When Essentials moved to using `System.Reflection` instead of the Crestron classes, there were some leftover `GetType` calls that were no longer necessary. These extra calls were preventing things from getting the correct type.

Join Map printing was also fixed to print out in an actual readable fashion.
2024-10-30 13:18:36 -05:00
Andrew Welker
1a11e9019c Merge pull request #1201 from PepperDash/feature-2.0.0/routing-feedback-manager-nullref
Feature 2.0.0/routing feedback manager nullref
2024-10-25 08:33:46 -05:00
Nick Genovese
0e16dff90c fix: checks the routing output port for null 2024-10-24 20:52:24 -04:00
Andrew Welker
d11827bc7b fix: remove verbose logging for feedback manager 2024-10-18 15:04:08 -05:00
Andrew Welker
631dd2b00d fix: check for nulls in SwitchingDevice property 2024-10-18 11:09:15 -05:00
Andrew Welker
639cd2abfb Merge pull request #1197 from PepperDash/feature-2.0.0/add-cpz-to-builds
Feature 2.0.0/add cpz to builds
2024-10-18 10:46:05 -05:00
Andrew Welker
f04f70495f fix: check for nulls in route switch descriptors 2024-10-18 10:43:53 -05:00
Neil Dorin
fa38e8a9a8 feat: Adds mechanism to track initialization status of EssentialsDevice as well as an event on DeviceManager to notify when all devices initialized. Room combiner now waits for all initialize before setting current scenario. 2024-10-04 10:33:09 -06:00
Neil Dorin
f351c036ed Merge branch 'feature-2.0.0/temp-humidity-interfaces' into feature-2.0.0/generic-sink-fix 2024-09-26 15:04:34 -06:00
Neil Dorin
0a7da79356 fix: Allows both BytesReceived and TextReceived to both fire on the ComPortController 2024-09-26 15:03:31 -06:00
Andrew Welker
82ebf45921 fix: use correct class for generic sink factory 2024-09-26 08:44:27 -05:00
Neil Dorin
d0dbe986f3 feat: Adds ITemperatureSensor and IHumiditySensor interfaces 2024-09-13 13:17:51 -06:00
Neil Dorin
aa503f3b29 feat: Allows for the ItemKey to be left undefined or empty and the ParentDeviceKey to be used on it's own instead. 2024-08-28 13:16:27 -06:00
Neil Dorin
90251d92df fix: adds condition to handle legacy and current portal URL structures and adds null check for getting list types in basic config helper methods 2024-08-26 12:47:31 -06:00
jtalborough
778af651d0 feature: adds pack for release builds 2024-08-05 10:40:33 -04:00
jtalborough
2e31719a84 fix: artifact path 2024-08-05 10:39:04 -04:00
jtalborough
a96fb21898 feature: add dotnet pack 2024-08-05 09:52:57 -04:00
jtalborough
3f45372e1e feature: add cpz test 2024-08-02 15:55:38 -04:00
jtalborough
60dbaf547d fix: artifact path 2024-08-02 11:00:07 -04:00
Andrew Welker
941e537d53 Merge pull request #1196 from PepperDash/feature-2.0.0/routing-cooldown-fixes
Feature 2.0.0/routing cooldown fixes
2024-08-01 15:36:36 -04:00
Andrew Welker
f7c5e18af8 fix: add input port matching to route descriptors 2024-07-26 11:19:43 -05:00
Andrew Welker
ab73bbf979 fix: remove call to remove routerequest
The routerequest is being removed if the route is successfully made after the display has cooled down. This was an extraneous removal
2024-07-26 06:53:18 -05:00
Andrew Welker
1fb1947158 fix: ReleaseRoute callse ExecuteSwitch with null for input selector
Most devices that implement IRouting will now need to handle the possiblity of a null as the input selector, with the idea being that a null input selector should clear the route to whatever device is selected as the input selector. This may change to a typed value in the future.
2024-07-26 06:51:45 -05:00
Andrew Welker
e374f7b50f fix: add some options to destination type enum 2024-07-26 06:48:23 -05:00
Andrew Welker
7a263a644a fix: devcommstatus response now prints correctly 2024-07-26 06:47:48 -05:00
Andrew Welker
97bd30e9c9 fix: add async/await patterns for activation/deactivation 2024-07-26 06:47:13 -05:00
Andrew Welker
c56841d95b fix: attempt to catch a null ref happening in the Route Descriptor 2024-07-22 11:19:34 -05:00
Andrew Welker
64d6df70b0 fix: deactivate all rooms on startup
As part of the Essentials startup process, ALL rooms are activated, meaning there are unnecessary rooms activated. Deactiving them all prior to determining a combination scenario helps keep unecessary activity from happening.
2024-07-22 11:19:08 -05:00
Andrew Welker
d970d806c9 fix: add mutex to prevent multiple scenarios from running at once 2024-07-19 13:09:47 -05:00
Andrew Welker
5a9b876d80 fix: remove route request once the request has been handled 2024-07-18 13:46:40 -05:00
Andrew Welker
fb60683af6 feat: add async method for devjson 2024-07-18 13:44:49 -05:00
Andrew Welker
b63996b9e6 fix: set partition state to match physical partitions when changing from manual to auto mode 2024-07-08 13:22:18 -05:00
Andrew Welker
bc217a2008 chore: add logging for current scenario searching 2024-07-08 08:44:45 -05:00
Andrew Welker
fec6b0d385 fix: change source for PartitionPresent depending on mode
Added `FireUpdate` calls for feedback on mode change in order to get correct values when changing modes.
2024-07-08 08:41:03 -05:00
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
41 changed files with 1757 additions and 1219 deletions

View File

@@ -66,6 +66,8 @@ jobs:
# Build the solutions in the docker image
- name: Build Solution
run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" -m
- name: Pack Solution
run: dotnet pack .\$($Env:SOLUTION_FILE).sln --configuration $env:BUILD_TYPE --output ./output /p:Version="${{ steps.setVersion.outputs.version }}"
- name: Create tag for non-rc builds
if: contains(steps.setVersion.outputs.version, 'alpha')
run: |

View File

@@ -37,6 +37,8 @@ jobs:
run: nuget restore .\$($Env:SOLUTION_FILE).sln
- name: Build Solution
run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" -m
- name: Pack Solution
run: dotnet pack .\$($Env:SOLUTION_FILE).sln --configuration $env:BUILD_TYPE --output ./output /p:Version="${{ steps.setVersion.outputs.version }}"
- name: Upload Release
id: create_release
uses: ncipollo/release-action@v1

View File

@@ -18,81 +18,6 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Helper methods for bridges
/// </summary>
public static class BridgeHelper
{
public static void PrintJoinMap(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;
if (bridge == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.PrintJoinMapForDevice(deviceKey);
}
else
{
bridge.PrintJoinMaps();
}
}
public static void JoinmapMarkdown(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;
if (bridge == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.MarkdownJoinMapForDevice(deviceKey, bridgeKey);
}
else
{
bridge.MarkdownForBridge(bridgeKey);
}
}
}
/// <summary>
/// Base class for all bridge class variants
/// </summary>
public class BridgeBase : EssentialsDevice
{
public BridgeApi Api { get; protected set; }
public BridgeBase(string key) :
base(key)
{
}
}
/// <summary>
/// Base class for bridge API variants
/// </summary>
@@ -168,19 +93,15 @@ namespace PepperDash.Essentials.Core.Bridges
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetType()))
if (device is IBridgeAdvanced bridge)
{
Debug.LogMessage(LogEventLevel.Information, this,
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
device.Key);
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
continue;
}
var bridge = device as IBridgeAdvanced;
if (bridge != null)
{
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
}
Debug.LogMessage(LogEventLevel.Information, this,
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
device.Key);
}
}
@@ -249,11 +170,11 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public virtual void PrintJoinMaps()
{
Debug.LogMessage(LogEventLevel.Information, this, "Join Maps for EISC IPID: {0}", Eisc.ID.ToString("X"));
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
foreach (var joinMap in JoinMaps)
{
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}':", joinMap.Key);
CrestronConsole.ConsoleCommandResponse("Join map for device '{0}':", joinMap.Key);
joinMap.Value.PrintJoinMapInfo();
}
}

View File

@@ -0,0 +1,66 @@
using PepperDash.Core;
using Serilog.Events;
//using PepperDash.Essentials.Devices.Common.Cameras;
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Helper methods for bridges
/// </summary>
public static class BridgeHelper
{
public static void PrintJoinMap(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
if (!(DeviceManager.GetDeviceForKey(bridgeKey) is EiscApiAdvanced bridge))
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.PrintJoinMapForDevice(deviceKey);
}
else
{
bridge.PrintJoinMaps();
}
}
public static void JoinmapMarkdown(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;
if (bridge == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.MarkdownJoinMapForDevice(deviceKey, bridgeKey);
}
else
{
bridge.MarkdownForBridge(bridgeKey);
}
}
}
}

View File

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

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
{
@@ -28,6 +29,9 @@ namespace PepperDash.Essentials.Core.Config
[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; }
@@ -41,6 +45,7 @@ namespace PepperDash.Essentials.Core.Config
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>();
}
@@ -50,7 +55,7 @@ namespace PepperDash.Essentials.Core.Config
/// </summary>
public Dictionary<string, SourceListItem> GetSourceListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
if (SourceLists == null || string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
return null;
return SourceLists[key];
@@ -63,7 +68,7 @@ namespace PepperDash.Essentials.Core.Config
/// <returns>DestinationList if the key exists, null otherwise</returns>
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
if (DestinationLists == null || string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
{
return null;
}
@@ -78,12 +83,23 @@ namespace PepperDash.Essentials.Core.Config
/// <returns>AudioControlPointList if the key exists, null otherwise</returns>
public AudioControlPointListItem GetAudioControlPointListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))
if (AudioControlPointLists == null || 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 (CameraLists == null || 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
/// </summary>

View File

@@ -31,9 +31,17 @@ namespace PepperDash.Essentials.Core.Config
if (string.IsNullOrEmpty(SystemUrl))
return "missing url";
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
return uuid;
if (SystemUrl.Contains("#"))
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
string uuid = result.Groups[1].Value;
return uuid;
}
}
}
@@ -44,10 +52,18 @@ namespace PepperDash.Essentials.Core.Config
{
if (string.IsNullOrEmpty(TemplateUrl))
return "missing template url";
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
return uuid;
if (TemplateUrl.Contains("#"))
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
string uuid = result.Groups[2].Value;
return uuid;
}
}
}

View File

@@ -15,8 +15,22 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
/// <example>
/// See MockDisplay for example implemntation
/// </example>
[Obsolete("Use IHasInputs<T> instead. Will be removed for 2.0 release")]
public interface IHasInputs<T, TSelector>: IKeyName
{
ISelectableItems<T> Inputs { get; }
}
/// <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

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface IHumiditySensor
{
/// <summary>
/// Reports the relative humidity level. Level ranging from 0 to 100 (for 0% to 100%
/// RH). EventIds: HumidityFeedbackFeedbackEventId will trigger to indicate change.
/// </summary>
IntFeedback HumidityFeedback { get; }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface ITemperatureSensor
{
/// <summary>
/// The values will range from -400 to +1760 (for -40° to +176° F) or -400 to +800
/// (for -40° to +80° C)in tenths of a degree.
/// </summary>
IntFeedback TemperatureFeedback { get; }
BoolFeedback TemperatureInCFeedback { get; }
void SetTemperatureFormat(bool setToC);
}
}

View File

@@ -9,9 +9,15 @@ namespace PepperDash.Essentials.Core
{
public abstract class AudioControlListItemBase
{
/// <summary>
/// Key of the parent device in the DeviceManager
/// </summary>
[JsonProperty("parentDeviceKey")]
public string ParentDeviceKey { get; set; }
/// <summary>
/// Optional key of the item in the parent device
/// </summary>
[JsonProperty("itemKey")]
public string ItemKey { 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

@@ -1,133 +1,198 @@
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
using Serilog.Events;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Crestron.SimplSharp;
using System.Reflection;
using Newtonsoft.Json;
using PepperDash.Core;
using Serilog.Events;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
public class DeviceJsonApi
{
/// <summary>
///
/// </summary>
/// <param name="json"></param>
public static void DoDeviceActionWithJson(string json)
{
if (String.IsNullOrEmpty(json))
{
CrestronConsole.ConsoleCommandResponse(
"Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted.");
return;
}
try
{
var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json);
public class DeviceJsonApi
{
/// <summary>
///
/// </summary>
/// <param name="json"></param>
public static void DoDeviceActionWithJson(string json)
{
if (String.IsNullOrEmpty(json))
{
CrestronConsole.ConsoleCommandResponse(
"Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted.");
return;
}
try
{
var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json);
DoDeviceAction(action);
}
catch (Exception ex)
{
CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}");
}
}
DoDeviceAction(action);
}
catch (Exception)
{
CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}");
}
}
/// <summary>
///
/// </summary>
/// <param name="action"></param>
public static void DoDeviceAction(DeviceActionWrapper action)
{
var key = action.DeviceKey;
var obj = FindObjectOnPath(key);
if (obj == null)
{
CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key);
return;
}
/// <summary>
///
/// </summary>
/// <param name="action"></param>
public static void DoDeviceAction(DeviceActionWrapper action)
{
var key = action.DeviceKey;
var obj = FindObjectOnPath(key);
if (obj == null)
{
CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key);
return;
}
if (action.Params == null)
{
if (action.Params == null)
{
//no params, so setting action.Params to empty array
action.Params = new object[0];
}
action.Params = new object[0];
}
Type t = obj.GetType();
try
{
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
Type t = obj.GetType();
try
{
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);
var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);
if (method == null)
{
CrestronConsole.ConsoleCommandResponse(
"Unable to find method with name {0} and that matches parameters {1}", action.MethodName,
action.Params);
return;
}
if (method == null)
{
CrestronConsole.ConsoleCommandResponse(
"Unable to find method with name {0} and that matches parameters {1}", action.MethodName,
action.Params);
return;
}
var mParams = method.GetParameters();
var convertedParams = mParams
.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);
}
catch (Exception ex)
{
CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName,
ex.Message);}
}
CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name,
action.DeviceKey);
}
catch (Exception ex)
{
CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName,
ex.Message);
}
}
private static object ConvertType(object value, Type conversionType)
{
if (!conversionType.IsEnum)
{
return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
}
public static async Task DoDeviceActionAsync(DeviceActionWrapper action)
{
var key = action.DeviceKey;
var obj = FindObjectOnPath(key);
if (obj == null)
{
Debug.LogMessage(LogEventLevel.Warning, "Unable to find object at path {deviceKey}", null, key);
return;
}
var stringValue = Convert.ToString(value);
if (action.Params == null)
{
//no params, so setting action.Params to empty array
action.Params = new object[0];
}
if (String.IsNullOrEmpty(stringValue))
{
throw new InvalidCastException(
String.Format("{0} cannot be converted to a string prior to conversion to enum"));
}
return Enum.Parse(conversionType, stringValue, true);
}
Type t = obj.GetType();
try
{
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
/// <summary>
/// Gets the properties on a device
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetProperties(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);
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);
}
if (method == null)
{
Debug.LogMessage(LogEventLevel.Warning,
"Unable to find method with name {methodName} and that matches parameters {@parameters}", null, action.MethodName,
action.Params);
return;
}
var mParams = method.GetParameters();
var convertedParams = mParams
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
.ToArray();
await Task.Run(() =>
{
try
{
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey} with {@params}", null, method.Name, action.DeviceKey, action.Params);
method.Invoke(obj, convertedParams);
}
catch (Exception e)
{
Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
}
});
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Unable to call method with name {methodName} with {@parameters}", null, action.MethodName, action.Params);
}
}
private static object ConvertType(object value, Type conversionType)
{
if (!conversionType.IsEnum)
{
return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
}
var stringValue = Convert.ToString(value);
if (String.IsNullOrEmpty(stringValue))
{
throw new InvalidCastException(
String.Format("{0} cannot be converted to a string prior to conversion to enum"));
}
return Enum.Parse(conversionType, stringValue, true);
}
/// <summary>
/// Gets the properties on a device
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetProperties(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
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);
}
/// <summary>
/// Gets a property from a device path by name
@@ -138,10 +203,10 @@ namespace PepperDash.Essentials.Core
public static object GetPropertyByName(string deviceObjectPath, string propertyName)
{
var dev = FindObjectOnPath(deviceObjectPath);
if(dev == null)
if (dev == null)
return "{ \"error\":\"No Device\"}";
object prop = dev.GetType().GetType().GetProperty(propertyName).GetValue(dev, null);
object prop = dev.GetType().GetProperty(propertyName).GetValue(dev, null);
// var prop = t.GetProperty(propertyName);
if (prop != null)
@@ -155,126 +220,126 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Gets the methods on a device
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetMethods(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
/// <summary>
/// Gets the methods on a device
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetMethods(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
// Package up method names using helper objects
Type t = obj.GetType();
var methods = t.GetMethods()
.Where(m => !m.IsSpecialName)
.Select(p => new MethodNameParams(p));
return JsonConvert.SerializeObject(methods, Formatting.Indented);
}
// Package up method names using helper objects
Type t = obj.GetType();
var methods = t.GetMethods()
.Where(m => !m.IsSpecialName)
.Select(p => new MethodNameParams(p));
return JsonConvert.SerializeObject(methods, Formatting.Indented);
}
public static string GetApiMethods(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
public static string GetApiMethods(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
// Package up method names using helper objects
Type t = obj.GetType();
var methods = t.GetMethods()
.Where(m => !m.IsSpecialName)
.Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any())
.Select(p => new MethodNameParams(p));
return JsonConvert.SerializeObject(methods, Formatting.Indented);
}
// Package up method names using helper objects
Type t = obj.GetType();
var methods = t.GetMethods()
.Where(m => !m.IsSpecialName)
.Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any())
.Select(p => new MethodNameParams(p));
return JsonConvert.SerializeObject(methods, Formatting.Indented);
}
/// <summary>
/// Walks down a dotted object path, starting with a Device, and returns the object
/// at the end of the path
/// </summary>
public static object FindObjectOnPath(string deviceObjectPath)
{
var path = deviceObjectPath.Split('.');
var dev = DeviceManager.GetDeviceForKey(path[0]);
if (dev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]);
return null;
}
/// <summary>
/// Walks down a dotted object path, starting with a Device, and returns the object
/// at the end of the path
/// </summary>
public static object FindObjectOnPath(string deviceObjectPath)
{
var path = deviceObjectPath.Split('.');
// loop through any dotted properties
object obj = dev;
if (path.Length > 1)
{
for (int i = 1; i < path.Length; i++)
{
var objName = path[i];
string indexStr = null;
var indexOpen = objName.IndexOf('[');
if (indexOpen != -1)
{
var indexClose = objName.IndexOf(']');
if (indexClose == -1)
{
Debug.LogMessage(LogEventLevel.Information, dev, "ERROR Unmatched index brackets");
return null;
}
// Get the index and strip quotes if any
indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace("\"", "");
objName = objName.Substring(0, indexOpen);
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
}
var dev = DeviceManager.GetDeviceForKey(path[0]);
if (dev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]);
return null;
}
Type oType = obj.GetType();
var prop = oType.GetProperty(objName);
if (prop == null)
{
Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} not found on {1}", objName, path[i - 1]);
return null;
}
// if there's an index, try to get the property
if (indexStr != null)
{
if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType))
{
Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} is not collection", objName);
return null;
}
var collection = prop.GetValue(obj, null) as ICollection;
// Get the indexed items "property"
var indexedPropInfo = prop.PropertyType.GetProperty("Item");
// These are the parameters for the indexing. Only care about one
var indexParams = indexedPropInfo.GetIndexParameters();
if (indexParams.Length > 0)
{
Debug.LogMessage(LogEventLevel.Information, " Indexed, param type: {0}", indexParams[0].ParameterType.Name);
var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType,
System.Globalization.CultureInfo.InvariantCulture);
try
{
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
}
// if the index is bad, catch it here.
catch (TargetInvocationException e)
{
if (e.InnerException is ArgumentOutOfRangeException)
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
else if (e.InnerException is KeyNotFoundException)
Debug.LogMessage(LogEventLevel.Information, " Key not found");
return null;
}
}
// loop through any dotted properties
object obj = dev;
if (path.Length > 1)
{
for (int i = 1; i < path.Length; i++)
{
var objName = path[i];
string indexStr = null;
var indexOpen = objName.IndexOf('[');
if (indexOpen != -1)
{
var indexClose = objName.IndexOf(']');
if (indexClose == -1)
{
Debug.LogMessage(LogEventLevel.Information, dev, "ERROR Unmatched index brackets");
return null;
}
// Get the index and strip quotes if any
indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace("\"", "");
objName = objName.Substring(0, indexOpen);
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
}
}
else
obj = prop.GetValue(obj, null);
}
}
return obj;
}
Type oType = obj.GetType();
var prop = oType.GetProperty(objName);
if (prop == null)
{
Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} not found on {1}", objName, path[i - 1]);
return null;
}
// if there's an index, try to get the property
if (indexStr != null)
{
if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType))
{
Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} is not collection", objName);
return null;
}
var collection = prop.GetValue(obj, null) as ICollection;
// Get the indexed items "property"
var indexedPropInfo = prop.PropertyType.GetProperty("Item");
// These are the parameters for the indexing. Only care about one
var indexParams = indexedPropInfo.GetIndexParameters();
if (indexParams.Length > 0)
{
Debug.LogMessage(LogEventLevel.Information, " Indexed, param type: {0}", indexParams[0].ParameterType.Name);
var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType,
System.Globalization.CultureInfo.InvariantCulture);
try
{
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
}
// if the index is bad, catch it here.
catch (TargetInvocationException e)
{
if (e.InnerException is ArgumentOutOfRangeException)
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
else if (e.InnerException is KeyNotFoundException)
Debug.LogMessage(LogEventLevel.Information, " Key not found");
return null;
}
}
}
else
obj = prop.GetValue(obj, null);
}
}
return obj;
}
/// <summary>
/// Sets a property on an object.
@@ -297,78 +362,85 @@ namespace PepperDash.Essentials.Core
//return JsonConvert.SerializeObject(props, Formatting.Indented);
}
}
public class DeviceActionWrapper
{
public string DeviceKey { get; set; }
public string MethodName { get; set; }
public object[] Params { get; set; }
}
}
public class PropertyNameType
{
object Parent;
public class DeviceActionWrapper
{
public string DeviceKey { get; set; }
public string MethodName { get; set; }
public object[] Params { get; set; }
}
[JsonIgnore]
public PropertyInfo PropInfo { get; private set; }
public string Name { get { return PropInfo.Name; } }
public string Type { get { return PropInfo.PropertyType.Name; } }
public string Value { get
public class PropertyNameType
{
private object Parent;
[JsonIgnore]
public PropertyInfo PropInfo { get; private set; }
public string Name { get { return PropInfo.Name; } }
public string Type { get { return PropInfo.PropertyType.Name; } }
public string Value
{
if (PropInfo.CanRead)
get
{
try
if (PropInfo.CanRead)
{
return PropInfo.GetValue(Parent, null).ToString();
try
{
return PropInfo.GetValue(Parent, null).ToString();
}
catch (Exception)
{
return null;
}
}
catch (Exception)
{
else
return null;
}
}
else
return null;
} }
}
public bool CanRead { get { return PropInfo.CanRead; } }
public bool CanWrite { get { return PropInfo.CanWrite; } }
public PropertyNameType(PropertyInfo info, object parent)
{
PropInfo = info;
public PropertyNameType(PropertyInfo info, object parent)
{
PropInfo = info;
Parent = parent;
}
}
}
}
public class MethodNameParams
{
[JsonIgnore]
public MethodInfo MethodInfo { get; private set; }
public class MethodNameParams
{
[JsonIgnore]
public MethodInfo MethodInfo { get; private set; }
public string Name { get { return MethodInfo.Name; } }
public IEnumerable<NameType> Params { get {
return MethodInfo.GetParameters().Select(p =>
new NameType { Name = p.Name, Type = p.ParameterType.Name });
} }
public string Name { get { return MethodInfo.Name; } }
public IEnumerable<NameType> Params
{
get
{
return MethodInfo.GetParameters().Select(p =>
new NameType { Name = p.Name, Type = p.ParameterType.Name });
}
}
public MethodNameParams(MethodInfo info)
{
MethodInfo = info;
}
}
public MethodNameParams(MethodInfo info)
{
MethodInfo = info;
}
}
public class NameType
{
public string Name { get; set; }
public string Type { get; set; }
}
public class NameType
{
public string Name { get; set; }
public string Type { get; set; }
}
[AttributeUsage(AttributeTargets.All)]
public class ApiAttribute : Attribute
{
[AttributeUsage(AttributeTargets.All)]
public class ApiAttribute : Attribute
{
}
}
}

View File

@@ -1,50 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace PepperDash.Essentials.Core
{
public static class DeviceManager
{
public static class DeviceManager
{
public static event EventHandler<EventArgs> AllDevicesActivated;
public static event EventHandler<EventArgs> AllDevicesRegistered;
public static event EventHandler<EventArgs> AllDevicesInitialized;
private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection();
private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true);
//public static List<Device> Devices { get { return _Devices; } }
//static List<Device> _Devices = new List<Device>();
private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection();
private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true);
static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
//public static List<Device> Devices { get { return _Devices; } }
//static List<Device> _Devices = new List<Device>();
/// <summary>
/// Returns a copy of all the devices in a list
/// </summary>
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
private static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
public static bool AddDeviceEnabled;
/// <summary>
/// Returns a copy of all the devices in a list
/// </summary>
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
public static void Initialize(CrestronControlSystem cs)
{
AddDeviceEnabled = true;
CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
public static bool AddDeviceEnabled;
public static void Initialize(CrestronControlSystem cs)
{
AddDeviceEnabled = true;
CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive",
"Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator);
@@ -52,77 +52,89 @@ namespace PepperDash.Essentials.Core
CrestronConsole.AddNewConsoleCommand(s => DisableAllDeviceStreamDebugging(), "disableallstreamdebug", "disables stream debugging on all devices", ConsoleAccessLevelEnum.AccessOperator);
}
/// <summary>
/// Calls activate steps on all Device class items
/// </summary>
public static void ActivateAll()
{
try
{
/// <summary>
/// Calls activate steps on all Device class items
/// </summary>
public static void ActivateAll()
{
try
{
OnAllDevicesRegistered();
DeviceCriticalSection.Enter();
DeviceCriticalSection.Enter();
AddDeviceEnabled = false;
// PreActivate all devices
Debug.LogMessage(LogEventLevel.Information,"****PreActivation starting...****");
foreach (var d in Devices.Values)
{
try
{
if (d is Device)
(d as Device).PreActivate();
}
catch (Exception e)
{
// PreActivate all devices
Debug.LogMessage(LogEventLevel.Information, "****PreActivation starting...****");
foreach (var d in Devices.Values)
{
try
{
if (d is Device)
(d as Device).PreActivate();
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
}
}
}
}
Debug.LogMessage(LogEventLevel.Information, "****PreActivation complete****");
Debug.LogMessage(LogEventLevel.Information, "****Activation starting...****");
Debug.LogMessage(LogEventLevel.Information, "****Activation starting...****");
// Activate all devices
foreach (var d in Devices.Values)
{
try
{
if (d is Device)
(d as Device).Activate();
}
catch (Exception e)
{
// Activate all devices
foreach (var d in Devices.Values)
{
try
{
if (d is Device)
(d as Device).Activate();
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
}
}
}
}
Debug.LogMessage(LogEventLevel.Information, "****Activation complete****");
Debug.LogMessage(LogEventLevel.Information, "****PostActivation starting...****");
// PostActivate all devices
foreach (var d in Devices.Values)
{
try
{
if (d is Device)
(d as Device).PostActivate();
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
}
}
// PostActivate all devices
foreach (var d in Devices.Values)
{
try
{
if (d is Device)
(d as Device).PostActivate();
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
}
}
Debug.LogMessage(LogEventLevel.Information, "****PostActivation complete****");
OnAllDevicesActivated();
}
finally
{
}
finally
{
DeviceCriticalSection.Leave();
}
}
}
}
private static void DeviceManager_Initialized(object sender, EventArgs e)
{
var allInitialized = Devices.Values.OfType<EssentialsDevice>().All(d => d.IsInitialized);
if (allInitialized)
{
Debug.LogMessage(LogEventLevel.Information, "****All Devices Initalized****");
OnAllDevicesInitialized();
}
}
private static void OnAllDevicesActivated()
{
@@ -142,77 +154,85 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Calls activate on all Device class items
/// </summary>
public static void DeactivateAll()
{
try
{
DeviceCriticalSection.Enter();
foreach (var d in Devices.Values.OfType<Device>())
{
d.Deactivate();
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
private static void OnAllDevicesInitialized()
{
var handler = AllDevicesInitialized;
if (handler != null)
{
handler(null, new EventArgs());
}
}
//static void ListMethods(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if(dev != null)
// {
// 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));
// foreach (var m in methods)
// {
// sb.Append(string.Format("{0}(", m.Name));
// var pars = m.GetParameters();
// foreach (var p in pars)
// sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name));
// sb.AppendLine(")");
// }
// CrestronConsole.ConsoleCommandResponse(sb.ToString());
// }
//}
/// <summary>
/// Calls activate on all Device class items
/// </summary>
public static void DeactivateAll()
{
try
{
DeviceCriticalSection.Enter();
foreach (var d in Devices.Values.OfType<Device>())
{
d.Deactivate();
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
private static void ListDevices(string s)
{
Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count);
var sorted = Devices.Values.ToList();
sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
//static void ListMethods(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if(dev != null)
// {
// 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));
// foreach (var m in methods)
// {
// sb.Append(string.Format("{0}(", m.Name));
// var pars = m.GetParameters();
// foreach (var p in pars)
// sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name));
// sb.AppendLine(")");
// }
// CrestronConsole.ConsoleCommandResponse(sb.ToString());
// }
//}
foreach (var d in sorted)
{
var name = d is IKeyName ? (d as IKeyName).Name : "---";
Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name);
}
}
private static void ListDevices(string s)
{
Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count);
var sorted = Devices.Values.ToList();
sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
private static void ListDeviceFeedbacks(string devKey)
{
var dev = GetDeviceForKey(devKey);
if (dev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
return;
}
var statusDev = dev as IHasFeedback;
if (statusDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
return;
}
statusDev.DumpFeedbacksToConsole(true);
}
foreach (var d in sorted)
{
var name = d is IKeyName ? (d as IKeyName).Name : "---";
Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name);
}
}
//static void ListDeviceCommands(string devKey)
private static void ListDeviceFeedbacks(string devKey)
{
var dev = GetDeviceForKey(devKey);
if (dev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
return;
}
if (!(dev is IHasFeedback statusDev))
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
return;
}
statusDev.DumpFeedbacksToConsole(true);
}
//static void ListDeviceCommands(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if (dev == null)
@@ -223,134 +243,135 @@ namespace PepperDash.Essentials.Core
// Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey);
//}
private static void ListDeviceCommStatuses(string input)
{
var sb = new StringBuilder();
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
{
sb.Append(string.Format("{0}: {1}\r", dev,
dev.CommunicationMonitor.Status));
}
CrestronConsole.ConsoleCommandResponse(sb.ToString());
}
private static void ListDeviceCommStatuses(string input)
{
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
{
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}");
}
}
//static void DoDeviceCommand(string command)
//static void DoDeviceCommand(string command)
//{
// Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned");
//}
public static void AddDevice(IKeyed newDev)
{
try
{
if (!DeviceCriticalSection.TryEnter())
{
Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again");
return;
}
// Check for device with same key
//var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase));
////// If it exists, remove or warn??
//if (existingDevice != null)
public static void AddDevice(IKeyed newDev)
{
try
{
if (!DeviceCriticalSection.TryEnter())
{
Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again");
return;
}
// Check for device with same key
//var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase));
////// If it exists, remove or warn??
//if (existingDevice != null)
if (!AddDeviceEnabled)
{
Debug.LogMessage(LogEventLevel.Information, "All devices have been activated. Adding new devices is not allowed.");
return;
}
if (!AddDeviceEnabled)
{
Debug.LogMessage(LogEventLevel.Information, "All devices have been activated. Adding new devices is not allowed.");
return;
}
if (Devices.ContainsKey(newDev.Key))
{
Debug.LogMessage(LogEventLevel.Information, newDev, "WARNING: A device with this key already exists. Not added to manager");
return;
}
Devices.Add(newDev.Key, newDev);
//if (!(_Devices.Contains(newDev)))
// _Devices.Add(newDev);
}
finally
{
DeviceCriticalSection.Leave();
}
}
if (Devices.ContainsKey(newDev.Key))
{
Debug.LogMessage(LogEventLevel.Information, newDev, "WARNING: A device with this key already exists. Not added to manager");
return;
}
Devices.Add(newDev.Key, newDev);
//if (!(_Devices.Contains(newDev)))
// _Devices.Add(newDev);
public static void AddDevice(IEnumerable<IKeyed> devicesToAdd)
{
try
{
if (!DeviceCriticalSection.TryEnter())
{
Debug.LogMessage(LogEventLevel.Information,
"Currently unable to add devices to Device Manager. Please try again");
return;
}
if (!AddDeviceEnabled)
{
Debug.LogMessage(LogEventLevel.Information,
"All devices have been activated. Adding new devices is not allowed.");
return;
}
if (newDev is EssentialsDevice essentialsDev)
essentialsDev.Initialized += DeviceManager_Initialized;
}
finally
{
DeviceCriticalSection.Leave();
}
}
foreach (var dev in devicesToAdd)
{
try
{
Devices.Add(dev.Key, dev);
}
catch (ArgumentException ex)
{
Debug.LogMessage(LogEventLevel.Information, "Error adding device with key {0} to Device Manager: {1}\r\nStack Trace: {2}",
dev.Key, ex.Message, ex.StackTrace);
}
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
public static void AddDevice(IEnumerable<IKeyed> devicesToAdd)
{
try
{
if (!DeviceCriticalSection.TryEnter())
{
Debug.LogMessage(LogEventLevel.Information,
"Currently unable to add devices to Device Manager. Please try again");
return;
}
if (!AddDeviceEnabled)
{
Debug.LogMessage(LogEventLevel.Information,
"All devices have been activated. Adding new devices is not allowed.");
return;
}
public static void RemoveDevice(IKeyed newDev)
{
try
{
foreach (var dev in devicesToAdd)
{
try
{
Devices.Add(dev.Key, dev);
}
catch (ArgumentException ex)
{
Debug.LogMessage(LogEventLevel.Information, "Error adding device with key {0} to Device Manager: {1}\r\nStack Trace: {2}",
dev.Key, ex.Message, ex.StackTrace);
}
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
public static void RemoveDevice(IKeyed newDev)
{
try
{
DeviceCriticalSection.Enter();
if (newDev == null)
return;
if (Devices.ContainsKey(newDev.Key))
Devices.Remove(newDev.Key);
//if (_Devices.Contains(newDev))
// _Devices.Remove(newDev);
else
Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key);
}
finally
{
DeviceCriticalSection.Leave();
}
}
if (newDev == null)
return;
if (Devices.ContainsKey(newDev.Key))
Devices.Remove(newDev.Key);
//if (_Devices.Contains(newDev))
// _Devices.Remove(newDev);
else
Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key);
}
finally
{
DeviceCriticalSection.Leave();
}
}
public static IEnumerable<string> GetDeviceKeys()
{
//return _Devices.Select(d => d.Key).ToList();
return Devices.Keys;
}
public static IEnumerable<string> GetDeviceKeys()
{
//return _Devices.Select(d => d.Key).ToList();
return Devices.Keys;
}
public static IEnumerable<IKeyed> GetDevices()
{
//return _Devices.Select(d => d.Key).ToList();
return Devices.Values;
}
public static IEnumerable<IKeyed> GetDevices()
{
//return _Devices.Select(d => d.Key).ToList();
return Devices.Values;
}
public static IKeyed GetDeviceForKey(string key)
{
//return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (key != null && Devices.ContainsKey(key))
return Devices[key];
public static IKeyed GetDeviceForKey(string key)
{
//return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (key != null && Devices.ContainsKey(key))
return Devices[key];
return null;
}
return null;
}
/// <summary>
/// Console handler that simulates com port data receive
@@ -367,8 +388,7 @@ namespace PepperDash.Essentials.Core
}
//Debug.LogMessage(LogEventLevel.Verbose, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value);
var com = GetDeviceForKey(match.Groups[1].Value) as ComPortController;
if (com == null)
if (!(GetDeviceForKey(match.Groups[1].Value) is ComPortController com))
{
CrestronConsole.ConsoleCommandResponse("'{0}' is not a comm port device", match.Groups[1].Value);
return;
@@ -423,16 +443,15 @@ namespace PepperDash.Essentials.Core
var deviceKey = args[0];
var setting = args[1];
var timeout= String.Empty;
var timeout = String.Empty;
if (args.Length >= 3)
{
timeout = args[2];
}
var device = GetDeviceForKey(deviceKey) as IStreamDebugging;
if (device == null)
if (!(GetDeviceForKey(deviceKey) is IStreamDebugging device))
{
CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey);
return;
@@ -479,13 +498,11 @@ namespace PepperDash.Essentials.Core
{
foreach (var device in AllDevices)
{
var streamDevice = device as IStreamDebugging;
if (streamDevice != null)
if (device is IStreamDebugging streamDevice)
{
streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off);
}
}
}
}
}
}

View File

@@ -17,6 +17,24 @@ namespace PepperDash.Essentials.Core
[Description("The base Essentials Device Class")]
public abstract class EssentialsDevice : Device
{
public event EventHandler Initialized;
private bool _isInitialized;
public bool IsInitialized {
get { return _isInitialized; }
private set
{
if (_isInitialized == value) return;
_isInitialized = value;
if (_isInitialized)
{
Initialized?.Invoke(this, new EventArgs());
}
}
}
protected EssentialsDevice(string key)
: base(key)
{
@@ -41,6 +59,8 @@ namespace PepperDash.Essentials.Core
try
{
Initialize();
IsInitialized = true;
}
catch (Exception ex)
{

View File

@@ -51,8 +51,18 @@ namespace PepperDash.Essentials.Core
/// 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}";
public string DeviceKey
{
get
{
if(string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey;
else
{
return 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
@@ -65,8 +75,8 @@ namespace PepperDash.Essentials.Core
[Flags]
public enum eLevelControlType
{
Level = 0,
Mute = 1,
Level = 1,
Mute = 2,
LevelAndMute = Level | Mute,
}
}

View File

@@ -1,128 +1,121 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
/// <summary>
///
/// </summary>
public enum eSourceListItemType
{
Route, Off, Other, SomethingAwesomerThanThese
}
/// <summary>
///
/// </summary>
public enum eSourceListItemType
{
Route, Off, Other, SomethingAwesomerThanThese
}
/// <summary>
/// Represents an item in a source list - can be deserialized into.
/// </summary>
public class SourceListItem
{
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
/// <summary>
/// Represents an item in a source list - can be deserialized into.
/// </summary>
public class SourceListItem
{
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
/// <summary>
/// Returns the source Device for this, if it exists in DeviceManager
/// </summary>
[JsonIgnore]
public Device SourceDevice
{
get
{
if (_SourceDevice == null)
_SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device;
return _SourceDevice;
}
}
Device _SourceDevice;
/// <summary>
/// Returns the source Device for this, if it exists in DeviceManager
/// </summary>
[JsonIgnore]
public Device SourceDevice
{
get
{
if (_SourceDevice == null)
_SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device;
return _SourceDevice;
}
}
/// <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 (SourceDevice == null)
return "---";
return SourceDevice.Name;
}
return Name;
}
}
private Device _SourceDevice;
/// <summary>
/// A name that will override the source's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <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 (SourceDevice == null)
return "---";
return SourceDevice.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; }
public string Icon { get; set; }
/// <summary>
/// Alternate icon
/// </summary>
[JsonProperty("altIcon")]
public string AltIcon { get; set; }
public string AltIcon { get; set; }
/// <summary>
/// Indicates if the item should be included in the source list
/// </summary>
[JsonProperty("includeInSourceList")]
public bool IncludeInSourceList { get; set; }
public bool IncludeInSourceList { 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; }
public int Order { get; set; }
/// <summary>
/// The key of the device for volume control
/// </summary>
[JsonProperty("volumeControlKey")]
public string VolumeControlKey { get; set; }
public string VolumeControlKey { get; set; }
/// <summary>
/// The type of source list item
/// </summary>
[JsonProperty("type")]
[JsonConverter(typeof(StringEnumConverter))]
public eSourceListItemType Type { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public eSourceListItemType Type { get; set; }
/// <summary>
/// The list of routes to execute for this source list item
/// </summary>
[JsonProperty("routeList")]
public List<SourceRouteListItem> RouteList { get; set; }
public List<SourceRouteListItem> RouteList { get; set; }
/// <summary>
/// Indicates if this source should be disabled for sharing to the far end call participants via codec content
/// </summary>
[JsonProperty("disableCodecSharing")]
public bool DisableCodecSharing { get; set; }
public bool DisableCodecSharing { get; set; }
/// <summary>
/// Indicates if this source should be disabled for routing to a shared output
/// </summary>
[JsonProperty("disableRoutedSharing")]
public bool DisableRoutedSharing { get; set; }
public bool DisableRoutedSharing { get; set; }
[JsonProperty("destinations")]
public List<eSourceListItemDestinationTypes> Destinations { get; set; }
@@ -156,10 +149,10 @@ namespace PepperDash.Essentials.Core
[JsonProperty("disableSimpleRouting")]
public bool DisableSimpleRouting { get; set; }
public SourceListItem()
{
Icon = "Blank";
}
public SourceListItem()
{
Icon = "Blank";
}
public override string ToString()
{
@@ -167,23 +160,23 @@ namespace PepperDash.Essentials.Core
}
}
public class SourceRouteListItem
{
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
public class SourceRouteListItem
{
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
[JsonProperty("sourcePortKey")]
public string SourcePortKey { get; set; }
[JsonProperty("destinationKey")]
public string DestinationKey { get; set; }
[JsonProperty("destinationKey")]
public string DestinationKey { get; set; }
[JsonProperty("destinationPortKey")]
public string DestinationPortKey { get; set; }
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
}
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
}
/// <summary>
/// Defines the valid destination types for SourceListItems in a room
@@ -193,7 +186,12 @@ namespace PepperDash.Essentials.Core
defaultDisplay,
leftDisplay,
rightDisplay,
centerDisplay,
programAudio,
codecContent
codecContent,
frontLeftDisplay,
frontRightDisplay,
rearLeftDisplay,
rearRightDisplay,
}
}

View File

@@ -108,18 +108,20 @@ namespace PepperDash.Essentials.Core
protected void AddJoins(Type type)
{
type.GetType();
var fields =
type.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true));
type.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true)).ToList();
Debug.LogMessage(LogEventLevel.Debug, "Got {fields} with JoinNameAttribute", fields.Count);
foreach (var field in fields)
{
var childClass = Convert.ChangeType(this, type, null);
var value = field.GetValue(childClass) as JoinDataComplete; //this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
//this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
if (value == null)
if (!(field.GetValue(childClass) is JoinDataComplete value))
{
Debug.LogMessage(LogEventLevel.Information, "Unable to cast base class to {0}", type.Name);
continue;
@@ -129,7 +131,7 @@ namespace PepperDash.Essentials.Core
var joinName = value.GetNameAttribute(field);
if (String.IsNullOrEmpty(joinName)) continue;
if (string.IsNullOrEmpty(joinName)) continue;
Joins.Add(joinName, value);
}
@@ -155,29 +157,37 @@ namespace PepperDash.Essentials.Core
{
var sb = new StringBuilder();
// Get the joins of each type and print them
sb.AppendLine(String.Format("# {0}", GetType().Name));
sb.AppendLine();
sb.AppendLine("## Digitals");
sb.AppendLine();
// Get the joins of each type and print them
var digitals =
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Digital) == eJoinType.Digital)
.ToDictionary(j => j.Key, j => j.Value);
var digitalSb = AppendJoinList(GetSortedJoins(digitals));
digitalSb.AppendLine("## Analogs");
digitalSb.AppendLine();
var lineEnding = "\r\n";
var analogs =
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == eJoinType.Analog)
var digitals =
Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Digital))
.ToDictionary(j => j.Key, j => j.Value);
var analogs = Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Analog))
.ToDictionary(j => j.Key, j => j.Value);
var analogSb = AppendJoinList(GetSortedJoins(analogs));
analogSb.AppendLine("## Serials");
analogSb.AppendLine();
var serials =
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Serial) == eJoinType.Serial)
Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Serial))
.ToDictionary(j => j.Key, j => j.Value);
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Digital join count {digitalCount} Analog join count {analogCount} Serial join count {serialCount}", null, digitals.Count, analogs.Count, serials.Count);
// Get the joins of each type and print them
sb.Append($"# {GetType().Name}\r\n");
sb.Append(lineEnding);
sb.Append($"## Digitals{lineEnding}");
sb.Append(lineEnding);
// Get the joins of each type and print them
var digitalSb = AppendJoinList(GetSortedJoins(digitals));
digitalSb.Append($"## Analogs{lineEnding}");
digitalSb.Append(lineEnding);
var analogSb = AppendJoinList(GetSortedJoins(analogs));
analogSb.Append($"## Serials{lineEnding}");
analogSb.Append(lineEnding);
var serialSb = AppendJoinList(GetSortedJoins(serials));
sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length);
@@ -202,7 +212,7 @@ namespace PepperDash.Essentials.Core
private static void WriteJoinmapMarkdown(StringBuilder stringBuilder, string pluginType, string bridgeKey, string deviceKey)
{
var fileName = String.Format("{0}{1}{2}__{3}__{4}.md", Global.FilePathPrefix, "joinMaps/", pluginType, bridgeKey, deviceKey);
var fileName = string.Format("{0}{1}{2}__{3}__{4}.md", Global.FilePathPrefix, "joinMaps/", pluginType, bridgeKey, deviceKey);
using (var sw = new StreamWriter(fileName))
{
@@ -230,7 +240,7 @@ namespace PepperDash.Essentials.Core
static StringBuilder AppendJoinList(List<KeyValuePair<string, JoinDataComplete>> joins)
{
var sb = new StringBuilder();
const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |";
const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |\r\n";
const int joinNumberLen = 11;
const int joinSpanLen = 9;
const int typeLen = 19;
@@ -238,25 +248,25 @@ namespace PepperDash.Essentials.Core
var descriptionLen = (from @join in joins select @join.Value into j select j.Metadata.Description.Length).Concat(new[] {11}).Max();
//build header
sb.AppendLine(String.Format(stringFormatter,
String.Format("Join Number").PadRight(joinNumberLen, ' '),
String.Format("Join Span").PadRight(joinSpanLen, ' '),
String.Format("Description").PadRight(descriptionLen, ' '),
String.Format("Type").PadRight(typeLen, ' '),
String.Format("Capabilities").PadRight(capabilitiesLen, ' ')));
sb.Append(string.Format(stringFormatter,
string.Format("Join Number").PadRight(joinNumberLen, ' '),
string.Format("Join Span").PadRight(joinSpanLen, ' '),
string.Format("Description").PadRight(descriptionLen, ' '),
string.Format("Type").PadRight(typeLen, ' '),
string.Format("Capabilities").PadRight(capabilitiesLen, ' ')));
//build table seperator
sb.AppendLine(String.Format(stringFormatter,
new String('-', joinNumberLen),
new String('-', joinSpanLen),
new String('-', descriptionLen),
new String('-', typeLen),
new String('-', capabilitiesLen)));
sb.Append(string.Format(stringFormatter,
new string('-', joinNumberLen),
new string('-', joinSpanLen),
new string('-', descriptionLen),
new string('-', typeLen),
new string('-', capabilitiesLen)));
foreach (var join in joins)
{
sb.AppendLine(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
sb.Append(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
}
sb.AppendLine();
sb.Append("\r\n");
return sb;
}
@@ -411,10 +421,10 @@ namespace PepperDash.Essentials.Core
{
//Fixed Width Headers
var joinNumberLen = String.Format("Join Number").Length;
var joinSpanLen = String.Format("Join Span").Length;
var typeLen = String.Format("AnalogDigitalSerial").Length;
var capabilitiesLen = String.Format("ToFromFusion").Length;
var joinNumberLen = string.Format("Join Number").Length;
var joinSpanLen = string.Format("Join Span").Length;
var typeLen = string.Format("AnalogDigitalSerial").Length;
var capabilitiesLen = string.Format("ToFromFusion").Length;
//Track which one failed, if it did
const string placeholder = "unknown";
@@ -430,13 +440,13 @@ namespace PepperDash.Essentials.Core
try
{
dataArray["joinNumber"] = String.Format("{0}", JoinNumber.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinNumberLen, ' ');
dataArray["joinSpan"] = String.Format("{0}", JoinSpan.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinSpanLen, ' ');
dataArray["description"] = String.Format("{0}", Metadata.Description.ReplaceIfNullOrEmpty(placeholder)).PadRight(descriptionLen, ' ');
dataArray["joinType"] = String.Format("{0}", Metadata.JoinType.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(typeLen, ' ');
dataArray["capabilities"] = String.Format("{0}", Metadata.JoinCapabilities.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(capabilitiesLen, ' ');
dataArray["joinNumber"] = string.Format("{0}", JoinNumber.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinNumberLen, ' ');
dataArray["joinSpan"] = string.Format("{0}", JoinSpan.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinSpanLen, ' ');
dataArray["description"] = string.Format("{0}", Metadata.Description.ReplaceIfNullOrEmpty(placeholder)).PadRight(descriptionLen, ' ');
dataArray["joinType"] = string.Format("{0}", Metadata.JoinType.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(typeLen, ' ');
dataArray["capabilities"] = string.Format("{0}", Metadata.JoinCapabilities.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(capabilitiesLen, ' ');
return String.Format(stringFormatter,
return string.Format(stringFormatter,
dataArray["joinNumber"],
dataArray["joinSpan"],
dataArray["description"],
@@ -454,8 +464,8 @@ namespace PepperDash.Essentials.Core
errorKey = item.Key;
break;
}
Debug.LogMessage(LogEventLevel.Information, "Unable to decode join metadata {1}- {0}", e.Message, !String.IsNullOrEmpty(errorKey) ? (' ' + errorKey) : String.Empty);
return String.Format(stringFormatter,
Debug.LogMessage(LogEventLevel.Information, "Unable to decode join metadata {1}- {0}", e.Message, !string.IsNullOrEmpty(errorKey) ? (' ' + errorKey) : string.Empty);
return string.Format(stringFormatter,
dataArray["joinNumber"],
dataArray["joinSpan"],
dataArray["description"],
@@ -520,7 +530,7 @@ namespace PepperDash.Essentials.Core
public JoinNameAttribute(string name)
{
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}", name);
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}",null, name);
_Name = name;
}

View File

@@ -1,9 +1,5 @@
using System;
using PepperDash.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
@@ -26,6 +22,11 @@ namespace PepperDash.Essentials.Core
{
get
{
if (IsInAutoMode)
{
return _partitionSensor.PartitionPresentFeedback.BoolValue;
}
return _partitionPresent;
}
set
@@ -73,11 +74,11 @@ namespace PepperDash.Essentials.Core
PartitionPresentFeedback.FireUpdate();
}
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
if (IsInAutoMode)
{
PartitionPresentFeedback.FireUpdate();
PartitionPresent = e.BoolValue;
}
}
@@ -87,6 +88,8 @@ namespace PepperDash.Essentials.Core
public void SetAutoMode()
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Auto Mode", this);
IsInAutoMode = true;
if (PartitionPresentFeedback != null)
{
@@ -101,11 +104,16 @@ namespace PepperDash.Essentials.Core
{
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
_partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;
}
PartitionPresentFeedback.FireUpdate();
}
public void SetManualMode()
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Manual Mode", this);
IsInAutoMode = false;
if (PartitionPresentFeedback != null)
{
@@ -119,7 +127,10 @@ namespace PepperDash.Essentials.Core
if (_partitionSensor != null)
{
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;
}
PartitionPresentFeedback.FireUpdate();
}

View File

@@ -1,11 +1,12 @@
using System;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp;
using PepperDash.Core;
using Serilog.Events;
using Newtonsoft.Json;
using System.Threading;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
@@ -35,7 +36,7 @@ namespace PepperDash.Essentials.Core
}
set
{
if(value == _isInAutoMode)
if (value == _isInAutoMode)
{
return;
}
@@ -49,6 +50,8 @@ namespace PepperDash.Essentials.Core
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
private Mutex _scenarioChange = new Mutex();
public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
: base(key)
{
@@ -81,7 +84,15 @@ namespace PepperDash.Essentials.Core
SetupPartitionStateProviders();
SetRooms();
});
// Subscribe to the AllDevicesInitialized event
// We need to wait until all devices are initialized in case
// any actions are dependent on 3rd party devices already being
// connected and initialized
DeviceManager.AllDevicesInitialized += (o, a) =>
{
if (IsInAutoMode)
{
DetermineRoomCombinationScenario();
@@ -90,10 +101,10 @@ namespace PepperDash.Essentials.Core
{
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
}
});
};
}
void CreateScenarios()
private void CreateScenarios()
{
foreach (var scenarioConfig in _propertiesConfig.Scenarios)
{
@@ -102,21 +113,29 @@ namespace PepperDash.Essentials.Core
}
}
void SetRooms()
private void SetRooms()
{
_rooms = new List<IEssentialsRoom>();
foreach (var roomKey in _propertiesConfig.RoomKeys)
{
var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom;
if (room != null)
var room = DeviceManager.GetDeviceForKey(roomKey);
if (DeviceManager.GetDeviceForKey(roomKey) is IEssentialsRoom essentialsRoom)
{
_rooms.Add(room);
_rooms.Add(essentialsRoom);
}
}
var rooms = DeviceManager.AllDevices.OfType<IEssentialsRoom>().Cast<Device>();
foreach (var room in rooms)
{
room.Deactivate();
}
}
void SetupPartitionStateProviders()
private void SetupPartitionStateProviders()
{
foreach (var pConfig in _propertiesConfig.Partitions)
{
@@ -130,18 +149,18 @@ namespace PepperDash.Essentials.Core
}
}
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
StartDebounceTimer();
}
void StartDebounceTimer()
private void StartDebounceTimer()
{
// default to 500ms for manual mode
var time = 500;
// if in auto mode, debounce the scenario change
if(IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
if (IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
if (_scenarioChangeDebounceTimer == null)
{
@@ -156,7 +175,7 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// Determines the current room combination scenario based on the state of the partition sensors
/// </summary>
void DetermineRoomCombinationScenario()
private void DetermineRoomCombinationScenario()
{
if (_scenarioChangeDebounceTimer != null)
{
@@ -164,14 +183,20 @@ namespace PepperDash.Essentials.Core
_scenarioChangeDebounceTimer = null;
}
this.LogInformation("Determining Combination Scenario");
var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) =>
{
this.LogDebug("Checking scenario {scenarioKey}", s.Key);
// iterate the partition states
foreach (var partitionState in s.PartitionStates)
{
this.LogDebug("checking PartitionState {partitionStateKey}", partitionState.PartitionKey);
// get the partition by key
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
this.LogDebug("Expected State: {partitionPresent} Actual State: {partitionState}", partitionState.PartitionPresent, partition.PartitionPresentFeedback.BoolValue);
if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue)
{
// the partition can't be found or the state doesn't match
@@ -184,10 +209,41 @@ namespace PepperDash.Essentials.Core
if (currentScenario != null)
{
CurrentScenario = currentScenario;
this.LogInformation("Found combination Scenario {scenarioKey}", currentScenario.Key);
ChangeScenario(currentScenario);
}
}
private async Task ChangeScenario(IRoomCombinationScenario newScenario)
{
if (newScenario == _currentScenario)
{
return;
}
// Deactivate the old scenario first
if (_currentScenario != null)
{
Debug.LogMessage(LogEventLevel.Information, "Deactivating scenario {currentScenario}", this, _currentScenario.Name);
await _currentScenario.Deactivate();
}
_currentScenario = newScenario;
// Activate the new scenario
if (_currentScenario != null)
{
Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this);
await _currentScenario.Activate();
}
RoomCombinationScenarioChanged?.Invoke(this, new EventArgs());
}
#region IEssentialsRoomCombiner Members
public event EventHandler<EventArgs> RoomCombinationScenarioChanged;
@@ -198,33 +254,6 @@ namespace PepperDash.Essentials.Core
{
return _currentScenario;
}
private set
{
if (value != _currentScenario)
{
// Deactivate the old scenario first
if (_currentScenario != null)
{
_currentScenario.Deactivate();
}
_currentScenario = value;
// Activate the new scenario
if (_currentScenario != null)
{
_currentScenario.Activate();
Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this);
}
var handler = RoomCombinationScenarioChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
}
public BoolFeedback IsInAutoModeFeedback { get; private set; }
@@ -232,16 +261,35 @@ namespace PepperDash.Essentials.Core
public void SetAutoMode()
{
IsInAutoMode = true;
foreach (var partition in Partitions)
{
partition.SetAutoMode();
}
DetermineRoomCombinationScenario();
}
public void SetManualMode()
{
IsInAutoMode = false;
foreach (var partition in Partitions)
{
partition.SetManualMode();
}
}
public void ToggleMode()
{
IsInAutoMode = !IsInAutoMode;
if (IsInAutoMode)
{
SetManualMode();
}
else
{
SetAutoMode();
}
}
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PepperDash.Core;
@@ -87,12 +88,12 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// Activates this room combination scenario
/// </summary>
void Activate();
Task Activate();
/// <summary>
/// Deactivates this room combination scenario
/// </summary>
void Deactivate();
Task Deactivate();
/// <summary>
/// The state of the partitions that would activate this scenario

View File

@@ -1,22 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
using Newtonsoft.Json;
using PepperDash.Core.Logging;
using Serilog.Events;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a room combination scenario
/// </summary>
public class RoomCombinationScenario: IRoomCombinationScenario, IKeyName
public class RoomCombinationScenario : IRoomCombinationScenario, IKeyName
{
private RoomCombinationScenarioConfig _config;
@@ -40,7 +34,7 @@ namespace PepperDash.Essentials.Core
get { return _isActive; }
set
{
if(value == _isActive)
if (value == _isActive)
{
return;
}
@@ -76,33 +70,43 @@ namespace PepperDash.Essentials.Core
IsActiveFeedback = new BoolFeedback(() => _isActive);
}
public void Activate()
public async Task Activate()
{
Debug.LogMessage(LogEventLevel.Debug, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count);
this.LogInformation("Activating Scenario {name} with {activationActionCount} action(s) defined", Name, activationActions.Count);
List<Task> tasks = new List<Task>();
if (activationActions != null)
{
foreach (var action in activationActions)
{
DeviceJsonApi.DoDeviceAction(action);
this.LogInformation("Running Activation action {@action}", action);
tasks.Add(DeviceJsonApi.DoDeviceActionAsync(action));
}
}
await Task.WhenAll(tasks);
IsActive = true;
}
public void Deactivate()
public async Task Deactivate()
{
Debug.LogMessage(LogEventLevel.Debug, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count);
this.LogInformation("Deactivating Scenario {name} with {deactivationActionCount} action(s) defined", Name, deactivationActions.Count);
List<Task> tasks = new List<Task>();
if (deactivationActions != null)
{
foreach (var action in deactivationActions)
{
DeviceJsonApi.DoDeviceAction(action);
this.LogInformation("Running deactivation action {actionDeviceKey}:{actionMethod}", action.DeviceKey, action.MethodName);
tasks.Add( DeviceJsonApi.DoDeviceActionAsync(action));
}
}
await Task.WhenAll(tasks);
IsActive = false;
}

View File

@@ -200,6 +200,9 @@ namespace PepperDash.Essentials.Room.Config
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; }

View File

@@ -133,7 +133,28 @@ namespace PepperDash.Essentials.Core
}
}
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

View File

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

View File

@@ -1,7 +1,9 @@
using PepperDash.Core;
using Serilog.Events;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Debug = PepperDash.Core.Debug;
namespace PepperDash.Essentials.Core
@@ -12,20 +14,52 @@ 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 = "")
{
// Remove this line before committing!!!!!
var frame = new StackFrame(1, true);
Debug.LogMessage(LogEventLevel.Information, "ReleaseAndMakeRoute Called from {method} with params {destinationKey}:{sourceKey}:{signalType}:{destinationPortKey}:{sourcePortKey}", frame.GetMethod().Name, destination.Key, source.Key, signalType.ToString(), destinationPortKey, 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);
}
public static void RemoveRouteRequestForDestination(string destinationKey)
{
Debug.LogMessage(LogEventLevel.Information, "Removing route request for {destination}", null, destinationKey);
var result = RouteRequests.Remove(destinationKey);
var messageTemplate = result ? "Route Request for {destination} removed" : "Route Request for {destination} not found";
Debug.LogMessage(LogEventLevel.Information, messageTemplate, null, destinationKey);
}
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.Information, "Destination port is null");
if (sourcePort == null) Debug.LogMessage(LogEventLevel.Information, "Source port is null");
var routeRequest = new RouteRequest
{
Destination = destination,
DestinationPort = destinationPort,
Source = source,
SourcePort = sourcePort,
SignalType = signalType
};
var coolingDevice = destination as IWarmingCooling;
@@ -39,7 +73,7 @@ namespace PepperDash.Essentials.Core
RouteRequests[destination.Key] = 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);
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down and already has a routing request stored. Storing new route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
return;
}
@@ -53,45 +87,58 @@ namespace PepperDash.Essentials.Core
RouteRequests.Add(destination.Key, 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);
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down. Storing route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
return;
}
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
{
RouteRequests.Remove(destination.Key);
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);
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
}
destination.ReleaseRoute();
destination.ReleaseRoute(destinationPort?.Key ?? string.Empty);
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)
{
public static void ReleaseRoute(this IRoutingInputs destination)
{
ReleaseRoute(destination, string.Empty);
}
/// <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, string inputPortKey)
{
Debug.LogMessage(LogEventLevel.Information, "Release route for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
{
@@ -102,138 +149,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, inputPortKey);
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, destinationPort, 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, destinationPort, 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, destinationPort, 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 +344,8 @@ namespace PepperDash.Essentials.Core
}
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

@@ -14,19 +14,27 @@ namespace PepperDash.Essentials.Core
public class RouteDescriptor
{
public IRoutingInputs Destination { get; private set; }
public RoutingInputPort InputPort { get; private set; }
public IRoutingOutputs Source { get; private set; }
public eRoutingSignalType SignalType { get; private set; }
public List<RouteSwitchDescriptor> Routes { get; private set; }
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType):this(source,destination, null, signalType)
{
Destination = destination;
Source = source;
SignalType = signalType;
Routes = new List<RouteSwitchDescriptor>();
}
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType)
{
Destination = destination;
Source = source;
SignalType = signalType;
InputPort = inputPort;
Routes = new List<RouteSwitchDescriptor>();
}
/// <summary>
/// Executes all routes described in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
@@ -60,17 +68,29 @@ namespace PepperDash.Essentials.Core
/// </summary>
public void ReleaseRoutes()
{
foreach (var route in Routes)
{
if (route.SwitchingDevice is IRouting)
{
// Pull the route from the port. Whatever is watching the output's in use tracker is
// responsible for responding appropriately.
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}",null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
}
}
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
{
if (route.SwitchingDevice is IRouting switchingDevice)
{
if (route.OutputPort == null)
{
continue;
}
switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType);
if (route.OutputPort.InUseTracker != null)
{
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
else
{
Debug.LogMessage(LogEventLevel.Error, "InUseTracker is null for OutputPort {0}", null, route.OutputPort.Key);
}
}
}
}
public override string ToString()
{

View File

@@ -1,8 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using PepperDash.Core;
using PepperDash.Core;
using Serilog.Events;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.Core
@@ -33,7 +32,13 @@ namespace PepperDash.Essentials.Core
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
if (descriptor == null)
{
return;
}
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination)
&& RouteDescriptors.Any(t => t.Destination == descriptor.Destination && t.InputPort != null && descriptor.InputPort != null && t.InputPort.Key == descriptor.InputPort.Key))
{
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
@@ -48,18 +53,33 @@ namespace PepperDash.Essentials.Core
/// <returns>null if no RouteDescriptor for a destination exists</returns>
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
{
Debug.LogMessage(LogEventLevel.Debug, "Getting route descriptor", destination);
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
public RouteDescriptor GetRouteDescriptorForDestinationAndInputPort(IRoutingInputs destination, string inputPortKey)
{
Debug.LogMessage(LogEventLevel.Debug, "Getting route descriptor for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination && rd.InputPort != null && rd.InputPort.Key == inputPortKey);
}
/// <summary>
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
/// Returns null if no route with the provided destination exists.
/// </summary>
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination, string inputPortKey = "")
{
var descr = GetRouteDescriptorForDestination(destination);
Debug.LogMessage(LogEventLevel.Debug, "Removing route descriptor for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
var descr = string.IsNullOrEmpty(inputPortKey)
? GetRouteDescriptorForDestination(destination)
: GetRouteDescriptorForDestinationAndInputPort(destination, inputPortKey);
if (descr != null)
RouteDescriptors.Remove(descr);
Debug.LogMessage(LogEventLevel.Debug, "Found route descriptor {routeDescriptor}", destination, descr);
return descr;
}
}

View File

@@ -1,44 +1,34 @@
namespace PepperDash.Essentials.Core
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
public class RouteRequest
{
public IRoutingSink Destination {get; set;}
public IRoutingOutputs Source {get; set;}
public eRoutingSignalType SignalType {get; set;}
public RoutingInputPort DestinationPort { get; set; }
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
var coolingDevice = sender as IWarmingCooling;
if(args.BoolValue == false)
{
Destination.ReleaseAndMakeRoute(Source, SignalType);
if(sender == null) return;
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
}
}
}
/*public class RouteRequest<TInputSelector, TOutputSelector>
{
public IRoutingSink<TInputSelector> Destination { get; set; }
public IRoutingOutputs<TOutputSelector> Source { 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;
Debug.LogMessage(LogEventLevel.Information, "Handling cooldown route request: {destination}:{destinationPort} -> {source}:{sourcePort} {type}", null, Destination.Key, DestinationPort.Key, Source.Key, SourcePort.Key, SignalType.ToString());
if (args.BoolValue == false)
if (args.BoolValue == true)
{
Destination.ReleaseAndMakeRoute(Source, SignalType);
return;
}
if (sender == null) return;
Debug.LogMessage(LogEventLevel.Information, "Cooldown complete. Making route from {destination} to {source}", Destination.Key, Source.Key);
Destination.ReleaseAndMakeRoute(Source, SignalType, DestinationPort?.Key ?? string.Empty, SourcePort?.Key ?? string.Empty);
if (sender is IWarmingCooling coolingDevice)
{
Debug.LogMessage(LogEventLevel.Debug, "Unsubscribing from cooling feedback for {destination}", null, Destination.Key);
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
}
}
}*/
}
}

View File

@@ -5,7 +5,7 @@
/// </summary>
public class RouteSwitchDescriptor
{
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } }
public RoutingOutputPort OutputPort { get; set; }
public RoutingInputPort InputPort { get; set; }
@@ -23,9 +23,9 @@
public override string ToString()
{
if (SwitchingDevice is IRouting)
return $"{SwitchingDevice?.Key} switches output {OutputPort.Key} to input {InputPort.Key}";
return $"{(SwitchingDevice != null ? SwitchingDevice.Key : "No Device")} switches output {(OutputPort != null ? OutputPort.Key : "No output port")} to input {(InputPort != null ? InputPort.Key : "No input port")}";
else
return $"{SwitchingDevice.Key} switches to input {InputPort.Key}";
return $"{(SwitchingDevice != null ? SwitchingDevice.Key : "No Device")} switches to input {(InputPort != null ? InputPort.Key : "No input port")}";
}
}

View File

@@ -65,7 +65,7 @@ namespace PepperDash.Essentials.Core.Routing
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
if(inputPort == null)
{
@@ -101,7 +101,7 @@ namespace PepperDash.Essentials.Core.Routing
return;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Getting source for first TieLine {tieLine}", this, firstTieLine);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Getting source for first TieLine {tieLine}", this, firstTieLine);
TieLine sourceTieLine;
try
@@ -128,7 +128,7 @@ namespace PepperDash.Essentials.Core.Routing
return;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root TieLine {tieLine}", this, sourceTieLine);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root TieLine {tieLine}", this, sourceTieLine);
// Does not handle combinable scenarios or other scenarios where a display might be part of multiple rooms yet.
var room = DeviceManager.AllDevices.OfType<IEssentialsRoom>().FirstOrDefault((r) => {
@@ -151,7 +151,7 @@ namespace PepperDash.Essentials.Core.Routing
return;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found room {room} for destination {destination}", this, room.Key, destination.Key);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found room {room} for destination {destination}", this, room.Key, destination.Key);
var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);
@@ -161,15 +161,15 @@ namespace PepperDash.Essentials.Core.Routing
return;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found sourceList for room {room}", this, room.Key);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found sourceList for room {room}", this, room.Key);
var sourceListItem = sourceList.FirstOrDefault(sli => {
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,
"SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}",
this,
sli.Key,
sli.Value.SourceKey,
sourceTieLine.SourcePort.ParentDevice.Key);
//// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,
// "SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}",
// this,
// sli.Key,
// sli.Value.SourceKey,
// sourceTieLine.SourcePort.ParentDevice.Key);
return sli.Value.SourceKey.Equals(sourceTieLine.SourcePort.ParentDevice.Key,StringComparison.InvariantCultureIgnoreCase);
});
@@ -191,7 +191,7 @@ namespace PepperDash.Essentials.Core.Routing
return;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {source}", this, source);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {source}", this, source);
destination.CurrentSourceInfo = source;
destination.CurrentSourceInfoKey = source.SourceKey;
@@ -202,11 +202,11 @@ namespace PepperDash.Essentials.Core.Routing
TieLine nextTieLine = null;
try
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "**Following tieLine {tieLine}**", this, tieLine);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "**Following tieLine {tieLine}**", this, tieLine);
if (tieLine.SourcePort.ParentDevice is IRoutingWithFeedback midpoint)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source device {sourceDevice} is midpoint", this, midpoint);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source device {sourceDevice} is midpoint", this, midpoint);
if(midpoint.CurrentRoutes == null || midpoint.CurrentRoutes.Count == 0)
{
@@ -215,9 +215,9 @@ namespace PepperDash.Essentials.Core.Routing
}
var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route => {
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
return route.OutputPort.Key == tieLine.SourcePort.Key && route.OutputPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key;
return route.OutputPort != null && route.InputPort != null && route.OutputPort?.Key == tieLine.SourcePort.Key && route.OutputPort?.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key;
});
if (currentRoute == null)
@@ -226,28 +226,28 @@ namespace PepperDash.Essentials.Core.Routing
return null;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found currentRoute {currentRoute} through {midpoint}", this, currentRoute, midpoint);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found currentRoute {currentRoute} through {midpoint}", this, currentRoute, midpoint);
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => {
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", tl.DestinationPort.Key, currentRoute.InputPort.Key);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", tl.DestinationPort.Key, currentRoute.InputPort.Key);
return tl.DestinationPort.Key == currentRoute.InputPort.Key && tl.DestinationPort.ParentDevice.Key == currentRoute.InputPort.ParentDevice.Key; });
if (nextTieLine != null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found next tieLine {tieLine}. Walking the chain", this, nextTieLine);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found next tieLine {tieLine}. Walking the chain", this, nextTieLine);
return GetRootTieLine(nextTieLine);
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root tieLine {tieLine}", this,nextTieLine);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root tieLine {tieLine}", this,nextTieLine);
return nextTieLine;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLIne Source Device {sourceDeviceKey} is IRoutingSource: {isIRoutingSource}", this, tieLine.SourcePort.ParentDevice.Key, tieLine.SourcePort.ParentDevice is IRoutingSource);
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source Device interfaces: {typeFullName}:{interfaces}", this, tieLine.SourcePort.ParentDevice.GetType().FullName, tieLine.SourcePort.ParentDevice.GetType().GetInterfaces().Select(i => i.Name));
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLIne Source Device {sourceDeviceKey} is IRoutingSource: {isIRoutingSource}", this, tieLine.SourcePort.ParentDevice.Key, tieLine.SourcePort.ParentDevice is IRoutingSource);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source Device interfaces: {typeFullName}:{interfaces}", this, tieLine.SourcePort.ParentDevice.GetType().FullName, tieLine.SourcePort.ParentDevice.GetType().GetInterfaces().Select(i => i.Name));
if (tieLine.SourcePort.ParentDevice is IRoutingSource || tieLine.SourcePort.ParentDevice is IRoutingOutputs) //end of the chain
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root: {tieLine}", this, tieLine);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root: {tieLine}", this, tieLine);
return tieLine;
}

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

@@ -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

@@ -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

@@ -4,9 +4,6 @@ using PepperDash.Essentials.Core.Config;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Generic
{
@@ -16,10 +13,9 @@ namespace PepperDash.Essentials.Devices.Common.Generic
{
InputPorts = new RoutingPortCollection<RoutingInputPort>();
var inputPort = new RoutingInputPort($"{Key}-{RoutingPortNames.AnyVideoIn}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
InputPorts.Add(inputPort);
}
public RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }
@@ -57,8 +53,8 @@ namespace PepperDash.Essentials.Devices.Common.Generic
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Source Device");
return new GenericSource(dc.Key, dc.Name);
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Sink Device");
return new GenericSink(dc.Key, dc.Name);
}
}
}

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

@@ -42,7 +42,7 @@ namespace PepperDash.Essentials.Devices.Common
public void PrintExpectedIrCommands()
{
var cmds = typeof (AppleTvIrCommands).GetType().GetFields(BindingFlags.Public | BindingFlags.Static);
var cmds = typeof (AppleTvIrCommands).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().GetType().FullName, entry.ParentFolderId);
entry.Name, entry.FolderId, entryIndex, entry.GetType().FullName, entry.ParentFolderId);
if (entry is DirectoryFolder)
{

View File

@@ -1,58 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectType>Program</ProjectType>
<Configurations>Debug;Release;Debug 4.7.2</Configurations>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>PepperDash.Essentials</RootNamespace>
<AssemblyName>PepperDashEssentials</AssemblyName>
<TargetFrameworks>net472;net6</TargetFrameworks>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<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>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<None Include="Example Configuration\EssentialsHuddleSpaceRoom\configurationFile-HuddleSpace-2-Source.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Example Configuration\EssentialsHuddleVtc1Room\configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Example Configuration\SIMPLBridging\configurationFile-dmps3300c-avRouting.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Example Configuration\SIMPLBridging\SIMPLBridgeExample_configurationFile.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="SGD\PepperDash Essentials iPad.sgd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="SGD\PepperDash Essentials TSW-560.sgd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="SGD\PepperDash Essentials TSW-760.sgd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />
<ProjectReference Include="..\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj" />
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectType>Program</ProjectType>
<Configurations>Debug;Release;Debug 4.7.2</Configurations>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>PepperDash.Essentials</RootNamespace>
<AssemblyName>PepperDashEssentials</AssemblyName>
<TargetFrameworks>net472;net6</TargetFrameworks>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<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>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<None Include="Example Configuration\EssentialsHuddleSpaceRoom\configurationFile-HuddleSpace-2-Source.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Example Configuration\EssentialsHuddleVtc1Room\configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Example Configuration\SIMPLBridging\configurationFile-dmps3300c-avRouting.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Example Configuration\SIMPLBridging\SIMPLBridgeExample_configurationFile.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="SGD\PepperDash Essentials iPad.sgd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="SGD\PepperDash Essentials TSW-560.sgd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="SGD\PepperDash Essentials TSW-760.sgd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />
<ProjectReference Include="..\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj" />
</ItemGroup>
</Project>