Compare commits

..

7 Commits

Author SHA1 Message Date
jtalborough
b9382216c1 ci: Install 7-Zip for packaging 2024-04-26 12:16:34 -04:00
jtalborough
d75d355dc7 ci: Add CPZ and CPLZ to NuGet Package 2024-04-26 12:11:42 -04:00
jtalborough
7277ccc687 style: Update NuGet package source and add NonInteractive 2024-04-26 12:06:41 -04:00
jtalborough
2e4601b5f9 style: update nuget add command order 2024-04-26 11:59:36 -04:00
jtalborough
fe1804f9c7 feat: Inject CPZ into NuGet Package 2024-04-26 10:28:27 -04:00
jtalborough
faa07e682f feat: Add PackageOutputPath to msbuild command 2024-04-26 10:06:56 -04:00
jtalborough
a1af0bf943 feature: include build in nuget package output 2024-04-24 12:08:13 -04:00
149 changed files with 3045 additions and 5010 deletions

View File

@@ -1,21 +0,0 @@
name: Build PepperDash Essentials
on:
push:
branches:
- '**'
jobs:
getVersion:
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-getversion.yml@main
secrets: inherit
build-4Series:
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-4Series-builds.yml@main
secrets: inherit
needs: getVersion
if: needs.getVersion.outputs.newVersion == 'true'
with:
newVersion: ${{ needs.getVersion.outputs.newVersion }}
version: ${{ needs.getVersion.outputs.version }}
tag: ${{ needs.getVersion.outputs.tag }}
channel: ${{ needs.getVersion.outputs.channel }}

