Compare commits

...

46 Commits

Author SHA1 Message Date
Andrew Welker
8108b9dfdb fix: attempt to get better logging for exception
Fixed some issues with log messages that were not formatted correctly for Serilog.
2025-02-18 11:59:26 -06:00
Nick Genovese
fb4f1482c7 fix: small null check in the release and make route method 2025-01-31 19:33:31 -05:00
Nick Genovese
54dcb5de08 feat: implement IPartitionStateProvider to Generic VersaPortInput 2025-01-30 15:21:12 -05:00
Nick Genovese
d8a88b2a07 Merge branch 'development-2.0.0' into feature-2.0.0/emergencyOSD 2025-01-10 07:31:35 -05:00
Andrew Welker
cc724ddf19 Merge pull request #1210 from PepperDash/hotfix-2.0.0/release-routes-clears-the-routes
Hotfix 2.0.0/release routes clears the routes
2024-12-30 13:33:54 -06:00
Nick Genovese
e29e800d9d fix: now pushes the tag whenever not rc 2024-12-10 07:52:43 -05:00
Nick Genovese
134e8ba02e fix: remove null route when releasing route 2024-12-10 07:44:10 -05:00
Neil Dorin
a83ba444d3 Merge pull request #1209 from PepperDash/feature-2.0.0/more-cooldown-fixes 2024-11-22 08:34:13 -07:00
Andrew Welker
f4c5e6fbeb fix: remove event sub for route request
When route requests made during a destination's cooldown cycle were handled, the event subscription was *NOT* being removed, resulting in the request being run on *EVERY* subsequent cooldown complete event.
2024-11-22 09:14:07 -06:00
Andrew Welker
35d7994cc8 Merge pull request #1208 from PepperDash/feature-2.0.0/cooldown-exception
fix: add try/catch for routing cooldown handler
2024-11-20 15:52:24 -06:00
Andrew Welker
c3e9d654c9 fix: add try/catch for routing cooldown handler
Fixed log statement to handle when a value is null
2024-11-20 15:47:33 -06:00
Andrew Knous
f68b1e9e49 feat: cleans up code, adds versiport comment, changes ShowEmergencyMessage arg name to "url" 2024-11-19 09:19:48 -08:00
Andrew Knous
cd81b8af73 feat: adds roomKey to ShowEmergencyMessage 2024-11-14 17:00:21 -05:00
Andrew Knous
cd52c245a6 feat: adds emergency OSD support 2024-11-14 16:23:31 -05:00
Neil Dorin
0b60f53d0e feat: Adds IEssentialsRoomEmergency interface and implements on contact closure device to provide state 2024-11-13 12:15:25 -07:00
Andrew Welker
ffed2dea8a Merge pull request #1206 from PepperDash/feature-2.0.0/catv-presets
docs: adds debug statement to print preset count
2024-10-31 09:54:47 -05:00
Andrew Welker
936dce2046 Merge pull request #1204 from PepperDash/feature-2.0.0/fix-version-info
feat: adds sdi in/out port names
2024-10-31 09:53:46 -05:00
Andrew Welker
b33704eabe Merge pull request #1203 from PepperDash/feature-2.0.0/bridge-issues
fix: joins in join maps get added correctly to a bridge
2024-10-30 13:27:25 -05:00
Andrew Welker
aca6fe9af5 chore: remove extraneous call 2024-10-30 13:20:43 -05:00
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
Nick Genovese
4449077a39 Merge pull request #1202 from PepperDash/feature-2.0.0/display-feedback-fix
Set CurrentSourceKey correctly & in the correct order
2024-10-30 11:32:29 -04:00
Andrew Welker
86ba9e0f16 fix: set currentSourceKey & currentSource in order 2024-10-30 10:19:55 -05:00
Andrew Welker
db2d8a213d fix: get order of source & source key correct 2024-10-29 10:59:26 -05:00
Andrew Welker
590e16298c fix: use correct key for destination CurrentSourceInfoKey 2024-10-28 16:41:30 -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
Neil Dorin
63c653b21f docs: adds debug statement to print preset count 2024-07-24 10:39:52 -06:00
Neil Dorin
b326ccf6c3 feat: adds sdi in/out port names 2024-06-19 13:08:55 -06:00
32 changed files with 608 additions and 354 deletions

