Compare commits

...

12 Commits

Author SHA1 Message Date
Neil Dorin
1017464980 Adds try/catch to Communication_BytesReceived callback to prevent exception from getting logged when malformed message is received 2020-01-16 16:40:02 -07:00
Neil Dorin
47f4d90c5a Adds support for CEN-IO-DIGIN-104 2020-01-13 21:56:41 -07:00
Neil Dorin
c3dbd41942 Adds configuratble property for current audio/video output text. Defaults to "". 2020-01-13 21:55:59 -07:00
Neil Dorin
45788a4d6b Adds debug statments with logging for Samsung MDC power on/off events 2020-01-13 21:54:49 -07:00
Neil Dorin
d63787bc78 Adds logging for room on/off and occupancy events 2020-01-13 21:54:12 -07:00
Neil Dorin
d269a04bab Modifes debug statments to also print to log as notices 2020-01-10 11:51:25 -07:00
Neil Dorin
cc5889385e Merged in bugfix/ecs-1220 (pull request #42)
Fixes issue with OutputAudioRouteNameFeebdack not being fired

Approved-by: Neil Dorin <ndorin@pepperdash.com>
2019-12-20 21:19:43 +00:00
Neil Dorin
fe14d543d6 Fixes issue with OutputAudioRouteNameFeebdack not being fired 2019-12-20 14:17:21 -07:00
Neil Dorin
9a4af1703b Merged in maintenance/PR-5 (pull request #41)
Update PD.Core version
2019-12-17 18:06:10 +00:00
Neil Dorin
5987b5b078 Update PD.Core version 2019-12-17 10:59:54 -07:00
Neil Dorin
531f93040a Merged in bugfix/ecs-1214 (pull request #40)
Fixed to DGE to allow use of Com ports via EfS bridge.  Updates PD.Core version

Approved-by: Neil Dorin <ndorin@pepperdash.com>
2019-12-16 22:58:35 +00:00
Neil Dorin
5fc93ca251 Fixed to DGE to allow use of Com ports via EfS bridge. Updates PD.Core version 2019-12-16 14:44:34 -07:00
15 changed files with 207 additions and 88 deletions

View File

@@ -38,7 +38,13 @@ namespace PepperDash.Essentials
else if (typeName == "dmdge200c")
dgeDevice = new DmDge200C(comm.IpIdInt, Global.ControlSystem);
var dgeController = new DgeController(config.Key, config.Name, dgeDevice, config, props);
if (dgeDevice == null)
{
Debug.Console(1, "Unable to create DGE device");
return null;
}
var dgeController = new DgeController(config.Key + "-comPorts", config.Name, dgeDevice, config, props);
DeviceManager.AddDevice(dgeController);

View File

@@ -223,6 +223,8 @@ namespace PepperDash.Essentials
CrestronEnvironment.Sleep(1000);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting down room");
RunRouteAction("roomOff");
}
@@ -275,8 +277,8 @@ namespace PepperDash.Essentials
// Run this on a separate thread
new CTimer(o =>
{
Debug.Console(1, this, "Run route action '{0}'", routeKey);
var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Run route action '{0}'", routeKey);
var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
if(dict == null)
{
Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey);

View File

@@ -334,8 +334,11 @@ namespace PepperDash.Essentials
{
// Add Occupancy object from config
if (PropertiesConfig.Occupancy != null)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Setting Occupancy Provider for room");
this.SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as
IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes);
}
this.LogoUrl = PropertiesConfig.Logo.GetUrl();
this.SourceListKey = PropertiesConfig.SourceListKey;
@@ -359,6 +362,8 @@ namespace PepperDash.Essentials
CrestronEnvironment.Sleep(1000);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting down room");
RunRouteAction("roomOff");
}
@@ -408,7 +413,7 @@ namespace PepperDash.Essentials
try
{
Debug.Console(1, this, "Run route action '{0}'", routeKey);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Run route action '{0}'", routeKey);
var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey);
if (dict == null)
{

View File

@@ -30,12 +30,17 @@ namespace PepperDash.Essentials
tsw.SigChange += Panel_SigChange;
}
public EssentialsTouchpanelController(string key, string name, Dge100 panel, string projectName, string sgdPath)
public EssentialsTouchpanelController(string key, string name, Dge100 dge, string projectName, string sgdPath)
: base(key, name)
{
Panel = panel;
panel.LoadSmartObjects(sgdPath);
panel.SigChange += Panel_SigChange;
Panel = dge;
if (!string.IsNullOrEmpty(sgdPath))
dge.LoadSmartObjects(sgdPath);
else
Debug.Console(1, this, "No SGD file path defined");
dge.SigChange += Panel_SigChange;
}
/// <summary>

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Wrapper class for CEN-IO-DIGIN-104 digital input module
/// </summary>
public class CenIoDigIn104Controller : Device, IDigitalInputPorts
{
public CenIoDi104 Di104 { get; private set; }
public CenIoDigIn104Controller(string key, string name, CenIoDi104 di104)
: base(key, name)
{
Di104 = di104;
}
#region IDigitalInputPorts Members
public CrestronCollection<DigitalInput> DigitalInputPorts
{
get { return Di104.DigitalInputPorts; }
}
public int NumberOfDigitalInputPorts
{
get { return Di104.NumberOfDigitalInputPorts; }
}
#endregion
}
}

View File

@@ -52,6 +52,13 @@ namespace PepperDash.Essentials.Core
Debug.Console(1, "Factory Attempting to create new Generic Comm Device");
return new GenericComm(dc);
}
else if (typeName == "ceniodigin104")
{
var control = CommFactory.GetControlPropertiesConfig(dc);
var ipid = control.CresnetIdInt;
return new CenIoDigIn104Controller(key, name, new Crestron.SimplSharpPro.GeneralIO.CenIoDi104(ipid, Global.ControlSystem));
}
// then check for types that have been added by plugin dlls.
if (FactoryMethods.ContainsKey(typeName))