100
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: Branch Build Using Docker
on:
push:
branches:
- feature-2.0.0/*
- hotfix-2.0.0/*
- release-2.0.0/*
- development-2.0.0
env:
# solution path doesn't need slashes unless there it is multiple folders deep
# solution name does not include extension. .sln is assumed
SOLUTION_PATH: .
SOLUTION_FILE: PepperDash.Essentials
# Do not edit this, we're just creating it here
VERSION: 0.0.0-buildtype-buildnumber
# Defaults to debug for build type
BUILD_TYPE: Debug
# Defaults to main as the release branch. Change as necessary
RELEASE_BRANCH: main
jobs:
Build_Project_4-Series:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Set Version Number
id: setVersion
shell: powershell
run: |
$latestVersion = [version]"2.0.0"
$newVersion = [version]$latestVersion
$phase = ""
$newVersionString = ""
switch -regex ($Env:GITHUB_REF) {
'^refs\/pull\/*.' {
$phase = 'beta';
$newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER
}
'^refs\/heads\/hotfix-2.0.0\/*.' {
$phase = 'hotfix'
$newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER
}
'^refs\/heads\/release-2.0.0\/*.' {
$splitRef = $Env:GITHUB_REF -split "/"
$version = [version]($splitRef[-1] -replace "v", "")
$phase = 'rc'
$newVersionString = "{0}-{1}-{2}" -f $version, $phase, $Env:GITHUB_RUN_NUMBER
}
'^refs\/heads\/feature-2.0.0\/*.' {
$phase = 'alpha'
$newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER
}
'development-2.0.0' {
$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
- 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
run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" -m
- name: Create tag for non-rc builds
if: contains(steps.setVersion.outputs.version, 'alpha')
run: |
git tag ${{ steps.setVersion.outputs.version }}
git push --tags origin
# Create the release on the source repo
- name: Install 7-Zip
run: choco install 7zip.install -y
- name: Add CPZ and CPLZ to NuGet Package
run: |
7z a PepperDash.Essentials.${{ steps.setVersion.outputs.version }}.nupkg bin\Debug\*.cpz
7z a PepperDash.Essentials.${{ steps.setVersion.outputs.version }}.nupkg bin\Debug\*.cplz
- 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)'
generateReleaseNotes: true
prerelease: ${{contains('debug', env.BUILD_TYPE)}}
tag: ${{ steps.setVersion.outputs.version }}
- name: Setup Nuget
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
- 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

55
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: main Build using Docker
on:
release:
types:
- published
branches:
- main-2.0.0
env:
# solution path doesn't need slashes unless there it is multiple folders deep
# solution name does not include extension. .sln is assumed
SOLUTION_PATH: PepperDashEssentials
SOLUTION_FILE: PepperDashEssentials
# Do not edit this, we're just creating it here
VERSION: 0.0.0-buildtype-buildnumber
# Defaults to debug for build type
BUILD_TYPE: Release
# Defaults to main as the release branch. Change as necessary
RELEASE_BRANCH: main
jobs:
Build_Project:
runs-on: windows-2019
steps:
# First we checkout the source repo
- name: Checkout repo
uses: actions/checkout@v3
# Generate the appropriate version number
- name: Set Version Number
shell: powershell
id: setVersion
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: echo "VERSION=$($Env:TAG_NAME)" | 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
- name: Build Solution
run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" -m
- name: Upload Release
id: create_release
uses: ncipollo/release-action@v1
with:
updateRelease: true
artifacts: 'output\**\*.*(cpz|cplz)'
tag: ${{ steps.setVersion.outputs.version }}
- name: Setup Nuget
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
- 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

1
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@@ -0,0 +1,3 @@
[submodule "Essentials-Template-UI"]
path = Essentials-Template-UI
url = https://github.com/PepperDash/Essentials-Template-UI.git

View File

@@ -1,36 +0,0 @@
{
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"releaseRules": [
{ "scope": "force-patch", "release": "patch" },
{ "scope": "no-release", "release": false }
]
}
],
"@semantic-release/release-notes-generator",
["@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@semantic-release/exec",
{
"verifyReleaseCmd": "echo \"newVersion=true\" >> $GITHUB_OUTPUT",
"publishCmd": "echo \"version=${nextRelease.version}\" >> $GITHUB_OUTPUT && echo \"tag=${nextRelease.gitTag}\" >> $GITHUB_OUTPUT && echo \"type=${nextRelease.type}\" >> $GITHUB_OUTPUT && echo \"channel=${nextRelease.channel}\" >> $GITHUB_OUTPUT"
}
]
],
"branches": [
"main",
{"name": "development", "prerelease": "beta", "channel": "beta"},
{"name": "release", "prerelease": "rc", "channel": "rc"},
{
"name": "replace-me-feature-branch",
"prerelease": "replace-me-prerelease",
"channel": "replace-me-prerelease"
}
]
}

View File

@@ -91,8 +91,8 @@ we receive and the availability of resources to evaluate contributions, we antic
project remains dynamic and relevant. This may affect our responsiveness and ability to accept pull requests
quickly. This does not mean we are ignoring them.
- Not all innovative ideas need to be accepted as pull requests into this GitHub project to be valuable to the community.
There may be times when we recommend that you just share your code for some enhancement to Essentials from your own
repository. As we identify and recognize extensions that are of general interest to Essentials, we
There may be times when we recommend that you just share your code for some enhancement to Ghidra from your own
repository. As we identify and recognize extensions that are of general interest to the reverse engineering community, we
may seek to incorporate them with our baseline.
## Legal

View File

@@ -1,85 +0,0 @@
using System.Collections.Generic;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core.CrestronIO
{
/// <summary>
/// Wrapper class for CEN-IO-COM-Xxx expander module
/// </summary>
[Description("Wrapper class for the CEN-IO-COM-102 & CEN-IO-COM-202 expander module")]
public class CenIoComController : CrestronGenericBaseDevice, IComPorts
{
private readonly CenIoCom _cenIoCom;
public CenIoComController(string key, string name, CenIoCom cenIo)
:base(key, name, cenIo)
{
_cenIoCom = cenIo;
}
#region Implementation of IComPorts
public CrestronCollection<ComPort> ComPorts
{
get { return _cenIoCom.ComPorts; }
}
public int NumberOfComPorts
{
get { return _cenIoCom.NumberOfComPorts; }
}
#endregion
}
public class CenIoCom102ControllerFactory : EssentialsDeviceFactory<CenIoComController>
{
private const string CenIoCom102Type = "ceniocom102";
private const string CenIoCom202Type = "ceniocom202";
public CenIoCom102ControllerFactory()
{
TypeNames = new List<string> { CenIoCom102Type, CenIoCom202Type };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.Console(1, "Factory Attempting to create new CEN-IO-COM-Xxx Device");
var control = CommFactory.GetControlPropertiesConfig(dc);
if (control == null)
{
Debug.Console(1, "Factory failed to create a new CEN-IO-COM-Xxx Device, control properties not found");
return null;
}
var ipid = control.IpIdInt;
if (ipid < 2)
{
Debug.Console(1, "Factory failed to create a new CEN-IO-COM-Xxx Device, invalid IP-ID found");
return null;
}
switch (dc.Type)
{
case CenIoCom102Type:
{
return new CenIoComController(dc.Key, dc.Name, new CenIoCom102(ipid, Global.ControlSystem));
}
case CenIoCom202Type:
{
return new CenIoComController(dc.Key, dc.Name, new CenIoCom202(ipid, Global.ControlSystem));
}
default:
{
Debug.Console(1, "Factory failed to create a new CEN-IO-COM-Xxx Device, invalid type '{0}'", dc.Type);
return null;
}
}
}
}
}

View File

@@ -1,185 +0,0 @@
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash_Essentials_DM.Chassis
{
public class HdPsXxxAnalogAuxMixerController : IKeyed,
IHasVolumeControlWithFeedback, IHasMuteControlWithFeedback
{
public string Key { get; private set; }
private readonly HdPsXxxAnalogAuxMixer _mixer;
public HdPsXxxAnalogAuxMixerController(string parent, uint mixer, HdPsXxx chassis)
{
Key = string.Format("{0}-analogMixer{1}", parent, mixer);
_mixer = chassis.AnalogAuxiliaryMixer[mixer];
_mixer.AuxMixerPropertyChange += OnAuxMixerPropertyChange;
_mixer.AuxiliaryMuteControl.MuteAndVolumeControlPropertyChange += OnMuteAndVolumeControlPropertyChange;
VolumeLevelFeedback = new IntFeedback(() => VolumeLevel);
MuteFeedback = new BoolFeedback(() => IsMuted);
}
#region Volume
private void OnAuxMixerPropertyChange(object sender, GenericEventArgs args)
{
Debug.Console(2, this, "OnAuxMixerPropertyChange: {0} > Index-{1}, EventId-{2}", sender.ToString(), args.Index, args.EventId);
switch (args.EventId)
{
case MuteAndVolumeContorlEventIds.VolumeFeedbackEventId:
{
VolumeLevel = _mixer.VolumeFeedback.ShortValue;
break;
}
case MuteAndVolumeContorlEventIds.MuteOnEventId:
case MuteAndVolumeContorlEventIds.MuteOffEventId:
{
IsMuted = _mixer.AuxiliaryMuteControl.MuteOnFeedback.BoolValue;
break;
}
default:
{
Debug.Console(1, this, "OnAuxMixerPropertyChange: {0} > Index-{1}, EventId-{2} - unhandled eventId", sender.ToString(), args.Index, args.EventId);
break;
}
}
}
private const ushort CrestronLevelMin = 0;
private const ushort CrestronLevelMax = 65535;
private const int DeviceLevelMin = -800;
private const int DeviceLevelMax = 200;
private const int RampTime = 5000;
private int _volumeLevel;
public int VolumeLevel
{
get { return _volumeLevel; }
private set
{
var level = value;
_volumeLevel = CrestronEnvironment.ScaleWithLimits(level, DeviceLevelMax, DeviceLevelMin, CrestronLevelMax, CrestronLevelMin);
Debug.Console(1, this, "VolumeFeedback: level-'{0}', scaled-'{1}'", level, _volumeLevel);
VolumeLevelFeedback.FireUpdate();
}
}
public IntFeedback VolumeLevelFeedback { get; private set; }
public void SetVolume(ushort level)
{
var levelScaled = CrestronEnvironment.ScaleWithLimits(level, CrestronLevelMax, CrestronLevelMin, DeviceLevelMax, DeviceLevelMin);
Debug.Console(1, this, "SetVolume: level-'{0}', levelScaled-'{1}'", level, levelScaled);
_mixer.Volume.ShortValue = (short)levelScaled;
}
public void VolumeUp(bool pressRelease)
{
if (pressRelease)
{
_mixer.Volume.CreateSignedRamp(DeviceLevelMax, RampTime);
}
else
{
_mixer.Volume.StopRamp();
}
}
public void VolumeDown(bool pressRelease)
{
if (pressRelease)
{
_mixer.Volume.CreateSignedRamp(DeviceLevelMin, RampTime);
}
else
{
_mixer.Volume.StopRamp();
}
}
#endregion
#region Mute
private void OnMuteAndVolumeControlPropertyChange(MuteControl device, GenericEventArgs args)
{
Debug.Console(2, this, "OnMuteAndVolumeControlPropertyChange: {0} > Index-{1}, EventId-{2}", device.ToString(), args.Index, args.EventId);
switch (args.EventId)
{
case MuteAndVolumeContorlEventIds.VolumeFeedbackEventId:
{
VolumeLevel = _mixer.VolumeFeedback.ShortValue;
break;
}
case MuteAndVolumeContorlEventIds.MuteOnEventId:
case MuteAndVolumeContorlEventIds.MuteOffEventId:
{
IsMuted = _mixer.AuxiliaryMuteControl.MuteOnFeedback.BoolValue;
break;
}
default:
{
Debug.Console(1, this, "OnMuteAndVolumeControlPropertyChange: {0} > Index-{1}, EventId-{2} - unhandled eventId", device.ToString(), args.Index, args.EventId);
break;
}
}
}
private bool _isMuted;
public bool IsMuted
{
get { return _isMuted; }
set
{
_isMuted = value;
Debug.Console(1, this, "IsMuted: _isMuted-'{0}'", _isMuted);
MuteFeedback.FireUpdate();
}
}
public BoolFeedback MuteFeedback { get; private set; }
public void MuteOn()
{
_mixer.AuxiliaryMuteControl.MuteOn();
}
public void MuteOff()
{
_mixer.AuxiliaryMuteControl.MuteOff();
}
public void MuteToggle()
{
if (IsMuted)
MuteOff();
else
MuteOn();
}
#endregion
}
}

View File

@@ -1,167 +0,0 @@
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash_Essentials_DM.Chassis
{
public class HdPsXxxOutputAudioController : IKeyed,
IHasVolumeControlWithFeedback, IHasMuteControlWithFeedback
{
public string Key { get; private set; }
private readonly HdPsXxxHdmiDmLiteOutputMixer _mixer; // volume/volumeFeedback
private readonly HdPsXxxOutputPort _port; // mute/muteFeedback
public HdPsXxxOutputAudioController(string parent, uint output, HdPsXxx chassis)
{
Key = string.Format("{0}-audioOut{1}", parent, output);
_port = chassis.HdmiDmLiteOutputs[output].OutputPort;
_mixer = chassis.HdmiDmLiteOutputs[output].Mixer;
chassis.DMOutputChange += ChassisOnDmOutputChange;
VolumeLevelFeedback = new IntFeedback(() => VolumeLevel);
MuteFeedback = new BoolFeedback(() => IsMuted);
}
private void ChassisOnDmOutputChange(Switch device, DMOutputEventArgs args)
{
switch (args.EventId)
{
case (DMOutputEventIds.VolumeEventId):
{
Debug.Console(2, this, "HdPsXxxOutputAudioController: {0} > Index-{1}, Number-{3}, EventId-{2} - AudioMute/UnmuteEventId",
device.ToString(), args.Index, args.EventId, args.Number);
VolumeLevel = _mixer.VolumeFeedback.ShortValue;
break;
}
case DMOutputEventIds.MuteOnEventId:
case DMOutputEventIds.MuteOffEventId:
{
Debug.Console(2, this, "HdPsXxxOutputAudioController: {0} > Index-{1}, Number-{3}, EventId-{2} - MuteOnEventId/MuteOffEventId",
device.ToString(), args.Index, args.EventId, args.Number);
IsMuted = _port.MuteOnFeedback.BoolValue;
break;
}
default:
{
Debug.Console(1, this, "HdPsXxxOutputAudioController: {0} > Index-{1}, Number-{3}, EventId-{2} - unhandled eventId",
device.ToString(), args.Index, args.EventId, args.Number);
break;
}
}
}
#region Volume
private const ushort CrestronLevelMin = 0;
private const ushort CrestronLevelMax = 65535;
private const int DeviceLevelMin = -800;
private const int DeviceLevelMax = 200;
private const int RampTime = 5000;
private int _volumeLevel;
public int VolumeLevel
{
get { return _volumeLevel; }
private set
{
var level = value;
_volumeLevel = CrestronEnvironment.ScaleWithLimits(level, DeviceLevelMax, DeviceLevelMin, CrestronLevelMax, CrestronLevelMin);
Debug.Console(2, this, "VolumeFeedback: level-'{0}', scaled-'{1}'", level, _volumeLevel);
VolumeLevelFeedback.FireUpdate();
}
}
public IntFeedback VolumeLevelFeedback { get; private set; }
public void SetVolume(ushort level)
{
var levelScaled = CrestronEnvironment.ScaleWithLimits(level, CrestronLevelMax, CrestronLevelMin, DeviceLevelMax, DeviceLevelMin);
Debug.Console(1, this, "SetVolume: level-'{0}', levelScaled-'{1}'", level, levelScaled);
_mixer.Volume.ShortValue = (short)levelScaled;
}
public void VolumeUp(bool pressRelease)
{
if (pressRelease)
{
_mixer.Volume.CreateSignedRamp(DeviceLevelMax, RampTime);
}
else
{
_mixer.Volume.StopRamp();
}
}
public void VolumeDown(bool pressRelease)
{
if (pressRelease)
{
_mixer.Volume.CreateSignedRamp(DeviceLevelMin, RampTime);
}
else
{
_mixer.Volume.StopRamp();
}
}
#endregion
#region Mute
private bool _isMuted;
public bool IsMuted
{
get { return _isMuted; }
set
{
_isMuted = value;
Debug.Console(1, this, "IsMuted: _isMuted-'{0}'", _isMuted);
MuteFeedback.FireUpdate();
}
}
public BoolFeedback MuteFeedback { get; private set; }
public void MuteOn()
{
_port.MuteOn();
}
public void MuteOff()
{
_port.MuteOff();
}
public void MuteToggle()
{
if (IsMuted)
MuteOff();
else
MuteOn();
}
#endregion
}
}

View File

@@ -1,41 +1,26 @@
<Project>
<ItemGroup>
<None Include="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz" Condition="$(ProjectType) == 'Program'">
<None Include="$(PackageOutputPath)\$(AssemblyName)\*.cpz" Condition="$(ProjectType) == 'Program'">
<Pack>true</Pack>
<PackagePath>build;</PackagePath>
</None>
<None Include="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz" Condition="$(ProjectType) == 'ProgramLibrary'">
<None Include="$(PackageOutputPath)\$(AssemblyName)\*.cplz" Condition="$(ProjectType) == 'ProgramLibrary'">
<Pack>true</Pack>
<PackagePath>build;</PackagePath>
</None>
</ItemGroup>
<PropertyGroup Condition="$(ProjectType) == 'ProgramLibrary'">
<FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz</FileName>
</PropertyGroup>
<PropertyGroup Condition="$(ProjectType) == 'Program'">
<FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>
</PropertyGroup>
<Target Name="DeleteCPLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != '' And Exists($(FileName))">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete>
<Message Text="Deleted files: '@(DeletedList)'" />
</Target>
<Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ">
<Target Name="Create CPLZ" AfterTargets="Build; AfterRebuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''">
<Message Text="Creating CPLZ $(TargetDir)"></Message>
<MakeDir Directories="$(PackageOutputPath)" Condition="!Exists($(PackageOutputPath))" />
<ZipDirectory SourceDirectory="$(TargetDir)" DestinationFile="$(PackageOutputPath)\$(TargetName).$(Version).$(TargetFramework).cplz" Overwrite="true"/>
<Copy SourceFiles="$(PackageOutputPath)\$(TargetName).$(Version).$(TargetFramework).cplz" DestinationFiles="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz" />
<MakeDir Directories="$(PackageOutputPath)" Condition="!Exists($(PackageOutputPath))" />
<MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)" Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" />
<ZipDirectory SourceDirectory="$(TargetDir)" DestinationFile="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cplz" Overwrite="true"/>
</Target>
<Target Name="DeleteCPZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Program' And $(TargetDir) != '' And Exists($(FileName))">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete>
<Message Text="Deleted files: '@(DeletedList)'" />
</Target>
<Target Name="Copy CPZ" AfterTargets="SimplSharpPostProcess" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))">
<Target Name="Copy CPZ NET6" AfterTargets="SimplSharpPostProcess" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' == 'net6.0' ) Or ( '$(TargetFramework)' == 'net8.0' ))">
<Message Text="Copying CPZ"></Message>
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz" />
<Copy SourceFiles="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz" DestinationFiles="$(PackageOutputPath)\$(TargetName).$(Version).$(TargetFramework).cpz" />
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
</Target>
<Target Name="Copy CPZ NET47" AfterTargets="SimplSharpPostProcess47" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))">
<Message Text="Copying CPZ"></Message>
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
</Target>
</Project>

View File

@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using System.Reflection;
using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication;
@@ -18,6 +18,81 @@ 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>
@@ -93,15 +168,19 @@ namespace PepperDash.Essentials.Core.Bridges
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
if (device is IBridgeAdvanced bridge)
if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetCType()))
{
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);
continue;
}
Debug.LogMessage(LogEventLevel.Information, this,
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
device.Key);
var bridge = device as IBridgeAdvanced;
if (bridge != null)
{
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
}
}
}
@@ -170,11 +249,11 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public virtual void PrintJoinMaps()
{
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
Debug.LogMessage(LogEventLevel.Information, this, "Join Maps for EISC IPID: {0}", Eisc.ID.ToString("X"));
foreach (var joinMap in JoinMaps)
{
CrestronConsole.ConsoleCommandResponse("Join map for device '{0}':", joinMap.Key);
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}':", joinMap.Key);
joinMap.Value.PrintJoinMapInfo();
}
}

View File

@@ -1,66 +0,0 @@
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

@@ -1,4 +1,4 @@
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges
{

View File

@@ -93,7 +93,6 @@ namespace PepperDash.Essentials.Core
void OnDataReceived(string s)
{
var eventSubscribed = false;
var bytesHandler = BytesReceived;
if (bytesHandler != null)
@@ -102,7 +101,6 @@ 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)
@@ -110,10 +108,7 @@ 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

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,6 @@ using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core.Config
{
@@ -24,13 +23,7 @@ namespace PepperDash.Essentials.Core.Config
public Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; }
[JsonProperty("destinationLists")]
public Dictionary<string, Dictionary<string, DestinationListItem>> DestinationLists { get; set; }
[JsonProperty("audioControlPointLists")]
public Dictionary<string, AudioControlPointListItem> AudioControlPointLists { get; set; }
[JsonProperty("cameraLists")]
public Dictionary<string, Dictionary<string, CameraListItem>> CameraLists { get; set; }
public Dictionary<string, Dictionary<string,DestinationListItem>> DestinationLists { get; set; }
[JsonProperty("tieLines")]
public List<TieLineConfig> TieLines { get; set; }
@@ -44,8 +37,6 @@ namespace PepperDash.Essentials.Core.Config
Devices = new List<DeviceConfig>();
SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();
DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();
AudioControlPointLists = new Dictionary<string, AudioControlPointListItem>();
CameraLists = new Dictionary<string, Dictionary<string, CameraListItem>>();
TieLines = new List<TieLineConfig>();
JoinMaps = new Dictionary<string, JObject>();
}
@@ -55,7 +46,7 @@ namespace PepperDash.Essentials.Core.Config
/// </summary>
public Dictionary<string, SourceListItem> GetSourceListForKey(string key)
{
if (SourceLists == null || string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
if (string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
return null;
return SourceLists[key];
@@ -64,41 +55,17 @@ namespace PepperDash.Essentials.Core.Config
/// <summary>
/// Retrieves a DestinationListItem based on the key
/// </summary>
/// <param name="key">key of the list to retrieve</param>
/// <returns>DestinationList if the key exists, null otherwise</returns>
/// <param name="key">key of the item to retrieve</param>
/// <returns>DestinationListItem if the key exists, null otherwise</returns>
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
{
if (DestinationLists == null || string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
{
return null;
}
return DestinationLists[key];
}
/// <summary>
/// Retrieves a AudioControlPointList based on the key
/// </summary>
/// <param name="key">key of the list to retrieve</param>
/// <returns>AudioControlPointList if the key exists, null otherwise</returns>
public AudioControlPointListItem GetAudioControlPointListForKey(string key)
{
if (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

View File

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

View File

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

View File

@@ -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, IPartitionStateProvider
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput
{
public Versiport InputPort { get; private set; }
@@ -35,15 +35,10 @@ 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(() =>
{
@@ -57,8 +52,7 @@ 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);
@@ -71,10 +65,7 @@ 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

@@ -1,14 +0,0 @@
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

@@ -15,22 +15,10 @@ 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
public interface IHasInputs<TKey, TSelector>: IKeyName
{
ISelectableItems<T> Inputs { get; }
}
ISelectableItems<TKey> 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; }
void SetInput(TSelector selector);
}
}

View File

@@ -1,18 +0,0 @@
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

@@ -44,10 +44,7 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
void AddDeviceMessenger(IMobileControlMessenger messenger);
bool CheckForDeviceMessenger(string key);
IMobileControlRoomMessenger GetRoomMessenger(string key);
}
}
/// <summary>
/// Describes a mobile control messenger
@@ -109,11 +106,4 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
Action<string,string, JToken> Action { get; }
}
public interface IMobileControlTouchpanelController : IKeyed
{
string DefaultRoomKey { get; }
void SetAppUrl(string url);
bool UseDirectServer { get; }
bool ZoomRoomController { get; }
}
}

View File

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

View File

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

View File

@@ -1,22 +0,0 @@
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

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

View File

@@ -1,76 +0,0 @@
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,203 +1,131 @@
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.Reflection;
using System.Threading.Tasks;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using Newtonsoft.Json;
using PepperDash.Core;
using Serilog.Events;
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)
{
CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}");
}
}
DoDeviceAction(action);
}
catch (Exception ex)
{
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();
CType 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();
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);
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();
try
{
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey} with {@params}", null, method.Name, action.DeviceKey, action.Params);
var result = method.Invoke(obj, convertedParams);
// If the method returns a Task, await it
if (result is Task task)
{
await task;
}
// If the method returns a Task<T>, await it
else if (result != null && result.GetType().IsGenericType && result.GetType().GetGenericTypeDefinition() == typeof(Task<>))
{
await (Task)result;
}
}
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);
}
CType 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
@@ -208,10 +136,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().GetProperty(propertyName).GetValue(dev, null);
object prop = dev.GetType().GetCType().GetProperty(propertyName).GetValue(dev, null);
// var prop = t.GetProperty(propertyName);
if (prop != null)
@@ -225,126 +153,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
CType 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
CType 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('.');
/// <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;
}
var dev = DeviceManager.GetDeviceForKey(path[0]);
if (dev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]);
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);
}
// 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);
}
CType 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 (Crestron.SimplSharp.Reflection.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;
}
}
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;
}
}
else
obj = prop.GetValue(obj, null);
}
}
return obj;
}
/// <summary>
/// Sets a property on an object.
@@ -359,7 +287,7 @@ namespace PepperDash.Essentials.Core
//if (obj == null)
// return "{\"error\":\"No object found\"}";
//Type t = obj.GetType();
//CType t = obj.GetType();
//// get the properties and set them into a new collection of NameType wrappers
@@ -367,85 +295,78 @@ 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 DeviceActionWrapper
{
public string DeviceKey { get; set; }
public string MethodName { get; set; }
public object[] Params { get; set; }
}
public class PropertyNameType
{
object Parent;
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
[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
{
get
if (PropInfo.CanRead)
{
if (PropInfo.CanRead)
try
{
try
{
return PropInfo.GetValue(Parent, null).ToString();
}
catch (Exception)
{
return null;
}
return PropInfo.GetValue(Parent, null).ToString();
}
else
catch (Exception)
{
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 : CAttribute
{
}
}
}

View File

@@ -1,50 +1,50 @@
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
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);
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>();
//public static List<Device> Devices { get { return _Devices; } }
//static List<Device> _Devices = new List<Device>();
static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Returns a copy of all the devices in a list
/// </summary>
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
/// <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 bool AddDeviceEnabled;
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);
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,89 +52,77 @@ 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()
{
@@ -154,85 +142,77 @@ 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>
public static void DeactivateAll()
{
try
{
DeviceCriticalSection.Enter();
foreach (var d in Devices.Values.OfType<Device>())
{
d.Deactivate();
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
/// <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();
}
}
//static void ListMethods(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if(dev != null)
// {
// var type = dev.GetType().GetCType();
// 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());
// }
//}
//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());
// }
//}
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 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));
foreach (var d in sorted)
{
var name = d is IKeyName ? (d as IKeyName).Name : "---";
Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name);
}
}
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 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);
}
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)
//static void ListDeviceCommands(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if (dev == null)
@@ -243,135 +223,134 @@ namespace PepperDash.Essentials.Core
// Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey);
//}
private static void ListDeviceCommStatuses(string input)
{
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
{
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}");
}
}
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());
}
//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);
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 (newDev is EssentialsDevice essentialsDev)
essentialsDev.Initialized += DeviceManager_Initialized;
}
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 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;
}
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();
}
}
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
{
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
@@ -388,7 +367,8 @@ namespace PepperDash.Essentials.Core
}
//Debug.LogMessage(LogEventLevel.Verbose, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value);
if (!(GetDeviceForKey(match.Groups[1].Value) is ComPortController com))
var com = GetDeviceForKey(match.Groups[1].Value) as ComPortController;
if (com == null)
{
CrestronConsole.ConsoleCommandResponse("'{0}' is not a comm port device", match.Groups[1].Value);
return;
@@ -443,15 +423,16 @@ 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 (!(GetDeviceForKey(deviceKey) is IStreamDebugging device))
if (device == null)
{
CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey);
return;
@@ -498,11 +479,13 @@ namespace PepperDash.Essentials.Core
{
foreach (var device in AllDevices)
{
if (device is IStreamDebugging streamDevice)
var streamDevice = device as IStreamDebugging;
if (streamDevice != null)
{
streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off);
}
}
}
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using System.Reflection;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
@@ -17,24 +17,6 @@ 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)
{
@@ -59,8 +41,6 @@ namespace PepperDash.Essentials.Core
try
{
Initialize();
IsInitialized = true;
}
catch (Exception ex)
{

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ using Crestron.SimplSharpPro;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Core;
using Serilog.Events;

View File

@@ -5,6 +5,7 @@ using Crestron.SimplSharpPro;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Core;
using Serilog.Events;
@@ -69,7 +70,7 @@ namespace PepperDash.Essentials.Core.Devices
{
public LaptopFactory()
{
TypeNames = new List<string>() { "deprecated" };
TypeNames = new List<string>() { "laptop" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)

View File

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

View File

@@ -1,121 +1,128 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core;
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
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;
}
}
/// <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;
private Device _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;
}
}
/// <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>
/// 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; }
@@ -137,46 +144,25 @@ namespace PepperDash.Essentials.Core
[JsonProperty("isAudioSource")]
public bool IsAudioSource { get; set; }
/// <summary>
/// Hide source on UI when Avanced Sharing is enabled
/// </summary>
[JsonProperty("disableAdvancedRouting")]
public bool DisableAdvancedRouting { get; set; }
public SourceListItem()
{
Icon = "Blank";
}
/// <summary>
/// Hide source on UI when Simpl Sharing is enabled
/// </summary>
[JsonProperty("disableSimpleRouting")]
public bool DisableSimpleRouting { get; set; }
}
public SourceListItem()
{
Icon = "Blank";
}
public class SourceRouteListItem
{
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
public override string ToString()
{
return $"{SourceKey}:{Name}";
}
}
[JsonProperty("destinationKey")]
public string DestinationKey { 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("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
@@ -186,12 +172,7 @@ namespace PepperDash.Essentials.Core
defaultDisplay,
leftDisplay,
rightDisplay,
centerDisplay,
programAudio,
codecContent,
frontLeftDisplay,
frontRightDisplay,
rearLeftDisplay,
rearRightDisplay,
codecContent
}
}

View File

@@ -10,6 +10,7 @@ using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
namespace PepperDash.Essentials.Core

View File

@@ -1,23 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using Crestron.SimplSharpPro.DM.Endpoints;
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.Core
{
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking
{
public event SourceInfoChangeHandler CurrentSourceChange;
public event InputChangedEventHandler InputChanged;
public string CurrentSourceInfoKey { get; set; }
public SourceListItem CurrentSourceInfo
@@ -94,9 +96,7 @@ namespace PepperDash.Essentials.Core
}
}
public RoutingInputPort CurrentInputPort => throw new NotImplementedException();
public abstract void ExecuteSwitch(object selector);
public abstract void ExecuteSwitch(object selector);
protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey,
EiscApiAdvanced bridge)
@@ -266,16 +266,16 @@ namespace PepperDash.Essentials.Core
abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }
// public static MockDisplay DefaultDisplay
// {
// get
// {
// if (_DefaultDisplay == null)
// _DefaultDisplay = new MockDisplay("default", "Default Display");
// return _DefaultDisplay;
// }
//}
//static MockDisplay _DefaultDisplay;
public static MockDisplay DefaultDisplay
{
get
{
if (_DefaultDisplay == null)
_DefaultDisplay = new MockDisplay("default", "Default Display");
return _DefaultDisplay;
}
}
static MockDisplay _DefaultDisplay;
public TwoWayDisplayBase(string key, string name)
: base(key, name)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronXml;
@@ -10,10 +14,6 @@ using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PepperDash.Essentials.Core.Fusion
{

View File

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

View File

@@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Reflection;
using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp;
@@ -107,20 +107,19 @@ namespace PepperDash.Essentials.Core
}
protected void AddJoins(Type type)
{
{
var fields =
type.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true)).ToList();
Debug.LogMessage(LogEventLevel.Debug, "Got {fields} with JoinNameAttribute", fields.Count);
type.GetCType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true));
foreach (var field in fields)
{
var childClass = Convert.ChangeType(this, type, null);
//this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
var value = field.GetValue(childClass) as JoinDataComplete; //this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
if (!(field.GetValue(childClass) is JoinDataComplete value))
if (value == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to cast base class to {0}", type.Name);
continue;
@@ -130,7 +129,7 @@ namespace PepperDash.Essentials.Core
var joinName = value.GetNameAttribute(field);
if (string.IsNullOrEmpty(joinName)) continue;
if (String.IsNullOrEmpty(joinName)) continue;
Joins.Add(joinName, value);
}
@@ -156,37 +155,29 @@ namespace PepperDash.Essentials.Core
{
var sb = new StringBuilder();
var lineEnding = "\r\n";
// 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.HasFlag(eJoinType.Digital))
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 analogs = Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Analog))
var analogs =
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == 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.HasFlag(eJoinType.Serial))
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Serial) == 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);
@@ -211,7 +202,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))
{
@@ -239,7 +230,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} |\r\n";
const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |";
const int joinNumberLen = 11;
const int joinSpanLen = 9;
const int typeLen = 19;
@@ -247,25 +238,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.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, ' ')));
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, ' ')));
//build table seperator
sb.Append(string.Format(stringFormatter,
new string('-', joinNumberLen),
new string('-', joinSpanLen),
new string('-', descriptionLen),
new string('-', typeLen),
new string('-', capabilitiesLen)));
sb.AppendLine(String.Format(stringFormatter,
new String('-', joinNumberLen),
new String('-', joinSpanLen),
new String('-', descriptionLen),
new String('-', typeLen),
new String('-', capabilitiesLen)));
foreach (var join in joins)
{
sb.Append(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
sb.AppendLine(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
}
sb.Append("\r\n");
sb.AppendLine();
return sb;
}
@@ -420,10 +411,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";
@@ -439,13 +430,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"],
@@ -463,8 +454,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"],
@@ -510,7 +501,7 @@ namespace PepperDash.Essentials.Core
public string GetNameAttribute(MemberInfo memberInfo)
{
var name = string.Empty;
var attribute = (JoinNameAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
var attribute = (JoinNameAttribute)CAttribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
if (attribute == null) return name;
@@ -523,13 +514,13 @@ namespace PepperDash.Essentials.Core
[AttributeUsage(AttributeTargets.All)]
public class JoinNameAttribute : Attribute
public class JoinNameAttribute : CAttribute
{
private string _Name;
public JoinNameAttribute(string name)
{
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}",null, name);
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}", name);
_Name = name;
}

View File

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

View File

@@ -1,5 +1,8 @@
using PepperDash.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core
{
@@ -14,36 +17,9 @@ namespace PepperDash.Essentials.Core
{
private IPartitionStateProvider _partitionSensor;
public bool IsInAutoMode { get; private set; }
private bool isInAutoMode;
private bool _partitionPresent;
public bool PartitionPresent
{
get
{
if (IsInAutoMode)
{
return _partitionSensor.PartitionPresentFeedback.BoolValue;
}
return _partitionPresent;
}
set
{
if (_partitionPresent == value)
{
return;
}
_partitionPresent = value;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.FireUpdate();
}
}
}
private bool partitionPresent;
public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys)
{
@@ -74,11 +50,11 @@ namespace PepperDash.Essentials.Core
PartitionPresentFeedback.FireUpdate();
}
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
if (IsInAutoMode)
if (isInAutoMode)
{
PartitionPresent = e.BoolValue;
PartitionPresentFeedback.FireUpdate();
}
}
@@ -88,9 +64,7 @@ namespace PepperDash.Essentials.Core
public void SetAutoMode()
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Auto Mode", this);
IsInAutoMode = true;
isInAutoMode = true;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
@@ -102,64 +76,52 @@ namespace PepperDash.Essentials.Core
if (_partitionSensor != null)
{
_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;
isInAutoMode = false;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.SetValueFunc(() => _partitionPresent);
PartitionPresentFeedback.SetValueFunc(() => partitionPresent);
}
else
{
PartitionPresentFeedback = new BoolFeedback(() => _partitionPresent);
PartitionPresentFeedback = new BoolFeedback(() => partitionPresent);
}
if (_partitionSensor != null)
{
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;
}
PartitionPresentFeedback.FireUpdate();
}
public void SetPartitionStatePresent()
{
if (!IsInAutoMode)
if (!isInAutoMode)
{
PartitionPresent = true;
partitionPresent = true;
PartitionPresentFeedback.FireUpdate();
}
}
public void SetPartitionStateNotPresent()
{
if (!IsInAutoMode)
if (!isInAutoMode)
{
PartitionPresent = false;
partitionPresent = false;
PartitionPresentFeedback.FireUpdate();
}
}
public void ToggglePartitionState()
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Toggling Partition State for {Key}", this);
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"IsInAutoMode: {IsInAutoMode}", this);
if (!IsInAutoMode)
if (!isInAutoMode)
{
PartitionPresent = !PartitionPresent;
partitionPresent = !partitionPresent;
PartitionPresentFeedback.FireUpdate();
}
}

View File

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

View File

@@ -4,16 +4,13 @@
<Configurations>Debug;Release;Debug 4.7.2</Configurations>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<TargetFrameworks>net472;net6</TargetFrameworks>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
<AssemblyName>PepperDash_Essentials_Core</AssemblyName>
<RootNamespace>PepperDash.Essentials.Core</RootNamespace>
<Title>PepperDash Essentials Core</Title>
<PackageId>PepperDash.Essentials.Core</PackageId>
<InformationalVersion>$(Version)</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>2.0.0-local</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
@@ -25,8 +22,8 @@
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" />
<PackageReference Include="PepperDashCore" Version="2.0.1" />
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.42" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-402" />
</ItemGroup>
<ItemGroup>
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />

View File

@@ -0,0 +1,432 @@
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PepperDash.Essentials.Core</RootNamespace>
<AssemblyName>PepperDash_Essentials_Core</AssemblyName>
<ProjectTypeGuids>{0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PlatformFamilyName>WindowsCE</PlatformFamilyName>
<PlatformID>E2BECB1F-8C8C-41ba-B736-9BE7D946A398</PlatformID>
<OSVersion>5.0</OSVersion>
<DeployDirSuffix>SmartDeviceProject1</DeployDirSuffix>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<NativePlatformName>Windows CE</NativePlatformName>
<FormFactorID>
</FormFactorID>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<FileAlignment>512</FileAlignment>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<FileAlignment>512</FileAlignment>
<NoStdLib>true</NoStdLib>
<NoConfig>true</NoConfig>
<GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="Crestron.SimplSharpPro.DeviceSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.DM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.EthernetCommunications, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.Fusion, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.Gateways, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.GeneralIO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.Remotes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.ThreeSeriesCards, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.ThreeSeriesCards.dll</HintPath>
</Reference>
<Reference Include="Crestron.SimplSharpPro.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="PepperDash_Core, Version=1.2.1.30543, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SimplSharpCWSHelperInterface, Version=2.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCWSHelperInterface.dll</HintPath>
</Reference>
<Reference Include="SimplSharpHelperInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SimplSharpNewtonsoft, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll</HintPath>
</Reference>
<Reference Include="SimplSharpPro, Version=1.5.3.17, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SimplSharpReflectionInterface, Version=1.0.5583.25238, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll</HintPath>
</Reference>
<Reference Include="SimplSharpTimerEventInterface, Version=1.0.6197.20052, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<Compile Include="Bridges\BridgeBase.cs" />
<Compile Include="Bridges\IBridge.cs" />
<Compile Include="Bridges\JoinMaps\AirMediaControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\AppleTvJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\GenericIrControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\HdPsXxxControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\IAnalogInputJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\IDigitalOutputJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\PduJoinMapBase.cs" />
<Compile Include="Bridges\JoinMaps\C2nRthsControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\CameraControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\CenOdtOccupancySensorBaseJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DigitalLoggerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DisplayControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmBladeChassisControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmChassisControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmpsAudioOutputControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmpsMicrophoneControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmpsRoutingControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmRmcControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\DmTxControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\GenericLightingJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\GenericRelayControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\GlsOccupancySensorBaseJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\GlsPartitionSensorJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\HdMdNxM4kEControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\HdMdxxxCEControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\Hrxxx0WirelessRemoteControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\IBasicCommunicationJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\IDigitalInputJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\IRBlurayBaseJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\SetTopBoxControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\StatusSignControllerJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\SystemMonitorJoinMap.cs" />
<Compile Include="Bridges\JoinMaps\VideoCodecControllerJoinMap.cs" />
<Compile Include="Comm and IR\CecPortController.cs" />
<Compile Include="Comm and IR\CommFactory.cs" />
<Compile Include="Comm and IR\CommunicationExtras.cs" />
<Compile Include="Comm and IR\ComPortController.cs" />
<Compile Include="Comm and IR\ComSpecJsonConverter.cs" />
<Compile Include="Comm and IR\ConsoleCommMockDevice.cs" />
<Compile Include="Comm and IR\GenericComm.cs" />
<Compile Include="Comm and IR\GenericHttpClient.cs" />
<Compile Include="Comm and IR\IRPortHelper.cs" />
<Compile Include="Config\Essentials\ConfigUpdater.cs" />
<Compile Include="Config\Essentials\ConfigReader.cs" />
<Compile Include="Config\Essentials\ConfigWriter.cs" />
<Compile Include="Config\Essentials\EssentialsConfig.cs" />
<Compile Include="Config\SourceDevicePropertiesConfigBase.cs" />
<Compile Include="Crestron IO\C2nIo\C2nIoController.cs" />
<Compile Include="Crestron IO\C2nRts\C2nRthsController.cs" />
<Compile Include="Crestron IO\Cards\C3CardControllerBase.cs" />
<Compile Include="Crestron IO\Cards\C3Com3Controller.cs" />
<Compile Include="Crestron IO\Cards\C3Io16Controller.cs" />
<Compile Include="Crestron IO\Cards\C3Ir8Controller.cs" />
<Compile Include="Crestron IO\Cards\C3Ry16Controller.cs" />
<Compile Include="Crestron IO\Cards\C3Ry8Controller.cs" />
<Compile Include="Crestron IO\Cards\CenCi31Controller.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Crestron IO\Cards\CenCi33Controller.cs" />
<Compile Include="Crestron IO\Cards\InternalCardCageController.cs" />
<Compile Include="Crestron IO\DinCenCn\DinCenCnController.cs" />
<Compile Include="Crestron IO\DinCenCn\IHasCresnetBranches.cs" />
<Compile Include="Crestron IO\DinIo8\DinIo8Controller.cs" />
<Compile Include="Crestron IO\Inputs\CenIoDigIn104Controller.cs" />
<Compile Include="Crestron IO\Inputs\GenericVersiportAnalogInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\GenericDigitalInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\GenericVersiportInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\IAnalogInput.cs" />
<Compile Include="Crestron IO\Inputs\IDigitalInput.cs" />
<Compile Include="Crestron IO\IOPortConfig.cs" />
<Compile Include="Crestron IO\Ir\CenIoIr104Controller.cs" />
<Compile Include="Crestron IO\Outputs\GenericVersiportOutputDevice.cs" />
<Compile Include="Crestron IO\Outputs\IDigitalOutput.cs" />
<Compile Include="Crestron IO\Relay\CenIoRy104Controller.cs" />
<Compile Include="Crestron IO\Relay\GenericRelayDevice.cs" />
<Compile Include="Crestron IO\Relay\ISwitchedOutput.cs" />
<Compile Include="Crestron IO\StatusSign\StatusSignController.cs" />
<Compile Include="Device Info\NetworkDeviceHelpers.cs" />
<Compile Include="Devices\PowerInterfaces.cs" />
<Compile Include="Web\RequestHandlers\AppDebugRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\GetFeedbacksForDeviceRequestHandler.cs" />
<Compile Include="Web\EssentialsWebApiHelpers.cs" />
<Compile Include="Web\RequestHandlers\GetTypesByFilterRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\GetJoinMapForDeviceKeyRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\DefaultRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\DevListRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\DevPropsRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\DevJsonRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\SetDeviceStreamDebugRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\DisableAllStreamDebugRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\ShowConfigRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\GetTypesRequestHandler.cs" />
<Compile Include="Web\RequestHandlers\GetJoinMapForBridgeKeyRequestHandler.cs" />
<Compile Include="Web\EssemtialsWebApi.cs" />
<Compile Include="Web\EssentialsWebApiFactory.cs" />
<Compile Include="Web\EssentialsWebApiPropertiesConfig.cs" />
<Compile Include="Web\RequestHandlers\ReportVersionsRequestHandler.cs" />
<Compile Include="Device Info\DeviceInfo.cs" />
<Compile Include="Device Info\DeviceInfoEventArgs.cs" />
<Compile Include="Device Info\IDeviceInfoProvider.cs" />
<Compile Include="Devices\CodecInterfaces.cs" />
<Compile Include="Devices\CrestronProcessor.cs" />
<Compile Include="Devices\DestinationListItem.cs" />
<Compile Include="Devices\DeviceApiBase.cs" />
<Compile Include="Devices\DeviceFeedbackExtensions.cs" />
<Compile Include="Devices\EssentialsBridgeableDevice.cs" />
<Compile Include="Devices\EssentialsDevice.cs" />
<Compile Include="Devices\GenericIRController.cs" />
<Compile Include="Devices\IDspPreset.cs" />
<Compile Include="Devices\IProjectorInterfaces.cs" />
<Compile Include="Devices\IReconfigurableDevice.cs" />
<Compile Include="Devices\PC\InRoomPc.cs" />
<Compile Include="Devices\PC\Laptop.cs" />
<Compile Include="Devices\PduInterfaces.cs" />
<Compile Include="Devices\ReconfigurableDevice.cs" />
<Compile Include="Devices\VolumeDeviceChangeEventArgs.cs" />
<Compile Include="DeviceTypeInterfaces\IPasswordPrompt.cs" />
<Compile Include="DeviceTypeInterfaces\ITvPresetsProvider.cs" />
<Compile Include="DeviceTypeInterfaces\LanguageLabel.cs" />
<Compile Include="DeviceTypeInterfaces\ILanguageProvider.cs" />
<Compile Include="DeviceTypeInterfaces\IHasBranding.cs" />
<Compile Include="DeviceTypeInterfaces\ILanguageDefinition.cs" />
<Compile Include="DeviceTypeInterfaces\IHasFarEndContentStatus.cs" />
<Compile Include="DeviceTypeInterfaces\IHasPhoneDialing.cs" />
<Compile Include="DeviceTypeInterfaces\IMobileControl.cs" />
<Compile Include="Extensions\JsonExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Factory\DeviceFactory.cs" />
<Compile Include="Factory\IDeviceFactory.cs" />
<Compile Include="Factory\ReadyEventArgs.cs" />
<Compile Include="Feedbacks\BoolFeedback.cs" />
<Compile Include="Feedbacks\FeedbackCollection.cs" />
<Compile Include="Feedbacks\FeedbackEventArgs.cs" />
<Compile Include="Feedbacks\IntFeedback.cs" />
<Compile Include="Feedbacks\SerialFeedback.cs" />
<Compile Include="Feedbacks\StringFeedback.cs" />
<Compile Include="File\FileIO.cs" />
<Compile Include="Fusion\EssentialsHuddleSpaceFusionSystemControllerBase.cs" />
<Compile Include="Fusion\FusionCustomPropertiesBridge.cs" />
<Compile Include="Fusion\FusionEventHandlers.cs" />
<Compile Include="Fusion\FusionProcessorQueries.cs" />
<Compile Include="Fusion\EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs" />
<Compile Include="Fusion\FusionRviDataClasses.cs" />
<Compile Include="Gateways\CenRfgwController.cs" />
<Compile Include="Gateways\EssentialsRfGatewayConfig.cs" />
<Compile Include="Global\EthernetAdapterInfo.cs" />
<Compile Include="Interfaces\ILogStrings.cs" />
<Compile Include="Interfaces\ILogStringsWithLevel.cs" />
<Compile Include="Occupancy\GlsOccupancySensorPropertiesConfig.cs" />
<Compile Include="Occupancy\GlsOirOccupancySensorController.cs" />
<Compile Include="PartitionSensor\EssentialsPartitionController.cs" />
<Compile Include="PartitionSensor\GlsPartitionSensorPropertiesConfig.cs" />
<Compile Include="PartitionSensor\IPartitionStateProvider.cs" />
<Compile Include="Occupancy\OccupancyAggregatorConfig.cs" />
<Compile Include="Queues\ComsMessage.cs" />
<Compile Include="Queues\ProcessStringMessage.cs" />
<Compile Include="Queues\GenericQueue.cs" />
<Compile Include="Global\JobTimer.cs" />
<Compile Include="Global\Scheduler.cs" />
<Compile Include="Queues\IQueue.cs" />
<Compile Include="JoinMaps\JoinMapBase.cs" />
<Compile Include="Lighting\Lighting Interfaces.cs" />
<Compile Include="Lighting\LightingBase.cs" />
<Compile Include="Monitoring\SystemMonitorController.cs" />
<Compile Include="Microphone Privacy\MicrophonePrivacyController.cs" />
<Compile Include="Microphone Privacy\MicrophonePrivacyControllerConfig.cs" />
<Compile Include="Occupancy\CenOdtOccupancySensorBaseController.cs" />
<Compile Include="Occupancy\GlsOccupancySensorBaseController.cs" />
<Compile Include="Occupancy\GlsOdtOccupancySensorController.cs" />
<Compile Include="Occupancy\IOccupancyStatusProviderAggregator.cs" />
<Compile Include="PartitionSensor\GlsPartitionSensorController.cs" />
<Compile Include="Plugins\PluginLoader.cs" />
<Compile Include="Presets\PresetBase.cs" />
<Compile Include="Plugins\IPluginDeviceFactory.cs" />
<Compile Include="Queues\IQueueMessage.cs" />
<Compile Include="Ramps and Increments\ActionIncrementer.cs" />
<Compile Include="Config\BasicConfig.cs" />
<Compile Include="Config\ConfigPropertiesHelpers.cs" />
<Compile Include="Config\InfoConfig.cs" />
<Compile Include="Config\DeviceConfig.cs" />
<Compile Include="Devices\DisplayUiConstants.cs" />
<Compile Include="Devices\IUsageTracking.cs" />
<Compile Include="Devices\DeviceJsonApi.cs" />
<Compile Include="Devices\SourceListItem.cs" />
<Compile Include="DeviceTypeInterfaces\IDisplayBasic.cs" />
<Compile Include="DeviceTypeInterfaces\IDumbSource.cs" />
<Compile Include="DeviceTypeInterfaces\IWarmingCooling.cs" />
<Compile Include="DeviceTypeInterfaces\IDiscPlayerControls.cs" />
<Compile Include="DeviceTypeInterfaces\IPower.cs" />
<Compile Include="DeviceTypeInterfaces\IUiDisplayInfo.cs" />
<Compile Include="DeviceTypeInterfaces\ISetTopBoxControls.cs" />
<Compile Include="DeviceTypeInterfaces\IChannel.cs" />
<Compile Include="DeviceTypeInterfaces\IColorFunctions.cs" />
<Compile Include="DeviceTypeInterfaces\IDPad.cs" />
<Compile Include="DeviceTypeInterfaces\IDvr.cs" />
<Compile Include="DeviceTypeInterfaces\Template.cs" />
<Compile Include="DeviceTypeInterfaces\ITransport.cs" />
<Compile Include="Devices\GenericMonitoredTcpDevice.cs" />
<Compile Include="DeviceTypeInterfaces\INumeric.cs" />
<Compile Include="Devices\IVolumeAndAudioInterfaces.cs" />
<Compile Include="Display\BasicIrDisplay.cs" />
<Compile Include="Feedbacks\BoolFeedbackOneShot.cs" />
<Compile Include="Ramps and Increments\NumericalHelpers.cs" />
<Compile Include="Ramps and Increments\UshortSigIncrementer.cs" />
<Compile Include="Remotes\ButtonExtensions.cs" />
<Compile Include="Remotes\CrestronRemotePropertiesConfig.cs" />
<Compile Include="Remotes\Hrxx0WirelessRemoteController.cs" />
<Compile Include="Room\Behaviours\RoomOnToDefaultSourceWhenOccupied.cs" />
<Compile Include="Room\Combining\EssentialsRoomCombiner.cs" />
<Compile Include="Room\Combining\EssentialsRoomCombinerPropertiesConfig.cs" />
<Compile Include="Room\Combining\IEssentialsRoomCombiner.cs" />
<Compile Include="Room\Combining\RoomCombinationScenario.cs" />
<Compile Include="Room\EssentialsRoomBase.cs" />
<Compile Include="Room\Config\EssentialsRoomScheduledEventsConfig.cs" />
<Compile Include="Room\IEssentialsRoom.cs" />
<Compile Include="Room\Interfaces.cs" />
<Compile Include="Room\iOccupancyStatusProvider.cs" />
<Compile Include="Routing\DummyRoutingInputsDevice.cs" />
<Compile Include="Routing\ICardPortsDevice.cs" />
<Compile Include="InUseTracking\IInUseTracking.cs" />
<Compile Include="InUseTracking\InUseTracking.cs" />
<Compile Include="Routing\IRoutingInputsExtensions.cs" />
<Compile Include="Monitoring\StatusMonitorCollection.cs" />
<Compile Include="Monitoring\CrestronGenericBaseCommunicationMonitor.cs" />
<Compile Include="Monitoring\StatusMonitorBase.cs" />
<Compile Include="Monitoring\Interfaces.cs" />
<Compile Include="Monitoring\GenericCommunicationMonitor.cs" />
<Compile Include="Devices\AudioInterfaces.cs" />
<Compile Include="Devices\IAttachVideoStatusExtensions.cs" />
<Compile Include="Devices\IHasFeedbacks.cs" />
<Compile Include="Devices\SmartObjectBaseTypes.cs" />
<Compile Include="Devices\PresentationDeviceType.cs" />
<Compile Include="Display\MockDisplay.cs" />
<Compile Include="Ethernet\EthernetStatistics.cs" />
<Compile Include="Global\Global.cs" />
<Compile Include="License\EssentialsLicenseManager.cs" />
<Compile Include="Feedbacks\BoolOutputLogicals.cs" />
<Compile Include="Presets\Interfaces.cs" />
<Compile Include="Presets\PresetsListSubpageReferenceListItem.cs" />
<Compile Include="Presets\DevicePresetsView.cs" />
<Compile Include="Presets\PresetChannel.cs" />
<Compile Include="Presets\DevicePresets.cs" />
<Compile Include="Routing\RoutingInterfaces.cs" />
<Compile Include="Routing\RoutingPort.cs" />
<Compile Include="Routing\RoutingPortCollection.cs" />
<Compile Include="Feedbacks\BoolFeedbackPulseExtender.cs" />
<Compile Include="Routing\RoutingPortNames.cs" />
<Compile Include="Routing\TieLineConfig.cs" />
<Compile Include="Secrets\CrestronGlobalSecretsProvider.cs" />
<Compile Include="Secrets\CrestronLocalSecretsProvider.cs" />
<Compile Include="Secrets\CrestronSecret.cs" />
<Compile Include="Secrets\Interfaces.cs" />
<Compile Include="Secrets\SecretsManager.cs" />
<Compile Include="Secrets\SecretsPropertiesConfig.cs" />
<Compile Include="Shades\Shade Interfaces.cs" />
<Compile Include="Shades\ShadeBase.cs" />
<Compile Include="Shades\ShadeController.cs" />
<Compile Include="SmartObjects\SmartObjectNumeric.cs" />
<Compile Include="SmartObjects\SmartObjectDynamicList.cs" />
<Compile Include="SmartObjects\SmartObjectDPad.cs" />
<Compile Include="SmartObjects\SmartObjectHelperBase.cs" />
<Compile Include="Routing\TieLine.cs" />
<Compile Include="Queues\StringResponseProcessor.cs" />
<Compile Include="Timers\CountdownTimer.cs" />
<Compile Include="Timers\RetriggerableTimer.cs" />
<Compile Include="Touchpanels\CrestronTouchpanelPropertiesConfig.cs" />
<Compile Include="Touchpanels\Interfaces.cs" />
<Compile Include="Touchpanels\Keyboards\HabaneroKeyboardController.cs" />
<Compile Include="Touchpanels\Mpc3Touchpanel.cs" />
<Compile Include="Touchpanels\TriListExtensions.cs" />
<Compile Include="UI PageManagers\BlurayPageManager.cs" />
<Compile Include="UI PageManagers\SetTopBoxThreePanelPageManager.cs" />
<Compile Include="UI PageManagers\SinglePageManager.cs" />
<Compile Include="UI PageManagers\PageManager.cs" />
<Compile Include="UI PageManagers\SetTopBoxTwoPanelPageManager.cs" />
<Compile Include="UI\TouchpanelBase.cs" />
<Compile Include="Utilities\ActionSequence.cs" />
<Compile Include="VideoStatus\VideoStatusOutputs.cs" />
<Compile Include="Crestron\CrestronGenericBaseDevice.cs" />
<Compile Include="DeviceControlsParentInterfaces\IPresentationSource.cs" />
<Compile Include="Devices\DeviceManager.cs" />
<Compile Include="Devices\IrOutputPortController.cs" />
<Compile Include="Display\DisplayBase.cs" />
<Compile Include="Feedbacks\FeedbackBase.cs" />
<Compile Include="Room\Room.cs" />
<Compile Include="SmartObjects\SubpageReferencList\SourceListSubpageReferenceList.cs" />
<Compile Include="Touchpanels\ModalDialog.cs" />
<Compile Include="TriListBridges\HandlerBridge.cs" />
<Compile Include="Devices\FIND HOMES Interfaces.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SigHelper.cs" />
<Compile Include="SmartObjects\SubpageReferencList\SubpageReferenceList.cs" />
<Compile Include="SmartObjects\SubpageReferencList\SubpageReferenceListItem.cs" />
<None Include="app.config" />
<None Include="Properties\ControlSystem.cfg" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
</VisualStudio>
</ProjectExtensions>
<PropertyGroup>
<PostBuildEvent>rem S# Pro preparation will execute after these operations</PostBuildEvent>
</PropertyGroup>
</Project>

View File

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

View File

@@ -130,8 +130,6 @@ 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

@@ -1,12 +1,11 @@
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
@@ -18,40 +17,12 @@ namespace PepperDash.Essentials.Core
private List<IEssentialsRoom> _rooms;
public List<IKeyName> Rooms
{
get
{
return _rooms.Cast<IKeyName>().ToList();
}
}
private bool _isInAutoMode;
public bool IsInAutoMode
{
get
{
return _isInAutoMode;
}
set
{
if (value == _isInAutoMode)
{
return;
}
_isInAutoMode = value;
IsInAutoModeFeedback.FireUpdate();
}
}
private bool isInAutoMode;
private CTimer _scenarioChangeDebounceTimer;
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
private Mutex _scenarioChange = new Mutex();
public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
: base(key)
{
@@ -65,14 +36,14 @@ namespace PepperDash.Essentials.Core
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
}
IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode);
IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
// default to auto mode
IsInAutoMode = true;
isInAutoMode = true;
if (_propertiesConfig.defaultToManualMode)
{
IsInAutoMode = false;
isInAutoMode = false;
}
IsInAutoModeFeedback.FireUpdate();
@@ -84,16 +55,8 @@ 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)
if (isInAutoMode)
{
DetermineRoomCombinationScenario();
}
@@ -101,10 +64,10 @@ namespace PepperDash.Essentials.Core
{
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
}
};
});
}
private void CreateScenarios()
void CreateScenarios()
{
foreach (var scenarioConfig in _propertiesConfig.Scenarios)
{
@@ -113,29 +76,21 @@ namespace PepperDash.Essentials.Core
}
}
private void SetRooms()
void SetRooms()
{
_rooms = new List<IEssentialsRoom>();
foreach (var roomKey in _propertiesConfig.RoomKeys)
{
var room = DeviceManager.GetDeviceForKey(roomKey);
if (DeviceManager.GetDeviceForKey(roomKey) is IEssentialsRoom essentialsRoom)
var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom;
if (room != null)
{
_rooms.Add(essentialsRoom);
_rooms.Add(room);
}
}
var rooms = DeviceManager.AllDevices.OfType<IEssentialsRoom>().Cast<Device>();
foreach (var room in rooms)
{
room.Deactivate();
}
}
private void SetupPartitionStateProviders()
void SetupPartitionStateProviders()
{
foreach (var pConfig in _propertiesConfig.Partitions)
{
@@ -149,18 +104,14 @@ namespace PepperDash.Essentials.Core
}
}
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
StartDebounceTimer();
}
private void StartDebounceTimer()
void StartDebounceTimer()
{
// default to 500ms for manual mode
var time = 500;
// if in auto mode, debounce the scenario change
if (IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
var time = _scenarioChangeDebounceTimeSeconds * 1000;
if (_scenarioChangeDebounceTimer == null)
{
@@ -175,7 +126,7 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// Determines the current room combination scenario based on the state of the partition sensors
/// </summary>
private void DetermineRoomCombinationScenario()
void DetermineRoomCombinationScenario()
{
if (_scenarioChangeDebounceTimer != null)
{
@@ -183,20 +134,14 @@ 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
@@ -209,41 +154,10 @@ namespace PepperDash.Essentials.Core
if (currentScenario != null)
{
this.LogInformation("Found combination Scenario {scenarioKey}", currentScenario.Key);
ChangeScenario(currentScenario);
CurrentScenario = 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;
@@ -254,42 +168,53 @@ 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, this, "Current Scenario: {0}", _currentScenario.Name);
}
var handler = RoomCombinationScenarioChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
}
public BoolFeedback IsInAutoModeFeedback { get; private set; }
public void SetAutoMode()
{
IsInAutoMode = true;
foreach (var partition in Partitions)
{
partition.SetAutoMode();
}
DetermineRoomCombinationScenario();
isInAutoMode = true;
IsInAutoModeFeedback.FireUpdate();
}
public void SetManualMode()
{
IsInAutoMode = false;
foreach (var partition in Partitions)
{
partition.SetManualMode();
}
isInAutoMode = false;
IsInAutoModeFeedback.FireUpdate();
}
public void ToggleMode()
{
if (IsInAutoMode)
{
SetManualMode();
}
else
{
SetAutoMode();
}
isInAutoMode = !isInAutoMode;
IsInAutoModeFeedback.FireUpdate();
}
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
@@ -298,7 +223,7 @@ namespace PepperDash.Essentials.Core
public void TogglePartitionState(string partitionKey)
{
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey));
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
if (partition != null)
{
@@ -308,7 +233,7 @@ namespace PepperDash.Essentials.Core
public void SetRoomCombinationScenario(string scenarioKey)
{
if (IsInAutoMode)
if (isInAutoMode)
{
Debug.LogMessage(LogEventLevel.Information, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
return;

View File

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

View File

@@ -1,50 +1,35 @@
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Newtonsoft.Json;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a room combination scenario
/// </summary>
public class RoomCombinationScenario : IRoomCombinationScenario, IKeyName
public class RoomCombinationScenario: IRoomCombinationScenario
{
private RoomCombinationScenarioConfig _config;
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("partitionStates")]
public List<PartitionState> PartitionStates { get; private set; }
[JsonProperty("uiMap")]
public Dictionary<string, string> UiMap { get; set; }
private bool _isActive;
[JsonProperty("isActive")]
public bool IsActive
{
get { return _isActive; }
set
{
if (value == _isActive)
{
return;
}
_isActive = value;
IsActiveFeedback.FireUpdate();
}
}
[JsonIgnore]
public BoolFeedback IsActiveFeedback { get; private set; }
private List<DeviceActionWrapper> activationActions;
@@ -70,40 +55,36 @@ namespace PepperDash.Essentials.Core
IsActiveFeedback = new BoolFeedback(() => _isActive);
}
public async Task Activate()
public void Activate()
{
this.LogInformation("Activating Scenario {name} with {activationActionCount} action(s) defined", Name, activationActions.Count);
List<Task> tasks = new List<Task>();
Debug.LogMessage(LogEventLevel.Debug, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count);
if (activationActions != null)
{
foreach (var action in activationActions)
{
this.LogInformation("Running Activation action {@action}", action);
await DeviceJsonApi.DoDeviceActionAsync(action);
DeviceJsonApi.DoDeviceAction(action);
}
}
IsActive = true;
_isActive = true;
IsActiveFeedback.FireUpdate();
}
public async Task Deactivate()
public void Deactivate()
{
this.LogInformation("Deactivating Scenario {name} with {deactivationActionCount} action(s) defined", Name, deactivationActions.Count);
List<Task> tasks = new List<Task>();
Debug.LogMessage(LogEventLevel.Debug, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count);
if (deactivationActions != null)
{
foreach (var action in deactivationActions)
{
this.LogInformation("Running deactivation action {actionDeviceKey}:{actionMethod}", action.DeviceKey, action.MethodName);
await DeviceJsonApi.DoDeviceActionAsync(action);
DeviceJsonApi.DoDeviceAction(action);
}
}
IsActive = false;
_isActive = false;
IsActiveFeedback.FireUpdate();
}
}

View File

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

View File

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

View File

@@ -4,16 +4,12 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials.Core
{
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase, IEssentialsRoomEmergency
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
{
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)
{
@@ -29,49 +25,14 @@ 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)
{
ContactClosure_StateChange(args.State);
}
void ContactClosure_StateChange(bool portState)
{
if (portState && TriggerOnClose || !portState && !TriggerOnClose)
{
InEmergency = true;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
if (args.State && TriggerOnClose || !args.State && !TriggerOnClose)
RunEmergencyBehavior();
}
else
{
InEmergency = false;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
}
}
/// <summary>
@@ -83,14 +44,4 @@ 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

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

View File

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

View File

@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
{
void RunRouteAction(string routeKey, string sourceListKey);
void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);
void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);
}
/// <summary>
@@ -77,34 +77,6 @@ namespace PepperDash.Essentials.Core
SecondsCountdownTimer ShutdownPromptTimer { get; }
void SetShutdownPromptSeconds(int seconds);
void StartShutdown(eShutdownType type);
}
/// <summary""'""">
/// Describes a room with a tech password
/// </summary>
public interface ITechPassword
{
event EventHandler<TechPasswordEventArgs> TechPasswordValidateResult;
event EventHandler<EventArgs> TechPasswordChanged;
int TechPasswordLength { get; }
void ValidateTechPassword(string password);
void SetTechPassword(string oldPassword, string newPassword);
}
public class TechPasswordEventArgs : EventArgs
{
public bool IsValid { get; private set; }
public TechPasswordEventArgs(bool isValid)
{
IsValid = isValid;
}
}
/// <summary>
@@ -159,13 +131,4 @@ namespace PepperDash.Essentials.Core
Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; }
}
public interface IHasAccessoryDevices : IKeyName
{
List<string> AccessoryDeviceKeys { get; }
}
public interface IHasCiscoNavigatorTouchpanel
{
string CiscoNavigatorTouchpanelKey { get; }
}
}

View File

@@ -8,7 +8,7 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core.Routing
{
public class DummyRoutingInputsDevice : Device, IRoutingSource, IRoutingOutputs
public class DummyRoutingInputsDevice : Device, IRoutingSource
{
/// <summary>
/// A single output port, backplane, audioVideo

View File

@@ -1,373 +0,0 @@
using PepperDash.Essentials.Core.Queues;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Debug = PepperDash.Core.Debug;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
/// on those destinations.
/// </summary>
public static class Extensions
{
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
private static readonly GenericQueue routeRequestQueue = new GenericQueue("routingQueue");
/// <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 ReleaseRoute(this IRoutingInputs destination)
{
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty));
}
public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)
{
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey));
}
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);
}
/// <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 (!destination.GetRouteToSource(source, null, null, signalType, 0, singleTypeRouteDescriptor, destinationPort, sourcePort))
singleTypeRouteDescriptor = null;
var routes = singleTypeRouteDescriptor?.Routes ?? new List<RouteSwitchDescriptor>();
foreach (var route in routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Route for device: {route}", destination, route.ToString());
}
return (singleTypeRouteDescriptor, null);
}
// otherwise, audioVideo needs to be handled as two steps.
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key);
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, destinationPort, eRoutingSignalType.Video);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, videoRouteDescriptor, destinationPort, sourcePort);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
foreach (var route in audioRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Audio route for device: {route}", destination, route.ToString());
}
foreach (var route in videoRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Video route for device: {route}", destination, route.ToString());
}
if (!audioSuccess && !videoSuccess)
return (null, null);
return (audioRouteDescriptor, videoRouteDescriptor);
}
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;
//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;
RouteRequests[destination.Key] = routeRequest;
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;
}
//New Request
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
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);
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);
}
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty));
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
}
private static void RunRouteRequest(RouteRequest request)
{
try
{
if (request.Source == null)
return;
var (audioOrSingleRoute, videoRoute) = request.Destination.GetRouteToSource(request.Source, request.SignalType, request.DestinationPort, request.SourcePort);
if (audioOrSingleRoute == null && videoRoute == null)
return;
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(audioOrSingleRoute);
if (videoRoute != null)
{
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(videoRoute);
}
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
audioOrSingleRoute.ExecuteRoutes();
videoRoute?.ExecuteRoutes();
} catch(Exception ex)
{
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
}
}
/// <summary>
/// Will release the existing route on the destination, if it is found in
/// RouteDescriptorCollection.DefaultCollection
/// </summary>
/// <param name="destination"></param>
private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey)
{
try
{
Debug.LogMessage(LogEventLevel.Information, "Release route for '{destination}':'{inputPortKey}'", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
{
var coolingDevice = destination as IWarmingCooling;
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
}
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(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
}
}
/// <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 tieLine in midpointTieLines)
{
var midpointDevice = tieLine.SourcePort.ParentDevice as IRoutingInputsOutputs;
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(midpointDevice))
{
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 = midpointDevice.GetRouteToSource(source, midpointOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable, null, sourcePort);
if (upstreamRoutingSuccess)
{
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
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)
{
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 (destination is IRoutingSink)
{
// it's a sink device
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
}
else if (destination is IRouting)
{
routeTable.Routes.Add(new RouteSwitchDescriptor(outputPortToUse, goodInputPort));
}
else // device is merely IRoutingInputOutputs
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
return true;
}
}
}

View File

@@ -1,30 +0,0 @@
/* Unmerged change from project 'PepperDash.Essentials.Core (net6)'
Before:
namespace PepperDash.Essentials.Core.Routing.Interfaces
After:
using PepperDash;
using PepperDash.Essentials;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Core.Routing.Interfaces
*/
namespace PepperDash.Essentials.Core
{
/// <summary>
/// The handler type for a Room's SourceInfoChange
/// </summary>
public delegate void SourceInfoChangeHandler(SourceListItem info, ChangeType type);
//*******************************************************************************************
// Interfaces
/// <summary>
/// For rooms with a single presentation source, change event
/// </summary>
public interface IHasCurrentSourceInfoChange
{
string CurrentSourceInfoKey { get; set; }
SourceListItem CurrentSourceInfo { get; set; }
event SourceInfoChangeHandler CurrentSourceChange;
}
}

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Routing
{
public interface IVideoSync : IKeyed
public interface IVideoSync: IKeyed
{
bool VideoSyncDetected { get; }

View File

@@ -1,10 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
/// </summary>
public interface IRmcRouting : IRoutingNumeric
{
IntFeedback AudioVideoSourceNumericFeedback { get; }
}
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRmcRouting with a feedback event
/// </summary>
public interface IRmcRoutingWithFeedback : IRmcRouting
{
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
/// signal chain, that do switching, must implement this for routing to work otherwise
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
/// device.
/// </summary>
public interface IRouting : IRoutingInputsOutputs
{
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
}
/*public interface IRouting<TInputSelector,TOutputSelector> : IRoutingInputsOutputs
{
void ExecuteSwitch(TInputSelector inputSelector, TOutputSelector outputSelector, eRoutingSignalType signalType);
}*/
}

View File

@@ -1,16 +0,0 @@
using System;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an event structure for reporting output route data
/// </summary>
public interface IRoutingFeedback : IKeyName
{
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
//void OnSwitchChange(RoutingNumericEventArgs e);
}
}

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public interface IRoutingHasVideoInputSyncFeedbacks
{
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
}
}