View File

@@ -22,7 +22,7 @@ env:
jobs:
Build_Project_4-Series:
runs-on: windows-latest
steps:
steps:
- uses: actions/checkout@v3
- name: Set Version Number
id: setVersion
@@ -57,26 +57,26 @@ jobs:
$phase = 'beta'
$newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER
}
}
echo "version=$newVersionString" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
}
echo "version=$newVersionString" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Setup MS Build
uses: microsoft/setup-msbuild@v1.1
- name: restore Nuget Packages
run: nuget restore .\$($Env:SOLUTION_FILE).sln
# Build the solutions in the docker image
- name: Build Solution
- 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')
if: ${{ !contains(steps.setVersion.outputs.version, 'rc') }}
run: |
git tag ${{ steps.setVersion.outputs.version }}
git push --tags origin
# Create the release on the source repo
- name: Create Release
id: create_release
# if: contains(steps.setVersion.outputs.version,'-rc-') ||
# contains(steps.setVersion.outputs.version,'-hotfix-') ||
# contains(steps.setVersion.outputs.version, '-beta-')
uses: ncipollo/release-action@v1
with:
artifacts: 'output\**\*.*(cpz|cplz)'
@@ -84,11 +84,11 @@ jobs:
prerelease: ${{contains('debug', env.BUILD_TYPE)}}
tag: ${{ steps.setVersion.outputs.version }}
- name: Setup Nuget
run: |
run: |
nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username pepperdash -password ${{ secrets.GITHUB_TOKEN }}
nuget setApiKey ${{ secrets.GITHUB_TOKEN }} -Source github
nuget setApiKey ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json
nuget setApiKey ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json
- name: Publish to Nuget
run: nuget push .\output\*.nupkg -Source https://api.nuget.org/v3/index.json
- name: Publish to Github Nuget
run: nuget push .\output\*.nupkg -Source github
run: nuget push .\output\*.nupkg -Source github

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

View File

@@ -55,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];
@@ -68,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;
}
@@ -83,7 +83,7 @@ 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];
@@ -94,7 +94,7 @@ namespace PepperDash.Essentials.Core.Config
/// </summary>
public Dictionary<string, CameraListItem> GetCameraListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))
if (CameraLists == null || string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))
return null;
return CameraLists[key];

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