View File

@@ -62,6 +62,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.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>
@@ -114,6 +118,7 @@
<Compile Include="Config\Essentials\ConfigWriter.cs" />
<Compile Include="Config\Essentials\EssentialsConfig.cs" />
<Compile Include="Config\SourceDevicePropertiesConfigBase.cs" />
<Compile Include="Crestron IO\Inputs\CenIoDigIn104Controller.cs" />
<Compile Include="Crestron IO\Inputs\GenericDigitalInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\GenericVersiportInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\IDigitalInput.cs" />

View File

@@ -100,7 +100,8 @@ namespace PepperDash.Essentials.Core
ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
ShutdownPromptSeconds = 60;
ShutdownVacancySeconds = 120;
ShutdownVacancySeconds = 120;
ShutdownType = eShutdownType.None;
RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer");
@@ -140,7 +141,7 @@ namespace PepperDash.Essentials.Core
case eVacancyMode.InShutdownWarning:
{
StartShutdown(eShutdownType.Vacancy);
Debug.Console(0, this, "Shutting Down due to vacancy.");
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting Down due to vacancy.");
break;
}
default:
@@ -163,7 +164,7 @@ namespace PepperDash.Essentials.Core
ShutdownType = type;
ShutdownPromptTimer.Start();
Debug.Console(0, this, "ShutdwonPromptTimer Started. Type: {0}. Seconds: {1}", ShutdownType, ShutdownPromptTimer.SecondsToCount);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "ShutdownPromptTimer Started. Type: {0}. Seconds: {1}", ShutdownType, ShutdownPromptTimer.SecondsToCount);
}
public void StartRoomVacancyTimer(eVacancyMode mode)
@@ -175,7 +176,7 @@ namespace PepperDash.Essentials.Core
VacancyMode = mode;
RoomVacancyShutdownTimer.Start();
Debug.Console(0, this, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount);
}
/// <summary>
@@ -211,6 +212,9 @@ namespace PepperDash.Essentials.Core
return;
}
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Room Occupancy set to device: '{0}'", (statusProvider as Device).Key);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Timeout Minutes from Config is: {0}", timeoutMinutes);
// If status provider is fusion, set flag to remote
if (statusProvider is Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
OccupancyStatusProviderIsRemote = true;
@@ -218,16 +222,14 @@ namespace PepperDash.Essentials.Core
if(timeoutMinutes > 0)
RoomVacancyShutdownSeconds = timeoutMinutes * 60;
Debug.Console(1, this, "RoomVacancyShutdownSeconds set to {0}", RoomVacancyShutdownSeconds);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "RoomVacancyShutdownSeconds set to {0}", RoomVacancyShutdownSeconds);
RoomOccupancy = statusProvider;
OnRoomOccupancyIsSet();
RoomOccupancy.RoomIsOccupiedFeedback.OutputChange -= RoomIsOccupiedFeedback_OutputChange;
RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
Debug.Console(0, this, "Room Occupancy set to device: '{0}'", (statusProvider as Device).Key);
OnRoomOccupancyIsSet();
}
void OnRoomOccupancyIsSet()
@@ -252,13 +254,13 @@ namespace PepperDash.Essentials.Core
{
if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false)
{
Debug.Console(1, this, "Notice: Vacancy Detected");
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Notice: Vacancy Detected");
// Trigger the timer when the room is vacant
StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
}
else
{
Debug.Console(1, this, "Notice: Occupancy Detected");
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Notice: Occupancy Detected");
// Reset the timer when the room is occupied
RoomVacancyShutdownTimer.Cancel();
}