View File

@@ -1,18 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a class that has a collection of RoutingInputPorts
/// </summary>
public interface IRoutingInputs : IKeyed
{
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
}
/* public interface IRoutingInputs<TSelector> : IKeyed
{
RoutingPortCollection<RoutingInputPort<TSelector>, TSelector> InputPorts { get; }
}*/
}

View File

@@ -0,0 +1,426 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
public class RouteRequest
{
public IRoutingSink Destination {get; set;}
public IRoutingOutputs Source {get; set;}
public eRoutingSignalType SignalType {get; set;}
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
var coolingDevice = sender as IWarmingCooling;
if(args.BoolValue == false)
{
Destination.ReleaseAndMakeRoute(Source, SignalType);
if(sender == null) return;
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
}
}
}
/// <summary>
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
/// on those destinations.
/// </summary>
public static class IRoutingInputsExtensions
{
private static 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
};
var coolingDevice = destination as IWarmingCooling;
RouteRequest existingRouteRequest;
//We already have a route request for this device, and it's a cooling device and is cooling
if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
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}", destination.Key, routeRequest.Source.Key);
return;
}
//New Request
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
RouteRequests.Add(destination.Key, routeRequest);
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down. Storing route request to route to source key: {1}", 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}", destination.Key, routeRequest.Source.Key);
}
destination.ReleaseRoute();
RunRouteRequest(routeRequest);
}
public static void RunRouteRequest(RouteRequest request)
{
if (request.Source == null) return;
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
if (newRoute == null) return;
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
Debug.LogMessage(LogEventLevel.Verbose, request.Destination, "Executing full route");
newRoute.ExecuteRoutes();
}
/// <summary>
/// Will release the existing route on the destination, if it is found in
/// RouteDescriptorCollection.DefaultCollection
/// </summary>
/// <param name="destination"></param>
public static void ReleaseRoute(this IRoutingSink destination)
{
RouteRequest existingRequest;
if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling)
{
var coolingDevice = destination as IWarmingCooling;
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
}
RouteRequests.Remove(destination.Key);
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
if (current != null)
{
Debug.LogMessage(LogEventLevel.Debug, destination, "Releasing current route: {0}", 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 routeDescr = new RouteDescriptor(source, destination, signalType);
// if it's a single signal type, find the route
if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video))
{
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build source route from {0}", source.Key);
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescr))
routeDescr = null;
}
// otherwise, audioVideo needs to be handled as two steps.
else
{
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build audio and video routes from {0}", source.Key);
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescr);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find audio route to {0}", source.Key);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find video route to {0}", source.Key);
if (!audioSuccess && !videoSuccess)
routeDescr = null;
}
//Debug.LogMessage(LogEventLevel.Debug, destination, "Route{0} discovered", routeDescr == null ? " NOT" : "");
return routeDescr;
}
/// <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++;
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", cycle, source.Key, destination.Key);
RoutingInputPort goodInputPort = null;
var destDevInputTies = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || (t.Type & (eRoutingSignalType.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video)));
// find a direct tie
var directTie = destDevInputTies.FirstOrDefault(
t => t.DestinationPort.ParentDevice == destination
&& t.SourcePort.ParentDevice == source);
if (directTie != null) // Found a tie directly to the source
{
goodInputPort = directTie.DestinationPort;
}
else // no direct-connect. Walk back devices.
{
Debug.LogMessage(LogEventLevel.Verbose, destination, "is not directly connected to {0}. Walking down tie lines", source.Key);
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
foreach (var inputTieToTry in attachedMidpoints)
{
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
Debug.LogMessage(LogEventLevel.Verbose, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
{
Debug.LogMessage(LogEventLevel.Verbose, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
continue;
}
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable);
if (upstreamRoutingSuccess)
{
Debug.LogMessage(LogEventLevel.Verbose, destination, "Upstream device route found");
goodInputPort = inputTieToTry.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
}
// we have a route on corresponding inputPort. *** Do the route ***
if (goodInputPort != null)
{
//Debug.LogMessage(LogEventLevel.Verbose, destination, "adding RouteDescriptor");
if (outputPortToUse == null)
{
// it's a sink device
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
}
else if (destination is IRouting)
{
routeTable.Routes.Add(new RouteSwitchDescriptor (outputPortToUse, goodInputPort));
}
else // device is merely IRoutingInputOutputs
Debug.LogMessage(LogEventLevel.Verbose, destination, " No routing. Passthrough device");
//Debug.LogMessage(LogEventLevel.Verbose, destination, "Exiting cycle {0}", cycle);
return true;
}
Debug.LogMessage(LogEventLevel.Verbose, destination, "No route found to {0}", source.Key);
return false;
}
}
// MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE
/// <summary>
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
/// </summary>
public class RouteDescriptorCollection
{
public static RouteDescriptorCollection DefaultCollection
{
get
{
if (_DefaultCollection == null)
_DefaultCollection = new RouteDescriptorCollection();
return _DefaultCollection;
}
}
static RouteDescriptorCollection _DefaultCollection;
List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
/// <summary>
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
/// destination exists already, it will not be added - in order to preserve
/// proper route releasing.
/// </summary>
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
{
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
return;
}
RouteDescriptors.Add(descriptor);
}
/// <summary>
/// Gets the RouteDescriptor for a destination
/// </summary>
/// <returns>null if no RouteDescriptor for a destination exists</returns>
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
{
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
/// <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)
{
var descr = GetRouteDescriptorForDestination(destination);
if (descr != null)
RouteDescriptors.Remove(descr);
return descr;
}
}
/// <summary>
/// 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 RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
{
Destination = destination;
Source = source;
SignalType = signalType;
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}", route.ToString());
if (route.SwitchingDevice is IRoutingSink)
{
var device = route.SwitchingDevice as IRoutingSinkWithSwitching;
if (device == null)
continue;
device.ExecuteSwitch(route.InputPort.Selector);
}
else if (route.SwitchingDevice is IRouting)
{
(route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", 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)
{
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}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
}
}
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 individual link for a route
/// </summary>
public class RouteSwitchDescriptor
{
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
public RoutingOutputPort OutputPort { get; set; }
public RoutingInputPort InputPort { get; set; }
public RouteSwitchDescriptor(RoutingInputPort inputPort)
{
InputPort = inputPort;
}
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
{
InputPort = inputPort;
OutputPort = outputPort;
}
public override string ToString()
{
if(SwitchingDevice is IRouting)
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
else
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
}
}
}