@@ -21,7 +21,7 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// <summary>
/// Represents a generic digital input deviced tied to a versiport
/// </summary>
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider
{
public Versiport InputPort { get; private set; }
@@ -35,10 +35,15 @@ namespace PepperDash.Essentials.Core.CrestronIO
}
}
public BoolFeedback PartitionPresentFeedback { get; }
public bool PartitionPresent => !InputStateFeedbackFunc();
public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name)
{
InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
PartitionPresentFeedback = new BoolFeedback(() => !InputStateFeedbackFunc());
AddPostActivationAction(() =>
{
@@ -52,7 +57,8 @@ namespace PepperDash.Essentials.Core.CrestronIO
InputPort.VersiportChange += InputPort_VersiportChange;
InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate();
Debug.LogMessage(LogEventLevel.Debug, this, "Created GenericVersiportDigitalInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor);
@@ -65,7 +71,10 @@ namespace PepperDash.Essentials.Core.CrestronIO
Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event);
if(args.Event == eVersiportEvent.DigitalInChange)
{
InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate();
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface IEmergencyOSD
{
void ShowEmergencyMessage(string url);
void HideEmergencyMessage();
}
}

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

@@ -206,7 +206,7 @@ namespace PepperDash.Essentials.Core
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)

View File

@@ -14,6 +14,7 @@ namespace PepperDash.Essentials.Core
{
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);
@@ -67,7 +68,7 @@ namespace PepperDash.Essentials.Core
foreach (var d in Devices.Values)
{
try
{
{
if (d is Device)
(d as Device).PreActivate();
}
@@ -123,6 +124,18 @@ namespace PepperDash.Essentials.Core
}
}
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()
{
var handler = AllDevicesActivated;
@@ -141,6 +154,15 @@ namespace PepperDash.Essentials.Core
}
}
private static void OnAllDevicesInitialized()
{
var handler = AllDevicesInitialized;
if (handler != null)
{
handler(null, new EventArgs());
}
}
/// <summary>
/// Calls activate on all Device class items
/// </summary>
@@ -264,6 +286,9 @@ namespace PepperDash.Essentials.Core
Devices.Add(newDev.Key, newDev);
//if (!(_Devices.Contains(newDev)))
// _Devices.Add(newDev);
if (newDev is EssentialsDevice essentialsDev)
essentialsDev.Initialized += DeviceManager_Initialized;
}
finally
{

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

View File

@@ -107,19 +107,20 @@ namespace PepperDash.Essentials.Core
}
protected void AddJoins(Type type)
{
{
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 +130,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 +156,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 +211,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 +239,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 +247,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 +420,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 +439,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 +463,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 +529,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

@@ -130,6 +130,8 @@ namespace PepperDash.Essentials.Core.Presets
var pl = JsonConvert.DeserializeObject<PresetsList>(File.ReadToEnd(_filePath, Encoding.ASCII));
Name = pl.Name;
PresetsList = pl.Channels;
Debug.LogMessage(LogEventLevel.Verbose, this, "Loaded {0} presets", PresetsList.Count);
}
catch (Exception e)
{

View File

@@ -84,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();
@@ -93,7 +101,7 @@ namespace PepperDash.Essentials.Core
{
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
}
});
};
}
private void CreateScenarios()

View File

@@ -24,6 +24,7 @@ namespace PepperDash.Essentials.Room.Config
//switch on emergency type here. Right now only contact and shutdown
var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room);
DeviceManager.AddDevice(e);
return e;
}
return null;
}

View File

@@ -16,7 +16,7 @@
public class EssentialsRoomEmergencyTriggerConfig
{
/// <summary>
/// contact,
/// contact,versiport
/// </summary>
public string Type { get; set; }
/// <summary>

View File

@@ -4,12 +4,16 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials.Core
{
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase, IEssentialsRoomEmergency
{
public event EventHandler<EventArgs> EmergencyStateChange;
IEssentialsRoom Room;
string Behavior;
bool TriggerOnClose;
public bool InEmergency { get; private set; }
public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) :
base(key)
{
@@ -25,14 +29,49 @@ namespace PepperDash.Essentials.Core
cs.DigitalInputPorts[portNum].StateChange += EsentialsRoomEmergencyContactClosure_StateChange;
}
}
else if (config.Trigger.Type.Equals("versiport", StringComparison.OrdinalIgnoreCase))
{
var portNum = (uint)config.Trigger.Number;
if (portNum <= cs.NumberOfVersiPorts)
{
cs.VersiPorts[portNum].Register();
cs.VersiPorts[portNum].SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);
cs.VersiPorts[portNum].DisablePullUpResistor = true;
cs.VersiPorts[portNum].VersiportChange += EssentialsRoomEmergencyContactClosure_VersiportChange;
}
}
Behavior = config.Behavior;
TriggerOnClose = config.Trigger.TriggerOnClose;
}
private void EssentialsRoomEmergencyContactClosure_VersiportChange(Versiport port, VersiportEventArgs args)
{
if (args.Event == eVersiportEvent.DigitalInChange)
{
ContactClosure_StateChange(port.DigitalIn);
}
}
void EsentialsRoomEmergencyContactClosure_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args)
{
if (args.State && TriggerOnClose || !args.State && !TriggerOnClose)
ContactClosure_StateChange(args.State);
}
void ContactClosure_StateChange(bool portState)
{
if (portState && TriggerOnClose || !portState && !TriggerOnClose)
{
InEmergency = true;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
RunEmergencyBehavior();
}
else
{
InEmergency = false;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
}
}
/// <summary>
@@ -44,4 +83,14 @@ namespace PepperDash.Essentials.Core
Room.Shutdown();
}
}
/// <summary>
/// Describes the functionality of a room emergency contact closure
/// </summary>
public interface IEssentialsRoomEmergency
{
event EventHandler<EventArgs> EmergencyStateChange;
bool InEmergency { get; }
}
}