View File

@@ -66,6 +66,11 @@ namespace PepperDash.Essentials.DM
public const int RouteOffTime = 500;
Dictionary<PortNumberType, CTimer> RouteOffTimers = new Dictionary<PortNumberType, CTimer>();
/// <summary>
/// Text that represents when an output has no source routed to it
/// </summary>
public string NoRouteText = "";
/// <summary>
/// Factory method to create a new chassis controller from config data. Limited to 8x8 right now
/// </summary>
@@ -128,6 +133,10 @@ namespace PepperDash.Essentials.DM
controller.InputNames = properties.InputNames;
controller.OutputNames = properties.OutputNames;
if (!string.IsNullOrEmpty(properties.NoRouteText))
controller.NoRouteText = properties.NoRouteText;
controller.PropertiesConfig = properties;
return controller;
}
@@ -217,7 +226,7 @@ namespace PepperDash.Essentials.DM
}
else
{
return "";
return NoRouteText;
}
});
OutputAudioRouteNameFeedbacks[tempX] = new StringFeedback(() =>
@@ -228,7 +237,7 @@ namespace PepperDash.Essentials.DM
}
else
{
return "";
return NoRouteText;
}
});
@@ -747,13 +756,19 @@ namespace PepperDash.Essentials.DM
case DMInputEventIds.UsbRoutedToEventId:
{
Debug.Console(2, this, "DM Input {0} UsbRoutedToEventId", args.Number);
UsbInputRoutedToFeebacks[args.Number].FireUpdate();
if(UsbInputRoutedToFeebacks[args.Number] != null)
UsbInputRoutedToFeebacks[args.Number].FireUpdate();
else
Debug.Console(1, this, "No index of {0} found in UsbInputRoutedToFeedbacks");
break;
}
case DMInputEventIds.HdcpCapabilityFeedbackEventId:
{
Debug.Console(2, this, "DM Input {0} HdcpCapabilityFeedbackEventId", args.Number);
InputCardHdcpCapabilityFeedbacks[args.Number].FireUpdate();
if (InputCardHdcpCapabilityFeedbacks[args.Number] != null)
InputCardHdcpCapabilityFeedbacks[args.Number].FireUpdate();
else
Debug.Console(1, this, "No index of {0} found in InputCardHdcpCapabilityFeedbacks");
break;
}
default:
@@ -818,6 +833,10 @@ namespace PepperDash.Essentials.DM
{
AudioOutputFeedbacks[output].FireUpdate();
}
if (OutputAudioRouteNameFeedbacks.ContainsKey(output))
{
OutputAudioRouteNameFeedbacks[output].FireUpdate();
}
break;
}
case DMOutputEventIds.OutputNameEventId:

View File