View File

@@ -1,16 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// For devices like RMCs, baluns, other devices with no switching.
/// </summary>
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
{
}
/* /// <summary>
/// For devices like RMCs, baluns, other devices with no switching.
/// </summary>
public interface IRoutingInputsOutputs<TInputSelector, TOutputSelector> : IRoutingInputs<TInputSelector>, IRoutingOutputs<TOutputSelector>
{
}*/
}

View File

@@ -1,7 +0,0 @@
namespace PepperDash.Essentials.Core
{
public interface IRoutingNumeric : IRouting
{
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
}
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRoutingNumeric with a feedback event
/// </summary>
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
{
}
}

View File

@@ -1,19 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a class that has a collection of RoutingOutputPorts
/// </summary>
public interface IRoutingOutputs : IKeyed
{
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
}
/* public interface IRoutingOutputs<TSelector> : IKeyed
{
RoutingPortCollection<RoutingOutputPort<TSelector>, TSelector> OutputPorts { get; }
}*/
}

View File

@@ -1,23 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
{
}
public interface IRoutingSinkWithInputPort :IRoutingSink
{
RoutingInputPort CurrentInputPort { get; }
}
/*/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSink<TSelector> : IRoutingInputs<TSelector>, IHasCurrentSourceInfoChange
{
void UpdateRouteRequest<TOutputSelector>(RouteRequest<TSelector, TOutputSelector> request);
RouteRequest<TSelector, TOutputSelector> GetRouteRequest<TOutputSelector>();
}*/
}