View File

@@ -61,12 +61,13 @@ namespace PepperDash.Essentials.Core
SignalType = signalType
};
var coolingDevice = destination as IWarmingCooling;
var coolingDevice = destination as IWarmingCooling;
//We already have a route request for this device, and it's a cooling device and is cooling
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
@@ -80,24 +81,27 @@ namespace PepperDash.Essentials.Core
//New Request
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
{
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
RouteRequests.Add(destination.Key, routeRequest);
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down. Storing route request to route to source key: {sourceKey}", 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)
{
var handledRequest = RouteRequests[destination.Key];
coolingDevice.IsCoolingDownFeedback.OutputChange -= handledRequest.HandleCooldown;
RouteRequests.Remove(destination.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);
}
@@ -125,28 +129,40 @@ namespace PepperDash.Essentials.Core
videoRoute?.ExecuteRoutes();
}
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)
/// <param name="destination"></param>
public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)
{
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
try
{
var coolingDevice = destination as IWarmingCooling;
Debug.LogMessage(LogEventLevel.Information, "Release route for '{destination}':'{inputPortKey}'", destination?.Key ?? "", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
}
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
{
var coolingDevice = destination as IWarmingCooling;
RouteRequests.Remove(destination.Key);
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
}
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
if (current != null)
RouteRequests.Remove(destination.Key);
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination, inputPortKey);
if (current != null)
{
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes();
}
} catch (Exception ex)
{
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes();
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? "", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
}
}
@@ -162,13 +178,14 @@ namespace PepperDash.Essentials.Core
// if it's a single signal type, find the route
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
{
var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, signalType);
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 (!destination.GetRouteToSource(source, null, null, signalType, 0, singleTypeRouteDescriptor, destinationPort, sourcePort))
singleTypeRouteDescriptor = null;
foreach (var route in singleTypeRouteDescriptor.Routes)
var routes = singleTypeRouteDescriptor?.Routes ?? new List<RouteSwitchDescriptor>();
foreach (var route in routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Route for device: {route}", destination, route.ToString());
}
@@ -179,14 +196,14 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key);
var audioRouteDescriptor = new RouteDescriptor(source, destination, eRoutingSignalType.Audio);
var audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio);
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, audioRouteDescriptor, destinationPort, sourcePort);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
var videoRouteDescriptor = new RouteDescriptor(source, destination, eRoutingSignalType.Video);
var videoRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Video);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, videoRouteDescriptor, destinationPort, sourcePort);

View File