@@ -48,6 +48,11 @@ namespace PepperDash.Essentials.DM
public const int RouteOffTime = 500;
Dictionary<PortNumberType, CTimer> RouteOffTimers = new Dictionary<PortNumberType, CTimer>();
/// <summary>
/// Text that represents when an output has no source routed to it
/// </summary>
public string NoRouteText = "";
public static DmpsRoutingController GetDmpsRoutingController(string key, string name,
DmpsRoutingPropertiesConfig properties)
{
@@ -67,6 +72,9 @@ namespace PepperDash.Essentials.DM
controller.InputNames = properties.InputNames;
controller.OutputNames = properties.OutputNames;
if (!string.IsNullOrEmpty(properties.NoRouteText))
controller.NoRouteText = properties.NoRouteText;
return controller;
}
@@ -191,7 +199,7 @@ namespace PepperDash.Essentials.DM
}
else
{
return "";
return NoRouteText;
}
});
OutputAudioRouteNameFeedbacks[outputCard.Number] = new StringFeedback(() =>
@@ -202,7 +210,7 @@ namespace PepperDash.Essentials.DM
}
else
{
return "";
return NoRouteText;
}
});

View File

@@ -32,6 +32,9 @@ namespace PepperDash.Essentials.DM.Config
[JsonProperty("outputNames")]
public Dictionary<uint, string> OutputNames { get; set; }
[JsonProperty("noRouteText")]
public string NoRouteText { get; set; }
[JsonProperty("inputSlotSupportsHdcp2")]
public Dictionary<uint, bool> InputSlotSupportsHdcp2 { get; set; }

View File