View File

@@ -1,25 +0,0 @@
using PepperDash.Essentials.Core.Routing;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSinkWithFeedback : IRoutingSinkWithSwitching
{
}
/* /// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSinkWithFeedback<TSelector> : IRoutingSinkWithSwitching<TSelector>
{
RouteSwitchDescriptor CurrentRoute { get; }
event EventHandler InputChanged;
}*/
}

View File

@@ -1,27 +0,0 @@
using System;
namespace PepperDash.Essentials.Core
{
public delegate void InputChangedEventHandler(IRoutingSinkWithSwitching destination, RoutingInputPort currentPort);
/// <summary>
/// Endpoint device like a display, that selects inputs
/// </summary>
public interface IRoutingSinkWithSwitching : IRoutingSink
{
void ExecuteSwitch(object inputSelector);
}
public interface IRoutingSinkWithSwitchingWithInputPort:IRoutingSinkWithSwitching, IRoutingSinkWithInputPort
{
event InputChangedEventHandler InputChanged;
}
/* /// <summary>
/// Endpoint device like a display, that selects inputs
/// </summary>
public interface IRoutingSinkWithSwitching<TSelector> : IRoutingSink<TSelector>
{
void ExecuteSwitch(TSelector inputSelector);
}*/
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
/// </summary>
public interface IRoutingSource : IRoutingOutputs
{
}
}