@@ -12,35 +12,43 @@ namespace PepperDash.Essentials.Core
/// Represents an collection of individual route steps between Source and Destination
/// </summary>
public class RouteDescriptor
{
public IRoutingInputs Destination { get; private set; }
public IRoutingOutputs Source { get; private set; }
public eRoutingSignalType SignalType { get; private set; }
public List<RouteSwitchDescriptor> Routes { get; private set; }
{
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)
{
Destination = destination;
Source = source;
SignalType = signalType;
Routes = new List<RouteSwitchDescriptor>();
}
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType)
{
}
/// <summary>
/// Executes all routes described in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ExecuteRoutes()
{
foreach (var route in Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}",null, route.ToString());
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()
/// </summary>
public void ExecuteRoutes()
{
foreach (var route in Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}", null, route.ToString());
if (route.SwitchingDevice is IRoutingSinkWithSwitching sink)
{
{
sink.ExecuteSwitch(route.InputPort.Selector);
continue;
continue;
}
if (route.SwitchingDevice is IRouting switchingDevice)
@@ -51,35 +59,43 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
}
}
}
}
/// <summary>
/// Releases all routes in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ReleaseRoutes()
{
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
{
if (route.SwitchingDevice is IRouting switchingDevice)
{
switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType);
/// <summary>
/// Releases all routes in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ReleaseRoutes()
{
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
{
if (route.SwitchingDevice is IRouting switchingDevice)
{
if (route.OutputPort == null)
{
continue;
}
// 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);
}
}
}
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()
{
var routesText = Routes.Select(r => r.ToString()).ToArray();
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
}
}
public override string ToString()
{
var routesText = Routes.Select(r => r.ToString()).ToArray();
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
}
}
/*/// <summary>
/// Represents an collection of individual route steps between Source and Destination

View File

@@ -37,7 +37,8 @@ namespace PepperDash.Essentials.Core
return;
}
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
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);
@@ -52,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.Information, "Getting route descriptor for '{destination}'", destination?.Key ?? "");
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
public RouteDescriptor GetRouteDescriptorForDestinationAndInputPort(IRoutingInputs destination, string inputPortKey)
{
Debug.LogMessage(LogEventLevel.Information, "Getting route descriptor for '{destination}':'{inputPortKey}'", destination?.Key ?? "", 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.Information, "Removing route descriptor for '{destination}':'{inputPortKey}'", destination.Key ?? "", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
var descr = string.IsNullOrEmpty(inputPortKey)
? GetRouteDescriptorForDestination(destination)
: GetRouteDescriptorForDestinationAndInputPort(destination, inputPortKey);
if (descr != null)
RouteDescriptors.Remove(descr);
Debug.LogMessage(LogEventLevel.Information, "Found route descriptor {routeDescriptor}", destination, descr);
return descr;
}
}

View File

@@ -1,5 +1,6 @@
using PepperDash.Core;
using Serilog.Events;
using System;
namespace PepperDash.Essentials.Core
{
@@ -14,20 +15,27 @@ namespace PepperDash.Essentials.Core
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
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 == true)
try
{
return;
}
Debug.LogMessage(LogEventLevel.Information, "Handling cooldown route request: {destination}:{destinationPort} -> {source}:{sourcePort} {type}", null, Destination?.Key ?? "empty destination", DestinationPort?.Key ?? "no destination port", Source?.Key ?? "empty source", SourcePort?.Key ?? "empty source port", SignalType.ToString());
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 (args.BoolValue == true)
{
return;
}
if (sender is IWarmingCooling coolingDevice)
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;
}
} catch(Exception ex)
{
Debug.LogMessage(LogEventLevel.Debug, "Unsubscribing from cooling feedback for {destination}", null, Destination.Key);
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
Debug.LogMessage(ex, "Exception handling cooldown", Destination);
}
}
}

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,20 +161,21 @@ 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);
});
var source = sourceListItem.Value;
var sourceKey = sourceListItem.Key;
if (source == null)
{
@@ -186,15 +187,16 @@ namespace PepperDash.Essentials.Core.Routing
Name = sourceTieLine.SourcePort.Key,
};
destination.CurrentSourceInfo = tempSourceListItem; ;
destination.CurrentSourceInfoKey = "$transient";
destination.CurrentSourceInfo = tempSourceListItem;
return;
}
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {source}", this, source);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {@source} with key {sourceKey}", this, source, sourceKey);
destination.CurrentSourceInfoKey = sourceKey;
destination.CurrentSourceInfo = source;
destination.CurrentSourceInfoKey = source.SourceKey;
}
private TieLine GetRootTieLine(TieLine tieLine)
@@ -202,11 +204,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 +217,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 +228,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

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

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