@@ -20,6 +20,9 @@ namespace PepperDash.Essentials.DM.Config
[JsonProperty("outputNames")]
public Dictionary<uint, string> OutputNames { get; set; }
[JsonProperty("noRouteText")]
public string NoRouteText { get; set; }
public DmpsRoutingPropertiesConfig()
{
InputNames = new Dictionary<uint, string>();

View File

@@ -28,6 +28,8 @@ namespace PepperDash.Essentials.DM.Endpoints.DGEs
public DgeController(string key, string name, Dge100 device, DeviceConfig dc, CrestronTouchpanelPropertiesConfig props)
:base(key, name, device)
{
DigitalGraphicsEngine = device;
DeviceConfig = dc;
PropertiesConfig = props;

View File

@@ -167,84 +167,91 @@ namespace PepperDash.Essentials.Devices.Displays
/// <param name="sender"></param>
void Communication_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e)
{
// This is probably not thread-safe buffering
// Append the incoming bytes with whatever is in the buffer
var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length];
IncomingBuffer.CopyTo(newBytes, 0);
e.Bytes.CopyTo(newBytes, IncomingBuffer.Length);
if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
Debug.Console(2, this, "Received:{0}", ComTextHelper.GetEscapedText(newBytes));
// Need to find AA FF and have
for (int i = 0; i < newBytes.Length; i++)
try
{
if (newBytes[i] == 0xAA && newBytes[i + 1] == 0xFF)
// This is probably not thread-safe buffering
// Append the incoming bytes with whatever is in the buffer
var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length];
IncomingBuffer.CopyTo(newBytes, 0);
e.Bytes.CopyTo(newBytes, IncomingBuffer.Length);
if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
Debug.Console(2, this, "Received:{0}", ComTextHelper.GetEscapedText(newBytes));
// Need to find AA FF and have
for (int i = 0; i < newBytes.Length; i++)
{
newBytes = newBytes.Skip(i).ToArray(); // Trim off junk if there's "dirt" in the buffer
// parse it
// If it's at least got the header, then process it,
while (newBytes.Length > 4 && newBytes[0] == 0xAA && newBytes[1] == 0xFF)
if (newBytes[i] == 0xAA && newBytes[i + 1] == 0xFF)
{
var msgLen = newBytes[3];
// if the buffer is shorter than the header (3) + message (msgLen) + checksum (1),
// give and save it for next time
if (newBytes.Length < msgLen + 4)
break;
newBytes = newBytes.Skip(i).ToArray(); // Trim off junk if there's "dirt" in the buffer
// Good length, grab the message
var message = newBytes.Skip(4).Take(msgLen).ToArray();
// At this point, the ack/nak is the first byte
if (message[0] == 0x41)
// parse it
// If it's at least got the header, then process it,
while (newBytes.Length > 4 && newBytes[0] == 0xAA && newBytes[1] == 0xFF)
{
switch (message[1]) // type byte
var msgLen = newBytes[3];
// if the buffer is shorter than the header (3) + message (msgLen) + checksum (1),
// give and save it for next time
if (newBytes.Length < msgLen + 4)
break;
// Good length, grab the message
var message = newBytes.Skip(4).Take(msgLen).ToArray();
// At this point, the ack/nak is the first byte
if (message[0] == 0x41)
{
case 0x00: // General status
//UpdatePowerFB(message[2], message[5]); // "power" can be misrepresented when the display sleeps
switch (message[1]) // type byte
{
case 0x00: // General status
//UpdatePowerFB(message[2], message[5]); // "power" can be misrepresented when the display sleeps
// Handle the first power on fb when waiting for it.
if (IsPoweringOnIgnorePowerFb && message[2] == 0x01)
IsPoweringOnIgnorePowerFb = false;
// Ignore general-status power off messages when powering up
if (!(IsPoweringOnIgnorePowerFb && message[2] == 0x00))
UpdatePowerFB(message[2]);
UpdateVolumeFB(message[3]);
UpdateMuteFb(message[4]);
UpdateInputFb(message[5]);
break;
// Handle the first power on fb when waiting for it.
if (IsPoweringOnIgnorePowerFb && message[2] == 0x01)
IsPoweringOnIgnorePowerFb = false;
// Ignore general-status power off messages when powering up
if (!(IsPoweringOnIgnorePowerFb && message[2] == 0x00))
UpdatePowerFB(message[2]);
UpdateVolumeFB(message[3]);
UpdateMuteFb(message[4]);
UpdateInputFb(message[5]);
break;
case 0x11:
UpdatePowerFB(message[2]);
break;
case 0x11:
UpdatePowerFB(message[2]);
break;
case 0x12:
UpdateVolumeFB(message[2]);
break;
case 0x12:
UpdateVolumeFB(message[2]);
break;
case 0x13:
UpdateMuteFb(message[2]);
break;
case 0x13:
UpdateMuteFb(message[2]);
break;
case 0x14:
UpdateInputFb(message[2]);
break;
case 0x14:
UpdateInputFb(message[2]);
break;
default:
break;
default:
break;
}
}
// Skip over what we've used and save the rest for next time
newBytes = newBytes.Skip(5 + msgLen).ToArray();
}
// Skip over what we've used and save the rest for next time
newBytes = newBytes.Skip(5 + msgLen).ToArray();
}
break; // parsing will mean we can stop looking for header in loop
}
}
// Save whatever partial message is here
IncomingBuffer = newBytes;
break; // parsing will mean we can stop looking for header in loop
}
}
// Save whatever partial message is here
IncomingBuffer = newBytes;
}
catch (Exception err)
{
Debug.Console(2, this, "Error parsing feedback: {0}", err);
}
}
/// <summary>
@@ -256,6 +263,7 @@ namespace PepperDash.Essentials.Devices.Displays
if (newVal != _PowerIsOn)
{
_PowerIsOn = newVal;
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Feedback Power State: {0}", _PowerIsOn);
PowerIsOnFeedback.FireUpdate();
}
}
@@ -364,6 +372,8 @@ namespace PepperDash.Essentials.Devices.Displays
/// </summary>
public override void PowerOn()
{
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Powering On Display");
IsPoweringOnIgnorePowerFb = true;
//Send(PowerOnCmd);
SendBytes(new byte[] { 0xAA, 0x11, 0x00, 0x01, 0x01, 0x00 });
@@ -387,6 +397,8 @@ namespace PepperDash.Essentials.Devices.Displays
/// </summary>
public override void PowerOff()
{
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Powering Off Display");
IsPoweringOnIgnorePowerFb = false;
// If a display has unreliable-power off feedback, just override this and
// remove this check.