View File

@@ -1,12 +0,0 @@
namespace PepperDash.Essentials.Core
{
public interface IRoutingWithClear : IRouting
{
/// <summary>
/// Clears a route to an output, however a device needs to do that
/// </summary>
/// <param name="outputSelector">Output to clear</param>
/// <param name="signalType">signal type to clear</param>
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
}
}

View File

@@ -1,16 +0,0 @@
using System.Collections.Generic;
using System;
namespace PepperDash.Essentials.Core
{
public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);
/// <summary>
/// Defines an IRouting with a feedback event
/// </summary>
public interface IRoutingWithFeedback : IRouting
{
List<RouteSwitchDescriptor> CurrentRoutes { get; }
event RouteChangedEventHandler RouteChanged;
}
}

View File

@@ -1,8 +0,0 @@
namespace PepperDash.Essentials.Core
{
public interface ITxRouting : IRoutingNumeric
{
IntFeedback VideoSourceNumericFeedback { get; }
IntFeedback AudioSourceNumericFeedback { get; }
}
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRmcRouting with a feedback event
/// </summary>
public interface ITxRoutingWithFeedback : ITxRouting
{
}
}

View File

@@ -1,170 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents an collection of individual route steps between Source and Destination
/// </summary>
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) : this(source, destination, null, signalType)
{
}
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;
}
if (route.SwitchingDevice is IRouting switchingDevice)
{
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
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)
{
if (route.OutputPort == null)
{
continue;
}
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));
}
}
/*/// <summary>
/// Represents an collection of individual route steps between Source and Destination
/// </summary>
public class RouteDescriptor<TInputSelector, TOutputSelector>
{
public IRoutingInputs<TInputSelector> Destination { get; private set; }
public IRoutingOutputs<TOutputSelector> Source { get; private set; }
public eRoutingSignalType SignalType { get; private set; }
public List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>> Routes { get; private set; }
public RouteDescriptor(IRoutingOutputs<TOutputSelector> source, IRoutingInputs<TInputSelector> destination, eRoutingSignalType signalType)
{
Destination = destination;
Source = source;
SignalType = signalType;
Routes = new List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>>();
}
/// <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<TInputSelector> sink)
{
sink.ExecuteSwitch(route.InputPort.Selector);
continue;
}
if (route.SwitchingDevice is IRouting switchingDevice)
{
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
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)
{
if (route.SwitchingDevice is IRouting<TInputSelector, TOutputSelector>)
{
// 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);
}
}
}
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));
}
}*/
}

View File

@@ -1,143 +0,0 @@
using PepperDash.Core;
using Serilog.Events;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
/// </summary>
public class RouteDescriptorCollection
{
public static RouteDescriptorCollection DefaultCollection
{
get
{
if (_DefaultCollection == null)
_DefaultCollection = new RouteDescriptorCollection();
return _DefaultCollection;
}
}
private static RouteDescriptorCollection _DefaultCollection;
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
/// <summary>
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
/// destination exists already, it will not be added - in order to preserve
/// proper route releasing.
/// </summary>
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
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);
return;
}
RouteDescriptors.Add(descriptor);
}
/// <summary>
/// Gets the RouteDescriptor for a destination
/// </summary>
/// <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 ?? null);
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 ?? null, 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, string inputPortKey = "")
{
Debug.LogMessage(LogEventLevel.Information, "Removing route descriptor for '{destination}':'{inputPortKey}'", destination.Key ?? null, 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;
}
}
/*/// <summary>
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
/// </summary>
public class RouteDescriptorCollection<TInputSelector, TOutputSelector>
{
public static RouteDescriptorCollection<TInputSelector, TOutputSelector> DefaultCollection
{
get
{
if (_DefaultCollection == null)
_DefaultCollection = new RouteDescriptorCollection<TInputSelector, TOutputSelector>();
return _DefaultCollection;
}
}
private static RouteDescriptorCollection<TInputSelector, TOutputSelector> _DefaultCollection;
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
/// <summary>
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
/// destination exists already, it will not be added - in order to preserve
/// proper route releasing.
/// </summary>
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
{
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
return;
}
RouteDescriptors.Add(descriptor);
}
/// <summary>
/// Gets the RouteDescriptor for a destination
/// </summary>
/// <returns>null if no RouteDescriptor for a destination exists</returns>
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs<TInputSelector> destination)
{
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
/// <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<TInputSelector> destination)
{
var descr = GetRouteDescriptorForDestination(destination);
if (descr != null)
RouteDescriptors.Remove(descr);
return descr;
}
}*/
}

View File

@@ -1,47 +0,0 @@
using PepperDash.Core;
using Serilog.Events;
using System;
namespace PepperDash.Essentials.Core
{
public class RouteRequest
{
public RoutingInputPort DestinationPort { get; set; }
public RoutingOutputPort SourcePort { get; set; }
public IRoutingInputs Destination { get; set; }
public IRoutingOutputs Source { get; set; }
public eRoutingSignalType SignalType { get; set; }
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
try
{
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());
if (args.BoolValue == true)
{
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;
}
} catch(Exception ex)
{
Debug.LogMessage(ex, "Exception handling cooldown", Destination);
}
}
public override string ToString()
{
return $"Route {Source?.Key ?? "No Source Device"}:{SourcePort?.Key ?? "auto"} to {Destination?.Key ?? "No Destination Device"}:{DestinationPort?.Key ?? "auto"}";
}
}
}

View File

@@ -1,45 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core.Queues;
using System;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Routing
{
public class RouteRequestQueueItem : IQueueMessage
{
private readonly Action<RouteRequest> action;
private readonly RouteRequest routeRequest;
public RouteRequestQueueItem(Action<RouteRequest> routeAction, RouteRequest request)
{
action = routeAction;
routeRequest = request;
}
public void Dispatch()
{
Debug.LogMessage(LogEventLevel.Information, "Dispatching route request {routeRequest}", null, routeRequest);
action(routeRequest);
}
}
public class ReleaseRouteQueueItem : IQueueMessage
{
private readonly Action<IRoutingInputs, string> action;
private readonly IRoutingInputs destination;
private readonly string inputPortKey;
public ReleaseRouteQueueItem(Action<IRoutingInputs, string> action, IRoutingInputs destination, string inputPortKey)
{
this.action = action;
this.destination = destination;
this.inputPortKey = inputPortKey;
}
public void Dispatch()
{
Debug.LogMessage(LogEventLevel.Information, "Dispatching release route request for {destination}:{inputPortKey}", null, destination?.Key ?? "no destination", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
action(destination, inputPortKey);
}
}
}

View File

@@ -1,60 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents an individual link for a route
/// </summary>
public class RouteSwitchDescriptor
{
public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } }
public RoutingOutputPort OutputPort { get; set; }
public RoutingInputPort InputPort { get; set; }
public RouteSwitchDescriptor(RoutingInputPort inputPort)
{
InputPort = inputPort;
}
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
{
InputPort = inputPort;
OutputPort = outputPort;
}
public override string ToString()
{
if (SwitchingDevice is IRouting)
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 != null ? SwitchingDevice.Key : "No Device")} switches to input {(InputPort != null ? InputPort.Key : "No input port")}";
}
}
/*/// <summary>
/// Represents an individual link for a route
/// </summary>
public class RouteSwitchDescriptor<TInputSelector, TOutputSelector>
{
public IRoutingInputs<TInputSelector> SwitchingDevice { get { return InputPort.ParentDevice; } }
public RoutingOutputPort<TOutputSelector> OutputPort { get; set; }
public RoutingInputPort<TInputSelector> InputPort { get; set; }
public RouteSwitchDescriptor(RoutingInputPort<TInputSelector> inputPort)
{
InputPort = inputPort;
}
public RouteSwitchDescriptor(RoutingOutputPort<TOutputSelector> outputPort, RoutingInputPort<TInputSelector> inputPort)
{
InputPort = inputPort;
OutputPort = outputPort;
}
public override string ToString()
{
if (SwitchingDevice is IRouting)
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
else
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
}
}*/
}

View File

@@ -1,271 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using System;
using System.Linq;
namespace PepperDash.Essentials.Core.Routing
{
public class RoutingFeedbackManager:EssentialsDevice
{
public RoutingFeedbackManager(string key, string name): base(key, name)
{
AddPreActivationAction(SubscribeForMidpointFeedback);
AddPreActivationAction(SubscribeForSinkFeedback);
}
private void SubscribeForMidpointFeedback()
{
var midpointDevices = DeviceManager.AllDevices.OfType<IRoutingWithFeedback>();
foreach (var device in midpointDevices)
{
device.RouteChanged += HandleMidpointUpdate;
}
}
private void SubscribeForSinkFeedback()
{
var sinkDevices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
foreach (var device in sinkDevices)
{
device.InputChanged += HandleSinkUpdate;
}
}
private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
{
try
{
var devices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
foreach (var device in devices)
{
UpdateDestination(device, device.CurrentInputPort);
}
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Error handling midpoint update from {midpointKey}:{Exception}", this, midpoint.Key, ex);
}
}
private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
{
try
{
UpdateDestination(sender, currentInputPort);
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Error handling Sink update from {senderKey}:{Exception}", this, sender.Key, ex);
}
}
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
{
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
if(inputPort == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Destination {destination} has not reported an input port yet", this,destination.Key);
return;
}
TieLine firstTieLine;
try
{
var tieLines = TieLineCollection.Default;
firstTieLine = tieLines.FirstOrDefault(tl => tl.DestinationPort.Key == inputPort.Key && tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key);
if (firstTieLine == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No tieline found for inputPort {inputPort}. Clearing current source", this, inputPort);
var tempSourceListItem = new SourceListItem
{
SourceKey = "$transient",
Name = inputPort.Key,
};
destination.CurrentSourceInfo = tempSourceListItem; ;
destination.CurrentSourceInfoKey = "$transient";
return;
}
} catch (Exception ex)
{
Debug.LogMessage(ex, "Error getting first tieline: {Exception}", this, ex);
return;
}
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Getting source for first TieLine {tieLine}", this, firstTieLine);
TieLine sourceTieLine;
try
{
sourceTieLine = GetRootTieLine(firstTieLine);
if (sourceTieLine == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route found to source for inputPort {inputPort}. Clearing current source", this, inputPort);
var tempSourceListItem = new SourceListItem
{
SourceKey = "$transient",
Name = "None",
};
destination.CurrentSourceInfo = tempSourceListItem;
destination.CurrentSourceInfoKey = string.Empty;
return;
}
} catch(Exception ex)
{
Debug.LogMessage(ex, "Error getting sourceTieLine: {Exception}", this, ex);
return;
}
// 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) => {
if(r is IHasMultipleDisplays roomMultipleDisplays)
{
return roomMultipleDisplays.Displays.Any(d => d.Value.Key == destination.Key);
}
if(r is IHasDefaultDisplay roomDefaultDisplay)
{
return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;
}
return false;
});
if(room == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No room found for display {destination}", this, destination.Key);
return;
}
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found room {room} for destination {destination}", this, room.Key, destination.Key);
var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);
if (sourceList == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No source list found for source list key {key}. Unable to find source for tieLine {sourceTieLine}", this, room.SourceListKey, sourceTieLine);
return;
}
// 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);
return sli.Value.SourceKey.Equals(sourceTieLine.SourcePort.ParentDevice.Key,StringComparison.InvariantCultureIgnoreCase);
});
var source = sourceListItem.Value;
var sourceKey = sourceListItem.Key;
if (source == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No source found for device {key}. Creating transient source for {destination}", this, sourceTieLine.SourcePort.ParentDevice.Key, destination);
var tempSourceListItem = new SourceListItem
{
SourceKey = "$transient",
Name = sourceTieLine.SourcePort.Key,
};
destination.CurrentSourceInfoKey = "$transient";
destination.CurrentSourceInfo = tempSourceListItem;
return;
}
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {@source} with key {sourceKey}", this, source, sourceKey);
destination.CurrentSourceInfoKey = sourceKey;
destination.CurrentSourceInfo = source;
}
private TieLine GetRootTieLine(TieLine tieLine)
{
TieLine nextTieLine = null;
try
{
//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);
if(midpoint.CurrentRoutes == null || midpoint.CurrentRoutes.Count == 0)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Midpoint {midpointKey} has no routes",this, midpoint.Key);
return null;
}
var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route => {
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
return route.OutputPort != null && route.InputPort != null && route.OutputPort?.Key == tieLine.SourcePort.Key && route.OutputPort?.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key;
});
if (currentRoute == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route through midpoint {midpoint} for outputPort {outputPort}", this, midpoint.Key, tieLine.SourcePort);
return null;
}
//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);
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);
return GetRootTieLine(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));
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);
return tieLine;
}
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => tl.DestinationPort.Key == tieLine.SourcePort.Key && tl.DestinationPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key );
if (nextTieLine != null)
{
return GetRootTieLine(nextTieLine);
}
} catch (Exception ex)
{
Debug.LogMessage(ex, "Error walking tieLines: {Exception}", this, ex);
return null;
}
return null;
}
}
}

Some files were not shown because too many files have changed in this diff Show More